Skip to main content

mint_cli/output/
checksum.rs

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