//! 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 ); }