1#![no_std]
20#![deny(
21 warnings,
22 future_incompatible,
23 unused,
24 unsafe_code,
25 clippy::all,
26 clippy::cargo,
27 clippy::pedantic,
28 rustdoc::all
29)]
30#![allow(clippy::cast_lossless, clippy::must_use_candidate)]
31
32#[inline]
33pub fn calculate(input: &[u8]) -> (u32, u32) {
41 const MASK: u32 = 0b1_1111;
42 let (left, right) = input.split_at(input.len() >> 1);
43 let (mut a, mut b, mut shift) = (0, 0, 0);
44 for &n in left {
45 a ^= u32::from(n) << shift;
46 shift = (shift + 8) & MASK;
47 }
48 shift = 0;
49 for &n in right {
50 let temp = u32::from(n) << shift;
51 b = (b ^ temp).rotate_right(temp & MASK);
52 shift = (shift + 8) & MASK;
53 }
54 (a, b)
55}
56
57#[cfg(test)]
58mod tests {
59 include!("../bsa_lists/morrowind.rs");
60 include!("../bsa_lists/tribunal.rs");
61 include!("../bsa_lists/bloodmoon.rs");
62
63 #[inline]
64 fn test_hashes(list: &[(&str, u32, u32)]) {
65 for &(filename, left_hash, right_hash) in list {
66 assert_eq!(
67 crate::calculate(filename.as_bytes()),
68 (left_hash, right_hash)
69 );
70 }
71 }
72
73 #[test]
74 fn morrowind_bsa() {
75 test_hashes(MORROWIND_BSA);
76 }
77
78 #[test]
79 fn tribunal_bsa() {
80 test_hashes(TRIBUNAL_BSA);
81 }
82
83 #[test]
84 fn bloodmoon_bsa() {
85 test_hashes(BLOODMOON_BSA);
86 }
87}