dat/tables.pak holds a 5798-entry SOUNDS record (cue name -> sound id) and a 5135-entry FILES record (.slb bank paths). Cue names are the join key, so a script message id now resolves all the way to the bank that voices it: MSG_VOICE_D_257 -> VOICE_D_257 -> 6945 -> jpn\etc\VOICE_D_257.slb. The prefix rule is MSG_ -> VOICE_, not strip-MSG_. My first rule was the latter; it left 88 names unresolved and I was about to write those families up as text-only announcements, until VOICE_TCAF_592.slb turned up in FILES and refuted it. Corrected rule resolves 1326 of 1338, and SOUNDS and FILES agree on exactly the same 12 absentees. Separately, MSG_DEMO_* is driven by its own IDXD tables in the language packs, which carry speaker, portrait, on-screen seconds and audio cue per page. Field count is 9*PageCount+2 for all 7 distinct PageCounts; 1252/1252 caption-key slots match <ID>_<page>_<line>; the 78 multi-page records equal the 78 counted independently from the caption side; 138 ids close exactly against the caption table both ways. Does not settle the known VOICE_D_452 wrong-recording case -- every cue id is distinct, so bank sharing is not happening at this layer.
4.2 KiB
The sound-cue table, and how dialogue binds to a recording
✅ Settled 2026-08-26. dat/tables.pak carries the game's cue index. It
closes the join from a message id to the .slb bank that voices it — the
missing link between the script/caption side and the audio side.
The table
One IDXD object in dat/tables.pak (373 680 bytes, 5 records, 10 937 fields)
holds:
| record | shape | content |
|---|---|---|
SETTINGS |
3 fields | PATH = game:\dat\sound.pak+, PARAM = Pj_Silph.xgs, and a positional BANK_SE |
BANK_SE |
1 positional | Static.slb |
FILES |
5 135 positional | bank paths — jpn\Voice\VOICE_TCAF_592.slb, BGM_001.slb, … |
STAGES |
0 fields | present but empty |
SOUNDS |
5 798 named | cue name → numeric sound id |
SOUNDS is the only record on the disc found so far that is entirely named
fields, and the names are the join key.
Cue families and their id ranges — the id space is partitioned by family, with no overlap:
VOICE n=5044 1500 .. 7331
SE n= 322 1 .. 901
DEMO n= 295 8000 .. 8408
BR n= 101 8500 .. 8600
BGM n= 32 1001 .. 1109
JNGL n= 3 1201 .. 1203
5 797 of the 5 798 values are numeric and all 5 797 are distinct — the ids are unique, so the id is not how banks get shared. One cue's value is non-numeric.
The id is not an index into FILES: ids run to 8 600 and FILES has only
5 135 entries. FILES is reached by name, not by id.
The join: a message id to a recording
MSG_VOICE_D_257 (built-in 64's operand, see isl-message-dialogue-link.md)
-> cue VOICE_D_257 `MSG_` becomes `VOICE_`
-> id 6945 SOUNDS
-> bank jpn\etc\VOICE_D_257.slb FILES, matched on stem
The prefix rule has one wrinkle, and getting it wrong is easy:
MSG_is replaced byVOICE_— it is not simply stripped.- A name that already carries
VOICE_keeps the one it has rather than doubling it:MSG_VOICE_D_257→VOICE_D_257, butMSG_TCAF_592→VOICE_TCAF_592(notTCAF_592).
I got this wrong first. Stripping MSG_ resolved 1 250 of 1 338 script
message names and left 88 unresolved, and the 88 were exactly the four-letter
ship families (ADAN 32, ADPL 27, TCAF 12, ACRO 3, RHIN 2) — which I
was ready to write up as "text-only announcements with no recording". Seeing
jpn\Voice\VOICE_TCAF_592.slb in FILES refuted that: the recordings exist,
my rule was wrong. Under the corrected rule the count is 1 326 of 1 338.
SOUNDS and FILES agree exactly — the same 12 names are absent from both,
and every one of the 5 135 bank files has a cue (zero orphans). 663 cues
have no bank file; those are SE/BGM-family cues living in other banks.
The 12 script lines with no recording
MSG_VOICE_A_273 MSG_VOICE_D_183
MSG_VOICE_D_600 … MSG_VOICE_D_608 MSG_VOICE_D_611
All twelve are in the VOICE family, have caption text, are called from the
stage scripts, and have neither a cue nor a bank. Ten of them form the near
contiguous block D_600–D_611. Whether they are subtitle-only by design or
a late cut is not settled — the shape (a contiguous high block) suggests
lines added after the recording sessions, but that is a guess, not a
measurement.
What this does not settle
- The known wrong-recording case is not explained by this table.
VOICE_D_452— the generic "Resupply complete" line documented in voice-bank-leading-region.md — has its own unique id (7140) and its own bank. Since every id is distinct, bank sharing is not happening at the cue level, so that symptom still has to be explained inside the bank/sound.paklayer. STAGESis empty, and what would have populated it is unknown.SOUNDS' one non-numeric value was not chased down.- Only the English pack and the
jpn\bank paths were examined. Every path inFILESbegins withjpn\, which is consistent with the already-recorded finding that the voice set is Japanese, but the other language packs' tables were not diffed against this one.
Tool
tools/re-capture/sound_cues.py summary | resolve <MSG_id> | demo | unbound