use memmap2::Mmap; use once_cell::sync::Lazy; use rayon::prelude::*; use regex::Regex; use serde_json::json; use std::{ collections::BTreeMap, env, fs, fs::File, path::{Path, PathBuf}, }; use walkdir::WalkDir; use xxhash_rust::xxh3::xxh3_64; static RE: Lazy = Lazy::new(|| { Regex::new(r"(metin2\.exe|granny2\.dll|SpeedTreeRT\.dll|BGM[/\\].*|lib[/\\].*|pack[/\\].*)") .unwrap() }); fn hash_file(path: &Path, base_path: &String) -> (String, String) { let file = File::open(path).expect("Failed to open file"); let mmap = unsafe { Mmap::map(&file).expect("Failed to mmap file") }; let hash = xxh3_64(&mmap); let hash_str = hash.to_string(); let filename = path.to_str().unwrap().to_string().replace(base_path, ""); (filename, hash_str) } fn collect_files(folder: &Path) -> Vec { WalkDir::new(folder) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.path().is_file() && RE.is_match(e.path().to_str().unwrap())) .map(|e| e.path().to_path_buf()) .collect() } fn main() -> std::io::Result<()> { use std::time::Instant; let now = Instant::now(); let args: Vec = env::args().collect(); if args.len() != 2 { eprintln!("Usage: {} ", args[0]); std::process::exit(1); } let folder = Path::new(&args[1]); if !folder.is_dir() { eprintln!("Error: '{}' is not a directory", folder.display()); std::process::exit(1); } let files = collect_files(folder); let results: BTreeMap = files.par_iter().map(|f| hash_file(f, &args[1])).collect(); let json_output = json!({ "checksums": results }); fs::write("checksum.json", serde_json::to_string_pretty(&json_output)?)?; let elapsed = now.elapsed(); println!("Generated 'checksum', elapsed {:.2?}", elapsed); Ok(()) }