use cd_da_reader::{CdReader, ReadOptions, SectorReadFormat};
const PVD_SECTOR_OFFSET: u32 = 16;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let reader = CdReader::open_default()?;
let toc = reader.read_toc()?;
let data_track = toc
.tracks
.iter()
.find(|t| !t.is_audio)
.ok_or("no data track on this disc (need a mixed-mode / enhanced CD)")?;
let pvd_lba = data_track.start_lba + PVD_SECTOR_OFFSET;
println!(
"Data track #{} starts at LBA {}; reading PVD at LBA {}\n",
data_track.number, data_track.start_lba, pvd_lba
);
let mut options = ReadOptions::default().with_format(SectorReadFormat::Mode1Raw);
let raw = reader.read_sector_range(pvd_lba, 1, &options)?;
if raw.len() != 2352 {
return Err(format!("raw read returned {} bytes, expected 2352", raw.len()).into());
}
let sync_ok = raw[0] == 0x00 && raw[1..11].iter().all(|&b| b == 0xFF) && raw[11] == 0x00;
let mode = raw[15];
println!("raw sync pattern : {}", pass(sync_ok));
println!("raw sector mode : Mode {mode}");
let user_offset = match mode {
1 => 16,
2 => 24,
other => return Err(format!("unexpected sector mode {other}").into()),
};
let raw_user = &raw[user_offset..user_offset + 2048];
let iso_ok = raw_user[0] == 0x01 && &raw_user[1..6] == b"CD001";
println!("ISO 9660 'CD001' : {}", pass(iso_ok));
if mode == 1 {
options = options.with_format(SectorReadFormat::Mode1Cooked);
let cooked = reader.read_sector_range(pvd_lba, 1, &options)?;
if cooked.len() != 2048 {
return Err(
format!("cooked read returned {} bytes, expected 2048", cooked.len()).into(),
);
}
let matches_raw = cooked == raw_user;
println!("cooked == raw[16..]: {}", pass(matches_raw));
if sync_ok && iso_ok && matches_raw {
println!("\nALL CHECKS PASSED — cooked and raw data reads are correct.");
return Ok(());
}
} else {
println!(
"\nData track is Mode {mode} (e.g. CD-Extra / Mode 2 Form 1). The cooked path \
targets Mode 1, so only the raw checks apply here."
);
if sync_ok && iso_ok {
println!("Raw read verified against on-disc ISO structure.");
return Ok(());
}
}
Err("one or more verification checks FAILED — see output above".into())
}
fn pass(ok: bool) -> &'static str {
if ok { "PASS" } else { "FAIL" }
}