//! Real-disc test for the movie manifest → voice binding. Skipped without //! `SYLPHEED_DISC`. use std::path::PathBuf; use sylpheed_formats::movie_manifest; use sylpheed_formats::slb::VoiceLang; use sylpheed_formats::PakArchive; fn disc_root() -> Option { let p = PathBuf::from(std::env::var("SYLPHEED_DISC").ok()?); p.join("dat").is_dir().then_some(p) } /// Read the manifest + `eng\sounds.tbl` out of `tables.pak`. fn load_manifest_and_sounds(root: &PathBuf) -> (Vec, Vec) { let pak = PakArchive::open(root.join("dat/tables.pak")).unwrap(); let manifest = pak .entries() .iter() .find_map(|e| pak.read(e).ok().filter(|b| movie_manifest::is_manifest(b))) .expect("movie manifest present in tables.pak"); let sounds = pak .read_by_name("eng\\sounds.tbl") .expect("sounds.tbl present") .unwrap(); (manifest, sounds) } #[test] fn binds_and_resolves_movie_voice() { let Some(root) = disc_root() else { eprintln!("SKIP: set SYLPHEED_DISC"); return; }; let (manifest, sounds) = load_manifest_and_sounds(&root); let entries = movie_manifest::parse(&manifest); assert!(entries.len() > 90, "expected ~101 movies, got {}", entries.len()); // Standard story movie → VOICE_ in \Movie\. assert_eq!( movie_manifest::voice_token(&manifest, "S13A").as_deref(), Some("VOICE_S13A") ); assert_eq!( movie_manifest::resolve_voice_entry(&manifest, &sounds, "S13A", VoiceLang::English) .as_deref(), Some("eng\\Movie\\VOICE_S13A.slb") ); // A resupply movie bound to an in-mission radio clip → VOICE_D_* in \etc\. // (This is what a `VOICE_` guess would miss entirely.) assert_eq!( movie_manifest::voice_token(&manifest, "hokyu_LS_s02A").as_deref(), Some("VOICE_D_450") ); assert_eq!( movie_manifest::resolve_voice_entry( &manifest, &sounds, "hokyu_LS_s02A", VoiceLang::English ) .as_deref(), Some("eng\\etc\\VOICE_D_450.slb") ); // A resupply movie the game leaves unbound in the manifest → no DIRECT // binding. (Extending to unbound movies by shared demo line was verified // WRONG against the running game, so we do NOT resolve these.) let e = entries .iter() .find(|e| e.movie == "hokyu_DS_s13A") .expect("hokyu_DS_s13A present in manifest"); assert_eq!(e.voice_token, None, "hokyu_DS_s13A has no direct voice binding"); assert_eq!( movie_manifest::resolve_voice_entry(&manifest, &sounds, "hokyu_DS_s13A", VoiceLang::English), None ); // Every resolved voice entry must actually exist in sound.pak. let snd = std::fs::read(root.join("dat/sound.pak")).unwrap(); let keys: std::collections::HashSet = PakArchive::parse_toc(&snd) .unwrap() .iter() .map(|e| e.name_hash) .collect(); let mut resolved = 0; let mut missing = Vec::new(); for e in &entries { if let Some(full) = movie_manifest::resolve_voice_entry(&manifest, &sounds, &e.movie, VoiceLang::English) { resolved += 1; if !keys.contains(&sylpheed_formats::hash::name_hash(&full)) { missing.push(full); } } } assert!(resolved > 80, "expected 80+ voiced movies, got {resolved}"); assert!(missing.is_empty(), "resolved but absent in sound.pak: {missing:?}"); }