Moved all implementations to common struct

This commit is contained in:
G2-Games 2024-05-08 11:28:34 -05:00
parent 3c01617a5c
commit f4eb9ea1b3
11 changed files with 232 additions and 750 deletions

View file

@ -10,6 +10,3 @@ authors = ["G2"]
[workspace.lints.rust] [workspace.lints.rust]
unsafe_code = "forbid" unsafe_code = "forbid"
[build]
rustflags = ["-C", "target-cpu=native"]

View file

@ -1,9 +1,6 @@
//! Shared types and traits between CZ# files //! Shared types and traits between CZ# files
use std::{ use std::io::{self, Read, Seek, Write};
io::{self, Read, Seek, Write},
path::PathBuf,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use thiserror::Error; use thiserror::Error;
@ -13,25 +10,51 @@ pub enum CzError {
#[error("Expected CZ{}, got CZ{}", 0, 1)] #[error("Expected CZ{}, got CZ{}", 0, 1)]
VersionMismatch(u8, u8), 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")] #[error("File data is incorrect, it might be corrupt")]
Corrupt, Corrupt,
#[error("File is not a CZ image")] #[error("File is not a CZ image")]
NotCzFile, NotCzFile,
#[error("Failed to read/write input/output: {}", 0)] #[error("Failed to read/write input/output")]
IoError(#[from] io::Error), IoError(#[from] io::Error),
#[error("Problem while decoding file")] #[error("Problem while decoding file")]
DecodeError, DecodeError,
} }
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum CzVersion { pub enum CzVersion {
CZ0, CZ0,
CZ1, CZ1,
CZ2, CZ2,
CZ3, CZ3,
CZ4, CZ4,
CZ5,
}
impl TryFrom<u8> for CzVersion {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
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 { pub trait CzHeader {
@ -46,10 +69,10 @@ pub trait CzHeader {
fn to_bytes(&self) -> Result<Vec<u8>, io::Error>; fn to_bytes(&self) -> Result<Vec<u8>, io::Error>;
/// The version of the image /// The version of the image
fn version(&self) -> u8; fn version(&self) -> CzVersion;
/// Set the version of the image /// 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 /// The length of the header in bytes
fn length(&self) -> usize; fn length(&self) -> usize;
@ -58,13 +81,13 @@ pub trait CzHeader {
fn width(&self) -> u16; fn width(&self) -> u16;
/// Set the width of the image /// Set the width of the image
fn set_width(&mut self); fn set_width(&mut self, width: u16);
/// The height of the image /// The height of the image
fn height(&self) -> u16; fn height(&self) -> u16;
/// Set the height of the image /// 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) /// The bit depth of the image (BPP)
fn depth(&self) -> u16; fn depth(&self) -> u16;
@ -81,22 +104,22 @@ pub trait CzHeader {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct CommonHeader { pub struct CommonHeader {
/// Format version from the magic bytes, (eg. CZ3, CZ4) /// Format version from the magic bytes, (eg. CZ3, CZ4)
pub version: u8, version: CzVersion,
/// Length of the header in bytes /// Length of the header in bytes
pub length: u32, length: u32,
/// Width of the image in pixels /// Width of the image in pixels
pub width: u16, width: u16,
/// Height of the image in pixels /// Height of the image in pixels
pub height: u16, height: u16,
/// Bit depth in Bits Per Pixel (BPP) /// Bit depth in Bits Per Pixel (BPP)
pub depth: u16, depth: u16,
/// Color block /// Color block? This byte's purpose is unclear
pub color_block: u8, unknown: u8,
} }
impl CzHeader for CommonHeader { impl CzHeader for CommonHeader {
@ -111,24 +134,42 @@ impl CzHeader for CommonHeader {
return Err(CzError::NotCzFile); return Err(CzError::NotCzFile);
} }
Ok(Self { // Ensure the version matches a CZ file type
version: magic[2] - b'0', 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::<LittleEndian>()?, length: bytes.read_u32::<LittleEndian>()?,
width: bytes.read_u16::<LittleEndian>()?, width: bytes.read_u16::<LittleEndian>()?,
height: bytes.read_u16::<LittleEndian>()?, height: bytes.read_u16::<LittleEndian>()?,
depth: bytes.read_u16::<LittleEndian>()?, depth: bytes.read_u16::<LittleEndian>()?,
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 { fn common(&self) -> &CommonHeader {
self self
} }
fn version(&self) -> u8 { fn version(&self) -> CzVersion {
self.version self.version
} }
fn set_version(&mut self, version: CzVersion) {
self.version = version
}
fn length(&self) -> usize { fn length(&self) -> usize {
self.length as usize self.length as usize
} }
@ -137,22 +178,30 @@ impl CzHeader for CommonHeader {
self.width self.width
} }
fn set_width(&mut self, width: u16) {
self.width = width
}
fn height(&self) -> u16 { fn height(&self) -> u16 {
self.height self.height
} }
fn set_height(&mut self, height: u16) {
self.height = height
}
fn depth(&self) -> u16 { fn depth(&self) -> u16 {
self.depth self.depth
} }
fn color_block(&self) -> u8 { fn color_block(&self) -> u8 {
self.color_block self.unknown
} }
fn to_bytes(&self) -> Result<Vec<u8>, io::Error> { fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
let mut buf = vec![]; 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_all(&magic_bytes)?;
buf.write_u32::<LittleEndian>(self.length() as u32)?; buf.write_u32::<LittleEndian>(self.length() as u32)?;
buf.write_u16::<LittleEndian>(self.width())?; buf.write_u16::<LittleEndian>(self.width())?;
@ -164,35 +213,7 @@ impl CzHeader for CommonHeader {
} }
} }
pub trait CzImage { pub fn get_palette<T: Seek + ReadBytesExt + Read>(
type Header;
/// Create a [crate::CzImage] from bytes
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized;
/// Save the image as its corresponding CZ# type
fn save_as_cz<T: Into<PathBuf>>(&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<u8>;
/// Get the raw underlying bitmap for an image
fn into_bitmap(self) -> Vec<u8>;
/// Set the bitmap the image contains
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16);
}
pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
input: &mut T, input: &mut T,
num_colors: usize, num_colors: usize,
) -> Result<Vec<[u8; 4]>, CzError> { ) -> Result<Vec<[u8; 4]>, CzError> {
@ -207,13 +228,20 @@ pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
Ok(colormap) Ok(colormap)
} }
pub fn apply_palette(input: &mut &[u8], palette: &[[u8; 4]]) -> Vec<u8> { pub fn apply_palette(
input: &[u8],
palette: &[[u8; 4]]
) -> Result<Vec<u8>, CzError> {
let mut output_map = Vec::new(); let mut output_map = Vec::new();
for byte in input.iter() { for byte in input.iter() {
let color = palette[*byte as usize]; let color = palette.get(*byte as usize);
output_map.extend_from_slice(&color); if let Some(color) = color {
output_map.extend_from_slice(color);
} else {
return Err(CzError::PaletteError)
}
} }
output_map Ok(output_map)
} }

View file

@ -41,7 +41,7 @@ pub struct CompressionInfo {
/// ///
/// These are defined by a length value, followed by the number of data chunks /// 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 /// that length value says split into compressed and original size u32 values
pub fn parse_chunk_info<T: Seek + ReadBytesExt + Read>( pub fn get_chunk_info<T: Seek + ReadBytesExt + Read>(
bytes: &mut T, bytes: &mut T,
) -> Result<CompressionInfo, CzError> { ) -> Result<CompressionInfo, CzError> {
let parts_count = bytes.read_u32::<LittleEndian>()?; let parts_count = bytes.read_u32::<LittleEndian>()?;
@ -218,7 +218,7 @@ pub fn line_diff<T: CzHeader>(header: &T, data: &[u8]) -> Vec<u8> {
let mut output_buf = data.to_vec(); let mut output_buf = data.to_vec();
let block_height = 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 pixel_byte_count = header.depth() >> 3;
let line_byte_count = (width * pixel_byte_count as u32) as usize; let line_byte_count = (width * pixel_byte_count as u32) as usize;

View file

@ -1,23 +1,18 @@
use std::{ use std::{
io::{BufReader, Read, Seek}, io::{BufReader, Read, Seek, SeekFrom},
path::Path path::Path
}; };
use byteorder::ReadBytesExt; use byteorder::ReadBytesExt;
use crate::{ use crate::{
common::{CommonHeader, CzError, CzVersion, CzHeader}, common::{apply_palette, get_palette, CommonHeader, CzError, CzHeader, CzVersion},
Cz0Image, formats::{cz0, cz1, cz2, cz3, cz4},
Cz1Image,
Cz2Image,
Cz3Image,
Cz4Image,
CzImage
}; };
struct DynamicCz { pub struct DynamicCz {
cz_type: CzVersion,
bitmap: Vec<u8>,
header_common: CommonHeader, header_common: CommonHeader,
palette: Option<Vec<[u8; 4]>>,
bitmap: Vec<u8>,
} }
impl DynamicCz { impl DynamicCz {
@ -46,74 +41,81 @@ impl DynamicCz {
} }
} }
impl CzImage for DynamicCz { impl DynamicCz {
type Header = CommonHeader; pub fn decode<T: Seek + ReadBytesExt + Read>(input: &mut T) -> Result<Self, CzError> {
// 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<T: Seek + ReadBytesExt + Read>(input: &mut T) // Get the color palette if the bit depth is 8 or less
-> Result<DynamicCz, crate::common::CzError> let palette = if header_common.depth() <= 8 {
{ let color_count = 1 << header_common.depth();
let common_header = CommonHeader::new(input)?; Some(get_palette(input, color_count)?)
input.seek(std::io::SeekFrom::Start(0))?; } else {
None
};
Ok(match common_header.version() { // Get the image data as a bitmap
0 => DynamicCz::CZ0(Cz0Image::decode(input)?), let mut bitmap = match header_common.version() {
1 => DynamicCz::CZ1(Cz1Image::decode(input)?), CzVersion::CZ0 => cz0::decode(input)?,
2 => DynamicCz::CZ2(Cz2Image::decode(input)?), CzVersion::CZ1 => cz1::decode(input)?,
3 => DynamicCz::CZ3(Cz3Image::decode(input)?), CzVersion::CZ2 => cz2::decode(input)?,
4 => DynamicCz::CZ4(Cz4Image::decode(input)?), CzVersion::CZ3 => cz3::decode(input, &header_common)?,
_ => return Err(CzError::NotCzFile), 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<T: Into<std::path::PathBuf>>(&self, path: T) -> Result<(), CzError> { pub fn save_as_cz<T: Into<std::path::PathBuf>>(&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 {
todo!() todo!()
} }
fn set_header(&mut self, header: &Self::Header) { pub fn header(&self) -> &CommonHeader {
todo!() &self.header_common
} }
fn bitmap(&self) -> &Vec<u8> { pub fn header_mut(&mut self) -> &mut CommonHeader {
match self { &mut self.header_common
DynamicCz::CZ0(img) => img.bitmap(), }
DynamicCz::CZ1(img) => img.bitmap(),
DynamicCz::CZ2(img) => img.bitmap(), pub fn set_header(&mut self, header: &CommonHeader) {
DynamicCz::CZ3(img) => img.bitmap(), self.header_common = header.to_owned()
DynamicCz::CZ4(img) => img.bitmap(), }
pub fn bitmap(&self) -> &Vec<u8> {
&self.bitmap
}
pub fn into_bitmap(self) -> Vec<u8> {
self.bitmap
}
pub fn set_bitmap(&mut self, bitmap: Vec<u8>, width: u16, height: u16) -> Result<(), CzError> {
if bitmap.len() != width as usize * height as usize {
return Err(CzError::BitmapFormat)
} }
}
fn into_bitmap(self) -> Vec<u8> { self.bitmap = bitmap;
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(),
}
}
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) { self.header_mut().set_width(width);
unimplemented!() self.header_mut().set_height(height);
Ok(())
} }
} }

View file

@ -1,12 +1,7 @@
use std::{ use std::io::{self, Read, Seek, Write};
fs::File,
io::{self, BufWriter, Cursor, Read, Seek, Write},
path::PathBuf,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::common::{CommonHeader, CzError, CzHeader, CzImage}; use crate::common::{CommonHeader, CzError, CzHeader, CzVersion};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Cz0Header { pub struct Cz0Header {
@ -37,21 +32,15 @@ pub struct Cz0Header {
unknown_2: Option<[u8; 4]>, unknown_2: Option<[u8; 4]>,
} }
#[derive(Debug)] impl Cz0Header {
pub struct Cz0Image {
header: Cz0Header,
bitmap: Vec<u8>,
}
impl CzHeader for Cz0Header {
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where where
Self: Sized, Self: Sized,
{ {
let common = CommonHeader::new(bytes)?; let common = CommonHeader::new(bytes)?;
if common.version() != 0 { if common.version() != CzVersion::CZ0 {
return Err(CzError::VersionMismatch(common.version(), 0)); return Err(CzError::VersionMismatch(common.version() as u8, 0));
} }
let mut unknown_1 = [0u8; 5]; 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<Vec<u8>, io::Error> { fn to_bytes(&self) -> Result<Vec<u8>, io::Error> {
let mut buf = vec![]; let mut buf = vec![];
@ -132,7 +93,7 @@ impl CzHeader for Cz0Header {
buf.write_u16::<LittleEndian>(self.bounds_width)?; buf.write_u16::<LittleEndian>(self.bounds_width)?;
buf.write_u16::<LittleEndian>(self.bounds_height)?; buf.write_u16::<LittleEndian>(self.bounds_height)?;
if self.length() > 28 { if self.common.length() > 28 {
buf.write_u16::<LittleEndian>(self.offset_width.unwrap())?; buf.write_u16::<LittleEndian>(self.offset_width.unwrap())?;
buf.write_u16::<LittleEndian>(self.offset_height.unwrap())?; buf.write_u16::<LittleEndian>(self.offset_height.unwrap())?;
buf.write_all(&self.unknown_2.unwrap())?; buf.write_all(&self.unknown_2.unwrap())?;
@ -142,74 +103,16 @@ impl CzHeader for Cz0Header {
} }
} }
impl CzImage for Cz0Image { #[derive(Debug)]
type Header = Cz0Header; pub struct Cz0Image {
header: Cz0Header,
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> { bitmap: Vec<u8>,
// 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<T: Into<PathBuf>>(&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<u8> {
&self.bitmap
}
fn into_bitmap(self) -> Vec<u8> {
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;
}
} }
impl TryFrom<&[u8]> for Cz0Image { pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, CzError> {
type Error = 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<Self, Self::Error> { Ok(bitmap)
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 })
}
} }

View file

@ -1,94 +1,16 @@
use byteorder::ReadBytesExt; use byteorder::ReadBytesExt;
use std::{ use std::io::{Read, Seek, SeekFrom};
fs::File,
io::{BufWriter, Read, Seek, SeekFrom, Write},
path::PathBuf,
};
use crate::common::{apply_palette, parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; use crate::common::CzError;
use crate::compression::{decompress, parse_chunk_info}; use crate::compression::{decompress, get_chunk_info};
#[derive(Debug, Clone)] pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, CzError> {
pub struct Cz1Image { // Get the information about the compressed chunks
header: CommonHeader, let block_info = get_chunk_info(bytes)?;
bitmap: Vec<u8>, bytes.seek(SeekFrom::Start(block_info.length as u64))?;
palette: Option<Vec<[u8; 4]>>,
} // Get the bitmap
let bitmap = decompress(bytes, &block_info).unwrap();
impl CzImage for Cz1Image {
type Header = CommonHeader; Ok(bitmap)
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
// 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<u8> {
&self.bitmap
}
fn into_bitmap(self) -> Vec<u8> {
self.bitmap
}
fn save_as_cz<T: Into<PathBuf>>(&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!()
}
} }

View file

@ -1,145 +1,16 @@
use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::ReadBytesExt;
use std::{ use std::io::{Read, Seek, SeekFrom};
fs::File,
io::{BufWriter, Read, Seek, SeekFrom, Write},
path::PathBuf,
};
use crate::common::{apply_palette, parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; use crate::common::CzError;
use crate::compression::{decompress_2, parse_chunk_info}; use crate::compression::{decompress_2, get_chunk_info};
#[derive(Debug, Clone, Copy)] pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T)
pub struct Cz2Header { -> Result<Vec<u8>, CzError>
common: CommonHeader, {
unknown_1: u8, let block_info = get_chunk_info(bytes)?;
unknown_2: u8, bytes.seek(SeekFrom::Start(block_info.length as u64))?;
unknown_3: u8,
} let bitmap = decompress_2(bytes, &block_info).unwrap();
impl CzHeader for Cz2Header { Ok(bitmap)
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
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<Vec<u8>, 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<u8>,
palette: Vec<[u8; 4]>,
}
impl CzImage for Cz2Image {
type Header = Cz2Header;
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
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<u8> {
&self.bitmap
}
fn into_bitmap(self) -> Vec<u8> {
self.bitmap
}
fn save_as_cz<T: Into<PathBuf>>(&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!()
}
} }

View file

@ -1,154 +1,17 @@
use std::{ use std::io::{Read, Seek, SeekFrom};
io::{self, Read, Seek, SeekFrom}, use byteorder::ReadBytesExt;
path::PathBuf,
};
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}; pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T, header: &CommonHeader)
use crate::compression::{decompress, line_diff, parse_chunk_info}; -> Result<Vec<u8>, CzError>
{
let block_info = get_chunk_info(bytes)?;
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
#[derive(Debug, Clone, Copy)] let bitmap = decompress(bytes, &block_info)?;
pub struct Cz3Header { let bitmap = line_diff(header, &bitmap);
/// Common CZ# header
common: CommonHeader,
/// Width of cropped image area Ok(bitmap)
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<u16>,
/// Offset height
pub offset_height: Option<u16>,
}
impl CzHeader for Cz3Header {
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
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::<LittleEndian>()?;
let crop_height = bytes.read_u16::<LittleEndian>()?;
let bounds_width = bytes.read_u16::<LittleEndian>()?;
let bounds_height = bytes.read_u16::<LittleEndian>()?;
let mut offset_width = None;
let mut offset_height = None;
if common.length() > 28 {
offset_width = Some(bytes.read_u16::<LittleEndian>()?);
offset_height = Some(bytes.read_u16::<LittleEndian>()?);
}
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<Vec<u8>, io::Error> {
todo!()
}
}
#[derive(Debug, Clone)]
pub struct Cz3Image {
header: Cz3Header,
bitmap: Vec<u8>,
}
impl CzImage for Cz3Image {
type Header = Cz3Header;
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
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<u8> {
&self.bitmap
}
fn into_bitmap(self) -> Vec<u8> {
self.bitmap
}
fn save_as_cz<T: Into<PathBuf>>(&self, path: T) -> Result<(), CzError> {
todo!()
}
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) {
todo!()
}
} }

View file

@ -1,127 +1,24 @@
use std::{ use std::io::{Read, Seek, SeekFrom};
io::{self, Read, Seek, SeekFrom},
path::PathBuf,
};
use byteorder::ReadBytesExt; use byteorder::ReadBytesExt;
use crate::common::{CommonHeader, CzError, CzHeader, CzImage}; use crate::common::{CommonHeader, CzError, CzHeader};
use crate::compression::{decompress, line_diff_cz4, parse_chunk_info}; use crate::compression::{decompress, line_diff_cz4, get_chunk_info};
#[derive(Debug, Clone, Copy)] pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T, header: &CommonHeader) -> Result<Vec<u8>, CzError> {
pub struct Cz4Header { let block_info = get_chunk_info(bytes)?;
/// Common CZ# header bytes.seek(SeekFrom::Start(block_info.length as u64))?;
common: CommonHeader,
} let pcount = (header.width() as usize * header.height() as usize) * 3;
let data = decompress(bytes, &block_info)?;
impl CzHeader for Cz4Header { let data2 = data[pcount..].to_vec();
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where let mut picture = image::RgbaImage::new(header.width() as u32, header.height() as u32);
Self: Sized,
{ let pixel_byte_count = 3;
let common = CommonHeader::new(bytes)?; line_diff_cz4(&mut picture, pixel_byte_count, &data);
if common.version() != 4 { let pixel_byte_count = 1;
return Err(CzError::VersionMismatch(4, common.version())); line_diff_cz4(&mut picture, pixel_byte_count, &data2);
}
Ok(picture.into_raw())
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<Vec<u8>, io::Error> {
todo!()
}
}
#[derive(Debug, Clone)]
pub struct Cz4Image {
header: Cz4Header,
bitmap: Vec<u8>,
}
impl CzImage for Cz4Image {
type Header = Cz4Header;
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
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<u8> {
&self.bitmap
}
fn into_bitmap(self) -> Vec<u8> {
self.bitmap
}
fn save_as_cz<T: Into<PathBuf>>(&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;
}
} }

View file

@ -3,6 +3,7 @@ mod compression;
pub mod dynamic; pub mod dynamic;
pub mod common; pub mod common;
pub mod formats { pub mod formats {
pub mod cz0; pub mod cz0;
pub mod cz1; pub mod cz1;
@ -11,6 +12,7 @@ pub mod formats {
pub mod cz4; pub mod cz4;
} }
/*
#[doc(inline)] #[doc(inline)]
pub use formats::cz0::Cz0Image; pub use formats::cz0::Cz0Image;
#[doc(inline)] #[doc(inline)]
@ -21,7 +23,4 @@ pub use formats::cz2::Cz2Image;
pub use formats::cz3::Cz3Image; pub use formats::cz3::Cz3Image;
#[doc(inline)] #[doc(inline)]
pub use formats::cz4::Cz4Image; pub use formats::cz4::Cz4Image;
*/
/// Traits for CZ# images
#[doc(inline)]
pub use common::CzImage;

View file

@ -8,7 +8,7 @@ fn main() {
let mut total_time = Duration::default(); let mut total_time = Duration::default();
let mut num_images = 0; let mut num_images = 0;
for entry in WalkDir::new("../../test_files/loopers/") { for entry in WalkDir::new("../../test_files/") {
let entry = entry.unwrap(); let entry = entry.unwrap();
if entry.path().is_dir() { if entry.path().is_dir() {
continue; continue;
@ -18,7 +18,7 @@ fn main() {
let img = match DynamicCz::open(entry.path()) { let img = match DynamicCz::open(entry.path()) {
Ok(img) => img, Ok(img) => img,
Err(err) => { Err(err) => {
println!("{}: {}", entry.path().file_name().unwrap().to_string_lossy(), err); println!("{}: {:?}", entry.path().file_name().unwrap().to_string_lossy(), err);
continue; continue;
}, },
}; };