2021-05-07 22:05:11 -04:00

785 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using UniversalEditor.Compression.Common;
using UniversalEditor.Compression.Modules.Gzip.Internal;
namespace UniversalEditor.Compression.Modules.Deflate.Internal
{
internal class Inflater
{
private int bfinal;
private int blockLength;
private byte[] blockLengthBuffer = new byte[4];
private BlockType blockType;
private int codeArraySize;
private int codeLengthCodeCount;
private HuffmanTree codeLengthTree;
private byte[] codeLengthTreeCodeLength;
private byte[] codeList;
private static readonly byte[] codeOrder = new byte[]
{
16,
17,
18,
0,
8,
7,
9,
6,
10,
5,
11,
4,
12,
3,
13,
2,
14,
1,
15
};
private uint crc32;
private static readonly int[] distanceBasePosition = new int[]
{
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,
0,
0
};
private int distanceCode;
private int distanceCodeCount;
private HuffmanTree distanceTree;
private int extraBits;
private static readonly byte[] extraLengthBits = new byte[]
{
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 GzipDecoder gZipDecoder;
private InputBuffer input;
private int length;
private static readonly int[] lengthBase = new int[]
{
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 int lengthCode;
private int literalLengthCodeCount;
private HuffmanTree literalLengthTree;
private int loopCounter;
private OutputWindow output;
private InflaterState state;
private static readonly byte[] staticDistanceTreeTable = new byte[]
{
0,
16,
8,
24,
4,
20,
12,
28,
2,
18,
10,
26,
6,
22,
14,
30,
1,
17,
9,
25,
5,
21,
13,
29,
3,
19,
11,
27,
7,
23,
15,
31
};
private uint streamSize;
private bool using_gzip;
public int AvailableOutput
{
get
{
return this.output.AvailableBytes;
}
}
public Inflater(bool doGZip)
{
this.using_gzip = doGZip;
this.output = new OutputWindow();
this.input = new InputBuffer();
this.gZipDecoder = new GzipDecoder(this.input);
this.codeList = new byte[320];
this.codeLengthTreeCodeLength = new byte[19];
this.Reset();
}
private bool Decode()
{
bool flag = false;
bool flag2 = false;
bool result;
if (this.Finished())
{
result = true;
}
else
{
if (this.using_gzip)
{
if (this.state == InflaterState.ReadingGZIPHeader)
{
if (!this.gZipDecoder.ReadGzipHeader())
{
result = false;
return result;
}
this.state = InflaterState.ReadingBFinal;
}
else
{
if (this.state == InflaterState.StartReadingGZIPFooter || this.state == InflaterState.ReadingGZIPFooter)
{
if (!this.gZipDecoder.ReadGzipFooter())
{
result = false;
return result;
}
this.state = InflaterState.VerifyingGZIPFooter;
result = true;
return result;
}
}
}
if (this.state == InflaterState.ReadingBFinal)
{
if (!this.input.EnsureBitsAvailable(1))
{
result = false;
return result;
}
this.bfinal = this.input.GetBits(1);
this.state = InflaterState.ReadingBType;
}
if (this.state == InflaterState.ReadingBType)
{
if (!this.input.EnsureBitsAvailable(2))
{
this.state = InflaterState.ReadingBType;
result = false;
return result;
}
this.blockType = (BlockType)this.input.GetBits(2);
if (this.blockType != BlockType.Dynamic)
{
if (this.blockType != BlockType.Static)
{
if (this.blockType != BlockType.Uncompressed)
{
throw new System.IO.InvalidDataException("UnknownBlockType");
}
this.state = InflaterState.UncompressedAligning;
}
else
{
this.literalLengthTree = HuffmanTree.StaticLiteralLengthTree;
this.distanceTree = HuffmanTree.StaticDistanceTree;
this.state = InflaterState.DecodeTop;
}
}
else
{
this.state = InflaterState.ReadingNumLitCodes;
}
}
if (this.blockType == BlockType.Dynamic)
{
if (this.state < InflaterState.DecodeTop)
{
flag2 = this.DecodeDynamicBlockHeader();
}
else
{
flag2 = this.DecodeBlock(out flag);
}
}
else
{
if (this.blockType == BlockType.Static)
{
flag2 = this.DecodeBlock(out flag);
}
else
{
if (this.blockType != BlockType.Uncompressed)
{
throw new System.IO.InvalidDataException("UnknownBlockType");
}
flag2 = this.DecodeUncompressedBlock(out flag);
}
}
if (flag && this.bfinal != 0)
{
if (this.using_gzip)
{
this.state = InflaterState.StartReadingGZIPFooter;
result = flag2;
return result;
}
this.state = InflaterState.Done;
}
result = flag2;
}
return result;
}
private bool DecodeBlock(out bool end_of_block_code_seen)
{
end_of_block_code_seen = false;
int freeBytes = this.output.FreeBytes;
bool result;
while (freeBytes > 258)
{
switch (this.state)
{
case InflaterState.DecodeTop:
{
int nextSymbol = this.literalLengthTree.GetNextSymbol(this.input);
if (nextSymbol < 0)
{
result = false;
return result;
}
if (nextSymbol < 256)
{
this.output.Write((byte)nextSymbol);
freeBytes--;
continue;
}
if (nextSymbol == 256)
{
end_of_block_code_seen = true;
this.state = InflaterState.ReadingBFinal;
result = true;
return result;
}
nextSymbol -= 257;
if (nextSymbol < 8)
{
nextSymbol += 3;
this.extraBits = 0;
}
else
{
if (nextSymbol == 28)
{
nextSymbol = 258;
this.extraBits = 0;
}
else
{
if (nextSymbol >= Inflater.extraLengthBits.Length)
{
this.extraBits = 0;
}
else
{
this.extraBits = (int)Inflater.extraLengthBits[nextSymbol];
}
}
}
this.length = nextSymbol;
goto IL_141;
}
case InflaterState.HaveInitialLength:
{
goto IL_141;
}
case InflaterState.HaveFullLength:
{
goto IL_1A1;
}
case InflaterState.HaveDistCode:
{
break;
}
default:
{
throw new System.IO.InvalidDataException("UnknownState");
}
}
IL_228:
int num6;
if (this.distanceCode > 3)
{
this.extraBits = this.distanceCode - 2 >> 1;
int num5 = this.input.GetBits(this.extraBits);
if (num5 < 0)
{
result = false;
return result;
}
num6 = Inflater.distanceBasePosition[this.distanceCode] + num5;
}
else
{
num6 = this.distanceCode + 1;
}
this.output.WriteLengthDistance(this.length, num6);
freeBytes -= this.length;
this.state = InflaterState.DecodeTop;
continue;
IL_1A1:
if (this.blockType == BlockType.Dynamic)
{
this.distanceCode = this.distanceTree.GetNextSymbol(this.input);
}
else
{
this.distanceCode = this.input.GetBits(5);
if (this.distanceCode >= 0)
{
this.distanceCode = (int)Inflater.staticDistanceTreeTable[this.distanceCode];
}
}
if (this.distanceCode < 0)
{
result = false;
return result;
}
this.state = InflaterState.HaveDistCode;
goto IL_228;
IL_141:
if (this.extraBits > 0)
{
this.state = InflaterState.HaveInitialLength;
int bits = this.input.GetBits(this.extraBits);
if (bits < 0)
{
result = false;
return result;
}
this.length = Inflater.lengthBase[this.length] + bits;
}
this.state = InflaterState.HaveFullLength;
goto IL_1A1;
}
result = true;
return result;
}
private bool DecodeDynamicBlockHeader()
{
bool result;
switch (this.state)
{
case InflaterState.ReadingNumLitCodes:
{
this.literalLengthCodeCount = this.input.GetBits(5);
if (this.literalLengthCodeCount < 0)
{
result = false;
return result;
}
this.literalLengthCodeCount += 257;
this.state = InflaterState.ReadingNumDistCodes;
break;
}
case InflaterState.ReadingNumDistCodes:
{
break;
}
case InflaterState.ReadingNumCodeLengthCodes:
{
goto IL_CF;
}
case InflaterState.ReadingCodeLengthCodes:
{
goto IL_118;
}
case InflaterState.ReadingTreeCodesBefore:
case InflaterState.ReadingTreeCodesAfter:
{
goto IL_1D6;
}
default:
{
throw new System.IO.InvalidDataException("UnknownState");
}
}
this.distanceCodeCount = this.input.GetBits(5);
if (this.distanceCodeCount < 0)
{
result = false;
return result;
}
this.distanceCodeCount++;
this.state = InflaterState.ReadingNumCodeLengthCodes;
IL_CF:
this.codeLengthCodeCount = this.input.GetBits(4);
if (this.codeLengthCodeCount < 0)
{
result = false;
return result;
}
this.codeLengthCodeCount += 4;
this.loopCounter = 0;
this.state = InflaterState.ReadingCodeLengthCodes;
IL_118:
while (this.loopCounter < this.codeLengthCodeCount)
{
int bits = this.input.GetBits(3);
if (bits < 0)
{
result = false;
return result;
}
this.codeLengthTreeCodeLength[(int)Inflater.codeOrder[this.loopCounter]] = (byte)bits;
this.loopCounter++;
}
for (int i = this.codeLengthCodeCount; i < Inflater.codeOrder.Length; i++)
{
this.codeLengthTreeCodeLength[(int)Inflater.codeOrder[i]] = 0;
}
this.codeLengthTree = new HuffmanTree(this.codeLengthTreeCodeLength);
this.codeArraySize = this.literalLengthCodeCount + this.distanceCodeCount;
this.loopCounter = 0;
this.state = InflaterState.ReadingTreeCodesBefore;
IL_1D6:
while (this.loopCounter < this.codeArraySize)
{
if (this.state == InflaterState.ReadingTreeCodesBefore && (this.lengthCode = this.codeLengthTree.GetNextSymbol(this.input)) < 0)
{
result = false;
return result;
}
if (this.lengthCode <= 15)
{
int CS_0_3 = this.loopCounter++;
this.codeList[CS_0_3] = (byte)this.lengthCode;
}
else
{
if (!this.input.EnsureBitsAvailable(7))
{
this.state = InflaterState.ReadingTreeCodesAfter;
result = false;
return result;
}
if (this.lengthCode == 16)
{
if (this.loopCounter == 0)
{
throw new System.IO.InvalidDataException();
}
byte num4 = this.codeList[this.loopCounter - 1];
int num5 = this.input.GetBits(2) + 3;
if (this.loopCounter + num5 > this.codeArraySize)
{
throw new System.IO.InvalidDataException();
}
for (int j = 0; j < num5; j++)
{
int CS_0_3 = this.loopCounter++;
this.codeList[CS_0_3] = num4;
}
}
else
{
if (this.lengthCode == 17)
{
int num5 = this.input.GetBits(3) + 3;
if (this.loopCounter + num5 > this.codeArraySize)
{
throw new System.IO.InvalidDataException();
}
for (int k = 0; k < num5; k++)
{
int CS_0_3 = this.loopCounter++;
this.codeList[CS_0_3] = 0;
}
}
else
{
int num5 = this.input.GetBits(7) + 11;
if (this.loopCounter + num5 > this.codeArraySize)
{
throw new System.IO.InvalidDataException();
}
for (int l = 0; l < num5; l++)
{
int CS_0_3 = this.loopCounter++;
this.codeList[CS_0_3] = 0;
}
}
}
}
this.state = InflaterState.ReadingTreeCodesBefore;
}
byte[] destinationArray = new byte[288];
byte[] buffer2 = new byte[32];
Array.Copy(this.codeList, destinationArray, this.literalLengthCodeCount);
Array.Copy(this.codeList, this.literalLengthCodeCount, buffer2, 0, this.distanceCodeCount);
if (destinationArray[256] == 0)
{
throw new System.IO.InvalidDataException();
}
this.literalLengthTree = new HuffmanTree(destinationArray);
this.distanceTree = new HuffmanTree(buffer2);
this.state = InflaterState.DecodeTop;
result = true;
return result;
}
private bool DecodeUncompressedBlock(out bool end_of_block)
{
end_of_block = false;
while (true)
{
switch (this.state)
{
case InflaterState.UncompressedAligning:
{
this.input.SkipToByteBoundary();
this.state = InflaterState.UncompressedByte1;
goto IL_09;
}
case InflaterState.UncompressedByte1:
case InflaterState.UncompressedByte2:
case InflaterState.UncompressedByte3:
case InflaterState.UncompressedByte4:
{
goto IL_09;
}
case InflaterState.DecodingUncompressed:
{
goto IL_FD;
}
}
goto Block_4;
IL_09:
int bits = this.input.GetBits(8);
if (bits < 0)
{
break;
}
this.blockLengthBuffer[(int)(this.state - InflaterState.UncompressedByte1)] = (byte)bits;
if (this.state == InflaterState.UncompressedByte4)
{
this.blockLength = (int)this.blockLengthBuffer[0] + (int)this.blockLengthBuffer[1] * 256;
int num2 = (int)this.blockLengthBuffer[2] + (int)this.blockLengthBuffer[3] * 256;
if ((ushort)this.blockLength != (ushort)(~(ushort)num2))
{
goto Block_3;
}
}
this.state |= (InflaterState)1;
}
bool result = false;
return result;
Block_3:
throw new System.IO.InvalidDataException("InvalidBlockLength");
Block_4:
throw new System.IO.InvalidDataException("UnknownState");
IL_FD:
int num3 = this.output.CopyFrom(this.input, this.blockLength);
this.blockLength -= num3;
if (this.blockLength != 0)
{
result = (this.output.FreeBytes == 0);
}
else
{
this.state = InflaterState.ReadingBFinal;
end_of_block = true;
result = true;
}
return result;
}
public bool Finished()
{
return this.state == InflaterState.Done || this.state == InflaterState.VerifyingGZIPFooter;
}
public int Inflate(byte[] bytes, int offset, int length)
{
int num = 0;
while (true)
{
int num2 = this.output.CopyTo(bytes, offset, length);
if (num2 > 0)
{
if (this.using_gzip)
{
this.crc32 = DecodeHelper.UpdateCrc32(this.crc32, bytes, offset, num2);
uint num3 = this.streamSize + (uint)num2;
if (num3 < this.streamSize)
{
break;
}
this.streamSize = num3;
}
offset += num2;
num += num2;
length -= num2;
}
if (length == 0 || this.Finished() || !this.Decode())
{
goto Block_6;
}
}
throw new System.IO.InvalidDataException("StreamSizeOverflow");
Block_6:
if (this.state == InflaterState.VerifyingGZIPFooter && this.output.AvailableBytes == 0)
{
if (this.crc32 != this.gZipDecoder.Crc32)
{
throw new System.IO.InvalidDataException("InvalidCRC");
}
if (this.streamSize != this.gZipDecoder.StreamSize)
{
throw new System.IO.InvalidDataException("InvalidStreamSize");
}
}
return num;
}
public bool NeedsInput()
{
return this.input.NeedsInput();
}
public void Reset()
{
if (this.using_gzip)
{
this.gZipDecoder.Reset();
this.state = InflaterState.ReadingGZIPHeader;
this.streamSize = 0u;
this.crc32 = 0u;
}
else
{
this.state = InflaterState.ReadingBFinal;
}
}
public void SetInput(byte[] inputBytes, int offset, int length)
{
this.input.SetInput(inputBytes, offset, length);
}
}
internal enum InflaterState
{
DecodeTop = 10,
DecodingUncompressed = 20,
Done = 24,
HaveDistCode = 13,
HaveFullLength = 12,
HaveInitialLength = 11,
ReadingBFinal = 2,
ReadingBType,
ReadingCodeLengthCodes = 7,
ReadingGZIPFooter = 22,
ReadingGZIPHeader = 0,
ReadingNumCodeLengthCodes = 6,
ReadingNumDistCodes = 5,
ReadingNumLitCodes = 4,
ReadingTreeCodesAfter = 9,
ReadingTreeCodesBefore = 8,
StartReadingGZIPFooter = 21,
UncompressedAligning = 15,
UncompressedByte1,
UncompressedByte2,
UncompressedByte3,
UncompressedByte4,
VerifyingGZIPFooter = 23
}
}