1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use std::{fmt, hash};
use bytemuck::{Zeroable, Pod};
use crate::bin::concat_bytes;
use crate::bin::{derive_readable_via_pod, derive_writable_via_pod};
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Zeroable, Pod)]
pub struct Hash {
low: u32,
high: u32,
}
derive_var_size_via_size_of!(Hash);
derive_readable_via_pod!(Hash);
derive_writable_via_pod!(Hash);
impl Hash {
pub fn v001<S>(s: S) -> Self
where S: AsRef<str> {
let path = sanitize(s);
let bytes = path.as_bytes();
let mid_point = bytes.len() >> 1;
Self {
low: (mid_point .. bytes.len())
.fold(0, |low, i| {
let temp = (bytes[i] as u32) << (((i - mid_point) & 3) << 3);
rot_right(low ^ temp, temp & 0x1F)
}),
high: concat_bytes({
let mut high: [u8; 4] = [0; 4];
for i in 0 .. mid_point {
high[i & 3] ^= bytes[i];
}
high
}),
}
}
pub fn v10x<S>(s: S) -> Self
where S: AsRef<str> {
let path = sanitize(s);
let (root, ext) = hash_v10x_parts(path.as_bytes());
Self {
low: concat_bytes([
root[root.len() - 1],
when(root.len() > 2, || root[root.len() - 2]),
root.len() as u8,
root[0],
]) | match &*ext {
b".nif" => 0x00008000,
b".kf" => 0x00000080,
b".dds" => 0x00008080,
b".wav" => 0x80000000,
_ => 0x00000000,
},
high: when(root.len() > 2, || hash_sdbm(&root[1 .. root.len() - 2]))
.wrapping_add(hash_sdbm(ext)),
}
}
}
impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:08x}{:08x}", self.low, self.high)
}
}
impl hash::Hash for Hash {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
state.write_u32(self.low);
state.write_u32(self.high);
}
}
fn sanitize<S>(path: S) -> String
where S: AsRef<str> {
path.as_ref()
.to_lowercase()
.replace('/', "\\")
}
fn hash_v10x_parts(bytes: &[u8]) -> (&[u8], &[u8]) {
for (i, c) in (0 .. bytes.len()).zip(bytes).rev() {
match *c as char {
'\\' => break,
'.' => return (&bytes[0 .. i], &bytes[i .. bytes.len()]),
_ => (),
}
}
(bytes, &[])
}
fn hash_sdbm(bytes: &[u8]) -> u32 {
bytes.into_iter()
.fold(0, |hash, c| hash.wrapping_mul(0x01003f) + *c as u32)
}
fn rot_right(value: u32, num_bits: u32) -> u32 {
value.wrapping_shl(32 - num_bits) | value. wrapping_shl(num_bits)
}
fn when<T: Default, F>(cond: bool, v: F) -> T
where F: FnOnce() -> T {
if cond { v() } else { T::default() }
}