All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.cabParser.decompress.zip.DecompressZip Maven / Gradle / Ivy

Go to download

Parse and extract data from inside Microsoft .cab files, specifically those compressed with LZX, for java.

There is a newer version: 3.4
Show newest version
/*
 * 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;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy