use sylpheed_formats::{hash::name_hash, movie_manifest, movie_subtitle as ms, movie_voice, slb, PakArchive}; use std::fs;use std::io::{Read,Seek,SeekFrom};use std::process::Command; fn rg(disc:&str,g:u64,n:usize)->Vec{let mut segs=vec![];let mut cum=0u64;for i in 0..5{let p=format!("{disc}/dat/sound.p{i:02}");if let Ok(m)=fs::metadata(&p){segs.push((cum,m.len(),p));cum+=m.len();}}let mut out=vec![];let(mut need,mut pos)=(n,g);for(base,len,path)in &segs{if need==0||pos>=base+len||pos<*base{continue;}let local=pos-base;let take=need.min((len-local)as usize);let mut f=fs::File::open(path).unwrap();f.seek(SeekFrom::Start(local)).unwrap();let mut b=vec![0u8;take];f.read_exact(&mut b).unwrap();out.extend_from_slice(&b);need-=take;pos+=take as u64;}out} fn ct(h:&[u8],n:&[u8])->bool{h.windows(n.len()).any(|w|w==n)} fn dur(w:&str)->String{let o=Command::new("ffprobe").args(["-v","error","-show_entries","format=duration","-of","default=nw=1:nk=1",w]).output().unwrap();String::from_utf8_lossy(&o.stdout).trim().to_string()} fn main(){ let disc=std::env::var("SYLPHEED_DISC").unwrap(); let tpak=PakArchive::open(format!("{disc}/dat/tables.pak")).unwrap(); let man=tpak.entries().iter().find_map(|e|tpak.read(e).ok().filter(|b|movie_manifest::is_manifest(b))).unwrap(); let lpak=PakArchive::open(format!("{disc}/dat/movie/eng.pak")).unwrap(); let reg=tpak.entries().iter().find_map(|e|tpak.read(e).ok().filter(|b|ct(b,b"eng\\Movie\\VOICE_ADV.slb"))).unwrap(); let ids=movie_voice::registry_voice_ids(®); let stoc=fs::read(format!("{disc}/dat/sound.pak")).unwrap(); let ents=PakArchive::parse_toc(&stoc).unwrap(); // demo->token from bound hokyu let hok:Vec<_>=movie_manifest::parse(&man).into_iter().filter(|m|m.movie.starts_with("hokyu_")).collect(); let resolve=|movie:&str|->Option{ movie_manifest::voice_token(&man,movie).or_else(||{ let want=ms::track_voice_cues(&lpak,movie).first().map(|&(d,_)|d)?; hok.iter().find_map(|e|{let t=e.voice_token.clone().filter(|_|e.movie.starts_with("hokyu_"))?; ms::track_voice_cues(&lpak,&e.movie).iter().any(|&(d,_)|d==want).then_some(t)}) }) }; for e in &hok{ let mv=&e.movie; let bound=e.voice_token.is_some(); let tok=resolve(mv); let demo=ms::track_voice_cues(&lpak,mv).first().map(|&(d,_)|d); let mut d="—".to_string(); if let Some(tok)=&tok{ if let Some(&id)=ids.get(tok){ if let Some(anchor)=["Movie","etc","Voice"].iter().find_map(|dir|{let h=name_hash(&format!("eng\\{dir}\\{tok}.slb"));ents.binary_search_by_key(&h,|x|x.name_hash).ok().map(|i|ents[i].offset as u64)}){ let ws=(anchor.saturating_sub(2*1024*1024))&!3; let win=rg(&disc,ws,8*1024*1024); if let Some(el)=movie_voice::find_descriptor(&win,id){let end=ws+el as u64; let start=movie_voice::find_descriptor(&win,id.wrapping_sub(1)).or_else(||movie_voice::find_descriptor_before(&win,el)).map(|o|ws+o as u64).filter(|&s|s {:11} voice={d}s", if bound{"BOUND"}else{"unbound"}, demo, tok.unwrap_or("(silent)".into())); } }