mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 07:12:55 -05:00
151 lines
4.6 KiB
Rust
151 lines
4.6 KiB
Rust
use std::{
|
|
fs::File, io::{BufReader, Cursor, Read, Seek, SeekFrom, Write}, path::Path
|
|
};
|
|
use byteorder::ReadBytesExt;
|
|
|
|
use crate::{
|
|
common::{apply_palette, get_palette, rgba_to_indexed, CommonHeader, CzError, CzHeader, CzVersion, ExtendedHeader},
|
|
formats::{cz0, cz1, cz2, cz3, cz4},
|
|
};
|
|
|
|
pub struct DynamicCz {
|
|
header_common: CommonHeader,
|
|
header_extended: Option<ExtendedHeader>,
|
|
palette: Option<Vec<[u8; 4]>>,
|
|
bitmap: Vec<u8>,
|
|
}
|
|
|
|
impl DynamicCz {
|
|
pub fn open<P: ?Sized + AsRef<Path>>(path: &P) -> Result<Self, CzError> {
|
|
let mut img_file = BufReader::new(std::fs::File::open(path)?);
|
|
|
|
Self::decode(&mut img_file)
|
|
}
|
|
|
|
pub fn save_as_png<P: ?Sized + AsRef<Path>>(&self, path: &P) -> Result<(), png::EncodingError> {
|
|
let file = std::fs::File::create(path).unwrap();
|
|
let writer = std::io::BufWriter::new(file);
|
|
|
|
let mut encoder = png::Encoder::new(
|
|
writer,
|
|
self.header().width() as u32,
|
|
self.header().height() as u32,
|
|
);
|
|
encoder.set_color(png::ColorType::Rgba);
|
|
encoder.set_depth(png::BitDepth::Eight);
|
|
let mut writer = encoder.write_header()?;
|
|
|
|
writer.write_image_data(self.bitmap())?; // Save
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
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)?;
|
|
let mut header_extended = None;
|
|
if header_common.length() > 15 && header_common.version() != CzVersion::CZ2 {
|
|
header_extended = Some(ExtendedHeader::new(input, &header_common)?);
|
|
}
|
|
input.seek(SeekFrom::Start(header_common.length() as u64))?;
|
|
|
|
// 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
|
|
};
|
|
|
|
// 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,
|
|
header_extended,
|
|
palette,
|
|
bitmap,
|
|
})
|
|
}
|
|
|
|
pub fn save_as_cz<T: Into<std::path::PathBuf>>(&self, path: T) -> Result<(), CzError> {
|
|
let mut out_file = File::create(path.into())?;
|
|
|
|
self.header_common.write_into(&mut out_file)?;
|
|
|
|
if let Some(ext) = self.header_extended {
|
|
ext.write_into(&mut out_file)?;
|
|
}
|
|
|
|
let output_bitmap;
|
|
match &self.palette {
|
|
Some(pal) if self.header_common.depth() <= 8 => {
|
|
output_bitmap = rgba_to_indexed(&self.bitmap(), &pal)?
|
|
},
|
|
_ => output_bitmap = self.bitmap().clone()
|
|
}
|
|
|
|
match self.header_common.version() {
|
|
CzVersion::CZ0 => cz0::encode(&mut out_file, &self.bitmap)?,
|
|
CzVersion::CZ1 => todo!(),
|
|
CzVersion::CZ2 => todo!(),
|
|
CzVersion::CZ3 => todo!(),
|
|
CzVersion::CZ4 => todo!(),
|
|
CzVersion::CZ5 => todo!(),
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn header(&self) -> &CommonHeader {
|
|
&self.header_common
|
|
}
|
|
|
|
pub fn header_mut(&mut self) -> &mut CommonHeader {
|
|
&mut self.header_common
|
|
}
|
|
|
|
pub fn set_header(&mut self, header: &CommonHeader) {
|
|
self.header_common = header.to_owned()
|
|
}
|
|
|
|
pub fn bitmap(&self) -> &Vec<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(())
|
|
}
|
|
}
|