use mercs2_engine::wad;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let targets: Vec<u32> = args
.iter()
.filter_map(|a| a.strip_prefix("0x").and_then(|h| u32::from_str_radix(h, 16).ok()))
.collect();
if targets.is_empty() {
eprintln!("usage: clipbones 0xHASH [0xHASH ...]");
std::process::exit(2);
}
let path = wad::resolve_vz_wad(None).unwrap_or_else(|| {
eprintln!("no vz.wad found — set VZ_WAD to the install folder, its data folder, or the file");
std::process::exit(1)
});
let mut w = wad::open(&path).expect("open vz.wad");
if let Some(i) = std::env::args().position(|a| a == "--order") {
let clip = std::env::args()
.nth(i + 1)
.and_then(|s| s.strip_prefix("0x").and_then(|h| u32::from_str_radix(h, 16).ok()))
.expect("--order 0xCLIPHASH");
for blk in wad::animgroup_blocks(&w) {
let Ok(data) = wad::decompress_block_index(&mut w, blk) else { continue };
let Ok(ag) = mercs2_formats::animgroup::parse_animgroup(&data) else { continue };
let Some(c) = ag.clips.iter().find(|c| c.name_hash == clip) else { continue };
let all: std::collections::BTreeSet<u32> =
c.binding.track_to_bone_hash.iter().copied().collect();
let nm = mercs2_engine::worldutil::rainbow_names(&all);
println!("clip 0x{clip:08X} in block {blk}: {} tracks", c.binding.track_to_bone_hash.len());
for (t, h) in c.binding.track_to_bone_hash.iter().enumerate() {
let n = nm.get(h).map(|s| s.as_str()).unwrap_or("******** UNNAMED ********");
println!(" track {t:>3} 0x{h:08X} {n}");
}
return;
}
eprintln!("clip 0x{clip:08X} not found");
return;
}
let names = {
let set: std::collections::BTreeSet<u32> = targets.iter().copied().collect();
mercs2_engine::worldutil::rainbow_names(&set)
};
for blk in wad::animgroup_blocks(&w) {
let Ok(data) = wad::decompress_block_index(&mut w, blk) else { continue };
let Ok(ag) = mercs2_formats::animgroup::parse_animgroup(&data) else { continue };
let mut any = false;
for c in &ag.clips {
let hit: Vec<u32> = targets
.iter()
.copied()
.filter(|t| c.binding.track_to_bone_hash.contains(t))
.collect();
if hit.is_empty() {
continue;
}
if !any {
let p = wad::block_paths(&w).get(blk as usize).cloned().unwrap_or_default();
println!("\n=== block {blk} {p}");
any = true;
}
let clip_set: std::collections::BTreeSet<u32> = std::iter::once(c.name_hash).collect();
let cn = mercs2_engine::worldutil::rainbow_names(&clip_set);
let cname = cn.get(&c.name_hash).cloned().unwrap_or_else(|| "?".into());
println!(
" clip 0x{:08X} {:<34} tracks={:<4} class={:?} binds {} of the targets:",
c.name_hash,
cname,
c.num_transform_tracks,
c.class,
hit.len()
);
for t in hit {
let n = names.get(&t).map(|s| s.as_str()).unwrap_or("** UNNAMED **");
println!(" 0x{t:08X} {n}");
}
}
}
}