Load /tmp/tmp.TJTHP26358/cdck-0.5.2 into
[debian/cdck.git] / src / lib_main.cpp
1 /*
2  * $RCSfile: lib_main.cpp,v $
3  * $Author: swaj $
4  * $Revision: 1.8 $
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <sys/ioctl.h>
19 #include <sys/time.h>
20 #include <stdlib.h>
21
22 #include <linux/cdrom.h>
23
24 #include "defines.h"
25 #include "debugging.h"
26 #include "lib_time.h"
27 #include "lib_cdinfo.h"
28 #include "lib_main.h"
29
30
31 Ccdrom::Ccdrom ()
32 {
33         // printf("Ccdrom::Ccdrom()");
34         Reset();
35
36         strncpy (cdrom_dev, DEFAULT_CDROM_DEV,  FILE_MAX);
37 }
38
39 Ccdrom::Ccdrom (const char *a_cdrom_dev)
40 {
41         // printf("Ccdrom::Ccdrom()");
42
43         Reset();
44
45         if (cdrom_dev)
46                 strncpy (cdrom_dev, a_cdrom_dev, FILE_MAX);
47         else
48                 strncpy (cdrom_dev, DEFAULT_CDROM_DEV, FILE_MAX);
49 }
50
51 Ccdrom::~Ccdrom ()
52 {
53         // printf("Ccdrom::~Ccdrom()");
54         if (fd > 0) {
55                 close (fd);
56                 fd = -1;
57         }
58         ClosePlotFile ();
59         DeallocateTimings();
60 }
61
62 void Ccdrom::Reset ()
63 {
64         fd = -1;
65         enable_info = false;
66         memset (cdrom_dev, 0x0, FILE_MAX);
67         memset (last_err, 0x0, ERR_STR_MAX);
68         pf = 0;
69 }
70
71 const char * Ccdrom::Error(void)
72 {
73         return last_err;
74 }
75
76 void Ccdrom::EnableInfo(bool a_enable_info)
77 {
78         // printf("Ccdrom::EnableInfo(bool a_enable_info = %d", (int)a_enable_info);
79         enable_info =  a_enable_info;
80 }
81
82 int Ccdrom::ReadTOC()
83 {
84         // printf("Ccdrom::ReadTOC()");
85         // printf("opening '%s'", cdrom_dev);
86         fd = open (cdrom_dev, O_RDONLY|O_LARGEFILE);
87
88         if (fd < 0) {
89                 snprintf (last_err, ERR_STR_MAX,
90                           "unable to open cdrom '%s', reason: %s", cdrom_dev, strerror(errno) );
91                 return RET_FAILED;
92         }
93
94         if ( ioctl (fd, CDROMREADTOCHDR, &toc_header) ) {
95                 snprintf (last_err, ERR_STR_MAX, "unable to read TOC, reason: %s", strerror(errno) );
96                 return RET_FAILED;
97         }
98
99         if (enable_info) {
100                 fprintf (stdout, "Track list (%i-%i):\n", toc_header[0],toc_header[1] );
101         }
102
103         struct cdrom_tocentry toc;
104         struct cdrom_tocentry tocs[CDROM_LEADOUT+1];
105         memset (&tocs, 0x0, sizeof (tocs));
106
107         int sectors=-1;
108         int first_data = -1;
109         int num_data = 0;
110         int first_audio = -1;
111         int num_audio = 0;
112
113         for (int i = toc_header[0]; i <= CDROM_LEADOUT; i++) {
114
115                 memset (&toc, 0x0, sizeof(struct cdrom_tocentry));
116                 toc.cdte_track  = i;
117                 toc.cdte_format = CDROM_MSF;
118
119                 if (ioctl(fd, CDROMREADTOCENTRY,& toc )) {
120                         snprintf (last_err, ERR_STR_MAX,
121                                   "read TOC entry ioctl failed for track %i, reason: %s",
122                                   toc.cdte_track, strerror(errno) );
123                         return RET_FAILED;
124                 }
125
126                 memcpy (&tocs[i], &toc, sizeof (struct cdrom_tocentry)); 
127
128                 if (enable_info) {
129                         fprintf (stdout, "%3d: %02d:%02d:%02d (sec: %06d) %s%s\n",
130                                  toc.cdte_track,
131                                  toc.cdte_addr.msf.minute,
132                                  toc.cdte_addr.msf.second,
133                                  toc.cdte_addr.msf.frame,
134
135                                  toc.cdte_addr.msf.frame +
136                                  toc.cdte_addr.msf.second*75 +
137                                  toc.cdte_addr.msf.minute*75*60 - 150,
138
139                                  (toc.cdte_ctrl & CDROM_DATA_TRACK) ? "data ":"audio",
140                                 CDROM_LEADOUT == i ? "(leadout)" : "" 
141                                 );
142
143                 }
144
145                 if (i == CDROM_LEADOUT) {
146                         if (toc.cdte_ctrl & CDROM_DATA_TRACK)
147                                 sectors = toc.cdte_addr.msf.frame +
148                                         toc.cdte_addr.msf.second*75 +
149                                         toc.cdte_addr.msf.minute*75*60 - 150;
150
151                         break;
152                 }
153
154                 if (toc.cdte_ctrl & CDROM_DATA_TRACK) {
155                         num_data++;
156                         if (first_data == -1)
157                                 first_data = toc.cdte_track;
158                 } else {
159                         num_audio++;
160                         if (first_audio == -1)
161                                 first_audio = toc.cdte_track;
162                 }
163
164                 // skip to leadout
165                 if (i == (toc_header[1]) ) {
166                         i = CDROM_LEADOUT-1;
167                 }
168         }
169
170         sectors_tot = sectors;
171
172         // printf("sectors = %d", sectors); 
173
174         /* get mcn */
175         /*
176         if (enable_info) {
177                 fprintf(stderr, "mcn: "); fflush (stdout);
178         }
179         struct cdrom_mcn mcn;
180         if (ioctl(fd, CDROM_GET_MCN, &mcn)) {
181                 if (enable_info)
182                         fprintf (stdout, "failed, reason: %s\n", strerror(errno) );
183         } else {
184                 if (enable_info)
185                         fprintf (stdout, "%s\n", (char*)mcn.medium_catalog_number );
186         }
187         */
188
189         /* get disk status */
190         if (enable_info) {
191                 fprintf(stdout, "\nDisc status: "); fflush(stdout);
192         }
193         
194         int rvs = ioctl (fd, CDROM_DISC_STATUS,0);
195         // printf("rvs = %d", rvs);
196         if (enable_info) {
197                 switch (rvs) {
198                 case CDS_NO_INFO: fprintf (stdout, "no info"); break;
199                 case CDS_NO_DISC: fprintf (stdout, "no disc"); break;
200                 case CDS_AUDIO:   fprintf (stdout, "audio"); break;
201                 case CDS_DATA_1:  fprintf (stdout, "data mode 1"); break;
202                 case CDS_DATA_2:  fprintf (stdout, "data mode 2"); break;
203                 case CDS_XA_2_1:  fprintf (stdout, "XA mode 1"); break;
204                 case CDS_XA_2_2:  fprintf (stdout, "XA mode 2"); break;
205                 default:          fprintf (stdout, "unknown (failed?)");
206                 }
207                 fprintf (stdout, "\n");
208         }
209         disc_status = rvs;
210
211         //------------------------ start extinfo
212
213         isofs_size = 0;
214         int start_track=0;
215         int ms_offset=0;
216         int data_start=0;
217         int fs=0;
218         int need_lf=0;
219
220         if (enable_info) {
221                 //Multisession 
222                 struct cdrom_multisession  ms;
223                 fprintf (stdout, "Multisession: "); fflush(stdout);
224                 ms.addr_format = CDROM_LBA;
225
226                 if (ioctl (fd ,CDROMMULTISESSION,&ms))
227                         fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) );
228                 else
229                         fprintf (stdout,"%d%s\n",ms.addr.lba,ms.xa_flag?" XA":"");
230
231
232                 //* get audio status
233                 struct cdrom_subchnl sub;
234                 fprintf (stdout, "Audio status: "); fflush(stdout);
235                 sub.cdsc_format = CDROM_MSF;
236
237                 if (ioctl (fd, CDROMSUBCHNL, &sub))
238                         fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) );
239                 else {
240                         switch (sub.cdsc_audiostatus) {
241                         case CDROM_AUDIO_INVALID:    fprintf (stdout, "invalid\n");   break;
242                         case CDROM_AUDIO_PLAY:       fprintf (stdout, "playing");     break;
243                         case CDROM_AUDIO_PAUSED:     fprintf (stdout, "paused");      break;
244                         case CDROM_AUDIO_COMPLETED:  fprintf (stdout, "completed\n"); break;
245                         case CDROM_AUDIO_ERROR:      fprintf (stdout, "error\n");     break;
246                         case CDROM_AUDIO_NO_STATUS:  fprintf (stdout, "no status\n"); break;
247                         default:                     fprintf (stdout, "Oops: unknown\n");
248                         }
249                         if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY ||
250                             sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) {
251                                 printf(" at: %02d:%02d abs / %02d:%02d track %d\n",
252                                        sub.cdsc_absaddr.msf.minute,
253                                        sub.cdsc_absaddr.msf.second,
254                                        sub.cdsc_reladdr.msf.minute,
255                                        sub.cdsc_reladdr.msf.second,
256                                        sub.cdsc_trk);
257                         }
258                 }
259
260                 
261                 fprintf (stdout, "\nTry to find out what sort of CD this is...\n");
262                 // try to find out what sort of CD we have
263                                 
264                 if (0 == num_data) {
265
266                         // no data track, may be a "real" audio CD or hidden track CD
267                         start_track = (int)tocs[1].cdte_addr.msf.frame +
268                                 (int)tocs[1].cdte_addr.msf.second*75 + 
269                                 (int)tocs[1].cdte_addr.msf.minute*75*60 - 150;
270                         // CD-I/Ready says start_track <= 30*75 then CDDA
271                         if (start_track > 100 /* 100 is just a guess */) {
272
273                 fs = guess_filesystem(0);
274                 if ((fs & FS_MASK) != FS_UNKNOWN)
275                         fs |= HIDDEN_TRACK;
276                 else {
277                         fs &= ~FS_MASK; /* del filesystem info */
278                         printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track);
279                 }
280         }
281                 } else {
282                         // we have data track(s)
283                         for (int j = 2, i = first_data; i <= toc_header[1]; i++) {
284                                 if (!(tocs[i].cdte_ctrl & CDROM_DATA_TRACK))
285                                         break;
286                                 start_track = (i == 1) ? 0 :
287                                         (int)tocs[i].cdte_addr.msf.frame +
288                                         (int)tocs[i].cdte_addr.msf.second*75 + 
289                                         (int)tocs[i].cdte_addr.msf.minute*75*60
290                                         -150;
291                                 // save the start of the data area
292                                 if (i == first_data)
293                                         data_start = start_track;
294             
295                                 // skip tracks which belong to the current walked session
296                                 if (start_track < data_start + isofs_size)
297                                         continue;
298
299                                 fs = guess_filesystem(start_track);
300                                 if (!(((fs & FS_MASK) == FS_ISO_9660 ||
301                                        (fs & FS_MASK) == FS_ISO_HFS ||
302                                        (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA)))
303                                         break;  // no method for non-iso9660 multisessions
304
305                                 if (i > 1) {
306                                         // track is beyond last session -> new session found
307                                         ms_offset = start_track;
308                                         printf("session #%d starts at track %2i, offset %6i, isofs size %6i\n",
309                                                j++,tocs[i].cdte_track,start_track,isofs_size);
310                                         fs |= MULTISESSION;
311                                 }
312                         }
313                 }
314         
315         switch(fs & FS_MASK) {
316         case FS_NO_DATA:
317                 if (num_audio > 0)
318                         printf("Audio CD\n");
319                 break;
320         case FS_ISO_9660:
321                 printf("CD-ROM with iso9660 fs\n");
322                 break;
323         case FS_ISO_9660_INTERACTIVE:
324                 printf("CD-ROM with CD-RTOS and iso9660 fs\n");
325                 break;
326         case FS_HIGH_SIERRA:
327                 printf("CD-ROM with high sierra fs\n");
328                 break;
329         case FS_INTERACTIVE:
330                 printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : "");
331                 break;
332         case FS_HFS:
333                 printf("CD-ROM with Macintosh HFS\n");
334                 break;
335         case FS_ISO_HFS:
336                 printf("CD-ROM with both Macintosh HFS and iso9660 fs\n");
337                 break;
338         case FS_UFS:
339                 printf("CD-ROM with Unix UFS\n");
340                 break;
341         case FS_EXT2:
342                 printf("CD-ROM with Linux second extended filesystem\n");
343                 break;
344         case FS_UNKNOWN:
345                 printf("CD-ROM with unknown filesystem\n");
346                 break;
347         }
348
349         switch(fs & FS_MASK) {
350         case FS_ISO_9660:
351         case FS_ISO_9660_INTERACTIVE:
352         case FS_ISO_HFS:
353                 printf("iso9660: %i MB size, label >%.32s<\n",
354                        isofs_size/512,buffer+40);
355                 break;
356         }
357
358         need_lf = 0;
359         if (first_data == 1 && num_audio > 0)
360                 need_lf += printf("mixed mode CD   ");
361         if (fs & XA)
362                 need_lf += printf("XA sectors   ");
363         if (fs & MULTISESSION)
364                 need_lf += printf("Multisession, offset = %i   ",ms_offset);
365         if (fs & HIDDEN_TRACK)
366                 need_lf += printf("Hidden Track   ");
367         if (fs & PHOTO_CD)
368                 need_lf += printf("%sPhoto CD   ", num_audio > 0 ? " Portfolio " : "");
369         if (fs & CDTV)
370                 need_lf += printf("Commodore CDTV   ");
371         if (first_data > 1)
372                 need_lf += printf("CD-Plus/Extra   ");
373         if (fs & BOOTABLE)
374                 need_lf += printf("bootable CD   ");
375         if (fs & VIDEOCDI && num_audio == 0)
376                 need_lf += printf("Video CD   ");
377         if (need_lf) puts("");
378         }
379
380         //------------------------ end extinfo
381         
382         //printf ("s = %d %s \n", s, strerror(errno) );
383         return RET_SUCCESS;
384 }
385
386 int Ccdrom::OpenPlotFile (const char *plotfile)
387 {
388         // printf("Ccdrom::OpenPlotFile(const char *)")
389         pf = fopen (plotfile, "w+");
390         return (pf) ? RET_SUCCESS : RET_FAILED;
391 }
392
393 void Ccdrom::ClosePlotFile ()
394 {
395         // printf("Ccdrom::ClosePlotFile ()");
396         if (pf) {
397                 fclose (pf);
398                 pf = NULL;
399         }
400 }
401
402 int Ccdrom::ReadCD ()
403 {
404         //DBG ("Ccdrom::ReadCD\n");
405
406         if (disc_status != CDS_DATA_1) {
407                 // printf("disc_status != CDS_DATA_1");
408                 snprintf (last_err, ERR_STR_MAX, "disc_status != CDS_DATA_1");          
409                 return RET_FAILED;
410         }
411
412         if (sectors_tot <= 0) {
413                 // printf("sectors_tot <= 0");
414                 snprintf (last_err, ERR_STR_MAX, "sectors_tot <= 0");           
415                 return RET_FAILED;
416         }
417
418         int s  = (int) ( (double)lseek (fd, 0, SEEK_END) / 2048.00);
419         lseek (fd, 0, SEEK_SET);
420
421         if (s != sectors_tot) {
422                 fprintf (stdout,
423                          "\n! TOC and lseek() return different information about size "
424                          ", using lseek()'s number of sectors which is %d\n\n", s);  
425                 sectors_tot = s;
426         }
427
428         ///DEBUG
429         //sectors_tot = 50;
430
431         fprintf (stdout, "Reading sectors 1-%d\n", sectors_tot);
432
433         char buf[ SECTOR_SIZE];
434
435         if (lseek ( fd, 0, SEEK_SET) < 0) {
436                 // printf("lseek error: %s", strerror(errno) );
437                 snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) );
438                 return RET_FAILED;
439         }
440         for (int i=0; i< 10; i++ ) {
441                  read (fd, buf,  SECTOR_SIZE);
442         }
443         if (lseek ( fd, 0, SEEK_SET) < 0) {
444                 // printf("lseek error: %s", strerror(errno) );
445                 snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) );
446                 return RET_FAILED;
447         }
448
449         int rv;
450         struct timeval t1, t2, td;
451
452         DeallocateTimings();
453         timings = (int*) smalloc ( sizeof(int) * sectors_tot );
454
455         if (!timings) {
456                 // printf("malloc error");
457                 snprintf (last_err, ERR_STR_MAX, "malloc error");
458                 return RET_FAILED;
459         }
460
461         for (int i=0; i< sectors_tot; i++ ) {
462
463                 gettimeofday(&t1, NULL);
464                 rv = read (fd, buf,  SECTOR_SIZE);
465                 gettimeofday(&t2, NULL);
466
467                 //// printf("%d.%d -- %d.%d", t2.tv_sec, t2.tv_usec, t1.tv_sec, t1.tv_usec ); 
468
469                 time_substract_timeval (&t2, &t1, &td);
470                 timings[i] = time_get_time_usec (&td);
471                 //printf ("T>%d<\n", tt );
472
473                 if (rv != SECTOR_SIZE) {
474                         // printf("read error: %s", strerror(errno) );
475                         snprintf (last_err, ERR_STR_MAX, "read error: %s", strerror(errno) ); 
476                         timings[i] = SECTOR_ERROR;
477                         fprintf (stdout, "! unable to read sector %d, reason: %s \n", i+1, strerror(errno)); 
478                         lseek (fd, (off_t)((double)(i+1) * 2048.0), SEEK_SET);
479                 } else {
480                         if ( (i & 0x3) == 0x3   ) {
481                                 fprintf (stdout, "%d ok\r", i+1); 
482                         }
483                 }
484         }
485
486         fprintf (stdout, "\n\n"); 
487         return RET_SUCCESS;
488 }
489
490 void Ccdrom::WritePlotData()
491 {
492         if (! timings  || !pf )
493                 return;
494
495         fprintf (pf, "# sector -- read time (usec)\n");
496         
497         int t;
498         for (int i=0; i< sectors_tot; i++) {
499                 t = timings[i];  
500                 if (t<0)
501                         t = -10;
502
503                 fprintf (pf, "%d %d\n", i, t);
504         }
505 }
506
507 void Ccdrom::AnalyzeResults ()
508 {
509         if (! timings )
510                 return;
511
512         int min_time = 1000000;
513         int max_time = 0;
514         int avg_time = 0;
515         int bad_sectors = 0;
516         int good_sectors = 0;
517
518         int t;
519         double td = 0;
520
521         for (int i=0; i< sectors_tot; i++) {
522                 //printf ("> %d \n", timings[i]);
523
524                 t = timings[i];
525
526                 if (t >= 0 ) {
527                         good_sectors++;
528
529                         if (t > max_time)
530                                 max_time = t;
531
532                         if (t < min_time)
533                                 min_time = t;
534
535
536                         td += (double)t;
537                 } else {
538                         bad_sectors++;
539                 }
540         }
541
542         fprintf (stdout, "CD overall:\n");
543         fprintf (stdout, "   Sectors total: %d:\n", sectors_tot);
544         fprintf (stdout, "   Good sectors: %d:\n", good_sectors);
545         fprintf (stdout, "   Bad sectors (incl. with poor timing): %d\n", bad_sectors);
546
547         avg_time = (int) ( td / (double)good_sectors);  
548
549         fprintf (stdout, "CD timings:\n");
550         fprintf (stdout, "   Minimal = %d usec (%fs)\n", min_time, (double)min_time / 1000000.0 );
551         fprintf (stdout, "   Maximal = %d usec (%fs)\n", max_time, (double)max_time / 1000000.0 );
552         fprintf (stdout, "   Average = %d usec (%fs)\n", avg_time, (double)avg_time / 1000000.0 );
553
554         fprintf (stdout, "\nConclusion:\n");
555
556         if (bad_sectors > 0) {
557                 fprintf (stdout, "   Disc contains BAD or even readable sectors, put it into trash can!\n");
558                 return;
559         }
560
561         if (max_time < 100000 ) { // < 0.1
562                 fprintf (stdout, "   Excellent disc!\n");
563                 return;
564         }
565
566         if (max_time < 1000000 ) { // < 0.1
567                 fprintf (stdout, "   Satisfactory disc\n");
568                 return;
569         }
570
571         fprintf (stdout, "   Even there is no unreadable sectors, disc is unstable!\n");
572 }
573
574 void Ccdrom::DeallocateTimings()
575 {
576         // printf("Ccdrom::DeallocateTimings()");
577         sfree (timings);
578 }
579
580 void * Ccdrom::smalloc (size_t size)
581 {
582         void* ptr;
583
584         ptr = malloc (size);
585
586         if (ptr) {
587                 memset (ptr, 0x0, size);
588         }
589
590         return ptr;
591 }
592
593 void Ccdrom::sfree (void*& ptr)
594 {
595         if (ptr) {
596                 free (ptr);
597                 ptr = 0;
598         }
599 }
600
601 void Ccdrom::sfree (int*& ptr)
602 {
603         if (ptr) {
604                 free (ptr);
605                 ptr = 0;
606         }
607 }
608
609
610
611 /*
612  * __END__
613  */