diff --git a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs index 8a88441b..f244a916 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.MSZIP.cs @@ -501,13 +501,22 @@ namespace BurnOutSharp.FileType public class MSZIPDeflate { + #region Constants + + /// + /// Maximum Huffman code bit count + /// + public 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 - public Dictionary LiteralLengths + public static Dictionary LiteralLengths { get { @@ -556,7 +565,7 @@ namespace BurnOutSharp.FileType /// /// Extra bits for literal codes 257..285 /// - public Dictionary LiteralExtraBits + public static Dictionary LiteralExtraBits { get { @@ -634,12 +643,12 @@ namespace BurnOutSharp.FileType /// /// Match lengths for literal codes 257..285 /// - private Dictionary _literalLengths = null; + private static Dictionary _literalLengths = null; /// /// Extra bits for literal codes 257..285 /// - private Dictionary _literalExtraBits = null; + private static Dictionary _literalExtraBits = null; #endregion @@ -682,33 +691,52 @@ namespace BurnOutSharp.FileType // Otherwise else { + // If compressed with dynamic Huffman codes + // read representation of code trees block.BlockData = block.BTYPE == MSZIPDeflateCompressionType.DynamicHuffman - ? (IMSZIPBlockData)new MSZIPDynamicHuffmanCompressedBlock() + ? (IMSZIPBlockData)new MSZIPDynamicHuffmanCompressedBlock(data) : (IMSZIPBlockData)new MSZIPFixedHuffmanCompressedBlock(); - // If compressed with dynamic Huffman codes - if (block.BTYPE == MSZIPDeflateCompressionType.DynamicHuffman) - { - // read representation of code trees (see subsection below) - } + var compressedBlock = (block.BlockData as MSZIPCompressedBlock); + + // 9 bits per entry, 288 max symbols + int[] literalDecodeTable = CreateTable(compressedBlock.LiteralLengths); + + // 6 bits per entry, 32 max symbols + int[] distanceDecodeTable = CreateTable(compressedBlock.DistanceCodes); // Loop until end of block code recognized while (true) { - /* - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream + // Decode literal/length value from input stream + int symbol = literalDecodeTable[data.ReadBitsLSB(9)]; - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ + // Copy value (literal byte) to output stream + if (symbol < 256) + { + decodedBytes.Add((byte)symbol); + } + // End of block (256) + else if (symbol == 256) + { + break; + } + else + { + ulong length = data.ReadBitsLSB(LiteralExtraBits[symbol]); + length += (ulong)LiteralLengths[symbol]; + + int code = distanceDecodeTable[length]; + + ulong distance = data.ReadBitsLSB(DistanceExtraBits[code]); + distance += (ulong)DistanceOffsets[code]; + + // Decode distance from input stream + + // Move backwards distance bytes in the output + // stream, and copy length bytes from this + // position to the output stream. + } } } } while (!block.BFINAL); @@ -726,6 +754,111 @@ namespace BurnOutSharp.FileType adds X,Y,X,Y,X to the output stream. */ } + + /// + /// 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. + /// + public static void CreateTable(MSZIPCompressedBlock tree) + { + // Count the number of codes for each code length. Let + // bl_count[N] be the number of codes of length N, N >= 1. + var bl_count = new Dictionary(); + for (int i = 0; i < tree.LiteralLengths.Length; i++) + { + if (!bl_count.ContainsKey(tree.LiteralLengths[i])) + bl_count[tree.LiteralLengths[i]] = 0; + + bl_count[tree.LiteralLengths[i]]++; + } + + // Find the numerical value of the smallest code for each + // code length: + var next_code = new Dictionary(); + 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. + for (int n = 0; n <= tree.LiteralLengths.Length; n++) + { + int len = tree.LiteralLengths[n]; + if (len != 0) + { + tree.DistanceCodes[n] = next_code[len]; + next_code[len]++; + } + } + } + + /// + /// 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. + /// + public 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. + var bl_count = new Dictionary(); + for (int i = 0; i < lengths.Length; i++) + { + if (!bl_count.ContainsKey(lengths[i])) + bl_count[lengths[i]] = 0; + + bl_count[lengths[i]]++; + } + + // Find the numerical value of the smallest code for each + // code length: + var next_code = new Dictionary(); + 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; + } } public class MSZIPDeflateBlock @@ -832,9 +965,7 @@ namespace BurnOutSharp.FileType { #region Properties - /// - /// Huffman code lengths for the literal / length alphabet - /// + /// public override int[] LiteralLengths { get @@ -866,9 +997,7 @@ namespace BurnOutSharp.FileType } } - /// - /// Huffman distance codes for the literal / length alphabet - /// + /// public override int[] DistanceCodes { get @@ -910,82 +1039,120 @@ namespace BurnOutSharp.FileType /// public class MSZIPDynamicHuffmanCompressedBlock : MSZIPCompressedBlock { - /// - /// The Huffman codes for the literal/length code - /// - public override int[] LiteralLengths => new int[19]; + #region Properties + + /// + public override int[] LiteralLengths { get; } = new int[19]; + + /// + public override int[] DistanceCodes { get; } = new int[19]; + + #endregion /// - /// The Huffman codes for the distance code + /// Constructor /// - public override int[] DistanceCodes => new int[19]; + public MSZIPDynamicHuffmanCompressedBlock(MSZIPDeflateStream stream) + { + // # of Literal/Length codes - 257 + ulong HLIT = stream.ReadBitsLSB(5) + 257; - /* - 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) + // # of Distance codes - 1 + ulong HDIST = stream.ReadBitsLSB(5) + 1; - The Huffman codes for the two alphabets appear in the block - immediately after the header bits and before the actual - compressed data, first the literal/length code and then the - distance code. Each code is defined by a sequence of code - lengths, as discussed in Paragraph 3.2.2, above. For even - greater compactness, the code length sequences themselves are - compressed using a Huffman code. The alphabet for code lengths - is as follows: + // HCLEN, # of Code Length codes - 4 + ulong HCLEN = stream.ReadBitsLSB(5) + 4; - 0 - 15: Represent code lengths of 0 - 15 - 16: 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) - 17: Repeat a code length of 0 for 3 - 10 times. - (3 bits of length) - 18: Repeat a code length of 0 for 11 - 138 times - (7 bits of length) + // (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[] codeLengthAlphabet = new int[19]; + for (ulong i = 0; i < HCLEN; i++) + codeLengthAlphabet[MSZIPDeflate.BitLengthOrder[i]] = (int)stream.ReadBitsLSB(3); - A code length of 0 indicates that the corresponding symbol in - the literal/length or distance alphabet will not occur in the - block, and should not participate in the Huffman code - construction algorithm given earlier. If only one distance - code is used, it is encoded using one bit, not zero bits; in - this case there is a single code length of one, with one unused - code. One distance code of zero bits means that there are no - distance codes used at all (the data is all literals). + for (ulong i = HCLEN; i < 19; i++) + codeLengthAlphabet[MSZIPDeflate.BitLengthOrder[i]] = 0; - We can now define the format of the block: + // Code length Huffman code + int[] codeLengthHuffmanCode = MSZIPDeflate.CreateTable(codeLengthAlphabet); - 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) - 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) - 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + // HLIT + 257 code lengths for the literal/length alphabet, + // encoded using the code length Huffman code + this.LiteralLengths = BuildHuffmanTree(stream, HLIT, codeLengthHuffmanCode); + // HDIST + 1 code lengths for the distance alphabet, + // encoded using the code length Huffman code + this.DistanceCodes = BuildHuffmanTree(stream, HDIST, codeLengthHuffmanCode); + } - (HCLEN + 4) x 3 bits: code lengths for the code length - alphabet given just above, in the order: 16, 17, 18, - 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + /// + /// The alphabet for code lengths is as follows + /// + private int[] BuildHuffmanTree(MSZIPDeflateStream stream, ulong codeCount, int[] codeLengths) + { + // Setup the huffman tree + int[] tree = new int[codeCount]; - 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. + // Setup the loop variables + int lastCode = 0, repeatLength = 0; + for (ulong i = 0; i < codeCount; i++) + { + int code = codeLengths[(int)stream.ReadBitsLSB(7)]; - HLIT + 257 code lengths for the literal/length alphabet, - encoded using the code length Huffman code + // Represent code lengths of 0 - 15 + if (code > 0 && code <= 15) + { + lastCode = code; + tree[i] = code; + } - HDIST + 1 code lengths for the distance alphabet, - encoded using the code length Huffman code + // 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 (code == 16) + { + repeatLength = (int)stream.ReadBitsLSB(2); + repeatLength += 2; + code = lastCode; + } - The actual compressed data of the block, - encoded using the literal/length and distance Huffman - codes + // Repeat a code length of 0 for 3 - 10 times. + // (3 bits of length) + else if (code == 17) + { + repeatLength = (int)stream.ReadBitsLSB(3); + repeatLength += 3; + code = 0; + } - The literal/length symbol 256 (end of data), - encoded using the literal/length Huffman code + // Repeat a code length of 0 for 11 - 138 times + // (7 bits of length) + else if (code == 18) + { + repeatLength = (int)stream.ReadBitsLSB(7); + repeatLength += 11; + code = 0; + } - The code length repeat codes can cross from HLIT + 257 to the - HDIST + 1 code lengths. In other words, all code lengths form - a single sequence of HLIT + HDIST + 258 values. - */ + // Everything else + else + { + throw new ArgumentOutOfRangeException(); + } + + // If we had a repeat length + for (; repeatLength > 0; repeatLength--) + { + tree[i++] = code; + } + } + + return tree; + } } #endregion