1use anyhow::{anyhow, bail, Result};
2
3#[inline]
7fn parse_header(src: &[u8]) -> Result<(usize, usize)> {
8 if src.len() < 8 {
9 bail!("lzss: input too short");
10 }
11 let arc_size = u32::from_le_bytes([src[0], src[1], src[2], src[3]]) as usize;
12 let org_size = u32::from_le_bytes([src[4], src[5], src[6], src[7]]) as usize;
13 Ok((arc_size, org_size))
14}
15
16pub fn lzss_decompressed_size(src: &[u8]) -> Result<usize> {
18 let (_, org_size) = parse_header(src)?;
19 Ok(org_size)
20}
21
22pub fn lzss_unpack(src: &[u8]) -> Result<Vec<u8>> {
25 let (arc_size, org_size) = parse_header(src)?;
26 if org_size == 0 {
27 bail!("lzss: org_size=0");
28 }
29
30 let payload_start = 8usize;
31 let payload_end = payload_start
32 .checked_add(arc_size)
33 .ok_or_else(|| anyhow!("lzss: arc_size overflow"))?;
34 if payload_end > src.len() {
35 bail!(
36 "lzss: arc_size out of bounds (end={}, len={})",
37 payload_end,
38 src.len()
39 );
40 }
41
42 let mut pos = payload_start;
43 let mut out: Vec<u8> = Vec::with_capacity(org_size);
44
45 while out.len() < org_size {
46 if pos >= payload_end {
47 break;
48 }
49 let mut flags = src[pos];
50 pos += 1;
51
52 for _ in 0..8 {
53 if out.len() >= org_size {
54 break;
55 }
56 if pos >= payload_end {
57 break;
58 }
59
60 if (flags & 1) != 0 {
61 out.push(src[pos]);
63 pos += 1;
64 } else {
65 if pos + 2 > payload_end {
66 bail!("lzss: truncated backref token");
67 }
68 let token = u16::from_le_bytes([src[pos], src[pos + 1]]);
69 pos += 2;
70
71 let offset = (token >> 4) as usize;
72 let len = ((token & 0x0F) as usize) + 2;
74
75 if offset == 0 {
76 bail!("lzss: invalid backref offset=0");
77 }
78 if offset > out.len() {
79 bail!(
80 "lzss: backref offset out of range (offset={}, out_len={})",
81 offset,
82 out.len()
83 );
84 }
85
86 let mut src_idx = out.len() - offset;
87 for _ in 0..len {
88 if out.len() >= org_size {
89 break;
90 }
91 let b = out[src_idx];
92 out.push(b);
93 src_idx += 1;
94 }
95 }
96
97 flags >>= 1;
98 }
99 }
100
101 if out.len() != org_size {
102 bail!(
103 "lzss: size mismatch (got={}, expected={})",
104 out.len(),
105 org_size
106 );
107 }
108
109 Ok(out)
110}
111
112pub fn lzss_unpack_lenient(src: &[u8]) -> Result<Vec<u8>> {
121 let (arc_size, org_size) = parse_header(src)?;
122 if org_size == 0 {
123 bail!("lzss(lenient): org_size=0");
124 }
125
126 let payload_start = 8usize;
127 let payload_end = payload_start
128 .checked_add(arc_size)
129 .ok_or_else(|| anyhow!("lzss(lenient): arc_size overflow"))?
130 .min(src.len());
131
132 let mut pos = payload_start;
133 let mut out: Vec<u8> = Vec::with_capacity(org_size);
134
135 while out.len() < org_size {
136 if pos >= payload_end {
137 break;
138 }
139 let mut flags = src[pos];
140 pos += 1;
141
142 for _ in 0..8 {
143 if out.len() >= org_size {
144 break;
145 }
146 if pos >= payload_end {
147 break;
148 }
149
150 if (flags & 1) != 0 {
151 out.push(src[pos]);
152 pos += 1;
153 } else {
154 if pos + 2 > payload_end {
155 break;
156 }
157 let token = u16::from_le_bytes([src[pos], src[pos + 1]]);
158 pos += 2;
159
160 let offset = (token >> 4) as usize;
161 let len = ((token & 0x0F) as usize) + 2;
162
163 if offset == 0 || offset > out.len() {
164 break;
166 }
167
168 let mut src_idx = out.len() - offset;
169 for _ in 0..len {
170 if out.len() >= org_size {
171 break;
172 }
173 let b = out[src_idx];
174 out.push(b);
175 src_idx += 1;
176 }
177 }
178
179 flags >>= 1;
180 }
181 }
182
183 if out.len() != org_size {
184 bail!(
185 "lzss(lenient): size mismatch (got={}, expected={})",
186 out.len(),
187 org_size
188 );
189 }
190
191 Ok(out)
192}
193
194pub fn lzss_unpack32(src: &[u8]) -> Result<Vec<u8>> {
198 let (arc_size, org_size) = parse_header(src)?;
199 if org_size == 0 {
200 bail!("lzss32: org_size=0");
201 }
202 if org_size % 4 != 0 {
203 bail!("lzss32: org_size not multiple of 4 (org_size={})", org_size);
204 }
205
206 let payload_start = 8usize;
207 let payload_end = payload_start
208 .checked_add(arc_size)
209 .ok_or_else(|| anyhow!("lzss32: arc_size overflow"))?;
210 if payload_end > src.len() {
211 bail!(
212 "lzss32: arc_size out of bounds (end={}, len={})",
213 payload_end,
214 src.len()
215 );
216 }
217
218 let mut pos = payload_start;
219 let mut out: Vec<u8> = Vec::with_capacity(org_size);
220
221 while out.len() < org_size {
222 if pos >= payload_end {
223 break;
224 }
225 let mut flags = src[pos];
226 pos += 1;
227
228 for _ in 0..8 {
229 if out.len() >= org_size {
230 break;
231 }
232
233 if (flags & 1) != 0 {
234 if pos + 3 > payload_end {
236 bail!("lzss32: truncated literal");
237 }
238 out.push(src[pos]);
239 out.push(src[pos + 1]);
240 out.push(src[pos + 2]);
241 out.push(255);
242 pos += 3;
243 } else {
244 if pos + 2 > payload_end {
246 bail!("lzss32: truncated backref token");
247 }
248 let token = u16::from_le_bytes([src[pos], src[pos + 1]]);
249 pos += 2;
250
251 let offset_dwords = (token >> 4) as usize;
252 let len_dwords = ((token & 0x0F) as usize) + 1;
253
254 if offset_dwords == 0 {
255 bail!("lzss32: invalid backref offset=0");
256 }
257
258 let offset_bytes = offset_dwords
259 .checked_mul(4)
260 .ok_or_else(|| anyhow!("lzss32: offset overflow"))?;
261
262 if offset_bytes > out.len() {
263 bail!(
264 "lzss32: backref offset out of range (offset_bytes={}, out_len={})",
265 offset_bytes,
266 out.len()
267 );
268 }
269
270 let mut src_idx = out.len() - offset_bytes;
271 for _ in 0..len_dwords {
272 if out.len() + 4 > org_size {
273 break;
274 }
275 let d0 = out[src_idx];
277 let d1 = out[src_idx + 1];
278 let d2 = out[src_idx + 2];
279 let d3 = out[src_idx + 3];
280 out.push(d0);
281 out.push(d1);
282 out.push(d2);
283 out.push(d3);
284 src_idx += 4;
285 }
286 }
287
288 flags >>= 1;
289 if pos > payload_end {
290 break;
291 }
292 }
293 }
294
295 if out.len() != org_size {
296 bail!(
297 "lzss32: size mismatch (got={}, expected={})",
298 out.len(),
299 org_size
300 );
301 }
302
303 Ok(out)
304}