From eea4e58e2bd3522931343841d68f3c2ad20f7a25 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Fri, 3 May 2024 13:42:15 -0500 Subject: [PATCH] Fixed CZ1 implementation, added preliminary CZ3 --- src/cz_common.rs | 11 ++-- src/formats/cz0.rs | 4 +- src/formats/cz1.rs | 128 ++++++++++++++++++++++++++------------------- src/formats/cz3.rs | 60 +++++++++++++++++++++ src/main.rs | 11 ++-- 5 files changed, 148 insertions(+), 66 deletions(-) create mode 100644 src/formats/cz3.rs diff --git a/src/cz_common.rs b/src/cz_common.rs index 0948bb6..cb93b7b 100644 --- a/src/cz_common.rs +++ b/src/cz_common.rs @@ -1,5 +1,6 @@ //! Shared types and traits between CZ# files +use image::Rgba; use thiserror::Error; #[derive(Error, Debug)] @@ -26,7 +27,7 @@ pub trait CzHeader { } /// The common first part of a header of a CZ# file -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct CommonHeader { /// Format version from the magic bytes, (eg. CZ3, CZ4) pub version: u8, @@ -69,17 +70,17 @@ pub trait CzImage { fn header(&self) -> &Self::Header; /// Get the raw underlying bitmap for an image - fn raw_bitmap(&self) -> &Vec; + fn into_bitmap(self) -> Vec; } -pub fn parse_colormap(input: &[u8], num_colors: usize) -> (Vec<[u8; 4]>, usize) { +pub fn parse_colormap(input: &[u8], num_colors: usize) -> Vec> { let mut colormap = Vec::with_capacity(num_colors); let input_iter = input.windows(4).step_by(4).take(num_colors); for color in input_iter { - colormap.push(color.try_into().unwrap()); + colormap.push(Rgba(color.try_into().unwrap())); } - (colormap, num_colors * 4) + colormap } diff --git a/src/formats/cz0.rs b/src/formats/cz0.rs index 96029f8..1ea7a24 100644 --- a/src/formats/cz0.rs +++ b/src/formats/cz0.rs @@ -110,7 +110,7 @@ impl CzImage for Cz0Image { &self.header } - fn raw_bitmap(&self) -> &Vec { - &self.bitmap + fn into_bitmap(self) -> Vec { + self.bitmap } } diff --git a/src/formats/cz1.rs b/src/formats/cz1.rs index eab8a1e..d50a0ff 100644 --- a/src/formats/cz1.rs +++ b/src/formats/cz1.rs @@ -1,27 +1,28 @@ -use std::io::Read; - +use image::{ImageFormat, Rgba}; use crate::cz_common::{parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Cz1Header { /// Common CZ# header common: CommonHeader, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Cz1Image { header: Cz1Header, bitmap: Vec, - palette: Vec<[u8; 4]>, + palette: Option>>, } impl CzHeader for Cz1Header { fn new(bytes: &[u8]) -> Result { let common = CommonHeader::new(bytes); + /* if common.version != 1 { return Err(CzError::VersionMismatch) } + */ Ok(Self { common, @@ -60,25 +61,31 @@ impl CzImage for Cz1Image { position += header.header_length(); // The color palette - let (palette, palette_length) = parse_colormap(&bytes[position..], 0x100); - position += palette_length; - - dbg!(&bytes[position..position + 4]); + let mut palette = None; + if header.common.depth == 8 { + let temp_palette = parse_colormap(&bytes[position..], 0x100); + position += temp_palette.len() * 4; + palette = Some(temp_palette); + } let parts_count = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()); position += 4; dbg!(parts_count); let mut part_sizes = vec![0; parts_count as usize]; let mut total_size = 0; + let mut decompressed_size = 0; for size in &mut part_sizes { let part_size = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) * 2; *size = part_size; total_size += part_size; + position += 4; - dbg!(part_size); + let orig_size = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) * 4; + decompressed_size += orig_size; + position += 4; - position += 8; + dbg!(part_size, orig_size); } if position + total_size as usize > bytes.len() { @@ -86,13 +93,7 @@ impl CzImage for Cz1Image { } let mut m_dst = 0; - let bitmap = vec![0; 4882176]; - - let mut image = Self { - header, - bitmap, - palette - }; + let mut bitmap = vec![0; decompressed_size as usize]; for size in part_sizes { let part = &bytes[position..position + size as usize]; @@ -102,68 +103,87 @@ impl CzImage for Cz1Image { let ctl = part[j + 1]; if ctl == 0 { - image.bitmap[m_dst] = part[j]; + bitmap[m_dst] = part[j]; m_dst += 1; } else { - m_dst += image.copy_range(part, get_offset(part, j), m_dst); + m_dst += copy_range(&mut bitmap, part, get_offset(part, j), m_dst); } } } + if let Some(temp_palette) = &palette { + apply_palette(&mut bitmap, &temp_palette); + } + + let image = Self { + header, + bitmap, + palette, + }; + Ok(image) } fn save_as_png(&self, name: &str) { - image::save_buffer( - name, - &self.bitmap, + let img = image::RgbaImage::from_raw( self.header.common.width as u32, self.header.common.height as u32, - image::ExtendedColorType::Rgba8 - ).unwrap() + self.bitmap.clone() + ).unwrap(); + + img.save_with_format(name, ImageFormat::Png).unwrap(); } fn header(&self) -> &Self::Header { &self.header } - fn raw_bitmap(&self) -> &Vec { - &self.bitmap + fn into_bitmap(self) -> Vec { + self.bitmap } } fn get_offset(input: &[u8], src: usize) -> usize { - (((input[src] as usize) | (input[src+1] as usize) << 8) - 0x101) * 2 + (((input[src] as usize) | (input[src + 1] as usize) << 8) - 0x101) * 2 } -impl Cz1Image { - fn copy_range(&mut self, input: &[u8], src: usize, dst: usize) -> usize { - let mut dst = dst; - let start_pos = dst; +fn apply_palette(input: &mut Vec, palette: &Vec>) { + let mut output_map = Vec::new(); - if input[src + 1] == 0 { - self.bitmap[dst] = input[src]; - dst += 1; - } else if get_offset(input, src) == src { - self.bitmap[dst] = 0; - dst += 1; - } else { - dst += self.copy_range(input, get_offset(input, src), dst); - } - - if input[src + 3] == 0 { - self.bitmap[dst] = input[src + 2]; - dst += 1; - } else if get_offset(input, src + 2) == src { - self.bitmap[dst] = self.bitmap[start_pos]; - dst += 1; - } else { - self.bitmap[dst] = copy_one(input, get_offset(input, src + 2)); - dst += 1; - } - - dst - start_pos + for byte in input.iter() { + let color = palette[*byte as usize].0; + output_map.extend_from_slice(&color); } + + *input = output_map +} + +fn copy_range(bitmap: &mut Vec, 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 { diff --git a/src/formats/cz3.rs b/src/formats/cz3.rs new file mode 100644 index 0000000..22885df --- /dev/null +++ b/src/formats/cz3.rs @@ -0,0 +1,60 @@ +use image::ImageFormat; + +use crate::cz_common::{CzError, CzHeader, CzImage}; + +use super::cz1::Cz1Header; + +#[derive(Debug)] +pub struct Cz3Image { + header: Cz1Header, + bitmap: Vec, +} + +impl CzImage for Cz3Image { + type Header = Cz1Header; + + fn decode(bytes: &[u8]) -> Result { + let cz1_image = crate::formats::cz1::Cz1Image::decode(bytes)?; + + let header = cz1_image.header().clone(); + let mut bitmap = cz1_image.into_bitmap(); + + dbg!(bitmap.len()); + + let stride = (header.width() * (header.depth() / 8)) as usize; + let third = ((header.height() + 2) / 3) as usize; + for y in 0..header.height() as usize { + let dst = y * stride; + if y % third != 0 { + for x in 0..stride { + bitmap[dst + x] += bitmap[dst + x - stride]; + } + } + } + + dbg!(bitmap.len()); + + Ok(Self { + header, + bitmap + }) + } + + fn save_as_png(&self, name: &str) { + let img = image::RgbaImage::from_raw( + self.header.width() as u32, + self.header.height() as u32, + self.bitmap.clone() + ).unwrap(); + + img.save_with_format(name, ImageFormat::Png).unwrap(); + } + + fn header(&self) -> &Self::Header { + &self.header + } + + fn into_bitmap(self) -> Vec { + self.bitmap + } +} diff --git a/src/main.rs b/src/main.rs index eb37bca..dbf8d7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,17 +2,18 @@ pub mod cz_common; pub mod formats{ pub mod cz0; pub mod cz1; + pub mod cz3; } // Generic tools use std::fs; -use crate::{cz_common::CzImage, formats::cz1::Cz1Image}; +use crate::{cz_common::CzImage, formats::cz3::Cz3Image}; fn main() { - let input = fs::read("../test_files/x5a3bvy.cz1").expect("Error, could not open image"); - let cz1_file = Cz1Image::decode(&input).unwrap(); - println!("{:#?}", cz1_file.header()); + let input = fs::read("../test_files/Old_TestFiles/129.CZ3").expect("Error, could not open image"); + let cz3_file = Cz3Image::decode(&input).unwrap(); + println!("{:#?}", cz3_file.header()); - cz1_file.save_as_png("test.png") + cz3_file.save_as_png("test.png") }