Say when a title set is dropped from a DVD scan

parse_vmg skipped any title set whose parse failed, with no log and no
counter. A real disc enumerated 38 titles from one image and 10 from
another, and the 28 discarded failures were invisible — the symptom read
as a scan difference rather than as dropped reads, which is most of why
it took so long to localise.

Behaviour is unchanged: a disc may legitimately carry placeholder TT_SRPT
entries, so one failure is still not fatal. It now warns per skip with the
title set and the error, and once at the end with kept-versus-declared.

Also adds an opt-in diagnostic that reads every VTS IFO from an image, to
separate a read failure from a parse failure. It reports all 13 sets
reading identically from both a CSS image and its decrypted copy, which is
what proves the 38-to-10 loss is downstream of these reads.
This commit is contained in:
Matthew Jackson
2026-08-05 19:41:43 -07:00
parent 1f70398774
commit 5c64662213
2 changed files with 52 additions and 2 deletions
+26
View File
@@ -824,3 +824,29 @@ fn dump_titles_for_an_image() {
}
}
}
/// Diagnostic (opt-in): can each VTS IFO be READ from an image?
///
/// `parse_vmg` skips a title set whose `parse_vts` fails, and `parse_vts`
/// begins by reading `/VIDEO_TS/VTS_nn_0.IFO`. This isolates the read.
#[test]
#[ignore = "diagnostic: needs FMKV_IMAGE"]
fn dump_vts_ifo_reads_for_an_image() {
let Ok(path) = std::env::var("FMKV_IMAGE") else {
return;
};
let mut img =
crate::io::file_sector_source::FileSectorSource::open(std::path::Path::new(&path))
.expect("open");
let fs = udf::read_filesystem(&mut img).expect("udf");
for n in 1..=20u32 {
let p = format!("/VIDEO_TS/VTS_{n:02}_0.IFO");
match fs.read_file(&mut img, &p) {
Ok(b) => {
let magic = String::from_utf8_lossy(&b[..12.min(b.len())]).to_string();
println!("VTS {n:02}: read ok, {} bytes, magic={magic:?}", b.len());
}
Err(e) => println!("VTS {n:02}: READ FAILED: {e}"),
}
}
}