Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ exclude = ["src/tests.rs", "tests/*"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bitflags = "2.6"
deflate = "1.0"
image = { version = "0.25.4", default-features = false, features = ["png"] }
inflate = "0.4.5"
thiserror = "1.0"
bitflags = "2"
crc32fast = "1"
deflate = "1"
image = { version = "0.25", default-features = false, features = ["png"] }
inflate = "0.4"
thiserror = "2"
232 changes: 116 additions & 116 deletions src/chunk.rs
Original file line number Diff line number Diff line change
@@ -1,116 +1,116 @@
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)?;
// 4 bytes for the length.
// 4 bytes for the type.
// Data can be 0 bytes.
// 4 bytes for the CRC.
// Total minimum size for an undetermined PNG chunk: 12 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 {} bytes, lower than the required 12.", chunk_length)));
};
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],
];
// The chunk type is made of four ascii characters. The valid ranges are A-Z and a-z.
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_crc(chunk_type.iter().chain(data.iter()));
if u32::from_be_bytes(crc) != recalculated_crc {
let chunk_name = String::from_utf8(chunk_type.to_vec())?;
return Err(error::DmiError::Generic(format!("Failed to load Chunk of type {}. Supplied CRC invalid: {:#?}. Its value ({}) does not match the recalculated one ({}).", chunk_name, crc, u32::from_be_bytes(crc), recalculated_crc)));
}
Ok(RawGenericChunk {
data_length,
chunk_type,
data,
crc,
})
}
pub fn save<W: Write>(&self, writter: &mut W) -> Result<usize, error::DmiError> {
let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};
let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};
let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};
let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};
Ok(total_bytes_written)
}
}
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)?;

// 4 bytes for the length.
// 4 bytes for the type.
// Data can be 0 bytes.
// 4 bytes for the CRC.

// Total minimum size for an undetermined PNG chunk: 12 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 {} bytes, lower than the required 12.", chunk_length)));
};

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],
];

// The chunk type is made of four ascii characters. The valid ranges are A-Z and a-z.
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);
if u32::from_be_bytes(crc) != recalculated_crc {
let chunk_name = String::from_utf8(chunk_type.to_vec())?;
return Err(error::DmiError::Generic(format!("Failed to load Chunk of type {}. Supplied CRC invalid: {:#?}. Its value ({}) does not match the recalculated one ({}).", chunk_name, crc, u32::from_be_bytes(crc), recalculated_crc)));
}

Ok(RawGenericChunk {
data_length,
chunk_type,
data,
crc,
})
}

pub fn save<W: Write>(&self, writter: &mut W) -> Result<usize, error::DmiError> {
let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};

let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};

let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};

let bytes_written = writter.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 {} bytes written.",
total_bytes_written
)));
};

Ok(total_bytes_written)
}
}
23 changes: 6 additions & 17 deletions src/crc.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
pub(crate) fn calculate_crc<'a, I: IntoIterator<Item = &'a u8>>(buffer: I) -> u32 {
const CRC_POLYNOMIAL: u32 = 0xedb8_8320;

fn update_crc(crc: u32, message: u8) -> u32 {
let message: u32 = u32::from(message);
let mut crc = crc ^ message;
for _ in 0..8 {
crc = (if crc & 1 != 0 { CRC_POLYNOMIAL } else { 0 }) ^ (crc >> 1);
}
crc
}

buffer
.into_iter()
.fold(u32::MAX, |crc, message| update_crc(crc, *message))
^ u32::MAX
}
pub(crate) fn calculate_chunk_data_crc(chunk_type: [u8; 4], data: &[u8]) -> u32 {
let mut hasher = crc32fast::Hasher::new();
hasher.update(&chunk_type);
hasher.update(data);
hasher.finalize()
}
23 changes: 11 additions & 12 deletions src/ztxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ pub fn create_ztxt_chunk(dmi_signature: &[u8]) -> Result<RawZtxtChunk, error::Dm
compressed_text,
..Default::default()
};
let mut data_bytes = vec![];
let mut data_bytes = Vec::with_capacity(data.length());
data.save(&mut data_bytes)?;
let data_length = (data_bytes.len() as u32).to_be_bytes();
let chunk_type = ZTXT_TYPE;
let crc = crc::calculate_crc(chunk_type.iter().chain(data_bytes.iter())).to_be_bytes();
let crc = crc::calculate_chunk_data_crc(chunk_type, &data_bytes).to_be_bytes();
Ok(RawZtxtChunk {
data_length,
chunk_type,
Expand Down Expand Up @@ -73,7 +73,7 @@ impl RawZtxtChunk {
raw_chunk_bytes[total_bytes_length - 2],
raw_chunk_bytes[total_bytes_length - 1],
];
let calculated_crc = crc::calculate_crc(chunk_type.iter().chain(data_bytes.iter()));
let calculated_crc = crc::calculate_chunk_data_crc(chunk_type, data_bytes);
if u32::from_be_bytes(crc) != calculated_crc {
return Err(error::DmiError::Generic(format!("Failed to load RawZtxtChunk from reader. Given CRC ({}) does not match the calculated one ({}).", u32::from_be_bytes(crc), calculated_crc)));
}
Expand Down Expand Up @@ -130,7 +130,7 @@ impl RawZtxtChunk {
data.save(&mut data_bytes)?;
let data_length = (data_bytes.len() as u32).to_be_bytes();
let chunk_type = ZTXT_TYPE;
let crc = crc::calculate_crc(chunk_type.iter().chain(data_bytes.iter())).to_be_bytes();
let crc = crc::calculate_chunk_data_crc(chunk_type, &data_bytes).to_be_bytes();
Ok(RawZtxtChunk {
data_length,
chunk_type,
Expand Down Expand Up @@ -197,7 +197,7 @@ impl TryFrom<Vec<u8>> for RawZtxtChunk {
let data_bytes = &raw_chunk_bytes[8..(total_bytes_length - 4)];
let data = RawZtxtData::load(data_bytes)?;
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]];
let calculated_crc = crc::calculate_crc(chunk_type.iter().chain(data_bytes.iter()));
let calculated_crc = crc::calculate_chunk_data_crc(chunk_type, data_bytes);
if u32::from_be_bytes(crc) != calculated_crc {
bail!("Failed to convert Vec<u8> into RawZtxtChunk. Given CRC ({}) does not match the calculated one ({}).", u32::from_be_bytes(crc), calculated_crc)
}
Expand Down Expand Up @@ -299,13 +299,12 @@ impl RawZtxtData {
}

fn crc(&self) -> u32 {
crc::calculate_crc(
ZTXT_TYPE
.iter()
.chain(self.keyword.iter())
.chain([self.null_separator, self.compression_method].iter())
.chain(self.compressed_text.iter()),
)
let mut hasher = crc32fast::Hasher::new();
hasher.update(&ZTXT_TYPE);
hasher.update(&self.keyword);
hasher.update(&[self.null_separator, self.compression_method]);
hasher.update(&self.compressed_text);
hasher.finalize()
}
}

Expand Down
Loading