You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
12 KiB

/*
* Copyright 2012 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.cabParser.decompress.zip;
import dorkbox.cabParser.CabException;
import dorkbox.cabParser.CorruptCabException;
import dorkbox.cabParser.decompress.Decompressor;
public final class DecompressZip implements Decompressor {
private static final int[] ar1 = {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,
35,43,51,59, 67,83,99,115,131,163,195,227,258};
private static final int[] ar2 = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,
769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
private static final int[] ar3 = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
private byte[] bytes = new byte[320];
private byte[] inputBytes;
private byte[] outputBytes;
private int index;
private int inputPlus4;
int int1;
private int int2;
private int int3;
private int outputLength;
private DecompressZipState state1;
private DecompressZipState state2;
private DecompressZipState state3;
@Override
public void init(int windowBits) {
this.state1 = new DecompressZipState(288, 9, this);
this.state2 = new DecompressZipState(32, 7, this);
this.state3 = new DecompressZipState(19, 7, this);
}
@Override
public void decompress(byte[] inputBytes, byte[] outputBytes, int inputLength, int outputLength) throws CabException {
this.inputBytes = inputBytes;
this.outputBytes = outputBytes;
if (this.inputBytes[0] != 67 || this.inputBytes[1] != 75) {
throw new CorruptCabException();
}
if (outputBytes.length < 33027) {
throw new CabException();
}
if (inputBytes.length < 28) {
throw new CabException();
}
this.index = 2;
this.inputPlus4 = inputLength + 4;
this.outputLength = outputLength;
this.int3 = 0;
maybeDecompress();
while (this.int3 < this.outputLength) {
decompressMore();
}
}
@Override
public int getMaxGrowth() {
return 28;
}
@Override
public void reset(int windowBits) {
}
private void maybeDecompress() throws CabException {
if (this.index + 4 > this.inputPlus4) {
throw new CorruptCabException();
}
this.int1 = readShort() | readShort() << 16;
this.int2 = 16;
}
void add(int paramInt) {
this.int1 >>>= paramInt;
this.int2 -= paramInt;
if (this.int2 <= 0) {
this.int2 += 16;
this.int1 |= (this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8) << this.int2;
this.index += 2;
}
}
private void check() throws CabException {
if (this.index > this.inputPlus4) {
throw new CorruptCabException();
}
}
@SuppressWarnings({"NumericCastThatLosesPrecision", "Duplicates"})
private void bits() throws CabException {
int i = this.int3;
int j = this.outputLength;
byte[] arrayOfByte1 = this.outputBytes;
int[] arrayOfInt1 = this.state1.intA2;
int[] arrayOfInt2 = this.state1.intA3;
int[] arrayOfInt3 = this.state1.intA4;
byte[] arrayOfByte2 = this.state1.byteA;
int[] arrayOfInt4 = this.state2.intA2;
int[] arrayOfInt5 = this.state2.intA3;
int[] arrayOfInt6 = this.state2.intA4;
byte[] arrayOfByte3 = this.state2.byteA;
int k = this.int1;
int m = this.int2;
do {
if (this.index > this.inputPlus4) {
break;
}
int n = arrayOfInt1[k & 0x1FF];
int i2;
while (n < 0) {
i2 = 512;
do {
n = -n;
if ((k & i2) == 0) {
n = arrayOfInt2[n];
} else {
n = arrayOfInt3[n];
}
i2 <<= 1;
} while (n < 0);
}
int i1 = arrayOfByte2[n];
k >>>= i1;
m -= i1;
if (m <= 0) {
m += 16;
k |= (this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8) << m;
this.index += 2;
}
if (n < 256) {
arrayOfByte1[i++] = (byte) n;
} else {
n -= 257;
if (n < 0) {
break;
}
if (n < 8) {
n += 3;
} else if (n != 28) {
int i4 = n - 4 >>> 2;
n = ar1[n] + (k & (1 << i4) - 1);
k >>>= i4;
m -= i4;
if (m <= 0) {
m += 16;
k |= (this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8) << m;
this.index += 2;
}
} else {
n = 258;
}
i2 = arrayOfInt4[k & 0x7F];
while (i2 < 0) {
int i5 = 128;
do {
i2 = -i2;
if ((k & i5) == 0) {
i2 = arrayOfInt5[i2];
} else {
i2 = arrayOfInt6[i2];
}
i5 <<= 1;
} while (i2 < 0);
}
i1 = arrayOfByte3[i2];
k >>>= i1;
m -= i1;
if (m <= 0) {
m += 16;
k |= (this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8) << m;
this.index += 2;
}
int i4 = i2 - 2 >> 1;
int i3;
if (i4 > 0) {
i3 = ar2[i2] + (k & (1 << i4) - 1);
k >>>= i4;
m -= i4;
if (m <= 0) {
m += 16;
k |= (this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8) << m;
this.index += 2;
}
} else {
i3 = i2 + 1;
}
do {
arrayOfByte1[i] = arrayOfByte1[i - i3 & 0x7FFF];
i++;
n--;
} while (n != 0);
}
} while (i <= j);
this.int3 = i;
this.int1 = k;
this.int2 = m;
check();
}
private void readStuffcommon() throws CabException {
this.state1.main();
this.state2.main();
}
private void setup() {
int i = 0;
do {
this.state1.byteA[i] = (byte) 8;
i++;
} while (i <= 143);
i = 144;
do {
this.state1.byteA[i] = (byte) 9;
i++;
} while (i <= 255);
i = 256;
do {
this.state1.byteA[i] = (byte) 7;
i++;
} while (i <= 279);
i = 280;
do {
this.state1.byteA[i] = (byte) 8;
i++;
} while (i <= 287);
i = 0;
do {
this.state2.byteA[i] = (byte) 5;
i++;
} while (i < 32);
}
private int makeShort(byte byte1, byte byte2) {
return byte1 & 0xFF | (byte2 & 0xFF) << 8;
}
@SuppressWarnings("NumericCastThatLosesPrecision")
private void expand() throws CabException {
check();
int i = calc(5) + 257;
int j = calc(5) + 1;
int k = calc(4) + 4;
for (int n = 0; n < k; n++) {
this.state3.byteA[ar3[n]] = (byte) calc(3);
check();
}
for (int n = k; n < ar3.length; n++) {
this.state3.byteA[ar3[n]] = (byte) 0;
}
this.state3.main();
int m = i + j;
int n = 0;
while (n < m) {
check();
int i1 = (byte) this.state3.read();
if (i1 <= 15) {
this.bytes[n++] = (byte) i1;
} else {
int i3;
int i2;
if (i1 == 16) {
if (n == 0) {
throw new CorruptCabException();
}
i3 = this.bytes[n - 1];
i2 = calc(2) + 3;
if (n + i2 > m) {
throw new CorruptCabException();
}
for (int i4 = 0; i4 < i2; i4++) {
this.bytes[n++] = (byte) i3;
}
} else if (i1 == 17) {
i2 = calc(3) + 3;
if (n + i2 > m) {
throw new CorruptCabException();
}
for (i3 = 0; i3 < i2; i3++) {
this.bytes[n++] = (byte) 0;
}
} else {
i2 = calc(7) + 11;
if (n + i2 > m) {
throw new CorruptCabException();
}
for (i3 = 0; i3 < i2; i3++) {
this.bytes[n++] = (byte) 0;
}
}
}
}
System.arraycopy(this.bytes, 0, this.state1.byteA, 0, i);
for (n = i; n < 288; n++) {
this.state1.byteA[n] = (byte) 0;
}
for (n = 0; n < j; n++) {
this.state2.byteA[n] = this.bytes[n + i];
}
for (n = j; n < 32; n++) {
this.state2.byteA[n] = (byte) 0;
}
if (this.state1.byteA[256] == 0) {
throw new CorruptCabException();
}
}
private int readShort() {
int i = this.inputBytes[this.index] & 0xFF | (this.inputBytes[this.index + 1] & 0xFF) << 8;
this.index += 2;
return i;
}
private int calc(int paramInt) {
int i = this.int1 & (1 << paramInt) - 1;
add(paramInt);
return i;
}
private void decompressMore() throws CabException {
@SuppressWarnings("unused")
int i = calc(1);
int j = calc(2);
if (j == 2) {
expand();
readStuffcommon();
bits();
return;
}
if (j == 1) {
setup();
readStuffcommon();
bits();
return;
}
if (j == 0) {
verify();
return;
}
throw new CabException();
}
private void verify() throws CabException {
mod();
if (this.index >= this.inputPlus4) {
throw new CorruptCabException();
}
int i = makeShort(this.inputBytes[this.index], this.inputBytes[this.index + 1]);
int j = makeShort(this.inputBytes[this.index + 2], this.inputBytes[this.index + 3]);
//noinspection NumericCastThatLosesPrecision
if ((short) i != (short) (~j)) {
throw new CorruptCabException();
}
if (this.index + i > this.inputPlus4 || this.int3 + i > this.outputLength) {
throw new CorruptCabException();
}
maybeDecompress();
System.arraycopy(this.inputBytes, this.index, this.outputBytes, this.int3, i);
this.int3 += i;
if (this.int3 < this.outputLength) {
maybeDecompress();
}
}
private void mod() {
if (this.int2 == 16) {
this.index -= 4;
} else if (this.int2 >= 8) {
this.index -= 3;
} else {
this.index -= 2;
}
if (this.index < 0) {
this.index = 0;
}
this.int2 = 0;
}
}