mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 07:12:55 -05:00
196 lines
4.6 KiB
Rust
196 lines
4.6 KiB
Rust
//! Shared types and traits between CZ# files
|
|
|
|
use std::{
|
|
io::{self, Read, Seek, Write},
|
|
path::PathBuf,
|
|
};
|
|
|
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
use thiserror::Error;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum CzError {
|
|
#[error("Expected CZ{}, got CZ{}", 0, 1)]
|
|
VersionMismatch(u8, u8),
|
|
|
|
#[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)]
|
|
IoError(#[from] io::Error),
|
|
|
|
#[error("Problem while decoding file")]
|
|
DecodeError,
|
|
}
|
|
|
|
pub trait CzHeader {
|
|
fn new<T: Seek + ReadBytesExt + Read>(bytes: &mut T) -> Result<Self, CzError>
|
|
where
|
|
Self: Sized;
|
|
|
|
/// The [CommonHeader] header from the image
|
|
fn common(&self) -> &CommonHeader;
|
|
|
|
/// 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)?;
|
|
|
|
if magic[0..2] != [b'C', b'Z'] {
|
|
return Err(CzError::NotCzFile);
|
|
}
|
|
|
|
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 common(&self) -> &CommonHeader {
|
|
&self
|
|
}
|
|
|
|
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 [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;
|
|
|
|
/// 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,
|
|
num_colors: usize,
|
|
) -> Result<Vec<[u8; 4]>, 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_buf);
|
|
}
|
|
|
|
Ok(colormap)
|
|
}
|
|
|
|
pub fn apply_palette(input: &mut Vec<u8>, palette: &[[u8; 4]]) -> Vec<u8> {
|
|
let mut output_map = Vec::new();
|
|
|
|
for byte in input.iter() {
|
|
let color = palette[*byte as usize];
|
|
output_map.extend_from_slice(&color);
|
|
}
|
|
|
|
output_map
|
|
}
|