/*! Read and cache the CD's Track Table of Contents and track info. Return true if successful or false if an error. */ bool read_toc_win32ioctl (_img_private_t *p_env) { CDROM_TOC cdrom_toc; DWORD dw_bytes_returned; unsigned int i, j; bool b_fulltoc_first; /* Do we do fulltoc or DeviceIoControl first? */ if ( ! p_env ) return false; /* The MMC5 spec says: For media other than CD, information may be fabricated in order ^^^ ^^ to emulate a CD structure for the specific media. There is no requirement though that it *has* to and some DVD drives like one by Thompson for XBOX don't support a IOCTL_CDROM_READ_TOC for DVD's. So if we have a DVD we should not prefer getting the TOC via MMC. But on the other hand in GNU/Linux it is reported that using the TOC via MMC gives better information such as for CD DATA Form 2 (used in SVCDs). So if we *don't* have a DVD I think we want to try MMC first. Is this complicated enough? I could be wrong... */ b_fulltoc_first = (CDIO_DISC_MODE_NO_INFO == dvd_discmode_win32ioctl(p_env)); if ( b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true; /* SCSI-MMC READ_TOC (FULTOC) read failed or we don't want to try it initiaily. Try reading TOC via DeviceIoControl... */ if( DeviceIoControl( p_env->h_device_handle, IOCTL_CDROM_READ_TOC, NULL, 0, &cdrom_toc, sizeof(CDROM_TOC), &dw_bytes_returned, NULL ) == 0 ) { char *psz_msg = NULL; long int i_err = GetLastError(); cdio_log_level_t loglevel = b_fulltoc_first ? CDIO_LOG_WARN : CDIO_LOG_DEBUG; FORMAT_ERROR(i_err, psz_msg); if (psz_msg) { cdio_log(loglevel, "could not read TOC (%ld): %s", i_err, psz_msg); LocalFree(psz_msg); } else cdio_log(loglevel, "could not read TOC (%ld)", i_err); if ( !b_fulltoc_first && read_fulltoc_win32mmc(p_env) ) return true; return false; } p_env->gen.i_first_track = cdrom_toc.FirstTrack; p_env->gen.i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1; j = p_env->gen.i_first_track; for( i = 0 ; i <= p_env->gen.i_tracks ; i++, j++ ) { p_env->tocent[ i ].start_lsn = cdio_lba_to_lsn( cdio_msf3_to_lba( cdrom_toc.TrackData[i].Address[1], cdrom_toc.TrackData[i].Address[2], cdrom_toc.TrackData[i].Address[3] ) ); p_env->tocent[i].Control = cdrom_toc.TrackData[i].Control; p_env->tocent[i].Format = cdrom_toc.TrackData[i].Adr; p_env->gen.track_flags[j].preemphasis = p_env->tocent[i].Control & 0x1 ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; p_env->gen.track_flags[j].copy_permit = p_env->tocent[i].Control & 0x2 ? CDIO_TRACK_FLAG_TRUE : CDIO_TRACK_FLAG_FALSE; p_env->gen.track_flags[j].channels = p_env->tocent[i].Control & 0x8 ? 4 : 2; cdio_debug("p_sectors: %i, %lu", i, (unsigned long int) (p_env->tocent[i].start_lsn)); } p_env->gen.toc_init = true; return true; }
/*! Convert a string of the form MM:SS:FF into the corresponding LBA. CDIO_INVALID_LBA is returned if there is an error. */ lba_t cdio_mmssff_to_lba (const char *psz_mmssff) { int psz_field; lba_t ret; unsigned char c; if (0 == strcmp (psz_mmssff, "0")) return 0; c = *psz_mmssff++; if(c >= '0' && c <= '9') psz_field = (c - '0'); else return CDIO_INVALID_LBA; while(':' != (c = *psz_mmssff++)) { if(c >= '0' && c <= '9') psz_field = psz_field * 10 + (c - '0'); else return CDIO_INVALID_LBA; } ret = cdio_msf3_to_lba (psz_field, 0, 0); c = *psz_mmssff++; if(c >= '0' && c <= '9') psz_field = (c - '0'); else return CDIO_INVALID_LBA; if(':' != (c = *psz_mmssff++)) { if(c >= '0' && c <= '9') { psz_field = psz_field * 10 + (c - '0'); c = *psz_mmssff++; if(c != ':') return CDIO_INVALID_LBA; } else return CDIO_INVALID_LBA; } if(psz_field >= CDIO_CD_SECS_PER_MIN) return CDIO_INVALID_LBA; ret += cdio_msf3_to_lba (0, psz_field, 0); c = *psz_mmssff++; if (isdigit(c)) psz_field = (c - '0'); else return -1; if('\0' != (c = *psz_mmssff++)) { if (isdigit(c)) { psz_field = psz_field * 10 + (c - '0'); c = *psz_mmssff++; } else return CDIO_INVALID_LBA; } if('\0' != c) return CDIO_INVALID_LBA; if(psz_field >= CDIO_CD_FRAMES_PER_SEC) return CDIO_INVALID_LBA; ret += psz_field; return ret; }
/*! Read and cache the CD's Track Table of Contents and track info. via a SCSI MMC READ_TOC (FULTOC). Return true if successful or false if an error. */ static bool read_fulltoc_win32mmc (_img_private_t *p_env) { mmc_cdb_t cdb = {{0, }}; CDROM_TOC_FULL cdrom_toc_full; int i_status, i, j; int i_track_format = 0; int i_seen_flag; /* Operation code */ CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); cdb.field[1] = 0x00; /* Format */ cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full)); /* Setup to read header, to get length of data */ CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full)); i_status = run_mmc_cmd_win32ioctl (p_env, 1000*60*3, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_READ, sizeof(cdrom_toc_full), &cdrom_toc_full); if ( 0 != i_status ) { cdio_debug ("SCSI MMC READ_TOC failed\n"); return false; } i_seen_flag=0; for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) { if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) { /* First track number */ p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN; i_track_format = cdrom_toc_full.TrackData[i].PSEC; i_seen_flag|=0x01; } if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) { /* Last track number */ p_env->gen.i_tracks = cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1; i_seen_flag|=0x02; } j = cdrom_toc_full.TrackData[i].POINT; if ( 0xA2 == j ) { /* Start position of the lead out */ p_env->tocent[ p_env->gen.i_tracks ].start_lsn = cdio_lba_to_lsn( cdio_msf3_to_lba( cdrom_toc_full.TrackData[i].PMIN, cdrom_toc_full.TrackData[i].PSEC, cdrom_toc_full.TrackData[i].PFRAME ) ); p_env->tocent[ p_env->gen.i_tracks ].Control = cdrom_toc_full.TrackData[i].Control; p_env->tocent[ p_env->gen.i_tracks ].Format = i_track_format; i_seen_flag|=0x04; } if (cdrom_toc_full.TrackData[i].POINT > 0 && cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) { p_env->tocent[j-1].start_lsn = cdio_lba_to_lsn( cdio_msf3_to_lba( cdrom_toc_full.TrackData[i].PMIN, cdrom_toc_full.TrackData[i].PSEC, cdrom_toc_full.TrackData[i].PFRAME ) ); p_env->tocent[j-1].Control = cdrom_toc_full.TrackData[i].Control; p_env->tocent[j-1].Format = i_track_format; set_track_flags(&(p_env->gen.track_flags[j]), p_env->tocent[j-1].Control); cdio_debug("p_sectors: %i, %lu", i, (unsigned long int) (p_env->tocent[i].start_lsn)); if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks) i_seen_flag|=0x08; } if ( 0x0F == i_seen_flag ) break; } if ( 0x0F == i_seen_flag ) { p_env->gen.toc_init = true; return true; } return false; }