This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/crates/sylpheed-formats/tests/slb_leading_segment_disc.rs
Sylpheed RE agent d15b3d8d85 slb: derive the leading-stream data offset instead of assuming 1392
HEADERLESS_DATA_OFFSET is the value the offset takes in <lang>\etc\, not a
property of the format. The leading stream is a whole number of 2048-byte XMA1
packets ending at the first RIFF, so its start is first_riff % XMA1_PACKET.
Disc-wide that takes four values -- 1392, 1468, 1600, 1728 -- varying by
language and subdirectory.

Verified by decoding, not by arithmetic: on a random 140-bank sample with a
non-empty leading region, the derived offset yields more audio in 85, identical
in 54 (the eng\etc controls, where it must and does reproduce the old
behaviour) and less in 1. Median gain among the improved is 70x --
eng\Voice\VOICE_TCAF_592 goes 1506 -> 97152 bytes, jpn 2910 -> 127178.

This withdraws my own claim from earlier today that the Japanese banks were a
different undecoded layout. They are the same format with a different offset;
I had treated a constant derived from one subdirectory as a property of the
format. The same error was hiding the identical defect in 1873 eng\Voice banks.
2026-08-26 03:56:50 +00:00

156 lines
6.1 KiB
Rust

//! The `.slb` leading segment — recovered, and scoped.
//!
//! `VOICE_D_453` used to decode to 0.14 s because its line lives in a headerless
//! packet stream *before* the first `RIFF`, and the decoder started at the
//! `RIFF`. The banks that looked fine were the ones whose leading segment is
//! silence. One rule, two outcomes.
use std::path::{Path, PathBuf};
use sylpheed_formats::{slb, PakArchive};
fn disc_root() -> Option<PathBuf> {
if let Ok(p) = std::env::var("SYLPHEED_DISC") {
let p = PathBuf::from(p);
if p.join("dat").is_dir() {
return Some(p);
}
}
let default = Path::new(
"/home/fabi/RE - Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja)",
);
default.join("dat").is_dir().then(|| default.to_path_buf())
}
macro_rules! skip_without_disc {
($root:ident) => {
let Some($root) = disc_root() else {
eprintln!("SKIP: set SYLPHEED_DISC");
return;
};
};
}
fn bank(root: &Path, n: u32) -> Vec<u8> {
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let path = format!("eng\\etc\\VOICE_D_{n}.slb");
let entry = snd.find_by_name(&path).expect("bank present");
snd.read(entry).expect("read")
}
/// The boundary is arithmetic and has no tunable: the first `RIFF` sits at
/// exactly `HEADERLESS_DATA_OFFSET + n*XMA1_PACKET` in every resupply bank.
#[test]
fn leading_segment_is_a_whole_number_of_packets() {
skip_without_disc!(root);
for (n, packets) in [(450u32, 8usize), (451, 1), (452, 7), (453, 22), (454, 29)] {
let b = bank(&root, n);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
assert!(ri > slb::HEADERLESS_DATA_OFFSET, "VOICE_D_{n}");
let lead = ri - slb::HEADERLESS_DATA_OFFSET;
assert_eq!(lead % slb::XMA1_PACKET, 0, "VOICE_D_{n} not a whole packet count");
assert_eq!(lead / slb::XMA1_PACKET, packets, "VOICE_D_{n} packet count");
}
}
/// The two banks whose line lives in the leading segment now yield it.
#[test]
fn broken_banks_recover_their_line() {
skip_without_disc!(root);
// (bank, sub-waves expected, payload of the leading one)
for (n, waves, lead_len) in [(453u32, 2usize, 45116usize), (454, 2, 59452)] {
let riffs = slb::to_xma_riffs(&bank(&root, n));
assert_eq!(riffs.len(), waves, "VOICE_D_{n} sub-wave count");
assert_eq!(riffs[0].len(), lead_len, "VOICE_D_{n} leading segment");
// It must be the LARGER part: that is the whole point.
assert!(
riffs[0].len() > riffs[1].len() * 5,
"VOICE_D_{n}: leading segment should dominate"
);
}
}
/// `VOICE_D_451`'s leading region is all zeros — the guard must skip it, so the
/// rule cannot prepend silence to a bank that does not need it.
#[test]
fn all_zero_leading_region_is_skipped() {
skip_without_disc!(root);
let b = bank(&root, 451);
let ri = b.windows(4).position(|w| w == b"RIFF").unwrap();
assert!(
b[slb::HEADERLESS_DATA_OFFSET..ri].iter().all(|x| *x == 0),
"expected an all-zero leading region"
);
// Two sub-waves, both from the RIFF section — no synthesised third.
assert_eq!(slb::to_xma_riffs(&b).len(), 2);
}
fn bank_named(root: &Path, path: &str) -> Vec<u8> {
let snd = PakArchive::open(root.join("dat/sound.pak")).expect("sound.pak");
let entry = snd.find_by_name(path).unwrap_or_else(|| panic!("{path} present"));
snd.read(entry).expect("read")
}
/// `HEADERLESS_DATA_OFFSET` is the `<lang>\etc\` case, not the format.
///
/// The leading stream is a whole number of packets ending at the first `RIFF`,
/// so its start is `first_riff % XMA1_PACKET`. Disc-wide that takes four values
/// and only 1392 matches the old constant — assuming it elsewhere starts the
/// decode mid-packet. See docs/re/structures/slb-data-offset.md.
#[test]
fn leading_data_offset_is_derived_not_assumed() {
skip_without_disc!(root);
// (bank, expected derived offset). The `etc` banks must still land on the
// old constant — that is the no-regression half of the test.
for (path, want) in [
("eng\\etc\\VOICE_D_452.slb", 1392usize),
("eng\\etc\\VOICE_D_453.slb", 1392),
("eng\\Voice\\VOICE_TCAF_592.slb", 1468),
("jpn\\Voice\\VOICE_TCAF_592.slb", 1728),
("jpn\\etc\\VOICE_D_452.slb", 1600),
] {
let b = bank_named(&root, path);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
let got = slb::leading_data_offset(ri);
assert_eq!(got, want, "{path}: derived offset");
assert_eq!(
(ri - got) % slb::XMA1_PACKET,
0,
"{path}: leading stream is not a whole packet count"
);
assert!(
got == slb::HEADERLESS_DATA_OFFSET || got > slb::HEADERLESS_DATA_OFFSET,
"{path}: offsets below the old constant are unexplained"
);
}
}
/// The banks the old constant mis-decoded now carry a leading sub-wave, and the
/// ones it decoded correctly are untouched.
#[test]
fn derived_offset_recovers_voice_banks_without_regressing_etc() {
skip_without_disc!(root);
for path in ["eng\\Voice\\VOICE_TCAF_592.slb", "jpn\\Voice\\VOICE_TCAF_592.slb"] {
let b = bank_named(&root, path);
let ri = b.windows(4).position(|w| w == b"RIFF").expect("has a RIFF");
// Under the old constant this leading region was not a whole packet
// count, so `to_xma_riffs` emitted no leading sub-wave at all.
assert_ne!(
(ri - slb::HEADERLESS_DATA_OFFSET) % slb::XMA1_PACKET,
0,
"{path}: expected the OLD constant to mis-align here"
);
let riffs = slb::to_xma_riffs(&b);
assert!(
riffs.len() >= 2,
"{path}: expected a leading sub-wave plus at least one RIFF, got {}",
riffs.len()
);
}
// Control: an `etc` bank still produces what it did before.
let b = bank_named(&root, "eng\\etc\\VOICE_D_452.slb");
assert_eq!(slb::leading_data_offset(
b.windows(4).position(|w| w == b"RIFF").unwrap()),
slb::HEADERLESS_DATA_OFFSET);
}