# ✅ A music bank's "third sub-wave" is its **header**, and the bug was arithmetic **Status:** ✅ `CONFIRMED` — **decoded**, with a disc-wide check over all 9 519 `sound.pak` entries, a decode control, and independent corroboration from the running game. Fixed in `sylpheed-formats` 2026-08-29. **Raised by the port**, on its P6 critical path: `sound_bank_riffs("BGM_103.slb")` returned **three** sub-waves against [`bgm-two-stems.md`](bgm-two-stems.md)'s census, which says a music bank is exactly two. Its exporter was summing all three, so the shipped menu music was the sum of three things where the corpus predicted two. It declined to choose which to drop, which was right — that is a decoding question. ## The answer The third thing is **the bank header**. Not a stem, not an artefact of the disc: our own reader was emitting it. `to_xma_riffs` has a hybrid branch for banks that carry a headerless packet stream *before* their first `RIFF` — the fix that recovered `VOICE_D_453`'s line ([`slb-data-offset.md`](slb-data-offset.md)). It derives that stream's start as ```rust first_riff % XMA1_PACKET // XMA1_PACKET = 2048 ``` which is correct **only when the bank header is smaller than one packet**. It is, in the voice banks the branch was written for: their headers put the first `RIFF` at 1392, 1468, 1600 or 1728 mod 2048. A music bank's header is **exactly five packets — 10 240 bytes** — so the modulus returns **0**, and the branch emitted `slb[0..10240]`: the whole header, as sub-wave 0. The header states its own length, so nothing here needs a heuristic: ``` BGM_103.slb +0x00 BE u32 1103 bank id +0x18 BE u32 0x00000800 block size = 2048 +0x1c BE u32 7839244 data size +0x20 BE u32 1103 the id again ← signature, with +0x18 +0x24 BE u32 5 HEADER LENGTH IN BLOCKS → 5 × 2048 = 10240 +0x28 BE u32 0x00100002 16 bit / 2 ch ``` ## The disc-wide check Over all **9 519** entries of `sound.pak` ([`tools/re-capture/slb_segment_phase.py`](../../../tools/re-capture/slb_segment_phase.py) supplies the reader): | | | |---|---| | entries matching the header signature at offset 0 | **28** | | ...whose declared header ends **exactly** at the first `RIFF` | **28 / 28** | | ...with a real gap between header and first `RIFF` | **0** | | false positives among the 9 491 others | **0** | The 28 are exactly the music banks — ids **1001–1023** and **1101–1105**. So on this disc a bank header at offset 0 and a leading packet stream **never coexist**, and the guard is not a threshold: if a bank states a header, believe it, and there is nothing before the first `RIFF`. ⚠️ `BGM_106`–`BGM_109` are **not** in the 28 and must not be: their pak entries start mid-bank, so they have no header at offset 0 and their leading region is real audio (the tail of the previous bank). That is the same straddle [`bgm-two-stems.md`](bgm-two-stems.md) already documents. ## The decode control Decoding the emitted region proves it is not audio, and the control is run through **the same chain, on the same bank, in the same invocation**: | | bytes | PCM decoded | |---|---|---| | `BGM_103` — what we emitted as "sub-wave 0" | 10 240 | **0.009 s** | | `BGM_103` — its real wave 0 (control) | 3 876 864 | **87.744 s** (declared 87.75) | | `BGM_001` — what we emitted as "sub-wave 0" | 10 240 | **0.009 s** | | `BGM_001` — its real wave 0 (control) | 4 466 688 | **173.809 s** (declared 173.82) | FFmpeg `xma1`, mono/stereo taken from the bank's own `fmt `. The region is also **99.1 % zero bytes** (67–93 non-zero of 10 240 across the 28 banks) and its last non-zero byte is at 6431, so its final 1.86 packets are entirely empty. ## Corroboration from the oracle, which was already in the corpus [`bgm-two-stems.md`](bgm-two-stems.md) records that at the **main menu**, with `--xma_param_probe=true`, the decoder was handed **two** stereo 48 kHz streams — of **3 876 864** and **3 930 112** bytes, byte-for-byte `BGM_103`'s two declared waves. A third stem would have been a third stream. The running game was already saying two. ## The fix `slb::bank_header_len` (new, `pub`) reads the signature and returns the declared length; the hybrid branch uses it in preference to the modulus: ```rust let start = bank_header_len(slb).unwrap_or_else(|| leading_data_offset(ri)); if ri > start { /* emit the leading stream */ } ``` Two regression tests in [`tests/slb_leading_segment_disc.rs`](../../../crates/sylpheed-formats/tests/slb_leading_segment_disc.rs): the disc-wide 28/28 identity, and `BGM_103`/`BGM_001` returning exactly two sub-waves at their declared payload sizes. The pre-existing voice-bank tests — `broken_banks_recover_their_line`, `derived_offset_recovers_voice_banks_without_regressing_etc` — still pass, so the `VOICE_D_453` recovery is untouched. 10/10 green with `SYLPHEED_DISC` set. ## Reach * The 28 are the only banks on the disc that state a header at offset 0. A bank format elsewhere with a header ≥ 2048 B that we have not seen would have had the same bug; nothing on this disc does. * This says nothing about **which** of the two remaining waves is which — that is still 🟡 in [`bgm-two-stems.md`](bgm-two-stems.md) (surround-rear pair vs a second intensity layer), and both readings predict playing them together. * It does not change the count for any voice bank: `VOICE_*` entries have no header at offset 0, so their leading region is emitted exactly as before.