code wiki / (root) / nx_lzma_lib.nx

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}