Parse the sound bank container framing
A level's sound extent is two regions: an `SBlk` descriptor table and the sample data that follows it. Four u32 at the head describe the layout, and the invariant that establishes it is sblk_offset + sblk_size == daw_offset, which holds on all 27 levels. That is what pins the field meanings; the numbers alone are ambiguous about which of them is the offset. DAW is a format tag inside the SBlk header, not a region signature. It occurs exactly once in the entire extent, at sblk_offset + 12, naming the sample format -- the sample data itself starts at daw_offset with no signature at all, roughly 19 KB away on level 0. Reading it as a region magic is the obvious reading and it is wrong. daw_size never reaches the end of the file. The tail is positive on all 27 levels, 232 to 1,848 bytes, so the field describes less than the region it names; the test asserts that direction rather than inventing a size that fits. 523,720 SBlk and 37,649,008 DAW bytes across the 27 levels. The sample format inside DAW is not decoded. It looks like VAG/ADPCM from the nibble packing, and it has no bearing on drawing anything, so it stays open rather than getting a half-answer.
This commit is contained in:
@@ -1393,3 +1393,69 @@ fn occlusion_grids_parse_on_every_level() {
|
||||
widest unexplained tail {widest_slack} bytes"
|
||||
);
|
||||
}
|
||||
|
||||
/// The sound extent framing.
|
||||
///
|
||||
/// The invariant here is `sblk_offset + sblk_size == daw_offset`, which holds
|
||||
/// on all 27 levels and pins the meaning of the four head fields. `DAW` never
|
||||
/// reaches the end of the file; the tail is positive on every level, so the
|
||||
/// size field is not the whole region and this test asserts the direction
|
||||
/// rather than a size.
|
||||
#[test]
|
||||
fn sound_bank_framing_holds_on_every_level() {
|
||||
require_disc!();
|
||||
let data = std::env::var_os("GC_EXTRACTED")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
|
||||
PathBuf::from(home).join("Documents/gc-extracted")
|
||||
});
|
||||
if !data.exists() {
|
||||
eprintln!("skipped: unpack the disc first");
|
||||
return;
|
||||
}
|
||||
|
||||
use gc_formats::sound_bank::parse_sound_bank;
|
||||
let mut levels = 0usize;
|
||||
let mut sblk_bytes = 0usize;
|
||||
let mut daw_bytes = 0usize;
|
||||
let (mut narrowest, mut widest) = (usize::MAX, 0usize);
|
||||
|
||||
for n in 0..27 {
|
||||
let raw = match std::fs::read(data.join(format!("level{n}")).join("sound_bank.bin")) {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let s = parse_sound_bank(&raw).unwrap_or_else(|e| panic!("level{n}: sound bank: {e}"));
|
||||
|
||||
assert_eq!(s.header.version, 3, "level{n}: sound bank version");
|
||||
assert_eq!(s.header.unknown_4, 2, "level{n}: sound bank unknown_4");
|
||||
assert_eq!(
|
||||
s.header.sblk_offset, 24,
|
||||
"level{n}: SBlk sits at 24 on every level"
|
||||
);
|
||||
assert!(
|
||||
s.header.sblk_offset + s.header.sblk_size == s.header.daw_offset,
|
||||
"level{n}: SBlk must run into DAW with no gap"
|
||||
);
|
||||
assert!(s.has_sblk_magic(), "level{n}: SBlk magic");
|
||||
assert!(s.has_daw_tag(), "level{n}: DAW tag at SBlk+12");
|
||||
assert_eq!(s.sblk_version(), Some(1), "level{n}: SBlk version");
|
||||
// The tail is always in the same direction: the extent is longer than
|
||||
// the DAW region it describes.
|
||||
narrowest = narrowest.min(s.daw_tail);
|
||||
widest = widest.max(s.daw_tail);
|
||||
|
||||
sblk_bytes += s.sblk.len();
|
||||
daw_bytes += s.daw.len();
|
||||
levels += 1;
|
||||
}
|
||||
|
||||
assert_eq!(levels, 27, "expected all 27 levels, got {levels}");
|
||||
assert!(sblk_bytes > 300_000, "only {sblk_bytes} SBlk bytes");
|
||||
assert!(daw_bytes > 20_000_000, "only {daw_bytes} DAW bytes");
|
||||
eprintln!(
|
||||
"sound_bank: {sblk_bytes} SBlk + {daw_bytes} DAW bytes across {levels} levels; \
|
||||
unexplained tail {narrowest}..{widest} bytes"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user