//! Real-disc tests for media assembly. Skipped without `SYLPHEED_DISC`. //! //! This logic used to live in the Bevy viewer, where it had no test at all. It //! is the trickiest reading on the disc — a cutscene's voice is a byte region of //! a continuous stream, not the bank its name points at — so it gets pinned here //! before anything else is built on top of it. use std::path::PathBuf; use sylpheed_formats::media::{self, DirectorySource, DiscSource}; use sylpheed_formats::slb::VoiceLang; fn disc() -> Option { let p = PathBuf::from(std::env::var("SYLPHEED_DISC").ok()?); p.join("dat").is_dir().then(|| DirectorySource::new(p)) } /// A segment-spanning read returns the same bytes as slicing the whole archive. /// /// The control that matters: `sound.pak`'s data is five segments, so a TOC /// offset late in the archive addresses a position no single file has. If the /// walk were off by a segment this would return plausible-looking wrong bytes /// rather than fail, which is exactly why it is asserted against the archive's /// own read rather than against a length. #[test] fn segment_range_matches_the_archive_read() { let Some(src) = disc() else { eprintln!("SKIP: set SYLPHEED_DISC"); return; }; let name = "BGM_020.slb"; let hash = sylpheed_formats::hash::name_hash(name); let via_range = media::read_sound_bank(&src, hash).expect("segment range read"); let toc = src.read_file("dat/sound.pak").unwrap(); let entries = sylpheed_formats::PakArchive::parse_toc(&toc).unwrap(); let e = entries .iter() .find(|e| e.name_hash == hash) .expect("BGM_020 in the TOC"); assert_eq!(via_range.len(), e.comp_size as usize); // And it decodes, which a misaligned read would not do. let riffs = media::sound_bank_riffs(&src, name).expect("riffs"); assert!(!riffs.is_empty(), "no sub-waves recovered"); } /// A movie's voice resolves to a byte region, and the region is sane. /// /// `RT01A` is one of the cutscenes whose voice spans more than one `.slb` /// chunk — the case that motivated regions over per-bank reads in the first /// place. The assertions are deliberately about *shape* (ordered, non-empty, /// smaller than one bank) rather than exact offsets, because the offsets are /// disc facts we have no independent oracle for here; a regression that /// reversed or emptied the region would still be caught. #[test] fn movie_voice_resolves_to_a_region_that_decodes() { let Some(src) = disc() else { eprintln!("SKIP: set SYLPHEED_DISC"); return; }; let (start, end) = media::resolve_movie_voice_region(&src, "RT01A", VoiceLang::English) .expect("RT01A has a bound voice track"); assert!(start < end, "region is inverted: {start}..{end}"); assert!(end - start > 4096, "region is implausibly small"); assert!(end - start < 1_500_000, "region spans more than one bank"); let riffs = media::voice_region_riffs(&src, start, end).expect("region riffs"); assert!(!riffs.is_empty(), "region decoded to no audio"); } /// An unbound movie stays unvoiced rather than borrowing a neighbour's clip. /// /// This is a *negative* the corpus paid for: extending resolution to unbound /// resupply movies by shared demo line played the WRONG recording. The guard /// keeps that door shut. #[test] fn manifest_binding_is_the_only_route() { let Some(src) = disc() else { eprintln!("SKIP: set SYLPHEED_DISC"); return; }; // A movie the manifest does not bind must resolve to nothing, not to a guess. assert_eq!( media::resolve_movie_voice_clip(&src, "no_such_movie_xyz", VoiceLang::English), None ); } /// The resolved `ADV` voice region must contain **all three** streams the running /// decoder decodes — not a truncated first one. /// /// Ground truth is the emulator, not this crate: booting with `--xma_param_probe` /// reports three XMA contexts with `byte_size` 1 294 336 / 1 118 208 / 1 171 456 /// (`docs/re/structures/voice-three-streams-are-concurrent.md`). Until 2026-08-30 /// the resolver's start filter capped a region at 1.5 MB, `ADV`'s span is 3.6 MB, /// so the start fell back to `anchor` — a TOC offset, 238 packets into the first /// stream — and this returned 806 912 for the first chunk. /// /// This is a regression test against an EXTERNAL measurement, which is the only /// kind that can catch the class of bug it was written for: every internal check /// passed happily while a third of a stream was missing. #[test] fn adv_voice_region_holds_all_three_decoded_streams() { let Some(src) = disc() else { eprintln!("SKIP: set SYLPHEED_DISC"); return; }; let (start, end) = media::resolve_movie_voice_region(&src, "ADV", VoiceLang::English) .expect("ADV voice region"); let bytes = src .read_segment_range("dat/sound", start, (end - start) as usize) .expect("region bytes"); let sizes: Vec = sylpheed_formats::slb::to_xma_riffs(&bytes) .iter() .map(|r| r.len() - 60) .collect(); assert_eq!( sizes, vec![1_294_336, 1_118_208, 1_171_456], "the region must reproduce the RUNNING DECODER's byte_sizes; \ a first chunk of 806912 means the start filter has come back" ); }