slb: guard the wave-boundary identity with a test, and expose data_at

A wave runs to data_at + declared_size, and there is seek magic exactly there
whose little-endian packet count at +12 times 2048 equals the declared size --
7620/7620 disc-wide. That is the decoder-independent boundary and the thing that
proves the declared sizes honest, so it should not be able to regress silently.

The boundary routinely lies outside the entry's own comp_size window, so reading
it needs the flat segment stream rather than the entry slice; PakArchive gains a
small data_at(offset, len) accessor for that.

Test walks a bounded slice to stay fast -- 792 banks in this run, all holding.
8 disc tests pass.
This commit is contained in:
Sylpheed RE agent
2026-08-26 05:33:01 +00:00
parent e19e2aa1e8
commit 0a0a0333dd
2 changed files with 61 additions and 0 deletions

View File

@@ -190,6 +190,17 @@ impl PakArchive {
Ok(Self::from_parts(index, Vec::new())?.entries) Ok(Self::from_parts(index, Vec::new())?.entries)
} }
/// A raw slice of the concatenated segment data, by absolute offset.
///
/// Entry windows are **not** wave boundaries in `sound.pak`: a `.slb` wave
/// runs to `data_at + declared_size`, which routinely overruns the entry's
/// own `comp_size` (see docs/re/structures/slb-data-offset.md). Reading the
/// boundary marker therefore needs the flat stream, not the entry slice.
/// Returns `None` if the range falls outside the loaded data.
pub fn data_at(&self, offset: usize, len: usize) -> Option<&[u8]> {
self.data.get(offset..offset.checked_add(len)?)
}
/// All TOC entries, in stored order (ascending `name_hash`). /// All TOC entries, in stored order (ascending `name_hash`).
pub fn entries(&self) -> &[PakEntry] { pub fn entries(&self) -> &[PakEntry] {
&self.entries &self.entries

View File

@@ -219,3 +219,53 @@ fn scan_only_returns_known_offsets() {
} }
assert!(seen >= 20, "expected banks to test, saw {seen}"); assert!(seen >= 20, "expected banks to test, saw {seen}");
} }
/// A wave's boundary is exact: `seek` magic sits at `data_at + declared_size`.
///
/// Established 2026-08-26 (docs/re/structures/slb-data-offset.md). Every
/// `RIFF`-bearing entry on the disc satisfies it — **7 620/7 620** in the full
/// sweep — and the `seek` chunk's little-endian packet count at `+12` times
/// 2048 equals the declared size. This is the decoder-independent boundary, and
/// it is what proves the declared sizes honest rather than over-stated.
///
/// The test walks a bounded slice so it stays fast; the identity is disc-wide.
#[test]
fn a_waves_declared_size_is_confirmed_by_the_next_seek() {
skip_without_disc!(root);
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let mut checked = 0usize;
for n in 1u32..400 {
for path in [
format!("eng\\etc\\VOICE_D_{n}.slb"),
format!("eng\\Voice\\VOICE_TCAF_{n:03}.slb"),
format!("jpn\\Voice\\VOICE_ADAN_{n:03}.slb"),
] {
let Some(entry) = snd.find_by_name(&path) else { continue };
let Ok(b) = snd.read(entry) else { continue };
let Some(ri) = b.windows(4).position(|w| w == b"RIFF") else { continue };
let Some(rel) = b[ri..].windows(4).position(|w| w == b"data") else { continue };
let di = ri + rel;
let Some(sz) = b.get(di + 4..di + 8) else { continue };
let declared = u32::from_le_bytes(sz.try_into().unwrap()) as usize;
// The boundary lies outside this entry's own TOC window whenever the
// declared size overruns it, which is the common case — so read from
// the archive's flat data rather than from the entry slice.
let probe = entry.offset as usize + di + 8 + declared;
let Some(tag) = snd.data_at(probe, 16) else { continue };
assert_eq!(
&tag[0..4],
b"seek",
"{path}: expected `seek` at data_at+declared ({probe})"
);
let packets = u32::from_le_bytes(tag[12..16].try_into().unwrap()) as usize;
assert_eq!(
packets * slb::XMA1_PACKET,
declared,
"{path}: seek packet count x 2048 != declared data size"
);
checked += 1;
}
}
assert!(checked >= 30, "expected banks to check, got {checked}");
eprintln!("wave-boundary identity held for {checked} banks");
}