From 0a0a0333dd7a8336f30f10e3bdc03aceba80d099 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Wed, 26 Aug 2026 05:33:01 +0000 Subject: [PATCH] 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. --- crates/sylpheed-formats/src/pak.rs | 11 ++++ .../tests/slb_leading_segment_disc.rs | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/crates/sylpheed-formats/src/pak.rs b/crates/sylpheed-formats/src/pak.rs index f4c117e..110e6ec 100644 --- a/crates/sylpheed-formats/src/pak.rs +++ b/crates/sylpheed-formats/src/pak.rs @@ -190,6 +190,17 @@ impl PakArchive { 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`). pub fn entries(&self) -> &[PakEntry] { &self.entries diff --git a/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs b/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs index 4d085f6..cfb3302 100644 --- a/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs +++ b/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs @@ -219,3 +219,53 @@ fn scan_only_returns_known_offsets() { } 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"); +}