# The cutscene message table — speaker, portrait, timing and audio cue **✅ Settled 2026-08-26.** The `MSG_DEMO_*` family is not called from any stage script; it is driven by its own IDXD tables in the language packs. Those tables carry, per line of cutscene dialogue: **who says it, which portrait is shown, how long it stays on screen, and which audio cue plays**. This answers the question left open by [isl-message-dialogue-link.md](isl-message-dialogue-link.md) — what drives cutscene dialogue, given that built-in 64 never mentions it. ## Where it lives 32 IDXD objects per language pack (`dat/GP_MAIN_GAME_.pak`). Each holds a `Generic` record with a `Count`, plus some number of `Message_NNN` records — **149 in total** across the English pack, covering **138 distinct ids**. ## Record layout A `Message_NNN` record has two named fields, `ID` and `PageCount`, and then **9 positional fields per page**: | slot | content | example | |---|---|---| | 0 | speaker | `CharacterNATALIE` | | 1 | portrait | `FaceNATALIE_01` | | 2 | constant `None` at all 149 records | `None` | | 3 | on-screen seconds, 1.00 – 5.00 (mean 2.48) | `1.9` | | 4 | audio cue, or empty | `DEMO_017` | | 5–8 | the four caption keys of that page | `MSG_DEMO_600_000_00` … `_03` | The field count is exactly **9 · PageCount + 2**, and that identity holds for every one of the 7 distinct PageCount values present (1→11, 2→20, 3→29, 4→38, 5→47, 6→56, 8→74). That is what pins the per-page grouping. ## Evidence * **1 252 of 1 252** caption-key slots equal `__` exactly, with **zero** mismatches. The grouping is not a guess about which slot is which. * **78** records span more than one page — which independently equals the 78 multi-page `MSG_DEMO` ids counted from the *caption* side, by a different method. Two measurements, same number. * In **68 of those 78** the speaker changes between pages, confirming from the data that successive pages are successive utterances rather than one long speech. * The id sets close exactly: **138 ids in the caption table, 138 in the message table, none on either side without the other.** * 28 distinct speakers, 98 distinct portraits. ## The audio cue (slot 4) 296 of 313 pages carry a cue, and there are **296 distinct values** — one per page, never reused. It joins to the `SOUNDS` table described in [sound-cue-table.md](sound-cue-table.md): DEMO_017 -> sound id 8017 The numbering rule is `DEMO_nnn` → `8000 + nnn`, which holds for **286 of 286** plain-numeric cues. The 9 remaining cues carry a letter suffix (`DEMO_067A`, `DEMO_190A`–`C`, `DEMO_216A`–`C`, `DEMO_278A`–`B`) and are assigned 8400–8408 in order. There is no unexplained residue. Within a message, **77 of the 78** multi-page records number their cues consecutively; one does not. **A guess I had and dropped:** slot 4 looked at first like a movie reference, since the values resemble the `DEMO_nnn` naming of a clip. It is not — there are 97 `.wmv` files on the disc and their names look nothing like this (`ADV.wmv`, `RT01A.wmv`), and the values are unique *per page*, which no movie reference would be. ## What this does not settle * **`MSG_DEMO_500`** — one page, KATANA, 2 s, **no text and no cue**. A placeholder, apparently. * **Slot 2 is `None` at all 149 records**, so what it would hold otherwise is unknown — a constant with no observed variation carries no information. * **Nothing here was run.** This is a static read of the tables; the playback order, and whether slot 3 is really the on-screen duration rather than an audio length or a delay, has not been checked against the running game. ## Artifact [`../data/cutscene-message-table.txt`](../data/cutscene-message-table.txt) — all 313 pages with speaker, portrait, seconds, cue and text. Regenerate with `tools/re-capture/sound_cues.py demo`. ## ✅ The cue-less pages are the resupply lines — and they corroborate the movie work **Settled 2026-08-26.** 16 of the 17 pages with an empty slot 4 belong to five ids, and those five are exactly the resupply lines already documented from the *movie* side in [voice-bank-leading-region.md](../voice-bank-leading-region.md). The caption text matches word for word: | id | speaker | line | bank | |---|---|---|---| | `MSG_DEMO_600` | NATALIE | "Rhino 3 has landed. Commencing resupply." | `VOICE_D_450` | | `MSG_DEMO_601` | NATALIE | "Rhino Leader has landed. Commencing resupply." | `VOICE_D_451` | | `MSG_DEMO_602` | NATALIE | "Resupply complete. You are cleared for take-off!" | `VOICE_D_452` | | `MSG_DEMO_603` | TCAFSUPPLY | "All right, beginning resupply." | `VOICE_D_453` | | `MSG_DEMO_604` | TCAFSUPPLY | "Resupply complete. Switching to take-off sequence." | `VOICE_D_454` | This also explains the duplicate records. Each of these ids appears **once per stage that uses it**, and the repeat counts equal the movie-slot counts recorded independently for the corresponding banks — **5 of 5**: DEMO_600 x3 DEMO_601 x3 DEMO_602 x4 DEMO_603 x4 DEMO_604 x2 D_450 x3 D_451 x3 D_452 x4 D_453 x4 D_454 x2 Two facts fall out: * the **11 extra records over 138 ids** are these repeats, not an anomaly; and * slot 4 is empty on purpose — a resupply line's audio is bound through the **movie** path (`VOICE_D_45x`), not through the `DEMO_nnn` cue path, so there is no cue to name. The agreement is worth something beyond closing two items: the cutscene message table and the movie subtitle chain were decoded separately, from different containers, and they land on the same five lines with the same multiplicities.