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!")
}
}