use dh-autoreconf
[debian/cdck.git] / src / lib_main.cpp
1 /*
2  * $RCSfile: lib_main.cpp,v $
3  * $Author: swaj $
4  * $Revision: 1.10 $
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_HFS:
351 #define PRINT_ISO9660 printf("iso9660: %i MB size, label '%.32s'\n", isofs_size/512,buffer+40);
352                 PRINT_ISO9660;
353                 break;
354
355         case FS_ISO_9660:
356         case FS_ISO_9660_INTERACTIVE:
357                 PRINT_ISO9660;
358                 if (strlen(sbuffer3)>1)
359                         printf ("Creating software: '%.80s'\n", sbuffer3);
360
361                 if (strlen(sbuffer)>1)
362                         printf ("Publisher: '%.80s'\n", sbuffer);
363
364                 if (strlen(sbuffer2)>1)
365                         printf ("Preparer: '%.80s'\n", sbuffer2);
366
367                 break;
368         }
369
370         need_lf = 0;
371         if (first_data == 1 && num_audio > 0)
372                 need_lf += printf("mixed mode CD   ");
373         if (fs & XA)
374                 need_lf += printf("XA sectors   ");
375         if (fs & MULTISESSION)
376                 need_lf += printf("Multisession, offset = %i   ",ms_offset);
377         if (fs & HIDDEN_TRACK)
378                 need_lf += printf("Hidden Track   ");
379         if (fs & PHOTO_CD)
380                 need_lf += printf("%sPhoto CD   ", num_audio > 0 ? " Portfolio " : "");
381         if (fs & CDTV)
382                 need_lf += printf("Commodore CDTV   ");
383         if (first_data > 1)
384                 need_lf += printf("CD-Plus/Extra   ");
385         if (fs & BOOTABLE)
386                 need_lf += printf("bootable CD   ");
387         if (fs & VIDEOCDI && num_audio == 0)
388                 need_lf += printf("Video CD   ");
389         if (need_lf) puts("");
390         }
391
392         //------------------------ end extinfo
393         
394         //printf ("s = %d %s \n", s, strerror(errno) );
395         return RET_SUCCESS;
396 }
397
398 int Ccdrom::OpenPlotFile (const char *plotfile)
399 {
400         // printf("Ccdrom::OpenPlotFile(const char *)")
401         pf = fopen (plotfile, "w+");
402         return (pf) ? RET_SUCCESS : RET_FAILED;
403 }
404
405 void Ccdrom::ClosePlotFile ()
406 {
407         // printf("Ccdrom::ClosePlotFile ()");
408         if (pf) {
409                 fclose (pf);
410                 pf = NULL;
411         }
412 }
413
414 int Ccdrom::ReadCD ()
415 {
416         //DBG ("Ccdrom::ReadCD\n");
417
418         if (disc_status != CDS_DATA_1) {
419                 // printf("disc_status != CDS_DATA_1");
420                 snprintf (last_err, ERR_STR_MAX, "disc_status != CDS_DATA_1");          
421                 return RET_FAILED;
422         }
423
424         if (sectors_tot <= 0) {
425                 // printf("sectors_tot <= 0");
426                 snprintf (last_err, ERR_STR_MAX, "sectors_tot <= 0");           
427                 return RET_FAILED;
428         }
429
430         int s  = (int) ( (double)lseek (fd, 0, SEEK_END) / 2048.00);
431         lseek (fd, 0, SEEK_SET);
432
433         if (s != sectors_tot) {
434                 fprintf (stdout,
435                          "\n! TOC and lseek() return different information about size "
436                          ", using lseek()'s number of sectors which is %d\n\n", s);  
437                 sectors_tot = s;
438         }
439
440         ///DEBUG
441         //sectors_tot = 50;
442
443         fprintf (stdout, "Reading sectors 1-%d\n", sectors_tot);
444
445         char buf[ SECTOR_SIZE];
446
447         if (lseek ( fd, 0, SEEK_SET) < 0) {
448                 // printf("lseek error: %s", strerror(errno) );
449                 snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) );
450                 return RET_FAILED;
451         }
452         for (int i=0; i< 10; i++ ) {
453                  read (fd, buf,  SECTOR_SIZE);
454         }
455         if (lseek ( fd, 0, SEEK_SET) < 0) {
456                 // printf("lseek error: %s", strerror(errno) );
457                 snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) );
458                 return RET_FAILED;
459         }
460
461         int rv;
462         struct timeval t1, t2, td;
463
464         DeallocateTimings();
465         timings = (int*) smalloc ( sizeof(int) * sectors_tot );
466
467         if (!timings) {
468                 // printf("malloc error");
469                 snprintf (last_err, ERR_STR_MAX, "malloc error");
470                 return RET_FAILED;
471         }
472
473         for (int i=0; i< sectors_tot; i++ ) {
474
475                 gettimeofday(&t1, NULL);
476                 rv = read (fd, buf,  SECTOR_SIZE);
477                 gettimeofday(&t2, NULL);
478
479                 //// printf("%d.%d -- %d.%d", t2.tv_sec, t2.tv_usec, t1.tv_sec, t1.tv_usec ); 
480
481                 time_substract_timeval (&t2, &t1, &td);
482                 timings[i] = time_get_time_usec (&td);
483                 //printf ("T>%d<\n", tt );
484
485                 if (rv != SECTOR_SIZE) {
486                         // printf("read error: %s", strerror(errno) );
487                         snprintf (last_err, ERR_STR_MAX, "read error: %s", strerror(errno) ); 
488                         timings[i] = SECTOR_ERROR;
489                         fprintf (stdout, "! unable to read sector %d, reason: %s \n", i+1, strerror(errno)); 
490                         lseek (fd, (off_t)((double)(i+1) * 2048.0), SEEK_SET);
491                 } else {
492                         if ( (i & 0x3) == 0x3   ) {
493                                 fprintf (stdout, "%d ok\r", i+1); 
494                         }
495                 }
496         }
497
498         fprintf (stdout, "\n\n"); 
499         return RET_SUCCESS;
500 }
501
502 void Ccdrom::WritePlotData()
503 {
504         if (! timings  || !pf )
505                 return;
506
507         fprintf (pf, "# sector -- read time (usec)\n");
508         
509         int t;
510         for (int i=0; i< sectors_tot; i++) {
511                 t = timings[i];  
512                 if (t<0)
513                         t = -10;
514
515                 fprintf (pf, "%d %d\n", i, t);
516         }
517 }
518
519 void Ccdrom::AnalyzeResults ()
520 {
521         if (! timings )
522                 return;
523
524         int min_time = 1000000;
525         int max_time = 0;
526         int avg_time = 0;
527         int bad_sectors = 0;
528         int good_sectors = 0;
529
530         int t;
531         double td = 0;
532
533         for (int i=0; i< sectors_tot; i++) {
534                 //printf ("> %d \n", timings[i]);
535
536                 t = timings[i];
537
538                 if (t >= 0 ) {
539                         good_sectors++;
540
541                         if (t > max_time)
542                                 max_time = t;
543
544                         if (t < min_time)
545                                 min_time = t;
546
547
548                         td += (double)t;
549                 } else {
550                         bad_sectors++;
551                 }
552         }
553
554         fprintf (stdout, "CD overall:\n");
555         fprintf (stdout, "   Sectors total: %d:\n", sectors_tot);
556         fprintf (stdout, "   Good sectors: %d:\n", good_sectors);
557         fprintf (stdout, "   Bad sectors (incl. with poor timing): %d\n", bad_sectors);
558
559         avg_time = (int) ( td / (double)good_sectors);  
560
561         fprintf (stdout, "CD timings:\n");
562         fprintf (stdout, "   Minimal = %d usec (%fs)\n", min_time, (double)min_time / 1000000.0 );
563         fprintf (stdout, "   Maximal = %d usec (%fs)\n", max_time, (double)max_time / 1000000.0 );
564         fprintf (stdout, "   Average = %d usec (%fs)\n", avg_time, (double)avg_time / 1000000.0 );
565
566         fprintf (stdout, "\nConclusion:\n");
567
568         if (bad_sectors > 0) {
569                 fprintf (stdout, "   Disc contains BAD or even readable sectors, put it into trash can!\n");
570                 return;
571         }
572
573         if (max_time < 100000 ) { // < 0.1
574                 fprintf (stdout, "   Excellent disc!\n");
575                 return;
576         }
577
578         if (max_time < 1000000 ) { // < 0.1
579                 fprintf (stdout, "   Satisfactory disc\n");
580                 return;
581         }
582
583         fprintf (stdout, "   Even there is no unreadable sectors, disc is unstable!\n");
584 }
585
586 void Ccdrom::DeallocateTimings()
587 {
588         // printf("Ccdrom::DeallocateTimings()");
589         sfree (timings);
590 }
591
592 void * Ccdrom::smalloc (size_t size)
593 {
594         void* ptr;
595
596         ptr = malloc (size);
597
598         if (ptr) {
599                 memset (ptr, 0x0, size);
600         }
601
602         return ptr;
603 }
604
605 void Ccdrom::sfree (void*& ptr)
606 {
607         if (ptr) {
608                 free (ptr);
609                 ptr = 0;
610         }
611 }
612
613 void Ccdrom::sfree (int*& ptr)
614 {
615         if (ptr) {
616                 free (ptr);
617                 ptr = 0;
618         }
619 }
620
621
622
623 /*
624  * __END__
625  */