mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 07:12:55 -05:00
Moved all implementations to common struct
This commit is contained in:
parent
3c01617a5c
commit
f4eb9ea1b3
11 changed files with 232 additions and 750 deletions
|
@ -10,6 +10,3 @@ authors = ["G2"]
|
|||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
|
||||
[build]
|
||||
rustflags = ["-C", "target-cpu=native"]
|
||||
|
|
140
cz/src/common.rs
140
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<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 {
|
||||
|
@ -46,10 +69,10 @@ pub trait CzHeader {
|
|||
fn to_bytes(&self) -> Result<Vec<u8>, 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::<LittleEndian>()?,
|
||||
width: bytes.read_u16::<LittleEndian>()?,
|
||||
height: 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 {
|
||||
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<Vec<u8>, 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::<LittleEndian>(self.length() as u32)?;
|
||||
buf.write_u16::<LittleEndian>(self.width())?;
|
||||
|
@ -164,35 +213,7 @@ impl CzHeader for CommonHeader {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait CzImage {
|
||||
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>(
|
||||
pub fn get_palette<T: Seek + ReadBytesExt + Read>(
|
||||
input: &mut T,
|
||||
num_colors: usize,
|
||||
) -> Result<Vec<[u8; 4]>, CzError> {
|
||||
|
@ -207,13 +228,20 @@ pub fn parse_colormap<T: Seek + ReadBytesExt + Read>(
|
|||
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();
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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<T: Seek + ReadBytesExt + Read>(
|
||||
pub fn get_chunk_info<T: Seek + ReadBytesExt + Read>(
|
||||
bytes: &mut T,
|
||||
) -> Result<CompressionInfo, CzError> {
|
||||
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 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;
|
||||
|
||||
|
|
|
@ -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<u8>,
|
||||
pub struct DynamicCz {
|
||||
header_common: CommonHeader,
|
||||
palette: Option<Vec<[u8; 4]>>,
|
||||
bitmap: Vec<u8>,
|
||||
}
|
||||
|
||||
impl DynamicCz {
|
||||
|
@ -46,74 +41,81 @@ impl DynamicCz {
|
|||
}
|
||||
}
|
||||
|
||||
impl CzImage for DynamicCz {
|
||||
type Header = CommonHeader;
|
||||
impl DynamicCz {
|
||||
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)
|
||||
-> Result<DynamicCz, crate::common::CzError>
|
||||
{
|
||||
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<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 {
|
||||
pub fn save_as_cz<T: Into<std::path::PathBuf>>(&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<u8> {
|
||||
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
|
||||
}
|
||||
|
||||
fn into_bitmap(self) -> Vec<u8> {
|
||||
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(),
|
||||
}
|
||||
pub fn set_header(&mut self, header: &CommonHeader) {
|
||||
self.header_common = header.to_owned()
|
||||
}
|
||||
|
||||
fn set_bitmap(&mut self, bitmap: &[u8], width: u16, height: u16) {
|
||||
unimplemented!()
|
||||
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)
|
||||
}
|
||||
|
||||
self.bitmap = bitmap;
|
||||
|
||||
self.header_mut().set_width(width);
|
||||
self.header_mut().set_height(height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<u8>,
|
||||
}
|
||||
|
||||
impl CzHeader for Cz0Header {
|
||||
impl Cz0Header {
|
||||
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
||||
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<Vec<u8>, io::Error> {
|
||||
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_height)?;
|
||||
|
||||
if self.length() > 28 {
|
||||
if self.common.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())?;
|
||||
|
@ -142,74 +103,16 @@ impl CzHeader for Cz0Header {
|
|||
}
|
||||
}
|
||||
|
||||
impl CzImage for Cz0Image {
|
||||
type Header = Cz0Header;
|
||||
|
||||
fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError> {
|
||||
// Get the header from the input
|
||||
let header = Cz0Header::new(bytes)?;
|
||||
bytes.seek(io::SeekFrom::Start(header.length() as u64))?;
|
||||
#[derive(Debug)]
|
||||
pub struct Cz0Image {
|
||||
header: Cz0Header,
|
||||
bitmap: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, CzError> {
|
||||
// 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 {
|
||||
type Error = CzError;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cz1Image {
|
||||
header: CommonHeader,
|
||||
bitmap: Vec<u8>,
|
||||
palette: Option<Vec<[u8; 4]>>,
|
||||
}
|
||||
|
||||
impl CzImage for Cz1Image {
|
||||
type Header = CommonHeader;
|
||||
|
||||
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())?);
|
||||
}
|
||||
use crate::common::CzError;
|
||||
use crate::compression::{decompress, get_chunk_info};
|
||||
|
||||
pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Vec<u8>, CzError> {
|
||||
// Get the information about the compressed chunks
|
||||
let chunk_info = parse_chunk_info(bytes)?;
|
||||
let block_info = get_chunk_info(bytes)?;
|
||||
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
||||
|
||||
// Get the bitmap
|
||||
let mut bitmap = decompress(bytes, &chunk_info).unwrap();
|
||||
let bitmap = decompress(bytes, &block_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!()
|
||||
}
|
||||
Ok(bitmap)
|
||||
}
|
||||
|
|
|
@ -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<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!()
|
||||
}
|
||||
pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T)
|
||||
-> Result<Vec<u8>, 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)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Cz3Header {
|
||||
/// Common CZ# header
|
||||
common: CommonHeader,
|
||||
|
||||
/// 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<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)?;
|
||||
pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T, header: &CommonHeader)
|
||||
-> Result<Vec<u8>, CzError>
|
||||
{
|
||||
let block_info = get_chunk_info(bytes)?;
|
||||
bytes.seek(SeekFrom::Start(block_info.length as u64))?;
|
||||
|
||||
let bitmap = decompress(bytes, &block_info)?;
|
||||
let bitmap = line_diff(header, &bitmap);
|
||||
|
||||
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!()
|
||||
}
|
||||
Ok(bitmap)
|
||||
}
|
||||
|
|
|
@ -1,79 +1,11 @@
|
|||
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<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
||||
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<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)?;
|
||||
pub fn decode<T: Seek + ReadBytesExt + Read>(bytes: &mut T, header: &CommonHeader) -> Result<Vec<u8>, 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;
|
||||
|
@ -88,40 +20,5 @@ impl CzImage for Cz4Image {
|
|||
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;
|
||||
}
|
||||
Ok(picture.into_raw())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue