//! Shared types and traits between CZ# files use std::{ io::{self, Read, Seek, Write}, path::PathBuf, }; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use thiserror::Error; #[derive(Error, Debug)] pub enum CzError { #[error("Expected CZ{}, got CZ{}", 0, 1)] VersionMismatch(u8, u8), #[error("File data is incorrect, it might be corrupt")] Corrupt, #[error("File is not a CZ image")] NotCzFile, #[error("Failed to read/write input/output: {}", 0)] IoError(#[from] io::Error), #[error("Problem while decoding file")] DecodeError, } pub trait CzHeader { fn new(bytes: &mut T) -> Result where Self: Sized; /// The [CommonHeader] header from the image fn common(&self) -> &CommonHeader; /// Turn the header into bytes equivalent to the original header from the file fn to_bytes(&self) -> Result, io::Error>; /// The version of the [CzImage] file fn version(&self) -> u8; /// The length of the header in bytes fn length(&self) -> usize; /// The width of the image fn width(&self) -> u16; /// The height of the image fn height(&self) -> u16; /// The bit depth of the image (BPP) fn depth(&self) -> u16; /// An unknown value? fn color_block(&self) -> u8; } /// The common first part of a header of a CZ# file #[derive(Debug, Clone, Copy)] pub struct CommonHeader { /// Format version from the magic bytes, (eg. CZ3, CZ4) pub version: u8, /// Length of the header in bytes pub length: u32, /// Width of the image in pixels pub width: u16, /// Height of the image in pixels pub height: u16, /// Bit depth in Bits Per Pixel (BPP) pub depth: u16, /// Color block pub color_block: u8, } impl CzHeader for CommonHeader { fn new(bytes: &mut T) -> Result where Self: Sized, { let mut magic = [0u8; 4]; bytes.read_exact(&mut magic)?; if magic[0..2] != [b'C', b'Z'] { return Err(CzError::NotCzFile); } Ok(Self { version: magic[2] - b'0', length: bytes.read_u32::()?, width: bytes.read_u16::()?, height: bytes.read_u16::()?, depth: bytes.read_u16::()?, color_block: bytes.read_u8()?, }) } fn common(&self) -> &CommonHeader { &self } fn version(&self) -> u8 { self.version } fn length(&self) -> usize { self.length as usize } fn width(&self) -> u16 { self.width } fn height(&self) -> u16 { self.height } fn depth(&self) -> u16 { self.depth } fn color_block(&self) -> u8 { self.color_block } fn to_bytes(&self) -> Result, io::Error> { let mut buf = vec![]; let magic_bytes = [b'C', b'Z', b'0' + self.version, 0]; buf.write_all(&magic_bytes)?; buf.write_u32::(self.length() as u32)?; buf.write_u16::(self.width())?; buf.write_u16::(self.height())?; buf.write_u16::(self.depth())?; buf.write_u8(self.color_block())?; Ok(buf) } } pub trait CzImage { type Header; /// Create a [crate::CzImage] from bytes fn decode(bytes: &mut T) -> Result where Self: Sized; /// Save the image as its corresponding CZ# type fn save_as_cz>(&self, path: T) -> Result<(), CzError>; /// Get the header for metadata fn header(&self) -> &Self::Header; /// Set the header with its metadata fn set_header(&mut self, header: &Self::Header); fn bitmap(&self) -> &Vec; /// Get the raw underlying bitmap for an image fn into_bitmap(self) -> Vec; /// Set the bitmap the image contains fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16); } pub fn parse_colormap( input: &mut T, num_colors: usize, ) -> Result, CzError> { let mut colormap = Vec::with_capacity(num_colors); let mut rgba_buf = [0u8; 4]; for _ in 0..num_colors { input.read_exact(&mut rgba_buf)?; colormap.push(rgba_buf); } Ok(colormap) } pub fn apply_palette(input: &mut Vec, palette: &[[u8; 4]]) -> Vec { let mut output_map = Vec::new(); for byte in input.iter() { let color = palette[*byte as usize]; output_map.extend_from_slice(&color); } output_map }