use crate::{crc, error};
use std::io::prelude::*;
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct RawGenericChunk {
pub data_length: [u8; 4],
pub chunk_type: [u8; 4],
pub data: Vec<u8>,
pub crc: [u8; 4],
}
impl RawGenericChunk {
pub fn load<R: Read>(reader: &mut R) -> Result<RawGenericChunk, error::DmiError> {
let mut chunk_bytes = Vec::new();
reader.read_to_end(&mut chunk_bytes)?;
let chunk_length = chunk_bytes.len();
if chunk_length < 12 {
return Err(error::DmiError::Generic(format!("Failed to load Chunk. Supplied reader contained size of {chunk_length} bytes, lower than the required 12.")));
};
let data_length = [
chunk_bytes[0],
chunk_bytes[1],
chunk_bytes[2],
chunk_bytes[3],
];
let chunk_type = [
chunk_bytes[4],
chunk_bytes[5],
chunk_bytes[6],
chunk_bytes[7],
];
if !chunk_type
.iter()
.all(|c| (b'A' <= *c && *c <= b'Z') || (b'a' <= *c && *c <= b'z'))
{
return Err(error::DmiError::Generic(format!(
"Failed to load Chunk. Type contained unlawful characters: {chunk_type:#?}",
)));
};
let data: Vec<u8> = chunk_bytes[8..(chunk_length - 4)].to_vec();
let crc = [
chunk_bytes[chunk_length - 4],
chunk_bytes[chunk_length - 3],
chunk_bytes[chunk_length - 2],
chunk_bytes[chunk_length - 1],
];
let recalculated_crc = crc::calculate_chunk_data_crc(chunk_type, &data);
let crc_le = u32::from_be_bytes(crc);
if crc_le != recalculated_crc {
let chunk_name = String::from_utf8(chunk_type.to_vec())?;
return Err(error::DmiError::Generic(format!("Failed to load Chunk of type {chunk_name}. Supplied CRC invalid: {crc:#?}. Its value ({crc_le}) does not match the recalculated one ({recalculated_crc}).")));
}
Ok(RawGenericChunk {
data_length,
chunk_type,
data,
crc,
})
}
pub fn save<W: Write>(&self, writer: &mut W) -> Result<usize, error::DmiError> {
let bytes_written = writer.write(&self.data_length)?;
let mut total_bytes_written = bytes_written;
if bytes_written < self.data_length.len() {
return Err(error::DmiError::Generic(format!(
"Failed to save Chunk. Buffer unable to hold the data, only {total_bytes_written} bytes written."
)));
};
let bytes_written = writer.write(&self.chunk_type)?;
total_bytes_written += bytes_written;
if bytes_written < self.chunk_type.len() {
return Err(error::DmiError::Generic(format!(
"Failed to save Chunk. Buffer unable to hold the data, only {total_bytes_written} bytes written."
)));
};
let bytes_written = writer.write(&self.data)?;
total_bytes_written += bytes_written;
if bytes_written < self.data.len() {
return Err(error::DmiError::Generic(format!(
"Failed to save Chunk. Buffer unable to hold the data, only {total_bytes_written} bytes written."
)));
};
let bytes_written = writer.write(&self.crc)?;
total_bytes_written += bytes_written;
if bytes_written < self.crc.len() {
return Err(error::DmiError::Generic(format!(
"Failed to save Chunk. Buffer unable to hold the data, only {total_bytes_written} bytes written."
)));
};
Ok(total_bytes_written)
}
}