diff --git a/ArcFormats/ArcCommon.cs b/ArcFormats/ArcCommon.cs index 8cd66218..ded88198 100644 --- a/ArcFormats/ArcCommon.cs +++ b/ArcFormats/ArcCommon.cs @@ -153,87 +153,6 @@ namespace GameRes.Formats } } - public class HuffmanDecoder - { - byte[] m_src; - byte[] m_dst; - - ushort[] lhs = new ushort[512]; - ushort[] rhs = new ushort[512]; - ushort token = 256; - - int m_input_pos; - int m_remaining; - int m_cached_bits; - int m_cache; - - public HuffmanDecoder (byte[] src, int index, int length, byte[] dst) - { - m_src = src; - m_dst = dst; - m_input_pos = index; - m_remaining = length; - m_cached_bits = 0; - m_cache = 0; - } - - public HuffmanDecoder (byte[] src, byte[] dst) : this (src, 0, src.Length, dst) - { - } - - public byte[] Unpack () - { - int dst = 0; - token = 256; - ushort v3 = CreateTree(); - while (dst < m_dst.Length) - { - ushort symbol = v3; - while ( symbol >= 0x100u ) - { - if ( 0 != GetBits (1) ) - symbol = rhs[symbol]; - else - symbol = lhs[symbol]; - } - m_dst[dst++] = (byte)symbol; - } - return m_dst; - } - - ushort CreateTree() - { - if ( 0 != GetBits (1) ) - { - ushort v = token++; - lhs[v] = CreateTree(); - rhs[v] = CreateTree(); - return v; - } - else - { - return (ushort)GetBits (8); - } - } - - uint GetBits (int n) - { - while (n > m_cached_bits) - { - if (0 == m_remaining) - throw new ApplicationException ("Invalid huffman-compressed stream"); - int v = m_src[m_input_pos++]; - --m_remaining; - m_cache = v | (m_cache << 8); - m_cached_bits += 8; - } - uint mask = (uint)m_cache; - m_cached_bits -= n; - m_cache &= ~(-1 << m_cached_bits); - return (uint)(((-1 << m_cached_bits) & mask) >> m_cached_bits); - } - } - /// /// Create stream in TGA format from the given image pixels. /// diff --git a/ArcFormats/HuffmanCompression.cs b/ArcFormats/HuffmanCompression.cs new file mode 100644 index 00000000..696c9c39 --- /dev/null +++ b/ArcFormats/HuffmanCompression.cs @@ -0,0 +1,140 @@ +//! \file HuffmanCompression.cs +//! \brief Huffman-compressed streams. +// +// C# implementation Copyright (C) 2014-2018 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.IO; +using GameRes.Formats; + +namespace GameRes.Compression +{ + public class HuffmanStream : PackedStream + { + public HuffmanStream (Stream input, bool leave_open = false) : base (input, leave_open) + { + } + } + + public class HuffmanDecompressor : Decompressor + { + MsbBitStream m_input; + + public override void Initialize (Stream input) + { + m_input = new MsbBitStream (input, true); + } + + const int TreeSize = 512; + + ushort[] lhs = new ushort[TreeSize]; + ushort[] rhs = new ushort[TreeSize]; + ushort m_token = 256; + + protected override IEnumerator Unpack () + { + m_token = 256; + ushort root = CreateTree(); + for (;;) + { + ushort symbol = root; + while (symbol >= 0x100) + { + int bit = m_input.GetBits (1); + if (-1 == bit) + yield break; + if (bit != 0) + symbol = rhs[symbol]; + else + symbol = lhs[symbol]; + } + m_buffer[m_pos++] = (byte)symbol; + if (0 == --m_length) + yield return m_pos; + } + } + + ushort CreateTree () + { + int bit = m_input.GetBits (1); + if (-1 == bit) + { + throw new EndOfStreamException ("Unexpected end of the Huffman-compressed stream."); + } + else if (bit != 0) + { + ushort v = m_token++; + if (v >= TreeSize) + throw new InvalidFormatException ("Invalid Huffman-compressed stream."); + lhs[v] = CreateTree(); + rhs[v] = CreateTree(); + return v; + } + else + { + return (ushort)m_input.GetBits (8); + } + } + + #region IDisposable Members + bool m_disposed = false; + protected override void Dispose (bool disposing) + { + if (disposing && !m_disposed) + { + m_input.Dispose(); + m_disposed = true; + } + } + #endregion + } + + public class HuffmanDecoder + { + byte[] m_src; + byte[] m_dst; + int m_input_pos; + int m_remaining; + + public HuffmanDecoder (byte[] src, int index, int length, byte[] dst) + { + m_src = src; + m_dst = dst; + m_input_pos = index; + m_remaining = length; + } + + public HuffmanDecoder (byte[] src, byte[] dst) : this (src, 0, src.Length, dst) + { + } + + public byte[] Unpack () + { + using (var packed = new BinMemoryStream (m_src, m_input_pos, m_remaining)) + using (var hstr = new HuffmanStream (packed)) + { + hstr.Read (m_dst, 0, m_dst.Length); + return m_dst; + } + } + } +}