re: there is no XACT container on this disc -- retracting my own lead and

closing Q8

Last iteration I named Pj_Silph.xgs as the route to the SE cue index, on
the reasoning that XACT keeps cue->wave there. That route is dead, and
the negative is properly bounded: its 533-byte entry is high entropy with
no XGSF magic, +/-8 KB around it has none either, all 1.08 GB of
sound.pak contains zero XGSF, SDBK and WBND, and the executable has no
XACT or .xgs string anywhere. Control run first -- the same scan finds
RIFF in BGM_001 exactly where the bank structure says it should be, so
the scan works and the magic genuinely is not there.

So the .xgs and .slb extensions came from the authoring tool and survived
into shipped data that is not XACT at all. That goes in METHOD as a
general trap: check for the magic before committing to a parser for the
format an extension advertises. Writing an XACT parser would have been
wasted work.

What that leaves for the SE audio is undecodable-with-reach rather than
pending: Static.slb has no wave boundaries, there is no project file, and
none of the sound table's five records carries an offset. The bank is
certainly the right one -- 8 353 472 bytes over 322 cues is 0.84 to 1.01
seconds each at the bitrates the disc uses elsewhere, exactly the shape
of a bank of short effects. Only the index into it is missing, and it
exists only at runtime.

Q8 closes: vocabulary and bank decoded, the event binding a name match on
the authors' own event names, and the audio explicitly something the port
must source or author rather than extract. With this every MISSION row is
answered.
This commit is contained in:
Sylpheed RE agent
2026-08-28 18:58:06 +00:00
parent c760cdc509
commit f5e7426cfa
4 changed files with 65 additions and 5 deletions

View File

@@ -34,7 +34,7 @@ authored version can be deleted.
| Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 1× `NEW GAME`); ⬆⬇ one step, **wraps both ends**; ⬅➡ do nothing; Ⓑ returns to the parent **with focus restored**; Ⓑ on the main menu → title; Ⓑ on the title → nothing — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) | | Q5 | navigation semantics | ✅ answered | **measured**: initial focus varies boot to boot (2× `TUTORIAL`, 1× `NEW GAME`); ⬆⬇ one step, **wraps both ends**; ⬅➡ do nothing; Ⓑ returns to the parent **with focus restored**; Ⓑ on the main menu → title; Ⓑ on the title → nothing — [`menu-navigation-semantics.md`](../re/menu-navigation-semantics.md) |
| Q6 | boot sequence + what drives it | ✅ answered | sequence **measured** end to end; the driver is **code, not data** — four search spaces closed, so the port **authors** the sequence — [`boot-config-and-gamepart-registry.md`](../re/boot-config-and-gamepart-registry.md) | | Q6 | boot sequence + what drives it | ✅ answered | sequence **measured** end to end; the driver is **code, not data** — four search spaces closed, so the port **authors** the sequence — [`boot-config-and-gamepart-registry.md`](../re/boot-config-and-gamepart-registry.md) |
| Q7 | transitions | ✅ answered | a **fade through black**, drawn by the screen's own last-painting `.prm` quad. Fade-in ramp is **decoded** from its keyframes; the ~0.4 s fade-out is **measured** (not in the file) — [`screen-transitions.md`](../re/screen-transitions.md) | | Q7 | transitions | ✅ answered | a **fade through black**, drawn by the screen's own last-painting `.prm` quad. Fade-in ramp is **decoded** from its keyframes; the ~0.4 s fade-out is **measured** (not in the file) — [`screen-transitions.md`](../re/screen-transitions.md) |
| Q8 | menu audio bindings | 🟡 mostly answered | the **UI cue vocabulary is decoded** (`SE_UI_CURSOR`/`DECIDE`/`CANSEL`/`IMPOSI`, all in `Static.slb` per `BANK_SE`); the event binding is a **name match**, and the SE audio is **not extractable yet** — [`menu-audio-cues.md`](../re/menu-audio-cues.md) | | Q8 | menu audio bindings | answered | cue vocabulary + bank **decoded**; event binding is a **name match** (the authors' own event names); SE audio is **undecodable from the disc** — no XACT container exists anywhere — [`menu-audio-cues.md`](../re/menu-audio-cues.md) |
| Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE``ADV.wmv` (boot intro *and* attract are one asset), `MS00A``S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. ✅ **one Ⓐ skips a movie** (title at 57 s vs a 193 s baseline) — [`movie-binding.md`](../re/movie-binding.md) | | Q9 | video binding + playback rules | ✅ answered | **decoded** from the movie manifest: `ADVERTISE_MOVIE``ADV.wmv` (boot intro *and* attract are one asset), `MS00A``S00A.wmv` is the new-game intro, `STAFF_ROLL`→the credits reel. ✅ **one Ⓐ skips a movie** (title at 57 s vs a 193 s baseline) — [`movie-binding.md`](../re/movie-binding.md) |
| Q10 | music-bank sub-wave roles (intro+loop?) | ✅ answered | **two stems of one performance, played together** — sample-synchronous, equal duration, 32/32 banks. **Concatenating is wrong.** Not a seamless loop either — [`structures/bgm-two-stems.md`](../re/structures/bgm-two-stems.md) | | Q10 | music-bank sub-wave roles (intro+loop?) | ✅ answered | **two stems of one performance, played together** — sample-synchronous, equal duration, 32/32 banks. **Concatenating is wrong.** Not a seamless loop either — [`structures/bgm-two-stems.md`](../re/structures/bgm-two-stems.md) |
| S1 | Ready Room go/no-go | ✅ **no-go** | it is 2D and enumerates fine (60 builds), but `GP_READY_ROOM.pak` holds **briefing/tactical-map** content, not the six-button Ready Room menu — [`ready-room-probe.md`](../re/ready-room-probe.md) | | S1 | Ready Room go/no-go | ✅ **no-go** | it is 2D and enumerates fine (60 builds), but `GP_READY_ROOM.pak` holds **briefing/tactical-map** content, not the six-button Ready Room menu — [`ready-room-probe.md`](../re/ready-room-probe.md) |
@@ -156,10 +156,15 @@ authored version can be deleted.
🟡 **Which event fires which cue is a name match**, not a measurement — strong, 🟡 **Which event fires which cue is a name match**, not a measurement — strong,
because these are the authors' own event names, but nobody has watched the game because these are the authors' own event names, but nobody has watched the game
emit cue 2 on a d-pad press. The port is authoring it. emit cue 2 on a d-pad press. The port is authoring it.
**The SE audio cannot be exported yet.** `Static.slb`'s 8 353 472 readable **The SE audio cannot be exported at all from the disc.** `Static.slb`'s
bytes contain **0 `RIFF`, 0 `seek`, 0 `WAVE`** — the boundary marker that works 8 353 472 readable bytes contain **0 `RIFF`, 0 `seek`, 0 `WAVE`** — the boundary
for all 7 620 other banks is simply absent, so an individual cue's wave is not marker that works for all 7 620 other banks is absent — and there is **no XACT
locatable. Next step is `Pj_Silph.xgs` (in `sound.pak`, TOC 9454). container anywhere**: 0 × `XGSF`/`SDBK`/`WBND` across all 1.08 GB of
`sound.pak`, and no `XACT`/`.xgs` string in the executable. The `.xgs`/`.slb`
names come from the authoring tool, not the shipped format. The bank is
certainly the right one — 8 353 472 B over 322 cues is 0.841.01 s each at the
disc's own bitrates — but the index into it exists only at runtime. **The UI
sound effects must be sourced or authored; they cannot be extracted.**
* **The boot sequence is not data-driven — the port authors it.** ❔ Four places * **The boot sequence is not data-driven — the port authors it.** ❔ Four places
were checked and the order is in none: `config.ini`'s `[SYSTEM]` is empty, the were checked and the order is in none: `config.ini`'s `[SYSTEM]` is empty, the

View File

@@ -178,3 +178,9 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
processes carries the whole loop prompt as its argv, so the listing is tens of processes carries the whole loop prompt as its argv, so the listing is tens of
kilobytes of the prompt you already have. Use `pgrep -x <name>` or kilobytes of the prompt you already have. Use `pgrep -x <name>` or
`ps -o pid=,stat= -C <name>`. `ps -o pid=,stat= -C <name>`.
* **A file extension is not a format.** `sound.pak` is full of `.slb` banks and
names a `.xgs` project, and neither is an XACT container: the whole 1.08 GB
archive contains zero `XGSF`, `SDBK` or `WBND` magic, and the executable has no
XACT string at all. The names came from the authoring tool and survived into the
shipped data. Check for the magic before committing to a parser for the format
the extension advertises.

View File

@@ -158,6 +158,11 @@ neighbourhood, not just the line.
## Audio ## Audio
* "`Pj_Silph.xgs` holds the cue→wave index, so parse XACT" → **no XACT container
exists on this disc**: 0 × `XGSF`/`SDBK`/`WBND` in all 1.08 GB of `sound.pak`,
and no `XACT`/`.xgs` string in the executable. The extensions are the authoring
tool's, not the format's. [`menu-audio-cues.md`](menu-audio-cues.md)
* "every sound cue resolves to its own `.slb` bank" → the 322 `SE_*` cues do not; * "every sound cue resolves to its own `.slb` bank" → the 322 `SE_*` cues do not;
**0 of 322** are in `FILES`, and `BANK_SE` puts them all in `Static.slb`. **0 of 322** are in `FILES`, and `BANK_SE` puts them all in `Static.slb`.
* "`Static.slb` can be split into waves like any other bank" → it holds **0 * "`Static.slb` can be split into waves like any other bank" → it holds **0

View File

@@ -98,3 +98,47 @@ parsing XACT.
* which BGM plays on which screen is **authored** — nothing on the disc says; * which BGM plays on which screen is **authored** — nothing on the disc says;
* and the UI **sound effects cannot be exported yet**. That is a gap in the * and the UI **sound effects cannot be exported yet**. That is a gap in the
assets, not in the naming. assets, not in the naming.
## ❔ `Pj_Silph.xgs` is a dead end — retracting the lead, with the reach
The section above named `Pj_Silph.xgs` as "the named next step", on the reasoning
that in XACT the cue→wave index lives in the project file. **That route is dead.**
| check | result |
|---|---|
| the entry's own bytes | 533 bytes, high entropy, **no `XGSF` magic** |
| ±8 KB around the entry in the flat concatenation | no `XGSF`, `SDBK`, `WBND`, `RIFF` or `seek` |
| **all of `sound.pak` — 1.08 GB across `.p00``.p04`** | **0 × `XGSF`, 0 × `SDBK`, 0 × `WBND`** |
| the executable | **0** occurrences of `XGSF`/`XACT`; no string matching `xact`, `.xgs`, `wavebank` or `soundbank` |
**Control, run first:** the same magic scan over `BGM_001.slb`'s neighbourhood
finds `RIFF` exactly where the bank structure says it should be. The scan works;
the magic genuinely is not there.
So the `.xgs` / `.slb` names are **inherited from the authoring tool, not from the
shipped format** — nothing on this disc is a parseable XACT container. There is no
XACT project file to read, and writing an XACT parser would have been wasted work.
### Where that leaves the SE audio: undecodable, with reach
Locating an individual SE cue's wave inside `Static.slb` is **not possible from
anything found on the disc**. Looked in:
* `Static.slb` itself — 8 353 472 readable bytes, **0 `RIFF` / 0 `seek` / 0 `WAVE`**,
where every one of the other 7 620 banks is delimited by `seek` magic at
`data_at + declared_size` ([`structures/slb-data-offset.md`](structures/slb-data-offset.md));
* the XACT project file — absent, as above;
* the sound table's five records — `SOUNDS` gives cue→id, `FILES` gives bank
paths, `BANK_SE` gives the one bank name, `SETTINGS` gives a path and the
(absent) project file, `STAGES` is empty. **No record carries an offset.**
**What does corroborate the binding:** `Static.slb` is 8 353 472 bytes, and at the
bitrates the disc uses elsewhere (25 69731 000 B/s) that is **269325 seconds of
audio — 0.84 to 1.01 s per cue over 322 cues.** Exactly the shape of a bank of
short UI/HUD/weapon effects, which is what `BANK_SE` says it is. The bank is the
right bank; only the index into it is missing.
**If it is ever picked up**, the route is the running game, not the disc: watch
the guest hand a wave offset/length to the decoder when a cue fires. That is the
same instrumentation the cue↔event binding needs, and the same one this container
cannot run.