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 # Ignore CZ image files
*.cz* *.cz*
*.CZ*
tmp.png tmp.png

View file

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