commit 7e38cb0bb0c15c919c682c7572cfec17a3c144a6 Author: Franco Colmenarez Date: Sun May 16 20:08:25 2021 -0500 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..633c00e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +*.pkz +.vscode diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9ade557 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "libc" +version = "0.2.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "pokemon_sprite" +version = "0.1.0" +dependencies = [ + "termion", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + +[[package]] +name = "termion" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..16aa9e4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "pokemon_sprite" +version = "0.1.0" +authors = ["Franco Colmenarez "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +termion = "1.5.6" diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..8f1b271 --- /dev/null +++ b/src/README.md @@ -0,0 +1,3 @@ +``` +dd if=pkmn-yellow-en.gb of=surprise.pkz ibs=1 skip=183637 count=244 +``` \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7451121 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,607 @@ +use std::env; +use std::cmp; +use std::io::prelude::*; +use std::fs::File; + +enum EncodingMode { + Mode1, + Mode2, + Mode3, +} + +struct BitStream { + bit_index: u8, + byte_index: usize, + bytes: Vec, + last_two_bits: u8, +} + +impl BitStream { + fn load_bytes_from_file(&mut self, filename: &str) { + let mut file = match File::open(&filename) { + Ok(file) => file, + Err(error) => panic!("Could not open the file! {:?}", error) + }; + match file.read_to_end(&mut self.bytes) { + Ok(_) => println!("File data loaded!"), + Err(_) => panic!("An error ocurred trying to read the file"), + }; + } + + fn next_bit(&mut self) { + self.bit_index += 1; + self.check_end_of_byte(); + self.update_last_two_bits(); + } + + fn current_byte(&self) -> u8 { + match self.bytes.get(self.byte_index) { + Some(byte) => *byte, + None => 0, + } + } + + fn current_bit(&self) -> u8 { + (self.current_byte() >> 7 - self.bit_index) & 0b00000001 + } + + fn update_last_two_bits(&mut self) { + self.last_two_bits = ((self.last_two_bits << 1) | self.current_bit()) & 0b00000011; + } + + fn check_end_of_byte(&mut self) { + let last_byte_index = self.bytes.len() - 1; + if self.byte_index >= last_byte_index { + self.byte_index = last_byte_index; + if self.bit_index >= 7 { + self.bit_index = 7; + } + } else { + if self.bit_index > 7 { + self.bit_index = 0; + self.byte_index += 1; + if self.byte_index > last_byte_index { + self.byte_index = last_byte_index; + } + } + } + } + + fn read_bits(&mut self, bits_amount: u8, write_from_left: bool) -> u8 { + let mut count: u8 = 0; + let mut byte: u8 = 0; + + while count < bits_amount { + byte = match write_from_left { + true => byte | (self.current_bit() << 7 - count), + false => (byte << 1) | self.current_bit(), + }; + + count += 1; + self.update_last_two_bits(); + self.next_bit(); + } + + byte + } + + fn bits_left(&self) -> usize { + (self.bytes.len() * 8) - ((self.byte_index * 8) + 1 + self.bit_index as usize) as usize + } +} + +struct Buffer { + bit_index: u8, + width: u8, + height: u8, + vertical_offset: u8, + horizontal_offset: u8, + byte_index: usize, + bytes: Vec, + bitplane_length: usize, + row_index: usize, +} + +impl Buffer { + fn allocate_space(&mut self, width: u8, height: u8) { + const MAX_SPRITE_SIZE: u8 = 7; // 7 tiles + self.width = width; + self.height = height; + // We will need the vertical and horizontal offsets later, this is used to center the resulting + // sprite in a box of 7 * 7 tiles + // vertical offset = 7 - height + // horizontal offset = ((7 - width) / 2) + (1/2) -> then round the result down + self.vertical_offset = MAX_SPRITE_SIZE - width; + self.horizontal_offset = ((MAX_SPRITE_SIZE - height) / 2) + (1 / 2); + // We need 3 bitplanes, the first and second ones are where the + // decompressed bytes will be, which are 7 x 7 each. + // The third one is usually 7 x 7 maximum too, but glitched pokemon could + // have way more + // Note: Each tile has 64 pixels + self.bitplane_length = ((7 * 7 * 2) + cmp::max(7 * 7, width as usize * height as usize)) * 8; + self.bytes = vec![0; self.bitplane_length]; + } + + fn write_pair(&mut self, data: u8) { + let column_height = self.height * 8; + + self.bytes[self.byte_index] = self.bytes[self.byte_index] | (data << 8 - (self.bit_index + 2)); + + self.byte_index += 1; + self.row_index += 1; + // We have reached the end of the column + if self.row_index >= column_height as usize { + self.row_index = 0; + self.bit_index += 2;// Next column (in bits) + self.byte_index -= column_height as usize; + if self.bit_index >= 7 { + self.bit_index = 0; + // Jump to the next column + self.byte_index += column_height as usize; + } + } + } + + fn write_zero_pairs(&mut self, zero_pairs_amount: usize) { + let mut count = 0; + + while count < zero_pairs_amount { + self.write_pair(0); + count += 1; + } + } + + fn decompress_to_bitplane(&mut self, bytes: &mut BitStream, initial_packet: u8, primary_buffer: bool, first_bitplane: bool) { + let mut rle_length: u8 = 0; + let mut reading_first_rle = initial_packet == 0; // 1 for data packet and 0 for RLE packet + let mut reading_second_rle = false; + let mut first_rle_bits_read: u16 = 0; + let mut bits_written: usize = 0; + let bytes_to_write: usize = self.width as usize * self.height as usize * 8; + // If the primary buffer is true, start decoding into buffer B at location 392, + // else, decode into buffer C at location 784 + // Glitched pokemons overflow from Buffer B to C + self.byte_index = if primary_buffer {7 * 7 * 8} else {7 * 7 * 8 * 2}; + self.bit_index = 0; + self.row_index = 0; + + println!("Bytes to write: {}", bytes_to_write); + while (first_bitplane && bits_written < bytes_to_write * 8) || (!first_bitplane && bytes.bits_left() > 0) { + + if reading_first_rle { + let current_bit = bytes.current_bit(); + rle_length += 1; // We have to count the length of the rle packet even if the bit is zero + if current_bit == 0 { + // Once we find a zero, we can start reading the amount of bits we counted + // If the bit is 1, we increment the count by 1 and jump to the next bit + first_rle_bits_read = (first_rle_bits_read << 1) | (current_bit as u16); + reading_first_rle = false; + reading_second_rle = true; + } else { + // If the bit is 1, we increment the count by 1 and jump to the next bit + first_rle_bits_read = (first_rle_bits_read << 1) | (current_bit as u16); + } + bytes.next_bit(); + continue; + } + if reading_second_rle { + // Read the amount of bits we counted + let mut second_rle_bits_read = 0; + let mut rle_bits_count = 0; + while rle_bits_count < rle_length { + let current_bit = bytes.current_bit(); + second_rle_bits_read = (second_rle_bits_read << 1) | (current_bit as u16); + rle_bits_count += 1; + bytes.next_bit(); + } + // Then, we add the first 2 groups plus one (the plus one is to take care of + // the "offset" of the compression algorithm) + // This is the amount to zero pairs that we have to add to the buffer. + // For example, if the result is 4, we will have to add 4 zero pairs, + // or 8 zeros in total + let zero_pairs = first_rle_bits_read + second_rle_bits_read + 1; + self.write_zero_pairs(zero_pairs as usize); // write the zero pairs to the buffer + bits_written += zero_pairs as usize * 2; + first_rle_bits_read = 0; + rle_length = 0; + // Aftrer reading RLE packets, the next is a data packet until we find a 00 pair + reading_first_rle = false; + reading_second_rle = false; + continue; + } + // When we are not reading RLE packets, we can read the pairs of data (data packets) until + // we find a 00 pair + if !reading_first_rle && !reading_second_rle { + let bits_pair = bytes.read_bits(2, false); + if bits_pair == 0 { + rle_length = 0; + reading_first_rle = true; + reading_second_rle = false; + } else { + self.write_pair(bits_pair); // write the zero pairs to the buffer + bits_written += 2; + } + } + } + println!("Bytes written: {}", bits_written / 8); + } + + fn delta_decode(&mut self, buffer: u8) { + let index_offset = match buffer{ + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + let mut row_index: usize = 0; + let row_height = self.height as usize * 8; // Height in bits + let col_width = self.width as usize; // Width in bytes + // The initial state is always zero at the beginning of each row + let delta_decode_nibble: [u8; 16] = [ + 0b0000, 0b0001, 0b0011, 0b0010, + 0b0111, 0b0110, 0b0100, 0b0101, + 0b1111, 0b1110, 0b1100, 0b1101, + 0b1000, 0b1001, 0b1011, 0b1010, + ]; + + // We have to process row by row, then pairs of 4 bits for each column + while row_index < row_height { + let mut prev_state = 0; + let mut col_index: usize = 0; + + while col_index < col_width { + // Calculate the index in the bytes + let index: usize = (col_index * (self.height as usize * 8) + row_index) + index_offset; + let byte = self.bytes[index]; + + // Getting the first sub-column (4 bits) + let first = delta_decode_nibble[(byte >> 4) as usize] ^ (0b1111 * prev_state); + prev_state = first & 1; + + // Then the second sub-column (4 bits) + let second = delta_decode_nibble[(byte & 0b1111) as usize] ^ (0b1111 * prev_state); + prev_state = second & 1; + + // Combine the two + self.bytes[index] = (first << 4) + second; + col_index += 1; + } + + row_index += 1; + self.byte_index += 1; + } + } + + fn xor_buffers(&mut self, buffer_index: u8, replace_buffer: u8) { + let buffer_index_offset: usize = match buffer_index { + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + let replace_index_offset: usize = match replace_buffer { + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + let mut row_index = 0; + let row_height = self.height as usize * 8; // Height in bits + while row_index < row_height { + + self.bytes[(row_index + replace_index_offset) as usize] = + self.bytes[(row_index + replace_index_offset) as usize] ^ + self.bytes[(row_index + buffer_index_offset) as usize]; + + row_index += 1; + } + } + + fn wipe_bitplane(&mut self, bitplane: u8) { + let offset: usize = match bitplane { + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + + // Wipe the "to" bitplane first + let mut index = offset; + let buffer_size = 7 * 7 * 8; + while index < offset + buffer_size { + self.bytes[index] = 0; + index += 1; + } + } + + fn copy_bitplane(&mut self, from: u8, to: u8) { + + self.wipe_bitplane(to); + + let to_bitplane_start: usize = match to { + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + let from_bitplane_start: usize = match from { + 0 => 0, // Address at 0 + 1 => 7 * 7 * 8, // Address at 392 + _ => 7 * 7 * 8 * 2, // Address at 784 + }; + let mut from_bitplane_index = from_bitplane_start; + + // Step 1: calculate the offset of the top-left corner + let mut index: usize = ((self.vertical_offset as usize * 8) + (self.horizontal_offset as usize * 8 * 7)) + to_bitplane_start; + + // Step 2: copy the columns (height) of tiles + let height = self.height as usize * 8; + let mut current_column = 0; + while current_column < self.width { + let mut row_count: usize = 0; + while row_count < height { + self.bytes[index] = self.bytes[from_bitplane_index]; + index += 1; + from_bitplane_index += 1; + row_count += 1; + } + + // Revert the pointer back to the previous offset and add 56 + // This will put the pointer to the next offset vertical offset + index -= height; + index += 56; + + current_column += 1; + } + } + + fn zip_buffers(&mut self) { + let mut last_index_buffer_a: usize = (7 * 7 * 8) - 1; + let mut last_index_buffer_b: usize = (7 * 7 * 8 * 2) - 1; + let mut last_index_buffer_c: usize = (7 * 7 * 8 * 3) - 1; + + println!("last index A: {}", last_index_buffer_a); + println!("last index B: {}", last_index_buffer_b); + println!("last index C: {}", last_index_buffer_c); + + loop { + self.bytes[last_index_buffer_c] = self.bytes[last_index_buffer_b]; + last_index_buffer_c -= 1; + self.bytes[last_index_buffer_c] = self.bytes[last_index_buffer_a]; + + if last_index_buffer_a == 0 { + break; + } + + last_index_buffer_a -= 1; + last_index_buffer_b -= 1; + last_index_buffer_c -= 1; + } + + println!("Buffer C result: {:02X?}", &self.bytes[last_index_buffer_c..]); + } + + fn render(&self) { + + println!("{}", termion::clear::All); + let pixel_height = 7 * 8; + let buffer_b_start = 7 * 7 * 8; // 392 + let mut index = buffer_b_start; + let buffer_c_end = (7 * 7 * 8 * 3) - 1; // 392 + let mut pixel_row = 0; + let mut pixel_col = 0; + let mut current_row_pixel_count = 0; + let mut total_pixels_count = 0; + + while index <= buffer_c_end { + let byte = self.bytes[index]; + let mut shift: u8 = 8; + let mut count = 0; + while count < 4 { + shift -= 2; + let bit_pair = (byte >> shift) & 0b00000011; + count += 1; + + let coords = termion::cursor::Goto(pixel_col + 1, pixel_row + 1); + + match bit_pair { + 0 => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::White)), + 1 => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::Blue)), + 2 => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::LightBlue)), + _ => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::Black)), + } + + /* match bit_pair { + 0 => print!("{goto}0", goto = coords), + 2 => print!("{goto}1", goto = coords), + 3 => print!("{goto}2", goto = coords), + _ => print!("{goto}3", goto = coords), + } */ + + total_pixels_count += 1; + + pixel_col += 1; + current_row_pixel_count += 1; + if current_row_pixel_count > 7 { + current_row_pixel_count = 0; + pixel_col -= 8; + pixel_row += 1; + if pixel_row > pixel_height { + pixel_col += 8; + pixel_row = 0; + } + } + } + index += 1; + } + + println!("{reset}", reset = termion::style::Reset); + println!("Total pixels count: {}", total_pixels_count); + } + + fn render_all_buffers(&self) { + + println!("{}", termion::clear::All); + let pixel_height = 7 * 8; + let mut pixel_row = 0; + let mut pixel_col = 0; + let mut current_row_pixel_count = 0; + let mut total_pixels_count = 0; + + for byte in &self.bytes[..] { + let mut shift: u8 = 8; + let mut count = 0; + while count < 4 { + shift -= 2; + let bit_pair = (byte >> shift) & 0b00000011; + count += 1; + + let coords = termion::cursor::Goto(pixel_col + 1, pixel_row + 1); + + match bit_pair { + 0 => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::White)), + _ => print!("{goto}{color} ", goto = coords, color = termion::color::Bg(termion::color::Black)), + } + + total_pixels_count += 1; + + pixel_col += 1; + current_row_pixel_count += 1; + if current_row_pixel_count > 7 { + current_row_pixel_count = 0; + pixel_col -= 8; + pixel_row += 1; + if pixel_row > pixel_height { + pixel_col += 8; + pixel_row = 0; + } + } + } + } + + println!("{reset}", reset = termion::style::Reset); + println!("Total pixels count: {}", total_pixels_count); + } +} + +fn main() { + + // Get the filename + let args: Vec = env::args().collect(); + + let filename = match &args.get(1) { + Some(filename) => filename.clone(), + None => panic!("No filename specified!"), + // None => "surprise.pkz", + }; + + println!("Filename: {}", &filename); + + let mut sprite_bytes = BitStream { + bit_index: 0, + byte_index: 0, + last_two_bits: 0, + bytes: Vec::new(), + }; + sprite_bytes.load_bytes_from_file(&filename); + println!("{:02X?}", sprite_bytes.bytes); + + // Read the first byte: + // The first 4 bits are for the sprite width and the second 4 bits for the height + + let sprite_width: u8 = sprite_bytes.read_bits(4, false); // Read next 4 bits + let sprite_height: u8 = sprite_bytes.read_bits(4, false); // Read next 4 bits + + // Width the width and height we can allocate the buffer + let mut buffer = Buffer { + bit_index: 0, + byte_index: 0, + width: 0, + height: 0, + vertical_offset: 0, + horizontal_offset: 0, + bytes: Vec::new(), + bitplane_length: 0, + row_index: 0, + }; + buffer.allocate_space(sprite_width, sprite_height); + + println!("Sprite width: {}", buffer.width); + println!("Sprite height: {}", buffer.height); + println!("Vertical offset: {}", buffer.vertical_offset); + println!("Horizontal offset: {}", buffer.horizontal_offset); + + // Primary buffer: this defines which bit buffer should be processed first + let primary_buffer: u8 = sprite_bytes.read_bits(1, false); // Read next 1 bit + println!("Primary buffer: {}", primary_buffer); + + + // Initial packet type of the data + // 0 means RLE packet and 1 means data packet + let initial_packet: u8 = sprite_bytes.read_bits(1, false); // Read next 1 bit + println!("Initial packet: {}", initial_packet); + println!("Total bitplane length: {}", buffer.bitplane_length * 8); + + println!("Starting to decompress the first buffer!"); + buffer.decompress_to_bitplane(&mut sprite_bytes, initial_packet, primary_buffer == 0, true); + println!("{:02X?}", buffer.bytes); + + let encoding_mode: EncodingMode = { + if sprite_bytes.current_bit() == 0 { + sprite_bytes.next_bit(); + println!("Encoding mode 1"); + EncodingMode::Mode1 + } else { + sprite_bytes.next_bit(); + match sprite_bytes.current_bit() { + 0 => { + println!("Encoding mode 2"); + EncodingMode::Mode2 + }, + _ => { + println!("Encoding mode 3"); + EncodingMode::Mode3 + }, + } + } + }; + sprite_bytes.next_bit(); + + println!("Starting to decompress the second buffer!"); + let initial_packet: u8 = sprite_bytes.read_bits(1, false); // Read next 1 bit + buffer.decompress_to_bitplane(&mut sprite_bytes, initial_packet, primary_buffer == 1, false); + println!("{:02X?}", buffer.bytes); + + // In mode 1 and 3, we have to delta-decode the buffer C + // In any mode, we have to delta-decode the buffer B + // In mode 2 and 3, xor buffer C against buffer B + + match encoding_mode { + EncodingMode::Mode1 => { + buffer.delta_decode(2); + buffer.delta_decode(1); + }, + EncodingMode::Mode2 => { + buffer.delta_decode(1); + buffer.xor_buffers(1, 2); + }, + EncodingMode::Mode3 => { + buffer.delta_decode(2); + buffer.xor_buffers(1, 2); + }, + } + println!("Encoding result:"); + println!("{:02X?}", buffer.bytes); + + // Now we need to copy the content from buffer B to A and from C to B, + // but in the right order for the Gameboy to draw + buffer.copy_bitplane(1, 0); + buffer.copy_bitplane(2, 1); + println!("Bitplane copy result:"); + println!("{:02X?}", buffer.bytes); + + // Almost there! + // Now we need to zipper the buffer A and B into buffer C and B going backwards + buffer.zip_buffers(); + println!("Resulting zip:"); + println!("{:02X?}", buffer.bytes); + + // And we can finally start rendering our sprite!!! + buffer.render(); + +}