mod deep;
mod heavy;
mod medium;
mod quick;
mod rle;
mod tables;
use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::header::{Mode, TrackFlags};
const WINDOW_SIZE: usize = 0x4000;
const WINDOW_CLEAR: usize = 0x3fc8;
const QUICK_INIT_POS: u16 = 251; const MEDIUM_INIT_POS: u16 = 0x3fbe; const DEEP_INIT_POS: u16 = 0x3fc4;
const HEAVY_NC: usize = 510;
const HEAVY_NPT: usize = 20;
const DEEP_N_CHAR: usize = 256 - 2 + 60;
const DEEP_T: usize = DEEP_N_CHAR * 2 - 1;
#[derive(Debug)]
pub struct Corrupt;
pub struct Decompressor {
window: Box<[u8; WINDOW_SIZE]>,
quick_pos: u16,
medium_pos: u16,
deep_pos: u16,
heavy_pos: u16,
heavy_last_match_len: u16,
c_len: [u8; HEAVY_NC],
pt_len: [u8; HEAVY_NPT],
c_table: Box<[u16; 4096]>,
pt_table: [u16; 256],
left: Box<[u16; 2 * HEAVY_NC - 1]>,
right: Box<[u16; 2 * HEAVY_NC - 1 + 9]>,
deep_freq: Box<[u16; DEEP_T + 1]>,
deep_prnt: Box<[u16; DEEP_T + DEEP_N_CHAR]>,
deep_son: Box<[u16; DEEP_T]>,
deep_init: bool,
scratch: Vec<u8>,
}
impl Decompressor {
pub fn new() -> Self {
let mut decompressor = Self {
window: Box::new([0u8; WINDOW_SIZE]),
quick_pos: 0,
medium_pos: 0,
deep_pos: 0,
heavy_pos: 0,
heavy_last_match_len: 0,
c_len: [0; HEAVY_NC],
pt_len: [0; HEAVY_NPT],
c_table: Box::new([0; 4096]),
pt_table: [0; 256],
left: Box::new([0; 2 * HEAVY_NC - 1]),
right: Box::new([0; 2 * HEAVY_NC - 1 + 9]),
deep_freq: Box::new([0; DEEP_T + 1]),
deep_prnt: Box::new([0; DEEP_T + DEEP_N_CHAR]),
deep_son: Box::new([0; DEEP_T]),
deep_init: true,
scratch: Vec::new(),
};
decompressor.reset();
decompressor
}
pub fn reset(&mut self) {
self.quick_pos = QUICK_INIT_POS;
self.medium_pos = MEDIUM_INIT_POS;
self.deep_pos = DEEP_INIT_POS;
self.heavy_pos = 0;
self.heavy_last_match_len = 0;
self.deep_init = true;
self.window[..WINDOW_CLEAR].fill(0);
}
pub fn unpack_track(
&mut self,
mode: Mode,
flags: TrackFlags,
packed: &[u8],
intermediate_len: usize,
out: &mut [u8],
) -> Result<(), Corrupt> {
match mode {
Mode::None => {
let src = packed.get(..out.len()).ok_or(Corrupt)?;
out.copy_from_slice(src);
Ok(())
}
Mode::Simple => rle::unpack_rle(packed, out),
Mode::Quick => self.with_scratch(intermediate_len, |me, stage1| {
me.unpack_quick(packed, stage1)?;
rle::unpack_rle(stage1, out)
}),
Mode::Medium => self.with_scratch(intermediate_len, |me, stage1| {
me.unpack_medium(packed, stage1)?;
rle::unpack_rle(stage1, out)
}),
Mode::Deep => self.with_scratch(intermediate_len, |me, stage1| {
me.unpack_deep(packed, stage1)?;
rle::unpack_rle(stage1, out)
}),
Mode::Heavy1 | Mode::Heavy2 => self.with_scratch(intermediate_len, |me, stage1| {
me.unpack_heavy(
mode == Mode::Heavy2,
flags.heavy_rebuild_trees(),
packed,
stage1,
)?;
if flags.heavy_rle() {
rle::unpack_rle(stage1, out)
} else {
let src = stage1.get(..out.len()).ok_or(Corrupt)?;
out.copy_from_slice(src);
Ok(())
}
}),
}
}
fn with_scratch<R>(&mut self, len: usize, decode: impl FnOnce(&mut Self, &mut [u8]) -> R) -> R {
let mut scratch = core::mem::take(&mut self.scratch);
scratch.clear();
scratch.resize(len, 0);
let result = decode(self, &mut scratch);
self.scratch = scratch;
result
}
}
fn push_window(window: &mut [u8], pos: &mut u16, mask: u16, byte: u8) {
window[(*pos & mask) as usize] = byte;
*pos = pos.wrapping_add(1);
}
fn copy_match(
window: &mut [u8],
pos: &mut u16,
mask: u16,
distance: u16,
length: u16,
out: &mut [u8],
out_pos: &mut usize,
) -> Result<(), Corrupt> {
let mut src = pos.wrapping_sub(distance).wrapping_sub(1);
for _ in 0..length {
let byte = window[(src & mask) as usize];
push_window(window, pos, mask, byte);
src = src.wrapping_add(1);
*out.get_mut(*out_pos).ok_or(Corrupt)? = byte;
*out_pos += 1;
}
Ok(())
}