mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 15:22:53 -05:00
Preliminary implementation of CZ0 encoding
This commit is contained in:
parent
e8a3dbd987
commit
9ff891b02e
1 changed files with 113 additions and 107 deletions
220
src/main.rs
220
src/main.rs
|
@ -1,73 +1,78 @@
|
||||||
use sdl2::pixels::Color;
|
|
||||||
use sdl2::rect::Point;
|
|
||||||
//use sdl2::render::Canvas;
|
|
||||||
//use sdl2::video::Window;
|
|
||||||
use sdl2::event::Event;
|
|
||||||
use sdl2::keyboard::Keycode;
|
|
||||||
|
|
||||||
use image::{RgbaImage, ImageFormat, open};
|
use image::{RgbaImage, ImageFormat, open};
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::thread;
|
use std::fs::File;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
struct Header {
|
#[derive(Copy, Clone)]
|
||||||
|
struct HeaderCZ0 {
|
||||||
magic: [u8; 3], // The magic bytes, can be CZ0, (CZ1, CZ2?)
|
magic: [u8; 3], // The magic bytes, can be CZ0, (CZ1, CZ2?)
|
||||||
|
length: u8,
|
||||||
res: (i16, i16), // The width in the header
|
res: (i16, i16), // The width in the header
|
||||||
depth: i16, // Bit depth
|
depth: u8, // 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
|
offset: (i16, i16), // Offset coordinates
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Image {
|
struct CZ0File {
|
||||||
header: Header,
|
header: HeaderCZ0,
|
||||||
bitmap: Vec<u8>,
|
bitmap: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts 8 bit bytes to a 16 bit little endian word
|
// Converts 8 bit bytes to a 16 bit little endian word or
|
||||||
fn bytes_to_word(first:u8, second:u8) -> i16 {
|
pub 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, usize) {
|
// Converts a 16 bit little endian word to 8 bit bytes
|
||||||
|
pub fn word_to_bytes(word:i16) -> [u8; 2] {
|
||||||
|
let first: u8 = (word & 0xFF) as u8; // Extract the first byte
|
||||||
|
let second: u8 = ((word >> 8) & 0xFF) as u8; // Extract the second byte
|
||||||
|
|
||||||
|
return [first, second];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract all the header information from a CZ0 file
|
||||||
|
fn extract_header_cz0(header_vec: &Vec<u8>) -> (HeaderCZ0, 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);
|
println!("Magic Bytes : {:?}", magic);
|
||||||
|
|
||||||
// Get the length of the header
|
// Get the length of the header
|
||||||
let header_length = header_vec[4];
|
let length = header_vec[4];
|
||||||
println!("Header Length: {:?}", header_length);
|
println!("Header Length: {:?}", 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);
|
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 = header_vec[12];
|
||||||
println!("Bit Depth: {} bits", depth);
|
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);
|
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!("Bound Coords: {}x{}", bound_width, bound_height);
|
println!("Bound Coords : {}x{}", bound_width, bound_height);
|
||||||
|
|
||||||
// Get offset coordinates
|
// Get offset coordinates
|
||||||
let offset_x = bytes_to_word(header_vec[28], header_vec[29]);
|
let offset_x = bytes_to_word(header_vec[28], header_vec[29]);
|
||||||
let offset_y = bytes_to_word(header_vec[30], header_vec[31]);
|
let offset_y = bytes_to_word(header_vec[30], header_vec[31]);
|
||||||
println!("Offset Coords: {}x{}", offset_x, offset_y);
|
println!("Offset Coords: {}x{}", offset_x, offset_y);
|
||||||
|
|
||||||
let image_header = Header {
|
let image_header = HeaderCZ0 {
|
||||||
magic,
|
magic,
|
||||||
|
length,
|
||||||
res: (width, height),
|
res: (width, height),
|
||||||
depth,
|
depth,
|
||||||
crop: (crop_width, crop_height),
|
crop: (crop_width, crop_height),
|
||||||
|
@ -75,19 +80,24 @@ fn extract_header(header_vec: &Vec<u8>) -> (Header, usize) {
|
||||||
offset: (offset_x, offset_y),
|
offset: (offset_x, offset_y),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (image_header, header_length as usize);
|
return (image_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 CZ0 file, returning a
|
||||||
fn decode_cz(mut input:Vec<u8>) -> Image {
|
/// struct containing the header information and bitmap
|
||||||
|
fn decode_cz0(input_filename:&str) -> CZ0File {
|
||||||
|
println!("Reading input file...");
|
||||||
|
let mut input = fs::read(input_filename).unwrap();
|
||||||
|
|
||||||
|
println!("Decoding input...");
|
||||||
|
|
||||||
// TODO Research the header more!
|
// TODO Research the header more!
|
||||||
let (header, header_length)= extract_header(&input);
|
let (header, header_length) = extract_header_cz0(&input);
|
||||||
input.drain(..header_length);
|
input.drain(..header_length);
|
||||||
|
|
||||||
// Construct the image struct
|
// Construct the output CZ0 image
|
||||||
let final_image = Image {
|
let final_image = CZ0File {
|
||||||
header,
|
header,
|
||||||
bitmap: input,
|
bitmap: input,
|
||||||
};
|
};
|
||||||
|
@ -95,70 +105,81 @@ fn decode_cz(mut input:Vec<u8>) -> Image {
|
||||||
return final_image;
|
return final_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Provided an image, extract the bitstream and create the
|
||||||
fn encode_cz() -> Image {
|
/// header information from a previous CZ0 file in order to
|
||||||
let on_top = open("path/to/some.png").unwrap().into_rgb8();
|
/// replace it
|
||||||
|
fn encode_cz0(original_file:CZ0File, in_name:&str, out_name:&str) -> io::Result<()> {
|
||||||
|
let input_image = open(in_name).unwrap().into_rgba8();
|
||||||
|
let (input_width, input_height) = input_image.dimensions();
|
||||||
|
|
||||||
// Construct the image struct
|
let width_diff = input_width as i16 - original_file.header.res.0;
|
||||||
let final_image = Image {
|
let height_diff = input_height as i16 - original_file.header.res.1;
|
||||||
header,
|
|
||||||
bitmap: input,
|
// Crop
|
||||||
|
let crop_width = input_width as i16;
|
||||||
|
let crop_height = input_height as i16;
|
||||||
|
|
||||||
|
// Offset
|
||||||
|
let offset_x = original_file.header.offset.0 + width_diff;
|
||||||
|
let offset_y = original_file.header.offset.1 + height_diff;
|
||||||
|
|
||||||
|
// Construct the header
|
||||||
|
let header = HeaderCZ0 {
|
||||||
|
magic: [67, 90, 48],
|
||||||
|
length: 36,
|
||||||
|
res: (input_width as i16, input_height as i16),
|
||||||
|
depth: 32,
|
||||||
|
crop: (crop_width, crop_height),
|
||||||
|
bounds: (original_file.header.bounds.0, original_file.header.bounds.1),
|
||||||
|
offset: (offset_x, offset_y),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Image;
|
let bitmap = input_image.to_vec();
|
||||||
}*/
|
let mut file = File::create(out_name)?;
|
||||||
|
|
||||||
|
// Write magic and length
|
||||||
|
file.write_all(&header.magic)?;
|
||||||
|
file.write_all(&[0])?;
|
||||||
|
file.write_all(&[header.length])?;
|
||||||
|
file.write_all(&vec![0u8; 3])?;
|
||||||
|
|
||||||
|
// Write width and height
|
||||||
|
file.write_all(&word_to_bytes(header.res.0))?;
|
||||||
|
file.write_all(&word_to_bytes(header.res.1))?;
|
||||||
|
|
||||||
|
// Write bit depth
|
||||||
|
file.write_all(&[header.depth])?;
|
||||||
|
file.write_all(&vec![0u8; 7])?;
|
||||||
|
|
||||||
|
// Write crop width and height
|
||||||
|
file.write_all(&word_to_bytes(header.crop.0))?;
|
||||||
|
file.write_all(&word_to_bytes(header.crop.1))?;
|
||||||
|
|
||||||
|
// Write bound width and height
|
||||||
|
file.write_all(&word_to_bytes(header.bounds.0))?;
|
||||||
|
file.write_all(&word_to_bytes(header.bounds.1))?;
|
||||||
|
|
||||||
|
// Write offset width and height
|
||||||
|
file.write_all(&word_to_bytes(header.offset.0))?;
|
||||||
|
file.write_all(&word_to_bytes(header.offset.1))?;
|
||||||
|
|
||||||
|
// Write unknown padding bytes
|
||||||
|
file.write_all(&vec![0u8; 4])?;
|
||||||
|
|
||||||
|
// Write the actual image data
|
||||||
|
file.write_all(&bitmap)?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let image = decode_cz0("775.cz0");
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
|
||||||
|
|
||||||
// Read all bytes of the CZ image to an array
|
/* Create and save a PNG of the image data
|
||||||
println!("Reading image...");
|
|
||||||
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()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut canvas = window.into_canvas().build().unwrap();
|
|
||||||
canvas.set_draw_color(Color::RGBA(255, 255, 255, 255));
|
|
||||||
canvas.clear();
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(40)); // Sleep to allow the window to display
|
|
||||||
|
|
||||||
// Draw the bitmap to the canvas
|
|
||||||
for (i, chunk) in image.bitmap.chunks(4).enumerate() {
|
|
||||||
let a = chunk[3] / 255;
|
|
||||||
|
|
||||||
// Get the pixel colors
|
|
||||||
let r = chunk[0] * a;
|
|
||||||
let g = chunk[1] * a;
|
|
||||||
let b = chunk[2] * a;
|
|
||||||
canvas.set_draw_color(Color::RGBA(r, g, b, a));
|
|
||||||
|
|
||||||
// Get the pixel location
|
|
||||||
let y = i as i32/width;
|
|
||||||
let x = i as i32%width;
|
|
||||||
canvas.draw_point(Point::new(x, y)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.present(); // Display the image on the SDL2 canvas
|
|
||||||
|
|
||||||
// Create and save a PNG of the image data
|
|
||||||
// This errors if the image data is too short
|
// This errors if the image data is too short
|
||||||
let tmp = match RgbaImage::from_raw(
|
let tmp = match RgbaImage::from_raw(
|
||||||
width as u32,
|
image.header.res.0 as u32,
|
||||||
height as u32,
|
image.header.res.1 as u32,
|
||||||
image.bitmap,
|
image.bitmap,
|
||||||
) {
|
) {
|
||||||
Some(img) => img,
|
Some(img) => img,
|
||||||
|
@ -175,25 +196,10 @@ fn main() -> io::Result<()> {
|
||||||
eprintln!("ERROR SAVING IMAGE: {}", e);
|
eprintln!("ERROR SAVING IMAGE: {}", e);
|
||||||
eprintln!("You probably have an image with the CZ0 offset bug!")
|
eprintln!("You probably have an image with the CZ0 offset bug!")
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// Wait for the user to press escape
|
encode_cz0(image, "melon_test.png", "test.cz0").unwrap();
|
||||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
decode_cz0("test.cz0");
|
||||||
loop {
|
|
||||||
for event in event_pump.poll_iter() {
|
|
||||||
match event {
|
|
||||||
Event::Quit { .. } | Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Escape),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
println!("Exiting");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
return Ok(());
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue