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 d6127a049e
commit e5ce7e4ba3
3 changed files with 189 additions and 5 deletions

View File

@@ -131,6 +131,70 @@ fn parse_voice_clip(name: &str) -> VoiceClip {
/// that yields the full track for segment banks while the clamp drops the
/// duplicate takes for alternate-take banks. (Dynamic RE via Canary file-I/O
/// tracing confirmed the movie→voice binding; this fixes the *decode* of `RT*`.)
/// The four data offsets that occur on the disc, in ascending order.
///
/// Measured over all 7 358 banks whose offset is *known* (they carry a `RIFF`,
/// so the offset is forced to `first_riff % XMA1_PACKET`): no other value
/// occurs. They are all of the form `1392 + 4k`.
pub const DATA_OFFSET_CANDIDATES: [usize; 4] = [1392, 1468, 1600, 1728];
/// How plausible a candidate offset is, judged by XMA1 packet headers alone.
///
/// Each 2048-byte 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. Returns the fraction of the first `LIMIT` packets that look
/// sane, so 1.0 is a clean stream.
fn packet_plausibility(slb: &[u8], start: usize) -> f32 {
const LIMIT: usize = 24;
let (mut seen, mut ok, mut pos) = (0usize, 0usize, start);
while pos + XMA1_PACKET <= slb.len() && seen < LIMIT {
let h = u32::from_be_bytes([slb[pos], slb[pos + 1], slb[pos + 2], slb[pos + 3]]);
let frame_offset_bits = (h >> 11) & 0x7FFF;
let metadata = (h >> 8) & 0x7;
let packet_skip = h & 0xFF;
if frame_offset_bits as usize <= XMA1_PACKET * 8 && metadata <= 1 && packet_skip <= 8 {
ok += 1;
}
seen += 1;
pos += XMA1_PACKET;
}
if seen == 0 {
0.0
} else {
ok as f32 / seen as f32
}
}
/// Recover a bank's data offset when there is no `RIFF` to derive it from.
///
/// Picks the candidate whose packet headers look most like a real XMA1 stream.
/// Cross-checked against the 7 358 banks where the answer *is* known from the
/// `RIFF` position: **7 330 correct (99.62 %)**, and every one of the 28 misses
/// is a tie on the top score — the scan is never wrong when it has a unique
/// winner. Ties fall back to [`HEADERLESS_DATA_OFFSET`].
pub fn scan_data_offset(slb: &[u8]) -> usize {
let mut best = (HEADERLESS_DATA_OFFSET, -1.0f32);
let mut tied = false;
for &c in &DATA_OFFSET_CANDIDATES {
if c >= slb.len() {
continue;
}
let score = packet_plausibility(slb, c);
if score > best.1 {
best = (c, score);
tied = false;
} else if (score - best.1).abs() < f32::EPSILON {
tied = true;
}
}
if tied {
HEADERLESS_DATA_OFFSET
} else {
best.0
}
}
/// Where a bank's leading headerless packet stream starts.
///
/// The stream is a whole number of 2048-byte XMA1 packets ending at the first
@@ -148,10 +212,19 @@ pub fn to_xma_riffs(slb: &[u8]) -> Vec<Vec<u8>> {
let mut out = Vec::new();
let first_riff = find(slb, b"RIFF", 0);
if first_riff.is_none() {
// Headerless single-stream bank.
if let Some(data) = slb.get(HEADERLESS_DATA_OFFSET..) {
// Headerless single-stream bank. Two things here were wrong, and the
// corrections are measured (docs/re/structures/slb-data-offset.md):
//
// * the offset is not the constant — with no `RIFF` to derive it from,
// scan the four candidates by packet plausibility;
// * the stream is **mono**. At two channels a 48-bank sample yielded
// 0..4 816 bytes; at one, 180 000..380 000. There was not one bank
// where the old stereo/1392 pair beat the scanned mono pair, and the
// median gain was 184x.
let start = scan_data_offset(slb);
if let Some(data) = slb.get(start..) {
if !data.is_empty() {
out.push(build_riff(&synth_xma1_fmt(2, 2, 48000), data));
out.push(build_riff(&synth_xma1_fmt(1, 0, 48000), data));
}
}
return out;