use adler2::adler32_slice;
use super::constants::{ERROR2_ENTRY_SIZE, ERROR2_FOOTER_SIZE, ERROR2_HEADER_SIZE};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EwfErrorRange {
pub start_sector: u32,
pub sector_count: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EwfErrorSection {
pub ranges: Vec<EwfErrorRange>,
}
impl EwfErrorSection {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < ERROR2_HEADER_SIZE + ERROR2_FOOTER_SIZE {
return Err(Error::invalid_format(
"ewf error2 section payload is too small".to_string(),
));
}
let entry_count = read_u32_le(data, 0) as usize;
let expected_size = ERROR2_HEADER_SIZE
.checked_add(
entry_count
.checked_mul(ERROR2_ENTRY_SIZE)
.ok_or_else(|| Error::invalid_range("ewf error2 entry array size overflow"))?,
)
.and_then(|size| size.checked_add(ERROR2_FOOTER_SIZE))
.ok_or_else(|| Error::invalid_range("ewf error2 size overflow"))?;
if data.len() != expected_size {
return Err(Error::invalid_format(format!(
"ewf error2 payload size does not match entry count: expected {expected_size}, got {}",
data.len()
)));
}
let stored_header_checksum = read_u32_le(data, 516);
let calculated_header_checksum = adler32_slice(&data[..516]);
if stored_header_checksum != calculated_header_checksum {
return Err(Error::invalid_format(format!(
"ewf error2 header checksum mismatch: stored 0x{stored_header_checksum:08x}, calculated 0x{calculated_header_checksum:08x}"
)));
}
let entries_start = ERROR2_HEADER_SIZE;
let entries_end = entries_start + entry_count * ERROR2_ENTRY_SIZE;
let stored_entry_checksum = read_u32_le(data, entries_end);
let calculated_entry_checksum = adler32_slice(&data[entries_start..entries_end]);
if stored_entry_checksum != calculated_entry_checksum {
return Err(Error::invalid_format(format!(
"ewf error2 entry checksum mismatch: stored 0x{stored_entry_checksum:08x}, calculated 0x{calculated_entry_checksum:08x}"
)));
}
let mut ranges = Vec::with_capacity(entry_count);
let mut offset = entries_start;
for _ in 0..entry_count {
ranges.push(EwfErrorRange {
start_sector: read_u32_le(data, offset),
sector_count: read_u32_le(data, offset + 4),
});
offset += ERROR2_ENTRY_SIZE;
}
Ok(Self { ranges })
}
}
fn read_u32_le(data: &[u8], offset: usize) -> u32 {
u32::from_le_bytes([
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
])
}
#[cfg(test)]
mod tests {
use adler2::adler32_slice;
use super::*;
#[test]
fn parses_error_ranges() {
let mut data = vec![0u8; ERROR2_HEADER_SIZE + ERROR2_ENTRY_SIZE + ERROR2_FOOTER_SIZE];
data[0..4].copy_from_slice(&1u32.to_le_bytes());
let header_checksum = adler32_slice(&data[..516]);
data[516..520].copy_from_slice(&header_checksum.to_le_bytes());
data[520..524].copy_from_slice(&301u32.to_le_bytes());
data[524..528].copy_from_slice(&11256u32.to_le_bytes());
let entry_checksum = adler32_slice(&data[520..528]);
data[528..532].copy_from_slice(&entry_checksum.to_le_bytes());
let section = EwfErrorSection::parse(&data).unwrap();
assert_eq!(section.ranges.len(), 1);
assert_eq!(section.ranges[0].start_sector, 301);
assert_eq!(section.ranges[0].sector_count, 11256);
}
}