use bitflags::bitflags;
use thiserror::Error;
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SectorType {
Unknown = 0,
Cdda, CddaGap, Mode1,
Mode1Gap,
Mode1Raw, Mode2, Mode2Gap,
Mode2XaGap,
Mode2Xa1,
Mode2Xa1Gap,
Mode2Xa2,
Mode2Xa2Gap,
}
impl From<u8> for SectorType {
fn from(value: u8) -> Self {
match value {
0 => SectorType::Unknown,
1 => SectorType::Cdda,
2 => SectorType::CddaGap,
3 => SectorType::Mode1,
4 => SectorType::Mode1Gap,
5 => SectorType::Mode1Raw,
6 => SectorType::Mode2,
7 => SectorType::Mode2Gap,
8 => SectorType::Mode2XaGap,
9 => SectorType::Mode2Xa1,
10 => SectorType::Mode2Xa1Gap,
11 => SectorType::Mode2Xa2,
12 => SectorType::Mode2Xa2Gap,
_ => SectorType::Unknown, }
}
}
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Optimizations: u8 {
const None = 0b0000_0000;
const RemoveSync = 0b0000_0001;
const RemoveMSF = 0b0000_0010;
const RemoveMode = 0b0000_0100;
const RemoveBlanks = 0b0000_1000;
const RemoveRedundantFlag = 0b0001_0000;
const RemoveECC = 0b0010_0000;
const RemoveEDC = 0b0100_0000;
const RemoveGap = 0b1000_0000;
}
}
#[derive(Debug, Error)]
pub enum StatusError {
#[error("Not enough sector data to be able to process it.")]
NotEnoughSectorData,
#[error("Unknown format detected: {0}")]
UnknownFormat(String),
}
pub struct MSF;
impl MSF {
fn to_bcd(value: u8) -> u8 {
assert!(value < 100, "BCD sólo soporta valores 0..=99");
((value / 10) << 4) | (value % 10)
}
pub fn sectors_to_msf(sector_number: u32) -> [u8; 3] {
let frames: u8 = (sector_number % 75) as u8;
let seconds: u8 = ((sector_number / 75) % 60) as u8;
let minutes: u8 = (sector_number / 75 / 60) as u8;
[
MSF::to_bcd(minutes),
MSF::to_bcd(seconds),
MSF::to_bcd(frames),
]
}
}
pub struct EccEdc {
ecc_f_lut: [u8; 256],
ecc_b_lut: [u8; 256],
edc_lut: [u32; 256],
}
impl EccEdc {
pub fn new() -> Self {
let mut ecc_f_lut = [0u8; 256];
let mut ecc_b_lut = [0u8; 256];
let mut edc_lut = [0u32; 256];
for i in 0..256 {
let mut edc = i as u32;
let j = ((i << 1) ^ if (i & 0x80) != 0 { 0x11D } else { 0 }) & 0xFF;
ecc_f_lut[i] = j as u8;
ecc_b_lut[i ^ j] = i as u8;
for _ in 0..8 {
edc = (edc >> 1) ^ if (edc & 1) != 0 { 0xD8018001 } else { 0 };
}
edc_lut[i] = edc;
}
Self {
ecc_f_lut,
ecc_b_lut,
edc_lut,
}
}
pub fn generate_edc(&self, data: &[u8]) -> u32 {
let mut edc = 0;
for &byte in data {
edc = (edc >> 8) ^ self.edc_lut[((edc ^ byte as u32) & 0xFF) as usize];
}
edc
}
pub fn generate_ecc_pq(
&self,
address: &[u8],
data: &[u8],
ecc: &mut [u8],
major_count: usize,
minor_count: usize,
major_mult: usize,
minor_inc: usize,
) {
let size = major_count * minor_count;
for major in 0..major_count {
let mut index = (major >> 1) * major_mult + (major & 1);
let mut ecc_a = 0u8;
let mut ecc_b = 0u8;
for _ in 0..minor_count {
let temp = if index < 4 {
address[index]
} else {
data[index - 4]
};
index += minor_inc;
if index >= size {
index -= size;
}
ecc_b ^= temp;
ecc_a = self.ecc_f_lut[(ecc_a ^ temp) as usize];
}
ecc_a = self.ecc_b_lut[(self.ecc_f_lut[ecc_a as usize] ^ ecc_b) as usize];
ecc[major] = ecc_a;
ecc[major + major_count] = ecc_a ^ ecc_b;
}
}
pub fn check_ecc(&self, address: &[u8], data: &[u8], ecc: &[u8]) -> bool {
let mut generated_ecc = [0u8; 276];
self.generate_ecc_pq(address, data, &mut generated_ecc[..0xAC], 86, 24, 2, 86);
self.generate_ecc_pq(address, data, &mut generated_ecc[0xAC..], 52, 43, 86, 88);
generated_ecc == ecc
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path::Path;
#[test]
fn generate_edc() {
let path = Path::new("tests/data/mode2_xa1.bin");
let sector = fs::read(path).expect("Cannot open test sector.");
assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");
let edc_ecc_calculator = EccEdc::new();
let original_edc = §or[0x818..0x81C];
let generated_edc: u32 = edc_ecc_calculator.generate_edc(§or[0x10..0x818]);
assert_eq!(
generated_edc.to_le_bytes(),
original_edc,
"Testing matching generated EDC."
);
}
#[test]
fn generate_ecc() {
let path = Path::new("tests/data/mode2_xa1.bin");
let sector = fs::read(path).expect("Cannot open test sector.");
assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");
let edc_ecc_calculator = EccEdc::new();
let address: [u8; 4] = [0, 0, 0, 0];
let mut generated_ecc_p: [u8; 172] = [0; 172];
let mut generated_ecc_q: [u8; 104] = [0; 104];
edc_ecc_calculator.generate_ecc_pq(
&address,
§or[0x10..],
&mut generated_ecc_p[..],
86,
24,
2,
86,
);
assert_eq!(
generated_ecc_p,
sector[0x81C..0x8C8],
"Testing matching ECC P generated data."
);
edc_ecc_calculator.generate_ecc_pq(
&address,
§or[0x10..],
&mut generated_ecc_q[..],
52,
43,
86,
88,
);
assert_eq!(
generated_ecc_q,
sector[0x8C8..0x930],
"Testing matching ECC Q generated data."
);
}
#[test]
fn check_ecc() {
let path = Path::new("tests/data/mode2_xa1.bin");
let sector = fs::read(path).expect("Cannot open test sector.");
assert_eq!(sector.len(), 2352, "The sector must be 2352 bytes length.");
let edc_ecc_calculator = EccEdc::new();
let address: [u8; 4] = [0, 0, 0, 0];
let is_correct_ecc =
edc_ecc_calculator.check_ecc(&address, §or[0x10..], §or[0x81C..0x930]);
assert_eq!(
is_correct_ecc, true,
"The check_ecc function must return Ok(true)."
);
}
#[test]
fn check_msf() {
let sector_number: u32 = 254874;
let msf: [u8; 3] = [0x56, 0x38, 0x24];
let generated_msf = MSF::sectors_to_msf(sector_number);
assert_eq!(
msf, generated_msf,
"The msf conversion is not properly working."
);
}
}