From da75d115e0d0389e3ccddf8c2de8d6a6605589e1 Mon Sep 17 00:00:00 2001 From: barubary Date: Fri, 1 Apr 2011 14:12:23 +0000 Subject: [PATCH] C#: completed the implementation for the decompression of the Huffman format. --- CSharp/DSDecmp/Formats/Nitro/Huffman.cs | 91 ++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/CSharp/DSDecmp/Formats/Nitro/Huffman.cs b/CSharp/DSDecmp/Formats/Nitro/Huffman.cs index 13f87ef..8882f11 100644 --- a/CSharp/DSDecmp/Formats/Nitro/Huffman.cs +++ b/CSharp/DSDecmp/Formats/Nitro/Huffman.cs @@ -56,7 +56,7 @@ namespace DSDecmp.Formats.Nitro blockSize = BlockSize.EIGHTBIT; if (type != (byte)blockSize) throw new InvalidDataException("The provided stream is not a valid Huffman " - + "compressed stream (invalid type 0x" + type.ToString("X") + ")"); + + "compressed stream (invalid type 0x" + type.ToString("X") + "); unknown block size."); byte[] sizeBytes = new byte[3]; instream.Read(sizeBytes, 0, 3); int decompressedSize = base.Bytes2Size(sizeBytes); @@ -84,7 +84,76 @@ namespace DSDecmp.Formats.Nitro // the given value is odd or even. HuffTreeNode rootNode = new HuffTreeNode(instream, false, 5, instream.Position + treeSize); - throw new NotImplementedException(); + int data = 0; + byte bitsLeft = 0; + + // a cache used for writing when the block size is four bits + int cachedByte = -1; + + int currentSize = 0; + HuffTreeNode currentNode = rootNode; + + while (currentSize < decompressedSize) + { + #region find the next reference to a data node + while (!currentNode.IsData) + { + // if there are no bits left to read in the data, get a new byte from the input + if (bitsLeft == 0) + { + if (readBytes >= inLength) + throw new NotEnoughDataException(currentSize, decompressedSize); + // the spec indicates the data is read in groups of four bytes, but because of + // the order the bits are read in, we might as well read one byte at a time. + data = instream.ReadByte(); + if (data < 0) + throw new StreamTooShortException(); + bitsLeft = 8; + } + // get the next bit + bitsLeft--; + bool nextIsOne = (data & (1 << bitsLeft)) > 0; + // go to the next node, the direction of the child depending on the value of the current/next bit + currentNode = nextIsOne ? currentNode.Child1 : currentNode.Child0; + } + #endregion + + #region write the data in the current node (when possible) + switch (blockSize) + { + case BlockSize.EIGHTBIT: + { + // just copy the data if the block size is a full byte + outstream.WriteByte(currentNode.Data); + currentSize++; + break; + } + case BlockSize.FOURBIT: + { + // cache the first half of the data if the block size is a half byte + if (cachedByte < 0) + { + cachedByte = currentNode.Data << 4; + } + else + { + // if we already cached a half-byte, combine the two halves and write the full byte. + cachedByte |= currentNode.Data; + outstream.WriteByte((byte)cachedByte); + currentSize++; + // be sure to forget the two written half-bytes + cachedByte = -1; + } + break; + } + default: + throw new Exception("Unknown block size " + blockSize.ToString()); + } + #endregion + + // make sure to start over next round + currentNode = rootNode; + } } public override void Compress(Stream instream, long inLength, Stream outstream) @@ -97,10 +166,14 @@ namespace DSDecmp.Formats.Nitro public class HuffTreeNode { /// - /// The data contained in this node. May not mena anything when isData == false + /// The data contained in this node. May not mean anything when isData == false /// private byte data; /// + /// The data contained in this node. May not mean anything when isData == false + /// + public byte Data { get { return this.data; } } + /// /// A flag indicating if this ia a 'child1' of another node. /// private bool isOne; @@ -108,15 +181,27 @@ namespace DSDecmp.Formats.Nitro /// A flag indicating if this node contains data. If not, this is not a leaf node. /// private bool isData; + /// + /// Returns true if this node represents data. + /// + public bool IsData { get { return this.isData; } } /// /// The child of this node at side 0 /// private HuffTreeNode child0; /// + /// The child of this node at side 0 + /// + public HuffTreeNode Child0 { get { return this.child0; } } + /// /// The child of this node at side 1 /// private HuffTreeNode child1; + /// + /// The child of this node at side 1 + /// + public HuffTreeNode Child1 { get { return this.child1; } } /// /// Creates a new node in the Huffman tree.