The movie cutscene subtitle + voice pipeline, driven by the ADVERTISE_MOVIE manifest (the authoritative movie -> subtitle -> voice index). Also flushes several sessions of local WIP (async viewer loading, grouped-pool XBG7/hero-ship decode, drawlog tooling). See docs/HANDOFF-movie-voice-subtitles-2026-07-19.md. Subtitles (movie_subtitle.rs): - Full movie->track->text chain; join multi-line captions sharing one timing (fixes S13A dropped "Look at it father" line); overlap-safe active_cues(); Latin-1 accents preserved. Voice (slb.rs): XACT .slb -> XMA1 RIFF; take the FIRST sub-wave bounded by its declared data size (fixes S10-S16 alternate-take garble); list_voice_clips. Manifest (movie_manifest.rs): parse ADVERTISE_MOVIE (0x5B983A08) for the real movie->voice binding (not always VOICE_<movie>; e.g. hokyu -> VOICE_D_* in etc\). Resolve the token's sound.pak path via sounds.tbl. DIRECT bindings only — the demo-id shared-clip fallback for unbound hokyu movies was verified WRONG in-game and reverted (unbound hokyu stay unvoiced; correct join key is an OPEN problem). Viewer: manifest-driven voice (movie player toggle + solo button), standalone "Voice Lines" browser, stacked caption overlay. Tests: 46 formats-lib + 11 viewer-lib + movie_manifest/movie_subtitle/slb disc tests; full workspace green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.7 KiB
Handoff — movie subtitles, voice, and the movie manifest (2026-07-19)
Branch feature/ipfb-idxd-parser. This commit flushes several sessions of local
WIP; the new, finished work is the movie subtitle + voice + audio pipeline
and the movie manifest. One item is deliberately left open (see §4).
1. What's DONE and verified (static RE + tests)
Movie subtitles (crates/sylpheed-formats/src/movie_subtitle.rs)
- Full movie→track→text chain (see
docs/re/structures/movie-subtitles.md). - Multi-line caption fix: a caption stored as several consecutive text tokens
sharing one timing (S13A:
"Look at it father"+"& beautiful isn't it"@01:14.80) —parse_tracknow joins them with\n. Previously all but the last line were dropped. Disc test asserts both lines survive. - Overlap rendering:
MovieSubtitles::active_cues(t)returns every cue active att; the viewer stacks them (was: only the first cue shown). - Umlauts / Latin-1 accents preserved (
utf16le_tokens); Japanese label romanized (CJK font still a known gap — see §5).
Voice decode (crates/sylpheed-formats/src/slb.rs)
sound.pakentries are XACT.slbbanks wrapping XMA1 (fmt tag0x0165, 48 kHz, mono content).to_xma_riffrebuilds a decodable RIFF; FFmpegxma1decodes it. Downmix mono via-af pan=mono|c0=c0.- Multi-subwave fix: take the FIRST sub-wave bounded by its declared
datasize — NOTdata..EOF(which appended later takes = the S10–S16 garble). Verified: S13A→83.78s == video. list_voice_clipsenumerates the voice library fromsounds.tbl.
Movie manifest (crates/sylpheed-formats/src/movie_manifest.rs) — the index
tables.pakentryADVERTISE_MOVIE(IDXD, hash0x5B983A08) is the authoritative movie → subtitle → voice map. Located by shape (is_manifest), parsed by grouping the string pool on.wmv.- Authoritative voice binding replaces the old
VOICE_<movie>guess: 101 movies, 83 with aVOICE_token, 18 without. The token is NOT alwaysVOICE_<movie>— 5hokyu_*movies bind toVOICE_D_450..454in\etc\, which a guess would miss. Token's subdir varies (Movie/etc/Voice) → resolve viasounds.tbl. Disc test: all 83 resolved entries exist insound.pak.
Viewer wiring (iso_loader.rs, ui.rs)
resolve_movie_voice_clipreads the manifest +sounds.tbl(DIRECT bindings only) →handle_voice_request(movie player 🗣 toggle) and the 🎧 solo button (RequestAudio.movie) use it. Unbound movies correctly post no audio.- Standalone View → 🎙 Voice Lines browser (filter + ▶ play any
sound.pakclip) viaVoiceLibrary+AudioPreview.
Tests: 46 formats-lib + 11 viewer-lib + disc (movie_manifest_disc,
movie_subtitle_disc, slb_disc) all green. Full cargo test --workspace green.
2. Also bundled in this commit (prior local WIP, not this session's focus)
- Viewer async stage/model loading (off-thread
prepare_xpr, cancellable) —iso_loader.rs/ui.rs/camera.rs. See memoryreborn-viewer-async-loading. - Grouped-pool XBG7 / hero-ship decode refinements in
mesh.rs,cli/main.rs,mesh_disc.rs. See memoryreborn-ship-drawlog/reborn-saber-texture-investigation. tools/analyze_drawlog_wvp.py,tools/extract_movie_subtitles.py.
These build and test green but were not re-verified for behaviour this session.
3. USER TO VERIFY IN GUI (couldn't be tested from CLI)
- S13A cutscene shows BOTH lines of the "Look at it father" caption.
- Story-movie voice (S13A etc.) lines up with the video after the subwave fix.
- The 5 directly-bound
hokyu_*movies play voice; other hokyu are silent (see §4). - View → 🎙 Voice Lines browser lists and plays clips.
4. OPEN PROBLEM — unbound-hokyu resupply voice (do NOT re-guess)
The resupply cutscenes clearly SHARE voice recordings (video-only varies per
mission), but the correct join key for the 13 unbound hokyu_* movies is
unknown:
- Manifest DIRECTLY binds only 5:
LS_s02A→450, LS_s09A→451, DS_s02A→452, LS_s02H→453, DS_s07H→454(grouping verified against the raw layout). - Keying unbound movies by subtitle demo id (600→450…604→454, so
hokyu_DS_s13Ademo602→VOICE_D_452) was implemented, tested against the running game by the user, and is WRONG (wrong line). It has been reverted — unbound hokyu now stay unvoiced rather than play wrong audio. - Red flag: only
VOICE_D_450..454exist; decoded durations450=2.8s 451=1.6s 452=2.2sbut453=0.14s 454=0.43s— far too short for the spoken line, so these.slbare likely multi-subwave / not cleanly sliced (same class as the deferred B/C banks below).
Next-box options: (a) get the real join key from mission-event data (mission
scripts/IDXD tables that trigger resupply), or (b) build a proper multi-subwave
.slb decoder and verify audio by ear. Needs a concrete calibration clue from the
user (e.g. "s13A should sound like " or the actual spoken words).
5. Other deferred items (unchanged)
- ~49 "layout-B/C" movie/voice banks (most RT + S07B/S11A/S12B/S13B) + some individual clips need an XMA1 packet/seek-table reassembler.
- CJK/Japanese text rendering in the viewer (needs a CJK TTF via egui FontDefinitions).
- Texture colours (channel order / sRGB) still on the dynamic-RE backlog.
6. Resume checklist (next box)
git pullonfeature/ipfb-idxd-parser; setSYLPHEED_DISC=<extract root>(had.../sylph_extract).- Build guardrail:
CARGO_BUILD_JOBS=4always (15 GB box; full-jOOM'd before). cargo test --workspace(withSYLPHEED_DISC) should be green.- Pick up §4 (unbound-hokyu voice) — the only open thread from this session.