diff --git a/MSZIP/Decompressor.cs b/MSZIP/Decompressor.cs index e2a7622..0d4e14e 100644 --- a/MSZIP/Decompressor.cs +++ b/MSZIP/Decompressor.cs @@ -119,66 +119,5 @@ namespace SabreTools.Compression.MSZIP header.NLEN = input.ReadUInt16() ?? 0; return header; } - - /// - /// The Huffman codes for the two alphabets are fixed, and are not - /// represented explicitly in the data. - /// - private int[] CreateFixedLiteralLengthLengths() - { - int[] lengths = new int[288]; - for (int i = 0; i <= 143; i++) - { - lengths[i] = 8; - } - for (int i = 144; i <= 255; i++) - { - lengths[i] = 9; - } - for (int i = 256; i <= 279; i++) - { - lengths[i] = 7; - } - for (int i = 280; i <= 287; i++) - { - lengths[i] = 8; - } - return lengths; - } - - private void CreateCodeValues(int[] lengths, int max_bits, int max_code, HuffmanNode[] tree) - { - // Count the number of codes for each code length - int[] bl_count = new int[max_bits]; - for (int i = 0; i < lengths.Length; i++) - { - int length = lengths[i]; - bl_count[length]++; - } - - // Find the numerical value of the smalles 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. - for (int n = 0; n <= max_code; n++) - { - int len = tree[n].Len; - if (len != 0) - { - tree[n].Code = next_code[len]; - next_code[len]++; - } - } - } } } \ No newline at end of file diff --git a/MSZIP/HuffmanDecoder.cs b/MSZIP/HuffmanDecoder.cs new file mode 100644 index 0000000..186b997 --- /dev/null +++ b/MSZIP/HuffmanDecoder.cs @@ -0,0 +1,138 @@ +using System.IO; +using System.Linq; + +namespace SabreTools.Compression.MSZIP +{ + public class HuffmanDecoder + { + /// + /// Root Huffman node for the tree + /// + private HuffmanNode _root; + + /// + /// Create a Huffman tree to decode with + /// + /// Array representing the number of bits for each value + /// Number of Huffman codes encoded + public HuffmanDecoder(byte[] lengths, uint num_codes) + { + // Set the root to null for now + _root = null; + + // Determine the value for max_bits + int max_bits = lengths.Max(); + + // Count the number of codes for each code length + int[] bl_count = new int[max_bits + 1]; + for (int i = 0; i < num_codes; i++) + { + int length = lengths[i]; + bl_count[length]++; + } + + // Find the numerical value of the smalles 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[] tree = new int[num_codes]; + for (int i = 0; i < num_codes; i++) + { + byte len = lengths[i]; + if (len == 0) + continue; + + // Set the value in the tree + tree[i] = next_code[len]; + next_code[len]++; + } + + // Now insert the values into the structure + for (int i = 0; i < num_codes; i++) + { + // If we have a 0-length code + byte len = lengths[i]; + if (len == 0) + continue; + + // Insert the value starting at the root + _root = Insert(_root, i, len, tree[i]); + } + } + + /// + /// Decode the next value from the stream as a Huffman-encoded value + /// + /// BitStream representing the input + /// Value of the node described by the input + public int Decode(BitStream input) + { + // Start at the root of the tree + var node = _root; + while (node.Left != null) + { + // Read the next bit to determine direction + byte? nextBit = input.ReadBit(); + if (nextBit == null) + throw new EndOfStreamException(); + + // Left == 0, Right == 1 + if (nextBit == 0) + node = node.Left; + else + node = node.Right; + } + + // We traversed to the bottom of the branch + return node.Value; + } + + /// + /// Insert a value based on an existing Huffman node + /// + /// Existing node to append to, or null if root + /// Value to append to the tree + /// Length of the current encoding + /// Encoding of the value to traverse + /// New instance of the node with value appended +#if NET48 + private static HuffmanNode Insert(HuffmanNode node, int value, int length, int code) +#else + private static HuffmanNode Insert(HuffmanNode? node, int value, int length, int code) +#endif + { + // If no node is provided, create a new one + if (node == null) + node = new HuffmanNode(); + + // If we're at the correct location, insert the value + if (length == 0) + { + node.Value = value; + return node; + } + + // Otherwise, get the next bit from the code + byte nextBit = (byte)(code >> (length - 1) & 1); + + // Left == 0, Right == 1 + if (nextBit == 0) + node.Left = Insert(node.Left, value, length - 1, code); + else + node.Right = Insert(node.Right, value, length - 1, code); + + // Now return the node + return node; + } + } +} \ No newline at end of file diff --git a/MSZIP/HuffmanNode.cs b/MSZIP/HuffmanNode.cs index 92543f7..ce02d42 100644 --- a/MSZIP/HuffmanNode.cs +++ b/MSZIP/HuffmanNode.cs @@ -1,9 +1,23 @@ namespace SabreTools.Compression.MSZIP { + /// + /// Represents a single node in a Huffman tree + /// public class HuffmanNode { - public int Len { get; set; } + /// + /// Left child of the current node + /// + public HuffmanNode Left { get; set; } - public int Code { get; set; } + /// + /// Right child of the current node + /// + public HuffmanNode Right { get; set; } + + /// + /// Value of the current node + /// + public int Value { get; set; } } } \ No newline at end of file