diff --git a/Cargo.toml b/Cargo.toml index a70ccc2..31eb562 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,3 @@ authors = ["G2"] [workspace.lints.rust] unsafe_code = "forbid" - -[build] -rustflags = ["-C", "target-cpu=native"] diff --git a/cz/src/common.rs b/cz/src/common.rs index 23280dc..4e3d6e3 100644 --- a/cz/src/common.rs +++ b/cz/src/common.rs @@ -1,9 +1,6 @@ //! Shared types and traits between CZ# files -use std::{ - io::{self, Read, Seek, Write}, - path::PathBuf, -}; +use std::io::{self, Read, Seek, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use thiserror::Error; @@ -13,25 +10,51 @@ pub enum CzError { #[error("Expected CZ{}, got CZ{}", 0, 1)] VersionMismatch(u8, u8), + #[error("Could not parse color index palette")] + PaletteError, + + #[error("Bitmap size does not match image size")] + BitmapFormat, + #[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)] + #[error("Failed to read/write input/output")] IoError(#[from] io::Error), #[error("Problem while decoding file")] DecodeError, } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum CzVersion { CZ0, CZ1, CZ2, CZ3, CZ4, + CZ5, +} + +impl TryFrom for CzVersion { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + let value = match value { + 0 => Self::CZ0, + 1 => Self::CZ1, + 2 => Self::CZ2, + 3 => Self::CZ3, + 4 => Self::CZ4, + 5 => Self::CZ5, + _ => return Err("Value is not a valid CZ version"), + }; + + Ok(value) + } } pub trait CzHeader { @@ -46,10 +69,10 @@ pub trait CzHeader { fn to_bytes(&self) -> Result, io::Error>; /// The version of the image - fn version(&self) -> u8; + fn version(&self) -> CzVersion; /// Set the version of the image - fn set_version(&mut self); + fn set_version(&mut self, version: CzVersion); /// The length of the header in bytes fn length(&self) -> usize; @@ -58,13 +81,13 @@ pub trait CzHeader { fn width(&self) -> u16; /// Set the width of the image - fn set_width(&mut self); + fn set_width(&mut self, width: u16); /// The height of the image fn height(&self) -> u16; /// Set the height of the image - fn set_height(&mut self); + fn set_height(&mut self, height: u16); /// The bit depth of the image (BPP) fn depth(&self) -> u16; @@ -81,22 +104,22 @@ pub trait CzHeader { #[derive(Debug, Clone, Copy)] pub struct CommonHeader { /// Format version from the magic bytes, (eg. CZ3, CZ4) - pub version: u8, + version: CzVersion, /// Length of the header in bytes - pub length: u32, + length: u32, /// Width of the image in pixels - pub width: u16, + width: u16, /// Height of the image in pixels - pub height: u16, + height: u16, /// Bit depth in Bits Per Pixel (BPP) - pub depth: u16, + depth: u16, - /// Color block - pub color_block: u8, + /// Color block? This byte's purpose is unclear + unknown: u8, } impl CzHeader for CommonHeader { @@ -111,24 +134,42 @@ impl CzHeader for CommonHeader { return Err(CzError::NotCzFile); } - Ok(Self { - version: magic[2] - b'0', + // Ensure the version matches a CZ file type + let version = match CzVersion::try_from(magic[2] - b'0') { + Ok(ver) => ver, + Err(_) => return Err(CzError::NotCzFile), + }; + + let mut header = Self { + version, length: bytes.read_u32::()?, width: bytes.read_u16::()?, height: bytes.read_u16::()?, depth: bytes.read_u16::()?, - color_block: bytes.read_u8()?, - }) + unknown: bytes.read_u8()?, + }; + + // Lock the color depth to 8 if it's over 32 + // This is obviously wrong, but why is it wrong? + if header.depth() > 32 { + header.depth = 8 + } + + Ok(header) } fn common(&self) -> &CommonHeader { self } - fn version(&self) -> u8 { + fn version(&self) -> CzVersion { self.version } + fn set_version(&mut self, version: CzVersion) { + self.version = version + } + fn length(&self) -> usize { self.length as usize } @@ -137,22 +178,30 @@ impl CzHeader for CommonHeader { self.width } + fn set_width(&mut self, width: u16) { + self.width = width + } + fn height(&self) -> u16 { self.height } + fn set_height(&mut self, height: u16) { + self.height = height + } + fn depth(&self) -> u16 { self.depth } fn color_block(&self) -> u8 { - self.color_block + self.unknown } fn to_bytes(&self) -> Result, io::Error> { let mut buf = vec![]; - let magic_bytes = [b'C', b'Z', b'0' + self.version, 0]; + let magic_bytes = [b'C', b'Z', b'0' + self.version as u8, b'\0']; buf.write_all(&magic_bytes)?; buf.write_u32::(self.length() as u32)?; buf.write_u16::(self.width())?; @@ -164,35 +213,7 @@ impl CzHeader for CommonHeader { } } -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; - - fn header_mut(&mut self) -> &mut 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( +pub fn get_palette( input: &mut T, num_colors: usize, ) -> Result, CzError> { @@ -207,13 +228,20 @@ pub fn parse_colormap( Ok(colormap) } -pub fn apply_palette(input: &mut &[u8], palette: &[[u8; 4]]) -> Vec { +pub fn apply_palette( + input: &[u8], + palette: &[[u8; 4]] +) -> Result, CzError> { let mut output_map = Vec::new(); for byte in input.iter() { - let color = palette[*byte as usize]; - output_map.extend_from_slice(&color); + let color = palette.get(*byte as usize); + if let Some(color) = color { + output_map.extend_from_slice(color); + } else { + return Err(CzError::PaletteError) + } } - output_map + Ok(output_map) } diff --git a/cz/src/compression.rs b/cz/src/compression.rs index 9291e55..fb8fb82 100644 --- a/cz/src/compression.rs +++ b/cz/src/compression.rs @@ -41,7 +41,7 @@ pub struct CompressionInfo { /// /// These are defined by a length value, followed by the number of data chunks /// that length value says split into compressed and original size u32 values -pub fn parse_chunk_info( +pub fn get_chunk_info( bytes: &mut T, ) -> Result { let parts_count = bytes.read_u32::()?; @@ -218,7 +218,7 @@ pub fn line_diff(header: &T, data: &[u8]) -> Vec { let mut output_buf = data.to_vec(); let block_height = - (f32::ceil(height as f32 / header.color_block() as f32) as u16) as usize; + (f32::ceil(height as f32 / 3.0) as u16) as usize; let pixel_byte_count = header.depth() >> 3; let line_byte_count = (width * pixel_byte_count as u32) as usize; diff --git a/cz/src/dynamic.rs b/cz/src/dynamic.rs index 46c85de..9b573d6 100644 --- a/cz/src/dynamic.rs +++ b/cz/src/dynamic.rs @@ -1,23 +1,18 @@ use std::{ - io::{BufReader, Read, Seek}, + io::{BufReader, Read, Seek, SeekFrom}, path::Path }; use byteorder::ReadBytesExt; use crate::{ - common::{CommonHeader, CzError, CzVersion, CzHeader}, - Cz0Image, - Cz1Image, - Cz2Image, - Cz3Image, - Cz4Image, - CzImage + common::{apply_palette, get_palette, CommonHeader, CzError, CzHeader, CzVersion}, + formats::{cz0, cz1, cz2, cz3, cz4}, }; -struct DynamicCz { - cz_type: CzVersion, - bitmap: Vec, +pub struct DynamicCz { header_common: CommonHeader, + palette: Option>, + bitmap: Vec, } impl DynamicCz { @@ -46,74 +41,81 @@ impl DynamicCz { } } -impl CzImage for DynamicCz { - type Header = CommonHeader; +impl DynamicCz { + pub fn decode(input: &mut T) -> Result { + // Get the header common to all CZ images + let header_common = CommonHeader::new(input)?; + input.seek(SeekFrom::Start(header_common.length() as u64))?; - fn decode(input: &mut T) - -> Result - { - let common_header = CommonHeader::new(input)?; - input.seek(std::io::SeekFrom::Start(0))?; + // Get the color palette if the bit depth is 8 or less + let palette = if header_common.depth() <= 8 { + let color_count = 1 << header_common.depth(); + Some(get_palette(input, color_count)?) + } else { + None + }; - Ok(match common_header.version() { - 0 => DynamicCz::CZ0(Cz0Image::decode(input)?), - 1 => DynamicCz::CZ1(Cz1Image::decode(input)?), - 2 => DynamicCz::CZ2(Cz2Image::decode(input)?), - 3 => DynamicCz::CZ3(Cz3Image::decode(input)?), - 4 => DynamicCz::CZ4(Cz4Image::decode(input)?), - _ => return Err(CzError::NotCzFile), + // Get the image data as a bitmap + let mut bitmap = match header_common.version() { + CzVersion::CZ0 => cz0::decode(input)?, + CzVersion::CZ1 => cz1::decode(input)?, + CzVersion::CZ2 => cz2::decode(input)?, + CzVersion::CZ3 => cz3::decode(input, &header_common)?, + CzVersion::CZ4 => cz4::decode(input, &header_common)?, + CzVersion::CZ5 => unimplemented!(), + }; + + let image_size = header_common.width() as usize * header_common.height() as usize; + if bitmap.len() != image_size * (header_common.depth() >> 3) as usize { + // If the bitmap is smaller or larger than the image size, it is likely wrong + return Err(CzError::Corrupt) + } + + if let Some(palette) = &palette { + bitmap = apply_palette(&bitmap, palette)?; + } + + Ok(Self { + header_common, + palette, + bitmap, }) } - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - match self { - DynamicCz::CZ0(img) => img.save_as_cz(path), - DynamicCz::CZ1(_) => unimplemented!(), - DynamicCz::CZ2(_) => unimplemented!(), - DynamicCz::CZ3(_) => unimplemented!(), - DynamicCz::CZ4(_) => unimplemented!(), - } - } - - fn header(&self) -> &Self::Header { - match self { - DynamicCz::CZ0(img) => img.header().common(), - DynamicCz::CZ1(img) => img.header().common(), - DynamicCz::CZ2(img) => img.header().common(), - DynamicCz::CZ3(img) => img.header().common(), - DynamicCz::CZ4(img) => img.header().common(), - } - } - - fn header_mut(&mut self) -> &mut Self::Header { + pub fn save_as_cz>(&self, path: T) -> Result<(), CzError> { todo!() } - fn set_header(&mut self, header: &Self::Header) { - todo!() + pub fn header(&self) -> &CommonHeader { + &self.header_common } - fn bitmap(&self) -> &Vec { - match self { - DynamicCz::CZ0(img) => img.bitmap(), - DynamicCz::CZ1(img) => img.bitmap(), - DynamicCz::CZ2(img) => img.bitmap(), - DynamicCz::CZ3(img) => img.bitmap(), - DynamicCz::CZ4(img) => img.bitmap(), + pub fn header_mut(&mut self) -> &mut CommonHeader { + &mut self.header_common + } + + pub fn set_header(&mut self, header: &CommonHeader) { + self.header_common = header.to_owned() + } + + pub fn bitmap(&self) -> &Vec { + &self.bitmap + } + + pub fn into_bitmap(self) -> Vec { + self.bitmap + } + + pub fn set_bitmap(&mut self, bitmap: Vec, width: u16, height: u16) -> Result<(), CzError> { + if bitmap.len() != width as usize * height as usize { + return Err(CzError::BitmapFormat) } - } - fn into_bitmap(self) -> Vec { - match self { - DynamicCz::CZ0(img) => img.into_bitmap(), - DynamicCz::CZ1(img) => img.into_bitmap(), - DynamicCz::CZ2(img) => img.into_bitmap(), - DynamicCz::CZ3(img) => img.into_bitmap(), - DynamicCz::CZ4(img) => img.into_bitmap(), - } - } + self.bitmap = bitmap; - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - unimplemented!() + self.header_mut().set_width(width); + self.header_mut().set_height(height); + + Ok(()) } } diff --git a/cz/src/formats/cz0.rs b/cz/src/formats/cz0.rs index eeb27b3..8b1f71a 100644 --- a/cz/src/formats/cz0.rs +++ b/cz/src/formats/cz0.rs @@ -1,12 +1,7 @@ -use std::{ - fs::File, - io::{self, BufWriter, Cursor, Read, Seek, Write}, - path::PathBuf, -}; - +use std::io::{self, Read, Seek, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use crate::common::{CommonHeader, CzError, CzHeader, CzImage}; +use crate::common::{CommonHeader, CzError, CzHeader, CzVersion}; #[derive(Debug, Clone, Copy)] pub struct Cz0Header { @@ -37,21 +32,15 @@ pub struct Cz0Header { unknown_2: Option<[u8; 4]>, } -#[derive(Debug)] -pub struct Cz0Image { - header: Cz0Header, - bitmap: Vec, -} - -impl CzHeader for Cz0Header { +impl Cz0Header { fn new(bytes: &mut T) -> Result where Self: Sized, { let common = CommonHeader::new(bytes)?; - if common.version() != 0 { - return Err(CzError::VersionMismatch(common.version(), 0)); + if common.version() != CzVersion::CZ0 { + return Err(CzError::VersionMismatch(common.version() as u8, 0)); } let mut unknown_1 = [0u8; 5]; @@ -94,34 +83,6 @@ impl CzHeader for Cz0Header { }) } - fn common(&self) -> &CommonHeader { - &self.common - } - - fn version(&self) -> u8 { - self.common.version() - } - - fn length(&self) -> usize { - self.common.length() - } - - fn width(&self) -> u16 { - self.common.width() - } - - fn height(&self) -> u16 { - self.common.height() - } - - fn depth(&self) -> u16 { - self.common.depth() - } - - fn color_block(&self) -> u8 { - self.common.color_block() - } - fn to_bytes(&self) -> Result, io::Error> { let mut buf = vec![]; @@ -132,7 +93,7 @@ impl CzHeader for Cz0Header { buf.write_u16::(self.bounds_width)?; buf.write_u16::(self.bounds_height)?; - if self.length() > 28 { + if self.common.length() > 28 { buf.write_u16::(self.offset_width.unwrap())?; buf.write_u16::(self.offset_height.unwrap())?; buf.write_all(&self.unknown_2.unwrap())?; @@ -142,74 +103,16 @@ impl CzHeader for Cz0Header { } } -impl CzImage for Cz0Image { - type Header = Cz0Header; - - fn decode(bytes: &mut T) -> Result { - // Get the header from the input - let header = Cz0Header::new(bytes)?; - bytes.seek(io::SeekFrom::Start(header.length() as u64))?; - - // Get the rest of the file, which is the bitmap - let mut bitmap = vec![]; - bytes.read_to_end(&mut bitmap)?; - - let bpp = (header.depth() >> 3) as usize; - - if bitmap.len() != (header.width() as usize * header.height() as usize) * bpp { - return Err(CzError::Corrupt) - } - - Ok(Self { header, bitmap }) - } - - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - let mut output_file = BufWriter::new(File::create(path.into())?); - - output_file.write_all(&self.header().to_bytes()?)?; - output_file.write_all(&self.bitmap)?; - output_file.flush()?; - - Ok(()) - } - - fn header(&self) -> &Self::Header { - &self.header - } - - fn set_header(&mut self, header: &Self::Header) { - self.header = *header - } - - fn bitmap(&self) -> &Vec { - &self.bitmap - } - - fn into_bitmap(self) -> Vec { - self.bitmap - } - - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - self.bitmap = bitmap.to_vec(); - - self.header.common.width = width; - self.header.common.height = height; - } +#[derive(Debug)] +pub struct Cz0Image { + header: Cz0Header, + bitmap: Vec, } -impl TryFrom<&[u8]> for Cz0Image { - type Error = CzError; +pub fn decode(bytes: &mut T) -> Result, CzError> { + // Get the rest of the file, which is the bitmap + let mut bitmap = vec![]; + bytes.read_to_end(&mut bitmap)?; - fn try_from(value: &[u8]) -> Result { - let mut input = Cursor::new(value); - - // Get the header from the input - let header = Cz0Header::new(&mut input)?; - - // Get the rest of the file, which is the bitmap - let mut bitmap = vec![]; - input.read_to_end(&mut bitmap)?; - - Ok(Self { header, bitmap }) - } + Ok(bitmap) } diff --git a/cz/src/formats/cz1.rs b/cz/src/formats/cz1.rs index d41a3a0..686c268 100644 --- a/cz/src/formats/cz1.rs +++ b/cz/src/formats/cz1.rs @@ -1,94 +1,16 @@ use byteorder::ReadBytesExt; -use std::{ - fs::File, - io::{BufWriter, Read, Seek, SeekFrom, Write}, - path::PathBuf, -}; +use std::io::{Read, Seek, SeekFrom}; -use crate::common::{apply_palette, parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; -use crate::compression::{decompress, parse_chunk_info}; +use crate::common::CzError; +use crate::compression::{decompress, get_chunk_info}; -#[derive(Debug, Clone)] -pub struct Cz1Image { - header: CommonHeader, - bitmap: Vec, - palette: Option>, -} - -impl CzImage for Cz1Image { - type Header = CommonHeader; - - fn decode(bytes: &mut T) -> Result { - // Get the header from the input - let mut header = CommonHeader::new(bytes).unwrap(); - bytes.seek(SeekFrom::Start(header.length() as u64))?; - - if header.version() != 1 { - return Err(CzError::VersionMismatch(1, header.version())); - } - - // Lock the color depth to 8 if it's over 32 - // This is obviously wrong, but why is it wrong? - if header.depth() > 32 { - header.depth = 8 - } - - // The color palette, gotten for 8 and 4 BPP images - let mut palette = None; - if header.depth() == 8 || header.depth() == 4 { - palette = Some(parse_colormap(bytes, 1 << header.depth())?); - } - - // Get the information about the compressed chunks - let chunk_info = parse_chunk_info(bytes)?; - - // Get the bitmap - let mut bitmap = decompress(bytes, &chunk_info).unwrap(); - - // Apply the palette if it exists - if let Some(pal) = &palette { - bitmap = apply_palette(&mut bitmap.as_slice(), pal); - } - - let image = Self { - header, - bitmap, - palette, - }; - - Ok(image) - } - - fn header(&self) -> &Self::Header { - &self.header - } - - fn header_mut(&mut self) -> &mut Self::Header { - &mut self.header - } - - fn set_header(&mut self, header:& Self::Header) { - self.header = *header - } - - fn bitmap(&self) -> &Vec { - &self.bitmap - } - - fn into_bitmap(self) -> Vec { - self.bitmap - } - - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - let mut output_file = BufWriter::new(File::create(path.into())?); - - output_file.write_all(&self.header.to_bytes()?)?; - - output_file.flush()?; - Ok(()) - } - - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - todo!() - } +pub fn decode(bytes: &mut T) -> Result, CzError> { + // Get the information about the compressed chunks + let block_info = get_chunk_info(bytes)?; + bytes.seek(SeekFrom::Start(block_info.length as u64))?; + + // Get the bitmap + let bitmap = decompress(bytes, &block_info).unwrap(); + + Ok(bitmap) } diff --git a/cz/src/formats/cz2.rs b/cz/src/formats/cz2.rs index cbf2742..42a4a3c 100644 --- a/cz/src/formats/cz2.rs +++ b/cz/src/formats/cz2.rs @@ -1,145 +1,16 @@ -use byteorder::{ReadBytesExt, WriteBytesExt}; -use std::{ - fs::File, - io::{BufWriter, Read, Seek, SeekFrom, Write}, - path::PathBuf, -}; +use byteorder::ReadBytesExt; +use std::io::{Read, Seek, SeekFrom}; -use crate::common::{apply_palette, parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; -use crate::compression::{decompress_2, parse_chunk_info}; +use crate::common::CzError; +use crate::compression::{decompress_2, get_chunk_info}; -#[derive(Debug, Clone, Copy)] -pub struct Cz2Header { - common: CommonHeader, - unknown_1: u8, - unknown_2: u8, - unknown_3: u8, -} - -impl CzHeader for Cz2Header { - fn new(bytes: &mut T) -> Result - where - Self: Sized, - { - let common = CommonHeader::new(bytes)?; - - if common.version() != 2 { - return Err(CzError::VersionMismatch(2, common.version())); - } - - Ok(Self { - common, - unknown_1: bytes.read_u8()?, - unknown_2: bytes.read_u8()?, - unknown_3: bytes.read_u8()?, - }) - } - - fn common(&self) -> &CommonHeader { - &self.common - } - - fn to_bytes(&self) -> Result, std::io::Error> { - let mut buf = vec![]; - - buf.write_all(&self.common.to_bytes()?)?; - buf.write_u8(self.unknown_1)?; - buf.write_u8(self.unknown_2)?; - buf.write_u8(self.unknown_3)?; - - Ok(buf) - } - - fn version(&self) -> u8 { - self.common.version() - } - - fn length(&self) -> usize { - self.common.length() - } - - fn width(&self) -> u16 { - self.common.width() - } - - fn height(&self) -> u16 { - self.common.height() - } - - fn depth(&self) -> u16 { - self.common.depth() - } - - fn color_block(&self) -> u8 { - self.common.color_block() - } -} - -#[derive(Debug, Clone)] -pub struct Cz2Image { - header: Cz2Header, - bitmap: Vec, - palette: Vec<[u8; 4]>, -} - -impl CzImage for Cz2Image { - type Header = Cz2Header; - - fn decode(bytes: &mut T) -> Result { - let header = Cz2Header::new(bytes).unwrap(); - bytes.seek(SeekFrom::Start(header.length() as u64))?; - - // The color palette, gotten for 8 and 4 BPP images - let mut palette = None; - if header.depth() == 8 || header.depth() == 4 { - palette = Some(parse_colormap(bytes, 1 << header.depth())?); - } - - let chunk_info = parse_chunk_info(bytes)?; - bytes.seek(SeekFrom::Start(chunk_info.length as u64))?; - - let mut bitmap = decompress_2(bytes, &chunk_info).unwrap(); - - // Apply the palette if it exists - if let Some(pal) = &palette { - bitmap = apply_palette(&mut bitmap.as_slice(), pal); - } - - let image = Self { - header, - bitmap, - palette: palette.unwrap(), - }; - - Ok(image) - } - - fn header(&self) -> &Self::Header { - &self.header - } - - fn set_header(&mut self, header: &Self::Header) { - self.header = *header - } - - fn bitmap(&self) -> &Vec { - &self.bitmap - } - - fn into_bitmap(self) -> Vec { - self.bitmap - } - - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - let mut output_file = BufWriter::new(File::create(path.into())?); - - output_file.write_all(&self.header.to_bytes()?)?; - - output_file.flush()?; - Ok(()) - } - - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - todo!() - } +pub fn decode(bytes: &mut T) + -> Result, CzError> +{ + let block_info = get_chunk_info(bytes)?; + bytes.seek(SeekFrom::Start(block_info.length as u64))?; + + let bitmap = decompress_2(bytes, &block_info).unwrap(); + + Ok(bitmap) } diff --git a/cz/src/formats/cz3.rs b/cz/src/formats/cz3.rs index 782eb83..5cf6f72 100644 --- a/cz/src/formats/cz3.rs +++ b/cz/src/formats/cz3.rs @@ -1,154 +1,17 @@ -use std::{ - io::{self, Read, Seek, SeekFrom}, - path::PathBuf, -}; +use std::io::{Read, Seek, SeekFrom}; +use byteorder::ReadBytesExt; -use byteorder::{LittleEndian, ReadBytesExt}; +use crate::common::{CommonHeader, CzError}; +use crate::compression::{decompress, line_diff, get_chunk_info}; -use crate::common::{CommonHeader, CzError, CzHeader, CzImage}; -use crate::compression::{decompress, line_diff, parse_chunk_info}; +pub fn decode(bytes: &mut T, header: &CommonHeader) + -> Result, CzError> +{ + let block_info = get_chunk_info(bytes)?; + bytes.seek(SeekFrom::Start(block_info.length as u64))?; -#[derive(Debug, Clone, Copy)] -pub struct Cz3Header { - /// Common CZ# header - common: CommonHeader, + let bitmap = decompress(bytes, &block_info)?; + let bitmap = line_diff(header, &bitmap); - /// Width of cropped image area - pub crop_width: u16, - - /// Height of cropped image area - pub crop_height: u16, - - /// Bounding box width - pub bounds_width: u16, - - /// Bounding box height - pub bounds_height: u16, - - /// Offset width - pub offset_width: Option, - - /// Offset height - pub offset_height: Option, -} - -impl CzHeader for Cz3Header { - fn new(bytes: &mut T) -> Result - where - Self: Sized, - { - let common = CommonHeader::new(bytes)?; - - if common.version() != 3 { - return Err(CzError::VersionMismatch(3, common.version())); - } - - let mut unknown_1 = [0u8; 5]; - bytes.read_exact(&mut unknown_1)?; - - let crop_width = bytes.read_u16::()?; - let crop_height = bytes.read_u16::()?; - - let bounds_width = bytes.read_u16::()?; - let bounds_height = bytes.read_u16::()?; - - let mut offset_width = None; - let mut offset_height = None; - if common.length() > 28 { - offset_width = Some(bytes.read_u16::()?); - offset_height = Some(bytes.read_u16::()?); - } - - Ok(Self { - common, - - crop_width, - crop_height, - - bounds_width, - bounds_height, - - offset_width, - offset_height, - }) - } - - fn common(&self) -> &CommonHeader { - &self.common - } - - fn version(&self) -> u8 { - self.common.version() - } - - fn length(&self) -> usize { - self.common.length() - } - - fn width(&self) -> u16 { - self.common.width() - } - - fn height(&self) -> u16 { - self.common.height() - } - - fn depth(&self) -> u16 { - self.common.depth() - } - - fn color_block(&self) -> u8 { - self.common.color_block() - } - - fn to_bytes(&self) -> Result, io::Error> { - todo!() - } -} - -#[derive(Debug, Clone)] -pub struct Cz3Image { - header: Cz3Header, - bitmap: Vec, -} - -impl CzImage for Cz3Image { - type Header = Cz3Header; - - fn decode(bytes: &mut T) -> Result { - let header = Cz3Header::new(bytes)?; - bytes.seek(SeekFrom::Start(header.length() as u64))?; - - let block_info = parse_chunk_info(bytes)?; - - let bitmap = decompress(bytes, &block_info)?; - - let bitmap = line_diff(&header, &bitmap); - - Ok(Self { header, bitmap }) - } - - fn header(&self) -> &Self::Header { - &self.header - } - - fn set_header(&mut self, header: &Self::Header) { - self.header = *header - } - - fn bitmap(&self) -> &Vec { - &self.bitmap - } - - fn into_bitmap(self) -> Vec { - self.bitmap - } - - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - todo!() - } - - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - todo!() - } + Ok(bitmap) } diff --git a/cz/src/formats/cz4.rs b/cz/src/formats/cz4.rs index 56d46ee..7dff272 100644 --- a/cz/src/formats/cz4.rs +++ b/cz/src/formats/cz4.rs @@ -1,127 +1,24 @@ -use std::{ - io::{self, Read, Seek, SeekFrom}, - path::PathBuf, -}; +use std::io::{Read, Seek, SeekFrom}; use byteorder::ReadBytesExt; -use crate::common::{CommonHeader, CzError, CzHeader, CzImage}; -use crate::compression::{decompress, line_diff_cz4, parse_chunk_info}; +use crate::common::{CommonHeader, CzError, CzHeader}; +use crate::compression::{decompress, line_diff_cz4, get_chunk_info}; -#[derive(Debug, Clone, Copy)] -pub struct Cz4Header { - /// Common CZ# header - common: CommonHeader, -} - -impl CzHeader for Cz4Header { - fn new(bytes: &mut T) -> Result - where - Self: Sized, - { - let common = CommonHeader::new(bytes)?; - - if common.version() != 4 { - return Err(CzError::VersionMismatch(4, common.version())); - } - - Ok(Self { common }) - } - - fn common(&self) -> &CommonHeader { - &self.common - } - - fn version(&self) -> u8 { - self.common.version() - } - - fn length(&self) -> usize { - self.common.length() - } - - fn width(&self) -> u16 { - self.common.width() - } - - fn height(&self) -> u16 { - self.common.height() - } - - fn depth(&self) -> u16 { - self.common.depth() - } - - fn color_block(&self) -> u8 { - self.common.color_block() - } - - fn to_bytes(&self) -> Result, io::Error> { - todo!() - } -} - -#[derive(Debug, Clone)] -pub struct Cz4Image { - header: Cz4Header, - bitmap: Vec, -} - -impl CzImage for Cz4Image { - type Header = Cz4Header; - - fn decode(bytes: &mut T) -> Result { - let header = Cz4Header::new(bytes)?; - bytes.seek(SeekFrom::Start(header.length() as u64))?; - - let block_info = parse_chunk_info(bytes)?; - bytes.seek(SeekFrom::Start(block_info.length as u64))?; - - let pcount = (header.width() as usize * header.height() as usize) * 3; - let data = decompress(bytes, &block_info)?; - let data2 = data[pcount..].to_vec(); - - let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32); - - let pixel_byte_count = 3; - line_diff_cz4(&mut picture, pixel_byte_count, &data); - - let pixel_byte_count = 1; - line_diff_cz4(&mut picture, pixel_byte_count, &data2); - - Ok(Self { - header, - bitmap: picture.into_raw() - }) - } - - fn header(&self) -> &Self::Header { - &self.header - } - - fn header_mut(&mut self) -> &mut Self::Header { - &mut self.header - } - - fn set_header(&mut self, header: &Self::Header) { - self.header = *header - } - - fn bitmap(&self) -> &Vec { - &self.bitmap - } - - fn into_bitmap(self) -> Vec { - self.bitmap - } - - fn save_as_cz>(&self, path: T) -> Result<(), CzError> { - todo!() - } - - fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { - self.bitmap = bitmap.to_vec(); - - self.header.common.width = width; - self.header.common.height = height; - } +pub fn decode(bytes: &mut T, header: &CommonHeader) -> Result, CzError> { + let block_info = get_chunk_info(bytes)?; + bytes.seek(SeekFrom::Start(block_info.length as u64))?; + + let pcount = (header.width() as usize * header.height() as usize) * 3; + let data = decompress(bytes, &block_info)?; + let data2 = data[pcount..].to_vec(); + + let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32); + + let pixel_byte_count = 3; + line_diff_cz4(&mut picture, pixel_byte_count, &data); + + let pixel_byte_count = 1; + line_diff_cz4(&mut picture, pixel_byte_count, &data2); + + Ok(picture.into_raw()) } diff --git a/cz/src/lib.rs b/cz/src/lib.rs index 05dd8c2..07a6f42 100644 --- a/cz/src/lib.rs +++ b/cz/src/lib.rs @@ -3,6 +3,7 @@ mod compression; pub mod dynamic; pub mod common; + pub mod formats { pub mod cz0; pub mod cz1; @@ -11,6 +12,7 @@ pub mod formats { pub mod cz4; } +/* #[doc(inline)] pub use formats::cz0::Cz0Image; #[doc(inline)] @@ -21,7 +23,4 @@ pub use formats::cz2::Cz2Image; pub use formats::cz3::Cz3Image; #[doc(inline)] pub use formats::cz4::Cz4Image; - -/// Traits for CZ# images -#[doc(inline)] -pub use common::CzImage; +*/ diff --git a/utils/src/main.rs b/utils/src/main.rs index 1d9c382..3cbe0ee 100644 --- a/utils/src/main.rs +++ b/utils/src/main.rs @@ -8,7 +8,7 @@ fn main() { let mut total_time = Duration::default(); let mut num_images = 0; - for entry in WalkDir::new("../../test_files/loopers/") { + for entry in WalkDir::new("../../test_files/") { let entry = entry.unwrap(); if entry.path().is_dir() { continue; @@ -18,7 +18,7 @@ fn main() { let img = match DynamicCz::open(entry.path()) { Ok(img) => img, Err(err) => { - println!("{}: {}", entry.path().file_name().unwrap().to_string_lossy(), err); + println!("{}: {:?}", entry.path().file_name().unwrap().to_string_lossy(), err); continue; }, };