Report the resolved extent alongside each IFO read
The diagnostic hashed IFO contents but not where they came from, so a content difference could not be told apart from a path resolving to a different place. It now prints file_start_lba and file_extents next to the hash. That measurement is what inverted this investigation: both a CSS disc and its decrypted copy resolve VIDEO_TS.IFO to the same extent, and only one sector of the fourteen differs — by exactly a descrambled payload with the scrambling-control bits cleared. So the decrypted copy holds the correct bytes, and an iso:// scan of a CSS disc is parsing a still-scrambled IFO sector, because scan_iso opens a plain FileSectorSource.
This commit is contained in:
+36
-1
@@ -839,6 +839,27 @@ fn dump_vts_ifo_reads_for_an_image() {
|
|||||||
crate::io::file_sector_source::FileSectorSource::open(std::path::Path::new(&path))
|
crate::io::file_sector_source::FileSectorSource::open(std::path::Path::new(&path))
|
||||||
.expect("open");
|
.expect("open");
|
||||||
let fs = udf::read_filesystem(&mut img).expect("udf");
|
let fs = udf::read_filesystem(&mut img).expect("udf");
|
||||||
|
// VIDEO_TS.IFO FIRST: it carries TT_SRPT, which decides the whole
|
||||||
|
// title-set map. Checking only the per-VTS IFOs leaves the input that
|
||||||
|
// actually drives enumeration unverified.
|
||||||
|
match fs.read_file(&mut img, "/VIDEO_TS/VIDEO_TS.IFO") {
|
||||||
|
Ok(b) => {
|
||||||
|
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
|
||||||
|
for byte in &b {
|
||||||
|
h ^= *byte as u64;
|
||||||
|
h = h.wrapping_mul(0x0000_0100_0000_01b3);
|
||||||
|
}
|
||||||
|
let lba = fs.file_start_lba(&mut img, "/VIDEO_TS/VIDEO_TS.IFO");
|
||||||
|
let ext = fs.file_extents(&mut img, "/VIDEO_TS/VIDEO_TS.IFO");
|
||||||
|
println!(
|
||||||
|
"VMG : {} bytes fnv={h:016x} start_lba={:?} extents={:?}",
|
||||||
|
b.len(),
|
||||||
|
lba.as_ref().map_err(|e| e.to_string()),
|
||||||
|
ext.as_ref().map(|v| v.to_vec()).map_err(|e| e.to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => println!("VMG : READ FAILED: {e}"),
|
||||||
|
}
|
||||||
for n in 1..=20u32 {
|
for n in 1..=20u32 {
|
||||||
let p = format!("/VIDEO_TS/VTS_{n:02}_0.IFO");
|
let p = format!("/VIDEO_TS/VTS_{n:02}_0.IFO");
|
||||||
match fs.read_file(&mut img, &p) {
|
match fs.read_file(&mut img, &p) {
|
||||||
@@ -852,7 +873,21 @@ fn dump_vts_ifo_reads_for_an_image() {
|
|||||||
h ^= *byte as u64;
|
h ^= *byte as u64;
|
||||||
h = h.wrapping_mul(0x0000_0100_0000_01b3);
|
h = h.wrapping_mul(0x0000_0100_0000_01b3);
|
||||||
}
|
}
|
||||||
println!("VTS {n:02}: {} bytes fnv={h:016x}", b.len());
|
// The two reader-dependent calls parse_vts makes AFTER this
|
||||||
|
// read are file_start_lba and the PGCIT parse. Report both.
|
||||||
|
let lba = fs.file_start_lba(&mut img, &p);
|
||||||
|
let pgcit = if b.len() >= 0xD0 {
|
||||||
|
u32::from_be_bytes([b[0xCC], b[0xCD], b[0xCE], b[0xCF]])
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let pgcit_off = pgcit as usize * 2048;
|
||||||
|
println!(
|
||||||
|
"VTS {n:02}: {} bytes fnv={h:016x} lba={:?} pgcit_sector={pgcit} pgcit_off={pgcit_off} in_file={}",
|
||||||
|
b.len(),
|
||||||
|
lba.as_ref().map_err(|e| e.to_string()),
|
||||||
|
pgcit_off < b.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => println!("VTS {n:02}: READ FAILED: {e}"),
|
Err(e) => println!("VTS {n:02}: READ FAILED: {e}"),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user