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