hash-gen/src/main.rs

67 lines
1.9 KiB
Rust

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<Regex> = 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<PathBuf> {
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<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <folder>", 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<String, String> =
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(())
}