Fixed header length issue, updated readme

This commit is contained in:
G2-Games 2023-04-18 19:29:45 -05:00
parent f4e2bd96a0
commit e8a3dbd987
3 changed files with 53 additions and 18 deletions

1
.gitignore vendored
View file

@ -11,4 +11,5 @@ Cargo.lock
# Ignore CZ image files
*.cz*
*.CZ*
tmp.png

View file

@ -1,4 +1,19 @@
# lbee-utils
A small collection of utilities for exporting and importing assets from Little Busters! English Edition
WORK IN PROGRESS
The CZ0 header:
| Offset | Ex. Values | ASCII | Purpose |
|--------|------------|-------|-----------------------------------|
| 0x00 | 43 5A 30 | CZ0 | Magic bytes to identify CZ0 files |
| 0x04 | 24 | 36 | Header length in bytes |
| 0x08 | 58 01 | 344 | Width of the image in pixels |
| 0x0A | DC 02 | 732 | Height of the image in pixels |
| 0x0C | 20 | 32 | Bit depth of the image |
| 0x14 | 58 01 | 344 | Width of image crop |
| 0x16 | DC 02 | 732 | Height of image crop |
| 0x18 | 00 05 | 1280 | Width of image bounding box |
| 0x1A | 34 03 | 820 | Height of image bounding box |
| 0x1C | 80 02 | 640 | X offset of image |
| 0x1E | 02 03 | 770 | Y offset of image |
<sup>Bytes are in Little Endian order</sup>

View file

@ -13,11 +13,12 @@ use std::thread;
use std::time::Duration;
struct Header {
magic: [u8; 3], // The magic bytes, can be CZ0, CZ1, CZ2
magic: [u8; 3], // The magic bytes, can be CZ0, (CZ1, CZ2?)
res: (i16, i16), // The width in the header
depth: i16, // Bit depth
crop: (i16, i16), // Crop dimensions
bounds: (i16, i16), // Bounding box dimensions
offset: (i16, i16), // Offset coordinates
}
struct Image {
@ -25,57 +26,65 @@ struct Image {
bitmap: Vec<u8>,
}
// Converts 8 bit bytes to 16 bit words in little endian
// Converts 8 bit bytes to a 16 bit little endian word
fn bytes_to_word(first:u8, second:u8) -> i16 {
let final_value = ((second as i16) << 8) | (first as i16);
return final_value;
}
fn extract_header(header_vec: Vec<u8>) -> Header {
fn extract_header(header_vec: &Vec<u8>) -> (Header, usize) {
// Get the magic bytes
let magic: [u8; 3] = header_vec[0..3].try_into().unwrap();
println!("Magic Bytes: {:?}", magic);
// Get the length of the header
let header_length = header_vec[4];
println!("Header Length: {:?}", header_length);
// Convert the width and height to i16 values
let width = bytes_to_word(header_vec[8], header_vec[9]);
let height = bytes_to_word(header_vec[10], header_vec[11]);
println!("Resolution: {}x{}", width, height);
// Get the bit depth
let depth = bytes_to_word(header_vec[12], header_vec[13]);
println!("Bit Depth: {} bits", depth);
// Get the crop resolution
let crop_width = bytes_to_word(header_vec[20], header_vec[21]);
let crop_height = bytes_to_word(header_vec[22], header_vec[23]);
println!("Crop Coords: {}x{}", crop_width, crop_height);
// Get bounding box
let bound_width = bytes_to_word(header_vec[24], header_vec[25]);
let bound_height = bytes_to_word(header_vec[26], header_vec[27]);
println!("Magic Bytes: {:?}", magic);
println!("Resolution: {}x{}", width, height);
println!("Bit Depth: {} bits", depth);
println!("Crop Coords: {}x{}", crop_width, crop_height);
println!("Bound Coords: {}x{}", bound_width, bound_height);
// Get offset coordinates
let offset_x = bytes_to_word(header_vec[28], header_vec[29]);
let offset_y = bytes_to_word(header_vec[30], header_vec[31]);
println!("Offset Coords: {}x{}", offset_x, offset_y);
let image_header = Header {
magic,
res: (width, height),
depth,
crop: (crop_width, crop_height),
bounds: (bound_width, bound_height),
offset: (offset_x, offset_y),
};
return image_header;
return (image_header, header_length as usize);
}
// Provided a bitstream, extract the header information and
// the rest of the metadata about a CZ file
fn decode_cz(mut input:Vec<u8>) -> Image {
// Get the header from the first 28 (?) bytes
// TODO Research the header more!
let header_bytes: Vec<u8> = input.drain(0..28).collect();
let header = extract_header(header_bytes);
let (header, header_length)= extract_header(&input);
input.drain(..header_length);
// Construct the image struct
let final_image = Image {
@ -105,13 +114,16 @@ fn main() -> io::Result<()> {
// Read all bytes of the CZ image to an array
println!("Reading image...");
let image_raw = fs::read("782s.cz0")?;
let image_raw = fs::read("775.cz0")?;
let image = decode_cz(image_raw);
let width = image.header.res.0 as i32;
let height = image.header.res.1 as i32;
println!("{}", width*height);
println!("{}", image.bitmap.len() / 4);
// Build the window
let window = video_subsystem.window("SDL2 Rust", width as u32, height as u32)
.position_centered()
@ -143,18 +155,25 @@ fn main() -> io::Result<()> {
canvas.present(); // Display the image on the SDL2 canvas
// Create and save a PNG of the image data
let tmp = RgbaImage::from_raw(
// This errors if the image data is too short
let tmp = match RgbaImage::from_raw(
width as u32,
height as u32,
image.bitmap
).unwrap();
image.bitmap,
) {
Some(img) => img,
None => {
RgbaImage::new(0, 0)
}
};
match tmp.save_with_format("tmp.png", ImageFormat::Png) {
Ok(()) => {
println!("Image saved successfully");
}
Err(e) => {
eprintln!("Error saving image: {}", e);
eprintln!("ERROR SAVING IMAGE: {}", e);
eprintln!("You probably have an image with the CZ0 offset bug!")
}
}