using System; namespace Unity.IO.Compression { internal class Inflater { private static readonly byte[] extraLengthBits = new byte[29] { 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 static readonly int[] lengthBase = new int[29] { 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 readonly int[] distanceBasePosition = new int[32] { 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 static readonly byte[] codeOrder = new byte[19] { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; private static readonly byte[] staticDistanceTreeTable = new byte[32] { 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 OutputWindow output; private InputBuffer input; private HuffmanTree literalLengthTree; private HuffmanTree distanceTree; private InflaterState state; private bool hasFormatReader; private int bfinal; private BlockType blockType; private byte[] blockLengthBuffer = new byte[4]; private int blockLength; private int length; private int distanceCode; private int extraBits; private int loopCounter; private int literalLengthCodeCount; private int distanceCodeCount; private int codeLengthCodeCount; private int codeArraySize; private int lengthCode; private byte[] codeList; private byte[] codeLengthTreeCodeLength; private HuffmanTree codeLengthTree; private IFileFormatReader formatReader; public int AvailableOutput { get { return output.AvailableBytes; } } public Inflater() { output = new OutputWindow(); input = new InputBuffer(); codeList = new byte[320]; codeLengthTreeCodeLength = new byte[19]; Reset(); } internal void SetFileFormatReader(IFileFormatReader reader) { formatReader = reader; hasFormatReader = true; Reset(); } private void Reset() { if (hasFormatReader) { state = InflaterState.ReadingHeader; } else { state = InflaterState.ReadingBFinal; } } public void SetInput(byte[] inputBytes, int offset, int length) { input.SetInput(inputBytes, offset, length); } public bool Finished() { return state == InflaterState.Done || state == InflaterState.VerifyingFooter; } public bool NeedsInput() { return input.NeedsInput(); } public int Inflate(byte[] bytes, int offset, int length) { int num = 0; do { int num2 = output.CopyTo(bytes, offset, length); if (num2 > 0) { if (hasFormatReader) { formatReader.UpdateWithBytesRead(bytes, offset, num2); } offset += num2; num += num2; length -= num2; } } while (length != 0 && !Finished() && Decode()); if (state == InflaterState.VerifyingFooter && output.AvailableBytes == 0) { formatReader.Validate(); } return num; } private bool Decode() { bool end_of_block = false; bool flag = false; if (Finished()) { return true; } if (hasFormatReader) { if (state == InflaterState.ReadingHeader) { if (!formatReader.ReadHeader(input)) { return false; } state = InflaterState.ReadingBFinal; } else if (state == InflaterState.StartReadingFooter || state == InflaterState.ReadingFooter) { if (!formatReader.ReadFooter(input)) { return false; } state = InflaterState.VerifyingFooter; return true; } } if (state == InflaterState.ReadingBFinal) { if (!input.EnsureBitsAvailable(1)) { return false; } bfinal = input.GetBits(1); state = InflaterState.ReadingBType; } if (state == InflaterState.ReadingBType) { if (!input.EnsureBitsAvailable(2)) { state = InflaterState.ReadingBType; return false; } blockType = (BlockType)input.GetBits(2); if (blockType == BlockType.Dynamic) { state = InflaterState.ReadingNumLitCodes; } else if (blockType == BlockType.Static) { literalLengthTree = HuffmanTree.StaticLiteralLengthTree; distanceTree = HuffmanTree.StaticDistanceTree; state = InflaterState.DecodeTop; } else { if (blockType != BlockType.Uncompressed) { throw new InvalidDataException(SR.GetString("Unknown block type")); } state = InflaterState.UncompressedAligning; } } if (blockType == BlockType.Dynamic) { flag = ((state >= InflaterState.DecodeTop) ? DecodeBlock(out end_of_block) : DecodeDynamicBlockHeader()); } else if (blockType == BlockType.Static) { flag = DecodeBlock(out end_of_block); } else { if (blockType != BlockType.Uncompressed) { throw new InvalidDataException(SR.GetString("Unknown block type")); } flag = DecodeUncompressedBlock(out end_of_block); } if (end_of_block && bfinal != 0) { if (hasFormatReader) { state = InflaterState.StartReadingFooter; } else { state = InflaterState.Done; } } return flag; } private bool DecodeUncompressedBlock(out bool end_of_block) { end_of_block = false; while (true) { switch (state) { case InflaterState.UncompressedAligning: input.SkipToByteBoundary(); state = InflaterState.UncompressedByte1; goto case InflaterState.UncompressedByte1; case InflaterState.UncompressedByte1: case InflaterState.UncompressedByte2: case InflaterState.UncompressedByte3: case InflaterState.UncompressedByte4: { int bits = input.GetBits(8); if (bits < 0) { return false; } blockLengthBuffer[(int)(state - 16)] = (byte)bits; if (state == InflaterState.UncompressedByte4) { blockLength = blockLengthBuffer[0] + blockLengthBuffer[1] * 256; int num2 = blockLengthBuffer[2] + blockLengthBuffer[3] * 256; if ((ushort)blockLength != (ushort)(~num2)) { throw new InvalidDataException(SR.GetString("Invalid block length")); } } break; } case InflaterState.DecodingUncompressed: { int num = output.CopyFrom(input, blockLength); blockLength -= num; if (blockLength == 0) { state = InflaterState.ReadingBFinal; end_of_block = true; return true; } if (output.FreeBytes == 0) { return true; } return false; } default: throw new InvalidDataException(SR.GetString("Unknown state")); } state++; } } private bool DecodeBlock(out bool end_of_block_code_seen) { end_of_block_code_seen = false; int num = output.FreeBytes; while (num > 258) { switch (state) { case InflaterState.DecodeTop: { int nextSymbol = literalLengthTree.GetNextSymbol(input); if (nextSymbol < 0) { return false; } if (nextSymbol < 256) { output.Write((byte)nextSymbol); num--; break; } if (nextSymbol == 256) { end_of_block_code_seen = true; state = InflaterState.ReadingBFinal; return true; } nextSymbol -= 257; if (nextSymbol < 8) { nextSymbol += 3; extraBits = 0; } else if (nextSymbol == 28) { nextSymbol = 258; extraBits = 0; } else { if (nextSymbol < 0 || nextSymbol >= extraLengthBits.Length) { throw new InvalidDataException(SR.GetString("Invalid data")); } extraBits = extraLengthBits[nextSymbol]; } length = nextSymbol; goto case InflaterState.HaveInitialLength; } case InflaterState.HaveInitialLength: if (extraBits > 0) { state = InflaterState.HaveInitialLength; int bits2 = input.GetBits(extraBits); if (bits2 < 0) { return false; } if (length < 0 || length >= lengthBase.Length) { throw new InvalidDataException(SR.GetString("Invalid data")); } length = lengthBase[length] + bits2; } state = InflaterState.HaveFullLength; goto case InflaterState.HaveFullLength; case InflaterState.HaveFullLength: if (blockType == BlockType.Dynamic) { distanceCode = distanceTree.GetNextSymbol(input); } else { distanceCode = input.GetBits(5); if (distanceCode >= 0) { distanceCode = staticDistanceTreeTable[distanceCode]; } } if (distanceCode < 0) { return false; } state = InflaterState.HaveDistCode; goto case InflaterState.HaveDistCode; case InflaterState.HaveDistCode: { int distance; if (distanceCode > 3) { extraBits = distanceCode - 2 >> 1; int bits = input.GetBits(extraBits); if (bits < 0) { return false; } distance = distanceBasePosition[distanceCode] + bits; } else { distance = distanceCode + 1; } output.WriteLengthDistance(length, distance); num -= length; state = InflaterState.DecodeTop; break; } default: throw new InvalidDataException(SR.GetString("Unknown state")); } } return true; } private bool DecodeDynamicBlockHeader() { switch (state) { case InflaterState.ReadingNumLitCodes: literalLengthCodeCount = input.GetBits(5); if (literalLengthCodeCount < 0) { return false; } literalLengthCodeCount += 257; state = InflaterState.ReadingNumDistCodes; goto case InflaterState.ReadingNumDistCodes; case InflaterState.ReadingNumDistCodes: distanceCodeCount = input.GetBits(5); if (distanceCodeCount < 0) { return false; } distanceCodeCount++; state = InflaterState.ReadingNumCodeLengthCodes; goto case InflaterState.ReadingNumCodeLengthCodes; case InflaterState.ReadingNumCodeLengthCodes: codeLengthCodeCount = input.GetBits(4); if (codeLengthCodeCount < 0) { return false; } codeLengthCodeCount += 4; loopCounter = 0; state = InflaterState.ReadingCodeLengthCodes; goto case InflaterState.ReadingCodeLengthCodes; case InflaterState.ReadingCodeLengthCodes: { while (loopCounter < codeLengthCodeCount) { int bits = input.GetBits(3); if (bits < 0) { return false; } codeLengthTreeCodeLength[codeOrder[loopCounter]] = (byte)bits; loopCounter++; } for (int l = codeLengthCodeCount; l < codeOrder.Length; l++) { codeLengthTreeCodeLength[codeOrder[l]] = 0; } codeLengthTree = new HuffmanTree(codeLengthTreeCodeLength); codeArraySize = literalLengthCodeCount + distanceCodeCount; loopCounter = 0; state = InflaterState.ReadingTreeCodesBefore; goto case InflaterState.ReadingTreeCodesBefore; } case InflaterState.ReadingTreeCodesBefore: case InflaterState.ReadingTreeCodesAfter: { while (loopCounter < codeArraySize) { if (state == InflaterState.ReadingTreeCodesBefore && (lengthCode = codeLengthTree.GetNextSymbol(input)) < 0) { return false; } if (lengthCode <= 15) { codeList[loopCounter++] = (byte)lengthCode; } else { if (!input.EnsureBitsAvailable(7)) { state = InflaterState.ReadingTreeCodesAfter; return false; } if (lengthCode == 16) { if (loopCounter == 0) { throw new InvalidDataException(); } byte b = codeList[loopCounter - 1]; int num = input.GetBits(2) + 3; if (loopCounter + num > codeArraySize) { throw new InvalidDataException(); } for (int i = 0; i < num; i++) { codeList[loopCounter++] = b; } } else if (lengthCode == 17) { int num = input.GetBits(3) + 3; if (loopCounter + num > codeArraySize) { throw new InvalidDataException(); } for (int j = 0; j < num; j++) { codeList[loopCounter++] = 0; } } else { int num = input.GetBits(7) + 11; if (loopCounter + num > codeArraySize) { throw new InvalidDataException(); } for (int k = 0; k < num; k++) { codeList[loopCounter++] = 0; } } } state = InflaterState.ReadingTreeCodesBefore; } byte[] array = new byte[288]; byte[] array2 = new byte[32]; Array.Copy(codeList, array, literalLengthCodeCount); Array.Copy(codeList, literalLengthCodeCount, array2, 0, distanceCodeCount); if (array[256] == 0) { throw new InvalidDataException(); } literalLengthTree = new HuffmanTree(array); distanceTree = new HuffmanTree(array2); state = InflaterState.DecodeTop; return true; } default: throw new InvalidDataException(SR.GetString("Unknown state")); } } } }