Files
Sylpheed/crates/sylpheed-formats/examples/voice_region_chunks.rs
MechaCat02 62376dd4a1 style: rustfmt sweep — 107 files the lint gate never saw
This branch predates CI on `main`. `cargo fmt --all` only; no behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 16:34:40 +02:00

101 lines
4.2 KiB
Rust

//! What ARE the chunks a movie-voice region decodes to?
//!
//! The port reports a resolved voice region decoding to **three** chunks — two
//! of equal duration each spanning the whole movie, and a leading one that
//! "matches nothing" — and notes that this is the same 2+1 signature the BGM
//! banks showed before `bank_header_len` attributed the extra to a bank header.
//! Two different asset kinds with one signature is worth checking, because if
//! the same explanation applies then `bank_header_len` is incomplete, and if it
//! does not then the leading chunk is something we are discarding.
//!
//! `slb.rs`'s own doc comment already predicts the answer and disagrees with
//! "drop it": the header signature fires on 28 entries, all music banks, with
//! "zero false positives on the 7 993 mid-bank windows, WHERE THE LEADING
//! REGION IS REAL". A voice region is a mid-bank window by construction —
//! `resolve_movie_voice_region` starts it at the PREDECESSOR cue's trailer.
//!
//! cargo run -p sylpheed-formats --example voice_region_chunks -- <disc-dir>
use sylpheed_formats::media::{self, DirectorySource, DiscSource};
use sylpheed_formats::slb::{self, VoiceLang};
fn main() {
let disc = std::env::args()
.nth(1)
.unwrap_or_else(|| std::env::var("SYLPHEED_DISC").expect("disc dir"));
let src = DirectorySource::new(std::path::PathBuf::from(&disc));
// Disc-wide, not the four movies that motivated the question: every movie
// the manifest binds a voice to.
let movies: Vec<String> = {
use sylpheed_formats::movie_manifest;
let tpak = src.open_pak("dat/tables.pak").expect("tables.pak");
let manifest = tpak
.entries()
.iter()
.find_map(|e| tpak.read(e).ok().filter(|b| movie_manifest::is_manifest(b)))
.expect("movie manifest");
movie_manifest::parse(&manifest)
.into_iter()
.map(|m| m.movie)
.collect()
};
println!("{} movies in the manifest\n", movies.len());
let mut census: std::collections::BTreeMap<usize, usize> = Default::default();
let (mut with_header, mut with_leading, mut no_leading) = (0, 0, 0);
for movie in movies.iter().map(|s| s.as_str()) {
let Some((start, end)) = media::resolve_movie_voice_region(&src, movie, VoiceLang::English)
else {
continue;
};
let len = end - start;
let Ok(bytes) = src.read_segment_range("dat/sound", start, len as usize) else {
println!("{movie:8} region {start}..{end} unreadable");
continue;
};
// Where does the first RIFF sit? Everything before it is the leading
// headerless packet region.
let first_riff = bytes
.windows(4)
.position(|w| w == b"RIFF")
.map(|p| p as i64)
.unwrap_or(-1);
let hdr = slb::bank_header_len(&bytes);
let riffs = slb::to_xma_riffs(&bytes);
let kind = if first_riff <= 0 {
no_leading += 1;
"no leading region".to_string()
} else if hdr == Some(first_riff as usize) {
with_header += 1;
format!(
"BANK HEADER ({first_riff} B = {} packets exactly)",
first_riff / 2048
)
} else {
with_leading += 1;
*census.entry(first_riff as usize % 2048).or_default() += 1;
format!(
"leading STREAM ({first_riff} B = {} packets + {} B)",
first_riff / 2048,
first_riff % 2048
)
};
println!(
"{movie:10} {start:12}..{end:12} {len:9} B chunks {} {kind}",
riffs.len()
);
if let Ok(dir) = std::env::var("VOICE_CHUNK_DUMP") {
for (i, r) in riffs.iter().enumerate() {
let _ = std::fs::write(format!("{dir}/{movie}-chunk{i}.wav"), r);
}
}
}
println!(
"\n{with_header} region(s) open with a BANK HEADER (bank_header_len fires)\n\
{with_leading} open with a leading STREAM\n{no_leading} start at a RIFF"
);
println!("leading-stream length mod 2048, i.e. the derived data offset:");
for (rem, n) in &census {
println!(" {rem:5} B x{n}");
}
}