Skip to main content

sit_algos/
adcr_v3.rs

1use std::io;
2
3use binrw::BinReaderExt;
4use bitstream_io::{BitRead, BitReader, LE};
5use cfor::cfor;
6
7use super::installer::hufftree::HuffTree;
8
9#[derive(Debug, thiserror::Error)]
10pub enum Error {
11    #[error(transparent)]
12    BinRw(#[from] binrw::Error),
13
14    #[error(transparent)]
15    Io(#[from] io::Error),
16
17    #[error(transparent)]
18    Installer(#[from] super::installer::Error),
19
20    #[error("Invalid Reference")]
21    DictionaryOutOfBounds,
22
23    #[error("Too Much Ouptut")]
24    TooMuchOutput,
25}
26
27const fn init_lengths<const N: usize>() -> ([u16; N], [u8; N]) {
28    let mut values = [0u16; N];
29    let mut paths = [0u8; N];
30
31    let mut k = 0;
32    cfor! {let mut i = 0; i < N; i+=1; {
33        let value = (i.saturating_sub(4) >> 2) as u8;
34
35        values[i] = k;
36        paths[i] = value;
37
38        k = k.wrapping_add(1 << (value & 0x3f));
39    }}
40
41    (values, paths)
42}
43
44/// This initialization differs from the mainline installer compression offset by only subbing 1 and shiftin 1
45const fn init_offsets<const N: usize>() -> ([u16; N], [u8; N]) {
46    let mut paths = [0u16; N];
47    let mut values = [0u8; N];
48
49    let mut k = 1;
50    cfor! {let mut i = 0; i < N; i+=1; {
51        let value = (i.saturating_sub(1) >> 1) as u8;
52
53        paths[i] = k;
54        values[i] = value;
55
56        k = k.wrapping_add(1 << (value & 0x3f));
57    }};
58
59    (paths, values)
60}
61
62pub fn decompress(
63    mut reader: impl io::Read + io::Seek,
64    uncompressed_size: usize,
65    dict: &[u8],
66) -> Result<Vec<u8>, Error> {
67    let (offsets, offsets_bits) = init_offsets::<0x1f>();
68    let (lengths, length_bits) = init_lengths::<0x24>();
69
70    let dynamic_code_size: u16 = reader.read_be::<u8>()? as u16 * 2 - 1;
71    assert!(dynamic_code_size <= 0x1f);
72    let mut reader = BitReader::<_, LE>::new(&mut reader);
73    let fixed = HuffTree::read_from(&mut reader, 0x124)?;
74    let dynamic = HuffTree::read_from(&mut reader, dynamic_code_size as usize)?;
75
76    let mut output = vec![0u8; uncompressed_size];
77    let mut output_ptr = 0;
78    while output_ptr < output.len() {
79        let mut sym = fixed.read_code(0, &mut reader)? as usize;
80        if sym < 0x100 {
81            // Emit literal
82            output[output_ptr] = (sym & 0xFF) as u8;
83            output_ptr += 1;
84            continue;
85        }
86        sym -= 0x100;
87
88        let mut length = lengths[sym] as usize + 3;
89        let bits = length_bits[sym] as u32;
90        length += reader.read_var::<u32>(bits)? as usize;
91
92        let sym = dynamic.read_code(0, &mut reader)? as usize;
93        let mut offset = offsets[sym] as usize;
94        let bits = offsets_bits[sym] as u32;
95        offset += reader.read_var::<u32>(bits)? as usize;
96
97        if output_ptr + length > output.len() {
98            return Err(Error::TooMuchOutput);
99        }
100
101        if offset <= output_ptr {
102            // Copy from output
103            for _ in 0..length {
104                output[output_ptr] = output[output_ptr - offset];
105                output_ptr += 1;
106            }
107        } else {
108            // Copy from dictionary
109            if dict.len() <= (offset - output_ptr) {
110                // TODO: Check if it is possible to copy parts from the dict and parts from output
111                return Err(Error::DictionaryOutOfBounds);
112            }
113
114            let dictionary_offset = dict.len() - (offset - output_ptr);
115            output[output_ptr..(output_ptr + length)]
116                .copy_from_slice(&dict[dictionary_offset..(dictionary_offset + length)]);
117            output_ptr += length;
118        }
119    }
120
121    Ok(output)
122}