mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 07:12:55 -05:00
Broken cz1 implementation
This commit is contained in:
parent
dc14fd1d65
commit
d11272463b
5 changed files with 208 additions and 13 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@ Cargo.lock
|
|||
*.cz*
|
||||
*.CZ*
|
||||
*.png
|
||||
|
||||
test_files/*
|
||||
|
|
|
@ -5,7 +5,10 @@ use thiserror::Error;
|
|||
#[derive(Error, Debug)]
|
||||
pub enum CzError {
|
||||
#[error("Version in header does not match expected version")]
|
||||
VersionMismatch
|
||||
VersionMismatch,
|
||||
|
||||
#[error("Format of supplied file is incorrect")]
|
||||
InvalidFormat,
|
||||
}
|
||||
|
||||
pub trait CzHeader {
|
||||
|
@ -19,7 +22,7 @@ pub trait CzHeader {
|
|||
|
||||
fn height(&self) -> u16;
|
||||
|
||||
fn depth(&self) -> u8;
|
||||
fn depth(&self) -> u16;
|
||||
}
|
||||
|
||||
/// The common first part of a header of a CZ# file
|
||||
|
@ -29,7 +32,7 @@ pub(crate) struct CommonHeader {
|
|||
pub version: u8,
|
||||
|
||||
/// Length of the header in bytes
|
||||
pub length: u8,
|
||||
pub length: u32,
|
||||
|
||||
/// Width of the image in pixels
|
||||
pub width: u16,
|
||||
|
@ -38,17 +41,17 @@ pub(crate) struct CommonHeader {
|
|||
pub height: u16,
|
||||
|
||||
/// Bit depth in Bits Per Pixel (BPP)
|
||||
pub depth: u8,
|
||||
pub depth: u16,
|
||||
}
|
||||
|
||||
impl CommonHeader {
|
||||
pub fn new(bytes: &[u8]) -> Self {
|
||||
Self {
|
||||
version: bytes[2] - b'0',
|
||||
length: bytes[4],
|
||||
length: u32::from_le_bytes(bytes[4..8].try_into().unwrap()),
|
||||
width: u16::from_le_bytes(bytes[8..10].try_into().unwrap()),
|
||||
height: u16::from_le_bytes(bytes[10..12].try_into().unwrap()),
|
||||
depth: bytes[12],
|
||||
depth: u16::from_le_bytes(bytes[12..14].try_into().unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,3 +71,15 @@ pub trait CzImage {
|
|||
/// Get the raw underlying bitmap for an image
|
||||
fn raw_bitmap(&self) -> &Vec<u8>;
|
||||
}
|
||||
|
||||
pub fn parse_colormap(input: &[u8], num_colors: usize) -> (Vec<[u8; 4]>, usize) {
|
||||
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, num_colors * 4)
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ impl CzHeader for Cz0Header {
|
|||
self.common.height
|
||||
}
|
||||
|
||||
fn depth(&self) -> u8 {
|
||||
fn depth(&self) -> u16 {
|
||||
self.common.depth
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ impl CzImage for Cz0Image {
|
|||
let header = Cz0Header::new(bytes)?;
|
||||
|
||||
// Get the rest of the file, which is the bitmap
|
||||
let bitmap = bytes[header.header_length() as usize..].to_vec();
|
||||
let bitmap = bytes[header.header_length()..].to_vec();
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
|
|
177
src/formats/cz1.rs
Normal file
177
src/formats/cz1.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use std::io::Read;
|
||||
|
||||
use crate::cz_common::{parse_colormap, CommonHeader, CzError, CzHeader, CzImage};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cz1Header {
|
||||
/// Common CZ# header
|
||||
common: CommonHeader,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cz1Image {
|
||||
header: Cz1Header,
|
||||
bitmap: Vec<u8>,
|
||||
palette: Vec<[u8; 4]>,
|
||||
}
|
||||
|
||||
impl CzHeader for Cz1Header {
|
||||
fn new(bytes: &[u8]) -> Result<Self, CzError> {
|
||||
let common = CommonHeader::new(bytes);
|
||||
|
||||
if common.version != 1 {
|
||||
return Err(CzError::VersionMismatch)
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
common,
|
||||
})
|
||||
}
|
||||
|
||||
fn version(&self) -> u8 {
|
||||
self.common.version
|
||||
}
|
||||
|
||||
fn header_length(&self) -> usize {
|
||||
self.common.length as usize
|
||||
}
|
||||
|
||||
fn width(&self) -> u16 {
|
||||
self.common.width
|
||||
}
|
||||
|
||||
fn height(&self) -> u16 {
|
||||
self.common.height
|
||||
}
|
||||
|
||||
fn depth(&self) -> u16 {
|
||||
self.common.depth
|
||||
}
|
||||
}
|
||||
|
||||
impl CzImage for Cz1Image {
|
||||
type Header = Cz1Header;
|
||||
|
||||
fn decode(bytes: &[u8]) -> Result<Self, CzError> {
|
||||
let mut position = 0;
|
||||
|
||||
// Get the header from the input
|
||||
let header = Cz1Header::new(bytes)?;
|
||||
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 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;
|
||||
|
||||
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;
|
||||
|
||||
dbg!(part_size);
|
||||
|
||||
position += 8;
|
||||
}
|
||||
|
||||
if position + total_size as usize > bytes.len() {
|
||||
return Err(CzError::InvalidFormat)
|
||||
}
|
||||
|
||||
let mut m_dst = 0;
|
||||
let bitmap = vec![0; 4882176];
|
||||
|
||||
let mut image = Self {
|
||||
header,
|
||||
bitmap,
|
||||
palette
|
||||
};
|
||||
|
||||
for size in part_sizes {
|
||||
let part = &bytes[position..position + size as usize];
|
||||
position += size as usize;
|
||||
|
||||
for j in (0..part.len()).step_by(2) {
|
||||
let ctl = part[j + 1];
|
||||
|
||||
if ctl == 0 {
|
||||
image.bitmap[m_dst] = part[j];
|
||||
m_dst += 1;
|
||||
} else {
|
||||
m_dst += image.copy_range(part, get_offset(part, j), m_dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
fn save_as_png(&self, name: &str) {
|
||||
image::save_buffer(
|
||||
name,
|
||||
&self.bitmap,
|
||||
self.header.common.width as u32,
|
||||
self.header.common.height as u32,
|
||||
image::ExtendedColorType::Rgba8
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
|
||||
fn raw_bitmap(&self) -> &Vec<u8> {
|
||||
&self.bitmap
|
||||
}
|
||||
}
|
||||
|
||||
fn get_offset(input: &[u8], src: usize) -> usize {
|
||||
(((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;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_one(input: &[u8], src: usize) -> u8 {
|
||||
if input[src + 1] == 0 {
|
||||
input[src]
|
||||
} else if get_offset(input, src) == src {
|
||||
0
|
||||
} else {
|
||||
copy_one(input, get_offset(input, src))
|
||||
}
|
||||
}
|
11
src/main.rs
11
src/main.rs
|
@ -1,17 +1,18 @@
|
|||
pub mod cz_common;
|
||||
pub mod formats{
|
||||
pub mod cz0;
|
||||
pub mod cz1;
|
||||
}
|
||||
|
||||
// Generic tools
|
||||
use std::fs;
|
||||
|
||||
use crate::{cz_common::CzImage, formats::cz0::Cz0Image};
|
||||
use crate::{cz_common::CzImage, formats::cz1::Cz1Image};
|
||||
|
||||
fn main() {
|
||||
let input = fs::read("../test_files/Old_TestFiles/EX_PT.CZ0").expect("Error, could not open image");
|
||||
let cz0_file = Cz0Image::decode(&input).unwrap();
|
||||
println!("{:#?}", cz0_file.header());
|
||||
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());
|
||||
|
||||
cz0_file.save_as_png("test.png")
|
||||
cz1_file.save_as_png("test.png")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue