re: the menu's music is BGM_103 -- found by accident while chasing Q6,

confirmed three ways

Chasing where the event code comes from, sub_821C5580 turned out to do
two things worth having.

For Q6 it arms the outer gate: li r11,3 ; stw r11,16(r28) is exactly the
value sub_821C7850 tests before dispatching, which answers the "what does
this+16 == 3 gate on" question I left open last iteration. It then passes
r5 = [r27+4], an event read out of a structure, so the vocabulary is
still not enumerable. Of the four callers of sub_821C7850, three pass
constants -- 0, 0 and 5 -- and this one does not.

The accident is a few instructions earlier: li r5, 1103 into a sound-play
call. 1103 is a BGM cue id, BGM_103. That closes a residual I had written
off as undecodable: which bank the menu plays. The cue table genuinely
cannot say, since its BGM entries are numeric -- but the code can, and it
checks out three independent ways. The census says BGM_103.slb is two
waves of 3876864 and 3930112 bytes; the XMA probe at the main menu saw
two stereo streams of 3876864 and 3930112 bytes. Byte for byte on both.

That also corrects me. bgm-two-stems.md said those observed sizes matched
no bank's declared waves and inferred the decoder gets a window rather
than a whole wave. Wrong on both counts -- I had checked only the BGM_0xx
rows of the census because that was the block on screen. METHOD gets it:
check a measurement against the whole reference set, not the part you
happened to be reading.
This commit is contained in:
Sylpheed RE agent
2026-08-28 20:10:41 +00:00
parent a98e96b0f0
commit 86e52b01d0
6 changed files with 97 additions and 13 deletions

View File

@@ -220,3 +220,9 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
into place later (`addi r27, r11, X``mr r5, r27`). A backward search for the
destination register alone found 7 of 48 sites and missed the two known-good
ones. Simulate forward over a window instead, tracking `lis`/`addi`/`mr`.
* **Check a measurement against the WHOLE reference set, not the part you were
reading.** Two observed BGM stream sizes were recorded as matching "no bank's
declared waves", which spawned a wrong theory that the decoder gets a window
rather than a whole wave. They match `BGM_103` exactly — the check had covered
only the `BGM_0xx` rows of the census, because that is the block that had been
on screen.

View File

@@ -174,6 +174,14 @@ neighbourhood, not just the line.
## Audio
* "which bank the menu plays is not on the disc" → the **cue table** cannot say
(all BGM cues are numeric), but the **code** can: `sub_821C5580` plays cue
**1103 = `BGM_103`**, and its two declared waves match the two streams the XMA
probe saw byte-for-byte.
* "the observed BGM stream sizes match no bank's declared waves, so the game hands
the decoder a window" → **mine, and wrong** — I checked only the `BGM_0xx` rows.
They are `BGM_103`'s two waves exactly; the game hands over the whole wave.
* "an individual SE cue's audio cannot be extracted" → **mine, and wrong.**
`--xma_param_probe=true` logs each stream's head bytes; searching them in
`Static.slb` locates the wave exactly. [`menu-audio-cues.md`](menu-audio-cues.md)

View File

@@ -441,3 +441,45 @@ arguments, and `r28` is `sub_821C7850`'s own third argument, passed through
untouched. So the event vocabulary is defined **at least one level above** this
function. Finding what `3`, `5`, `8`, `10`, `25` mean means going up again, and
that was not done.
## Going up one more level — three of four callers pass a constant event
`sub_821C7850` has **four** direct callers. Recovering the `r5` argument at each:
| caller | event passed |
|---|---|
| `sub_821C7CB8` | **0** (→ back to the title) |
| `sub_821C7BA0` | **0** |
| `sub_821C47A0` | **5** (→ `LOADING`) |
| `sub_821C5580` | **not constant**`lwz r5, 4(r27)`, read out of a structure |
So the vocabulary is still not enumerable from here: the interesting caller reads
its event from a field. ❔ What `3`, `8`, `10` and `25` mean remains open.
**`sub_821C5580` is also where the outer gate is set:**
```
821c5640 li r11, 3
821c5644 stw r11, 16(r28) ; this+16 = 3 — the exact value sub_821C7850 tests
821c5650 lwz r5, 4(r27) ; event from a struct field
821c5658 bl 0x821C7850
```
That answers the "what does `this+16 == 3` gate on" question from the previous
section: this function arms it.
### 🎁 And it names the title's music
The same function, a few instructions earlier:
```
821c560c li r5, 1103
821c5610 li r4, 4
821c561c lwz r3, 0(r29)
821c5620 bl 0x8217ACF8 ; a sound-play call
```
**1103 is a BGM cue id**`BGM_103`. That closes an open residual from Q8/Q10
("which bank is the menu's music is not on the disc"), and it checks out three
ways; see [`structures/bgm-two-stems.md`](structures/bgm-two-stems.md).

View File

@@ -63,12 +63,20 @@ same instrumentation Q4's residual wants, and not something this container can d
today (it runs `--mute=true` against an SDL dummy device, so there is no audio
path to watch either).
## Which BGM per screen — undecodable, reach already recorded
## Which BGM per screen — the TABLE cannot say, but the CODE can
All 32 BGM cues are named `BGM_001``BGM_109`. Nothing in `SOUNDS`, `FILES` or the
bank headers names a screen — see
[`structures/bgm-two-stems.md`](structures/bgm-two-stems.md). Unchanged by this
iteration.
All 32 BGM cues are named `BGM_001``BGM_109`, and nothing in `SOUNDS`, `FILES` or
the bank headers names a screen. That negative stands **for the tables**.
It does not stand for the executable. `GamePart_Title`'s phase handler
`sub_821C5580` contains `li r5, 1103` feeding a sound call — **cue 1103 =
`BGM_103`** — and `BGM_103.slb`'s two declared waves (3 876 864 / 3 930 112 bytes)
are byte-for-byte the two streams the XMA probe saw decoding at the main menu.
Three routes, one answer; see
[`structures/bgm-two-stems.md`](structures/bgm-two-stems.md).
So the per-screen BGM binding is recoverable **per screen, from the code that
plays it** — not from any table.
## ❔ And a new negative: an individual SE's audio is not extractable yet

View File

@@ -129,8 +129,25 @@ bank's two waves are simultaneous, not sequential.** Concatenating them is wrong
now measured as well as inferred.
🟡 It still does **not** separate the two readings — surround-rear pair versus a
second intensity layer — because both predict simultaneity. And the two byte
sizes do not match any bank's declared wave sizes in
[`../data/bgm-wave-census.txt`](../data/bgm-wave-census.txt), so *which* bank the
menu plays is still not established; the game hands the decoder a window, not the
whole declared wave.
second intensity layer — because both predict simultaneity.
## ✅ The menu's music is `BGM_103` — three independent routes agree
An earlier version of this section said the two observed byte sizes "do not match
any bank's declared wave sizes". **That was wrong** — I had checked only the
`BGM_0xx` rows. They match `BGM_103` exactly, and the code names it:
| route | evidence |
|---|---|
| **static, code** | `GamePart_Title`'s phase handler `sub_821C5580` does `li r5, 1103` into a sound call (`sub_8217ACF8`, with `r4 = 4`) — **cue 1103 = `BGM_103`** |
| **static, disc** | [`../data/bgm-wave-census.txt`](../data/bgm-wave-census.txt): `BGM_103.slb` = two waves of **3 876 864** and **3 930 112** bytes, 87.75 s each |
| **runtime** | the XMA probe at the **main menu** saw two stereo 48 kHz streams decoding simultaneously, of **3 876 864** and **3 930 112** bytes |
Byte-for-byte on both waves. So:
* **which bank the menu plays is decoded** — `BGM_103`, from the executable, since
the cue *table*'s BGM entries are numeric and name no screen;
* and the two-stems-play-together finding is now confirmed **on a named bank**,
with the runtime stream sizes equal to the bank's declared wave sizes — so the
game hands the decoder the whole wave, not a window, which is the opposite of
what the earlier note guessed.