Skip to content

Instantly share code, notes, and snippets.

@yupferris
Created February 23, 2017 21:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yupferris/308ae85241f40ccca680d50e79f08e34 to your computer and use it in GitHub Desktop.
Save yupferris/308ae85241f40ccca680d50e79f08e34 to your computer and use it in GitHub Desktop.
You know the drill
use std::io::{self, Read};
use std::fs::File;
use std::path::Path;
fn main() {
let rom_file_name = "C:\\msys64\\home\\ferris\\dev\\projects\\vb-rs-corpus\\Mario's Tennis (Japan, USA).vb";
let buf = load(rom_file_name).unwrap();
let src = 0xfff9fb4c;
let dst = 0x00078000;
decompress(src, dst, &buf);
println!("Hello, world!");
}
pub fn load<P: AsRef<Path>>(file_name: P) -> io::Result<Box<[u8]>> {
let mut file = File::open(file_name)?;
let mut vec = Vec::new();
file.read_to_end(&mut vec)?;
Ok(vec.into_boxed_slice())
}
fn read_byte(addr: u32, buf: &[u8]) -> u8 {
println!("Read 0x{:08x}", addr);
let offset = (((addr & 0x07ffffff) - 0x07000000) as usize) & (buf.len() - 1);
buf[offset]
}
fn write_byte(addr: u32, value: u8) {
// TODO: Backreferences won't work unless we actually write shit lol
//println!("Write 0x{:08x}: 0x{:02x}", addr, value);
}
fn decompress(mut src: u32, mut dst: u32, buf: &[u8]) {
// Skip first 2 bytes (they're always zero)
src += 2;
let original_dst = dst;
// Load bytes_left
let mut bytes_left = ((read_byte(src, buf) as u32) << 8) + 1;
src += 1;
bytes_left += read_byte(src, buf) as u32;
src += 1;
println!("Bytes left: 0x{:08x}", bytes_left);
loop {
// Load packet_flags
let mut packet_flags = read_byte(src, buf);
src += 1;
// Iterate over control bits (lsb -> msb)
for _ in 0..8 {
// Load data_byte
let data_byte = read_byte(src, buf);
src += 1;
// Load packet_flag from packet_flags lsb
let packet_flag = packet_flags & 0x1;
if packet_flag != 0 {
// Literal byte; output data_byte directly
write_byte(dst, data_byte);
dst += 1;
bytes_left = bytes_left.wrapping_sub(1);
} else {
// Backreference; data_byte and next byte encode distance+length
let run_distance = ((read_byte(src, buf) as u32) << 4) | ((data_byte as u32) >> 4);
src += 1;
let mut run_length = ((data_byte as u32) & 0xf) + 3;
bytes_left = bytes_left.wrapping_sub(run_length);
// run_length may have been greater than bytes_left; this is some weird logic to correct that :)
// (it totally works; don't think too much about it.. I could make this more clear but I want
// it to match the original as much as possible)
if bytes_left >= 0x80000000 { // < 0
run_length = run_length.wrapping_add(bytes_left);
}
let mut run_source = dst - run_distance;
if run_source < original_dst {
let num_zeroes = original_dst - run_source;
for _ in 0..num_zeroes {
// Output zero and inc dst pointer
write_byte(dst, 0);
dst += 1;
}
run_length -= num_zeroes;
run_source = original_dst;
}
if run_length > 0 {
for _ in 0..run_length {
// Output byte at [run_source] and inc run_source and dst pointer
write_byte(dst, read_byte(run_source, buf));
run_source += 1;
dst += 1;
}
}
}
// Shift out packet flag from this iteration
packet_flags >>= 1;
if bytes_left >= 0x80000000 { // < 0
break;
}
}
if bytes_left >= 0x80000000 { // < 0
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment