//! Does the `.slb` leading region decode as XMA1 under ANY plausible format? //! //! A first attempt at this hand-rolled the `fmt ` chunk and produced 0 PCM bytes //! for all 36 combinations — including ones that should have matched the crate's //! own working format. So it tested the chunk construction, not the hypothesis. //! This version replicates `slb::synth_xma1_fmt`'s exact byte layout and starts //! by reproducing its known result as a CONTROL; if the control does not match, //! nothing below it means anything. use std::io::Write; use std::process::{Command, Stdio}; use sylpheed_formats::{movie_subtitle, slb, PakArchive}; /// Byte-for-byte `slb::synth_xma1_fmt` (private there). Note the second /// parameter is a **channel mask**, not a stream count — mistaking it is what /// made the first probe meaningless. fn xma1_fmt(channels: u8, channel_mask: u16, rate: u32) -> Vec { let mut fmt = Vec::with_capacity(40); fmt.extend_from_slice(b"fmt "); fmt.extend_from_slice(&32u32.to_le_bytes()); fmt.extend_from_slice(&0x0165u16.to_le_bytes()); // XMA1 fmt.extend_from_slice(&16u16.to_le_bytes()); // BitsPerSample fmt.extend_from_slice(&0u16.to_le_bytes()); // EncodeOptions fmt.extend_from_slice(&0u16.to_le_bytes()); // LargestSkip fmt.extend_from_slice(&1u16.to_le_bytes()); // NumStreams fmt.push(0); // LoopCount fmt.push(3); // Version fmt.extend_from_slice(&(rate * channels as u32 * 2).to_le_bytes()); fmt.extend_from_slice(&rate.to_le_bytes()); fmt.extend_from_slice(&0u32.to_le_bytes()); fmt.extend_from_slice(&0u32.to_le_bytes()); fmt.push(4); // SubframeData fmt.push(channels); fmt.extend_from_slice(&channel_mask.to_le_bytes()); fmt } fn riff(fmt: &[u8], data: &[u8]) -> Vec { let mut out = Vec::with_capacity(12 + fmt.len() + 8 + data.len()); out.extend_from_slice(b"RIFF"); out.extend_from_slice(&((4 + fmt.len() + 8 + data.len()) as u32).to_le_bytes()); out.extend_from_slice(b"WAVE"); out.extend_from_slice(fmt); out.extend_from_slice(b"data"); out.extend_from_slice(&(data.len() as u32).to_le_bytes()); out.extend_from_slice(data); out } fn decode_bytes(r: &[u8]) -> usize { let Ok(mut c) = Command::new("ffmpeg") .args(["-v", "error", "-i", "pipe:0", "-f", "s16le", "pipe:1"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::null()) .spawn() else { return 0; }; let buf = r.to_vec(); let mut stdin = c.stdin.take().unwrap(); std::thread::spawn(move || { let _ = stdin.write_all(&buf); }); c.wait_with_output().map(|o| o.stdout.len()).unwrap_or(0) } 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"); let lang = PakArchive::open(format!("{disc}/dat/movie/eng.pak")).expect("eng.pak"); // The leading region decodes only as MONO. At channels=2 it yields 1792 // bytes; at channels=1, 203648 for VOICE_D_453. The decoded SAMPLE COUNT is // independent of the declared rate (the rate only sets playback speed), so // the subtitle cue can be used to solve for the real rate instead. let bound = [ ("hokyu_LS_s02A", 450u32), ("hokyu_LS_s09A", 451), ("hokyu_DS_s13A", 452), ("hokyu_LS_s02H", 453), ("hokyu_DS_s07H", 454), ]; println!( "{:<16} {:>6} {:>9} {:>9} {:>10} {:>9} {:>12}", "movie", "bank", "stereo B", "mono B", "samples", "cue s", "implied Hz" ); for (movie, n) in bound { let path = format!("eng\\etc\\VOICE_D_{n}.slb"); let Some(entry) = snd.find_by_name(&path) else { continue }; let bytes = snd.read(entry).expect("read"); let Some(first_riff) = bytes.windows(4).position(|w| w == b"RIFF") else { continue }; if first_riff <= slb::HEADERLESS_DATA_OFFSET { println!("{movie:<16} {:>6} (no leading region)", format!("D_{n}")); continue; } let lead = &bytes[slb::HEADERLESS_DATA_OFFSET..first_riff]; let stereo = decode_bytes(&riff(&xma1_fmt(2, 2, 48000), lead)); let mono = decode_bytes(&riff(&xma1_fmt(1, 0, 48000), lead)); let samples = mono / 2; // 16-bit mono let cue = movie_subtitle::track_voice_cues(&lang, movie) .iter() .map(|(_, t)| *t) .fold(0.0f32, f32::max); let implied = if cue > 0.0 { samples as f32 / cue } else { f32::NAN }; println!( "{movie:<16} {:>6} {stereo:>9} {mono:>9} {samples:>10} {cue:>9.2} {implied:>12.0}", format!("D_{n}") ); } println!("\n'implied Hz' = decoded samples / the movie's last subtitle cue."); println!("A consistent value near a standard rate is the real sample rate."); }