//! How many sub-waves does each resupply voice bank hold? //! //! The corpus records `VOICE_D_452` as the binding the game rejected in-game //! ("wrong recording"), and separately notes that `VOICE_D_453`/`454` decode to //! 0.14 s / 0.43 s — "far too short for the spoken line". Both observations are //! explained if these banks are multi-sub-wave and the extractor plays only the //! first. This prints the shape so that stops being a guess. use sylpheed_formats::{slb, PakArchive}; fn main() { let disc = std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC"); let snd = PakArchive::open(format!("{disc}/dat/sound.pak")).expect("sound.pak"); println!( "{:<14} {:>7} {:>6} {:>6} {:>7} sub-wave data sizes", "bank", "bytes", "RIFFs", "waves", "cover" ); for n in 450..=454 { for dir in ["etc", "Voice", "Movie"] { let path = format!("eng\\{dir}\\VOICE_D_{n}.slb"); let Some(entry) = snd.find_by_name(&path) else { continue }; let bytes = snd.read(entry).expect("read"); let riffs = slb::to_xma_riffs(&bytes); let sizes: Vec = riffs.iter().map(|r| r.len()).collect(); // How many RIFF magics does the bank actually contain, versus how // many sub-waves the walker recovered? A gap means the walk stops // early, and the missing bytes are the missing audio. let magics = bytes.windows(4).filter(|w| *w == b"RIFF").count(); let covered: usize = sizes.iter().sum(); // What are the UNCOVERED bytes? If the tail past the last data // chunk is all zero it is padding and the short duration is real; // if it is high-entropy it is audio the parse is throwing away. let last = bytes .windows(4) .rposition(|w| w == b"data") .map(|i| { let sz = u32::from_le_bytes(bytes[i + 4..i + 8].try_into().unwrap()) as usize; (i + 8 + sz).min(bytes.len()) }) .unwrap_or(0); // Where does the RIFF structure START? If it begins far into the // file, the uncovered bytes are a leading region the parse skips, // not a missed sub-wave. let first_riff = bytes.windows(4).position(|w| w == b"RIFF").unwrap_or(0); let datas = bytes.windows(4).filter(|w| *w == b"data").count(); // Is the leading region padding, or content? Padding is nearly all // zero and uses few distinct byte values. let head = &bytes[..first_riff]; let head_zero = head.iter().filter(|b| **b == 0).count(); let head_distinct = { let mut seen = [false; 256]; for b in head { seen[*b as usize] = true; } seen.iter().filter(|s| **s).count() }; let tail = &bytes[last..]; let zeros = tail.iter().filter(|b| **b == 0).count(); // Dump the first bytes of the leading region so its structure is // visible rather than guessed at. let hex: String = head .iter() .take(48) .map(|b| format!("{b:02x}")) .collect::>() .join(" "); println!(" head[0..48] {hex}"); println!( "{:<14} {:>7} {:>6} {:>6} {:>6.1}% 1st RIFF @{:>6} data chunks {} head {:>5.1}% zero/{:>3} distinct tail {:>5} B ({:>5.1}% zero) {:?}", format!("VOICE_D_{n}"), bytes.len(), magics, riffs.len(), 100.0 * covered as f64 / bytes.len() as f64, first_riff, datas, if head.is_empty() { 0.0 } else { 100.0 * head_zero as f64 / head.len() as f64 }, head_distinct, tail.len(), if tail.is_empty() { 0.0 } else { 100.0 * zeros as f64 / tail.len() as f64 }, sizes ); break; } } }