use core::cell::RefCell;
use neure::map::{fixed_size, from_be_bytes};
use neure::prelude::*;
use neure::{err::Error, map::FallibleMap};
use std::process::exit;
pub fn parse<'a, P, O>(parser: P, bc: &mut BytesCtx<'a>) -> Result<O, Error>
where
P: FallibleMap<&'a [u8], O>,
{
let size = parser.out_size();
bc.ctor(®ex::consume(size).try_map(parser))
}
#[derive(Debug)]
pub struct Trunk<'a> {
ancillary: u8,
private: u8,
reserverd: u8,
safe_copy: u8,
crc_value: u32,
data: &'a [u8],
}
impl<'a> Trunk<'a> {
pub fn new(bc: &mut BytesCtx<'a>, length: usize) -> Result<Self, neure::err::Error> {
let start = bc.offset();
let ancillary = parse(from_be_bytes(), bc)?;
let private = parse(from_be_bytes(), bc)?;
let reserverd = parse(from_be_bytes(), bc)?;
let safe_copy = parse(from_be_bytes(), bc)?;
let data: &[u8] = parse(fixed_size(length), bc)?;
let crc_data = bc.orig_sub(start, bc.offset() - start)?;
let crc_value: u32 = parse(from_be_bytes(), bc)?;
let calc_value = calc_crc(crc_data);
if crc_value != calc_value {
panic!("Incorrectly crc value => got {crc_value} in file <-> {calc_value}");
}
Ok(Trunk {
ancillary,
private,
reserverd,
safe_copy,
crc_value,
data,
})
}
pub fn is_ancillary(&self) -> bool {
self.ancillary.is_ascii_lowercase()
}
pub fn is_private(&self) -> bool {
self.private.is_ascii_lowercase()
}
pub fn is_reserved(&self) -> bool {
self.reserverd.is_ascii_lowercase()
}
pub fn is_safe_copy(&self) -> bool {
self.safe_copy.is_ascii_lowercase()
}
pub fn data(&self) -> &[u8] {
self.data
}
pub fn crc_value(&self) -> u32 {
self.crc_value
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.data.len()
}
}
pub struct Png<'a> {
trunks: Vec<Trunk<'a>>,
}
impl<'a> Png<'a> {
pub fn new(bc: &mut BytesCtx<'a>) -> Result<Self, neure::err::Error> {
let mut trunks = vec![];
while let Ok(length) = parse(from_be_bytes::<u32>(), bc) {
trunks.push(Trunk::new(bc, length as usize)?);
}
Ok(Self { trunks })
}
fn trunks(&self) -> &[Trunk<'a>] {
&self.trunks
}
}
fn main() -> Result<(), Box<dyn core::error::Error>> {
tracing_subscriber::fmt::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
if let Some(file) = std::env::args().nth(1) {
let buff = std::fs::read(file)?;
let ctx = &mut BytesCtx::new(&buff);
if ctx.ctor(&[137, 80, 78, 71, 13, 10, 26, 10]).is_ok() {
println!("Matching the head, the file seems like a png file");
} else {
println!("Not a png file");
exit(1)
}
let png = Png::new(ctx)?;
for (idx, trunk) in png.trunks().iter().enumerate() {
println!(
"In trunk {idx}: {}",
if trunk.is_ancillary() {
"critical"
} else {
"ancillary"
}
);
println!(
"In trunk {idx}: {}",
if !trunk.is_private() {
"public"
} else {
"private"
}
);
println!("In trunk {idx}: {}", !trunk.is_reserved());
println!(
"In trunk {idx}: {}",
if !trunk.is_safe_copy() {
"unsafe to copy"
} else {
"safe to copy"
}
);
println!("In trunk {idx}: data length = {}", trunk.len());
}
}
Ok(())
}
thread_local! {
static CRC_TABLE: RefCell<Option<[u32; 256]>> = const { RefCell::new(None) };
}
pub fn initialize_crc_table() {
CRC_TABLE
.try_with(|table| {
let mut table = table.borrow_mut();
if table.is_none() {
*table = Some({
let mut table: [u32; 256] = [0; 256];
for n in 0..256 {
let mut c = n;
for _ in 0..8 {
if c & 1 == 1 {
c = 0xedb88320u32 ^ (c >> 1);
} else {
c >>= 1;
}
}
table[n as usize] = c;
}
table
})
}
})
.unwrap()
}
pub fn calc_crc(buff: &[u8]) -> u32 {
let mut c = 0xffffffffu32;
initialize_crc_table();
CRC_TABLE
.try_with(|table| {
let table = table.borrow();
if let Some(table) = *table {
for n in 0..buff.len() {
c = table[((c ^ (buff[n] as u32)) & 0xff) as usize] ^ (c >> 8);
}
}
})
.unwrap();
c ^ 0xffffffffu32
}