movie_manifest::parse now reads BASE_INFO's positional field keys (the game's own cutscene ids, stage*100 + slot) and follows each to its record, instead of scraping the string pool. The pool stores each distinct string once, so a REPEAT reference produced no token and read as "no binding". That single cause explains every wrong cell: 13 later references to VOICE_D_450..454, two to SUBTITLE_hokyu_LS_s11A.tbl, and MS01A's share of pwterop_s01a.prt. All 18 hokyu movies are bound, not five. Counts, verified independently by me against the disc before recording: 104 cutscene SLOTS binding 101 distinct MOVIES; 99 slots / 96 movies with a voice track, 99 / 96 with a subtitle, 22 / 22 with a telop. The docs' old 94 / 83 / 21 are exactly the counts of DISTINCT POOL STRINGS -- not wrong measurements, measurements of the wrong thing. Three denominators were being conflated; the new test pins all three. Two assertions in movie_manifest_disc.rs were false and are corrected: hokyu_DS_s13A binds VOICE_D_452 and resolves to eng\etc\VOICE_D_452.slb. The in-game verdict that rejected that value tested an INFERENCE from a shared demo id, on a decoder that discards 85-87% of banks in this class -- see voice-bank-leading-region.md, committed earlier today. The ~104 script ids are no longer open: they are literal positional keys, each naming its record, and all 104 resolve. The old "counts differ by three, positional pairing does not work" has a concrete cause -- three resupply movies are bound by TWO slots each. Also corrected: the naming convention has 3 subtitle exceptions (s24A/s27A borrow s11A's track) and 18 voice exceptions, not one and five. The legacy scraper is kept as a fallback for blobs with no record table, so the synthetic unit fixtures still exercise it. Artifacts: examples/movie_map_csv.rs regenerates the CSV, now slot-keyed (104 rows; the movie-keyed version silently dropped one slot of each duplicate). Disc tests green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
99 lines
4.6 KiB
Markdown
99 lines
4.6 KiB
Markdown
# The movie ↔ subtitle ↔ voice link ✅ (static)
|
|
|
|
[`INDEX`](INDEX.md) carried the IXUD subtitle format as 🟡 with **"movie↔track
|
|
link unknown (dynamic item)"** — i.e. a question expected to need the running
|
|
game. It does not: the link is a config record on the disc, and it verifies
|
|
against the disc's own contents.
|
|
|
|
## Where it is
|
|
|
|
`dat/tables.pak`, the IDXD record with schema `0x067025b9`. Its keys come from
|
|
the movie game-part — the executable carries them next to the symbol
|
|
`silph::GamePart_Movie::Impl::OnPrepare`:
|
|
|
|
```
|
|
PATH = dat\movie\ SUBTITLE_FONT = fra.pak+GOTHICB.TTF
|
|
SUBTITLE_Y = 600 SUBTITLE_HEIGHT = 33
|
|
MOVIE TELOP SUBTITLE VOICETRACK
|
|
```
|
|
|
|
Each movie is one group of values:
|
|
|
|
```
|
|
S02A.wmv MOVIE
|
|
fra.pak+pwterop_s02a.prt TELOP (optional overlay)
|
|
fra.pak+SUBTITLE_S02A.tbl SUBTITLE
|
|
VOICE_S02A VOICETRACK
|
|
```
|
|
|
|
`<lang>.pak+<member>` resolves inside `dat/movie/<lang>.pak`, and the six
|
|
language paks (`eng`, `fra`, `deu`, `esp`, `ita`, `jpn`) each carry the same
|
|
member names — so the prefix in the table is just the build's language and the
|
|
member name is the portable part. The recovered map is
|
|
[`captures/movie-subtitle-voice-map.csv`](captures/movie-subtitle-voice-map.csv),
|
|
now regenerated from the **record table** (`examples/movie_map_csv.rs`) and keyed
|
|
by cutscene **slot**:
|
|
|
|
**104 slots binding 101 distinct movies.** 99 slots / 96 movies have a subtitle,
|
|
99 slots / 96 movies have a voice track, 22 slots / 22 movies have a telop —
|
|
drawing on 94 distinct subtitle tables, 83 distinct voice banks and 21 distinct
|
|
telop overlays.
|
|
|
|
### ❌ The old counts were measuring the wrong thing
|
|
|
|
This file used to say *"101 movies, 94 with a subtitle, 83 with a voice track, 21
|
|
with a telop"*. Those three numbers are exactly the counts of **distinct pool
|
|
strings** — which is all a string-pool scraper can see. The pool stores each
|
|
value once, so a *repeat* reference contributes no token and read as "no
|
|
binding": 13 later references to `VOICE_D_450..454`, two to
|
|
`SUBTITLE_hokyu_LS_s11A.tbl`, and `MS01A`'s share of `pwterop_s01a.prt`.
|
|
|
|
Slots, movies and distinct strings are three different denominators and this file
|
|
conflated them. Pinned by `manifest_slot_and_movie_counts`.
|
|
|
|
## Verified, and one real defect
|
|
|
|
Every subtitle reference was looked up in the language paks. **93 of the 94
|
|
distinct members resolve. `SUBTITLE_S12B.tbl` resolves in none of the six
|
|
languages** — a dangling reference on the retail disc, not a decode failure on
|
|
our side. Worth knowing before the reimplementation treats a missing subtitle
|
|
table as a bug of its own.
|
|
|
|
The naming is a convention, with more exceptions than were recorded:
|
|
|
|
- subtitles are `SUBTITLE_<movie base>.tbl`, with **3** departures — the staff
|
|
roll (`SYLPH_HD720p_8M-CBR_2ch.wmv` → `SYLPH_HD720p_8M-CBR_2ch.tbl`), and
|
|
`hokyu_LS_s24A` / `hokyu_LS_s27A`, which **borrow `SUBTITLE_hokyu_LS_s11A.tbl`**;
|
|
- voice tracks are `VOICE_<movie base>` except **all 18 hokyu movies** (21 supply
|
|
slots), which share `VOICE_D_450…454`. The old text said five; the other 13
|
|
were invisible to the scraper for the dedup reason above.
|
|
|
|
So a reimplementation should read this table, **not** apply the convention with a
|
|
short exception list.
|
|
|
|
## ✅ The script ids ARE recoverable — the old ❔ is closed
|
|
|
|
The record also lists 104 **script ids** — the names stage scripts use to trigger
|
|
a movie. This file used to say pairing them with the movie groups "does not
|
|
work: the counts differ by three". The counts differ by three for a concrete
|
|
reason: **three resupply movies are bound by two slots each**
|
|
(`hokyu_LS_s02A` by S02 and S05, `hokyu_DS_s07A` and `hokyu_DS_s07H` by S07 and
|
|
S12). Positional pairing was never the right model — the ids are stored as
|
|
**literal positional field keys** in `BASE_INFO`, each naming its record
|
|
directly, so every one of the 104 resolves with nothing to infer. The id is
|
|
`stage*100 + slot`, with 91/92 for `_SUPPLY_ACROPOLIS`/`_SUPPLY_TANKER`.
|
|
|
|
|
|
The cause is the same IDXD property that bit the Arsenal table: **the string pool
|
|
stores each distinct string once**, so an id whose movie was already named
|
|
earlier contributes no new value token and its group silently disappears. The id
|
|
list is therefore ❔ until the binary node/index region is decoded — and the CSV
|
|
is deliberately keyed by **movie file**, which is the part the data actually
|
|
supports.
|
|
|
|
## Note for the intro-movie investigation
|
|
|
|
`ADV.wmv` — the one the emulator work is about — has `VOICETRACK = VOICE_ADV`
|
|
and **no subtitle and no telop**. So a correct playback of it needs the video and
|
|
that one voice track, nothing else from this table.
|