mint_cli/output/
checksum.rs

1use crate::layout::settings::CrcData;
2
3/// Hand-rolled CRC32 calculation matching the crc crate's NoTable implementation.
4/// This removes the need for static state and allows each block to use its own CRC settings.
5pub fn calculate_crc(data: &[u8], crc_settings: &CrcData) -> u32 {
6    // Initialize CRC based on ref_in
7    let mut crc = if crc_settings.ref_in {
8        crc_settings.start.reverse_bits()
9    } else {
10        crc_settings.start
11    };
12
13    // Prepare polynomial
14    let poly = if crc_settings.ref_in {
15        crc_settings.polynomial.reverse_bits()
16    } else {
17        crc_settings.polynomial
18    };
19
20    // Process each byte
21    for &byte in data {
22        let idx = if crc_settings.ref_in {
23            (crc ^ (byte as u32)) & 0xFF
24        } else {
25            ((crc >> 24) ^ (byte as u32)) & 0xFF
26        };
27
28        // Perform 8 rounds of bitwise CRC calculation
29        let mut step = if crc_settings.ref_in { idx } else { idx << 24 };
30        if crc_settings.ref_in {
31            for _ in 0..8 {
32                step = (step >> 1) ^ ((step & 1) * poly);
33            }
34        } else {
35            for _ in 0..8 {
36                step = (step << 1) ^ (((step >> 31) & 1) * poly);
37            }
38        }
39
40        crc = if crc_settings.ref_in {
41            step ^ (crc >> 8)
42        } else {
43            step ^ (crc << 8)
44        };
45    }
46
47    // Finalize
48    if crc_settings.ref_in ^ crc_settings.ref_out {
49        crc = crc.reverse_bits();
50    }
51
52    crc ^ crc_settings.xor_out
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use crate::layout::settings::CrcArea;
59
60    // Verify our CRC32 implementation against the well-known test vector
61    // This tests the standard CRC32 settings used in all project examples
62    #[test]
63    fn test_crc32_standard_test_vector() {
64        let crc_settings = CrcData {
65            polynomial: 0x04C11DB7,
66            start: 0xFFFF_FFFF,
67            xor_out: 0xFFFF_FFFF,
68            ref_in: true,
69            ref_out: true,
70            area: CrcArea::Data,
71        };
72
73        // The standard CRC32 test vector - "123456789" should produce 0xCBF43926
74        // This is the well-known test vector for CRC-32 (used in ZIP, PNG, etc.)
75        let test_str = b"123456789";
76        let result = calculate_crc(test_str, &crc_settings);
77        assert_eq!(
78            result, 0xCBF43926,
79            "Standard CRC32 test vector failed (expected 0xCBF43926 for \"123456789\")"
80        );
81
82        // Test with simple data to ensure the implementation is stable
83        let simple_data = vec![0x01, 0x02, 0x03, 0x04];
84        let simple_result = calculate_crc(&simple_data, &crc_settings);
85        assert_eq!(simple_result, 0xB63CFBCD, "CRC32 for [1,2,3,4] failed");
86    }
87
88    #[test]
89    fn test_crc32_mpeg2_non_reflected_vector() {
90        let crc_settings = CrcData {
91            polynomial: 0x04C11DB7,
92            start: 0xFFFF_FFFF,
93            xor_out: 0x0000_0000,
94            ref_in: false,
95            ref_out: false,
96            area: CrcArea::Data,
97        };
98
99        // CRC-32/MPEG-2 parameters (non-reflected) over "123456789" should produce 0x0376E6E7
100        let test_str = b"123456789";
101        let result = calculate_crc(test_str, &crc_settings);
102        assert_eq!(
103            result, 0x0376E6E7,
104            "CRC32/MPEG-2 test vector failed (expected 0x0376E6E7 for \"123456789\")"
105        );
106    }
107}