From 5c646622131d9007de844f3e2432352cdf34187e Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Wed, 5 Aug 2026 19:41:43 -0700 Subject: [PATCH] Say when a title set is dropped from a DVD scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/dirimage/tests.rs | 26 ++++++++++++++++++++++++++ src/ifo.rs | 28 ++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/dirimage/tests.rs b/src/dirimage/tests.rs index 2a7bff9..31710de 100644 --- a/src/dirimage/tests.rs +++ b/src/dirimage/tests.rs @@ -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}"), + } + } +} diff --git a/src/ifo.rs b/src/ifo.rs index b16a38b..a31ffec 100644 --- a/src/ifo.rs +++ b/src/ifo.rs @@ -421,16 +421,40 @@ pub fn parse_vmg(reader: &mut dyn SectorSource, udf: &UdfFs) -> Result // Parse each VTS IFO let mut title_sets = Vec::new(); + let mut skipped = 0usize; for (&vts_number, titles_info) in &title_set_map { match parse_vts(reader, udf, vts_number, titles_info) { Ok(ts) => title_sets.push(ts), - Err(_) => { - // Skip unreadable title sets — some DVDs have placeholder entries. + Err(e) => { + // Some discs carry placeholder TT_SRPT entries for title sets + // that are not really there, so one failure is not fatal. But + // the failure must not be INVISIBLE: every skipped set is a + // title the user will never see, and swallowing the reason made + // a disc that enumerated 38 titles from one image and 10 from + // another look like a scan difference rather than 28 dropped + // reads. + skipped += 1; + tracing::warn!( + target: "freemkv::scan", + vts = vts_number, + titles = titles_info.len(), + error = %e, + "title set could not be parsed; its titles are omitted" + ); continue; } } } + if skipped > 0 { + tracing::warn!( + target: "freemkv::scan", + skipped, + kept = title_sets.len(), + declared = title_set_map.len(), + "some title sets were omitted from the scan" + ); + } Ok(DvdInfo { title_sets }) }