use super::error::Ps2mcError;
const fn calculate_parity(mut byte: u8) -> u8 {
byte ^= byte >> 1;
byte ^= byte >> 2;
byte ^= byte >> 4;
byte & 1
}
const PARITY_TABLE: [u8; 256] = {
let mut table = [0; 256];
let mut b = 0;
while b < 256 {
table[b] = calculate_parity(b as u8);
b += 1;
}
table
};
const COLUMN_PARITY_MASKS: [u8; 256] = {
let mut table = [0; 256];
let cpmasks = [0x55, 0x33, 0x0F, 0x00, 0xAA, 0xCC, 0xF0];
let mut b = 0;
while b < 256 {
let mut mask = 0;
let mut i = 0;
while i < 7 {
let bit = calculate_parity((b as u8) & cpmasks[i]);
mask |= bit << i;
i += 1;
}
table[b] = mask;
b += 1;
}
table
};
pub fn calculate(data: &[u8]) -> Result<[u8; 3], Ps2mcError> {
if data.len() != 128 {
return Err(Ps2mcError::UnexpectedEof {
context: "ecc block",
expected: 128,
actual: data.len(),
});
}
let mut cp = 0x77;
let mut lp0 = 0x7F;
let mut lp1 = 0x7F;
for (i, &b) in data.iter().enumerate() {
let idx = b as usize;
cp ^= COLUMN_PARITY_MASKS[idx];
if PARITY_TABLE[idx] == 1 {
lp0 ^= !(i as u8);
lp1 ^= i as u8;
}
}
Ok([cp, lp0 & 0x7F, lp1 & 0x7F])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ecc_empty_block() {
let data = [0u8; 128];
let ecc = calculate(&data).unwrap();
assert_eq!(ecc, [0x77, 0x7F, 0x7F]);
}
}