lzxpress/
lznt1.rs

1use std::mem;
2
3pub use crate::error::Error;
4
5const LZNT1_COMPRESSED_FLAG: usize = 0x8000;
6
7macro_rules! load16le{
8    ($dst:expr,$src:expr,$idx:expr)=>{
9        {
10            $dst = (u32::from($src[$idx + 1]) << 8
11            | u32::from($src[$idx])) as usize;
12        }
13    }
14}
15
16pub fn decompress(
17    in_buf: &[u8]
18) -> Result<Vec<u8>, Error>
19{
20    let mut out_buf: Vec<u8> = Vec::with_capacity(in_buf.len());
21    
22    match decompress2(in_buf, &mut out_buf) {
23        Err(e) => println!("{:?}", e),
24        _ => ()
25    }
26
27    Ok(out_buf)
28}
29
30pub fn decompress2(
31    in_buf: &[u8],
32    out_buf: &mut Vec<u8>
33) -> Result<(), Error>
34{
35    let mut out_idx: usize = 0;
36    let mut in_idx:  usize = 0;
37
38    let mut header:     usize;
39    let mut length:     usize;
40    let mut block_len:  usize;
41    let mut offset:     usize;
42
43    let mut _block_id = 0;
44    while in_idx < in_buf.len() {
45        let in_chunk_base = in_idx;
46        load16le!(header, in_buf, in_idx);
47        in_idx += mem::size_of::<u16>();
48        block_len = (header & 0xfff) + 1;
49        if block_len > (in_buf.len() - in_idx) {
50            return Err(Error::MemLimit);
51        } else {
52            if header & LZNT1_COMPRESSED_FLAG != 0 {
53                let in_base_idx = in_idx;
54                let out_base_idx = out_idx;
55                while (in_idx - in_base_idx) < block_len {
56                    if in_idx >= in_buf.len() {
57                        break;
58                    }
59                    let flags = in_buf[in_idx];
60                    in_idx += mem::size_of::<u8>();
61
62                    for n in 0..8 {
63                        if ((flags >> n) & 1) == 0 {
64                            if in_idx >= in_buf.len() || (in_idx - in_base_idx) >= block_len {
65                                break;
66                            }
67                            out_buf.push(in_buf[in_idx]);
68                            out_idx += mem::size_of::<u8>();
69                            in_idx += mem::size_of::<u8>();
70                        } else {
71                            let flag;
72                            if in_idx >= in_buf.len() || (in_idx - in_base_idx) >= block_len {
73                                break;
74                            }
75                            load16le!(flag, in_buf, in_idx);
76                            in_idx += mem::size_of::<u16>();
77
78                            let mut pos = out_idx - out_base_idx - 1;
79                            let mut l_mask = 0xFFF;
80                            let mut o_shift = 12;
81                            while pos >= 0x10 {
82                                l_mask >>= 1;
83                                o_shift -= 1;
84                                pos >>= 1;
85                            }
86
87                            length = (flag & l_mask) + 3;
88                            offset = (flag >> o_shift) + 1;
89                            if length >= offset {
90                                let count = (0xfff / offset) + 1;
91                                if offset > out_idx {
92                                    return Err(Error::CorruptedData);
93                                }
94
95                                let chunk_pos = out_idx - offset;
96                                let chunk_len = offset;
97
98                                let mut x = 0;
99                                while x < length {
100                                    for _i in 0..count {
101                                        for _j in 0..chunk_len {
102                                            out_buf.push(out_buf[chunk_pos + _j]);
103                                            out_idx += mem::size_of::<u8>();
104                                            x += 1;
105                                            if x >= length {
106                                                break;
107                                            }
108                                        }
109
110                                        if x >= length {
111                                            break;
112                                        }
113                                    }
114                                }
115                            } else {
116                                for _i in 0..length {
117                                    if offset > out_idx {
118                                        return Err(Error::CorruptedData);
119                                    }
120                                    out_buf.push(out_buf[out_idx - offset]);
121                                    out_idx += mem::size_of::<u8>();
122                                }
123                            }
124                        }
125                    }
126                }
127            } else {
128                // Not compressed
129                for _i in 0..block_len {
130                    out_buf.push(in_buf[in_idx]);
131                    out_idx += mem::size_of::<u8>();
132                    in_idx += mem::size_of::<u8>();
133                }
134            }
135        }
136
137        in_idx = in_chunk_base + 2 + block_len;
138        _block_id += 1;
139    }
140
141    Ok(())
142}