physis/
crc.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use libz_rs_sys::z_off_t;
5use std::ops::{Add, AddAssign, BitXor, BitXorAssign};
6
7/// CRC used for filepath hashes in index file
8pub(crate) struct Jamcrc {
9    table: [u32; 256],
10}
11
12impl Jamcrc {
13    pub(crate) const fn new() -> Self {
14        let mut table: [u32; 256] = [0u32; 256];
15
16        let polynomial: u32 = 0xEDB88320;
17        let mut i = 0;
18        while i < table.len() {
19            let mut c: u32 = i as u32;
20            let mut j = 0;
21            while j < 8 {
22                if (c & 1u32) == 1u32 {
23                    c = polynomial ^ (c >> 1);
24                } else {
25                    c >>= 1;
26                }
27                j += 1;
28            }
29
30            table[i] = c;
31            i += 1;
32        }
33
34        Self { table }
35    }
36
37    pub(crate) fn checksum(&self, bytes: &[u8]) -> u32 {
38        let mut c: u32 = 0xFFFFFFFF;
39        for byte in bytes {
40            c = self.table[((c ^ *byte as u32) & 0xFF) as usize] ^ (c >> 8);
41        }
42
43        !(c ^ 0xFFFFFFFF)
44    }
45}
46
47fn crc32(crc: u32, s: &[u8]) -> u32 {
48    unsafe { libz_rs_sys::crc32(crc.into(), s.as_ptr(), s.len() as u32) as u32 }
49}
50
51fn crc32_combine(crc1: u32, crc2: u32, len2: usize) -> u32 {
52    libz_rs_sys::crc32_combine(crc1.into(), crc2.into(), len2 as z_off_t) as u32
53}
54
55/// CRC used for shader keys
56/// Credit to https://github.com/NotNite/crcracker/ for the original Rust code
57#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
58pub(crate) struct XivCrc32 {
59    pub crc: u32,
60    pub len: usize,
61}
62
63impl XivCrc32 {
64    pub(crate) fn new(crc: u32, len: usize) -> Self {
65        Self { crc, len }
66    }
67}
68
69impl From<&[u8]> for XivCrc32 {
70    fn from(s: &[u8]) -> Self {
71        Self::new(!crc32(0xFFFFFFFF, s), s.len())
72    }
73}
74
75impl<const N: usize> From<&[u8; N]> for XivCrc32 {
76    fn from(s: &[u8; N]) -> Self {
77        Self::new(!crc32(0xFFFFFFFF, s), N)
78    }
79}
80
81impl From<&str> for XivCrc32 {
82    fn from(s: &str) -> Self {
83        Self::from(s.as_bytes())
84    }
85}
86
87impl Add<XivCrc32> for XivCrc32 {
88    type Output = XivCrc32;
89
90    fn add(self, rhs: XivCrc32) -> Self::Output {
91        Self::new(
92            crc32_combine(self.crc, rhs.crc, rhs.len),
93            self.len + rhs.len,
94        )
95    }
96}
97
98impl AddAssign<XivCrc32> for XivCrc32 {
99    fn add_assign(&mut self, rhs: XivCrc32) {
100        self.crc = crc32_combine(self.crc, rhs.crc, rhs.len);
101        self.len += rhs.len;
102    }
103}
104
105impl BitXor<XivCrc32> for XivCrc32 {
106    type Output = XivCrc32;
107
108    fn bitxor(self, rhs: XivCrc32) -> Self::Output {
109        Self::new(self.crc ^ rhs.crc, self.len.max(rhs.len))
110    }
111}
112
113impl BitXorAssign<XivCrc32> for XivCrc32 {
114    fn bitxor_assign(&mut self, rhs: XivCrc32) {
115        self.crc ^= rhs.crc;
116        self.len = self.len.max(rhs.len);
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn check_jamcrc() {
126        const CRC: Jamcrc = Jamcrc::new();
127        let bytes: [u8; 9] = [1, 1, 2, 4, 5, 6, 12, 12, 12];
128
129        assert_eq!(2411431516, CRC.checksum(&bytes))
130    }
131
132    #[test]
133    fn check_xivcrc() {
134        let str = "Default";
135
136        assert_eq!(XivCrc32::from(str).crc, 2978997821);
137    }
138}