use std::{ collections::HashMap, fs::{read_dir, read_to_string, File}, }; use truth::{context::RootEmitter, io::BinReader, AnmFile, Game, Mapfile, StdFile}; fn parse_files( game_folder: &str, game: Game, map_file_name: &str, ext: &str, csv_path: &str, hit_file: impl Fn(&mut BinReader) -> Vec, ) { let map_file = Mapfile::load(map_file_name, Some(game), &RootEmitter::new_stderr(), |_| { Ok((None, read_to_string(map_file_name).unwrap())) }) .unwrap(); let dir = read_dir(format!("assets/{game_folder}")).unwrap(); let mut map = HashMap::new(); for file in dir { let file = file.unwrap(); if file.path().extension().unwrap_or_default() != ext { continue; } log::info!("file: {file:?}"); for op in hit_file(&mut BinReader::from_reader( &RootEmitter::new_stderr(), &file.file_name().to_string_lossy().to_owned(), &File::open(file.path()).unwrap(), )) { *map.entry(op).or_insert(0) += 1; } } let mut csv = csv::Writer::from_path(csv_path).unwrap(); let mut vec = map.into_iter().collect::>(); vec.sort_by_key(|c| c.0); for (op, count) in vec { log::info!("hitting op {op}"); let str_name = map_file .ins_names .iter() .find(|i| i.0 == op as i32) .map(|v| v.1.value.as_str().to_owned()) .unwrap_or(format!("unk_inst_{op}")); csv.write_field(str_name).unwrap(); csv.write_field(op.to_string()).unwrap(); csv.write_field(count.to_string()).unwrap(); csv.write_record(None::<&[u8]>).unwrap(); } csv.flush().unwrap(); } fn parse_game(game_folder: &str, game: Game) { let thver = format!("th{}", game.as_number()); parse_files( game_folder, game, &format!("utils/{thver}.eclm"), "ecl", &format!("target/{game_folder}_ecl_inst.csv"), |reader| { let file = truth::StackEclFile::read_from_stream(reader, game).unwrap(); file.subs.values().flat_map(|sub| sub.iter().map(|f| f.opcode)).collect() }, ); parse_files( game_folder, game, &format!("utils/{thver}.stdm"), "std", &format!("target/{game_folder}_std_inst.csv"), |reader| { let file = StdFile::read_from_stream(reader, game).unwrap(); file.script.into_iter().map(|f| f.opcode).collect() }, ); parse_files( game_folder, game, &format!("utils/{thver}.anmm"), "anm", &format!("target/{game_folder}_anm_inst.csv"), |reader| { let file = AnmFile::read_from_stream(reader, game, false).unwrap(); file.entries.iter().flat_map(|f| f.scripts.values()).flat_map(|f| f.instrs.iter()).map(|f| f.opcode).collect() }, ); } fn main() { env_logger::init(); parse_game("sa", Game::Th11); parse_game("mof", Game::Th10); }