use sylpheed_formats::PakArchive; use std::collections::BTreeMap; fn be32(b:&[u8],o:usize)->u32{ if o+4<=b.len(){u32::from_be_bytes([b[o],b[o+1],b[o+2],b[o+3]])}else{0} } fn magic(b:&[u8])->String{ if b.len()<4 {return "(<4B)".into();} let m=&b[0..4]; if m.iter().all(|&c|(0x20..0x7f).contains(&c)){ String::from_utf8_lossy(m).into() } else if m==b"\x89PNG"{"PNG".into()} else if m==[0,1,0,0]{"ttf".into()} else { format!("{:02x}{:02x}{:02x}{:02x}",m[0],m[1],m[2],m[3]) } } fn main(){ let disc=std::env::var("SYLPHEED_DISC").unwrap(); // Known/understood IDXD schemas (semantic parsers we have) let known_schema:BTreeMap=[(0x067025b9,"ADVERTISE_MOVIE (movie_manifest)"),(0x13cb84ba,"sound registry / sounds.tbl")].into(); let mut fmt_count:BTreeMap=BTreeMap::new(); // fmt -> (count, bytes) let mut schema_census:BTreeMap=BTreeMap::new(); // schema -> (count,bytes,sample pak) let mut unknown_magics:BTreeMap)>=BTreeMap::new(); let paks:Vec={let mut v=vec![]; for e in std::fs::read_dir(format!("{disc}/dat")).unwrap(){let p=e.unwrap().path(); if p.extension().map(|x|x=="pak").unwrap_or(false){v.push(p.file_stem().unwrap().to_string_lossy().into());}} v.sort(); v}; println!("pak entries decompressed formats"); for pk in &paks{ let Ok(arc)=PakArchive::open(format!("{disc}/dat/{pk}.pak")) else{continue}; let mut per:BTreeMap=BTreeMap::new(); let mut tot=0u64; let n=arc.entries().len(); for e in arc.entries(){ let Ok(b)=arc.read(e) else{continue}; tot+=b.len() as u64; let mut f=magic(&b); if f=="IDXD"{ let s=be32(&b,8); schema_census.entry(s).or_insert((0,0,pk.clone())).0+=1; schema_census.get_mut(&s).unwrap().1+=b.len() as u64; f=format!("IDXD:{s:08x}"); } else if !["XPR2","IXUD","LSTA","RATC","RIFF","XBG7","OTTO","PNG","ttf","DDS ","T8AD"].contains(&f.as_str()){ let ent=unknown_magics.entry(f.clone()).or_insert((0,vec![])); ent.0+=1; if ent.1.len()<3 && !ent.1.contains(pk){ent.1.push(pk.clone());} } *per.entry(if f.starts_with("IDXD:"){"IDXD".into()}else{f.clone()}).or_default()+=1; let g=fmt_count.entry(if f.starts_with("IDXD:"){"IDXD".into()}else{f}).or_insert((0,0)); g.0+=1; g.1+=b.len() as u64; } let mut fs:Vec<_>=per.into_iter().collect(); fs.sort_by_key(|x|std::cmp::Reverse(x.1)); let top:String=fs.iter().take(4).map(|(k,v)|format!("{k}×{v}")).collect::>().join(" "); println!("{pk:26} {n:>7} {:>10}KB {top}", tot/1024); } println!("\n=== FORMAT TOTALS (across all paks) ==="); let mut fv:Vec<_>=fmt_count.into_iter().collect(); fv.sort_by_key(|x|std::cmp::Reverse(x.1.1)); for (f,(c,b)) in &fv{ println!(" {f:12} {c:>6} entries {:>8}KB", b/1024); } println!("\n=== IDXD SCHEMA CENSUS ({} distinct schemas) ===", schema_census.len()); let mut sv:Vec<_>=schema_census.into_iter().collect(); sv.sort_by_key(|x|std::cmp::Reverse(x.1.1)); for (s,(c,b,pk)) in &sv{ let tag=known_schema.get(s).copied().unwrap_or("??? UNDECODED"); println!(" {s:08x} {c:>5} ent {:>7}KB e.g. {pk:22} {tag}", b/1024); } println!("\n=== UNKNOWN / UNCLASSIFIED MAGICS ==="); for (m,(c,pks)) in &unknown_magics{ println!(" {m:12} ×{c:<5} in {pks:?}"); } }