Created
February 23, 2017 21:08
-
-
Save yupferris/308ae85241f40ccca680d50e79f08e34 to your computer and use it in GitHub Desktop.
You know the drill
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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