Ejemplo n.º 1
0
 /// <summary>
 /// Gets the tree node with the lowest priority (frequency) from the leaf and node queues.
 /// If the priority is the same for both head items in the queues, the node from the leaf queue is picked.
 /// </summary>
 private HuffTreeNode GetLowest(SimpleReversedPrioQueue <int, HuffTreeNode> leafQueue, SimpleReversedPrioQueue <int, HuffTreeNode> nodeQueue, out int prio)
 {
     if (leafQueue.Count == 0)
     {
         return(nodeQueue.Dequeue(out prio));
     }
     else if (nodeQueue.Count == 0)
     {
         return(leafQueue.Dequeue(out prio));
     }
     else
     {
         int leafPrio, nodePrio;
         leafQueue.Peek(out leafPrio);
         nodeQueue.Peek(out nodePrio);
         // pick a node from the leaf queue when the priorities are equal.
         if (leafPrio <= nodePrio)
         {
             return(leafQueue.Dequeue(out prio));
         }
         else
         {
             return(nodeQueue.Dequeue(out prio));
         }
     }
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Applies Huffman compression with a datablock size of 8 bits.
        /// </summary>
        /// <param name="instream">The stream to compress.</param>
        /// <param name="inLength">The length of the input stream.</param>
        /// <param name="outstream">The stream to write the decompressed data to.</param>
        /// <returns>The size of the decompressed data.</returns>
        private int Compress8(Stream instream, long inLength, Stream outstream)
        {
            if (inLength > 0xFFFFFF)
            {
                throw new InputTooLargeException();
            }

            // cache the input, as we need to build a frequency table
            byte[] inputData = new byte[inLength];
            instream.Read(inputData, 0, (int)inLength);

            // build that frequency table.
            int[] frequencies = new int[0x100];
            for (int i = 0; i < inLength; i++)
            {
                frequencies[inputData[i]]++;
            }

            #region Build the Huffman tree

            SimpleReversedPrioQueue <int, HuffTreeNode> leafQueue = new SimpleReversedPrioQueue <int, HuffTreeNode>();
            SimpleReversedPrioQueue <int, HuffTreeNode> nodeQueue = new SimpleReversedPrioQueue <int, HuffTreeNode>();
            int nodeCount = 0;
            // make all leaf nodes, and put them in the leaf queue. Also save them for later use.
            HuffTreeNode[] leaves = new HuffTreeNode[0x100];
            for (int i = 0; i < 0x100; i++)
            {
                // there is no need to store leaves that are not used
                if (frequencies[i] == 0)
                {
                    continue;
                }
                HuffTreeNode node = new HuffTreeNode((byte)i, true, null, null);
                leaves[i] = node;
                leafQueue.Enqueue(frequencies[i], node);
                nodeCount++;
            }

            while (leafQueue.Count + nodeQueue.Count > 1)
            {
                // get the two nodes with the lowest priority.
                HuffTreeNode one = null, two = null;
                int          onePrio, twoPrio;
                one = GetLowest(leafQueue, nodeQueue, out onePrio);
                two = GetLowest(leafQueue, nodeQueue, out twoPrio);

                // give those two a common parent, and put that node in the node queue
                HuffTreeNode newNode = new HuffTreeNode(0, false, one, two);
                nodeQueue.Enqueue(onePrio + twoPrio, newNode);
                nodeCount++;
            }
            int          rootPrio;
            HuffTreeNode root = nodeQueue.Dequeue(out rootPrio);
            // set the depth of all nodes in the tree, such that we know for each leaf how long
            // its codeword is.
            root.Depth = 0;

            #endregion

            // now that we have a tree, we can write that tree and follow with the data.

            // write the compression header first
            outstream.WriteByte((byte)BlockSize.EIGHTBIT); // this is block size 8 only
            outstream.WriteByte((byte)(inLength & 0xFF));
            outstream.WriteByte((byte)((inLength >> 8) & 0xFF));
            outstream.WriteByte((byte)((inLength >> 16) & 0xFF));

            int compressedLength = 4;

            #region write the tree

            outstream.WriteByte((byte)((nodeCount - 1) / 2));
            compressedLength++;

            // use a breadth-first traversal to store the tree, such that we do not need to store/calculate the side of each sub-tree.
            LinkedList <HuffTreeNode> printQueue = new LinkedList <HuffTreeNode>();
            printQueue.AddLast(root);
            while (printQueue.Count > 0)
            {
                HuffTreeNode node = printQueue.First.Value;
                printQueue.RemoveFirst();
                if (node.IsData)
                {
                    outstream.WriteByte(node.Data);
                }
                else
                {
                    // bits 0-5: 'offset' = # nodes in queue left
                    // bit 6: node1 end flag
                    // bit 7: node0 end flag
                    byte data = (byte)(printQueue.Count / 2);
                    data = (byte)(data & 0x3F);
                    if (node.Child0.IsData)
                    {
                        data |= 0x80;
                    }
                    if (node.Child1.IsData)
                    {
                        data |= 0x40;
                    }
                    outstream.WriteByte(data);

                    printQueue.AddLast(node.Child0);
                    printQueue.AddLast(node.Child1);
                }
                compressedLength++;
            }

            #endregion

            #region write the data

            // the codewords are stored in blocks of 32 bits
            uint datablock       = 0;
            byte bitsLeftToWrite = 32;

            for (int i = 0; i < inLength; i++)
            {
                byte         data = inputData[i];
                HuffTreeNode node = leaves[data];
                // the depth of the node is the length of the codeword required to encode the byte
                int    depth = node.Depth;
                bool[] path  = new bool[depth];
                for (int d = 0; d < depth; d++)
                {
                    path[depth - d - 1] = node.IsChild1;
                    node = node.Parent;
                }
                for (int d = 0; d < depth; d++)
                {
                    if (bitsLeftToWrite == 0)
                    {
                        outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                        compressedLength += 4;
                        datablock         = 0;
                        bitsLeftToWrite   = 32;
                    }
                    bitsLeftToWrite--;
                    if (path[d])
                    {
                        datablock |= (uint)(1 << bitsLeftToWrite);
                    }
                    // no need to OR the buffer with 0 if it is child0
                }
            }

            // write the partly filled data block as well
            if (bitsLeftToWrite != 32)
            {
                outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                compressedLength += 4;
            }

            #endregion

            return(compressedLength);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Applies Huffman compression with a datablock size of 8 bits.
        /// </summary>
        /// <param name="instream">The stream to compress.</param>
        /// <param name="inLength">The length of the input stream.</param>
        /// <param name="outstream">The stream to write the decompressed data to.</param>
        /// <returns>The size of the decompressed data.</returns>
        public override int Compress(Stream instream, long inLength, Stream outstream)
        {
            if (inLength > 0xFFFFFF)
            {
                throw new InputTooLargeException();
            }

            // cache the input, as we need to build a frequency table
            byte[] inputData = new byte[inLength];
            instream.Read(inputData, 0, (int)inLength);

            // build that frequency table.
            int[] frequencies = new int[0x100];
            for (int i = 0; i < inLength; i++)
            {
                frequencies[inputData[i]]++;
            }

            #region Build the Huffman tree

            SimpleReversedPrioQueue <int, HuffTreeNode> leafQueue = new SimpleReversedPrioQueue <int, HuffTreeNode>();
            SimpleReversedPrioQueue <int, HuffTreeNode> nodeQueue = new SimpleReversedPrioQueue <int, HuffTreeNode>();
            int nodeCount = 0;
            // make all leaf nodes, and put them in the leaf queue. Also save them for later use.
            HuffTreeNode[] leaves = new HuffTreeNode[0x100];
            for (int i = 0; i < 0x100; i++)
            {
                // there is no need to store leaves that are not used
                if (frequencies[i] == 0)
                {
                    continue;
                }
                HuffTreeNode node = new HuffTreeNode((byte)i, true, null, null);
                leaves[i] = node;
                leafQueue.Enqueue(frequencies[i], node);
                nodeCount++;
            }

            while (leafQueue.Count + nodeQueue.Count > 1)
            {
                // get the two nodes with the lowest priority.
                HuffTreeNode one = null, two = null;
                int          onePrio, twoPrio;
                one = GetLowest(leafQueue, nodeQueue, out onePrio);
                two = GetLowest(leafQueue, nodeQueue, out twoPrio);

                // give those two a common parent, and put that node in the node queue
                HuffTreeNode newNode = new HuffTreeNode(0, false, one, two);
                nodeQueue.Enqueue(onePrio + twoPrio, newNode);
                nodeCount++;
            }
            int          rootPrio;
            HuffTreeNode root = nodeQueue.Dequeue(out rootPrio);
            // set the depth of all nodes in the tree, such that we know for each leaf how long
            // its codeword is.
            root.Depth = 0;

            #endregion

            // now that we have a tree, we can write that tree and follow with the data.

            // write the compression header first
            outstream.WriteByte((byte)BlockSize.EIGHTBIT); // this is block size 8 only
            outstream.WriteByte((byte)(inLength & 0xFF));
            outstream.WriteByte((byte)((inLength >> 8) & 0xFF));
            outstream.WriteByte((byte)((inLength >> 16) & 0xFF));

            int compressedLength = 4;

            #region write the tree

            outstream.WriteByte((byte)((nodeCount - 1) / 2));
            compressedLength++;

            // use a breadth-first traversal to store the tree, such that we do not need to store/calculate the size of each sub-tree.
            // NO! BF results in an ordering that may overflow the offset field.

            // find the BF order of all nodes that have two leaves as children. We're going to insert them in an array in reverse BF order,
            // inserting the parent whenever both children have been inserted.

            LinkedList <HuffTreeNode> leafStemQueue = new LinkedList <HuffTreeNode>();

            #region fill the leaf queue; first->last will be reverse BF
            LinkedList <HuffTreeNode> nodeCodeStack = new LinkedList <HuffTreeNode>();
            nodeCodeStack.AddLast(root);
            while (nodeCodeStack.Count > 0)
            {
                HuffTreeNode node = nodeCodeStack.First.Value;
                nodeCodeStack.RemoveFirst();
                if (node.IsData)
                {
                    continue;
                }
                if (node.Child0.IsData && node.Child1.IsData)
                {
                    leafStemQueue.AddFirst(node);
                }
                else
                {
                    nodeCodeStack.AddLast(node.Child0);
                    nodeCodeStack.AddLast(node.Child1);
                }
            }
            #endregion

            HuffTreeNode[] nodeArray = new HuffTreeNode[0x1FF]; // this array does not contain the leaves themselves!
            while (leafStemQueue.Count > 0)
            {
                Insert(leafStemQueue.First.Value, nodeArray, 0x3F + 1);
                leafStemQueue.RemoveFirst();
            }

            // update the indices to ignore all gaps
            int nodeIndex = 0;
            for (int i = 0; i < nodeArray.Length; i++)
            {
                if (nodeArray[i] != null)
                {
                    nodeArray[i].index = nodeIndex++;
                }
            }

            // write the nodes in their given order. However when 'writing' a node, write the data of its children instead.
            // the root node is always the first node.
            byte rootData = 0;
            if (root.Child0.IsData)
            {
                rootData |= 0x80;
            }
            if (root.Child1.IsData)
            {
                rootData |= 0x40;
            }
            outstream.WriteByte(rootData); compressedLength++;

            for (int i = 0; i < nodeArray.Length; i++)
            {
                if (nodeArray[i] != null)
                {
                    // nodes in this array are never data!
                    HuffTreeNode node0 = nodeArray[i].Child0;
                    if (node0.IsData)
                    {
                        outstream.WriteByte(node0.Data);
                    }
                    else
                    {
                        int offset = node0.index - nodeArray[i].index - 1;
                        if (offset > 0x3F)
                        {
                            throw new Exception("Offset overflow!");
                        }
                        byte data = (byte)offset;
                        if (node0.Child0.IsData)
                        {
                            data |= 0x80;
                        }
                        if (node0.Child1.IsData)
                        {
                            data |= 0x40;
                        }
                        outstream.WriteByte(data);
                    }

                    HuffTreeNode node1 = nodeArray[i].Child1;
                    if (node1.IsData)
                    {
                        outstream.WriteByte(node1.Data);
                    }
                    else
                    {
                        int offset = node1.index - nodeArray[i].index - 1;
                        if (offset > 0x3F)
                        {
                            throw new Exception("Offset overflow!");
                        }
                        byte data = (byte)offset;
                        if (node0.Child0.IsData)
                        {
                            data |= 0x80;
                        }
                        if (node0.Child1.IsData)
                        {
                            data |= 0x40;
                        }
                        outstream.WriteByte(data);
                    }

                    compressedLength += 2;
                }
            }
            #endregion

            #region write the data

            // the codewords are stored in blocks of 32 bits
            uint datablock       = 0;
            byte bitsLeftToWrite = 32;

            for (int i = 0; i < inLength; i++)
            {
                byte         data = inputData[i];
                HuffTreeNode node = leaves[data];
                // the depth of the node is the length of the codeword required to encode the byte
                int    depth = node.Depth;
                bool[] path  = new bool[depth];
                for (int d = 0; d < depth; d++)
                {
                    path[depth - d - 1] = node.IsChild1;
                    node = node.Parent;
                }
                for (int d = 0; d < depth; d++)
                {
                    if (bitsLeftToWrite == 0)
                    {
                        outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                        compressedLength += 4;
                        datablock         = 0;
                        bitsLeftToWrite   = 32;
                    }
                    bitsLeftToWrite--;
                    if (path[d])
                    {
                        datablock |= (uint)(1 << bitsLeftToWrite);
                    }
                    // no need to OR the buffer with 0 if it is child0
                }
            }

            // write the partly filled data block as well
            if (bitsLeftToWrite != 32)
            {
                outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                compressedLength += 4;
            }

            #endregion

            return(compressedLength);
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Gets the tree node with the lowest priority (frequency) from the leaf and node queues.
 /// If the priority is the same for both head items in the queues, the node from the leaf queue is picked.
 /// </summary>
 private HuffTreeNode GetLowest(SimpleReversedPrioQueue<int, HuffTreeNode> leafQueue, SimpleReversedPrioQueue<int, HuffTreeNode> nodeQueue, out int prio)
 {
     if (leafQueue.Count == 0)
         return nodeQueue.Dequeue(out prio);
     else if (nodeQueue.Count == 0)
         return leafQueue.Dequeue(out prio);
     else
     {
         int leafPrio, nodePrio;
         leafQueue.Peek(out leafPrio);
         nodeQueue.Peek(out nodePrio);
         // pick a node from the leaf queue when the priorities are equal.
         if (leafPrio <= nodePrio)
             return leafQueue.Dequeue(out prio);
         else
             return nodeQueue.Dequeue(out prio);
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Applies Huffman compression with a datablock size of 8 bits.
        /// </summary>
        /// <param name="instream">The stream to compress.</param>
        /// <param name="inLength">The length of the input stream.</param>
        /// <param name="outstream">The stream to write the decompressed data to.</param>
        /// <returns>The size of the decompressed data.</returns>
        private int Compress8(Stream instream, long inLength, Stream outstream)
        {
            if (inLength > 0xFFFFFF)
                throw new InputTooLargeException();

            // cache the input, as we need to build a frequency table
            byte[] inputData = new byte[inLength];
            instream.Read(inputData, 0, (int)inLength);

            // build that frequency table.
            int[] frequencies = new int[0x100];
            for (int i = 0; i < inLength; i++)
                frequencies[inputData[i]]++;

            #region Build the Huffman tree

            SimpleReversedPrioQueue<int, HuffTreeNode> leafQueue = new SimpleReversedPrioQueue<int, HuffTreeNode>();
            SimpleReversedPrioQueue<int, HuffTreeNode> nodeQueue = new SimpleReversedPrioQueue<int, HuffTreeNode>();
            int nodeCount = 0;
            // make all leaf nodes, and put them in the leaf queue. Also save them for later use.
            HuffTreeNode[] leaves = new HuffTreeNode[0x100];
            for (int i = 0; i < 0x100; i++)
            {
                // there is no need to store leaves that are not used
                if (frequencies[i] == 0)
                    continue;
                HuffTreeNode node = new HuffTreeNode((byte)i, true, null, null);
                leaves[i] = node;
                leafQueue.Enqueue(frequencies[i], node);
                nodeCount++;
            }

            while (leafQueue.Count + nodeQueue.Count > 1)
            {
                // get the two nodes with the lowest priority.
                HuffTreeNode one = null, two = null;
                int onePrio, twoPrio;
                one = GetLowest(leafQueue, nodeQueue, out onePrio);
                two = GetLowest(leafQueue, nodeQueue, out twoPrio);

                // give those two a common parent, and put that node in the node queue
                HuffTreeNode newNode = new HuffTreeNode(0, false, one, two);
                nodeQueue.Enqueue(onePrio + twoPrio, newNode);
                nodeCount++;
            }
            int rootPrio;
            HuffTreeNode root = nodeQueue.Dequeue(out rootPrio);
            // set the depth of all nodes in the tree, such that we know for each leaf how long
            // its codeword is.
            root.Depth = 0;

            #endregion

            // now that we have a tree, we can write that tree and follow with the data.

            // write the compression header first
            outstream.WriteByte((byte)BlockSize.EIGHTBIT); // this is block size 8 only
            outstream.WriteByte((byte)(inLength & 0xFF));
            outstream.WriteByte((byte)((inLength >> 8) & 0xFF));
            outstream.WriteByte((byte)((inLength >> 16) & 0xFF));

            int compressedLength = 4;

            #region write the tree

            outstream.WriteByte((byte)((nodeCount - 1) / 2));
            compressedLength++;

            // use a breadth-first traversal to store the tree, such that we do not need to store/calculate the side of each sub-tree.
            LinkedList<HuffTreeNode> printQueue = new LinkedList<HuffTreeNode>();
            printQueue.AddLast(root);
            while (printQueue.Count > 0)
            {
                HuffTreeNode node = printQueue.First.Value;
                printQueue.RemoveFirst();
                if (node.IsData)
                {
                    outstream.WriteByte(node.Data);
                }
                else
                {
                    // bits 0-5: 'offset' = # nodes in queue left
                    // bit 6: node1 end flag
                    // bit 7: node0 end flag
                    byte data = (byte)(printQueue.Count / 2);
                    data = (byte)(data & 0x3F);
                    if (node.Child0.IsData)
                        data |= 0x80;
                    if (node.Child1.IsData)
                        data |= 0x40;
                    outstream.WriteByte(data);

                    printQueue.AddLast(node.Child0);
                    printQueue.AddLast(node.Child1);
                }
                compressedLength++;
            }

            #endregion

            #region write the data

            // the codewords are stored in blocks of 32 bits
            uint datablock = 0;
            byte bitsLeftToWrite = 32;

            for (int i = 0; i < inLength; i++)
            {
                byte data = inputData[i];
                HuffTreeNode node = leaves[data];
                // the depth of the node is the length of the codeword required to encode the byte
                int depth = node.Depth;
                bool[] path = new bool[depth];
                for (int d = 0; d < depth; d++)
                {
                    path[depth - d - 1] = node.IsChild1;
                    node = node.Parent;
                }
                for (int d = 0; d < depth; d++)
                {
                    if (bitsLeftToWrite == 0)
                    {
                        outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                        compressedLength += 4;
                        datablock = 0;
                        bitsLeftToWrite = 32;
                    }
                    bitsLeftToWrite--;
                    if (path[d])
                        datablock |= (uint)(1 << bitsLeftToWrite);
                    // no need to OR the buffer with 0 if it is child0
                }
            }

            // write the partly filled data block as well
            if (bitsLeftToWrite != 32)
            {
                outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                compressedLength += 4;
            }

            #endregion

            return compressedLength;
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Applies Huffman compression with a datablock size of 8 bits.
        /// </summary>
        /// <param name="instream">The stream to compress.</param>
        /// <param name="inLength">The length of the input stream.</param>
        /// <param name="outstream">The stream to write the decompressed data to.</param>
        /// <returns>The size of the decompressed data.</returns>
        public override int Compress(Stream instream, long inLength, Stream outstream)
        {
            if (inLength > 0xFFFFFF)
                throw new InputTooLargeException();

            // cache the input, as we need to build a frequency table
            byte[] inputData = new byte[inLength];
            instream.Read(inputData, 0, (int)inLength);

            // build that frequency table.
            int[] frequencies = new int[0x100];
            for (int i = 0; i < inLength; i++)
                frequencies[inputData[i]]++;

            #region Build the Huffman tree

            SimpleReversedPrioQueue<int, HuffTreeNode> leafQueue = new SimpleReversedPrioQueue<int, HuffTreeNode>();
            SimpleReversedPrioQueue<int, HuffTreeNode> nodeQueue = new SimpleReversedPrioQueue<int, HuffTreeNode>();
            int nodeCount = 0;
            // make all leaf nodes, and put them in the leaf queue. Also save them for later use.
            HuffTreeNode[] leaves = new HuffTreeNode[0x100];
            for (int i = 0; i < 0x100; i++)
            {
                // there is no need to store leaves that are not used
                if (frequencies[i] == 0)
                    continue;
                HuffTreeNode node = new HuffTreeNode((byte)i, true, null, null);
                leaves[i] = node;
                leafQueue.Enqueue(frequencies[i], node);
                nodeCount++;
            }

            while (leafQueue.Count + nodeQueue.Count > 1)
            {
                // get the two nodes with the lowest priority.
                HuffTreeNode one = null, two = null;
                int onePrio, twoPrio;
                one = GetLowest(leafQueue, nodeQueue, out onePrio);
                two = GetLowest(leafQueue, nodeQueue, out twoPrio);

                // give those two a common parent, and put that node in the node queue
                HuffTreeNode newNode = new HuffTreeNode(0, false, one, two);
                nodeQueue.Enqueue(onePrio + twoPrio, newNode);
                nodeCount++;
            }
            int rootPrio;
            HuffTreeNode root = nodeQueue.Dequeue(out rootPrio);
            // set the depth of all nodes in the tree, such that we know for each leaf how long
            // its codeword is.
            root.Depth = 0;

            #endregion

            // now that we have a tree, we can write that tree and follow with the data.

            // write the compression header first
            outstream.WriteByte((byte)BlockSize.EIGHTBIT); // this is block size 8 only
            outstream.WriteByte((byte)(inLength & 0xFF));
            outstream.WriteByte((byte)((inLength >> 8) & 0xFF));
            outstream.WriteByte((byte)((inLength >> 16) & 0xFF));

            int compressedLength = 4;

            #region write the tree

            outstream.WriteByte((byte)((nodeCount - 1) / 2));
            compressedLength++;

            // use a breadth-first traversal to store the tree, such that we do not need to store/calculate the size of each sub-tree.
            // NO! BF results in an ordering that may overflow the offset field.
            
            // find the BF order of all nodes that have two leaves as children. We're going to insert them in an array in reverse BF order,
            // inserting the parent whenever both children have been inserted.
            
            LinkedList<HuffTreeNode> leafStemQueue = new LinkedList<HuffTreeNode>();

            #region fill the leaf queue; first->last will be reverse BF
            LinkedList<HuffTreeNode> nodeCodeStack = new LinkedList<HuffTreeNode>();
            nodeCodeStack.AddLast(root);
            while (nodeCodeStack.Count > 0)
            {
                HuffTreeNode node = nodeCodeStack.First.Value;
                nodeCodeStack.RemoveFirst();
                if (node.IsData)
                    continue;
                if (node.Child0.IsData && node.Child1.IsData)
                {
                    leafStemQueue.AddFirst(node);
                }
                else
                {
                    nodeCodeStack.AddLast(node.Child0);
                    nodeCodeStack.AddLast(node.Child1);
                }

            }
            #endregion

            HuffTreeNode[] nodeArray = new HuffTreeNode[0x1FF]; // this array does not contain the leaves themselves!
            while (leafStemQueue.Count > 0)
            {
                Insert(leafStemQueue.First.Value, nodeArray, 0x3F + 1);
                leafStemQueue.RemoveFirst();
            }

            // update the indices to ignore all gaps
            int nodeIndex = 0;
            for (int i = 0; i < nodeArray.Length; i++)
            {
                if (nodeArray[i] != null)
                    nodeArray[i].index = nodeIndex++;
            }

            // write the nodes in their given order. However when 'writing' a node, write the data of its children instead.
            // the root node is always the first node.
            byte rootData = 0;
            if (root.Child0.IsData)
                rootData |= 0x80;
            if (root.Child1.IsData)
                rootData |= 0x40;
            outstream.WriteByte(rootData); compressedLength++;

            for (int i = 0; i < nodeArray.Length; i++)
            {
                if (nodeArray[i] != null)
                {
                    // nodes in this array are never data!
                    HuffTreeNode node0 = nodeArray[i].Child0;
                    if (node0.IsData)
                        outstream.WriteByte(node0.Data);
                    else
                    {
                        int offset = node0.index - nodeArray[i].index - 1;
                        if (offset > 0x3F)
                            throw new Exception("Offset overflow!");
                        byte data = (byte)offset;
                        if (node0.Child0.IsData)
                            data |= 0x80;
                        if (node0.Child1.IsData)
                            data |= 0x40;
                        outstream.WriteByte(data);
                    }

                    HuffTreeNode node1 = nodeArray[i].Child1;
                    if (node1.IsData)
                        outstream.WriteByte(node1.Data);
                    else
                    {
                        int offset = node1.index - nodeArray[i].index - 1;
                        if (offset > 0x3F)
                            throw new Exception("Offset overflow!");
                        byte data = (byte)offset;
                        if (node0.Child0.IsData)
                            data |= 0x80;
                        if (node0.Child1.IsData)
                            data |= 0x40;
                        outstream.WriteByte(data);
                    }

                    compressedLength += 2;
                }
            }
            #endregion

            #region write the data

            // the codewords are stored in blocks of 32 bits
            uint datablock = 0;
            byte bitsLeftToWrite = 32;

            for (int i = 0; i < inLength; i++)
            {
                byte data = inputData[i];
                HuffTreeNode node = leaves[data];
                // the depth of the node is the length of the codeword required to encode the byte
                int depth = node.Depth;
                bool[] path = new bool[depth];
                for (int d = 0; d < depth; d++)
                {
                    path[depth - d - 1] = node.IsChild1;
                    node = node.Parent;
                }
                for (int d = 0; d < depth; d++)
                {
                    if (bitsLeftToWrite == 0)
                    {
                        outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                        compressedLength += 4;
                        datablock = 0;
                        bitsLeftToWrite = 32;
                    }
                    bitsLeftToWrite--;
                    if (path[d])
                        datablock |= (uint)(1 << bitsLeftToWrite);
                    // no need to OR the buffer with 0 if it is child0
                }
            }

            // write the partly filled data block as well
            if (bitsLeftToWrite != 32)
            {
                outstream.Write(IOUtils.FromNDSu32(datablock), 0, 4);
                compressedLength += 4;
            }

            #endregion

            return compressedLength;
        }