1use cdrom::cdtime::CdTime; 2use cdrom::cue::{Track, TrackType}; 3use cdrom::reader::{CdRom, CdRomFileFormat}; 4use std::env; 5use std::error::Error; 6 7fn main() -> Result<(), Box<dyn Error>> { 8 let mut args = env::args(); 9 let exe_name = args.next().expect("Executable name arg not present"); 10 11 let Some(path) = args.next() else { panic!("USAGE: {exe_name} file_path") }; 12 13 let Some(cdrom_format) = CdRomFileFormat::from_file_path(&path) else { 14 panic!("Unable to determine CD-ROM image format for path '{path}'"); 15 }; 16 17 validate(CdRom::open(&path, cdrom_format)?)?; 18 validate(CdRom::open_in_memory(&path, cdrom_format)?)?; 19 20 Ok(()) 21} 22 23fn validate(mut disc: CdRom) -> Result<(), Box<dyn Error>> { 24 let mut sector_buffer = vec![0; cdrom::BYTES_PER_SECTOR as usize]; 25 26 let last_track_number = disc.cue().last_track().number; 27 28 for track_number in 1..=last_track_number { 29 let track = disc.cue().track(track_number); 30 let &Track { track_type, start_time, end_time, .. } = track; 31 32 // In data tracks, sector MSF addresses should be valid for at least the 2 seconds leading 33 // up to effective start time (INDEX 01 in CUE files). Sectors before this may not have 34 // valid addresses but that's fine 35 let data_address_valid_threshold = 36 track.effective_start_time().saturating_sub(CdTime::new(0, 2, 0)); 37 38 let mut time = start_time; 39 while time < end_time { 40 let relative_time = time - start_time; 41 disc.read_sector(track_number, relative_time, &mut sector_buffer)?; 42 43 if track_type == TrackType::Data && time >= data_address_valid_threshold { 44 // First 12 sector bytes are sync, next 3 bytes are MSF time 45 let header_msf = CdTime::new( 46 bcd_to_binary(sector_buffer[12]), 47 bcd_to_binary(sector_buffer[13]), 48 bcd_to_binary(sector_buffer[14]), 49 ); 50 51 assert_eq!( 52 header_msf, time, 53 "Time mismatch in data track {track_number} at relative time {relative_time}! Expected {time}, got {header_msf}" 54 ); 55 } 56 57 time += CdTime::new(0, 0, 1); 58 } 59 } 60 61 Ok(()) 62} 63 64fn bcd_to_binary(bcd_value: u8) -> u8 { 65 (bcd_value & 0x0F) + 10 * (bcd_value >> 4) 66}