diff --git a/src/aacs/keys.rs b/src/aacs/keys.rs index 1c65e48..d99cb10 100644 --- a/src/aacs/keys.rs +++ b/src/aacs/keys.rs @@ -2113,7 +2113,7 @@ mod tests { #[test] fn resolve_keys_path2_5_mk_pool_brute_resolves_unkeyed_disc() { - // The Dunkirk case: this disc's own hash/VID are NOT in keydb, but its + // The keyless-disc case: this disc's own hash/VID are NOT in keydb, but its // Media Key IS — filed under a sibling disc that shares its MKB. Path // 2.5 must km_verifies that MK against the MKB and resolve. use super::super::decrypt::aes_ecb_encrypt as enc; diff --git a/src/disc/mod.rs b/src/disc/mod.rs index aaab6f2..901630e 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1299,7 +1299,7 @@ impl Disc { /// zero-padded, so reading either ships up to ~124 MiB of nothing — /// trim to the record stream so callers send/store a few MB, not /// 128 MiB. - fn read_aacs_inputs_from_reader( + pub(crate) fn read_aacs_inputs_from_reader( reader: &mut dyn SectorSource, udf_fs: &udf::UdfFs, ) -> Result<(Vec, Vec)> { diff --git a/src/keysource.rs b/src/keysource.rs index 970b288..154236b 100644 --- a/src/keysource.rs +++ b/src/keysource.rs @@ -36,7 +36,7 @@ pub struct DiscInputs { /// [`crate::Disc::inputs`] leaves it empty for the caller to fill. pub samples: Vec>, /// The disc's human title — the UDF/ISO volume identifier (e.g. - /// `WICKED_FOR_GOOD`), falling back to the BDMV `` when present. + /// `TITLE_2024`), falling back to the BDMV `` when present. /// `None` when not captured. Identity only, no secret; a key service may /// record it (keyed by `disc_hash`) to build a hash→title catalog. Not used /// in any AACS derivation. diff --git a/src/labels/mod.rs b/src/labels/mod.rs index 6b60b42..7b768e2 100644 --- a/src/labels/mod.rs +++ b/src/labels/mod.rs @@ -1358,7 +1358,7 @@ mod apply_tests { #[test] fn apply_rejects_mismatched_codec_hint_and_uses_stream_codec() { - // Paddington case: a TrueHD+Atmos main track the parser mislabeled + // TrueHD+Atmos relabel case: a TrueHD+Atmos main track the parser mislabeled // "AC-3 2.0" (a compat-core hint bound to the wrong stream). The hint // contradicts the stream's real codec → discard it, use the stream's own. let mut titles = vec![title_with(vec![audio( @@ -1378,7 +1378,7 @@ mod apply_tests { #[test] fn apply_unshuffles_cross_labeled_streams() { - // Wicked case: hints fully cross-bound — a TrueHD stream wears "AC-3 5.1" + // Cross-bound hints case: hints fully cross-bound — a TrueHD stream wears "AC-3 5.1" // and a DD+ stream wears "TrueHD 5.1". Each is corrected from its own // stream codec, eliminating the shuffle. let mut titles = vec![title_with(vec![ @@ -1463,7 +1463,7 @@ mod apply_tests { #[test] fn apply_normalizes_plain_consistent_hint_to_marketing() { - // Wicked's French track: a DD+ stream whose hint "AC-3+ 5.1" is correct + // A French DD+ track: a DD+ stream whose hint "AC-3+ 5.1" is correct // but short-form. A sibling DD+ track that fell back uses the marketing // form — keeping the short form here would read inconsistently, so a // plain (non-richer) consistent hint is normalized to the stream's own. diff --git a/src/mux/codec/dts.rs b/src/mux/codec/dts.rs index a6ed62c..df44a08 100644 --- a/src/mux/codec/dts.rs +++ b/src/mux/codec/dts.rs @@ -19,7 +19,7 @@ const DTS_HD_EXT_SYNC: [u8; 4] = [0x64, 0x58, 0x20, 0x25]; /// a core frame plus all of its trailing DTS-HD extension substreams are /// emitted together as one access unit, delimited by the next valid core sync. /// This preserves the lossless extension data instead of downgrading to lossy -/// core (the Dunkirk / Fight Club lossy-core bug). +/// core (the lossy-core downgrade bug). pub struct DtsParser { buf: Vec, /// PTS of the access unit currently being assembled in `buf` (the unit @@ -140,8 +140,8 @@ impl CodecParser for DtsParser { // sync. Emitting on the core boundary keeps the core + every following // extension substream together (the lossless data), instead of the // old per-PES emit that dropped the extension PES packets and - // downgraded the track to lossy DTS core (the Dunkirk / Fight Club - // bug). The PTS is the core frame's PTS, captured when the unit began. + // downgraded the track to lossy DTS core (the lossy-core + // downgrade bug). The PTS is the core frame's PTS, captured when the unit began. // Capture the access unit's PTS base on a fresh buffer, or whenever a // prior forced (safety-valve) flush left it invalidated — in the // forced case the bytes still in `buf` are not a real core frame, so @@ -534,12 +534,12 @@ mod tests { #[test] fn keeps_dts_hd_extension_in_separate_pes_packets() { - // The real Blu-ray layout (ground-truthed on Dunkirk): the DTS core + // The real Blu-ray layout (ground-truthed on real UHD discs): the DTS core // arrives in one PES, then its DTS-HD MA extension substreams arrive // in SEPARATE following PES packets on the same PID. The parser must // stitch core + all trailing extensions into one access unit — not // emit a core-only (lossy) frame and drop the extension PES packets - // (the Dunkirk / Fight Club lossy-core bug). + // (the lossy-core downgrade bug). let mut parser = DtsParser::new(); // Frame 1: core (512) + two extension substreams (256 + 200). diff --git a/src/mux/codec/hevc.rs b/src/mux/codec/hevc.rs index 6f3d25e..35139e2 100644 --- a/src/mux/codec/hevc.rs +++ b/src/mux/codec/hevc.rs @@ -28,7 +28,7 @@ pub struct HevcParser { // This is the ONLY copy the player gets out-of-band, and a player re-applies // it at every keyframe (ffmpeg's hvcC→Annex-B insertion). A stream may // redefine a parameter set mid-title under the SAME id with a different body - // (Fight Club redefines PPS id 0 partway through). Any occurrence whose body + // (some discs redefine PPS id 0 partway through). Any occurrence whose body // DIFFERS from this codecPrivate copy must therefore be emitted IN-BAND at // each point it appears (i.e. at every keyframe of the redefined segment) so // it overrides the re-applied codecPrivate set; otherwise those frames decode @@ -65,7 +65,7 @@ impl HevcParser { /// same id) → emitted IN-BAND (length-prefixed) at EVERY occurrence, so it /// overrides the hvcC copy the player re-applies at each keyframe. Emitting it /// only once is not enough — the next keyframe's hvcC re-insertion would revert -/// it. This matches what a conforming muxer produces and fixes the Fight Club +/// it. This matches what a conforming muxer produces and fixes mid-title /// PPS-id-0 redefinition. fn handle_param_set(first: &mut Option>, nal: &[u8], frame_data: &mut Vec) { match first { @@ -829,10 +829,10 @@ mod tests { ); } - // --- parameter-set redefinition (Fight Club bug) --- + // --- parameter-set redefinition (mid-title redefinition bug) --- /// A parameter set REDEFINED mid-stream (same id, different body) must be - /// emitted INLINE so the decoder re-activates it. Fight Club redefines PPS + /// emitted INLINE so the decoder re-activates it. Some discs redefine PPS /// id 0 partway through the title; the old parser kept only the first PPS, /// so the second segment decoded against the wrong PPS (CABAC desync). #[test] diff --git a/src/udf.rs b/src/udf.rs index a4858a4..256723b 100644 --- a/src/udf.rs +++ b/src/udf.rs @@ -1461,7 +1461,7 @@ mod tests { // Regression for BOTH 0.31.0 bugs through `read_file`: this is the // exact path `Disc::read_aacs_inputs_from_reader` uses to read // `/AACS/MKB_RO.inf` + `Unit_Key_RO.inf`. A Long-AD, multi-extent - // file (Dunkirk-class UHD layout) must return ALL its bytes. With the + // file (large UHD/Blu-ray layout) must return ALL its bytes. With the // pre-fix Short-AD-only parser this read stopped after the first // extent, which (a) truncated the mux and (b) made autorip's // `key_files()` see a short/garbage AACS file → `MissingInputs` → @@ -1491,6 +1491,56 @@ mod tests { assert!(data[4096..].iter().all(|&b| b == 0xCC)); } + #[test] + fn read_aacs_inputs_reads_long_ad_files_in_full() { + // PRECOMMIT proof for the autorip online-keyserver path (no disc / no + // deploy). autorip's key request is gated on Disc::read_aacs_inputs + // (keysource.rs key_files()): it reads /AACS/Unit_Key_RO.inf and + // /AACS/MKB_RO.inf. On a Long-AD disc (UHD / large Blu-ray) the + // pre-0.31.1 Short-AD-only reader truncated those files at their first + // extent, breaking key derivation. This + // fixture lays a Long-AD, multi-extent Unit_Key_RO.inf under /AACS and + // asserts read_aacs_inputs returns its FULL content — i.e. the keyserver + // inputs are complete, so the request is built correctly. + let aacs = DirEntry { + name: "AACS".to_string(), + is_dir: true, + meta_lba: 0, + size: 0, + entries: vec![ + file_entry("Unit_Key_RO.inf", 5, 4096), // Long-AD, 2 extents + file_entry("MKB_RO.inf", 7, 2048), + ], + }; + let root = DirEntry { + name: String::new(), + is_dir: true, + meta_lba: 0, + size: 0, + entries: vec![aacs], + }; + let mut reader = MapReader::new(); + // Unit_Key_RO.inf: Long-AD ICB with two recorded extents. + reader.put(5, build_efe_long(4096, &[(0, 2048, 10), (0, 2048, 30)])); + reader.put(10, [0xAA; 2048]); + reader.put(30, [0xBB; 2048]); + // MKB_RO.inf: single Long-AD extent (content is opaque to this test). + reader.put(7, build_efe_long(2048, &[(0, 2048, 50)])); + reader.put(50, [0xCC; 2048]); + + let fs = fs_with(0, 0, root); + let (inf, _mkb) = crate::disc::Disc::read_aacs_inputs_from_reader(&mut reader, &fs) + .expect("read_aacs_inputs must succeed for a Long-AD disc"); + assert_eq!( + inf.len(), + 4096, + "Unit_Key_RO.inf (Long-AD, multi-extent) must read in full — the \ + pre-0.31.1 Short-AD parser truncated it to the first 2048-byte extent" + ); + assert!(inf[..2048].iter().all(|&b| b == 0xAA)); + assert!(inf[2048..].iter().all(|&b| b == 0xBB)); + } + #[test] fn merge_ranges_saturates_near_u32_max() { // Adjacent ranges near u32::MAX must not panic (debug) or wrap.