/* * $RCSfile: lib_main.cpp,v $ * $Author: swaj $ * $Revision: 1.8 $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" #include "debugging.h" #include "lib_time.h" #include "lib_cdinfo.h" #include "lib_main.h" Ccdrom::Ccdrom () { // printf("Ccdrom::Ccdrom()"); Reset(); strncpy (cdrom_dev, DEFAULT_CDROM_DEV, FILE_MAX); } Ccdrom::Ccdrom (const char *a_cdrom_dev) { // printf("Ccdrom::Ccdrom()"); Reset(); if (cdrom_dev) strncpy (cdrom_dev, a_cdrom_dev, FILE_MAX); else strncpy (cdrom_dev, DEFAULT_CDROM_DEV, FILE_MAX); } Ccdrom::~Ccdrom () { // printf("Ccdrom::~Ccdrom()"); if (fd > 0) { close (fd); fd = -1; } ClosePlotFile (); DeallocateTimings(); } void Ccdrom::Reset () { fd = -1; enable_info = false; memset (cdrom_dev, 0x0, FILE_MAX); memset (last_err, 0x0, ERR_STR_MAX); pf = 0; } const char * Ccdrom::Error(void) { return last_err; } void Ccdrom::EnableInfo(bool a_enable_info) { // printf("Ccdrom::EnableInfo(bool a_enable_info = %d", (int)a_enable_info); enable_info = a_enable_info; } int Ccdrom::ReadTOC() { // printf("Ccdrom::ReadTOC()"); // printf("opening '%s'", cdrom_dev); fd = open (cdrom_dev, O_RDONLY|O_LARGEFILE); if (fd < 0) { snprintf (last_err, ERR_STR_MAX, "unable to open cdrom '%s', reason: %s", cdrom_dev, strerror(errno) ); return RET_FAILED; } if ( ioctl (fd, CDROMREADTOCHDR, &toc_header) ) { snprintf (last_err, ERR_STR_MAX, "unable to read TOC, reason: %s", strerror(errno) ); return RET_FAILED; } if (enable_info) { fprintf (stdout, "Track list (%i-%i):\n", toc_header[0],toc_header[1] ); } struct cdrom_tocentry toc; struct cdrom_tocentry tocs[CDROM_LEADOUT+1]; memset (&tocs, 0x0, sizeof (tocs)); int sectors=-1; int first_data = -1; int num_data = 0; int first_audio = -1; int num_audio = 0; for (int i = toc_header[0]; i <= CDROM_LEADOUT; i++) { memset (&toc, 0x0, sizeof(struct cdrom_tocentry)); toc.cdte_track = i; toc.cdte_format = CDROM_MSF; if (ioctl(fd, CDROMREADTOCENTRY,& toc )) { snprintf (last_err, ERR_STR_MAX, "read TOC entry ioctl failed for track %i, reason: %s", toc.cdte_track, strerror(errno) ); return RET_FAILED; } memcpy (&tocs[i], &toc, sizeof (struct cdrom_tocentry)); if (enable_info) { fprintf (stdout, "%3d: %02d:%02d:%02d (sec: %06d) %s%s\n", toc.cdte_track, toc.cdte_addr.msf.minute, toc.cdte_addr.msf.second, toc.cdte_addr.msf.frame, toc.cdte_addr.msf.frame + toc.cdte_addr.msf.second*75 + toc.cdte_addr.msf.minute*75*60 - 150, (toc.cdte_ctrl & CDROM_DATA_TRACK) ? "data ":"audio", CDROM_LEADOUT == i ? "(leadout)" : "" ); } if (i == CDROM_LEADOUT) { if (toc.cdte_ctrl & CDROM_DATA_TRACK) sectors = toc.cdte_addr.msf.frame + toc.cdte_addr.msf.second*75 + toc.cdte_addr.msf.minute*75*60 - 150; break; } if (toc.cdte_ctrl & CDROM_DATA_TRACK) { num_data++; if (first_data == -1) first_data = toc.cdte_track; } else { num_audio++; if (first_audio == -1) first_audio = toc.cdte_track; } // skip to leadout if (i == (toc_header[1]) ) { i = CDROM_LEADOUT-1; } } sectors_tot = sectors; // printf("sectors = %d", sectors); /* get mcn */ /* if (enable_info) { fprintf(stderr, "mcn: "); fflush (stdout); } struct cdrom_mcn mcn; if (ioctl(fd, CDROM_GET_MCN, &mcn)) { if (enable_info) fprintf (stdout, "failed, reason: %s\n", strerror(errno) ); } else { if (enable_info) fprintf (stdout, "%s\n", (char*)mcn.medium_catalog_number ); } */ /* get disk status */ if (enable_info) { fprintf(stdout, "\nDisc status: "); fflush(stdout); } int rvs = ioctl (fd, CDROM_DISC_STATUS,0); // printf("rvs = %d", rvs); if (enable_info) { switch (rvs) { case CDS_NO_INFO: fprintf (stdout, "no info"); break; case CDS_NO_DISC: fprintf (stdout, "no disc"); break; case CDS_AUDIO: fprintf (stdout, "audio"); break; case CDS_DATA_1: fprintf (stdout, "data mode 1"); break; case CDS_DATA_2: fprintf (stdout, "data mode 2"); break; case CDS_XA_2_1: fprintf (stdout, "XA mode 1"); break; case CDS_XA_2_2: fprintf (stdout, "XA mode 2"); break; default: fprintf (stdout, "unknown (failed?)"); } fprintf (stdout, "\n"); } disc_status = rvs; //------------------------ start extinfo isofs_size = 0; int start_track=0; int ms_offset=0; int data_start=0; int fs=0; int need_lf=0; if (enable_info) { //Multisession struct cdrom_multisession ms; fprintf (stdout, "Multisession: "); fflush(stdout); ms.addr_format = CDROM_LBA; if (ioctl (fd ,CDROMMULTISESSION,&ms)) fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) ); else fprintf (stdout,"%d%s\n",ms.addr.lba,ms.xa_flag?" XA":""); //* get audio status struct cdrom_subchnl sub; fprintf (stdout, "Audio status: "); fflush(stdout); sub.cdsc_format = CDROM_MSF; if (ioctl (fd, CDROMSUBCHNL, &sub)) fprintf (stdout, "failed to get, reason: %s\n", strerror(errno) ); else { switch (sub.cdsc_audiostatus) { case CDROM_AUDIO_INVALID: fprintf (stdout, "invalid\n"); break; case CDROM_AUDIO_PLAY: fprintf (stdout, "playing"); break; case CDROM_AUDIO_PAUSED: fprintf (stdout, "paused"); break; case CDROM_AUDIO_COMPLETED: fprintf (stdout, "completed\n"); break; case CDROM_AUDIO_ERROR: fprintf (stdout, "error\n"); break; case CDROM_AUDIO_NO_STATUS: fprintf (stdout, "no status\n"); break; default: fprintf (stdout, "Oops: unknown\n"); } if (sub.cdsc_audiostatus == CDROM_AUDIO_PLAY || sub.cdsc_audiostatus == CDROM_AUDIO_PAUSED) { printf(" at: %02d:%02d abs / %02d:%02d track %d\n", sub.cdsc_absaddr.msf.minute, sub.cdsc_absaddr.msf.second, sub.cdsc_reladdr.msf.minute, sub.cdsc_reladdr.msf.second, sub.cdsc_trk); } } fprintf (stdout, "\nTry to find out what sort of CD this is...\n"); // try to find out what sort of CD we have if (0 == num_data) { // no data track, may be a "real" audio CD or hidden track CD start_track = (int)tocs[1].cdte_addr.msf.frame + (int)tocs[1].cdte_addr.msf.second*75 + (int)tocs[1].cdte_addr.msf.minute*75*60 - 150; // CD-I/Ready says start_track <= 30*75 then CDDA if (start_track > 100 /* 100 is just a guess */) { fs = guess_filesystem(0); if ((fs & FS_MASK) != FS_UNKNOWN) fs |= HIDDEN_TRACK; else { fs &= ~FS_MASK; /* del filesystem info */ printf("Oops: %i unused sectors at start, but hidden track check failed.\n",start_track); } } } else { // we have data track(s) for (int j = 2, i = first_data; i <= toc_header[1]; i++) { if (!(tocs[i].cdte_ctrl & CDROM_DATA_TRACK)) break; start_track = (i == 1) ? 0 : (int)tocs[i].cdte_addr.msf.frame + (int)tocs[i].cdte_addr.msf.second*75 + (int)tocs[i].cdte_addr.msf.minute*75*60 -150; // save the start of the data area if (i == first_data) data_start = start_track; // skip tracks which belong to the current walked session if (start_track < data_start + isofs_size) continue; fs = guess_filesystem(start_track); if (!(((fs & FS_MASK) == FS_ISO_9660 || (fs & FS_MASK) == FS_ISO_HFS || (fs & FS_MASK) == FS_ISO_9660_INTERACTIVE) && (fs & XA))) break; // no method for non-iso9660 multisessions if (i > 1) { // track is beyond last session -> new session found ms_offset = start_track; printf("session #%d starts at track %2i, offset %6i, isofs size %6i\n", j++,tocs[i].cdte_track,start_track,isofs_size); fs |= MULTISESSION; } } } switch(fs & FS_MASK) { case FS_NO_DATA: if (num_audio > 0) printf("Audio CD\n"); break; case FS_ISO_9660: printf("CD-ROM with iso9660 fs\n"); break; case FS_ISO_9660_INTERACTIVE: printf("CD-ROM with CD-RTOS and iso9660 fs\n"); break; case FS_HIGH_SIERRA: printf("CD-ROM with high sierra fs\n"); break; case FS_INTERACTIVE: printf("CD-Interactive%s\n", num_audio > 0 ? "/Ready" : ""); break; case FS_HFS: printf("CD-ROM with Macintosh HFS\n"); break; case FS_ISO_HFS: printf("CD-ROM with both Macintosh HFS and iso9660 fs\n"); break; case FS_UFS: printf("CD-ROM with Unix UFS\n"); break; case FS_EXT2: printf("CD-ROM with Linux second extended filesystem\n"); break; case FS_UNKNOWN: printf("CD-ROM with unknown filesystem\n"); break; } switch(fs & FS_MASK) { case FS_ISO_9660: case FS_ISO_9660_INTERACTIVE: case FS_ISO_HFS: printf("iso9660: %i MB size, label >%.32s<\n", isofs_size/512,buffer+40); break; } need_lf = 0; if (first_data == 1 && num_audio > 0) need_lf += printf("mixed mode CD "); if (fs & XA) need_lf += printf("XA sectors "); if (fs & MULTISESSION) need_lf += printf("Multisession, offset = %i ",ms_offset); if (fs & HIDDEN_TRACK) need_lf += printf("Hidden Track "); if (fs & PHOTO_CD) need_lf += printf("%sPhoto CD ", num_audio > 0 ? " Portfolio " : ""); if (fs & CDTV) need_lf += printf("Commodore CDTV "); if (first_data > 1) need_lf += printf("CD-Plus/Extra "); if (fs & BOOTABLE) need_lf += printf("bootable CD "); if (fs & VIDEOCDI && num_audio == 0) need_lf += printf("Video CD "); if (need_lf) puts(""); } //------------------------ end extinfo //printf ("s = %d %s \n", s, strerror(errno) ); return RET_SUCCESS; } int Ccdrom::OpenPlotFile (const char *plotfile) { // printf("Ccdrom::OpenPlotFile(const char *)") pf = fopen (plotfile, "w+"); return (pf) ? RET_SUCCESS : RET_FAILED; } void Ccdrom::ClosePlotFile () { // printf("Ccdrom::ClosePlotFile ()"); if (pf) { fclose (pf); pf = NULL; } } int Ccdrom::ReadCD () { //DBG ("Ccdrom::ReadCD\n"); if (disc_status != CDS_DATA_1) { // printf("disc_status != CDS_DATA_1"); snprintf (last_err, ERR_STR_MAX, "disc_status != CDS_DATA_1"); return RET_FAILED; } if (sectors_tot <= 0) { // printf("sectors_tot <= 0"); snprintf (last_err, ERR_STR_MAX, "sectors_tot <= 0"); return RET_FAILED; } int s = (int) ( (double)lseek (fd, 0, SEEK_END) / 2048.00); lseek (fd, 0, SEEK_SET); if (s != sectors_tot) { fprintf (stdout, "\n! TOC and lseek() return different information about size " ", using lseek()'s number of sectors which is %d\n\n", s); sectors_tot = s; } ///DEBUG //sectors_tot = 50; fprintf (stdout, "Reading sectors 1-%d\n", sectors_tot); char buf[ SECTOR_SIZE]; if (lseek ( fd, 0, SEEK_SET) < 0) { // printf("lseek error: %s", strerror(errno) ); snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) ); return RET_FAILED; } for (int i=0; i< 10; i++ ) { read (fd, buf, SECTOR_SIZE); } if (lseek ( fd, 0, SEEK_SET) < 0) { // printf("lseek error: %s", strerror(errno) ); snprintf (last_err, ERR_STR_MAX, "lseek error: %s", strerror(errno) ); return RET_FAILED; } int rv; struct timeval t1, t2, td; DeallocateTimings(); timings = (int*) smalloc ( sizeof(int) * sectors_tot ); if (!timings) { // printf("malloc error"); snprintf (last_err, ERR_STR_MAX, "malloc error"); return RET_FAILED; } for (int i=0; i< sectors_tot; i++ ) { gettimeofday(&t1, NULL); rv = read (fd, buf, SECTOR_SIZE); gettimeofday(&t2, NULL); //// printf("%d.%d -- %d.%d", t2.tv_sec, t2.tv_usec, t1.tv_sec, t1.tv_usec ); time_substract_timeval (&t2, &t1, &td); timings[i] = time_get_time_usec (&td); //printf ("T>%d<\n", tt ); if (rv != SECTOR_SIZE) { // printf("read error: %s", strerror(errno) ); snprintf (last_err, ERR_STR_MAX, "read error: %s", strerror(errno) ); timings[i] = SECTOR_ERROR; fprintf (stdout, "! unable to read sector %d, reason: %s \n", i+1, strerror(errno)); lseek (fd, (off_t)((double)(i+1) * 2048.0), SEEK_SET); } else { if ( (i & 0x3) == 0x3 ) { fprintf (stdout, "%d ok\r", i+1); } } } fprintf (stdout, "\n\n"); return RET_SUCCESS; } void Ccdrom::WritePlotData() { if (! timings || !pf ) return; fprintf (pf, "# sector -- read time (usec)\n"); int t; for (int i=0; i< sectors_tot; i++) { t = timings[i]; if (t<0) t = -10; fprintf (pf, "%d %d\n", i, t); } } void Ccdrom::AnalyzeResults () { if (! timings ) return; int min_time = 1000000; int max_time = 0; int avg_time = 0; int bad_sectors = 0; int good_sectors = 0; int t; double td = 0; for (int i=0; i< sectors_tot; i++) { //printf ("> %d \n", timings[i]); t = timings[i]; if (t >= 0 ) { good_sectors++; if (t > max_time) max_time = t; if (t < min_time) min_time = t; td += (double)t; } else { bad_sectors++; } } fprintf (stdout, "CD overall:\n"); fprintf (stdout, " Sectors total: %d:\n", sectors_tot); fprintf (stdout, " Good sectors: %d:\n", good_sectors); fprintf (stdout, " Bad sectors (incl. with poor timing): %d\n", bad_sectors); avg_time = (int) ( td / (double)good_sectors); fprintf (stdout, "CD timings:\n"); fprintf (stdout, " Minimal = %d usec (%fs)\n", min_time, (double)min_time / 1000000.0 ); fprintf (stdout, " Maximal = %d usec (%fs)\n", max_time, (double)max_time / 1000000.0 ); fprintf (stdout, " Average = %d usec (%fs)\n", avg_time, (double)avg_time / 1000000.0 ); fprintf (stdout, "\nConclusion:\n"); if (bad_sectors > 0) { fprintf (stdout, " Disc contains BAD or even readable sectors, put it into trash can!\n"); return; } if (max_time < 100000 ) { // < 0.1 fprintf (stdout, " Excellent disc!\n"); return; } if (max_time < 1000000 ) { // < 0.1 fprintf (stdout, " Satisfactory disc\n"); return; } fprintf (stdout, " Even there is no unreadable sectors, disc is unstable!\n"); } void Ccdrom::DeallocateTimings() { // printf("Ccdrom::DeallocateTimings()"); sfree (timings); } void * Ccdrom::smalloc (size_t size) { void* ptr; ptr = malloc (size); if (ptr) { memset (ptr, 0x0, size); } return ptr; } void Ccdrom::sfree (void*& ptr) { if (ptr) { free (ptr); ptr = 0; } } void Ccdrom::sfree (int*& ptr) { if (ptr) { free (ptr); ptr = 0; } } /* * __END__ */