Author Topic: LZSS decompression... am I doing it right?  (Read 106 times)

Schala Zeal

  • Radical Dreamer (+2000)
  • *
  • Posts: 2047
  • 7th Elemental Innate
    • View Profile
LZSS decompression... am I doing it right?
« on: September 21, 2017, 07:18:56 pm »
So I recently got into a programming language called Rust which is a systems language that's supposed to strongly prevent any possibility of memory leaks, data races, etc. Out of just randomness, I followed the wiki explanation of Chrono Cross's LZSS algorithm, and used the '2540.out' file from the CD to test this on.

I compared it to decompression via Purple Cat Tools and the decompressed files don't match. The decompressed file mine outputs is 4765 bytes  as opposed to PCT's 2160 bytes, where the decompressed size info in the file header says 2048. The contents of my file are mostly null (0 value) bytes with some data here and there while PCT outputs a file filled with data.

I noticed PCT also bitswaps in 512-byte chunks (512 * 8 = 4096, so I'm guessing the data following header is the initial buffer content?), which wasn't mentioned on the wiki, so I did the same, with... not much luck. Below is the entire code for the decode function + the test. It should be similar to C/C++.

Code: [Select]
extern crate bitstream_io;
extern crate bit_reverse;

use bitstream_io::{BitReader,LE};
use bit_reverse::ParallelReverse;
use std::io::Cursor;

pub const BUFFER_SIZE: usize = 4096;
pub const LIT_SIZE: u32 = 8;
pub const PTR_SIZE: u32 = 12;
pub const VAL_SIZE: usize = 4;
pub const VAL_ADD: isize = 2;

pub fn decode(data: &[u8]) -> Vec<u8> {
let mut ic = Cursor::new(&data);
let mut br = BitReader::<LE>::new(&mut ic);

let mut header: [u8; 4] = [0; 4];
let _ = br.read_bytes(&mut header);
assert_eq!(&header, b"sszl");

let dsize = br.read::<u32>(32).unwrap_or(0) as usize;
assert!(dsize != 0);

let unknown = br.read::<u32>(32).unwrap_or(0);

        // bitswap every byte past the header
let mut rdata = vec![0u8; data.len()-12];
rdata.copy_from_slice(&data[12..]);
for i in rdata.iter_mut() {
*i = i.swap_bits();
}
let mut ic = Cursor::new(&rdata);
let mut br = BitReader::<LE>::new(&mut ic);

let mut outbuf = vec![0u8; dsize];
let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut idx: usize = 0;
while let Ok(is_val) = br.read_bit() { // TL;DR: while not end of the data
if is_val == true {
buf[idx % BUFFER_SIZE] = br.read::<u8>(LIT_SIZE).unwrap();
idx += 1;
} else {
if let Ok(offs) = br.read::<u16>(PTR_SIZE) { // compiler tends to choke without the if-let guard on this part
if let Ok(size) = br.read::<u8>(VAL_SIZE as u32) {
let sz = (size as isize) + VAL_ADD;
for i in 0..sz {
outbuf.push(buf[(offs as usize)+(i as usize)]);
}
}
}
}
}

outbuf
}

#[test]
fn dec_test() {
use std::fs::File;
use std::io::prelude::*;

let test_data = include_bytes!("2540.out");
let mut f = File::create("test.dat").unwrap();
let ucmp = decode(&test_data[..]);
f.write_all(&ucmp).unwrap();
}