namespace com.hitrust.util.Zip.Compression
|
|
{
|
|
using util;
|
|
using Checksums;
|
|
using Streams;
|
|
using System;
|
|
|
|
public class Inflater
|
|
{
|
|
private readonly Adler32 adler;
|
|
private static readonly int[] CPDEXT = new int[] {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
|
|
private static readonly int[] CPDIST = new int[] {1, 2, 3, 4, 5, 7, 9, 13, 0x11, 0x19, 0x21, 0x31, 0x41, 0x61, 0x81, 0xc1, 0x101, 0x181, 0x201, 0x301, 0x401, 0x601, 0x801, 0xc01, 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001};
|
|
private static readonly int[] CPLENS = new int[] {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 0x11, 0x13, 0x17, 0x1b, 0x1f, 0x23, 0x2b, 0x33, 0x3b, 0x43, 0x53, 0x63, 0x73, 0x83, 0xa3, 0xc3, 0xe3, 0x102};
|
|
private static readonly int[] CPLEXT = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
|
|
private const int DECODE_BLOCKS = 2;
|
|
private const int DECODE_CHKSUM = 11;
|
|
private const int DECODE_DICT = 1;
|
|
private const int DECODE_DYN_HEADER = 6;
|
|
private const int DECODE_HEADER = 0;
|
|
private const int DECODE_HUFFMAN = 7;
|
|
private const int DECODE_HUFFMAN_DIST = 9;
|
|
private const int DECODE_HUFFMAN_DISTBITS = 10;
|
|
private const int DECODE_HUFFMAN_LENBITS = 8;
|
|
private const int DECODE_STORED = 5;
|
|
private const int DECODE_STORED_LEN1 = 3;
|
|
private const int DECODE_STORED_LEN2 = 4;
|
|
private InflaterHuffmanTree distTree;
|
|
private InflaterDynHeader dynHeader;
|
|
private const int FINISHED = 12;
|
|
private readonly StreamManipulator input;
|
|
private bool isLastBlock;
|
|
private InflaterHuffmanTree litlenTree;
|
|
private int mode;
|
|
private int neededBits;
|
|
private readonly bool noHeader;
|
|
private readonly OutputWindow outputWindow;
|
|
private int readAdler;
|
|
private int repDist;
|
|
private int repLength;
|
|
private int totalIn;
|
|
private int totalOut;
|
|
private int uncomprLen;
|
|
|
|
public Inflater() : this(false) {}
|
|
|
|
public Inflater(bool noHeader)
|
|
{
|
|
this.noHeader = noHeader;
|
|
this.adler = new Adler32();
|
|
this.input = new StreamManipulator();
|
|
this.outputWindow = new OutputWindow();
|
|
this.mode = noHeader ? 2 : 0;
|
|
}
|
|
|
|
private bool Decode()
|
|
{
|
|
int num2;
|
|
int num3;
|
|
switch (this.mode) {
|
|
case 0:
|
|
return this.DecodeHeader();
|
|
|
|
case 1:
|
|
return this.DecodeDict();
|
|
|
|
case 2:
|
|
if (!this.isLastBlock) {
|
|
int num = this.input.PeekBits(3);
|
|
if (num < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(3);
|
|
if ((num & 1) != 0) {
|
|
this.isLastBlock = true;
|
|
}
|
|
switch ((num >> 1)) {
|
|
case 0:
|
|
this.input.SkipToByteBoundary();
|
|
this.mode = 3;
|
|
break;
|
|
|
|
case 1:
|
|
this.litlenTree = InflaterHuffmanTree.defLitLenTree;
|
|
this.distTree = InflaterHuffmanTree.defDistTree;
|
|
this.mode = 7;
|
|
break;
|
|
|
|
case 2:
|
|
this.dynHeader = new InflaterDynHeader();
|
|
this.mode = 6;
|
|
break;
|
|
}
|
|
throw new SharpZipBaseException("Unknown block type " + num);
|
|
}
|
|
if (!this.noHeader) {
|
|
this.input.SkipToByteBoundary();
|
|
this.neededBits = 0x20;
|
|
this.mode = 11;
|
|
return true;
|
|
}
|
|
this.mode = 12;
|
|
return false;
|
|
|
|
case 3:
|
|
this.uncomprLen = this.input.PeekBits(0x10);
|
|
if (this.uncomprLen >= 0) {
|
|
this.input.DropBits(0x10);
|
|
this.mode = 4;
|
|
goto Label_0163;
|
|
}
|
|
return false;
|
|
|
|
case 4:
|
|
goto Label_0163;
|
|
|
|
case 5:
|
|
goto Label_01A5;
|
|
|
|
case 6:
|
|
if (this.dynHeader.Decode(this.input)) {
|
|
this.litlenTree = this.dynHeader.BuildLitLenTree();
|
|
this.distTree = this.dynHeader.BuildDistTree();
|
|
this.mode = 7;
|
|
goto Label_0229;
|
|
}
|
|
return false;
|
|
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
goto Label_0229;
|
|
|
|
case 11:
|
|
return this.DecodeChksum();
|
|
|
|
case 12:
|
|
return false;
|
|
|
|
default:
|
|
throw new SharpZipBaseException("Inflater.Decode unknown mode");
|
|
}
|
|
Label_0163:
|
|
num2 = this.input.PeekBits(0x10);
|
|
if (num2 < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(0x10);
|
|
if (num2 != (this.uncomprLen ^ 0xffff)) {
|
|
throw new SharpZipBaseException("broken uncompressed block");
|
|
}
|
|
this.mode = 5;
|
|
Label_01A5:
|
|
num3 = this.outputWindow.CopyStored(this.input, this.uncomprLen);
|
|
this.uncomprLen -= num3;
|
|
if (this.uncomprLen == 0) {
|
|
this.mode = 2;
|
|
return true;
|
|
}
|
|
return !this.input.IsNeedingInput;
|
|
Label_0229:
|
|
return this.DecodeHuffman();
|
|
}
|
|
|
|
private bool DecodeChksum()
|
|
{
|
|
while (this.neededBits > 0) {
|
|
int num = this.input.PeekBits(8);
|
|
if (num < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(8);
|
|
this.readAdler = (this.readAdler << 8) | num;
|
|
this.neededBits -= 8;
|
|
}
|
|
if (((int)this.adler.Value) != this.readAdler) {
|
|
throw new SharpZipBaseException(string.Concat(new object[] {"Adler chksum doesn't match: ", (int)this.adler.Value, " vs. ", this.readAdler}));
|
|
}
|
|
this.mode = 12;
|
|
return false;
|
|
}
|
|
|
|
private bool DecodeDict()
|
|
{
|
|
while (this.neededBits > 0) {
|
|
int num = this.input.PeekBits(8);
|
|
if (num < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(8);
|
|
this.readAdler = (this.readAdler << 8) | num;
|
|
this.neededBits -= 8;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool DecodeHeader()
|
|
{
|
|
int num = this.input.PeekBits(0x10);
|
|
if (num < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(0x10);
|
|
num = ((num << 8) | (num >> 8)) & 0xffff;
|
|
if ((num % 0x1f) != 0) {
|
|
throw new SharpZipBaseException("Header checksum illegal");
|
|
}
|
|
if ((num & 0xf00) != (Deflater.DEFLATED << 8)) {
|
|
throw new SharpZipBaseException("Compression Method unknown");
|
|
}
|
|
if ((num & 0x20) == 0) {
|
|
this.mode = 2;
|
|
} else {
|
|
this.mode = 1;
|
|
this.neededBits = 0x20;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool DecodeHuffman()
|
|
{
|
|
int freeSpace = this.outputWindow.GetFreeSpace();
|
|
while (freeSpace >= 0x102) {
|
|
int num2;
|
|
switch (this.mode) {
|
|
case 7:
|
|
goto Label_0051;
|
|
|
|
case 8:
|
|
goto Label_00C5;
|
|
|
|
case 9:
|
|
goto Label_0114;
|
|
|
|
case 10:
|
|
goto Label_0154;
|
|
|
|
default:
|
|
throw new SharpZipBaseException("Inflater unknown mode");
|
|
}
|
|
Label_0037:
|
|
this.outputWindow.Write(num2);
|
|
if (--freeSpace < 0x102) {
|
|
return true;
|
|
}
|
|
Label_0051:
|
|
if (((num2 = this.litlenTree.GetSymbol(this.input)) & -256) == 0) {
|
|
goto Label_0037;
|
|
}
|
|
if (num2 < 0x101) {
|
|
if (num2 < 0) {
|
|
return false;
|
|
}
|
|
this.distTree = null;
|
|
this.litlenTree = null;
|
|
this.mode = 2;
|
|
return true;
|
|
}
|
|
try {
|
|
this.repLength = CPLENS[num2 - 0x101];
|
|
this.neededBits = CPLEXT[num2 - 0x101];
|
|
} catch (Exception) {
|
|
throw new SharpZipBaseException("Illegal rep length code");
|
|
}
|
|
Label_00C5:
|
|
if (this.neededBits > 0) {
|
|
this.mode = 8;
|
|
int num3 = this.input.PeekBits(this.neededBits);
|
|
if (num3 < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(this.neededBits);
|
|
this.repLength += num3;
|
|
}
|
|
this.mode = 9;
|
|
Label_0114:
|
|
num2 = this.distTree.GetSymbol(this.input);
|
|
if (num2 < 0) {
|
|
return false;
|
|
}
|
|
try {
|
|
this.repDist = CPDIST[num2];
|
|
this.neededBits = CPDEXT[num2];
|
|
} catch (Exception) {
|
|
throw new SharpZipBaseException("Illegal rep dist code");
|
|
}
|
|
Label_0154:
|
|
if (this.neededBits > 0) {
|
|
this.mode = 10;
|
|
int num4 = this.input.PeekBits(this.neededBits);
|
|
if (num4 < 0) {
|
|
return false;
|
|
}
|
|
this.input.DropBits(this.neededBits);
|
|
this.repDist += num4;
|
|
}
|
|
this.outputWindow.Repeat(this.repLength, this.repDist);
|
|
freeSpace -= this.repLength;
|
|
this.mode = 7;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public int Inflate(byte[] buf)
|
|
{
|
|
return this.Inflate(buf, 0, buf.Length);
|
|
}
|
|
|
|
public int Inflate(byte[] buf, int offset, int len)
|
|
{
|
|
if (len < 0) {
|
|
throw new ArgumentOutOfRangeException("len < 0");
|
|
}
|
|
if (len == 0) {
|
|
if (!this.IsFinished) {
|
|
this.Decode();
|
|
}
|
|
return 0;
|
|
}
|
|
int num = 0;
|
|
do {
|
|
if (this.mode != 11) {
|
|
int num2 = this.outputWindow.CopyOutput(buf, offset, len);
|
|
this.adler.Update(buf, offset, num2);
|
|
offset += num2;
|
|
num += num2;
|
|
this.totalOut += num2;
|
|
len -= num2;
|
|
if (len == 0) {
|
|
return num;
|
|
}
|
|
}
|
|
} while (this.Decode() || ((this.outputWindow.GetAvailable() > 0) && (this.mode != 11)));
|
|
return num;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
this.mode = this.noHeader ? 2 : 0;
|
|
this.totalIn = this.totalOut = 0;
|
|
this.input.Reset();
|
|
this.outputWindow.Reset();
|
|
this.dynHeader = null;
|
|
this.litlenTree = null;
|
|
this.distTree = null;
|
|
this.isLastBlock = false;
|
|
this.adler.Reset();
|
|
}
|
|
|
|
public void SetDictionary(byte[] buffer)
|
|
{
|
|
this.SetDictionary(buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public void SetDictionary(byte[] buffer, int offset, int len)
|
|
{
|
|
if (!this.IsNeedingDictionary) {
|
|
throw new InvalidOperationException();
|
|
}
|
|
this.adler.Update(buffer, offset, len);
|
|
if (((int)this.adler.Value) != this.readAdler) {
|
|
throw new SharpZipBaseException("Wrong adler checksum");
|
|
}
|
|
this.adler.Reset();
|
|
this.outputWindow.CopyDict(buffer, offset, len);
|
|
this.mode = 2;
|
|
}
|
|
|
|
public void SetInput(byte[] buf)
|
|
{
|
|
this.SetInput(buf, 0, buf.Length);
|
|
}
|
|
|
|
public void SetInput(byte[] buffer, int offset, int length)
|
|
{
|
|
this.input.SetInput(buffer, offset, length);
|
|
this.totalIn += length;
|
|
}
|
|
|
|
public int Adler
|
|
{
|
|
get
|
|
{
|
|
if (!this.IsNeedingDictionary) {
|
|
return (int)this.adler.Value;
|
|
}
|
|
return this.readAdler;
|
|
}
|
|
}
|
|
|
|
public bool IsFinished
|
|
{
|
|
get { return ((this.mode == 12) && (this.outputWindow.GetAvailable() == 0)); }
|
|
}
|
|
|
|
public bool IsNeedingDictionary
|
|
{
|
|
get { return ((this.mode == 1) && (this.neededBits == 0)); }
|
|
}
|
|
|
|
public bool IsNeedingInput
|
|
{
|
|
get { return this.input.IsNeedingInput; }
|
|
}
|
|
|
|
public int RemainingInput
|
|
{
|
|
get { return this.input.AvailableBytes; }
|
|
}
|
|
|
|
public int TotalIn
|
|
{
|
|
get { return (this.totalIn - this.RemainingInput); }
|
|
}
|
|
|
|
public int TotalOut
|
|
{
|
|
get { return this.totalOut; }
|
|
}
|
|
}
|
|
}
|