2 * $RCSfile: lib_main.cpp,v $
13 #include <sys/types.h>
18 #include <sys/ioctl.h>
22 #include <linux/cdrom.h>
25 #include "debugging.h"
27 #include "lib_cdinfo.h"
33 // printf("Ccdrom::Ccdrom()");
36 strncpy (cdrom_dev, DEFAULT_CDROM_DEV, FILE_MAX);
39 Ccdrom::Ccdrom (const char *a_cdrom_dev)
41 // printf("Ccdrom::Ccdrom()");
46 strncpy (cdrom_dev, a_cdrom_dev, FILE_MAX);
48 strncpy (cdrom_dev, DEFAULT_CDROM_DEV, FILE_MAX);
53 // printf("Ccdrom::~Ccdrom()");
66 memset (cdrom_dev, 0x0, FILE_MAX);
67 memset (last_err, 0x0, ERR_STR_MAX);
71 const char * Ccdrom::Error(void)
76 void Ccdrom::EnableInfo(bool a_enable_info)
78 // printf("Ccdrom::EnableInfo(bool a_enable_info = %d", (int)a_enable_info);
79 enable_info = a_enable_info;
84 // printf("Ccdrom::ReadTOC()");
85 // printf("opening '%s'", cdrom_dev);
86 fd = open (cdrom_dev, O_RDONLY|O_LARGEFILE);
89 snprintf (last_err, ERR_STR_MAX,
90 "unable to open cdrom '%s', reason: %s", cdrom_dev, strerror(errno) );
94 if ( ioctl (fd, CDROMREADTOCHDR, &toc_header) ) {
95 snprintf (last_err, ERR_STR_MAX, "unable to read TOC, reason: %s", strerror(errno) );
100 fprintf (stdout, "Track list (%i-%i):\n", toc_header[0],toc_header[1] );
103 struct cdrom_tocentry toc;
104 struct cdrom_tocentry tocs[CDROM_LEADOUT+1];
105 memset (&tocs, 0x0, sizeof (tocs));
110 int first_audio = -1;
113 for (int i = toc_header[0]; i <= CDROM_LEADOUT; i++) {
115 memset (&toc, 0x0, sizeof(struct cdrom_tocentry));
117 toc.cdte_format = CDROM_MSF;
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) );
126 memcpy (&tocs[i], &toc, sizeof (struct cdrom_tocentry));
129 fprintf (stdout, "%3d: %02d:%02d:%02d (sec: %06d) %s%s\n",
131 toc.cdte_addr.msf.minute,
132 toc.cdte_addr.msf.second,
133 toc.cdte_addr.msf.frame,
135 toc.cdte_addr.msf.frame +
136 toc.cdte_addr.msf.second*75 +
137 toc.cdte_addr.msf.minute*75*60 - 150,
139 (toc.cdte_ctrl & CDROM_DATA_TRACK) ? "data ":"audio",
140 CDROM_LEADOUT == i ? "(leadout)" : ""
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;
154 if (toc.cdte_ctrl & CDROM_DATA_TRACK) {
156 if (first_data == -1)
157 first_data = toc.cdte_track;
160 if (first_audio == -1)
161 first_audio = toc.cdte_track;
165 if (i == (toc_header[1]) ) {
170 sectors_tot = sectors;
172 // printf("sectors = %d", sectors);
177 fprintf(stderr, "mcn: "); fflush (stdout);
179 struct cdrom_mcn mcn;
180 if (ioctl(fd, CDROM_GET_MCN, &mcn)) {
182 fprintf (stdout, "failed, reason: %s\n", strerror(errno) );
185 fprintf (stdout, "%s\n", (char*)mcn.medium_catalog_number );
189 /* get disk status */
191 fprintf(stdout, "\nDisc status: "); fflush(stdout);
194 int rvs = ioctl (fd, CDROM_DISC_STATUS,0);
195 // printf("rvs = %d", 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?)");
207 fprintf (stdout, "\n");
211 //------------------------ start extinfo
222 struct cdrom_multisession ms;
223 fprintf (stdout, "Multisession: "); fflush(stdout);
224 ms.addr_format = CDROM_LBA;
226 if (ioctl (fd ,CDROMMULTISESSION,&ms))
227 fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) );
229 fprintf (stdout,"%d%s\n",ms.addr.lba,ms.xa_flag?" XA":"");
233 struct cdrom_subchnl sub;
234 fprintf (stdout, "Audio status: "); fflush(stdout);
235 sub.cdsc_format = CDROM_MSF;
237 if (ioctl (fd, CDROMSUBCHNL, &sub))
238 fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) );
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");
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,
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
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 */) {
273 fs = guess_filesystem(0);
274 if ((fs & FS_MASK) != FS_UNKNOWN)
277 fs &= ~FS_MASK; /* del filesystem info */
278 printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track);
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))
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
291 // save the start of the data area
293 data_start = start_track;
295 // skip tracks which belong to the current walked session
296 if (start_track < data_start + isofs_size)
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
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);
315 switch(fs & FS_MASK) {
318 printf("Audio CD\n");
321 printf("CD-ROM with iso9660 fs\n");
323 case FS_ISO_9660_INTERACTIVE:
324 printf("CD-ROM with CD-RTOS and iso9660 fs\n");
327 printf("CD-ROM with high sierra fs\n");
330 printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : "");
333 printf("CD-ROM with Macintosh HFS\n");
336 printf("CD-ROM with both Macintosh HFS and iso9660 fs\n");
339 printf("CD-ROM with Unix UFS\n");
342 printf("CD-ROM with Linux second extended filesystem\n");
345 printf("CD-ROM with unknown filesystem\n");
349 switch(fs & FS_MASK) {
351 case FS_ISO_9660_INTERACTIVE:
353 printf("iso9660: %i MB size, label >%.32s<\n",
354 isofs_size/512,buffer+40);
359 if (first_data == 1 && num_audio > 0)
360 need_lf += printf("mixed mode CD ");
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 ");
368 need_lf += printf("%sPhoto CD ", num_audio > 0 ? " Portfolio " : "");
370 need_lf += printf("Commodore CDTV ");
372 need_lf += printf("CD-Plus/Extra ");
374 need_lf += printf("bootable CD ");
375 if (fs & VIDEOCDI && num_audio == 0)
376 need_lf += printf("Video CD ");
377 if (need_lf) puts("");
380 //------------------------ end extinfo
382 //printf ("s = %d %s \n", s, strerror(errno) );
386 int Ccdrom::OpenPlotFile (const char *plotfile)
388 // printf("Ccdrom::OpenPlotFile(const char *)")
389 pf = fopen (plotfile, "w+");
390 return (pf) ? RET_SUCCESS : RET_FAILED;
393 void Ccdrom::ClosePlotFile ()
395 // printf("Ccdrom::ClosePlotFile ()");
402 int Ccdrom::ReadCD ()
404 //DBG ("Ccdrom::ReadCD\n");
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");
412 if (sectors_tot <= 0) {
413 // printf("sectors_tot <= 0");
414 snprintf (last_err, ERR_STR_MAX, "sectors_tot <= 0");
418 int s = (int) ( (double)lseek (fd, 0, SEEK_END) / 2048.00);
419 lseek (fd, 0, SEEK_SET);
421 if (s != sectors_tot) {
423 "\n! TOC and lseek() return different information about size "
424 ", using lseek()'s number of sectors which is %d\n\n", s);
431 fprintf (stdout, "Reading sectors 1-%d\n", sectors_tot);
433 char buf[ SECTOR_SIZE];
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) );
440 for (int i=0; i< 10; i++ ) {
441 read (fd, buf, SECTOR_SIZE);
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) );
450 struct timeval t1, t2, td;
453 timings = (int*) smalloc ( sizeof(int) * sectors_tot );
456 // printf("malloc error");
457 snprintf (last_err, ERR_STR_MAX, "malloc error");
461 for (int i=0; i< sectors_tot; i++ ) {
463 gettimeofday(&t1, NULL);
464 rv = read (fd, buf, SECTOR_SIZE);
465 gettimeofday(&t2, NULL);
467 //// printf("%d.%d -- %d.%d", t2.tv_sec, t2.tv_usec, t1.tv_sec, t1.tv_usec );
469 time_substract_timeval (&t2, &t1, &td);
470 timings[i] = time_get_time_usec (&td);
471 //printf ("T>%d<\n", tt );
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);
480 if ( (i & 0x3) == 0x3 ) {
481 fprintf (stdout, "%d ok\r", i+1);
486 fprintf (stdout, "\n\n");
490 void Ccdrom::WritePlotData()
492 if (! timings || !pf )
495 fprintf (pf, "# sector -- read time (usec)\n");
498 for (int i=0; i< sectors_tot; i++) {
503 fprintf (pf, "%d %d\n", i, t);
507 void Ccdrom::AnalyzeResults ()
512 int min_time = 1000000;
516 int good_sectors = 0;
521 for (int i=0; i< sectors_tot; i++) {
522 //printf ("> %d \n", timings[i]);
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);
547 avg_time = (int) ( td / (double)good_sectors);
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 );
554 fprintf (stdout, "\nConclusion:\n");
556 if (bad_sectors > 0) {
557 fprintf (stdout, " Disc contains BAD or even readable sectors, put it into trash can!\n");
561 if (max_time < 100000 ) { // < 0.1
562 fprintf (stdout, " Excellent disc!\n");
566 if (max_time < 1000000 ) { // < 0.1
567 fprintf (stdout, " Satisfactory disc\n");
571 fprintf (stdout, " Even there is no unreadable sectors, disc is unstable!\n");
574 void Ccdrom::DeallocateTimings()
576 // printf("Ccdrom::DeallocateTimings()");
580 void * Ccdrom::smalloc (size_t size)
587 memset (ptr, 0x0, size);
593 void Ccdrom::sfree (void*& ptr)
601 void Ccdrom::sfree (int*& ptr)