Skip to main content

banished_formats/
crc.rs

1//! Standard CRC-32 (IEEE), as used by `System::CRC` and for type ids.
2
3const fn table() -> [u32; 256] {
4    let mut t = [0u32; 256];
5    let mut i = 0;
6    while i < 256 {
7        let mut c = i as u32;
8        let mut k = 0;
9        while k < 8 {
10            c = if c & 1 != 0 {
11                0xEDB8_8320 ^ (c >> 1)
12            } else {
13                c >> 1
14            };
15            k += 1;
16        }
17        t[i] = c;
18        i += 1;
19    }
20    t
21}
22
23static TABLE: [u32; 256] = table();
24
25/// The CRC-32 (IEEE) of bytes.
26pub fn crc32(bytes: impl IntoIterator<Item = u8>) -> u32 {
27    !bytes.into_iter().fold(!0u32, |c, b| {
28        TABLE[((c ^ b as u32) & 0xFF) as usize] ^ (c >> 8)
29    })
30}
31
32/// The table loop alone, from `c` and without the final complement (the engine's
33/// `System::CRC` steps; [`crc32`] is `!crc32_raw(!0, ..)`).
34pub fn crc32_raw(c: u32, bytes: &[u8]) -> u32 {
35    bytes.iter().fold(c, |c, &b| {
36        TABLE[((c ^ b as u32) & 0xFF) as usize] ^ (c >> 8)
37    })
38}
39
40/// Resource type id: CRC-32 over the class name as UTF-16LE (`Object::Factory::Startup`).
41pub fn type_id(class_name: &str) -> u32 {
42    crc32(class_name.encode_utf16().flat_map(u16::to_le_bytes))
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn known_vectors() {
51        assert_eq!(crc32(*b"123456789"), 0xCBF4_3926);
52        assert_eq!(type_id("ImageBuffer"), 0x84E6_F2DD);
53        assert_eq!(type_id("GraphicsMesh"), 0xE61C_2FFE);
54        assert_eq!(type_id("MaterialInstance"), 0x2EBE_ED54);
55    }
56}