slb: the headerless path was decoding stereo at a fixed offset; both are wrong

1495 banks carry no RIFF and take a separate path that hardcoded both the
offset and stereo. Across a random 48-bank sample there was NOT ONE where the
old stereo-at-1392 pair beat the best mono offset; median gain 184x, individual
banks going from 0-4816 decoded bytes to 180000-380000. Stereo shows the same
stop-after-one-frame signature already recorded for the leading segment.

With no RIFF the offset cannot be derived, so scan_data_offset picks among the
four disc offsets by XMA1 packet-header plausibility. Validated on the LABELLED
set -- all 7358 banks that do have a RIFF, where the answer is forced: 7330
correct (99.62%), and all 28 misses are ties on the top score, never a wrong
unique winner. Ties fall back to 1392.

The winning offsets also reproduce, by directory, the distribution measured
independently from the RIFF-bearing banks. jpn\etc splits 1468/1600, so path
alone is not sufficient -- which is why this is a scan and not a lookup table.

7 disc tests pass (build-reborn test -p sylpheed-formats --test
slb_leading_segment_disc, SYLPHEED_DISC wired up).
This commit is contained in:
Sylpheed RE agent
2026-08-26 04:03:05 +00:00
parent e4ca3ff36b
commit 57a9d98de1
3 changed files with 189 additions and 5 deletions

View File

@@ -75,13 +75,58 @@ the sample the format was derived from.
The same error was hiding a defect in the **English** set too: 1 873 `eng\Voice`
banks sit at 1468 and were being decoded mid-packet just as badly.
## The `RIFF`-less banks had the same bug, plus a worse one
**✅ Settled 2026-08-26.** 1 495 banks (799 `jpn`, 696 `eng`) carry no `RIFF` at
all and take a separate code path. That path was wrong twice over:
1. it used the constant offset, with no `RIFF` to derive from; and
2. it built a **stereo** `fmt` chunk.
Decoded across a random 48-bank sample:
| | |
|---|---|
| banks where the old stereo-at-1392 beat the best mono offset | **0 of 48** |
| median gain | **184×** |
| range | 25× 489 344× |
Stereo is the same failure signature recorded for the leading segment: it stops
after one frame. Individual banks went from 04 816 bytes to 180 000380 000.
The winning offsets fall out **by directory**, and they reproduce the
distribution measured independently from the `RIFF`-bearing banks — which is the
cross-check that makes this more than curve-fitting:
eng\etc 1392 (11/11) eng\Voice 1468 (9/9) eng\Briefing 1392 (2/2)
jpn\Voice 1600 (12/13) jpn\etc 1468 (8/12), 1600 (4)
Note `jpn\etc` splits, so the **path alone is not enough** to pick the offset.
### Picking the offset without a decoder
An XMA1 packet opens with a big-endian header — 6 bits frame count, 15 bits
frame-offset-in-bits, 3 bits metadata, 8 bits packet-skip. At the true offset
those fields stay in range packet after packet; one byte off and they do not.
Scoring the first 24 packets and taking the best candidate:
**7 330 of 7 358 (99.62 %)** on the labelled set — every bank that *has* a
`RIFF`, where the answer is forced and therefore known. All **28** misses are
ties on the top score; there is not a single case where the scan picks wrongly
with a unique winner. `scan_data_offset` therefore falls back to 1392 on a tie.
This is used only for the `RIFF`-less banks. Where a `RIFF` exists the offset is
derived from it exactly, never scanned.
## What this does not settle
* **Why the offset takes those four values**, and what the bytes before it are.
There is no length field in the first 64 bytes — banks open on high-entropy
data — so the offset is derived, not read.
* **`eng\Voice\VOICE_TCAF_608.slb`**, above.
* **The 799 jpn / 696 eng banks with no `RIFF` at all** are untouched by this;
they go down the headerless path and were not re-examined.
* **The 28 ties.** The scan cannot separate them and falls back to 1392, which
is right for roughly a third of that population and wrong for the rest.
* **Why the offset takes exactly these four values by directory** is still
unexplained — see above.
* Nothing here was run **in the game** — this is a decoder-side result measured
with FFmpeg as the oracle.