touhou-reimpl/examples/inst_search.rs

104 lines
2.7 KiB
Rust

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<u16>,
) {
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<_>>();
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);
}