diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.LZX.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.LZX.cs new file mode 100644 index 00000000..1045ae73 --- /dev/null +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.LZX.cs @@ -0,0 +1,7 @@ +namespace BurnOutSharp.Wrappers +{ + public partial class MicrosoftCabinet : WrapperBase + { + // TODO: Implement LZX decompression + } +} \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.MSZIP.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.MSZIP.cs new file mode 100644 index 00000000..f810adda --- /dev/null +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.MSZIP.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections.Generic; +using BurnOutSharp.Utilities; + +namespace BurnOutSharp.Wrappers +{ + public partial class MicrosoftCabinet : WrapperBase + { + #region Constants + + /// + /// Maximum Huffman code bit count + /// + private const int MAX_BITS = 16; + + #endregion + + #region Properties + + /// + /// Match lengths for literal codes 257..285 + /// + /// Each value here is the lower bound for lengths represented + private static Dictionary LiteralLengths + { + get + { + // If we have cached length mappings, use those + if (_literalLengths != null) + return _literalLengths; + + // Otherwise, build it from scratch + _literalLengths = new Dictionary + { + [257] = 3, + [258] = 4, + [259] = 5, + [260] = 6, + [261] = 7, + [262] = 8, + [263] = 9, + [264] = 10, + [265] = 11, // 11,12 + [266] = 13, // 13,14 + [267] = 15, // 15,16 + [268] = 17, // 17,18 + [269] = 19, // 19-22 + [270] = 23, // 23-26 + [271] = 27, // 27-30 + [272] = 31, // 31-34 + [273] = 35, // 35-42 + [274] = 43, // 43-50 + [275] = 51, // 51-58 + [276] = 59, // 59-66 + [277] = 67, // 67-82 + [278] = 83, // 83-98 + [279] = 99, // 99-114 + [280] = 115, // 115-130 + [281] = 131, // 131-162 + [282] = 163, // 163-194 + [283] = 195, // 195-226 + [284] = 227, // 227-257 + [285] = 258, + }; + + return _literalLengths; + } + } + + /// + /// Extra bits for literal codes 257..285 + /// + private static Dictionary LiteralExtraBits + { + get + { + // If we have cached bit mappings, use those + if (_literalExtraBits != null) + return _literalExtraBits; + + // Otherwise, build it from scratch + _literalExtraBits = new Dictionary(); + + // Literal Value 257 - 264, 0 bits + for (int i = 257; i < 265; i++) + _literalExtraBits[i] = 0; + + // Literal Value 265 - 268, 1 bit + for (int i = 265; i < 269; i++) + _literalExtraBits[i] = 1; + + // Literal Value 269 - 272, 2 bits + for (int i = 269; i < 273; i++) + _literalExtraBits[i] = 2; + + // Literal Value 273 - 276, 3 bits + for (int i = 273; i < 277; i++) + _literalExtraBits[i] = 3; + + // Literal Value 277 - 280, 4 bits + for (int i = 277; i < 281; i++) + _literalExtraBits[i] = 4; + + // Literal Value 281 - 284, 5 bits + for (int i = 281; i < 285; i++) + _literalExtraBits[i] = 5; + + // Literal Value 285, 0 bits + _literalExtraBits[285] = 0; + + return _literalExtraBits; + } + } + + /// + /// Match offsets for distance codes 0..29 + /// + /// Each value here is the lower bound for lengths represented + public static readonly int[] DistanceOffsets = new int[30] + { + 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, + }; + + /// + /// Extra bits for distance codes 0..29 + /// + private static readonly int[] DistanceExtraBits = new int[30] + { + 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, + }; + + /// + /// The order of the bit length Huffman code lengths + /// + private static readonly int[] BitLengthOrder = new int[19] + { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, + }; + + #endregion + + #region Instance Variables + + /// + /// Match lengths for literal codes 257..285 + /// + private static Dictionary _literalLengths = null; + + /// + /// Extra bits for literal codes 257..285 + /// + private static Dictionary _literalExtraBits = null; + + #endregion + + #region Parsing + + /// + /// Read the block header from the block data, if possible + /// + /// BitStream representing the block + /// Offset within the array to parse + /// Filled block header on success, null on error + private static Models.MicrosoftCabinet.MSZIP.BlockHeader AsBlockHeader(BitStream data) + { + // If the data is invalid + if (data == null) + return null; + + var header = new Models.MicrosoftCabinet.MSZIP.BlockHeader(); + + header.Signature = data.ReadAlignedUInt16(); + if (header.Signature != 0x4B43) + return null; + + return header; + } + + /// + /// Read the deflate block header from the block data, if possible + /// + /// Byte array representing the block + /// Offset within the array to parse + /// Filled deflate block header on success, null on error + private static Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader AsDeflateBlockHeader(BitStream data) + { + // If the data is invalid + if (data == null) + return null; + + var header = new Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader(); + + header.BFINAL = data.ReadBits(1)[0]; + header.BTYPE = (Models.MicrosoftCabinet.DeflateCompressionType)data.ReadBits(2).AsByte(); + + return header; + } + + /// + /// Read the block header from the block data, if possible + /// + /// Byte array representing the block + /// Offset within the array to parse + /// Filled dynamic Huffman compressed block header on success, null on error + private static Models.MicrosoftCabinet.MSZIP.DynamicHuffmanCompressedBlockHeader AsDynamicHuffmanCompressedBlockHeader(BitStream data) + { + // If the data is invalid + if (data == null) + return null; + + var header = new Models.MicrosoftCabinet.MSZIP.DynamicHuffmanCompressedBlockHeader(); + + // # of Literal/Length codes - 257 + ushort HLIT = (ushort)(data.ReadBits(5).AsUInt16() + 257); + + // # of Distance codes - 1 + byte HDIST = (byte)(data.ReadBits(5).AsByte() + 1); + + // HCLEN, # of Code Length codes - 4 + byte HCLEN = (byte)(data.ReadBits(4).AsByte() + 4); + + // (HCLEN + 4) x 3 bits: code lengths for the code length + // alphabet given just above + // + // These code lengths are interpreted as 3-bit integers + // (0-7); as above, a code length of 0 means the + // corresponding symbol (literal/ length or distance code + // length) is not used. + int[] bitLengths = new int[19]; + for (ulong i = 0; i < HCLEN; i++) + bitLengths[BitLengthOrder[i]] = data.ReadBits(3).AsByte(); + + // Code length Huffman code + int[] bitLengthTable = CreateTable(bitLengths); + + // HLIT + 257 code lengths for the literal/length alphabet, + // encoded using the code length Huffman code + header.LiteralLengths = BuildHuffmanTree(data, HLIT, bitLengthTable); + + // HDIST + 1 code lengths for the distance alphabet, + // encoded using the code length Huffman code + header.DistanceCodes = BuildHuffmanTree(data, HDIST, bitLengthTable); + + return header; + } + + /// + /// Read the block header from the block data, if possible + /// + /// Byte array representing the block + /// Offset within the array to parse + /// Filled non-compressed block header on success, null on error + private static Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader AsNonCompressedBlockHeader(BitStream data) + { + // If the data is invalid + if (data == null) + return null; + + var header = new Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader(); + + header.LEN = data.ReadAlignedUInt16(); + header.NLEN = data.ReadAlignedUInt16(); + // TODO: Confirm NLEN is 1's compliment of LEN + + return header; + } + + #endregion + + #region Helpers + + /// + /// The alphabet for code lengths is as follows + /// + private static int[] BuildHuffmanTree(BitStream data, ushort codeCount, int[] codeLengths) + { + // Setup the huffman tree + int[] tree = new int[codeCount]; + + // Setup the loop variables + int lastCode = 0, repeatLength = 0; + for (ulong i = 0; i < codeCount; i++) + { + int codeLength = codeLengths[data.ReadBits(7).AsUInt16()]; + if (codeLengths[codeLength] > 7) + _ = data.ReadBits(codeLengths[codeLength] - 7); + + // Represent code lengths of 0 - 15 + if (codeLength > 0 && codeLength <= 15) + { + lastCode = codeLength; + tree[i] = codeLength; + } + + // Copy the previous code length 3 - 6 times. + // The next 2 bits indicate repeat length (0 = 3, ... , 3 = 6) + // Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will expand to 12 code lengths of 8 (1 + 6 + 5) + else if (codeLength == 16) + { + repeatLength = data.ReadBits(2).AsByte(); + repeatLength += 2; + codeLength = lastCode; + } + + // Repeat a code length of 0 for 3 - 10 times. + // (3 bits of length) + else if (codeLength == 17) + { + repeatLength = data.ReadBits(3).AsByte(); + repeatLength += 3; + codeLength = 0; + } + + // Repeat a code length of 0 for 11 - 138 times + // (7 bits of length) + else if (codeLength == 18) + { + repeatLength = data.ReadBits(7).AsByte(); + repeatLength += 11; + codeLength = 0; + } + + // Everything else + else + { + throw new ArgumentOutOfRangeException(); + } + + // If we had a repeat length + for (; repeatLength > 0; repeatLength--) + { + tree[i++] = codeLength; + } + } + + return tree; + } + + /// + /// Given this rule, we can define the Huffman code for an alphabet + /// just by giving the bit lengths of the codes for each symbol of + /// the alphabet in order; this is sufficient to determine the + /// actual codes. In our example, the code is completely defined + /// by the sequence of bit lengths (2, 1, 3, 3). The following + /// algorithm generates the codes as integers, intended to be read + /// from most- to least-significant bit. The code lengths are + /// initially in tree[I].Len; the codes are produced in + /// tree[I].Code. + /// + private static int[] CreateTable(int[] lengths) + { + // Count the number of codes for each code length. Let + // bl_count[N] be the number of codes of length N, N >= 1. + int[] bl_count = new int[259]; + for (int i = 0; i < lengths.Length; i++) + { + bl_count[lengths[i]]++; + } + + // Find the numerical value of the smallest code for each + // code length. + int[] next_code = new int[MAX_BITS + 1]; + int code = 0; + bl_count[0] = 0; + for (int bits = 1; bits <= MAX_BITS; bits++) + { + code = (code + bl_count[bits - 1]) << 1; + next_code[bits] = code; + } + + // Assign numerical values to all codes, using consecutive + // values for all codes of the same length with the base + // values determined at step 2. Codes that are never used + // (which have a bit length of zero) must not be assigned a + // value. + int[] distances = new int[lengths.Length]; + for (int n = 0; n < lengths.Length; n++) + { + int len = lengths[n]; + if (len != 0) + { + distances[n] = next_code[len]; + next_code[len]++; + } + } + + return distances; + } + + #endregion + + #region Folders + + /// + /// Decompress MSZIP data + /// + private byte[] DecompressMSZIPData(byte[] data) + { + // Create the bitstream to read from + var dataStream = new BitStream(data); + + // Get the block header + var blockHeader = AsBlockHeader(dataStream); + if (blockHeader == null) + return null; + + // Create the output byte array + List decodedBytes = new List(); + + // Create the loop variable block + Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader deflateBlockHeader; + + do + { + deflateBlockHeader = AsDeflateBlockHeader(dataStream); + + // We should never get a reserved block + if (deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.Reserved) + throw new Exception(); + + // If stored with no compression + if (deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.NoCompression) + { + // Skip any remaining bits in current partially processed byte + dataStream.DiscardBuffer(); + + // Read the block header + deflateBlockHeader.BlockDataHeader = AsNonCompressedBlockHeader(dataStream); + + // Copy LEN bytes of data to output + var header = deflateBlockHeader.BlockDataHeader as Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader; + ushort length = header.LEN; + decodedBytes.AddRange(dataStream.ReadAlignedBytes(length)); + } + + // Otherwise + else + { + // If compressed with dynamic Huffman codes + // read representation of code trees + deflateBlockHeader.BlockDataHeader = deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.DynamicHuffman + ? (Models.MicrosoftCabinet.MSZIP.IBlockDataHeader)AsDynamicHuffmanCompressedBlockHeader(dataStream) + : (Models.MicrosoftCabinet.MSZIP.IBlockDataHeader)new Models.MicrosoftCabinet.MSZIP.FixedHuffmanCompressedBlockHeader(); + + var header = deflateBlockHeader.BlockDataHeader as Models.MicrosoftCabinet.MSZIP.CompressedBlockHeader; + + // 9 bits per entry, 288 max symbols + int[] literalDecodeTable = CreateTable(header.LiteralLengths); + + // 6 bits per entry, 32 max symbols + int[] distanceDecodeTable = CreateTable(header.DistanceCodes); + + // Loop until end of block code recognized + while (true) + { + // Decode literal/length value from input stream + int symbol = literalDecodeTable[dataStream.ReadBits(9).AsUInt16()]; + + // Copy value (literal byte) to output stream + if (symbol < 256) + { + decodedBytes.Add((byte)symbol); + } + // End of block (256) + else if (symbol == 256) + { + break; + } + else + { + // Decode distance from input stream + ulong length = dataStream.ReadBits(LiteralExtraBits[symbol]).AsUInt64(); + length += (ulong)LiteralLengths[symbol]; + + int code = distanceDecodeTable[length]; + + ulong distance = dataStream.ReadBits(DistanceExtraBits[code]).AsUInt64(); + distance += (ulong)DistanceOffsets[code]; + + + // Move backwards distance bytes in the output + // stream, and copy length bytes from this + // position to the output stream. + } + } + } + } while (!deflateBlockHeader.BFINAL); + + /* + Note that a duplicated string reference may refer to a string + in a previous block; i.e., the backward distance may cross one + or more block boundaries. However a distance cannot refer past + the beginning of the output stream. (An application using a + preset dictionary might discard part of the output stream; a + distance can refer to that part of the output stream anyway) + Note also that the referenced string may overlap the current + position; for example, if the last 2 bytes decoded have values + X and Y, a string reference with + adds X,Y,X,Y,X to the output stream. + */ + + return decodedBytes.ToArray(); + } + + #endregion + } +} diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.Quantum.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.Quantum.cs new file mode 100644 index 00000000..858ef91a --- /dev/null +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.Quantum.cs @@ -0,0 +1,7 @@ +namespace BurnOutSharp.Wrappers +{ + public partial class MicrosoftCabinet : WrapperBase + { + // TODO: Implement Quantum decompression + } +} \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs index 93a4ecec..e541c006 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs @@ -2,11 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using BurnOutSharp.Utilities; namespace BurnOutSharp.Wrappers { - public class MicrosoftCabinet : WrapperBase + public partial class MicrosoftCabinet : WrapperBase { #region Pass-Through Properties @@ -194,413 +193,6 @@ namespace BurnOutSharp.Wrappers #endregion - #region Compression - - #region LZX - - // TODO: Implement LZX decompression - - #endregion - - #region MSZIP - - #region Constants - - /// - /// Maximum Huffman code bit count - /// - private const int MAX_BITS = 16; - - #endregion - - #region Properties - - /// - /// Match lengths for literal codes 257..285 - /// - /// Each value here is the lower bound for lengths represented - private static Dictionary LiteralLengths - { - get - { - // If we have cached length mappings, use those - if (_literalLengths != null) - return _literalLengths; - - // Otherwise, build it from scratch - _literalLengths = new Dictionary - { - [257] = 3, - [258] = 4, - [259] = 5, - [260] = 6, - [261] = 7, - [262] = 8, - [263] = 9, - [264] = 10, - [265] = 11, // 11,12 - [266] = 13, // 13,14 - [267] = 15, // 15,16 - [268] = 17, // 17,18 - [269] = 19, // 19-22 - [270] = 23, // 23-26 - [271] = 27, // 27-30 - [272] = 31, // 31-34 - [273] = 35, // 35-42 - [274] = 43, // 43-50 - [275] = 51, // 51-58 - [276] = 59, // 59-66 - [277] = 67, // 67-82 - [278] = 83, // 83-98 - [279] = 99, // 99-114 - [280] = 115, // 115-130 - [281] = 131, // 131-162 - [282] = 163, // 163-194 - [283] = 195, // 195-226 - [284] = 227, // 227-257 - [285] = 258, - }; - - return _literalLengths; - } - } - - /// - /// Extra bits for literal codes 257..285 - /// - private static Dictionary LiteralExtraBits - { - get - { - // If we have cached bit mappings, use those - if (_literalExtraBits != null) - return _literalExtraBits; - - // Otherwise, build it from scratch - _literalExtraBits = new Dictionary(); - - // Literal Value 257 - 264, 0 bits - for (int i = 257; i < 265; i++) - _literalExtraBits[i] = 0; - - // Literal Value 265 - 268, 1 bit - for (int i = 265; i < 269; i++) - _literalExtraBits[i] = 1; - - // Literal Value 269 - 272, 2 bits - for (int i = 269; i < 273; i++) - _literalExtraBits[i] = 2; - - // Literal Value 273 - 276, 3 bits - for (int i = 273; i < 277; i++) - _literalExtraBits[i] = 3; - - // Literal Value 277 - 280, 4 bits - for (int i = 277; i < 281; i++) - _literalExtraBits[i] = 4; - - // Literal Value 281 - 284, 5 bits - for (int i = 281; i < 285; i++) - _literalExtraBits[i] = 5; - - // Literal Value 285, 0 bits - _literalExtraBits[285] = 0; - - return _literalExtraBits; - } - } - - /// - /// Match offsets for distance codes 0..29 - /// - /// Each value here is the lower bound for lengths represented - public static readonly int[] DistanceOffsets = new int[30] - { - 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, - }; - - /// - /// Extra bits for distance codes 0..29 - /// - private static readonly int[] DistanceExtraBits = new int[30] - { - 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, - }; - - /// - /// The order of the bit length Huffman code lengths - /// - private static readonly int[] BitLengthOrder = new int[19] - { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15, - }; - - #endregion - - #region Instance Variables - - /// - /// Match lengths for literal codes 257..285 - /// - private static Dictionary _literalLengths = null; - - /// - /// Extra bits for literal codes 257..285 - /// - private static Dictionary _literalExtraBits = null; - - #endregion - - #region Parsing - - /// - /// Read the block header from the block data, if possible - /// - /// BitStream representing the block - /// Offset within the array to parse - /// Filled block header on success, null on error - private static Models.MicrosoftCabinet.MSZIP.BlockHeader AsBlockHeader(BitStream data) - { - // If the data is invalid - if (data == null) - return null; - - var header = new Models.MicrosoftCabinet.MSZIP.BlockHeader(); - - header.Signature = data.ReadAlignedUInt16(); - if (header.Signature != 0x4B43) - return null; - - return header; - } - - /// - /// Read the deflate block header from the block data, if possible - /// - /// Byte array representing the block - /// Offset within the array to parse - /// Filled deflate block header on success, null on error - private static Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader AsDeflateBlockHeader(BitStream data) - { - // If the data is invalid - if (data == null) - return null; - - var header = new Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader(); - - header.BFINAL = data.ReadBits(1)[0]; - header.BTYPE = (Models.MicrosoftCabinet.DeflateCompressionType)data.ReadBits(2).AsByte(); - - return header; - } - - /// - /// Read the block header from the block data, if possible - /// - /// Byte array representing the block - /// Offset within the array to parse - /// Filled dynamic Huffman compressed block header on success, null on error - private static Models.MicrosoftCabinet.MSZIP.DynamicHuffmanCompressedBlockHeader AsDynamicHuffmanCompressedBlockHeader(BitStream data) - { - // If the data is invalid - if (data == null) - return null; - - var header = new Models.MicrosoftCabinet.MSZIP.DynamicHuffmanCompressedBlockHeader(); - - // # of Literal/Length codes - 257 - ushort HLIT = (ushort)(data.ReadBits(5).AsUInt16() + 257); - - // # of Distance codes - 1 - byte HDIST = (byte)(data.ReadBits(5).AsByte() + 1); - - // HCLEN, # of Code Length codes - 4 - byte HCLEN = (byte)(data.ReadBits(4).AsByte() + 4); - - // (HCLEN + 4) x 3 bits: code lengths for the code length - // alphabet given just above - // - // These code lengths are interpreted as 3-bit integers - // (0-7); as above, a code length of 0 means the - // corresponding symbol (literal/ length or distance code - // length) is not used. - int[] bitLengths = new int[19]; - for (ulong i = 0; i < HCLEN; i++) - bitLengths[BitLengthOrder[i]] = data.ReadBits(3).AsByte(); - - // Code length Huffman code - int[] bitLengthTable = CreateTable(bitLengths); - - // HLIT + 257 code lengths for the literal/length alphabet, - // encoded using the code length Huffman code - header.LiteralLengths = BuildHuffmanTree(data, HLIT, bitLengthTable); - - // HDIST + 1 code lengths for the distance alphabet, - // encoded using the code length Huffman code - header.DistanceCodes = BuildHuffmanTree(data, HDIST, bitLengthTable); - - return header; - } - - /// - /// Read the block header from the block data, if possible - /// - /// Byte array representing the block - /// Offset within the array to parse - /// Filled non-compressed block header on success, null on error - private static Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader AsNonCompressedBlockHeader(BitStream data) - { - // If the data is invalid - if (data == null) - return null; - - var header = new Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader(); - - header.LEN = data.ReadAlignedUInt16(); - header.NLEN = data.ReadAlignedUInt16(); - // TODO: Confirm NLEN is 1's compliment of LEN - - return header; - } - - #endregion - - #region Helpers - - /// - /// The alphabet for code lengths is as follows - /// - private static int[] BuildHuffmanTree(BitStream data, ushort codeCount, int[] codeLengths) - { - // Setup the huffman tree - int[] tree = new int[codeCount]; - - // Setup the loop variables - int lastCode = 0, repeatLength = 0; - for (ulong i = 0; i < codeCount; i++) - { - int codeLength = codeLengths[data.ReadBits(7).AsUInt16()]; - if (codeLengths[codeLength] > 7) - _ = data.ReadBits(codeLengths[codeLength] - 7); - - // Represent code lengths of 0 - 15 - if (codeLength > 0 && codeLength <= 15) - { - lastCode = codeLength; - tree[i] = codeLength; - } - - // Copy the previous code length 3 - 6 times. - // The next 2 bits indicate repeat length (0 = 3, ... , 3 = 6) - // Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will expand to 12 code lengths of 8 (1 + 6 + 5) - else if (codeLength == 16) - { - repeatLength = data.ReadBits(2).AsByte(); - repeatLength += 2; - codeLength = lastCode; - } - - // Repeat a code length of 0 for 3 - 10 times. - // (3 bits of length) - else if (codeLength == 17) - { - repeatLength = data.ReadBits(3).AsByte(); - repeatLength += 3; - codeLength = 0; - } - - // Repeat a code length of 0 for 11 - 138 times - // (7 bits of length) - else if (codeLength == 18) - { - repeatLength = data.ReadBits(7).AsByte(); - repeatLength += 11; - codeLength = 0; - } - - // Everything else - else - { - throw new ArgumentOutOfRangeException(); - } - - // If we had a repeat length - for (; repeatLength > 0; repeatLength--) - { - tree[i++] = codeLength; - } - } - - return tree; - } - - /// - /// Given this rule, we can define the Huffman code for an alphabet - /// just by giving the bit lengths of the codes for each symbol of - /// the alphabet in order; this is sufficient to determine the - /// actual codes. In our example, the code is completely defined - /// by the sequence of bit lengths (2, 1, 3, 3). The following - /// algorithm generates the codes as integers, intended to be read - /// from most- to least-significant bit. The code lengths are - /// initially in tree[I].Len; the codes are produced in - /// tree[I].Code. - /// - private static int[] CreateTable(int[] lengths) - { - // Count the number of codes for each code length. Let - // bl_count[N] be the number of codes of length N, N >= 1. - int[] bl_count = new int[259]; - for (int i = 0; i < lengths.Length; i++) - { - bl_count[lengths[i]]++; - } - - // Find the numerical value of the smallest code for each - // code length. - int[] next_code = new int[MAX_BITS + 1]; - int code = 0; - bl_count[0] = 0; - for (int bits = 1; bits <= MAX_BITS; bits++) - { - code = (code + bl_count[bits - 1]) << 1; - next_code[bits] = code; - } - - // Assign numerical values to all codes, using consecutive - // values for all codes of the same length with the base - // values determined at step 2. Codes that are never used - // (which have a bit length of zero) must not be assigned a - // value. - int[] distances = new int[lengths.Length]; - for (int n = 0; n < lengths.Length; n++) - { - int len = lengths[n]; - if (len != 0) - { - distances[n] = next_code[len]; - next_code[len]++; - } - } - - return distances; - } - - #endregion - - #endregion - - #region Quantum - - // TODO: Implement Quantum decompression - - #endregion - - #endregion - #region Folders /// @@ -659,117 +251,6 @@ namespace BurnOutSharp.Wrappers return data.ToArray(); } - /// - /// Decompress MSZIP data - /// - private byte[] DecompressMSZIPData(byte[] data) - { - // Create the bitstream to read from - var dataStream = new BitStream(data); - - // Get the block header - var blockHeader = AsBlockHeader(dataStream); - if (blockHeader == null) - return null; - - // Create the output byte array - List decodedBytes = new List(); - - // Create the loop variable block - Models.MicrosoftCabinet.MSZIP.DeflateBlockHeader deflateBlockHeader; - - do - { - deflateBlockHeader = AsDeflateBlockHeader(dataStream); - - // We should never get a reserved block - if (deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.Reserved) - throw new Exception(); - - // If stored with no compression - if (deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.NoCompression) - { - // Skip any remaining bits in current partially processed byte - dataStream.DiscardBuffer(); - - // Read the block header - deflateBlockHeader.BlockDataHeader = AsNonCompressedBlockHeader(dataStream); - - // Copy LEN bytes of data to output - var header = deflateBlockHeader.BlockDataHeader as Models.MicrosoftCabinet.MSZIP.NonCompressedBlockHeader; - ushort length = header.LEN; - decodedBytes.AddRange(dataStream.ReadAlignedBytes(length)); - } - - // Otherwise - else - { - // If compressed with dynamic Huffman codes - // read representation of code trees - deflateBlockHeader.BlockDataHeader = deflateBlockHeader.BTYPE == Models.MicrosoftCabinet.DeflateCompressionType.DynamicHuffman - ? (Models.MicrosoftCabinet.MSZIP.IBlockDataHeader)AsDynamicHuffmanCompressedBlockHeader(dataStream) - : (Models.MicrosoftCabinet.MSZIP.IBlockDataHeader)new Models.MicrosoftCabinet.MSZIP.FixedHuffmanCompressedBlockHeader(); - - var header = deflateBlockHeader.BlockDataHeader as Models.MicrosoftCabinet.MSZIP.CompressedBlockHeader; - - // 9 bits per entry, 288 max symbols - int[] literalDecodeTable = CreateTable(header.LiteralLengths); - - // 6 bits per entry, 32 max symbols - int[] distanceDecodeTable = CreateTable(header.DistanceCodes); - - // Loop until end of block code recognized - while (true) - { - // Decode literal/length value from input stream - int symbol = literalDecodeTable[dataStream.ReadBits(9).AsUInt16()]; - - // Copy value (literal byte) to output stream - if (symbol < 256) - { - decodedBytes.Add((byte)symbol); - } - // End of block (256) - else if (symbol == 256) - { - break; - } - else - { - // Decode distance from input stream - ulong length = dataStream.ReadBits(LiteralExtraBits[symbol]).AsUInt64(); - length += (ulong)LiteralLengths[symbol]; - - int code = distanceDecodeTable[length]; - - ulong distance = dataStream.ReadBits(DistanceExtraBits[code]).AsUInt64(); - distance += (ulong)DistanceOffsets[code]; - - - // Move backwards distance bytes in the output - // stream, and copy length bytes from this - // position to the output stream. - } - } - } - } while (!deflateBlockHeader.BFINAL); - - /* - Note that a duplicated string reference may refer to a string - in a previous block; i.e., the backward distance may cross one - or more block boundaries. However a distance cannot refer past - the beginning of the output stream. (An application using a - preset dictionary might discard part of the output stream; a - distance can refer to that part of the output stream anyway) - Note also that the referenced string may overlap the current - position; for example, if the last 2 bytes decoded have values - X and Y, a string reference with - adds X,Y,X,Y,X to the output stream. - */ - - return decodedBytes.ToArray(); - } - #endregion #region Files