A RuneTek3 client (377) that is deobfuscated, converted to Kotlin, and includes QoL improvements.
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'worktree-kotlin-bzip2-decompressor' into 'master'

Rewrite BZip2 decompressor in Kotlin (Phase 3)

See merge request high-level-alchemy/client!3

+560 -540
-498
src/main/java/com/jagex/runescape/cache/bzip/BZip2Decompressor.java
··· 1 - package com.jagex.runescape.cache.bzip; 2 - 3 - public class BZip2Decompressor { 4 - 5 - public static Bzip2Context state = new Bzip2Context(); 6 - 7 - public static int decompress(byte[] output, int lenght, byte[] compressed, int decompressedLength, int minLen) { 8 - synchronized (state) { 9 - state.compressed = compressed; 10 - state.nextIn = minLen; 11 - state.buf = output; 12 - state.nextOut = 0; 13 - state.decompressedLength = decompressedLength; 14 - state.availOut = lenght; 15 - state.bsLive = 0; 16 - state.bsBuff = 0; 17 - state.totalInLo32 = 0; 18 - state.totalInHi32 = 0; 19 - state.totalOutLo32 = 0; 20 - state.totalOutHigh32 = 0; 21 - state.currentBlock = 0; 22 - decompress(state); 23 - lenght -= state.availOut; 24 - return lenght; 25 - } 26 - } 27 - 28 - public static void method313(Bzip2Context bzip2Context) { 29 - byte stateOutCh = bzip2Context.stateOutCh; 30 - int stateOutLen = bzip2Context.stateOutLen; 31 - int nBlockUsed = bzip2Context.nBlockUsed; 32 - int k0 = bzip2Context.k0; 33 - int[] tt = Bzip2Context.tt; 34 - int tPos = bzip2Context.tPos; 35 - byte[] buf = bzip2Context.buf; 36 - int csNextOut = bzip2Context.nextOut; 37 - int csAvailOut = bzip2Context.availOut; 38 - int availOutInit = csAvailOut; 39 - int savedNBlockPP = bzip2Context.nBlock + 1; 40 - outer: do { 41 - if (stateOutLen > 0) { 42 - do { 43 - if (csAvailOut == 0) 44 - break outer; 45 - if (stateOutLen == 1) 46 - break; 47 - buf[csNextOut] = stateOutCh; 48 - stateOutLen--; 49 - csNextOut++; 50 - csAvailOut--; 51 - } while (true); 52 - if (csAvailOut == 0) { 53 - stateOutLen = 1; 54 - break; 55 - } 56 - buf[csNextOut] = stateOutCh; 57 - csNextOut++; 58 - csAvailOut--; 59 - } 60 - boolean flag = true; 61 - while (flag) { 62 - flag = false; 63 - if (nBlockUsed == savedNBlockPP) { 64 - stateOutLen = 0; 65 - break outer; 66 - } 67 - stateOutCh = (byte) k0; 68 - tPos = tt[tPos]; 69 - byte k1 = (byte) (tPos & 0xff); 70 - tPos >>= 8; 71 - nBlockUsed++; 72 - if (k1 != k0) { 73 - k0 = k1; 74 - if (csAvailOut == 0) { 75 - stateOutLen = 1; 76 - } else { 77 - buf[csNextOut] = stateOutCh; 78 - csNextOut++; 79 - csAvailOut--; 80 - flag = true; 81 - continue; 82 - } 83 - break outer; 84 - } 85 - if (nBlockUsed != savedNBlockPP) 86 - continue; 87 - if (csAvailOut == 0) { 88 - stateOutLen = 1; 89 - break outer; 90 - } 91 - buf[csNextOut] = stateOutCh; 92 - csNextOut++; 93 - csAvailOut--; 94 - flag = true; 95 - } 96 - stateOutLen = 2; 97 - tPos = tt[tPos]; 98 - byte k1 = (byte) (tPos & 0xff); 99 - tPos >>= 8; 100 - if (++nBlockUsed != savedNBlockPP) 101 - if (k1 != k0) { 102 - k0 = k1; 103 - } else { 104 - stateOutLen = 3; 105 - tPos = tt[tPos]; 106 - byte k1_ = (byte) (tPos & 0xff); 107 - tPos >>= 8; 108 - if (++nBlockUsed != savedNBlockPP) 109 - if (k1_ != k0) { 110 - k0 = k1_; 111 - } else { 112 - tPos = tt[tPos]; 113 - byte byte3 = (byte) (tPos & 0xff); 114 - tPos >>= 8; 115 - nBlockUsed++; 116 - stateOutLen = (byte3 & 0xff) + 4; 117 - tPos = tt[tPos]; 118 - k0 = (byte) (tPos & 0xff); 119 - tPos >>= 8; 120 - nBlockUsed++; 121 - } 122 - } 123 - } while (true); 124 - int oldTotalOutLo32 = bzip2Context.totalOutLo32; 125 - bzip2Context.totalOutLo32 += availOutInit - csAvailOut; 126 - if (bzip2Context.totalOutLo32 < oldTotalOutLo32) 127 - bzip2Context.totalOutHigh32++; 128 - bzip2Context.stateOutCh = stateOutCh; 129 - bzip2Context.stateOutLen = stateOutLen; 130 - bzip2Context.nBlockUsed = nBlockUsed; 131 - bzip2Context.k0 = k0; 132 - Bzip2Context.tt = tt; 133 - bzip2Context.tPos = tPos; 134 - bzip2Context.buf = buf; 135 - bzip2Context.nextOut = csNextOut; 136 - bzip2Context.availOut = csAvailOut; 137 - } 138 - 139 - public static void decompress(Bzip2Context bzip2Context) { 140 - int gMinLen = 0; 141 - int[] gLimit = null; 142 - int[] gBase = null; 143 - int[] gPerm = null; 144 - bzip2Context.blockSize100k = 1; 145 - if (Bzip2Context.tt == null) 146 - Bzip2Context.tt = new int[bzip2Context.blockSize100k * 0x186a0]; 147 - boolean flag19 = true; 148 - while (flag19) { 149 - byte uc = getUChar(bzip2Context); 150 - if (uc == 23) 151 - return; 152 - uc = getUChar(bzip2Context); 153 - uc = getUChar(bzip2Context); 154 - uc = getUChar(bzip2Context); 155 - uc = getUChar(bzip2Context); 156 - uc = getUChar(bzip2Context); 157 - bzip2Context.currentBlock++; 158 - uc = getUChar(bzip2Context); 159 - uc = getUChar(bzip2Context); 160 - uc = getUChar(bzip2Context); 161 - uc = getUChar(bzip2Context); 162 - uc = getBit(bzip2Context); 163 - bzip2Context.blockRandomised = uc != 0; 164 - if (bzip2Context.blockRandomised) 165 - System.out.println("PANIC! RANDOMISED BLOCK!"); 166 - bzip2Context.origPtr = 0; 167 - uc = getUChar(bzip2Context); 168 - bzip2Context.origPtr = bzip2Context.origPtr << 8 | uc & 0xff; 169 - uc = getUChar(bzip2Context); 170 - bzip2Context.origPtr = bzip2Context.origPtr << 8 | uc & 0xff; 171 - uc = getUChar(bzip2Context); 172 - bzip2Context.origPtr = bzip2Context.origPtr << 8 | uc & 0xff; 173 - for (int i = 0; i < 16; i++) { 174 - byte bit = getBit(bzip2Context); 175 - bzip2Context.inUse16[i] = bit == 1; 176 - } 177 - 178 - for (int i = 0; i < 256; i++) 179 - bzip2Context.inUse[i] = false; 180 - 181 - for (int i = 0; i < 16; i++) 182 - if (bzip2Context.inUse16[i]) { 183 - for (int j = 0; j < 16; j++) { 184 - byte byte2 = getBit(bzip2Context); 185 - if (byte2 == 1) 186 - bzip2Context.inUse[i * 16 + j] = true; 187 - } 188 - 189 - } 190 - 191 - makeMaps(bzip2Context); 192 - int alphaSize = bzip2Context.nInUse + 2; 193 - int nGroups = getBits(3, bzip2Context); 194 - int nSelectors = getBits(15, bzip2Context); 195 - for (int i = 0; i < nSelectors; i++) { 196 - int count = 0; 197 - do { 198 - byte terminator = getBit(bzip2Context); 199 - if (terminator == 0) 200 - break; 201 - count++; 202 - } while (true); 203 - bzip2Context.selectorMtf[i] = (byte) count; 204 - } 205 - 206 - byte[] pos = new byte[6]; 207 - for (byte v = 0; v < nGroups; v++) 208 - pos[v] = v; 209 - 210 - for (int i = 0; i < nSelectors; i++) { 211 - byte v = bzip2Context.selectorMtf[i]; 212 - byte temp = pos[v]; 213 - for (; v > 0; v--) 214 - pos[v] = pos[v - 1]; 215 - 216 - pos[0] = temp; 217 - bzip2Context.selector[i] = temp; 218 - } 219 - 220 - for (int t = 0; t < nGroups; t++) { 221 - int curr = getBits(5, bzip2Context); 222 - for (int i = 0; i < alphaSize; i++) { 223 - do { 224 - byte bit = getBit(bzip2Context); 225 - if (bit == 0) 226 - break; 227 - bit = getBit(bzip2Context); 228 - if (bit == 0) 229 - curr++; 230 - else 231 - curr--; 232 - } while (true); 233 - bzip2Context.len[t][i] = (byte) curr; 234 - } 235 - 236 - } 237 - 238 - for (int t = 0; t < nGroups; t++) { 239 - byte minLen = 32; 240 - int maxLen = 0; 241 - for (int i = 0; i < alphaSize; i++) { 242 - if (bzip2Context.len[t][i] > maxLen) 243 - maxLen = bzip2Context.len[t][i]; 244 - if (bzip2Context.len[t][i] < minLen) 245 - minLen = bzip2Context.len[t][i]; 246 - } 247 - 248 - createDecodeTables(bzip2Context.limit[t], bzip2Context.base[t], bzip2Context.perm[t], 249 - bzip2Context.len[t], minLen, maxLen, alphaSize); 250 - bzip2Context.minLens[t] = minLen; 251 - } 252 - 253 - int eob = bzip2Context.nInUse + 1; 254 - //int l5 = 0x186a0 * class1.blockSize100k; 255 - int groupNo = -1; 256 - int groupPos = 0; 257 - for (int i = 0; i <= 255; i++) 258 - bzip2Context.unzftab[i] = 0; 259 - 260 - int kk = 4095; 261 - for (int ii = 15; ii >= 0; ii--) { 262 - for (int jj = 15; jj >= 0; jj--) { 263 - bzip2Context.mtfa[kk] = (byte) (ii * 16 + jj); 264 - kk--; 265 - } 266 - 267 - bzip2Context.mtfbase[ii] = kk + 1; 268 - } 269 - 270 - int nblock = 0; 271 - if (groupPos == 0) { 272 - groupNo++; 273 - groupPos = 50; 274 - byte gSel = bzip2Context.selector[groupNo]; 275 - gMinLen = bzip2Context.minLens[gSel]; 276 - gLimit = bzip2Context.limit[gSel]; 277 - gPerm = bzip2Context.perm[gSel]; 278 - gBase = bzip2Context.base[gSel]; 279 - } 280 - groupPos--; 281 - int zn = gMinLen; 282 - int zvec; 283 - byte zj; 284 - for (zvec = getBits(zn, bzip2Context); zvec > gLimit[zn]; zvec = zvec << 1 | zj) { 285 - zn++; 286 - zj = getBit(bzip2Context); 287 - } 288 - 289 - for (int nextSym = gPerm[zvec - gBase[zn]]; nextSym != eob;) 290 - if (nextSym == 0 || nextSym == 1) { 291 - int es = -1; 292 - int n = 1; 293 - do { 294 - if (nextSym == 0) 295 - es += n; 296 - else if (nextSym == 1) 297 - es += 2 * n; 298 - n *= 2; 299 - if (groupPos == 0) { 300 - groupNo++; 301 - groupPos = 50; 302 - byte gSel = bzip2Context.selector[groupNo]; 303 - gMinLen = bzip2Context.minLens[gSel]; 304 - gLimit = bzip2Context.limit[gSel]; 305 - gPerm = bzip2Context.perm[gSel]; 306 - gBase = bzip2Context.base[gSel]; 307 - } 308 - groupPos--; 309 - int zn_ = gMinLen; 310 - int zvec_; 311 - byte byte10; 312 - for (zvec_ = getBits(zn_, bzip2Context); zvec_ > gLimit[zn_]; zvec_ = zvec_ << 1 | byte10) { 313 - zn_++; 314 - byte10 = getBit(bzip2Context); 315 - } 316 - 317 - nextSym = gPerm[zvec_ - gBase[zn_]]; 318 - } while (nextSym == 0 || nextSym == 1); 319 - es++; 320 - byte ec = bzip2Context.seqToUnseq[bzip2Context.mtfa[bzip2Context.mtfbase[0]] & 0xff]; 321 - bzip2Context.unzftab[ec & 0xff] += es; 322 - for (; es > 0; es--) { 323 - Bzip2Context.tt[nblock] = ec & 0xff; 324 - nblock++; 325 - } 326 - 327 - } else { 328 - int nn = nextSym - 1; 329 - byte uc_; 330 - if (nn < 16) { 331 - int j10 = bzip2Context.mtfbase[0]; 332 - uc_ = bzip2Context.mtfa[j10 + nn]; 333 - for (; nn > 3; nn -= 4) { 334 - int k11 = j10 + nn; 335 - bzip2Context.mtfa[k11] = bzip2Context.mtfa[k11 - 1]; 336 - bzip2Context.mtfa[k11 - 1] = bzip2Context.mtfa[k11 - 2]; 337 - bzip2Context.mtfa[k11 - 2] = bzip2Context.mtfa[k11 - 3]; 338 - bzip2Context.mtfa[k11 - 3] = bzip2Context.mtfa[k11 - 4]; 339 - } 340 - 341 - for (; nn > 0; nn--) 342 - bzip2Context.mtfa[j10 + nn] = bzip2Context.mtfa[(j10 + nn) - 1]; 343 - 344 - bzip2Context.mtfa[j10] = uc_; 345 - } else { 346 - int l10 = nn / 16; 347 - int i11 = nn % 16; 348 - int k10 = bzip2Context.mtfbase[l10] + i11; 349 - uc_ = bzip2Context.mtfa[k10]; 350 - for (; k10 > bzip2Context.mtfbase[l10]; k10--) 351 - bzip2Context.mtfa[k10] = bzip2Context.mtfa[k10 - 1]; 352 - 353 - bzip2Context.mtfbase[l10]++; 354 - for (; l10 > 0; l10--) { 355 - bzip2Context.mtfbase[l10]--; 356 - bzip2Context.mtfa[bzip2Context.mtfbase[l10]] = bzip2Context.mtfa[(bzip2Context.mtfbase[l10 - 1] + 16) - 1]; 357 - } 358 - 359 - bzip2Context.mtfbase[0]--; 360 - bzip2Context.mtfa[bzip2Context.mtfbase[0]] = uc_; 361 - if (bzip2Context.mtfbase[0] == 0) { 362 - int i10 = 4095; 363 - for (int k9 = 15; k9 >= 0; k9--) { 364 - for (int l9 = 15; l9 >= 0; l9--) { 365 - bzip2Context.mtfa[i10] = bzip2Context.mtfa[bzip2Context.mtfbase[k9] + l9]; 366 - i10--; 367 - } 368 - 369 - bzip2Context.mtfbase[k9] = i10 + 1; 370 - } 371 - 372 - } 373 - } 374 - bzip2Context.unzftab[bzip2Context.seqToUnseq[uc_ & 0xff] & 0xff]++; 375 - Bzip2Context.tt[nblock] = bzip2Context.seqToUnseq[uc_ & 0xff] & 0xff; 376 - nblock++; 377 - if (groupPos == 0) { 378 - groupNo++; 379 - groupPos = 50; 380 - byte byte14 = bzip2Context.selector[groupNo]; 381 - gMinLen = bzip2Context.minLens[byte14]; 382 - gLimit = bzip2Context.limit[byte14]; 383 - gPerm = bzip2Context.perm[byte14]; 384 - gBase = bzip2Context.base[byte14]; 385 - } 386 - groupPos--; 387 - int k7 = gMinLen; 388 - int j8; 389 - byte byte11; 390 - for (j8 = getBits(k7, bzip2Context); j8 > gLimit[k7]; j8 = j8 << 1 | byte11) { 391 - k7++; 392 - byte11 = getBit(bzip2Context); 393 - } 394 - 395 - nextSym = gPerm[j8 - gBase[k7]]; 396 - } 397 - 398 - bzip2Context.stateOutLen = 0; 399 - bzip2Context.stateOutCh = 0; 400 - bzip2Context.cftab[0] = 0; 401 - System.arraycopy(bzip2Context.unzftab, 0, bzip2Context.cftab, 1, 256); 402 - 403 - for (int k2 = 1; k2 <= 256; k2++) 404 - bzip2Context.cftab[k2] += bzip2Context.cftab[k2 - 1]; 405 - 406 - for (int l2 = 0; l2 < nblock; l2++) { 407 - byte byte7 = (byte) (Bzip2Context.tt[l2] & 0xff); 408 - Bzip2Context.tt[bzip2Context.cftab[byte7 & 0xff]] |= l2 << 8; 409 - bzip2Context.cftab[byte7 & 0xff]++; 410 - } 411 - 412 - bzip2Context.tPos = Bzip2Context.tt[bzip2Context.origPtr] >> 8; 413 - bzip2Context.nBlockUsed = 0; 414 - bzip2Context.tPos = Bzip2Context.tt[bzip2Context.tPos]; 415 - bzip2Context.k0 = (byte) (bzip2Context.tPos & 0xff); 416 - bzip2Context.tPos >>= 8; 417 - bzip2Context.nBlockUsed++; 418 - bzip2Context.nBlock = nblock; 419 - method313(bzip2Context); 420 - flag19 = bzip2Context.nBlockUsed == bzip2Context.nBlock + 1 && bzip2Context.stateOutLen == 0; 421 - } 422 - } 423 - 424 - public static byte getUChar(Bzip2Context bzip2Context) { 425 - return (byte) getBits(8, bzip2Context); 426 - } 427 - 428 - public static byte getBit(Bzip2Context bzip2Context) { 429 - return (byte) getBits(1, bzip2Context); 430 - } 431 - 432 - public static int getBits(int numBits, Bzip2Context bzip2Context) { 433 - int bits; 434 - do { 435 - if (bzip2Context.bsLive >= numBits) { 436 - int k = bzip2Context.bsBuff >> bzip2Context.bsLive - numBits & (1 << numBits) - 1; 437 - bzip2Context.bsLive -= numBits; 438 - bits = k; 439 - break; 440 - } 441 - bzip2Context.bsBuff = bzip2Context.bsBuff << 8 | bzip2Context.compressed[bzip2Context.nextIn] & 0xff; 442 - bzip2Context.bsLive += 8; 443 - bzip2Context.nextIn++; 444 - bzip2Context.decompressedLength--; 445 - bzip2Context.totalInLo32++; 446 - if (bzip2Context.totalInLo32 == 0) 447 - bzip2Context.totalInHi32++; 448 - } while (true); 449 - return bits; 450 - } 451 - 452 - public static void makeMaps(Bzip2Context bzip2Context) { 453 - bzip2Context.nInUse = 0; 454 - for (int i = 0; i < 256; i++) 455 - if (bzip2Context.inUse[i]) { 456 - bzip2Context.seqToUnseq[bzip2Context.nInUse] = (byte) i; 457 - bzip2Context.nInUse++; 458 - } 459 - 460 - } 461 - 462 - public static void createDecodeTables(int[] limit, int[] base, int[] ai2, byte[] len, int minLen, int maxLen, int alphaSize) { 463 - int pp = 0; 464 - for (int i = minLen; i <= maxLen; i++) { 465 - for (int j = 0; j < alphaSize; j++) 466 - if (len[j] == i) { 467 - ai2[pp] = j; 468 - pp++; 469 - } 470 - 471 - } 472 - 473 - for (int i = 0; i < 23; i++) 474 - base[i] = 0; 475 - 476 - for (int i = 0; i < alphaSize; i++) 477 - base[len[i] + 1]++; 478 - 479 - for (int i = 1; i < 23; i++) 480 - base[i] += base[i - 1]; 481 - 482 - for (int i = 0; i < 23; i++) 483 - limit[i] = 0; 484 - 485 - int vec = 0; 486 - for (int i = minLen; i <= maxLen; i++) { 487 - vec += base[i + 1] - base[i]; 488 - limit[i] = vec - 1; 489 - vec <<= 1; 490 - } 491 - 492 - for (int i = minLen + 1; i <= maxLen; i++) 493 - base[i] = (limit[i - 1] + 1 << 1) - base[i]; 494 - 495 - } 496 - 497 - 498 - }
+515
src/main/java/com/jagex/runescape/cache/bzip/BZip2Decompressor.kt
··· 1 + package com.jagex.runescape.cache.bzip 2 + 3 + /** 4 + * BZip2 decompressor for the RS cache format. 5 + * 6 + * Byte/int type semantics (critical for correctness): 7 + * - [getBit] and [getUChar] return [Int] to avoid Kotlin's lack of implicit byte-to-int promotion. 8 + * Java's original `(byte) getBits(...)` truncated then auto-widened — returning Int directly 9 + * sidesteps the sign-extension traps that caused the previous Kotlin conversion to be reverted. 10 + * - `toByte().toInt()` is used where Java does `(byte)(x & 0xff)` followed by auto-widening to int. 11 + * This replicates the sign-extended byte value stored in int fields like [Bzip2Context.k0]. 12 + * - `.toInt() and 0xff` is used for unsigned reads from [ByteArray], replicating Java's `byte & 0xff`. 13 + */ 14 + object BZip2Decompressor { 15 + 16 + @JvmField val state = Bzip2Context() 17 + 18 + @JvmStatic 19 + fun decompress( 20 + output: ByteArray, 21 + length: Int, 22 + compressed: ByteArray, 23 + decompressedLength: Int, 24 + minLen: Int 25 + ): Int { 26 + synchronized(state) { 27 + state.compressed = compressed 28 + state.nextIn = minLen 29 + state.buf = output 30 + state.nextOut = 0 31 + state.decompressedLength = decompressedLength 32 + state.availOut = length 33 + state.bsLive = 0 34 + state.bsBuff = 0 35 + state.totalInLo32 = 0 36 + state.totalInHi32 = 0 37 + state.totalOutLo32 = 0 38 + state.totalOutHigh32 = 0 39 + state.currentBlock = 0 40 + decompress(state) 41 + return length - state.availOut 42 + } 43 + } 44 + 45 + /** 46 + * Inverse BWT output with run-length decoding. 47 + * 48 + * Traverses the BWT inverse via the [Bzip2Context.tt] table, decoding run-length encoded 49 + * output into [Bzip2Context.buf]. After 4 consecutive identical bytes, the next byte encodes 50 + * the additional repeat count. 51 + */ 52 + private fun writeRunLengthOutput(ctx: Bzip2Context) { 53 + var stateOutCh = ctx.stateOutCh 54 + var stateOutLen = ctx.stateOutLen 55 + var nBlockUsed = ctx.nBlockUsed 56 + var k0 = ctx.k0 57 + val tt = Bzip2Context.tt!! 58 + var tPos = ctx.tPos 59 + val buf = ctx.buf 60 + var csNextOut = ctx.nextOut 61 + var csAvailOut = ctx.availOut 62 + val availOutInit = csAvailOut 63 + val savedNBlockPP = ctx.nBlock + 1 64 + 65 + outer@ while (true) { 66 + if (stateOutLen > 0) { 67 + while (true) { 68 + if (csAvailOut == 0) break@outer 69 + if (stateOutLen == 1) break 70 + buf[csNextOut] = stateOutCh 71 + stateOutLen-- 72 + csNextOut++ 73 + csAvailOut-- 74 + } 75 + if (csAvailOut == 0) { 76 + stateOutLen = 1 77 + break 78 + } 79 + buf[csNextOut] = stateOutCh 80 + csNextOut++ 81 + csAvailOut-- 82 + } 83 + 84 + var flag = true 85 + while (flag) { 86 + flag = false 87 + if (nBlockUsed == savedNBlockPP) { 88 + stateOutLen = 0 89 + break@outer 90 + } 91 + stateOutCh = k0.toByte() 92 + tPos = tt[tPos] 93 + // Extract low byte with sign extension to match Java's (byte)(tPos & 0xff) → int widening 94 + val k1 = (tPos and 0xff).toByte().toInt() 95 + tPos = tPos shr 8 96 + nBlockUsed++ 97 + if (k1 != k0) { 98 + k0 = k1 99 + if (csAvailOut == 0) { 100 + stateOutLen = 1 101 + } else { 102 + buf[csNextOut] = stateOutCh 103 + csNextOut++ 104 + csAvailOut-- 105 + flag = true 106 + continue 107 + } 108 + break@outer 109 + } 110 + if (nBlockUsed != savedNBlockPP) continue 111 + if (csAvailOut == 0) { 112 + stateOutLen = 1 113 + break@outer 114 + } 115 + buf[csNextOut] = stateOutCh 116 + csNextOut++ 117 + csAvailOut-- 118 + flag = true 119 + } 120 + 121 + stateOutLen = 2 122 + tPos = tt[tPos] 123 + var k1 = (tPos and 0xff).toByte().toInt() 124 + tPos = tPos shr 8 125 + if (++nBlockUsed != savedNBlockPP) { 126 + if (k1 != k0) { 127 + k0 = k1 128 + } else { 129 + stateOutLen = 3 130 + tPos = tt[tPos] 131 + k1 = (tPos and 0xff).toByte().toInt() 132 + tPos = tPos shr 8 133 + if (++nBlockUsed != savedNBlockPP) { 134 + if (k1 != k0) { 135 + k0 = k1 136 + } else { 137 + tPos = tt[tPos] 138 + val runCount = tPos and 0xff 139 + tPos = tPos shr 8 140 + nBlockUsed++ 141 + stateOutLen = runCount + 4 142 + tPos = tt[tPos] 143 + k0 = (tPos and 0xff).toByte().toInt() 144 + tPos = tPos shr 8 145 + nBlockUsed++ 146 + } 147 + } 148 + } 149 + } 150 + } 151 + 152 + val oldTotalOutLo32 = ctx.totalOutLo32 153 + ctx.totalOutLo32 += availOutInit - csAvailOut 154 + if (ctx.totalOutLo32 < oldTotalOutLo32) ctx.totalOutHigh32++ 155 + ctx.stateOutCh = stateOutCh 156 + ctx.stateOutLen = stateOutLen 157 + ctx.nBlockUsed = nBlockUsed 158 + ctx.k0 = k0 159 + Bzip2Context.tt = tt 160 + ctx.tPos = tPos 161 + ctx.buf = buf 162 + ctx.nextOut = csNextOut 163 + ctx.availOut = csAvailOut 164 + } 165 + 166 + private fun decompress(ctx: Bzip2Context) { 167 + var gMinLen = 0 168 + var gLimit: IntArray? = null 169 + var gBase: IntArray? = null 170 + var gPerm: IntArray? = null 171 + 172 + ctx.blockSize100k = 1 173 + if (Bzip2Context.tt == null) { 174 + Bzip2Context.tt = IntArray(ctx.blockSize100k * 0x186a0) 175 + } 176 + 177 + do { 178 + var uc = getUChar(ctx) 179 + if (uc == 23) return 180 + 181 + uc = getUChar(ctx) 182 + uc = getUChar(ctx) 183 + uc = getUChar(ctx) 184 + uc = getUChar(ctx) 185 + uc = getUChar(ctx) 186 + ctx.currentBlock++ 187 + uc = getUChar(ctx) 188 + uc = getUChar(ctx) 189 + uc = getUChar(ctx) 190 + uc = getUChar(ctx) 191 + 192 + uc = getBit(ctx) 193 + ctx.blockRandomised = uc != 0 194 + if (ctx.blockRandomised) println("PANIC! RANDOMISED BLOCK!") 195 + 196 + ctx.origPtr = 0 197 + uc = getUChar(ctx) 198 + ctx.origPtr = (ctx.origPtr shl 8) or (uc and 0xff) 199 + uc = getUChar(ctx) 200 + ctx.origPtr = (ctx.origPtr shl 8) or (uc and 0xff) 201 + uc = getUChar(ctx) 202 + ctx.origPtr = (ctx.origPtr shl 8) or (uc and 0xff) 203 + 204 + for (i in 0 until 16) { 205 + ctx.inUse16[i] = getBit(ctx) == 1 206 + } 207 + 208 + ctx.inUse.fill(false) 209 + 210 + for (i in 0 until 16) { 211 + if (ctx.inUse16[i]) { 212 + for (j in 0 until 16) { 213 + if (getBit(ctx) == 1) { 214 + ctx.inUse[i * 16 + j] = true 215 + } 216 + } 217 + } 218 + } 219 + 220 + makeMaps(ctx) 221 + val alphaSize = ctx.nInUse + 2 222 + val nGroups = getBits(3, ctx) 223 + val nSelectors = getBits(15, ctx) 224 + 225 + for (i in 0 until nSelectors) { 226 + var count = 0 227 + while (getBit(ctx) != 0) { 228 + count++ 229 + } 230 + ctx.selectorMtf[i] = count.toByte() 231 + } 232 + 233 + val pos = ByteArray(6) 234 + for (v in 0 until nGroups) pos[v] = v.toByte() 235 + 236 + for (i in 0 until nSelectors) { 237 + var v = ctx.selectorMtf[i].toInt() and 0xff 238 + val temp = pos[v] 239 + while (v > 0) { 240 + pos[v] = pos[v - 1] 241 + v-- 242 + } 243 + pos[0] = temp 244 + ctx.selector[i] = temp 245 + } 246 + 247 + for (t in 0 until nGroups) { 248 + var curr = getBits(5, ctx) 249 + for (i in 0 until alphaSize) { 250 + while (true) { 251 + val bit = getBit(ctx) 252 + if (bit == 0) break 253 + if (getBit(ctx) == 0) curr++ else curr-- 254 + } 255 + ctx.len[t][i] = curr.toByte() 256 + } 257 + } 258 + 259 + for (t in 0 until nGroups) { 260 + var minLen = 32 261 + var maxLen = 0 262 + for (i in 0 until alphaSize) { 263 + val l = ctx.len[t][i].toInt() 264 + if (l > maxLen) maxLen = l 265 + if (l < minLen) minLen = l 266 + } 267 + createDecodeTables( 268 + ctx.limit[t], ctx.base[t], ctx.perm[t], 269 + ctx.len[t], minLen, maxLen, alphaSize 270 + ) 271 + ctx.minLens[t] = minLen 272 + } 273 + 274 + val eob = ctx.nInUse + 1 275 + var groupNo = -1 276 + var groupPos = 0 277 + 278 + ctx.unzftab.fill(0) 279 + 280 + var kk = 4095 281 + for (ii in 15 downTo 0) { 282 + for (jj in 15 downTo 0) { 283 + ctx.mtfa[kk] = (ii * 16 + jj).toByte() 284 + kk-- 285 + } 286 + ctx.mtfbase[ii] = kk + 1 287 + } 288 + 289 + var nblock = 0 290 + 291 + if (groupPos == 0) { 292 + groupNo++ 293 + groupPos = 50 294 + val gSel = ctx.selector[groupNo].toInt() and 0xff 295 + gMinLen = ctx.minLens[gSel] 296 + gLimit = ctx.limit[gSel] 297 + gPerm = ctx.perm[gSel] 298 + gBase = ctx.base[gSel] 299 + } 300 + groupPos-- 301 + 302 + var zn = gMinLen 303 + var zvec = getBits(zn, ctx) 304 + while (zvec > gLimit!![zn]) { 305 + zn++ 306 + zvec = (zvec shl 1) or getBit(ctx) 307 + } 308 + 309 + var nextSym = gPerm!![zvec - gBase!![zn]] 310 + while (nextSym != eob) { 311 + if (nextSym == 0 || nextSym == 1) { 312 + var es = -1 313 + var n = 1 314 + do { 315 + if (nextSym == 0) 316 + es += n 317 + else if (nextSym == 1) 318 + es += 2 * n 319 + n *= 2 320 + if (groupPos == 0) { 321 + groupNo++ 322 + groupPos = 50 323 + val gSel = ctx.selector[groupNo].toInt() and 0xff 324 + gMinLen = ctx.minLens[gSel] 325 + gLimit = ctx.limit[gSel] 326 + gPerm = ctx.perm[gSel] 327 + gBase = ctx.base[gSel] 328 + } 329 + groupPos-- 330 + var innerZn = gMinLen 331 + var innerZvec = getBits(innerZn, ctx) 332 + while (innerZvec > gLimit!![innerZn]) { 333 + innerZn++ 334 + innerZvec = (innerZvec shl 1) or getBit(ctx) 335 + } 336 + nextSym = gPerm!![innerZvec - gBase!![innerZn]] 337 + } while (nextSym == 0 || nextSym == 1) 338 + 339 + es++ 340 + val ec = ctx.seqToUnseq[ctx.mtfa[ctx.mtfbase[0]].toInt() and 0xff] 341 + ctx.unzftab[ec.toInt() and 0xff] += es 342 + while (es > 0) { 343 + Bzip2Context.tt!![nblock] = ec.toInt() and 0xff 344 + nblock++ 345 + es-- 346 + } 347 + } else { 348 + var nn = nextSym - 1 349 + val uc2: Byte 350 + if (nn < 16) { 351 + val pp = ctx.mtfbase[0] 352 + uc2 = ctx.mtfa[pp + nn] 353 + while (nn > 3) { 354 + val k11 = pp + nn 355 + ctx.mtfa[k11] = ctx.mtfa[k11 - 1] 356 + ctx.mtfa[k11 - 1] = ctx.mtfa[k11 - 2] 357 + ctx.mtfa[k11 - 2] = ctx.mtfa[k11 - 3] 358 + ctx.mtfa[k11 - 3] = ctx.mtfa[k11 - 4] 359 + nn -= 4 360 + } 361 + while (nn > 0) { 362 + ctx.mtfa[pp + nn] = ctx.mtfa[pp + nn - 1] 363 + nn-- 364 + } 365 + ctx.mtfa[pp] = uc2 366 + } else { 367 + var lll = nn / 16 368 + val mmm = nn % 16 369 + var kkk = ctx.mtfbase[lll] + mmm 370 + uc2 = ctx.mtfa[kkk] 371 + while (kkk > ctx.mtfbase[lll]) { 372 + ctx.mtfa[kkk] = ctx.mtfa[kkk - 1] 373 + kkk-- 374 + } 375 + ctx.mtfbase[lll]++ 376 + while (lll > 0) { 377 + ctx.mtfbase[lll]-- 378 + ctx.mtfa[ctx.mtfbase[lll]] = 379 + ctx.mtfa[ctx.mtfbase[lll - 1] + 16 - 1] 380 + lll-- 381 + } 382 + ctx.mtfbase[0]-- 383 + ctx.mtfa[ctx.mtfbase[0]] = uc2 384 + if (ctx.mtfbase[0] == 0) { 385 + var idx = 4095 386 + for (k9 in 15 downTo 0) { 387 + for (l9 in 15 downTo 0) { 388 + ctx.mtfa[idx] = ctx.mtfa[ctx.mtfbase[k9] + l9] 389 + idx-- 390 + } 391 + ctx.mtfbase[k9] = idx + 1 392 + } 393 + } 394 + } 395 + 396 + ctx.unzftab[ctx.seqToUnseq[uc2.toInt() and 0xff].toInt() and 0xff]++ 397 + Bzip2Context.tt!![nblock] = ctx.seqToUnseq[uc2.toInt() and 0xff].toInt() and 0xff 398 + nblock++ 399 + 400 + if (groupPos == 0) { 401 + groupNo++ 402 + groupPos = 50 403 + val gSel = ctx.selector[groupNo].toInt() and 0xff 404 + gMinLen = ctx.minLens[gSel] 405 + gLimit = ctx.limit[gSel] 406 + gPerm = ctx.perm[gSel] 407 + gBase = ctx.base[gSel] 408 + } 409 + groupPos-- 410 + zn = gMinLen 411 + zvec = getBits(zn, ctx) 412 + while (zvec > gLimit!![zn]) { 413 + zn++ 414 + zvec = (zvec shl 1) or getBit(ctx) 415 + } 416 + nextSym = gPerm!![zvec - gBase!![zn]] 417 + } 418 + } 419 + 420 + ctx.stateOutLen = 0 421 + ctx.stateOutCh = 0 422 + ctx.cftab[0] = 0 423 + ctx.unzftab.copyInto(ctx.cftab, destinationOffset = 1, startIndex = 0, endIndex = 256) 424 + 425 + for (i in 1..256) { 426 + ctx.cftab[i] += ctx.cftab[i - 1] 427 + } 428 + 429 + for (i in 0 until nblock) { 430 + val ch = Bzip2Context.tt!![i] and 0xff 431 + Bzip2Context.tt!![ctx.cftab[ch]] = Bzip2Context.tt!![ctx.cftab[ch]] or (i shl 8) 432 + ctx.cftab[ch]++ 433 + } 434 + 435 + ctx.tPos = Bzip2Context.tt!![ctx.origPtr] shr 8 436 + ctx.nBlockUsed = 0 437 + ctx.tPos = Bzip2Context.tt!![ctx.tPos] 438 + // Sign-extended byte: replicates Java's k0 = (byte)(tPos & 0xff) with auto-widening 439 + ctx.k0 = (ctx.tPos and 0xff).toByte().toInt() 440 + ctx.tPos = ctx.tPos shr 8 441 + ctx.nBlockUsed++ 442 + ctx.nBlock = nblock 443 + writeRunLengthOutput(ctx) 444 + } while (ctx.nBlockUsed == ctx.nBlock + 1 && ctx.stateOutLen == 0) 445 + } 446 + 447 + private fun getUChar(ctx: Bzip2Context): Int = getBits(8, ctx) 448 + 449 + private fun getBit(ctx: Bzip2Context): Int = getBits(1, ctx) 450 + 451 + private fun getBits(numBits: Int, ctx: Bzip2Context): Int { 452 + while (true) { 453 + if (ctx.bsLive >= numBits) { 454 + val k = (ctx.bsBuff shr (ctx.bsLive - numBits)) and ((1 shl numBits) - 1) 455 + ctx.bsLive -= numBits 456 + return k 457 + } 458 + ctx.bsBuff = (ctx.bsBuff shl 8) or (ctx.compressed[ctx.nextIn].toInt() and 0xff) 459 + ctx.bsLive += 8 460 + ctx.nextIn++ 461 + ctx.decompressedLength-- 462 + ctx.totalInLo32++ 463 + if (ctx.totalInLo32 == 0) ctx.totalInHi32++ 464 + } 465 + } 466 + 467 + private fun makeMaps(ctx: Bzip2Context) { 468 + ctx.nInUse = 0 469 + for (i in 0 until 256) { 470 + if (ctx.inUse[i]) { 471 + ctx.seqToUnseq[ctx.nInUse] = i.toByte() 472 + ctx.nInUse++ 473 + } 474 + } 475 + } 476 + 477 + private fun createDecodeTables( 478 + limit: IntArray, 479 + base: IntArray, 480 + perm: IntArray, 481 + len: ByteArray, 482 + minLen: Int, 483 + maxLen: Int, 484 + alphaSize: Int 485 + ) { 486 + var pp = 0 487 + for (i in minLen..maxLen) { 488 + for (j in 0 until alphaSize) { 489 + if (len[j].toInt() == i) { 490 + perm[pp] = j 491 + pp++ 492 + } 493 + } 494 + } 495 + 496 + for (i in 0 until 23) base[i] = 0 497 + 498 + for (i in 0 until alphaSize) base[len[i].toInt() + 1]++ 499 + 500 + for (i in 1 until 23) base[i] += base[i - 1] 501 + 502 + for (i in 0 until 23) limit[i] = 0 503 + 504 + var vec = 0 505 + for (i in minLen..maxLen) { 506 + vec += base[i + 1] - base[i] 507 + limit[i] = vec - 1 508 + vec = vec shl 1 509 + } 510 + 511 + for (i in minLen + 1..maxLen) { 512 + base[i] = ((limit[i - 1] + 1) shl 1) - base[i] 513 + } 514 + } 515 + }
-42
src/main/java/com/jagex/runescape/cache/bzip/Bzip2Context.java
··· 1 - package com.jagex.runescape.cache.bzip; 2 - 3 - public class Bzip2Context { 4 - public byte compressed[]; 5 - public int nextIn; 6 - public int decompressedLength; 7 - public int totalInLo32; 8 - public int totalInHi32; 9 - public byte buf[]; 10 - public int nextOut; 11 - public int availOut; 12 - public int totalOutLo32; 13 - public int totalOutHigh32; 14 - public byte stateOutCh; 15 - public int stateOutLen; 16 - public boolean blockRandomised; 17 - public int bsBuff; 18 - public int bsLive; 19 - public int blockSize100k; 20 - public int currentBlock; 21 - public int origPtr; 22 - public int tPos; 23 - public int k0; 24 - public int unzftab[] = new int[256]; 25 - public int nBlockUsed; 26 - public int cftab[] = new int[257]; 27 - public static int tt[]; 28 - public int nInUse; 29 - public boolean inUse[] = new boolean[256]; 30 - public boolean inUse16[] = new boolean[16]; 31 - public byte seqToUnseq[] = new byte[256]; 32 - public byte mtfa[] = new byte[4096]; 33 - public int mtfbase[] = new int[16]; 34 - public byte selector[] = new byte[18002]; 35 - public byte selectorMtf[] = new byte[18002]; 36 - public byte len[][] = new byte[6][258]; 37 - public int limit[][] = new int[6][258]; 38 - public int base[][] = new int[6][258]; 39 - public int perm[][] = new int[6][258]; 40 - public int minLens[] = new int[6]; 41 - public int nBlock; 42 - }
+45
src/main/java/com/jagex/runescape/cache/bzip/Bzip2Context.kt
··· 1 + package com.jagex.runescape.cache.bzip 2 + 3 + class Bzip2Context { 4 + @JvmField var compressed: ByteArray = ByteArray(0) 5 + @JvmField var nextIn: Int = 0 6 + @JvmField var decompressedLength: Int = 0 7 + @JvmField var totalInLo32: Int = 0 8 + @JvmField var totalInHi32: Int = 0 9 + @JvmField var buf: ByteArray = ByteArray(0) 10 + @JvmField var nextOut: Int = 0 11 + @JvmField var availOut: Int = 0 12 + @JvmField var totalOutLo32: Int = 0 13 + @JvmField var totalOutHigh32: Int = 0 14 + @JvmField var stateOutCh: Byte = 0 15 + @JvmField var stateOutLen: Int = 0 16 + @JvmField var blockRandomised: Boolean = false 17 + @JvmField var bsBuff: Int = 0 18 + @JvmField var bsLive: Int = 0 19 + @JvmField var blockSize100k: Int = 0 20 + @JvmField var currentBlock: Int = 0 21 + @JvmField var origPtr: Int = 0 22 + @JvmField var tPos: Int = 0 23 + @JvmField var k0: Int = 0 24 + @JvmField var unzftab: IntArray = IntArray(256) 25 + @JvmField var nBlockUsed: Int = 0 26 + @JvmField var cftab: IntArray = IntArray(257) 27 + @JvmField var nInUse: Int = 0 28 + @JvmField var inUse: BooleanArray = BooleanArray(256) 29 + @JvmField var inUse16: BooleanArray = BooleanArray(16) 30 + @JvmField var seqToUnseq: ByteArray = ByteArray(256) 31 + @JvmField var mtfa: ByteArray = ByteArray(4096) 32 + @JvmField var mtfbase: IntArray = IntArray(16) 33 + @JvmField var selector: ByteArray = ByteArray(18002) 34 + @JvmField var selectorMtf: ByteArray = ByteArray(18002) 35 + @JvmField var len: Array<ByteArray> = Array(6) { ByteArray(258) } 36 + @JvmField var limit: Array<IntArray> = Array(6) { IntArray(258) } 37 + @JvmField var base: Array<IntArray> = Array(6) { IntArray(258) } 38 + @JvmField var perm: Array<IntArray> = Array(6) { IntArray(258) } 39 + @JvmField var minLens: IntArray = IntArray(6) 40 + @JvmField var nBlock: Int = 0 41 + 42 + companion object { 43 + @JvmField var tt: IntArray? = null 44 + } 45 + }