From 77ad147563bb7c1b34679321e56a60995f120370 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 5 Aug 2026 20:11:55 -0700 Subject: [PATCH] Diagnostics that localise the decrypted-DVD title loss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two opt-in dumps, driven by FMKV_IMAGE, that narrow where a decrypted DVD image loses titles: dump_title_sets_for_an_image reports what survives parse_vmg. On one disc the CSS image yields 13 title sets and 38 titles; its decrypted copy yields 8 and 10. Sets 8, 9, 10, 12 and 13 are dropped outright, set 11 parses but returns no titles at all, and set 7 returns 3 of 5. dump_vts_ifo_reads_for_an_image reads every VTS IFO and hashes the CONTENT. All thirteen are byte-identical across the two images, so parse_vts is handed the same bytes and the same TT_SRPT info and still fails on one of them — the divergence is in what it reads from the READER afterwards, which is file_start_lba and the PGCIT. An earlier version of the second dump compared only length and magic and so wrongly reported the images as identical; it hashes the bytes now. --- src/dirimage/tests.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/dirimage/tests.rs b/src/dirimage/tests.rs index 31710de..50f9d16 100644 --- a/src/dirimage/tests.rs +++ b/src/dirimage/tests.rs @@ -843,10 +843,39 @@ fn dump_vts_ifo_reads_for_an_image() { 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()); + // Hash the CONTENT, not just the length: two IFOs of equal + // size and magic can still differ, and comparing only length + // and magic is what made an earlier pass of this diagnostic + // wrongly report the images as identical. + let mut h: u64 = 0xcbf2_9ce4_8422_2325; + for byte in &b { + h ^= *byte as u64; + h = h.wrapping_mul(0x0000_0100_0000_01b3); + } + println!("VTS {n:02}: {} bytes fnv={h:016x}", b.len()); } Err(e) => println!("VTS {n:02}: READ FAILED: {e}"), } } } + +/// Diagnostic (opt-in): how many title sets survive `parse_vmg`, and how many +/// titles does each carry? Splits "the set was dropped" from "the set parsed +/// but yielded fewer titles". +#[test] +#[ignore = "diagnostic: needs FMKV_IMAGE"] +fn dump_title_sets_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"); + let info = crate::ifo::parse_vmg(&mut img, &fs).expect("parse_vmg"); + let total: usize = info.title_sets.iter().map(|ts| ts.titles.len()).sum(); + println!("title_sets={} total_titles={total}", info.title_sets.len()); + for ts in &info.title_sets { + println!(" vts={} titles={}", ts.vts_number, ts.titles.len()); + } +}