Skip to main content

read_toc/
read_toc.rs

1/// Opens the default CD drive and prints the Table of Contents.
2use cd_da_reader::CdReader;
3
4const CD_EXTRA_TRAILING_DATA_GAP_SECTORS: u32 = 11_400;
5
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let reader = CdReader::open_default()?;
8    let toc = reader.read_toc()?;
9
10    println!("Table of Contents\n");
11
12    println!(
13        "Tracks {}-{} ({} total), lead-out at LBA {}\n",
14        toc.first_track,
15        toc.last_track,
16        toc.tracks.len(),
17        toc.leadout_lba,
18    );
19
20    for track in &toc.tracks {
21        let kind = if track.is_audio { "audio" } else { "data " };
22        let (m, s, f) = track.start_msf;
23        let sectors = track_end_lba(&toc, track.number) - track.start_lba;
24        let duration_secs = sectors as f64 / 75.0;
25        let mins = (duration_secs / 60.0) as u32;
26        let secs = (duration_secs % 60.0) as u32;
27
28        println!(
29            "  #{:>2}  {}  LBA {:>6}  MSF {:02}:{:02}.{:02}  duration: {:02}:{:02}",
30            track.number, kind, track.start_lba, m, s, f, mins, secs,
31        );
32    }
33
34    Ok(())
35}
36
37/// Returns the exclusive end LBA for a track.
38fn track_end_lba(toc: &cd_da_reader::Toc, track_no: u8) -> u32 {
39    let idx = toc
40        .tracks
41        .iter()
42        .position(|t| t.number == track_no)
43        .unwrap();
44
45    // in case all next tracks are data (but they do exist),
46    // we need to subtract 11,400 sectors
47    if toc.tracks[idx].is_audio
48        && idx + 1 < toc.tracks.len()
49        && toc.tracks[idx + 1..].iter().all(|track| !track.is_audio)
50    {
51        return toc.tracks[idx + 1]
52            .start_lba
53            .saturating_sub(CD_EXTRA_TRAILING_DATA_GAP_SECTORS);
54    }
55
56    if idx + 1 < toc.tracks.len() {
57        toc.tracks[idx + 1].start_lba
58    } else {
59        toc.leadout_lba
60    }
61}