Fixed issues, improved layout of files

This commit is contained in:
G2-Games 2024-05-04 15:11:33 -05:00
parent 920fb8c697
commit d5ae80005e
7 changed files with 451 additions and 352 deletions

184
src/common.rs Normal file
View file

@ -0,0 +1,184 @@
//! Shared types and traits between CZ# files
use std::{
io::{self, Cursor, Read, Seek, Write}, path::PathBuf
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use image::Rgba;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CzError {
#[error("Version in header does not match expected version")]
VersionMismatch,
#[error(
"Format of supplied file is incorrect; expected {} bytes, got {}",
expected,
got
)]
InvalidFormat { expected: usize, got: usize },
#[error("Failed to read/write input/output")]
IoError(#[from] io::Error),
}
pub trait CzHeader {
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized;
/// Turn the header into bytes equivalent to the original header from the file
fn to_bytes(&self) -> Result<Vec<u8>, 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<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized,
{
let mut magic = [0u8; 4];
bytes.read_exact(&mut magic)?;
Ok(Self {
version: magic[2] - b'0',
length: bytes.read_u32::<LittleEndian>()?,
width: bytes.read_u16::<LittleEndian>()?,
height: bytes.read_u16::<LittleEndian>()?,
depth: bytes.read_u16::<LittleEndian>()?,
color_block: bytes.read_u8()?,
})
}
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<Vec<u8>, 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::<LittleEndian>(self.length() as u32)?;
buf.write_u16::<LittleEndian>(self.width())?;
buf.write_u16::<LittleEndian>(self.height())?;
buf.write_u16::<LittleEndian>(self.depth())?;
buf.write_u8(self.color_block())?;
Ok(buf)
}
}
pub trait CzImage {
type Header;
/// Create a [CZImage] from bytes
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized;
/// Save the image as a PNG
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError>;
/// 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;
/// Set the header with its metadata
fn set_header(&mut self, header: Self::Header);
/// 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], header: &Self::Header);
}
pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
input: &mut T,
num_colors: usize,
) -> Result<Vec<Rgba<u8>>, 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(rgba_buf));
}
Ok(colormap)
}
pub fn apply_palette(input: &mut Vec<u8>, palette: &[Rgba<u8>]) -> Vec<u8> {
let mut output_map = Vec::new();
for byte in input.iter() {
let color = palette[*byte as usize].0;
output_map.extend_from_slice(&color);
}
output_map
}

119
src/compression.rs Normal file
View file

@ -0,0 +1,119 @@
use std::io::{Read, Seek};
use byteorder::{LittleEndian, ReadBytesExt};
use crate::common::CzError;
#[derive(Debug, Clone, Copy)]
pub struct ChunkInfo {
pub size_compressed: usize,
pub size_raw: usize,
}
#[derive(Debug, Clone)]
pub struct CompressionInfo {
pub chunk_count: usize,
pub total_size_compressed: usize,
pub total_size_raw: usize,
pub chunks: Vec<ChunkInfo>,
/// Length of the compression chunk info
pub length: usize,
}
/// Get info about the compression chunks
pub fn parse_chunk_info<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<CompressionInfo, CzError> {
let parts_count = bytes.read_u32::<LittleEndian>()?;
let mut part_sizes = vec![];
let mut total_size = 0;
let mut total_size_raw = 0;
// Loop over the compressed bytes
for _ in 0..parts_count {
let compressed_size = bytes.read_u32::<LittleEndian>()? * 2;
total_size += compressed_size;
let raw_size = bytes.read_u32::<LittleEndian>()?;
total_size_raw += raw_size;
part_sizes.push(ChunkInfo {
size_compressed: compressed_size as usize,
size_raw: raw_size as usize,
});
}
Ok(CompressionInfo {
chunk_count: parts_count as usize,
total_size_compressed: total_size as usize,
total_size_raw: total_size_raw as usize,
chunks: part_sizes,
length: bytes.stream_position()? as usize,
})
}
/// Decompress an LZW compressed stream, like CZ1
pub fn decompress<T: Seek + ReadBytesExt + Read>(
input: &mut T,
chunk_info: &CompressionInfo,
) -> Result<Vec<u8>, CzError> {
let mut m_dst = 0;
let mut bitmap = vec![0; chunk_info.total_size_raw];
for chunk in &chunk_info.chunks {
let mut part = vec![0u8; chunk.size_compressed];
input.read_exact(&mut part)?;
for j in (0..part.len()).step_by(2) {
let ctl = part[j + 1];
if ctl == 0 {
bitmap[m_dst] = part[j];
m_dst += 1;
} else {
m_dst += copy_range(&mut bitmap, &part, get_offset(&part, j), m_dst);
}
}
}
Ok(bitmap)
}
fn get_offset(input: &[u8], src: usize) -> usize {
(((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2
}
fn copy_range(bitmap: &mut Vec<u8>, input: &[u8], src: usize, dst: usize) -> usize {
let mut dst = dst;
let start_pos = dst;
if input[src + 1] == 0 {
bitmap[dst] = input[src];
dst += 1;
} else if get_offset(input, src) == src {
bitmap[dst] = 0;
dst += 1;
} else {
dst += copy_range(bitmap, input, get_offset(input, src), dst);
}
if input[src + 3] == 0 {
bitmap[dst] = input[src + 2];
dst += 1;
} else if get_offset(input, src + 2) == src {
bitmap[dst] = bitmap[start_pos];
dst += 1;
} else {
bitmap[dst] = copy_one(input, get_offset(input, src + 2));
dst += 1;
}
dst - start_pos
}
fn copy_one(input: &[u8], src: usize) -> u8 {
if input[src + 1] == 0 {
input[src]
} else if get_offset(input, src) == src {
0
} else {
copy_one(input, get_offset(input, src))
}
}

View file

@ -1,262 +0,0 @@
//! Shared types and traits between CZ# files
use std::{
collections::HashMap,
io::{self, Cursor, Read},
};
use byteorder::{LittleEndian, ReadBytesExt};
use image::Rgba;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CzError {
#[error("Version in header does not match expected version")]
VersionMismatch,
#[error(
"Format of supplied file is incorrect; expected {} bytes, got {}",
expected,
got
)]
InvalidFormat { expected: usize, got: usize },
#[error("Failed to read input")]
ReadError(#[from] io::Error),
}
pub trait CzHeader {
fn new(bytes: &mut Cursor<&[u8]>) -> Result<Self, CzError>
where
Self: Sized;
fn version(&self) -> u8;
fn header_length(&self) -> usize;
fn width(&self) -> u16;
fn height(&self) -> u16;
fn depth(&self) -> u16;
}
/// 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)
version: u8,
/// Length of the header in bytes
length: u32,
/// Width of the image in pixels
width: u16,
/// Height of the image in pixels
height: u16,
/// Bit depth in Bits Per Pixel (BPP)
depth: u16,
color_block: u8,
}
impl CzHeader for CommonHeader {
fn new(bytes: &mut Cursor<&[u8]>) -> Result<Self, CzError>
where
Self: Sized,
{
let mut magic = [0u8; 4];
bytes.read_exact(&mut magic)?;
Ok(Self {
version: magic[2] - b'0',
length: bytes.read_u32::<LittleEndian>()?,
width: bytes.read_u16::<LittleEndian>()?,
height: bytes.read_u16::<LittleEndian>()?,
depth: bytes.read_u16::<LittleEndian>()?,
color_block: bytes.read_u8()?,
})
}
fn version(&self) -> u8 {
self.version
}
fn header_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
}
}
pub trait CzImage {
type Header;
/// Create a [CZImage] from bytes
fn decode(bytes: &[u8]) -> Result<Self, CzError>
where
Self: Sized;
/// Save the image as a PNG
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError>;
/// Save the image as its corresponding CZ# type
fn save_as_cz(&self) -> 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);
/// 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: Vec<u8>, header: Self::Header);
}
pub fn parse_colormap(
input: &mut Cursor<&[u8]>,
num_colors: usize,
) -> Result<Vec<Rgba<u8>>, 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(rgba_buf));
}
Ok(colormap)
}
#[derive(Debug)]
pub struct ChunkInfo {
pub size_compressed: usize,
pub size_raw: usize,
}
#[derive(Debug)]
pub struct CompressionInfo {
pub chunk_count: usize,
pub total_size_compressed: usize,
pub total_size_raw: usize,
pub chunks: Vec<ChunkInfo>,
/// Length of the compression chunk info
pub length: usize,
}
/// Get info about the compression chunks
pub fn parse_chunk_info(bytes: &mut Cursor<&[u8]>) -> Result<CompressionInfo, CzError> {
let parts_count = bytes.read_u32::<LittleEndian>()?;
dbg!(parts_count);
let mut part_sizes = vec![];
let mut total_size = 0;
let mut total_size_raw = 0;
for _ in 0..parts_count {
let compressed_size = bytes.read_u32::<LittleEndian>()? * 2;
total_size += compressed_size;
let raw_size = bytes.read_u32::<LittleEndian>()? * 4;
total_size_raw += raw_size;
part_sizes.push(ChunkInfo {
size_compressed: compressed_size as usize,
size_raw: raw_size as usize,
});
}
dbg!(&part_sizes);
Ok(CompressionInfo {
chunk_count: parts_count as usize,
total_size_compressed: total_size as usize,
total_size_raw: total_size_raw as usize,
chunks: part_sizes,
length: bytes.position() as usize,
})
}
/// Decompress an LZW compressed stream, like CZ1
pub fn decompress(
input: &mut Cursor<&[u8]>,
chunk_info: CompressionInfo,
) -> Result<Vec<u8>, CzError> {
let mut m_dst = 0;
let mut bitmap = vec![0; chunk_info.total_size_raw];
for chunk in chunk_info.chunks {
let mut part = vec![0u8; chunk.size_compressed];
input.read_exact(&mut part)?;
for j in (0..part.len()).step_by(2) {
let ctl = part[j + 1];
if ctl == 0 {
bitmap[m_dst] = part[j];
m_dst += 1;
} else {
m_dst += copy_range(&mut bitmap, &part, get_offset(&part, j), m_dst);
}
}
}
Ok(bitmap)
}
fn get_offset(input: &[u8], src: usize) -> usize {
(((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2
}
fn copy_range(bitmap: &mut Vec<u8>, input: &[u8], src: usize, dst: usize) -> usize {
let mut dst = dst;
let start_pos = dst;
if input[src + 1] == 0 {
bitmap[dst] = input[src];
dst += 1;
} else if get_offset(input, src) == src {
bitmap[dst] = 0;
dst += 1;
} else {
dst += copy_range(bitmap, input, get_offset(input, src), dst);
}
if input[src + 3] == 0 {
bitmap[dst] = input[src + 2];
dst += 1;
} else if get_offset(input, src + 2) == src {
bitmap[dst] = bitmap[start_pos];
dst += 1;
} else {
bitmap[dst] = copy_one(input, get_offset(input, src + 2));
dst += 1;
}
dst - start_pos
}
fn copy_one(input: &[u8], src: usize) -> u8 {
if input[src + 1] == 0 {
input[src]
} else if get_offset(input, src) == src {
0
} else {
copy_one(input, get_offset(input, src))
}
}

View file

@ -1,13 +1,16 @@
use std::io::{self, Cursor, Read};
use std::{fs::File, io::{self, BufWriter, Cursor, Read, Seek, Write}, path::PathBuf};
use byteorder::{LittleEndian, ReadBytesExt};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::cz_common::{CommonHeader, CzError, CzHeader, CzImage};
use crate::common::{CommonHeader, CzError, CzHeader, CzImage};
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct Cz0Header {
/// Common CZ# header
common: CommonHeader,
pub common: CommonHeader,
/// Unknown bytes
unknown_1: [u8; 5],
/// Width of cropped image area
pub crop_width: u16,
@ -26,6 +29,8 @@ pub struct Cz0Header {
/// Offset height
pub offset_height: Option<u16>,
unknown_2: Option<[u8; 4]>,
}
#[derive(Debug)]
@ -35,7 +40,7 @@ pub struct Cz0Image {
}
impl CzHeader for Cz0Header {
fn new(bytes: &mut Cursor<&[u8]>) -> Result<Self, CzError>
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized,
{
@ -45,8 +50,8 @@ impl CzHeader for Cz0Header {
return Err(CzError::VersionMismatch);
}
let _unknown = bytes.read_u32::<LittleEndian>()?;
let _unknown = bytes.read_u8()?;
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>()?;
@ -56,14 +61,22 @@ impl CzHeader for Cz0Header {
let mut offset_width = None;
let mut offset_height = None;
if common.header_length() > 28 {
let mut unknown_2 = None;
if common.length() > 28 {
offset_width = Some(bytes.read_u16::<LittleEndian>()?);
offset_height = Some(bytes.read_u16::<LittleEndian>()?);
let mut un_2 = [0u8; 4];
bytes.read_exact(&mut un_2)?;
unknown_2 = Some(un_2);
}
Ok(Self {
common,
unknown_1,
crop_width,
crop_height,
@ -72,6 +85,8 @@ impl CzHeader for Cz0Header {
offset_width,
offset_height,
unknown_2,
})
}
@ -79,8 +94,8 @@ impl CzHeader for Cz0Header {
self.common.version()
}
fn header_length(&self) -> usize {
self.common.header_length()
fn length(&self) -> usize {
self.common.length()
}
fn width(&self) -> u16 {
@ -94,32 +109,64 @@ impl CzHeader for Cz0Header {
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> {
let mut buf = vec![];
buf.write_all(&self.common.to_bytes()?)?;
buf.write_all(&self.unknown_1)?;
buf.write_u16::<LittleEndian>(self.crop_width)?;
buf.write_u16::<LittleEndian>(self.crop_height)?;
buf.write_u16::<LittleEndian>(self.bounds_width)?;
buf.write_u16::<LittleEndian>(self.bounds_height)?;
if self.length() > 28 {
buf.write_u16::<LittleEndian>(self.offset_width.unwrap())?;
buf.write_u16::<LittleEndian>(self.offset_height.unwrap())?;
buf.write_all(&self.unknown_2.unwrap())?;
}
Ok(buf)
}
}
impl CzImage for Cz0Image {
type Header = Cz0Header;
fn decode(bytes: &[u8]) -> Result<Self, CzError> {
let mut input = Cursor::new(bytes);
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
// Get the header from the input
let header = Cz0Header::new(&mut 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![0u8; bytes.len() - header.header_length()];
input.read_exact(&mut bitmap)?;
let mut bitmap = vec![];
bytes.read_to_end(&mut bitmap)?;
Ok(Self { header, bitmap })
}
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError> {
Ok(image::save_buffer(
image::save_buffer(
name,
&self.bitmap,
self.header.width() as u32,
self.header.height() as u32,
image::ExtendedColorType::Rgba8,
)?)
)
}
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 {
@ -134,14 +181,10 @@ impl CzImage for Cz0Image {
self.bitmap
}
fn save_as_cz(&self) -> Result<(), CzError> {
todo!()
}
fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
self.bitmap = bitmap.to_vec();
fn set_bitmap(&mut self, bitmap: Vec<u8>, header: Self::Header) {
self.bitmap = bitmap;
self.header = header;
self.header = *header;
}
}

View file

@ -1,12 +1,14 @@
use crate::cz_common::{
decompress, parse_chunk_info, parse_colormap, CommonHeader, CzError, CzHeader, CzImage,
};
use byteorder::ReadBytesExt;
use image::{ImageFormat, Rgba};
use std::io::Cursor;
use std::{fs::File, io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}, path::PathBuf};
use crate::compression::{decompress, parse_chunk_info};
use crate::common::{apply_palette, parse_colormap, CommonHeader, CzError, CzHeader, CzImage};
#[derive(Debug, Clone)]
pub struct Cz1Image {
header: CommonHeader,
raw_bitmap: Option<Vec<u8>>,
bitmap: Vec<u8>,
palette: Vec<Rgba<u8>>,
}
@ -14,37 +16,48 @@ pub struct Cz1Image {
impl CzImage for Cz1Image {
type Header = CommonHeader;
fn decode(bytes: &[u8]) -> Result<Self, CzError> {
let mut input = Cursor::new(bytes);
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
// Get the header from the input
let header = CommonHeader::new(&mut input).unwrap();
let header = CommonHeader::new(bytes).unwrap();
bytes.seek(SeekFrom::Start(header.length() as u64));
if header.version() != 1 {
return Err(CzError::VersionMismatch)
}
// 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(&mut input, 1 << header.depth())?);
palette = Some(parse_colormap(bytes, 1 << header.depth())?);
}
let chunk_info = parse_chunk_info(&mut input)?;
let chunk_info = parse_chunk_info(bytes)?;
if chunk_info.total_size_compressed as usize > bytes.len() {
/*
if chunk_info.total_size_compressed as usize > bytes. {
return Err(CzError::InvalidFormat {
expected: chunk_info.total_size_compressed,
got: bytes.len(),
});
}
*/
let mut bitmap = decompress(&mut input, chunk_info).unwrap();
let mut bitmap = decompress(bytes, &chunk_info).unwrap();
let mut raw_bitmap = None;
// Apply the palette if it exists
if let Some(pal) = &palette {
apply_palette(&mut bitmap, pal);
if let Some(raw) = &mut raw_bitmap {
bitmap.clone_into(raw);
}
bitmap = apply_palette(&mut bitmap, pal);
}
let image = Self {
header,
bitmap,
raw_bitmap,
palette: palette.unwrap(),
};
@ -52,16 +65,14 @@ impl CzImage for Cz1Image {
}
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError> {
let img = image::RgbaImage::from_raw(
image::save_buffer_with_format(
name,
&self.bitmap,
self.header.width() as u32,
self.header.height() as u32,
self.bitmap.clone(),
image::ExtendedColorType::Rgba8,
ImageFormat::Png
)
.expect("Creating image failed");
img.save_with_format(name, ImageFormat::Png)?;
Ok(())
}
fn header(&self) -> &Self::Header {
@ -76,22 +87,17 @@ impl CzImage for Cz1Image {
self.bitmap
}
fn save_as_cz(&self) -> Result<(), CzError> {
todo!()
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: Vec<u8>, header: Self::Header) {
fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
todo!()
}
}
fn apply_palette(input: &mut Vec<u8>, palette: &[Rgba<u8>]) {
let mut output_map = Vec::new();
for byte in input.iter() {
let color = palette[*byte as usize].0;
output_map.extend_from_slice(&color);
}
*input = output_map
}

View file

@ -1,9 +1,9 @@
use std::io::Cursor;
use std::{io::{self, Cursor, Read, Seek, SeekFrom}, path::PathBuf};
use byteorder::{LittleEndian, ReadBytesExt};
use image::ImageFormat;
use crate::cz_common::{decompress, parse_chunk_info, CommonHeader, CzError, CzHeader, CzImage};
use crate::compression::{decompress, parse_chunk_info};
use crate::common::{CommonHeader, CzError, CzHeader, CzImage};
#[derive(Debug, Clone, Copy)]
pub struct Cz3Header {
@ -30,7 +30,7 @@ pub struct Cz3Header {
}
impl CzHeader for Cz3Header {
fn new(bytes: &mut Cursor<&[u8]>) -> Result<Self, CzError>
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
where
Self: Sized,
{
@ -40,7 +40,8 @@ impl CzHeader for Cz3Header {
return Err(CzError::VersionMismatch);
}
let _unknown = bytes.read_u48::<LittleEndian>()?;
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>()?;
@ -50,7 +51,7 @@ impl CzHeader for Cz3Header {
let mut offset_width = None;
let mut offset_height = None;
if common.header_length() > 28 {
if common.length() > 28 {
offset_width = Some(bytes.read_u16::<LittleEndian>()?);
offset_height = Some(bytes.read_u16::<LittleEndian>()?);
}
@ -73,8 +74,8 @@ impl CzHeader for Cz3Header {
self.common.version()
}
fn header_length(&self) -> usize {
self.common.header_length()
fn length(&self) -> usize {
self.common.length()
}
fn width(&self) -> u16 {
@ -88,6 +89,14 @@ impl CzHeader for Cz3Header {
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)]
@ -99,14 +108,13 @@ pub struct Cz3Image {
impl CzImage for Cz3Image {
type Header = Cz3Header;
fn decode(bytes: &[u8]) -> Result<Self, CzError> {
let mut input = Cursor::new(bytes);
let header = Cz3Header::new(&mut input)?;
input.set_position(header.header_length() as u64);
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(&mut input)?;
let block_info = parse_chunk_info(bytes)?;
let mut bitmap = decompress(&mut input, block_info)?;
let mut bitmap = decompress(bytes, &block_info)?;
let stride = (header.width() * (header.depth() / 8)) as usize;
let third = ((header.height() + 2) / 3) as usize;
@ -123,16 +131,13 @@ impl CzImage for Cz3Image {
}
fn save_as_png(&self, name: &str) -> Result<(), image::error::ImageError> {
let img = image::RgbaImage::from_raw(
image::save_buffer(
name,
&self.bitmap,
self.header.width() as u32,
self.header.height() as u32,
self.bitmap.clone(),
image::ExtendedColorType::Rgba8,
)
.expect("Creating image failed");
img.save_with_format(name, ImageFormat::Png)?;
Ok(())
}
fn header(&self) -> &Self::Header {
@ -147,11 +152,11 @@ impl CzImage for Cz3Image {
self.bitmap
}
fn save_as_cz(&self) -> Result<(), CzError> {
fn save_as_cz<T: Into<PathBuf>>(&self, path: T) -> Result<(), CzError> {
todo!()
}
fn set_bitmap(&mut self, bitmap: Vec<u8>, header: Self::Header) {
fn set_bitmap(&mut self, bitmap: &[u8], header: &Self::Header) {
todo!()
}
}

View file

@ -1,19 +1,23 @@
pub mod cz_common;
pub mod common;
pub mod compression;
pub mod formats {
pub mod cz0;
pub mod cz1;
pub mod cz3;
}
use common::CzImage;
pub use formats::cz0::Cz0Image;
pub use formats::cz1::Cz1Image;
pub use formats::cz3::Cz3Image;
// Generic tools
use std::fs;
use crate::{cz_common::CzImage, formats::{cz0::Cz0Image, cz3::Cz3Image}};
fn main() {
let input = fs::read("../test_files/Old_TestFiles/782.cz0").expect("Error, could not open image");
let img_file = Cz0Image::decode(&input).unwrap();
println!("{:#?}", img_file.header());
let mut input = fs::File::open("../test_files/font_files/24-style1.cz1").unwrap();
let img_file = Cz1Image::decode(&mut input).unwrap();
img_file.save_as_png("test.png").unwrap();
img_file.save_as_cz("test1.cz1").unwrap();
img_file.save_as_png("test1.png").unwrap();
}