Fixed CZ1 implementation, added preliminary CZ3

This commit is contained in:
G2-Games 2024-05-03 13:42:15 -05:00
parent d11272463b
commit eea4e58e2b
5 changed files with 148 additions and 66 deletions

View file

@ -1,5 +1,6 @@
//! Shared types and traits between CZ# files //! Shared types and traits between CZ# files
use image::Rgba;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -26,7 +27,7 @@ pub trait CzHeader {
} }
/// The common first part of a header of a CZ# file /// The common first part of a header of a CZ# file
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub(crate) struct CommonHeader { pub(crate) struct CommonHeader {
/// Format version from the magic bytes, (eg. CZ3, CZ4) /// Format version from the magic bytes, (eg. CZ3, CZ4)
pub version: u8, pub version: u8,
@ -69,17 +70,17 @@ pub trait CzImage {
fn header(&self) -> &Self::Header; fn header(&self) -> &Self::Header;
/// Get the raw underlying bitmap for an image /// Get the raw underlying bitmap for an image
fn raw_bitmap(&self) -> &Vec<u8>; fn into_bitmap(self) -> Vec<u8>;
} }
pub fn parse_colormap(input: &[u8], num_colors: usize) -> (Vec<[u8; 4]>, usize) { pub fn parse_colormap(input: &[u8], num_colors: usize) -> Vec<Rgba<u8>> {
let mut colormap = Vec::with_capacity(num_colors); let mut colormap = Vec::with_capacity(num_colors);
let input_iter = input.windows(4).step_by(4).take(num_colors); let input_iter = input.windows(4).step_by(4).take(num_colors);
for color in input_iter { for color in input_iter {
colormap.push(color.try_into().unwrap()); colormap.push(Rgba(color.try_into().unwrap()));
} }
(colormap, num_colors * 4) colormap
} }

View file

@ -110,7 +110,7 @@ impl CzImage for Cz0Image {
&self.header &self.header
} }
fn raw_bitmap(&self) -> &Vec<u8> { fn into_bitmap(self) -> Vec<u8> {
&self.bitmap self.bitmap
} }
} }

View file

@ -1,27 +1,28 @@
use std::io::Read; use image::{ImageFormat, Rgba};
use crate::cz_common::{parse_colormap, CommonHeader, CzError, CzHeader, CzImage}; use crate::cz_common::{parse_colormap, CommonHeader, CzError, CzHeader, CzImage};
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Cz1Header { pub struct Cz1Header {
/// Common CZ# header /// Common CZ# header
common: CommonHeader, common: CommonHeader,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Cz1Image { pub struct Cz1Image {
header: Cz1Header, header: Cz1Header,
bitmap: Vec<u8>, bitmap: Vec<u8>,
palette: Vec<[u8; 4]>, palette: Option<Vec<Rgba<u8>>>,
} }
impl CzHeader for Cz1Header { impl CzHeader for Cz1Header {
fn new(bytes: &[u8]) -> Result<Self, CzError> { fn new(bytes: &[u8]) -> Result<Self, CzError> {
let common = CommonHeader::new(bytes); let common = CommonHeader::new(bytes);
/*
if common.version != 1 { if common.version != 1 {
return Err(CzError::VersionMismatch) return Err(CzError::VersionMismatch)
} }
*/
Ok(Self { Ok(Self {
common, common,
@ -60,25 +61,31 @@ impl CzImage for Cz1Image {
position += header.header_length(); position += header.header_length();
// The color palette // The color palette
let (palette, palette_length) = parse_colormap(&bytes[position..], 0x100); let mut palette = None;
position += palette_length; if header.common.depth == 8 {
let temp_palette = parse_colormap(&bytes[position..], 0x100);
dbg!(&bytes[position..position + 4]); position += temp_palette.len() * 4;
palette = Some(temp_palette);
}
let parts_count = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()); let parts_count = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
position += 4; position += 4;
dbg!(parts_count); dbg!(parts_count);
let mut part_sizes = vec![0; parts_count as usize]; let mut part_sizes = vec![0; parts_count as usize];
let mut total_size = 0; let mut total_size = 0;
let mut decompressed_size = 0;
for size in &mut part_sizes { for size in &mut part_sizes {
let part_size = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) * 2; let part_size = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()) * 2;
*size = part_size; *size = part_size;
total_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() { if position + total_size as usize > bytes.len() {
@ -86,13 +93,7 @@ impl CzImage for Cz1Image {
} }
let mut m_dst = 0; let mut m_dst = 0;
let bitmap = vec![0; 4882176]; let mut bitmap = vec![0; decompressed_size as usize];
let mut image = Self {
header,
bitmap,
palette
};
for size in part_sizes { for size in part_sizes {
let part = &bytes[position..position + size as usize]; let part = &bytes[position..position + size as usize];
@ -102,33 +103,43 @@ impl CzImage for Cz1Image {
let ctl = part[j + 1]; let ctl = part[j + 1];
if ctl == 0 { if ctl == 0 {
image.bitmap[m_dst] = part[j]; bitmap[m_dst] = part[j];
m_dst += 1; m_dst += 1;
} else { } 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) Ok(image)
} }
fn save_as_png(&self, name: &str) { fn save_as_png(&self, name: &str) {
image::save_buffer( let img = image::RgbaImage::from_raw(
name,
&self.bitmap,
self.header.common.width as u32, self.header.common.width as u32,
self.header.common.height as u32, self.header.common.height as u32,
image::ExtendedColorType::Rgba8 self.bitmap.clone()
).unwrap() ).unwrap();
img.save_with_format(name, ImageFormat::Png).unwrap();
} }
fn header(&self) -> &Self::Header { fn header(&self) -> &Self::Header {
&self.header &self.header
} }
fn raw_bitmap(&self) -> &Vec<u8> { fn into_bitmap(self) -> Vec<u8> {
&self.bitmap self.bitmap
} }
} }
@ -136,35 +147,44 @@ 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 apply_palette(input: &mut Vec<u8>, palette: &Vec<Rgba<u8>>) {
fn copy_range(&mut self, input: &[u8], src: usize, dst: usize) -> usize { 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
}
fn copy_range(bitmap: &mut Vec<u8>, input: &[u8], src: usize, dst: usize) -> usize {
let mut dst = dst; let mut dst = dst;
let start_pos = dst; let start_pos = dst;
if input[src + 1] == 0 { if input[src + 1] == 0 {
self.bitmap[dst] = input[src]; bitmap[dst] = input[src];
dst += 1; dst += 1;
} else if get_offset(input, src) == src { } else if get_offset(input, src) == src {
self.bitmap[dst] = 0; bitmap[dst] = 0;
dst += 1; dst += 1;
} else { } else {
dst += self.copy_range(input, get_offset(input, src), dst); dst += copy_range(bitmap, input, get_offset(input, src), dst);
} }
if input[src + 3] == 0 { if input[src + 3] == 0 {
self.bitmap[dst] = input[src + 2]; bitmap[dst] = input[src + 2];
dst += 1; dst += 1;
} else if get_offset(input, src + 2) == src { } else if get_offset(input, src + 2) == src {
self.bitmap[dst] = self.bitmap[start_pos]; bitmap[dst] = bitmap[start_pos];
dst += 1; dst += 1;
} else { } else {
self.bitmap[dst] = copy_one(input, get_offset(input, src + 2)); bitmap[dst] = copy_one(input, get_offset(input, src + 2));
dst += 1; dst += 1;
} }
dst - start_pos dst - start_pos
} }
}
fn copy_one(input: &[u8], src: usize) -> u8 { fn copy_one(input: &[u8], src: usize) -> u8 {
if input[src + 1] == 0 { if input[src + 1] == 0 {

60
src/formats/cz3.rs Normal file
View file

@ -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<u8>,
}
impl CzImage for Cz3Image {
type Header = Cz1Header;
fn decode(bytes: &[u8]) -> Result<Self, CzError> {
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<u8> {
self.bitmap
}
}

View file

@ -2,17 +2,18 @@ pub mod cz_common;
pub mod formats{ pub mod formats{
pub mod cz0; pub mod cz0;
pub mod cz1; pub mod cz1;
pub mod cz3;
} }
// Generic tools // Generic tools
use std::fs; use std::fs;
use crate::{cz_common::CzImage, formats::cz1::Cz1Image}; use crate::{cz_common::CzImage, formats::cz3::Cz3Image};
fn main() { fn main() {
let input = fs::read("../test_files/x5a3bvy.cz1").expect("Error, could not open image"); let input = fs::read("../test_files/Old_TestFiles/129.CZ3").expect("Error, could not open image");
let cz1_file = Cz1Image::decode(&input).unwrap(); let cz3_file = Cz3Image::decode(&input).unwrap();
println!("{:#?}", cz1_file.header()); println!("{:#?}", cz3_file.header());
cz1_file.save_as_png("test.png") cz3_file.save_as_png("test.png")
} }