From e8a3dbd9873af1457ea6f4d53033f905488f1b02 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Tue, 18 Apr 2023 19:29:45 -0500 Subject: [PATCH] Fixed header length issue, updated readme --- .gitignore | 1 + README.md | 17 ++++++++++++++++- src/main.rs | 53 ++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index dbf7bc5..b8a41c8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ Cargo.lock # Ignore CZ image files *.cz* +*.CZ* tmp.png diff --git a/README.md b/README.md index dd4bdab..1992311 100644 --- a/README.md +++ b/README.md @@ -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 | + +Bytes are in Little Endian order diff --git a/src/main.rs b/src/main.rs index dc5e936..bdd33b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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,37 +26,45 @@ struct Image { bitmap: Vec, } -// 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) -> Header { +fn extract_header(header_vec: &Vec) -> (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!("Bound Coords: {}x{}", bound_width, bound_height); - 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, @@ -63,19 +72,19 @@ fn extract_header(header_vec: Vec) -> Header { 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) -> Image { - // Get the header from the first 28 (?) bytes // TODO Research the header more! - let header_bytes: Vec = 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!") } }