use include_flate::flate;
use roead::yaz0::decompress;
use std::collections::HashMap;
use std::hash::Hasher;
use twox_hash::XxHash64;
flate!(static HASHES_U: str from "data/wiiu_hashes.json");
flate!(static HASHES_NX: str from "data/switch_hashes.json");
pub type HashTable = HashMap<&'static str, Vec<u64>>;
#[derive(Debug, Eq, PartialEq)]
pub enum Platform {
WiiU,
Switch,
}
#[inline]
pub fn get_hash_table(platform: &Platform) -> HashTable {
match platform {
Platform::WiiU => serde_json::from_str(HASHES_U.as_ref()).unwrap(),
Platform::Switch => serde_json::from_str(HASHES_NX.as_ref()).unwrap(),
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct StockHashTable {
table: HashTable,
}
impl StockHashTable {
#[inline]
pub fn new(platform: &Platform) -> StockHashTable {
StockHashTable {
table: get_hash_table(platform),
}
}
#[inline]
pub fn get_stock_files(&self) -> impl Iterator<Item = &&str> {
self.table.keys()
}
#[inline]
pub fn list_stock_files(&self) -> Vec<String> {
self.table.keys().map(|x| x.to_owned().to_owned()).collect()
}
pub fn is_file_modded<S: AsRef<str>, D: AsRef<[u8]>>(
&self,
file_name: S,
data: D,
flag_new: bool,
) -> bool {
if self.table.contains_key(file_name.as_ref()) {
let data = data.as_ref();
let mut hasher = XxHash64::with_seed(0);
if &data[0..4] == b"Yaz0" {
match decompress(data) {
Ok(data) => hasher.write(&data),
Err(_) => return true,
}
} else {
hasher.write(data);
}
let hash: u64 = hasher.finish();
!self.table[file_name.as_ref()].contains(&hash)
} else {
flag_new
}
}
#[inline]
pub fn is_file_new<S: AsRef<str>>(&self, file_name: S) -> bool {
!self.table.contains_key(file_name.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cst_hash_table() {
get_hash_table(&Platform::WiiU);
get_hash_table(&Platform::Switch);
}
#[test]
fn check_val() {
let table = get_hash_table(&Platform::WiiU);
assert_eq!(
table
.get("Actor/ModelList/DgnMrgPrt_Dungeon023.bmodellist")
.unwrap(),
&vec![3_305_211_212_481_695_363_u64, 6_042_644_272_755_124_234_u64]
)
}
#[test]
fn is_file_modded() {
let tbl = StockHashTable::new(&Platform::Switch);
assert!(tbl.is_file_modded(
"Actor/Physics/FldObj_MountainSheikerWall_A_06.bphysics",
b"Random data",
true
))
}
#[test]
fn print_files() {
let tbl = StockHashTable::new(&Platform::WiiU);
for file in tbl.get_stock_files() {
println!("{}", file)
}
}
}