mux: FMTS reads only our-phase units (AacsKeyMap::read_plan)
An FMTS forensic segment interleaves our device group's variant with a foreign group's at the aligned-unit level. The mux was decrypting only our phase but leaving the alternate (foreign) units in the buffer as ciphertext, trusting the demux to 'drop untouched ciphertext cleanly'. It doesn't: random 0x47 bytes at the 192-byte stride hit tracked PIDs, mis-parse, and trip the demux's concealed-gap keyframe-resync — which drops GOOD frames of ours around every segment (349 resyncs / ~6391 packets on Stand by Me, visible as playback flaws). The map already knows which unit each LBA is and, for a forensic segment, which phase is ours. AacsKeyMap::read_plan turns that into the title's read plan: every default/CPS unit, plus inside a segment ONLY our-phase units. The alternate units are never fetched, decrypted, or handed to the demux — the demux sees one gapless our-variant stream. - read_plan is general (single-CPS, multi-CPS, FMTS): a map with no Even/Odd range returns the extents unchanged, so DVD/CSS, single-CPS UHD and multi-CPS Blu-ray read byte-for-byte as before. - Wired into build_iso_pipeline (the file-backed highway that muxes resumed ISOs). Producer re-anchors unit_base per extent, so per-unit segment reads stay unit-aligned and decrypt correctly. - Extent gains PartialEq/Eq for the read_plan tests. Tests: read_plan non-forensic unchanged; forensic omits exactly the alternate units, kept units match the decrypt gate unit-for-unit. All 2314 lib tests pass; precommit green on 1.86. Pending: end-to-end ISO re-mux validation (concealed gaps 349 -> ~0).
This commit is contained in:
@@ -1038,6 +1038,18 @@ pub fn build_iso_pipeline<S: SectorSource + Send + 'static>(
|
||||
)),
|
||||
_ => None,
|
||||
};
|
||||
// The map IS the title's read plan: it says which CPS unit / forensic segment
|
||||
// each LBA belongs to. Walk ONLY the units it marks as ours — every default /
|
||||
// CPS unit, and inside an FMTS forensic segment only our-phase units. The
|
||||
// alternate-phase units are a different device group's variant; a licensed
|
||||
// player never reads them, and neither do we — they are never fetched,
|
||||
// decrypted, or handed to the demux, so the demux sees one gapless our-variant
|
||||
// stream (no ciphertext to trip a concealed-gap resync). A non-forensic map
|
||||
// returns the extents unchanged, so the common disc reads exactly as before.
|
||||
let extents = match &key_map {
|
||||
Some(map) => map.read_plan(&extents, unit_align as u32),
|
||||
None => extents,
|
||||
};
|
||||
let mut decrypting =
|
||||
crate::sector::DecryptingSectorSource::new(Box::new(reader) as Box<dyn SectorSource>, keys);
|
||||
if let Some(map) = key_map {
|
||||
|
||||
Reference in New Issue
Block a user