From 24e2bc33cf2bc7fb066512efac10a0d2e28a5018 Mon Sep 17 00:00:00 2001
From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com>
Date: Fri, 10 Jul 2026 10:28:18 -0700
Subject: [PATCH] hddvd: authoritative title composition from the
Advanced-Content playlist
Parse ADV_OBJ/VPLST000.XPL (the real HD-DVD player playlist) with roxmltree
into one DiscTitle per
: its PrimaryAudioVideoClip clips in order (EVO
via the .MAP sidecar), the titleDuration, the displayName, and the ChapterList.
A layer-break split (FEATURE_1+FEATURE_2, feature/feature_Divide) composes into
ONE title with the two parts as clips, each carrying its title-time in/out
points (45kHz ticks) for seamless-join splicing. Falls back to the clip-name
heuristic when no playlist is present.
Fixes unknown runtimes (real durations), poor names (FEATURE -> 'Main Movie'),
and gives authoritative composition + chapters. Validated on ANCHORMAN (97m),
SHAUN (99m), HARRY_POTTER (152m).
---
Cargo.toml | 6 +
src/disc/hddvd.rs | 462 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 450 insertions(+), 18 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 85bdda6..ac31225 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,12 @@ rand = "0.8"
cmac = "0.7"
zip = { version = "2", default-features = false, features = ["deflate"] }
base64 = "0.22.1"
+# Read-only XML DOM parser (pure Rust, forbid(unsafe_code), entity-expansion
+# bounded). Parses the HD-DVD Advanced-Content playlist `ADV_OBJ/VPLST000.XPL`
+# — untrusted disc bytes — into authoritative titles/clips/chapters. A real
+# parser, not a hand-rolled scanner: the XPL is genuine XML (comments, varied
+# attribute order, self-closing tags).
+roxmltree = "0.20"
# Trace-level instrumentation for Disc::copy + SgIoTransport::execute. Permitted
# under CLAUDE.md ("Acceptable strings: debug/trace logging"). Consumers (autorip)
# wire a tracing subscriber and pipe events into the JSONL debug log.
diff --git a/src/disc/hddvd.rs b/src/disc/hddvd.rs
index 250a583..9252dc3 100644
--- a/src/disc/hddvd.rs
+++ b/src/disc/hddvd.rs
@@ -9,26 +9,28 @@
//! rule: a genuinely different format is a new enumerator, not an extension
//! bolted into the BD path.
//!
-//! Title composition: the `HVA*.VTI` navigation file (the DVD-IFO analogue) names
-//! every clip in authored order. Standard Content splits the main feature across
-//! clips at the layer break (`FEATURE_1`/`FEATURE_2`, or `feature`/`feature_Divide`);
-//! the scanner parses the VTI clip table and concatenates those parts into ONE
-//! [`DiscTitle`] (so the largest-title pick is the whole movie, not just part 1),
-//! emitting every other clip as its own title. Container is
-//! [`ContentFormat::MpegPs`], so the existing PS mux path handles it. Per-clip
-//! streams ARE enumerated: the clip head is demuxed through the PS demuxer and one
-//! [`Stream`] is built per distinct elementary stream (video + DD+ audio
-//! sub-streams), with the codec sniffed from the ES bytes.
+//! Title composition — authoritative, from the Advanced-Content playlist. HD-DVD
+//! ships a real player playlist at `ADV_OBJ/VPLST000.XPL` (DVD-Forum
+//! `HDDVDVideo/Playlist` XML). The scanner parses it (with a real XML parser,
+//! `roxmltree`) into one [`DiscTitle`] per ``: its ``
+//! clips in playback order (each an EVO, referenced via its `.MAP` sidecar), the
+//! `titleDuration`, the `displayName`, and the ``. A layer-break
+//! split (`FEATURE_1` + `FEATURE_2`, or `feature`/`feature_Divide`) is composed
+//! into ONE title with the two parts as clips, carrying each clip's title-time
+//! in/out points (45 kHz ticks) so a seamless join can be spliced onto one
+//! timeline. Container is [`ContentFormat::MpegPs`], so the existing PS mux path
+//! handles it. Per-clip streams are enumerated by demuxing the clip head and
+//! building one [`Stream`] per distinct elementary stream (video + DD+ audio),
+//! codec sniffed from the ES bytes.
//!
-//! What is NOT parsed yet — and is honestly stubbed, not faked:
-//! * full VTI program-chain parsing (chapters, non-feature title grouping) —
-//! the feature join uses the clip table + the `feature*` naming convention,
-//! not the authoritative PGC,
-//! * `.map` timemap → real durations,
-//! * subtitles (8-bit RLC on `0xBD` sub `0x20..=0x3F`).
+//! When no playlist is present (or it fails to parse), the scanner falls back to
+//! the older clip-name heuristic: parse the `HVA*.VTI` clip table, join the
+//! `feature*`-named clips into one title, and emit every other clip on its own.
//!
-//! Extents and size ARE real (the ripper needs those to image a clip); durations
-//! and chapters are left empty rather than guessed.
+//! Not parsed yet: subtitles (8-bit RLC on `0xBD` sub `0x20..=0x3F`) and per-track
+//! audio languages (the XPL carries `