nx_lzma_lib.nx source
↩ module page · 509 lines · 22498 B
1// nx_lzma_lib.nx -- THE LZMA AND LZMA2 DECODER (/compare/modding MD29, 2026-09-06), written from the mirrored specification
2// (knowledge/fetched/cmp_modding_lzma-specification.txt, Igor Pavlov 2015-06-14, pin 6a4d441c) and the LZMA2 chunk rules of
3// the public-domain SDK decoder (cmp_modding_Lzma2Dec.c, pin 41303267), both READ before a line of this was written.
4// It is the decode-to-one-buffer variant the specification names: the OUTPUT BUFFER IS THE SLIDING WINDOW, so a match at
5// distance d reads out[pos - d] and a dictionary reset simply moves the base the distance check measures from. The
6// probability counters are i64 slots (11-bit values), the range coder keeps Range and Code as 32-bit values inside i64
7// (masked after every shift, exactly as the specification's note on wider integers requires), and every corruption the
8// specification lets the reference decoder IGNORE is likewise ignored here so the output matches the reference byte for byte;
9// the conditions the specification calls errors (distance past the window or dictionary, a literal past the unpack size,
10// a match longer than what remains, a non-zero first byte, a marker with a non-zero code) are REFUSALS, named by code.
11// LZMA2 (the 7z and xz container coder): a control byte per chunk -- 0 ends the stream; 1 and 2 copy an uncompressed chunk
12// (1 resets the dictionary); 0x80 and above carry an LZMA chunk whose low five bits are the unpack size's high bits, then
13// two big-endian bytes each of unpack size minus one and pack size minus one, then a properties byte when bit 6 is set;
14// bits 5 and 6 select the reset: 0 nothing, 1 state, 2 state plus new properties, 3 state plus properties plus dictionary.
15// Every LZMA chunk re-initialises the range coder from its own first five bytes (the SDK sets needFlush on every chunk).
16// license_tier: ORIGINAL No hw writes (Rule 26).
17import "nx_syscalls.nx"
18
19const LZ_NUM_STATES: i64 = 12
20const LZ_POS_BITS_MAX: i64 = 4
21const LZ_NUM_POS_STATES_MAX: i64 = 16
22const LZ_NUM_LEN_TO_POS_STATES: i64 = 4
23const LZ_END_POS_MODEL_INDEX: i64 = 14
24const LZ_NUM_FULL_DISTANCES: i64 = 128
25const LZ_NUM_ALIGN_BITS: i64 = 4
26const LZ_MATCH_MIN_LEN: i64 = 2
27const LZ_PROB_TOTAL: i64 = 2048
28const LZ_PROB_INIT: i64 = 1024
29const LZ_MOVE_DIV: i64 = 32
30const LZ_TOP: i64 = 16777216
31const LZ_MASK32: i64 = 4294967295
32const LZ_DIC_MIN: i64 = 4096
33const LZ_LIT_TABLE: i64 = 768
34const LZ_MARKER: i64 = 4294967295
35const LZ_LCLP_MAX: i64 = 4
36const LZ_PROPS_MAX_BYTE: i64 = 225
37// probability slot map (i64 per counter)
38const LZ_P_ISMATCH: i64 = 0
39const LZ_P_ISREP: i64 = 192
40const LZ_P_ISREPG0: i64 = 204
41const LZ_P_ISREPG1: i64 = 216
42const LZ_P_ISREPG2: i64 = 228
43const LZ_P_ISREP0LONG: i64 = 240
44const LZ_P_POSSLOT: i64 = 432
45const LZ_P_POSDEC: i64 = 688
46const LZ_P_ALIGN: i64 = 803
47const LZ_P_LEN: i64 = 819
48const LZ_P_REPLEN: i64 = 1333
49const LZ_P_LIT: i64 = 1847
50const LZ_LEN_CHOICE: i64 = 0
51const LZ_LEN_CHOICE2: i64 = 1
52const LZ_LEN_LOW: i64 = 2
53const LZ_LEN_MID: i64 = 130
54const LZ_LEN_HIGH: i64 = 258
55const LZ_LEN_WORDS: i64 = 514
56// decoder slots
57const LZ_D_IN: i64 = 0
58const LZ_D_INPOS: i64 = 1
59const LZ_D_INEND: i64 = 2
60const LZ_D_OUT: i64 = 3
61const LZ_D_OUTPOS: i64 = 4
62const LZ_D_OUTEND: i64 = 5
63const LZ_D_BASE: i64 = 6
64const LZ_D_RANGE: i64 = 7
65const LZ_D_CODE: i64 = 8
66const LZ_D_LC: i64 = 9
67const LZ_D_LP: i64 = 10
68const LZ_D_PB: i64 = 11
69const LZ_D_DICT: i64 = 12
70const LZ_D_STATE: i64 = 13
71const LZ_D_REP0: i64 = 14
72const LZ_D_REP1: i64 = 15
73const LZ_D_REP2: i64 = 16
74const LZ_D_REP3: i64 = 17
75const LZ_D_PROBS: i64 = 18
76const LZ_D_NPROBS: i64 = 19
77const LZ_D_CORRUPT: i64 = 20
78const LZ_D_INOVER: i64 = 21
79const LZ_D_CHUNKS: i64 = 22
80const LZ_D_N: i64 = 32
81// results
82const LZ_OK: i64 = 0
83const LZ_ERR_PROPS: i64 = 0 - 1
84const LZ_ERR_FIRST_BYTE: i64 = 0 - 2
85const LZ_ERR_INPUT_SHORT: i64 = 0 - 3
86const LZ_ERR_DISTANCE: i64 = 0 - 4
87const LZ_ERR_OUTPUT_OVER: i64 = 0 - 5
88const LZ_ERR_MARKER_CODE: i64 = 0 - 6
89const LZ_ERR_CONTROL: i64 = 0 - 7
90const LZ_ERR_UNPACK_MISMATCH: i64 = 0 - 8
91const LZ_ERR_WINDOW_EMPTY: i64 = 0 - 9
92
93func lz_err_name(e: i64) -> *u8 {
94 if e == LZ_ERR_PROPS { return "lzma-properties-out-of-range" as *u8 }
95 if e == LZ_ERR_FIRST_BYTE { return "lzma-first-byte-not-zero" as *u8 }
96 if e == LZ_ERR_INPUT_SHORT { return "lzma-input-ran-out" as *u8 }
97 if e == LZ_ERR_DISTANCE { return "lzma-match-distance-past-window-or-dictionary" as *u8 }
98 if e == LZ_ERR_OUTPUT_OVER { return "lzma-output-past-declared-unpack-size" as *u8 }
99 if e == LZ_ERR_MARKER_CODE { return "lzma-end-marker-with-nonzero-code" as *u8 }
100 if e == LZ_ERR_CONTROL { return "lzma2-control-byte-refused" as *u8 }
101 if e == LZ_ERR_UNPACK_MISMATCH { return "lzma2-chunk-unpack-size-not-reached" as *u8 }
102 if e == LZ_ERR_WINDOW_EMPTY { return "lzma-rep-match-on-empty-window" as *u8 }
103 return "unnamed" as *u8
104}
105func lz_pow2(k: i64) -> i64 { var v: i64 = 1; var i: i64 = 0; while i < k { v = v * 2; i = i + 1 } return v }
106func lz_probs_count(lc: i64, lp: i64) -> i64 { return LZ_P_LIT + LZ_LIT_TABLE * lz_pow2(lc + lp) }
107// ---- input ----
108func lz_in(d: *i64) -> i64 {
109 if d[LZ_D_INPOS] >= d[LZ_D_INEND] { d[LZ_D_INOVER] = 1; return 0 }
110 let b: *u8 = d[LZ_D_IN] as *u8
111 let v: i64 = (b[d[LZ_D_INPOS]] & 0xff) as i64
112 d[LZ_D_INPOS] = d[LZ_D_INPOS] + 1
113 return v
114}
115// ---- range decoder ----
116func lz_rc_init(d: *i64) -> i64 {
117 let b: i64 = lz_in(d)
118 var code: i64 = 0
119 var i: i64 = 0
120 while i < 4 { code = ((code * 256) | lz_in(d)) & LZ_MASK32; i = i + 1 }
121 d[LZ_D_RANGE] = LZ_MASK32
122 d[LZ_D_CODE] = code
123 if b != 0 { d[LZ_D_CORRUPT] = 1; return 0 }
124 if code == LZ_MASK32 { d[LZ_D_CORRUPT] = 1 }
125 return 1
126}
127func lz_rc_norm(d: *i64) -> i64 {
128 if d[LZ_D_RANGE] < LZ_TOP {
129 d[LZ_D_RANGE] = (d[LZ_D_RANGE] * 256) & LZ_MASK32
130 d[LZ_D_CODE] = ((d[LZ_D_CODE] * 256) | lz_in(d)) & LZ_MASK32
131 }
132 return 0
133}
134func lz_bit(d: *i64, pi: i64) -> i64 {
135 let probs: *i64 = d[LZ_D_PROBS] as *i64
136 var v: i64 = probs[pi]
137 let bound: i64 = (d[LZ_D_RANGE] / LZ_PROB_TOTAL) * v
138 var sym: i64 = 0
139 if d[LZ_D_CODE] < bound {
140 v = v + (LZ_PROB_TOTAL - v) / LZ_MOVE_DIV
141 d[LZ_D_RANGE] = bound
142 sym = 0
143 } else {
144 v = v - v / LZ_MOVE_DIV
145 d[LZ_D_CODE] = d[LZ_D_CODE] - bound
146 d[LZ_D_RANGE] = d[LZ_D_RANGE] - bound
147 sym = 1
148 }
149 probs[pi] = v
150 lz_rc_norm(d)
151 return sym
152}
153func lz_direct(d: *i64, nbits: i64) -> i64 {
154 var res: i64 = 0
155 var i: i64 = 0
156 while i < nbits {
157 d[LZ_D_RANGE] = d[LZ_D_RANGE] / 2
158 let c: i64 = d[LZ_D_CODE] - d[LZ_D_RANGE]
159 var bit: i64 = 0
160 if c < 0 { bit = 0 } else { d[LZ_D_CODE] = c; bit = 1 }
161 if d[LZ_D_CODE] == d[LZ_D_RANGE] { d[LZ_D_CORRUPT] = 1 }
162 lz_rc_norm(d)
163 res = res * 2 + bit
164 i = i + 1
165 }
166 return res
167}
168func lz_tree(d: *i64, base: i64, nbits: i64) -> i64 {
169 var m: i64 = 1
170 var i: i64 = 0
171 while i < nbits { m = m * 2 + lz_bit(d, base + m); i = i + 1 }
172 return m - lz_pow2(nbits)
173}
174func lz_tree_rev(d: *i64, base: i64, nbits: i64) -> i64 {
175 var m: i64 = 1
176 var sym: i64 = 0
177 var i: i64 = 0
178 while i < nbits {
179 let bit: i64 = lz_bit(d, base + m)
180 m = m * 2 + bit
181 sym = sym + bit * lz_pow2(i)
182 i = i + 1
183 }
184 return sym
185}
186func lz_len(d: *i64, base: i64, pos_state: i64) -> i64 {
187 if lz_bit(d, base + LZ_LEN_CHOICE) == 0 { return lz_tree(d, base + LZ_LEN_LOW + pos_state * 8, 3) }
188 if lz_bit(d, base + LZ_LEN_CHOICE2) == 0 { return 8 + lz_tree(d, base + LZ_LEN_MID + pos_state * 8, 3) }
189 return 16 + lz_tree(d, base + LZ_LEN_HIGH, 8)
190}
191func lz_dist(d: *i64, len: i64) -> i64 {
192 var len_state: i64 = len
193 if len_state > LZ_NUM_LEN_TO_POS_STATES - 1 { len_state = LZ_NUM_LEN_TO_POS_STATES - 1 }
194 let pos_slot: i64 = lz_tree(d, LZ_P_POSSLOT + len_state * 64, 6)
195 if pos_slot < 4 { return pos_slot }
196 let nd: i64 = pos_slot / 2 - 1
197 var dist: i64 = (2 | (pos_slot & 1)) * lz_pow2(nd)
198 if pos_slot < LZ_END_POS_MODEL_INDEX {
199 dist = dist + lz_tree_rev(d, LZ_P_POSDEC + dist - pos_slot, nd)
200 } else {
201 dist = dist + lz_direct(d, nd - LZ_NUM_ALIGN_BITS) * lz_pow2(LZ_NUM_ALIGN_BITS)
202 dist = dist + lz_tree_rev(d, LZ_P_ALIGN, LZ_NUM_ALIGN_BITS)
203 }
204 return dist
205}
206func lz_state_lit(s: i64) -> i64 { if s < 4 { return 0 } if s < 10 { return s - 3 } return s - 6 }
207func lz_state_match(s: i64) -> i64 { if s < 7 { return 7 } return 10 }
208func lz_state_rep(s: i64) -> i64 { if s < 7 { return 8 } return 11 }
209func lz_state_shortrep(s: i64) -> i64 { if s < 7 { return 9 } return 11 }
210// ---- state ----
211func lz_init_probs(d: *i64) -> i64 {
212 let probs: *i64 = d[LZ_D_PROBS] as *i64
213 let n: i64 = lz_probs_count(d[LZ_D_LC], d[LZ_D_LP])
214 var i: i64 = 0
215 while i < n { probs[i] = LZ_PROB_INIT; i = i + 1 }
216 return n
217}
218func lz_reset_state(d: *i64) -> i64 {
219 lz_init_probs(d)
220 d[LZ_D_STATE] = 0
221 d[LZ_D_REP0] = 0; d[LZ_D_REP1] = 0; d[LZ_D_REP2] = 0; d[LZ_D_REP3] = 0
222 return 0
223}
224// properties byte (pb * 5 + lp) * 9 + lc, refused when out of range; lclp_max bounds lc + lp (4 for LZMA2, 12 for LZMA)
225func lz_set_props_byte(d: *i64, pbyte: i64, lclp_max: i64) -> i64 {
226 if pbyte >= LZ_PROPS_MAX_BYTE { return LZ_ERR_PROPS }
227 let lc: i64 = pbyte % 9
228 let r: i64 = pbyte / 9
229 let lp: i64 = r % 5
230 let pb: i64 = r / 5
231 if lc + lp > lclp_max { return LZ_ERR_PROPS }
232 d[LZ_D_LC] = lc; d[LZ_D_LP] = lp; d[LZ_D_PB] = pb
233 return LZ_OK
234}
235// allocate the decoder for the given properties; the probability arena is sized for lc + lp (never below the LZMA2 bound)
236func lz_new(out: *u8, outcap: i64, lc: i64, lp: i64, pb: i64, dict: i64) -> *i64 {
237 let d: *i64 = sys_mmap(8 * LZ_D_N) as *i64
238 var i: i64 = 0
239 while i < LZ_D_N { d[i] = 0; i = i + 1 }
240 d[LZ_D_OUT] = out as i64
241 d[LZ_D_OUTPOS] = 0
242 d[LZ_D_OUTEND] = outcap
243 d[LZ_D_BASE] = 0
244 d[LZ_D_LC] = lc; d[LZ_D_LP] = lp; d[LZ_D_PB] = pb
245 var dc: i64 = dict
246 if dc < LZ_DIC_MIN { dc = LZ_DIC_MIN }
247 d[LZ_D_DICT] = dc
248 var alloc_lclp: i64 = lc + lp
249 if alloc_lclp < LZ_LCLP_MAX { alloc_lclp = LZ_LCLP_MAX }
250 let n: i64 = LZ_P_LIT + LZ_LIT_TABLE * lz_pow2(alloc_lclp)
251 d[LZ_D_PROBS] = sys_mmap(8 * n) as i64
252 d[LZ_D_NPROBS] = n
253 lz_reset_state(d)
254 return d
255}
256// ---- one LZMA stream over src[..srclen) producing exactly unpack bytes (an end marker is accepted, never required) ----
257func lz_decode(d: *i64, src: *u8, srclen: i64, unpack: i64) -> i64 {
258 d[LZ_D_IN] = src as i64
259 d[LZ_D_INPOS] = 0
260 d[LZ_D_INEND] = srclen
261 d[LZ_D_INOVER] = 0
262 d[LZ_D_CORRUPT] = 0
263 if unpack > d[LZ_D_OUTEND] - d[LZ_D_OUTPOS] { return LZ_ERR_OUTPUT_OVER }
264 if lz_rc_init(d) == 0 { return LZ_ERR_FIRST_BYTE }
265 let out: *u8 = d[LZ_D_OUT] as *u8
266 let lc: i64 = d[LZ_D_LC]
267 let lp_mask: i64 = lz_pow2(d[LZ_D_LP]) - 1
268 let pb_mask: i64 = lz_pow2(d[LZ_D_PB]) - 1
269 var remain: i64 = unpack
270 var state: i64 = d[LZ_D_STATE]
271 var rep0: i64 = d[LZ_D_REP0]
272 var rep1: i64 = d[LZ_D_REP1]
273 var rep2: i64 = d[LZ_D_REP2]
274 var rep3: i64 = d[LZ_D_REP3]
275 var pos: i64 = d[LZ_D_OUTPOS]
276 let base: i64 = d[LZ_D_BASE]
277 var result: i64 = 1 // 1 = still decoding
278 while result == 1 {
279 if d[LZ_D_INOVER] == 1 { result = LZ_ERR_INPUT_SHORT; break }
280 if remain == 0 { if d[LZ_D_CODE] == 0 { result = LZ_OK; break } }
281 let total_pos: i64 = pos - base
282 let pos_state: i64 = total_pos & pb_mask
283 let state2: i64 = state * LZ_NUM_POS_STATES_MAX + pos_state
284 if lz_bit(d, LZ_P_ISMATCH + state2) == 0 {
285 // LITERAL
286 if remain == 0 { result = LZ_ERR_OUTPUT_OVER; break }
287 var prev: i64 = 0
288 if total_pos > 0 { prev = (out[pos - 1] & 0xff) as i64 }
289 let lit_state: i64 = ((total_pos & lp_mask) * lz_pow2(lc)) + prev / lz_pow2(8 - lc)
290 let pb0: i64 = LZ_P_LIT + LZ_LIT_TABLE * lit_state
291 var sym: i64 = 1
292 if state >= 7 {
293 var match_byte: i64 = (out[pos - rep0 - 1] & 0xff) as i64
294 var go: i64 = 1
295 while go == 1 {
296 let match_bit: i64 = (match_byte / 128) & 1
297 match_byte = (match_byte * 2) & 255
298 let bit: i64 = lz_bit(d, pb0 + (1 + match_bit) * 256 + sym)
299 sym = sym * 2 + bit
300 if match_bit != bit { go = 0 }
301 if sym >= 256 { go = 0 }
302 }
303 }
304 while sym < 256 { sym = sym * 2 + lz_bit(d, pb0 + sym) }
305 out[pos] = (sym - 256) as u8
306 pos = pos + 1
307 remain = remain - 1
308 state = lz_state_lit(state)
309 } else {
310 var len: i64 = 0
311 if lz_bit(d, LZ_P_ISREP + state) == 0 {
312 // SIMPLE MATCH
313 rep3 = rep2; rep2 = rep1; rep1 = rep0
314 len = lz_len(d, LZ_P_LEN, pos_state)
315 state = lz_state_match(state)
316 rep0 = lz_dist(d, len)
317 if rep0 == LZ_MARKER {
318 if d[LZ_D_CODE] == 0 { result = LZ_OK } else { result = LZ_ERR_MARKER_CODE }
319 break
320 }
321 if remain == 0 { result = LZ_ERR_OUTPUT_OVER; break }
322 if rep0 >= d[LZ_D_DICT] { result = LZ_ERR_DISTANCE; break }
323 if rep0 + 1 > total_pos { result = LZ_ERR_DISTANCE; break }
324 } else {
325 // REP MATCH
326 if remain == 0 { result = LZ_ERR_OUTPUT_OVER; break }
327 if total_pos == 0 { result = LZ_ERR_WINDOW_EMPTY; break }
328 if lz_bit(d, LZ_P_ISREPG0 + state) == 0 {
329 if lz_bit(d, LZ_P_ISREP0LONG + state2) == 0 {
330 // SHORT REP
331 state = lz_state_shortrep(state)
332 out[pos] = out[pos - rep0 - 1]
333 pos = pos + 1
334 remain = remain - 1
335 len = 0 - 1
336 }
337 } else {
338 var dist: i64 = 0
339 if lz_bit(d, LZ_P_ISREPG1 + state) == 0 { dist = rep1 }
340 else {
341 if lz_bit(d, LZ_P_ISREPG2 + state) == 0 { dist = rep2 }
342 else { dist = rep3; rep3 = rep2 }
343 rep2 = rep1
344 }
345 rep1 = rep0
346 rep0 = dist
347 }
348 if len >= 0 {
349 len = lz_len(d, LZ_P_REPLEN, pos_state)
350 state = lz_state_rep(state)
351 }
352 }
353 if len >= 0 {
354 // COPY THE MATCH
355 len = len + LZ_MATCH_MIN_LEN
356 var over: i64 = 0
357 if remain < len { len = remain; over = 1 }
358 let dist1: i64 = rep0 + 1
359 var k: i64 = 0
360 while k < len { out[pos] = out[pos - dist1]; pos = pos + 1; k = k + 1 }
361 remain = remain - len
362 if over == 1 { result = LZ_ERR_OUTPUT_OVER; break }
363 }
364 }
365 }
366 d[LZ_D_STATE] = state
367 d[LZ_D_REP0] = rep0; d[LZ_D_REP1] = rep1; d[LZ_D_REP2] = rep2; d[LZ_D_REP3] = rep3
368 d[LZ_D_OUTPOS] = pos
369 return result
370}
371// ---- the 7z LZMA coder (id 03 01 01): five property bytes, one stream, one unpack size ----
372func lz_lzma_props_dict(props: *u8) -> i64 {
373 return ((props[1] & 0xff) as i64) | (((props[2] & 0xff) as i64) * 256) | (((props[3] & 0xff) as i64) * 65536) | (((props[4] & 0xff) as i64) * 16777216)
374}
375func lz_lzma_decode(src: *u8, srclen: i64, props: *u8, out: *u8, unpack: i64) -> i64 {
376 let d0: *i64 = sys_mmap(8 * LZ_D_N) as *i64
377 d0[LZ_D_LC] = 0; d0[LZ_D_LP] = 0; d0[LZ_D_PB] = 0
378 let pr: i64 = lz_set_props_byte(d0, (props[0] & 0xff) as i64, 12)
379 if pr < 0 { return pr }
380 let d: *i64 = lz_new(out, unpack, d0[LZ_D_LC], d0[LZ_D_LP], d0[LZ_D_PB], lz_lzma_props_dict(props))
381 let rc: i64 = lz_decode(d, src, srclen, unpack)
382 if rc < 0 { return rc }
383 if d[LZ_D_OUTPOS] != unpack { return LZ_ERR_UNPACK_MISMATCH }
384 return unpack
385}
386// ---- the 7z LZMA2 coder (id 21): one property byte (the dictionary), a chunk stream ----
387func lz_lzma2_dict(p: i64) -> i64 {
388 if p > 40 { return 0 - 1 }
389 if p == 40 { return LZ_MASK32 }
390 return (2 | (p & 1)) * lz_pow2(p / 2 + 11)
391}
392func lz_lzma2_decode(src: *u8, srclen: i64, dict_prop: i64, out: *u8, unpack: i64) -> i64 {
393 let dict: i64 = lz_lzma2_dict(dict_prop)
394 if dict < 0 { return LZ_ERR_PROPS }
395 let d: *i64 = lz_new(out, unpack, 0, 0, 0, dict)
396 var ip: i64 = 0
397 var need_props: i64 = 1 // the first LZMA chunk must carry properties (control 0xC0 or above)
398 var result: i64 = 1
399 while result == 1 {
400 if ip >= srclen { result = LZ_ERR_INPUT_SHORT; break }
401 let control: i64 = (src[ip] & 0xff) as i64
402 ip = ip + 1
403 if control == 0 { result = LZ_OK; break }
404 if control < 128 {
405 // uncompressed chunk: 1 resets the dictionary, 2 does not; anything else is refused
406 if control > 2 { result = LZ_ERR_CONTROL; break }
407 if ip + 2 > srclen { result = LZ_ERR_INPUT_SHORT; break }
408 let usz: i64 = ((src[ip] & 0xff) as i64) * 256 + ((src[ip + 1] & 0xff) as i64) + 1
409 ip = ip + 2
410 if ip + usz > srclen { result = LZ_ERR_INPUT_SHORT; break }
411 if d[LZ_D_OUTPOS] + usz > d[LZ_D_OUTEND] { result = LZ_ERR_OUTPUT_OVER; break }
412 if control == 1 { d[LZ_D_BASE] = d[LZ_D_OUTPOS]; need_props = 1 }
413 let o: *u8 = d[LZ_D_OUT] as *u8
414 var k: i64 = 0
415 while k < usz { o[d[LZ_D_OUTPOS] + k] = src[ip + k]; k = k + 1 }
416 d[LZ_D_OUTPOS] = d[LZ_D_OUTPOS] + usz
417 ip = ip + usz
418 d[LZ_D_CHUNKS] = d[LZ_D_CHUNKS] + 1
419 } else {
420 if ip + 4 > srclen { result = LZ_ERR_INPUT_SHORT; break }
421 let usz: i64 = (control & 31) * 65536 + ((src[ip] & 0xff) as i64) * 256 + ((src[ip + 1] & 0xff) as i64) + 1
422 let psz: i64 = ((src[ip + 2] & 0xff) as i64) * 256 + ((src[ip + 3] & 0xff) as i64) + 1
423 ip = ip + 4
424 let mode: i64 = (control / 32) & 3
425 if mode >= 2 {
426 if ip >= srclen { result = LZ_ERR_INPUT_SHORT; break }
427 let pr: i64 = lz_set_props_byte(d, (src[ip] & 0xff) as i64, LZ_LCLP_MAX)
428 if pr < 0 { result = pr; break }
429 ip = ip + 1
430 need_props = 0
431 } else {
432 if need_props == 1 { result = LZ_ERR_CONTROL; break }
433 }
434 if mode == 3 { d[LZ_D_BASE] = d[LZ_D_OUTPOS] }
435 if mode >= 1 { lz_reset_state(d) }
436 if ip + psz > srclen { result = LZ_ERR_INPUT_SHORT; break }
437 let before: i64 = d[LZ_D_OUTPOS]
438 let rc: i64 = lz_decode(d, src + ip, psz, usz)
439 if rc < 0 { result = rc; break }
440 if d[LZ_D_OUTPOS] - before != usz { result = LZ_ERR_UNPACK_MISMATCH; break }
441 ip = ip + psz
442 d[LZ_D_CHUNKS] = d[LZ_D_CHUNKS] + 1
443 }
444 }
445 if result < 0 { return result }
446 if d[LZ_D_OUTPOS] != unpack { return LZ_ERR_UNPACK_MISMATCH }
447 return unpack
448}
449// ---- the x86 BCJ branch converter (7z coder 03 03 01 03), transliterated from the mirrored public-domain Bra86.c
450// (knowledge/fetched/cmp_modding_Bra86.c, Igor Pavlov 2017-04-03, pin f6ffa9c7): CALL/JMP rel32 targets are made absolute by the
451// encoder and relative again by the decoder so compressed executables repeat; size-preserving, in place; the last four bytes are
452// never converted. encoding=1 converts forward (used only by gates to build a fixture), encoding=0 restores. Returns the bytes
453// processed; statebox[0] carries the mask between calls exactly as the SDK's state word does. ----
454const LZ_BCJ_OP_MASK: i64 = 254
455const LZ_BCJ_OP: i64 = 232 // E8 (CALL) and E9 (JMP) both match under the 0xFE mask
456const LZ_BCJ_TAIL: i64 = 4
457const LZ_BCJ_HEAD: i64 = 5
458func lz_test86(b: i64) -> i64 { if ((b + 1) & LZ_BCJ_OP_MASK) == 0 { return 1 } return 0 }
459func lz_bcj_x86(data: *u8, size0: i64, ip0: i64, statebox: *i64, encoding: i64) -> i64 {
460 var pos: i64 = 0
461 var mask: i64 = statebox[0] & 7
462 if size0 < LZ_BCJ_HEAD { return 0 }
463 let size: i64 = size0 - LZ_BCJ_TAIL
464 let ip: i64 = ip0 + LZ_BCJ_HEAD
465 var go: i64 = 1
466 while go == 1 {
467 var p: i64 = pos
468 while p < size { if (((data[p] & 0xff) as i64) & LZ_BCJ_OP_MASK) == LZ_BCJ_OP { break } p = p + 1 }
469 let dd: i64 = p - pos
470 pos = p
471 if p >= size {
472 if dd > 2 { statebox[0] = 0 } else { statebox[0] = mask / lz_pow2(dd) }
473 return pos
474 }
475 var skip: i64 = 0
476 if dd > 2 { mask = 0 }
477 else {
478 mask = mask / lz_pow2(dd)
479 if mask != 0 {
480 var cond: i64 = 0
481 if mask > 4 { cond = 1 }
482 if mask == 3 { cond = 1 }
483 if lz_test86((data[p + (mask / 2) + 1] & 0xff) as i64) == 1 { cond = 1 }
484 if cond == 1 { mask = (mask / 2) | 4; pos = pos + 1; skip = 1 }
485 }
486 }
487 if skip == 0 {
488 if lz_test86((data[p + 4] & 0xff) as i64) == 1 {
489 var v: i64 = ((data[p + 4] & 0xff) as i64) * 16777216 + ((data[p + 3] & 0xff) as i64) * 65536 + ((data[p + 2] & 0xff) as i64) * 256 + ((data[p + 1] & 0xff) as i64)
490 let cur: i64 = (ip + pos) & LZ_MASK32
491 pos = pos + 5
492 if encoding == 1 { v = (v + cur) & LZ_MASK32 } else { v = (v - cur + LZ_MASK32 + 1) & LZ_MASK32 }
493 if mask != 0 {
494 let sh: i64 = (mask & 6) * 4
495 if lz_test86((v / lz_pow2(sh)) & 255) == 1 {
496 v = (v ^ (256 * lz_pow2(sh) - 1)) & LZ_MASK32
497 if encoding == 1 { v = (v + cur) & LZ_MASK32 } else { v = (v - cur + LZ_MASK32 + 1) & LZ_MASK32 }
498 }
499 mask = 0
500 }
501 data[p + 1] = (v & 255) as u8
502 data[p + 2] = ((v / 256) & 255) as u8
503 data[p + 3] = ((v / 65536) & 255) as u8
504 data[p + 4] = ((0 - ((v / 16777216) & 1)) & 255) as u8
505 } else { mask = (mask / 2) | 4; pos = pos + 1 }
506 }
507 }
508 return pos
509}