diff --git a/src/cz_common.rs b/src/cz_common.rs new file mode 100644 index 0000000..670c65f --- /dev/null +++ b/src/cz_common.rs @@ -0,0 +1,54 @@ +//! Shared types and traits between CZ# files + +pub trait CzHeader { + fn new(bytes: &[u8]) -> Self; + + fn version(&self) -> u8; + + fn header_length(&self) -> u16; + + fn width(&self) -> u16; + + fn height(&self) -> u16; + + fn depth(&self) -> u8; +} + +/// The common first part of a header of a CZ# file +#[derive(Debug)] +pub(crate) struct CommonHeader { + /// Format version from the magic bytes, (eg. CZ3, CZ4) + pub version: u8, + + /// Length of the header in bytes + pub length: u8, + + /// 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: u8, +} + +impl CommonHeader { + pub fn new(bytes: &[u8]) -> Self { + Self { + version: bytes[2] - b'0', + length: bytes[4], + width: u16::from_le_bytes(bytes[8..10].try_into().unwrap()), + height: u16::from_le_bytes(bytes[10..12].try_into().unwrap()), + depth: bytes[12], + } + } +} + +pub trait CzImage { + /// Create a [CZImage] from bytes + fn decode(bytes: &[u8]) -> Self; + + /// Get the underlying bitmap for an image + fn raw_bitmap(&self) -> &Vec; +} diff --git a/src/cz_utils.rs b/src/cz_utils.rs deleted file mode 100644 index 5ad6746..0000000 --- a/src/cz_utils.rs +++ /dev/null @@ -1,126 +0,0 @@ -use image::{ImageFormat, RgbaImage}; - -/// The header of a CZ# file -#[derive(Debug)] -pub struct CZHeader { - version: u8, // The version from the magic bytes, (eg. CZ3, CZ4) - length: u8, - res: (u16, u16), // The width in the header - depth: u8, // Bit depth - crop: (u16, u16), // Crop dimensions - bounds: (u16, u16), // Bounding box dimensions - offset: (u16, u16), // Offset coordinates -} - -impl CZHeader { - pub fn new(bytes: &[u8]) -> Self { - CZHeader { - version: bytes[2] - b'0', - length: bytes[4], - res: ( - u16::from_le_bytes(bytes[8..10].try_into().unwrap()), - u16::from_le_bytes(bytes[10..12].try_into().unwrap()) - ), - depth: bytes[12], - crop: ( - u16::from_le_bytes(bytes[20..22].try_into().unwrap()), - u16::from_le_bytes(bytes[22..24].try_into().unwrap()) - ), - bounds: ( - u16::from_le_bytes(bytes[24..26].try_into().unwrap()), - u16::from_le_bytes(bytes[26..28].try_into().unwrap()) - ), - offset: ( - u16::from_le_bytes(bytes[28..30].try_into().unwrap()), - u16::from_le_bytes(bytes[30..32].try_into().unwrap()) - ), - } - } -} - -/// Defines a full file that has a header of type `CZHeader` and a vector bitmap as the body -#[derive(Debug)] -pub struct CZFile { - header: CZHeader, - bitmap: Vec, -} - -impl CZFile { - /// Create and save a PNG of the image data - /// This errors if the data end up too short - pub fn to_rgba8(&self) -> RgbaImage { - let process_bitmap = self.bitmap.clone(); - - RgbaImage::from_raw( - self.header.res.0 as u32, - self.header.res.1 as u32, - process_bitmap, - ).expect("Error encoding the image") - } - - pub fn to_png(&self, out_name:&str) { - let image_data = self.to_rgba8(); - - match image_data.save_with_format(out_name, ImageFormat::Png) { - Ok(()) => {} - Err(e) => { - eprintln!("ERROR SAVING IMAGE: {}", e); - eprintln!("You probably have an image with the CZ0 offset bug!") - } - } - } - - /// Pretty-print information about a CZ image - pub fn info(&self) { - let mut image_size = self.bitmap.len() as f32 + self.header.length as f32; - image_size /= 1024.0; - - println!("\n--IMAGE INFORMATION--"); - println!("Image size : {:.2} KB", image_size); - println!("Version : {:?}", self.header.version); - println!("Header Length : {:?} bytes", self.header.length); - println!( - "Resolution : {}x{}", - self.header.res.0, self.header.res.1 - ); - println!("Bit Depth : {} bits", self.header.depth); - println!( - "Crop Coords : {}x{}", - self.header.crop.0, self.header.crop.1 - ); - println!( - "Bound Coords : {}x{}", - self.header.bounds.0, self.header.bounds.1 - ); - println!( - "Offset Coords : {}x{}", - self.header.offset.0, self.header.offset.1 - ); - } -} - -/// Utilities for manipulating CZ0 images -pub mod cz0 { - use std::fs; - use crate::cz_utils::{CZFile, CZHeader}; - - /// Provided a bitstream, extract the header information and the rest of the metadata about a CZ0 file, returning a struct containing the header information and bitmap - pub fn decode_cz0(input_filename: &str) -> CZFile { - let mut input = fs::read(input_filename).expect("Error, could not open image"); - - // TODO Research the header more! - let header = CZHeader::new(&input); - - // Chop off the header and keep only the bitmap after it - input.drain(..header.length as usize); - - // Construct the output CZ0 image - let final_image = CZFile { - header, - bitmap: input, - }; - - println!("Decoded {}", input_filename); - final_image - } -} diff --git a/src/formats/cz0.rs b/src/formats/cz0.rs new file mode 100644 index 0000000..e8673c5 --- /dev/null +++ b/src/formats/cz0.rs @@ -0,0 +1,56 @@ +use crate::cz_common::{CommonHeader, CzHeader, CzImage}; + +struct Cz0Header { + /// Common CZ# header + common_header: CommonHeader, + + /// Dimensions of cropped image area + crop: (u16, u16), + + /// Bounding box dimensions + bounds: (u16, u16), + + // Offset coordinates + offset: (u16, u16), +} + +struct Cz0Image { + header: Cz0Header, + bitmap: Vec, +} + +impl CzHeader for Cz0Header { + fn new(bytes: &[u8]) -> Self { + todo!() + } + + fn version(&self) -> u8 { + todo!() + } + + fn header_length(&self) -> u16 { + todo!() + } + + fn width(&self) -> u16 { + todo!() + } + + fn height(&self) -> u16 { + todo!() + } + + fn depth(&self) -> u8 { + todo!() + } +} + +impl CzImage for Cz0Image { + fn decode(bytes: &[u8]) -> Self { + let header = CZH + } + + fn raw_bitmap(&self) -> &Vec { + &self.bitmap + } +} diff --git a/src/main.rs b/src/main.rs index 1d8f6e5..a5e3982 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,14 @@ -// Create the modules -pub mod cz_utils; -pub mod utils; +pub mod cz_common; +pub mod formats{ + pub mod cz0; +} // Generic tools use std::fs; - -use crate::cz_utils::CZHeader; - +use crate::cz_common::CommonHeader; fn main() { - let input = fs::read( - "/home/g2/Documents/projects/lbee-utils/test_files/GOOD_extra_bg.cz3" - ).expect("Error, could not open image"); - - let header = CZHeader::new(&input); - - dbg!(header); + let input = fs::read("../test_files/x5a3bvy.cz1").expect("Error, could not open image"); + let header = CommonHeader::new(&input); + println!("{:?}", header); } diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index f89e9b9..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,22 +0,0 @@ -/// Converts 8 bit bytes to a 16 bit little endian word -pub fn bytes_to_word(first: u8, second: u8) -> u16 { - ((second as u16) << 8) | (first as u16) -} - -/// Converts a 16 bit little endian word to 8 bit bytes -pub fn word_to_bytes(word: u16) -> [u8; 2] { - let first: u8 = (word & 0xFF) as u8; // Extract the first byte - let second: u8 = ((word >> 8) & 0xFF) as u8; // Extract the second byte - - [first, second] -} - -pub fn get_bytes(iterator: &mut std::vec::IntoIter) -> [u8; S] { - let mut bytes = [0; S]; - - for byte in bytes.iter_mut().take(S) { - *byte = iterator.next().unwrap(); - } - - bytes -}