use crate::{chunk, error};
use std::convert::TryFrom;
use std::io::prelude::*;
pub const IEND_TYPE: [u8; 4] = [b'I', b'E', b'N', b'D'];
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct RawIendChunk {
pub data_length: [u8; 4],
pub chunk_type: [u8; 4],
pub crc: [u8; 4],
}
impl RawIendChunk {
pub fn new() -> RawIendChunk {
RawIendChunk {
..Default::default()
}
}
pub fn length(&self) -> usize {
self.data_length.len() + self.chunk_type.len() + self.crc.len()
}
pub fn load<R: Read>(reader: &mut R) -> Result<RawIendChunk, error::DmiError> {
let default_iend_chunk = RawIendChunk::new();
let mut raw_chunk_bytes = Vec::new();
reader.read_to_end(&mut raw_chunk_bytes)?;
let total_bytes_length = raw_chunk_bytes.len();
if total_bytes_length != default_iend_chunk.length() {
return Err(error::DmiError::Generic(format!(
"Failed to load RawIendChunk from reader. Size: {}. Expected: {}.",
raw_chunk_bytes.len(),
default_iend_chunk.length()
)));
}
let data_length = [
raw_chunk_bytes[0],
raw_chunk_bytes[1],
raw_chunk_bytes[2],
raw_chunk_bytes[3],
];
if data_length != default_iend_chunk.data_length {
return Err(error::DmiError::Generic(format!(
"Failed to load RawIendChunk from reader. Lengh field value: {data_length:#?}. Expected: {:#?}.",
default_iend_chunk.data_length
)));
}
let chunk_type = [
raw_chunk_bytes[4],
raw_chunk_bytes[5],
raw_chunk_bytes[6],
raw_chunk_bytes[7],
];
if chunk_type != default_iend_chunk.chunk_type {
return Err(error::DmiError::Generic(format!(
"Failed to load RawIendChunk from reader. Chunk type: {chunk_type:#?}. Expected {:#?}.",
default_iend_chunk.chunk_type
)));
}
let crc = [
raw_chunk_bytes[total_bytes_length - 4],
raw_chunk_bytes[total_bytes_length - 3],
raw_chunk_bytes[total_bytes_length - 2],
raw_chunk_bytes[total_bytes_length - 1],
];
if crc != default_iend_chunk.crc {
return Err(error::DmiError::Generic(format!(
"Failed to load RawIendChunk from reader. CRC: {crc:#?}. Expected {:#?}.",
default_iend_chunk.crc
)));
}
Ok(default_iend_chunk)
}
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 IEND 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 IEND 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 IEND chunk. Buffer unable to hold the data, only {total_bytes_written} bytes written."
)));
};
Ok(total_bytes_written)
}
}
impl Default for RawIendChunk {
fn default() -> Self {
let data_length = [0, 0, 0, 0];
let chunk_type = IEND_TYPE;
let crc = [174, 66, 96, 130];
RawIendChunk {
data_length,
chunk_type,
crc,
}
}
}
impl TryFrom<chunk::RawGenericChunk> for RawIendChunk {
type Error = error::DmiError;
fn try_from(raw_generic_chunk: chunk::RawGenericChunk) -> Result<Self, Self::Error> {
if !raw_generic_chunk.data.is_empty() {
return Err(error::DmiError::Generic(format!(
"Failed to convert RawGenericChunk into RawIendChunk. Non-empty data field. Chunk: {raw_generic_chunk:#?}."
)));
};
let default_iend_chunk = RawIendChunk::new();
if raw_generic_chunk.chunk_type != default_iend_chunk.chunk_type {
return Err(error::DmiError::Generic(format!(
"Failed to convert RawGenericChunk into RawIendChunk. Wrong type: {:#?}. Expected: {:#?}.",
raw_generic_chunk.chunk_type, default_iend_chunk.chunk_type
)));
};
if raw_generic_chunk.crc != default_iend_chunk.crc {
return Err(error::DmiError::Generic(format!("Failed to convert RawGenericChunk into RawIendChunk. Mismatching CRC: {:#?}. Expected: {:#?}.", raw_generic_chunk.crc, default_iend_chunk.crc)));
}
Ok(default_iend_chunk)
}
}