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 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}