diff --git a/BurnOutSharp/External/libmspack/CHM/Decompressor.cs b/BurnOutSharp/External/libmspack/CHM/Decompressor.cs index 97bffc8b..cb2706d4 100644 --- a/BurnOutSharp/External/libmspack/CHM/Decompressor.cs +++ b/BurnOutSharp/External/libmspack/CHM/Decompressor.cs @@ -19,6 +19,7 @@ using System.IO; using System.Text; using LibMSPackSharp.Compression; using static LibMSPackSharp.Constants; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.CHM { @@ -242,7 +243,7 @@ namespace LibMSPackSharp.CHM return Error = err; // Validate reset_interval - if (lzxControlData.ResetInterval == 0 || (lzxControlData.ResetInterval % LZX.LZX_FRAME_SIZE) != 0) + if (lzxControlData.ResetInterval == 0 || (lzxControlData.ResetInterval % LZX_FRAME_SIZE) != 0) { Console.WriteLine("Bad controldata reset interval"); return Error = Error.MSPACK_ERR_DATAFORMAT; @@ -252,7 +253,7 @@ namespace LibMSPackSharp.CHM int entry = (int)(file.Offset / lzxControlData.ResetInterval); // Convert from reset interval multiple (usually 64k) to 32k frames - entry *= (int)lzxControlData.ResetInterval / LZX.LZX_FRAME_SIZE; + entry *= (int)lzxControlData.ResetInterval / LZX_FRAME_SIZE; // Read the reset table entry if (ReadResetTable(sec, (uint)entry, out long length, out long offset)) @@ -284,11 +285,11 @@ namespace LibMSPackSharp.CHM State.InOffset = file.Section.Header.Sec0.Offset + sec.Content.Offset + offset; // Set start offset and overall remaining stream length - State.Offset = entry * LZX.LZX_FRAME_SIZE; + State.Offset = entry * LZX_FRAME_SIZE; length -= State.Offset; // Initialise LZX stream - State.State = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, windowBits, (int)lzxControlData.ResetInterval / LZX.LZX_FRAME_SIZE, 4096, length, false); + State.State = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, windowBits, (int)lzxControlData.ResetInterval / LZX_FRAME_SIZE, 4096, length, false); if (State.State == null) Error = Error.MSPACK_ERR_NOMEMORY; @@ -1081,7 +1082,7 @@ namespace LibMSPackSharp.CHM return false; // Check sanity of reset table - if (lzxResetTable.FrameLength != LZX.LZX_FRAME_SIZE) + if (lzxResetTable.FrameLength != LZX_FRAME_SIZE) { Console.WriteLine("Bad reset table frame length"); return false; diff --git a/BurnOutSharp/External/libmspack/CHM/_LZXControlData.cs b/BurnOutSharp/External/libmspack/CHM/_LZXControlData.cs index 6d8e5687..c8f3ed09 100644 --- a/BurnOutSharp/External/libmspack/CHM/_LZXControlData.cs +++ b/BurnOutSharp/External/libmspack/CHM/_LZXControlData.cs @@ -8,7 +8,7 @@ */ using System; -using LibMSPackSharp.Compression; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.CHM { @@ -94,8 +94,8 @@ namespace LibMSPackSharp.CHM controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010); break; case 2: - controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C) * LZX.LZX_FRAME_SIZE; - controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010) * LZX.LZX_FRAME_SIZE; + controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C) * LZX_FRAME_SIZE; + controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010) * LZX_FRAME_SIZE; break; default: return Error.MSPACK_ERR_DATAFORMAT; diff --git a/BurnOutSharp/External/libmspack/Compression/BufferState.cs b/BurnOutSharp/External/libmspack/Compression/BufferState.cs new file mode 100644 index 00000000..4d52c162 --- /dev/null +++ b/BurnOutSharp/External/libmspack/Compression/BufferState.cs @@ -0,0 +1,78 @@ +/* This file is part of libmspack. + * (C) 2003-2013 Stuart Caie. + * + * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted + * by Microsoft Corporation. + * + * libmspack is free software { get; set; } you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +namespace LibMSPackSharp.Compression +{ + public class BufferState + { + /// + /// i_ptr + /// + public int InputPointer { get; set; } + + /// + /// i_end + /// + public int InputEnd { get; set; } + + /// + /// bit_buffer + /// + public uint BitBuffer { get; set; } + + /// + /// bits_left + /// + public int BitsLeft { get; set; } + + #region Common + + /// + /// Initialises bitstream state in state structure + /// + public void Init() + { + InputPointer = 0; + InputEnd = 0; + BitBuffer = 0; + BitsLeft = 0; + } + + #endregion + + #region MSB + + /// + /// Removes N bits from the bit buffer + /// + public void REMOVE_BITS_MSB(int nbits) + { + BitBuffer <<= nbits; + BitsLeft -= nbits; + } + + #endregion + + #region LSB + + /// + /// Removes N bits from the bit buffer + /// + public void REMOVE_BITS_LSB(int nbits) + { + BitBuffer >>= nbits; + BitsLeft -= nbits; + } + + #endregion + } +} diff --git a/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs b/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs index 1d01bc06..1ddeac7d 100644 --- a/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs +++ b/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs @@ -17,42 +17,17 @@ namespace LibMSPackSharp.Compression { public abstract class CompressionStream : BaseDecompressState { - #region Constants - - /// - /// Number of bits in a character - /// - private const int CHAR_BIT = 8; - - /// - /// Bit width of a UInt32 bit buffer - /// - public const int BITBUF_WIDTH = 4 * CHAR_BIT; - - /// - /// Maximum bits in a Huffman code - /// - public const int HUFF_MAXBITS = 16; - - #endregion - #region I/O buffering public byte[] InputBuffer { get; set; } public uint InputBufferSize { get; set; } - public int InputPointer { get; set; } - - public int InputEnd { get; set; } - public int OutputPointer { get; set; } public int OutputEnd { get; set; } - public uint BitBuffer { get; set; } - - public int BitsLeft { get; set; } + internal BufferState BufferState { get; set; } /// /// Have we reached the end of input? @@ -137,43 +112,44 @@ namespace LibMSPackSharp.Compression /// public void INIT_BITS() { - InputPointer = 0; - InputEnd = 0; - BitBuffer = 0; - BitsLeft = 0; + BufferState = new BufferState(); + BufferState.Init(); EndOfInput = 0; } /// /// Stores bitstream state in state structure /// - public void STORE_BITS(int i_ptr, int i_end, uint bit_buffer, int bits_left) + public void STORE_BITS(BufferState state) { - InputPointer = i_ptr; - InputEnd = i_end; - BitBuffer = bit_buffer; - BitsLeft = bits_left; + BufferState.InputPointer = state.InputPointer; + BufferState.InputEnd = state.InputEnd; + BufferState.BitBuffer = state.BitBuffer; + BufferState.BitsLeft = state.BitsLeft; } /// /// Restores bitstream state from state structure /// - public void RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left) + public BufferState RESTORE_BITS() { - i_ptr = InputPointer; - i_end = InputEnd; - bit_buffer = BitBuffer; - bits_left = BitsLeft; + return new BufferState() + { + InputPointer = BufferState.InputPointer, + InputEnd = BufferState.InputEnd, + BitBuffer = BufferState.BitBuffer, + BitsLeft = BufferState.BitsLeft, + }; } /// /// Ensure there are at least N bits in the bit buffer /// - public void ENSURE_BITS(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public void ENSURE_BITS(int nbits, BufferState state) { - while (bits_left < nbits) + while (state.BitsLeft < nbits) { - READ_BYTES(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + READ_BYTES(state); if (Error != Error.MSPACK_ERR_OK) return; } @@ -182,23 +158,23 @@ namespace LibMSPackSharp.Compression /// /// Read from the input if the buffer is empty /// - public void READ_IF_NEEDED(ref int i_ptr, ref int i_end) + public void READ_IF_NEEDED(BufferState state) { - if (i_ptr >= i_end) + if (state.InputPointer >= state.InputEnd) { ReadInput(); if (Error != Error.MSPACK_ERR_OK) return; - i_ptr = InputPointer; - i_end = InputEnd; + state.InputPointer = BufferState.InputPointer; + state.InputEnd = BufferState.InputEnd; } } /// /// Read bytes from the input into the bit buffer /// - public abstract void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left); + public abstract void READ_BYTES(BufferState state); /// /// Read an input stream and fill the buffer @@ -231,8 +207,8 @@ namespace LibMSPackSharp.Compression } // Update i_ptr and i_end - InputPointer = 0; - InputEnd = read; + BufferState.InputPointer = 0; + BufferState.InputEnd = read; } #endregion @@ -242,10 +218,10 @@ namespace LibMSPackSharp.Compression /// /// Inject data into the bit buffer /// - public void INJECT_BITS_MSB(int bitdata, int nbits, ref uint bit_buffer, ref int bits_left) + public void INJECT_BITS_MSB(int bitdata, int nbits, BufferState state) { - bit_buffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - bits_left)); - bits_left += nbits; + state.BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - state.BitsLeft)); + state.BitsLeft += nbits; } /// @@ -256,52 +232,43 @@ namespace LibMSPackSharp.Compression /// /// Takes N bits from the buffer and puts them in var /// - public long READ_BITS_MSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_BITS_MSB(int nbits, BufferState state) { - ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + ENSURE_BITS(nbits, state); if (Error != Error.MSPACK_ERR_OK) return -1; - long temp = PEEK_BITS_MSB(nbits, bit_buffer); + long temp = PEEK_BITS_MSB(nbits, state.BitBuffer); - REMOVE_BITS_MSB(nbits, ref bit_buffer, ref bits_left); + state.REMOVE_BITS_MSB(nbits); return temp; } /// /// Read multiple bits and put them in var /// - public long READ_MANY_BITS_MSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_MANY_BITS_MSB(int nbits, BufferState state) { byte needed = (byte)(nbits), bitrun; long temp = 0; while (needed > 0) { - if (bits_left <= (BITBUF_WIDTH - 16)) + if (state.BitsLeft <= (BITBUF_WIDTH - 16)) { - READ_BYTES(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + READ_BYTES(state); if (Error != Error.MSPACK_ERR_OK) return -1; } - bitrun = (byte)((bits_left < needed) ? bits_left : needed); - temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun, bit_buffer); - REMOVE_BITS_MSB(bitrun, ref bit_buffer, ref bits_left); + bitrun = (byte)((state.BitsLeft < needed) ? state.BitsLeft : needed); + temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun, state.BitBuffer); + state.REMOVE_BITS_MSB(bitrun); needed -= bitrun; } return temp; } - /// - /// Removes N bits from the bit buffer - /// - public void REMOVE_BITS_MSB(int nbits, ref uint bit_buffer, ref int bits_left) - { - bit_buffer <<= nbits; - bits_left -= nbits; - } - #endregion #region LSB @@ -309,10 +276,10 @@ namespace LibMSPackSharp.Compression /// /// Inject data into the bit buffer /// - public void INJECT_BITS_LSB(int bitdata, int nbits, ref uint bit_buffer, ref int bits_left) + public void INJECT_BITS_LSB(int bitdata, int nbits, BufferState state) { - bit_buffer |= (uint)(bitdata << bits_left); - bits_left += nbits; + state.BitBuffer |= (uint)(bitdata << state.BitsLeft); + state.BitsLeft += nbits; } /// @@ -323,47 +290,38 @@ namespace LibMSPackSharp.Compression /// /// Extracts without removing N bits from the bit buffer using a bit mask /// - public long PEEK_BITS_T_LSB(int nbits, uint bit_buffer) => bit_buffer & lsb_bit_mask[(nbits)]; + public long PEEK_BITS_T_LSB(int nbits, uint bit_buffer) => bit_buffer & LSBBitMask[(nbits)]; /// /// Takes N bits from the buffer and puts them in var /// - public long READ_BITS_LSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_BITS_LSB(int nbits, BufferState state) { - ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + ENSURE_BITS(nbits, state); if (Error != Error.MSPACK_ERR_OK) return -1; - long temp = PEEK_BITS_LSB(nbits, bit_buffer); + long temp = PEEK_BITS_LSB(nbits, state.BitBuffer); - REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left); + state.REMOVE_BITS_LSB(nbits); return temp; } /// /// Takes N bits from the buffer and puts them in var using a bit mask /// - public long READ_BITS_T_LSB(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_BITS_T_LSB(int nbits, BufferState state) { - ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + ENSURE_BITS(nbits, state); if (Error != Error.MSPACK_ERR_OK) return -1; - long temp = PEEK_BITS_T_LSB(nbits, bit_buffer); + long temp = PEEK_BITS_T_LSB(nbits, state.BitBuffer); - REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left); + state.REMOVE_BITS_LSB(nbits); return temp; } - /// - /// Removes N bits from the bit buffer - /// - public void REMOVE_BITS_LSB(int nbits, ref uint bit_buffer, ref int bits_left) - { - bit_buffer >>= nbits; - bits_left -= nbits; - } - #endregion #endregion @@ -385,14 +343,14 @@ namespace LibMSPackSharp.Compression /// Decodes the next huffman symbol from the input bitstream into var. /// Do not use this macro on a table unless build_decode_table() succeeded. /// - public long READ_HUFFSYM_MSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_HUFFSYM_MSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state) { - ENSURE_BITS(HUFF_MAXBITS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - ushort sym = table[PEEK_BITS_MSB(tablebits, bit_buffer)]; + ENSURE_BITS(HUFF_MAXBITS, state); + ushort sym = table[PEEK_BITS_MSB(tablebits, state.BitBuffer)]; if (sym >= maxsymbols) - HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols, bit_buffer); + HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols, state.BitBuffer); - REMOVE_BITS_MSB(lengths[sym], ref bit_buffer, ref bits_left); + state.REMOVE_BITS_MSB(lengths[sym]); return sym; } @@ -523,14 +481,14 @@ namespace LibMSPackSharp.Compression /// Decodes the next huffman symbol from the input bitstream into var. /// Do not use this macro on a table unless build_decode_table() succeeded. /// - public long READ_HUFFSYM_LSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_HUFFSYM_LSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state) { - ENSURE_BITS(HUFF_MAXBITS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - ushort sym = table[PEEK_BITS_LSB(tablebits, bit_buffer)]; + ENSURE_BITS(HUFF_MAXBITS, state); + ushort sym = table[PEEK_BITS_LSB(tablebits, state.BitBuffer)]; if (sym >= maxsymbols) - HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols, bit_buffer); + HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols, state.BitBuffer); - REMOVE_BITS_LSB(lengths[sym], ref bit_buffer, ref bits_left); + state.REMOVE_BITS_LSB(lengths[sym]); return sym; } diff --git a/BurnOutSharp/External/libmspack/Compression/Constants.cs b/BurnOutSharp/External/libmspack/Compression/Constants.cs index f205d21f..2ebc4788 100644 --- a/BurnOutSharp/External/libmspack/Compression/Constants.cs +++ b/BurnOutSharp/External/libmspack/Compression/Constants.cs @@ -11,11 +11,282 @@ namespace LibMSPackSharp.Compression { internal static class Constants { - /* lsb_bit_mask[n] = (1 << n) - 1 */ - internal static readonly ushort[] lsb_bit_mask = new ushort[17] + #region readbits.h + + /// + /// Bit width of a UInt32 bit buffer + /// + public const int BITBUF_WIDTH = 4 * CHAR_BIT; + + /// + /// Number of bits in a character + /// + internal const int CHAR_BIT = 8; + + // lsb_bit_mask[n] = (1 << n) - 1 + internal static readonly ushort[] LSBBitMask = new ushort[17] { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; + + #endregion + + #region readhuff.h + + /// + /// Maximum bits in a Huffman code + /// + public const int HUFF_MAXBITS = 16; + + #endregion + + #region LZSS + + /// + /// Size of an LZSS window + /// + public const int LZSS_WINDOW_SIZE = 4096; + + /// + /// LZSS window fill byte + /// + public const byte LZSS_WINDOW_FILL = 0x20; + + #endregion + + #region LZX + + // Some constants defined by the LZX specification + public const int LZX_MIN_MATCH = 2; + public const int LZX_MAX_MATCH = 257; + public const int LZX_NUM_CHARS = 256; + + public const int LZX_PRETREE_NUM_ELEMENTS = 20; + public const int LZX_ALIGNED_NUM_ELEMENTS = 8; // Aligned offset tree #elements + public const int LZX_NUM_PRIMARY_LENGTHS = 7; // This one missing from spec! + public const int LZX_NUM_SECONDARY_LENGTHS = 249; // Length tree #elements + + // LZX huffman defines: tweak tablebits as desired + + public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS; + public const byte LZX_PRETREE_TABLEBITS = 6; + public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8; + public const byte LZX_MAINTREE_TABLEBITS = 12; + public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1; + public const byte LZX_LENGTH_TABLEBITS = 12; + public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS; + public const byte LZX_ALIGNED_TABLEBITS = 7; + public const int LZX_LENTABLE_SAFETY = 64; // Table decoding overruns are allowed + + public const int LZX_FRAME_SIZE = 32768; // The size of a frame in LZX + + #region LZX static data tables + + /* LZX static data tables: + * + * LZX uses 'position slots' to represent match offsets. For every match, + * a small 'position slot' number and a small offset from that slot are + * encoded instead of one large offset. + * + * The number of slots is decided by how many are needed to encode the + * largest offset for a given window size. This is easy when the gap between + * slots is less than 128Kb, it's a linear relationship. But when extra_bits + * reaches its limit of 17 (because LZX can only ensure reading 17 bits of + * data at a time), we can only jump 128Kb at a time and have to start + * using more and more position slots as each window size doubles. + * + * position_base[] is an index to the position slot bases + * + * extra_bits[] states how many bits of offset-from-base data is needed. + * + * They are calculated as follows: + * extra_bits[i] = 0 where i < 4 + * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36 + * extra_bits[i] = 17 where i >= 36 + * position_base[0] = 0 + * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1]) + */ + + public static readonly uint[] LZXPositionSlots = new uint[11] + { + 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 + }; + + public static readonly byte[] LZXExtraBits = new byte[36] + { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16 + }; + + public static readonly uint[] LZXPositionBase = new uint[290] + { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, + 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, + 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, + 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, + 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512, + 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088, + 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664, + 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240, + 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816, + 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392, + 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968, + 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544, + 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048, + 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552, + 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056, + 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560, + 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064, + 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568, + 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072, + 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576, + 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080, + 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584, + 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088, + 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592, + 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096, + 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600, + 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104, + 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608, + 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112, + 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616, + 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120, + 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624, + 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128, + 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632, + 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136, + 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640, + 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144, + 33161216, 33292288, 33423360 + }; + + #endregion + + #endregion + + #region MSZIP + + public const int MSZIP_FRAME_SIZE = 32768; // Size of LZ history window + public const int MSZIP_LITERAL_MAXSYMBOLS = 288; // literal/length huffman tree + public const int MSZIP_LITERAL_TABLEBITS = 9; + public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; // Distance huffman tree + public const int MSZIP_DISTANCE_TABLEBITS = 6; + + // If there are less direct lookup entries than symbols, the longer + // code pointers will be <= maxsymbols. This must not happen, or we + // will decode entries badly + + //public const int MSZIP_LITERAL_TABLESIZE = (MSZIP_LITERAL_MAXSYMBOLS * 4); + public const int MSZIP_LITERAL_TABLESIZE = ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2)); + + //public const int MSZIP_DISTANCE_TABLESIZE = (MSZIP_DISTANCE_MAXSYMBOLS * 4); + public const int MSZIP_DISTANCE_TABLESIZE = ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2)); + + /// + /// Match lengths for literal codes 257.. 285 + /// + public static readonly ushort[] LiteralLengths = new ushort[29] + { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + + /// + /// Match offsets for distance codes 0 .. 29 + /// + public static readonly ushort[] DistanceOffsets = new ushort[30] + { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, + 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 + }; + + /// + /// Extra bits required for literal codes 257.. 285 + /// + public static readonly byte[] LiteralExtraBits = new byte[29] + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, + 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + /// + /// Extra bits required for distance codes 0 .. 29 + /// + public static readonly byte[] DistanceExtraBits = new byte[30] + { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 + }; + + /// + /// The order of the bit length Huffman code lengths + /// + public static readonly byte[] BitLengthOrder = new byte[19] + { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + #endregion + + #region QTM + + public const int QTM_FRAME_SIZE = 32768; + + /* Quantum static data tables: + * + * Quantum uses 'position slots' to represent match offsets. For every + * match, a small 'position slot' number and a small offset from that slot + * are encoded instead of one large offset. + * + * position_base[] is an index to the position slot bases + * + * extra_bits[] states how many bits of offset-from-base data is needed. + * + * length_base[] and length_extra[] are equivalent in function, but are + * used for encoding selector 6 (variable length match) match lengths, + * instead of match offsets. + * + * They are generated with the following code: + * uint i, offset; + * for (i = 0, offset = 0; i < 42; i++) { + * position_base[i] = offset; + * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; + * offset += 1 << extra_bits[i]; + * } + * for (i = 0, offset = 0; i < 26; i++) { + * length_base[i] = offset; + * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; + * offset += 1 << length_extra[i]; + * } + * length_base[26] = 254; length_extra[26] = 0; + */ + + public static readonly uint[] QTMPositionBase = new uint[42] + { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, + 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864 + }; + + public static readonly byte[] QTMExtraBits = new byte[42] + { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 + }; + + public static readonly byte[] QTMLengthBase = new byte[27] + { + 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, + 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254 + }; + + public static readonly byte[] QTMLengthExtra = new byte[27] + { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + #endregion } } diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs index b72977ea..13a0004c 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs @@ -9,6 +9,7 @@ using System.IO; using static LibMSPackSharp.Constants; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { @@ -47,39 +48,39 @@ namespace LibMSPackSharp.Compression // Reset global state lzh.INIT_BITS(); - lzh.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); + BufferState state = lzh.RESTORE_BITS(); - for (i = 0; i < LZSS.LZSS_WINDOW_SIZE; i++) + for (i = 0; i < LZSS_WINDOW_SIZE; i++) { - lzh.Window[i] = LZSS.LZSS_WINDOW_FILL; + lzh.Window[i] = LZSS_WINDOW_FILL; } // Read 6 encoding types (for byte alignment) but only 5 are needed for (i = 0; i < 6; i++) { - types[i] = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + types[i] = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; } // Read huffman table symbol lengths and build huffman trees - BUILD_TREE(lzh, types[0], lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - BUILD_TREE(lzh, types[1], lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - BUILD_TREE(lzh, types[2], lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - BUILD_TREE(lzh, types[3], lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - BUILD_TREE(lzh, types[4], lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + BUILD_TREE(lzh, types[0], lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS, state); + BUILD_TREE(lzh, types[1], lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS, state); + BUILD_TREE(lzh, types[2], lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS, state); + BUILD_TREE(lzh, types[3], lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS, state); + BUILD_TREE(lzh, types[4], lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS, state); while (lzh.EndOfInput == 0) { if (lit_run != 0) { - len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN2_table, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; } else { - len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + len = (int)lzh.READ_HUFFSYM_SAFE(lzh.MATCHLEN1_table, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; } @@ -89,13 +90,13 @@ namespace LibMSPackSharp.Compression len += 2; lit_run = 0; // Not the end of a literal run - j = (int)lzh.READ_HUFFSYM_SAFE(lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + j = (int)lzh.READ_HUFFSYM_SAFE(lzh.OFFSET_table, lzh.OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; offset = j << 6; - j = (int)lzh.READ_BITS_SAFE(6, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + j = (int)lzh.READ_BITS_SAFE(6, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -115,7 +116,7 @@ namespace LibMSPackSharp.Compression } else { - len = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + len = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITLEN_table, lzh.LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -123,7 +124,7 @@ namespace LibMSPackSharp.Compression lit_run = (len == 32) ? 0 : 1; // End of a literal run? while (len-- > 0) { - j = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + j = (int)lzh.READ_HUFFSYM_SAFE(lzh.LITERAL_table, lzh.LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -142,15 +143,19 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - private static Error BUILD_TREE(LZHKWAJStream lzh, uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + private static Error BUILD_TREE(LZHKWAJStream lzh, uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state) { - lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + lzh.STORE_BITS(state); Error err = ReadLens(lzh, type, (uint)maxsymbols, lzh.MATCHLEN1_len); if (err != Error.MSPACK_ERR_OK) return err; - lzh.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); + BufferState temp = lzh.RESTORE_BITS(); + state.InputPointer = temp.InputPointer; + state.InputEnd = temp.InputEnd; + state.BitBuffer = temp.BitBuffer; + state.BitsLeft = temp.BitsLeft; if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table)) return Error.MSPACK_ERR_DATAFORMAT; @@ -162,7 +167,7 @@ namespace LibMSPackSharp.Compression { uint i, c, sel; - lzh.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); + BufferState state = lzh.RESTORE_BITS(); switch (type) { @@ -177,14 +182,14 @@ namespace LibMSPackSharp.Compression break; case 1: - c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + c = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; lens[0] = (byte)c; for (i = 1; i < numsyms; i++) { - sel = (uint)lzh.READ_BITS_SAFE(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + sel = (uint)lzh.READ_BITS_SAFE(1, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -194,7 +199,7 @@ namespace LibMSPackSharp.Compression } else { - sel = (uint)lzh.READ_BITS_SAFE(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + sel = (uint)lzh.READ_BITS_SAFE(1, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -204,7 +209,7 @@ namespace LibMSPackSharp.Compression } else { - c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + c = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -216,20 +221,20 @@ namespace LibMSPackSharp.Compression break; case 2: - c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + c = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; lens[0] = (byte)c; for (i = 1; i < numsyms; i++) { - sel = (uint)lzh.READ_BITS_SAFE(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + sel = (uint)lzh.READ_BITS_SAFE(2, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; if (sel == 3) { - c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + c = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; } @@ -246,7 +251,7 @@ namespace LibMSPackSharp.Compression case 3: for (i = 0; i < numsyms; i++) { - c = (uint)lzh.READ_BITS_SAFE(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + c = (uint)lzh.READ_BITS_SAFE(4, state); if (lzh.Error == Error.MSPACK_ERR_NOMEMORY) return Error.MSPACK_ERR_OK; @@ -256,7 +261,7 @@ namespace LibMSPackSharp.Compression break; } - lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + lzh.STORE_BITS(state); return Error.MSPACK_ERR_OK; } diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs index 4f84eb4e..094c33e1 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs @@ -8,6 +8,7 @@ */ using static LibMSPackSharp.Constants; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { @@ -33,7 +34,7 @@ namespace LibMSPackSharp.Compression // History window - public byte[] Window { get; set; } = new byte[LZSS.LZSS_WINDOW_SIZE]; + public byte[] Window { get; set; } = new byte[LZSS_WINDOW_SIZE]; #endregion @@ -53,10 +54,10 @@ namespace LibMSPackSharp.Compression /// /// Safely read bits from the buffer /// - public long READ_BITS_SAFE(int nbits, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_BITS_SAFE(int nbits, BufferState state) { - long val = READ_BITS_MSB(nbits, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (EndOfInput != 0 && BitsLeft < EndOfInput) + long val = READ_BITS_MSB(nbits, state); + if (EndOfInput != 0 && BufferState.BitsLeft < EndOfInput) Error = Error.MSPACK_ERR_NOMEMORY; else Error = Error.MSPACK_ERR_OK; @@ -67,10 +68,10 @@ namespace LibMSPackSharp.Compression /// /// Safely read a symbol from a Huffman tree /// - public long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state) { - long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (EndOfInput != 0 && BitsLeft < EndOfInput) + long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols, state); + if (EndOfInput != 0 && BufferState.BitsLeft < EndOfInput) Error = Error.MSPACK_ERR_NOMEMORY; else Error = Error.MSPACK_ERR_OK; @@ -93,19 +94,19 @@ namespace LibMSPackSharp.Compression public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT; /// - public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public override void READ_BYTES(BufferState state) { - if (i_ptr >= i_end) + if (state.InputPointer >= state.InputEnd) { ReadInput(); if (Error != Error.MSPACK_ERR_OK) return; - i_ptr = InputPointer; - i_end = InputEnd; + state.InputPointer = BufferState.InputPointer; + state.InputEnd = BufferState.InputEnd; } - INJECT_BITS_MSB(InputBuffer[i_ptr++], 8, ref bit_buffer, ref bits_left); + INJECT_BITS_MSB(InputBuffer[state.InputPointer++], 8, state); } /// @@ -129,15 +130,15 @@ namespace LibMSPackSharp.Compression if (read == 0) { - InputEnd = 8; + BufferState.InputEnd = 8; InputBuffer[0] = 0; read = 1; } } // Update InputPointer and InputLength - InputPointer = 0; - InputEnd = read; + BufferState.InputPointer = 0; + BufferState.InputEnd = read; } } } diff --git a/BurnOutSharp/External/libmspack/Compression/LZSS.cs b/BurnOutSharp/External/libmspack/Compression/LZSS.cs index 082d6075..f9859432 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZSS.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZSS.cs @@ -12,18 +12,12 @@ */ using System.IO; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { public class LZSS { - #region LZSS compression / decompression definitions - - public const int LZSS_WINDOW_SIZE = 4096; - public const byte LZSS_WINDOW_FILL = 0x20; - - #endregion - /// /// Decompresses an LZSS stream. /// diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.cs b/BurnOutSharp/External/libmspack/Compression/LZX.cs index 667a6c09..680311a0 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZX.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZX.cs @@ -76,142 +76,12 @@ using System; using System.IO; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { public class LZX { - #region LZX compression / decompression definitions - - // Some constants defined by the LZX specification - public const int LZX_MIN_MATCH = 2; - public const int LZX_MAX_MATCH = 257; - public const int LZX_NUM_CHARS = 256; - - public const int LZX_PRETREE_NUM_ELEMENTS = 20; - public const int LZX_ALIGNED_NUM_ELEMENTS = 8; // Aligned offset tree #elements - public const int LZX_NUM_PRIMARY_LENGTHS = 7; // This one missing from spec! - public const int LZX_NUM_SECONDARY_LENGTHS = 249; // Length tree #elements - - // LZX huffman defines: tweak tablebits as desired - - public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS; - public const byte LZX_PRETREE_TABLEBITS = 6; - public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8; - public const byte LZX_MAINTREE_TABLEBITS = 12; - public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1; - public const byte LZX_LENGTH_TABLEBITS = 12; - public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS; - public const byte LZX_ALIGNED_TABLEBITS = 7; - public const int LZX_LENTABLE_SAFETY = 64; // Table decoding overruns are allowed - - public const int LZX_FRAME_SIZE = 32768; // The size of a frame in LZX - - #endregion - - #region LZX static data tables - - /* LZX static data tables: - * - * LZX uses 'position slots' to represent match offsets. For every match, - * a small 'position slot' number and a small offset from that slot are - * encoded instead of one large offset. - * - * The number of slots is decided by how many are needed to encode the - * largest offset for a given window size. This is easy when the gap between - * slots is less than 128Kb, it's a linear relationship. But when extra_bits - * reaches its limit of 17 (because LZX can only ensure reading 17 bits of - * data at a time), we can only jump 128Kb at a time and have to start - * using more and more position slots as each window size doubles. - * - * position_base[] is an index to the position slot bases - * - * extra_bits[] states how many bits of offset-from-base data is needed. - * - * They are calculated as follows: - * extra_bits[i] = 0 where i < 4 - * extra_bits[i] = floor(i/2)-1 where i >= 4 && i < 36 - * extra_bits[i] = 17 where i >= 36 - * position_base[0] = 0 - * position_base[i] = position_base[i-1] + (1 << extra_bits[i-1]) - */ - - private static readonly uint[] position_slots = new uint[11] - { - 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 - }; - - private static readonly byte[] extra_bits = new byte[36] - { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, - 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16 - }; - - private static readonly uint[] position_base = new uint[290] - { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, - 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, - 49152, 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, - 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, - 1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512, - 2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088, - 3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664, - 4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240, - 6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816, - 7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392, - 8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968, - 9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544, - 10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048, - 11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552, - 12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056, - 12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560, - 13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064, - 14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568, - 15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072, - 16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576, - 17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080, - 18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584, - 19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088, - 20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592, - 21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096, - 22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600, - 23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104, - 23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608, - 24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112, - 25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616, - 26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120, - 27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624, - 28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128, - 29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632, - 30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136, - 31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640, - 32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144, - 33161216, 33292288, 33423360 - }; - - private static void ResetState(LZXDStream lzx) - { - lzx.R0 = 1; - lzx.R1 = 1; - lzx.R2 = 1; - lzx.HeaderRead = 0; - lzx.BlockRemaining = 0; - lzx.BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0; - - // Initialise tables to 0 (because deltas will be applied to them) - for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) - { - lzx.MAINTREE_len[i] = 0; - } - - for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) - { - lzx.LENGTH_len[i] = 0; - } - } - - #endregion - /// /// Allocates and initialises LZX decompression state for decoding an LZX /// stream. @@ -268,9 +138,6 @@ namespace LibMSPackSharp.Compression /// public static LZXDStream Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int reset_interval, int input_buffer_size, long output_length, bool is_delta) { - uint window_size = (uint)(1 << window_bits); - LZXDStream lzx; - if (system == null) return null; @@ -299,10 +166,10 @@ namespace LibMSPackSharp.Compression return null; // Allocate decompression state - lzx = new LZXDStream() + LZXDStream lzx = new LZXDStream() { // Allocate decompression window and input buffer - Window = new byte[window_size], + Window = new byte[1 << window_bits], InputBuffer = new byte[input_buffer_size], System = system, @@ -321,15 +188,15 @@ namespace LibMSPackSharp.Compression IntelFileSize = 0, IntelStarted = false, Error = Error.MSPACK_ERR_OK, - NumOffsets = position_slots[window_bits - 15] << 3, + NumOffsets = LZXPositionSlots[window_bits - 15] << 3, IsDelta = is_delta, - // e8_buf OutputPointer = 0, OutputEnd = 0, + OutputIsE8 = true, }; - ResetState(lzx); + lzx.ResetState(); lzx.INIT_BITS(); return lzx; @@ -351,7 +218,7 @@ namespace LibMSPackSharp.Compression /// the length of the reference data. Cannot be longer /// than the LZX window size. /// - /// an error code, or MSPACK_ERR_OK if successful + /// An error code, or MSPACK_ERR_OK if successful public static Error SetReferenceData(LZXDStream lzx, SystemImpl system, FileStream input, uint length) { if (lzx == null) @@ -397,7 +264,7 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - // See description of outputLength in lzxd_init() + // See description of outputLength in Init() public static void SetOutputLength(LZXDStream lzx, long outputLength) { if (lzx != null && outputLength > 0) @@ -435,12 +302,8 @@ namespace LibMSPackSharp.Compression if (lzx == null) return Error.MSPACK_ERR_ARGS; - int match_length, length_footer, extra, verbatim_bits, bytes_todo; - int this_run, main_element, aligned_bits, j, warned = 0; - byte[] window, buf = new byte[12]; - int runsrc, rundest; - uint frame_size, end_frame, match_offset, window_posn; - uint R0, R1, R2; + int warned = 0; + byte[] buf = new byte[12]; // Easy answers if (lzx == null || (out_bytes < 0)) @@ -450,32 +313,30 @@ namespace LibMSPackSharp.Compression return lzx.Error; // Flush out any stored-up bytes before we begin - int i = lzx.OutputEnd - lzx.OutputPointer; - if (i > out_bytes) - i = (int)out_bytes; + int leftover_bytes = lzx.OutputEnd - lzx.OutputPointer; + if (leftover_bytes > out_bytes) + leftover_bytes = (int)out_bytes; - if (i != 0) + if (leftover_bytes != 0) { - try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); } + try { lzx.System.Write(lzx.OutputFileHandle, lzx.OutputIsE8 ? lzx.E8Buffer : lzx.Window, lzx.OutputPointer, leftover_bytes); } catch { return lzx.Error = Error.MSPACK_ERR_WRITE; } - lzx.OutputPointer += i; - lzx.Offset += i; - out_bytes -= i; + lzx.OutputPointer += leftover_bytes; + lzx.Offset += leftover_bytes; + out_bytes -= leftover_bytes; } if (out_bytes == 0) return Error.MSPACK_ERR_OK; // Restore local state - lzx.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); - window = lzx.Window; - window_posn = lzx.WindowPosition; - R0 = lzx.R0; - R1 = lzx.R1; - R2 = lzx.R2; + BufferState state = lzx.RESTORE_BITS(); + byte[] window = lzx.Window; + int window_posn = lzx.WindowPosition; + uint[] R = lzx.R; - end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1; + uint end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1; while (lzx.Frame < end_frame) { @@ -494,410 +355,93 @@ namespace LibMSPackSharp.Compression } // Re-read the intel header and reset the huffman lengths - ResetState(lzx); - R0 = lzx.R0; - R1 = lzx.R1; - R2 = lzx.R2; + lzx.ResetState(); + R = lzx.R; } // LZX DELTA format has chunk_size, not present in LZX format if (lzx.IsDelta) { - lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - lzx.REMOVE_BITS_MSB(16, ref bit_buffer, ref bits_left); + lzx.ENSURE_BITS(16, state); + state.REMOVE_BITS_MSB(16); } + //// Read header if necessary + //if (lzx.HeaderRead == 0) + //{ + // // Read 1 bit. If bit=0, intel_filesize = 0. + // // If bit=1, read intel filesize (32 bits) + // int j = 0; + // int i = (int)lzx.READ_BITS_MSB(1, state); + + // if (i != 0) + // { + // i = (int)lzx.READ_BITS_MSB(16, state); + // j = (int)lzx.READ_BITS_MSB(16, state); + // } + + // lzx.IntelFileSize = (i << 16) | j; + // lzx.HeaderRead = 1; + //} + // Calculate size of frame: all frames are 32k except the final frame // which is 32kb or less. this can only be calculated when lzx.Length // has been filled in. - frame_size = LZX_FRAME_SIZE; + uint frame_size = LZX_FRAME_SIZE; if (lzx.Length != 0 && (lzx.Length - lzx.Offset) < frame_size) frame_size = (uint)(lzx.Length - lzx.Offset); // Decode until one more frame is available - bytes_todo = (int)(lzx.FramePosition + frame_size - window_posn); + int bytes_todo = (int)(lzx.FramePosition + frame_size - window_posn); while (bytes_todo > 0) { - // Initialise new block, if one is needed - if (lzx.BlockRemaining == 0) + // Realign if previous block was an odd-sized UNCOMPRESSED block + if ((lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED) && (lzx.BlockLength & 1) != 0) { - // Realign if previous block was an odd-sized UNCOMPRESSED block - if ((lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED) && (lzx.BlockLength & 1) != 0) - { - lzx.READ_IF_NEEDED(ref i_ptr, ref i_end); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; + lzx.READ_IF_NEEDED(state); + if (lzx.Error != Error.MSPACK_ERR_OK) + return lzx.Error; - i_ptr++; - } - - // Read block type (3 bits) and block length (24 bits) - - // THIS IS NOT 3 BECAUSE OF OTHER CODE I FOUND - lzx.BlockType = (LZXBlockType)lzx.READ_BITS_MSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - // Read header if necessary - if (lzx.HeaderRead == 0) - { - // Read 1 bit. if bit=0, intel_filesize = 0. - // if bit=1, read intel filesize (32 bits) - j = 0; - i = (int)lzx.READ_BITS_MSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - if (i != 0) - { - i = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - j = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - } - - lzx.IntelFileSize = (i << 16) | j; - lzx.HeaderRead = 1; - } - - i = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - j = (int)lzx.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - lzx.BlockRemaining = lzx.BlockLength = (uint)((i << 8) | j); - // Console.WriteLine($"New block t {lzx.BlockType} len {lzx.BlockLength}"); - - // Read individual block headers - switch (lzx.BlockType) - { - case LZXBlockType.LZX_BLOCKTYPE_ALIGNED: - // Read lengths of and build aligned huffman decoding tree - for (i = 0; i < 8; i++) - { - j = (int)lzx.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - lzx.ALIGNED_len[i] = (byte)j; - } - - BUILD_TABLE(lzx, lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - // Read lengths of and build main huffman decoding tree - READ_LENGTHS(lzx, lzx.MAINTREE_len, 0, 256, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - READ_LENGTHS(lzx, lzx.MAINTREE_len, 256, LZX_NUM_CHARS + lzx.NumOffsets, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - BUILD_TABLE(lzx, lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - // If the literal 0xE8 is anywhere in the block... - if (lzx.MAINTREE_len[0xE8] != 0) - lzx.IntelStarted = true; - - // Read lengths of and build lengths huffman decoding tree - READ_LENGTHS(lzx, lzx.LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - BUILD_TABLE_MAYBE_EMPTY(lzx); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - break; - - case LZXBlockType.LZX_BLOCKTYPE_VERBATIM: - // Read lengths of and build main huffman decoding tree - READ_LENGTHS(lzx, lzx.MAINTREE_len, 0, 256, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - READ_LENGTHS(lzx, lzx.MAINTREE_len, 256, LZX_NUM_CHARS + lzx.NumOffsets, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - BUILD_TABLE(lzx, lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - // If the literal 0xE8 is anywhere in the block... - if (lzx.MAINTREE_len[0xE8] != 0) - lzx.IntelStarted = true; - - // Read lengths of and build lengths huffman decoding tree - READ_LENGTHS(lzx, lzx.LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - BUILD_TABLE_MAYBE_EMPTY(lzx); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - break; - - case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED: - // Because we can't assume otherwise - lzx.IntelStarted = true; - - // Read 1-16 (not 0-15) bits to align to bytes - if (bits_left == 0) - lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - bits_left = 0; bit_buffer = 0; - - // Read 12 bytes of stored R0 / R1 / R2 values - for (rundest = 0, i = 0; i < 12; i++) - { - lzx.READ_IF_NEEDED(ref i_ptr, ref i_end); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - buf[rundest++] = lzx.InputBuffer[i_ptr++]; - } - - R0 = (uint)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); - R1 = (uint)(buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24)); - R2 = (uint)(buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24)); - - break; - - default: - Console.WriteLine("Bad block type"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } + state.InputPointer++; } + lzx.ReadBlockHeader(buf, ref R, state); + if (lzx.Error != Error.MSPACK_ERR_OK) + return lzx.Error; + // Decode more of the block: - // run = min(what's available, what's needed) - this_run = (int)lzx.BlockRemaining; - if (this_run > bytes_todo) - this_run = bytes_todo; + int this_run = Math.Min(lzx.BlockRemaining, bytes_todo); // Assume we decode exactly this_run bytes, for now bytes_todo -= this_run; - lzx.BlockRemaining -= (uint)this_run; + lzx.BlockRemaining -= this_run; // Decode at least this_run bytes switch (lzx.BlockType) { case LZXBlockType.LZX_BLOCKTYPE_ALIGNED: case LZXBlockType.LZX_BLOCKTYPE_VERBATIM: - while (this_run > 0) - { - main_element = (int)lzx.READ_HUFFSYM_MSB(lzx.MAINTREE_table, lzx.MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if (main_element < LZX_NUM_CHARS) - { - // Literal: 0 to LZX_NUM_CHARS-1 - window[window_posn++] = (byte)main_element; - this_run--; - } - else - { - // Match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) - main_element -= LZX_NUM_CHARS; + lzx.DecompressBlock(window, ref window_posn, ref this_run, ref R, state); + if (lzx.Error != Error.MSPACK_ERR_OK) + return lzx.Error; - // Get match length - match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; - if (match_length == LZX_NUM_PRIMARY_LENGTHS) - { - if (lzx.LENGTH_empty != 0) - { - Console.WriteLine("LENGTH symbol needed but tree is empty"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - - length_footer = (int)lzx.READ_HUFFSYM_MSB(lzx.LENGTH_table, lzx.LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_length += length_footer; - } - - match_length += LZX_MIN_MATCH; - - // Get match offset - switch ((match_offset = (uint)(main_element >> 3))) - { - case 0: - match_offset = R0; - break; - - case 1: - match_offset = R1; - R1 = R0; - R0 = match_offset; - break; - - case 2: - match_offset = R2; - R2 = R0; - R0 = match_offset; - break; - - default: - if (lzx.BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM) - { - if (match_offset == 3) - { - match_offset = 1; - } - else - { - extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; - verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset = (uint)(position_base[match_offset] - 2 + verbatim_bits); - } - } - - // LZX_BLOCKTYPE_ALIGNED - else - { - extra = (match_offset >= 36) ? 17 : extra_bits[match_offset]; - match_offset = position_base[match_offset] - 2; - - // >3: verbatim and aligned bits - if (extra > 3) - { - extra -= 3; - verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset += (uint)(verbatim_bits << 3); - - aligned_bits = (int)lzx.READ_HUFFSYM_MSB(lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset += (uint)aligned_bits; - } - - // 3: aligned bits only - else if (extra == 3) - { - aligned_bits = (int)lzx.READ_HUFFSYM_MSB(lzx.ALIGNED_table, lzx.ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset += (uint)aligned_bits; - } - - // 1-2: verbatim bits only - else if (extra > 0) - { - verbatim_bits = (int)lzx.READ_BITS_MSB(extra, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset += (uint)verbatim_bits; - } - - // 0: not defined in LZX specification! - else - { - match_offset = 1; - } - } - - // Update repeated offset LRU queue - R2 = R1; R1 = R0; R0 = match_offset; - break; - } - - // LZX DELTA uses max match length to signal even longer match - if (match_length == LZX_MAX_MATCH && lzx.IsDelta) - { - int extra_len; - - // 4 entry huffman tree - lzx.ENSURE_BITS(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - // '0' . 8 extra length bits - if (lzx.PEEK_BITS_MSB(1, bit_buffer) == 0) - { - lzx.REMOVE_BITS_MSB(1, ref bit_buffer, ref bits_left); - extra_len = (int)lzx.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - } - - // '10' . 10 extra length bits + 0x100 - else if (lzx.PEEK_BITS_MSB(2, bit_buffer) == 2) - { - lzx.REMOVE_BITS_MSB(2, ref bit_buffer, ref bits_left); - extra_len = (int)lzx.READ_BITS_MSB(10, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra_len += 0x100; - } - - // '110' . 12 extra length bits + 0x500 - else if (lzx.PEEK_BITS_MSB(3, bit_buffer) == 6) - { - lzx.REMOVE_BITS_MSB(3, ref bit_buffer, ref bits_left); - extra_len = (int)lzx.READ_BITS_MSB(12, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra_len += 0x500; - } - - // '111' . 15 extra length bits - else - { - lzx.REMOVE_BITS_MSB(3, ref bit_buffer, ref bits_left); - extra_len = (int)lzx.READ_BITS_MSB(15, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - } - - match_length += extra_len; - } - - if ((window_posn + match_length) > lzx.WindowSize) - { - Console.WriteLine("Match ran over window wrap"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - - // Copy match - rundest = (int)window_posn; - i = match_length; - - // Does match offset wrap the window? - if (match_offset > window_posn) - { - if (match_offset > lzx.Offset && (match_offset - window_posn) > lzx.ReferenceDataSize) - { - Console.WriteLine("Match offset beyond LZX stream"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - - // j = length from match offset to end of window - j = (int)(match_offset - window_posn); - if (j > (int)lzx.WindowSize) - { - Console.WriteLine("Match offset beyond window boundaries"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - - runsrc = (int)(lzx.WindowSize - j); - if (j < i) - { - // If match goes over the window edge, do two copy runs - i -= j; - while (j-- > 0) - { - window[rundest++] = window[runsrc++]; - } - - runsrc = 0; - } - - while (i-- > 0) - { - window[rundest++] = window[runsrc++]; - } - } - else - { - runsrc = (int)(rundest - match_offset); - while (i-- > 0) - { - window[rundest++] = window[runsrc++]; - } - } - - this_run -= match_length; - window_posn += (uint)match_length; - } - } + // If the literal 0xE8 is anywhere in the block... + if (lzx.MAINTREE_len[0xE8] != 0) + lzx.IntelStarted = true; break; case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED: // As this_run is limited not to wrap a frame, this also means it // won't wrap the window (as the window is a multiple of 32k) - rundest = (int)window_posn; - window_posn += (uint)this_run; + int rundest = window_posn; + window_posn += this_run; while (this_run > 0) { - if ((i = i_end - i_ptr) == 0) + int i = state.InputEnd - state.InputPointer; + if (i == 0) { - lzx.READ_IF_NEEDED(ref i_ptr, ref i_end); + lzx.READ_IF_NEEDED(state); if (lzx.Error != Error.MSPACK_ERR_OK) return lzx.Error; } @@ -906,13 +450,17 @@ namespace LibMSPackSharp.Compression if (i > this_run) i = this_run; - Array.Copy(lzx.InputBuffer, i_ptr, window, rundest, i); + Array.Copy(lzx.InputBuffer, state.InputPointer, window, rundest, i); rundest += i; - i_ptr += i; + state.InputPointer += i; this_run -= i; } } + + // Because we can't assume otherwise + lzx.IntelStarted = true; + break; default: @@ -928,7 +476,7 @@ namespace LibMSPackSharp.Compression return lzx.Error = Error.MSPACK_ERR_DECRUNCH; } - lzx.BlockRemaining -= (uint)-this_run; + lzx.BlockRemaining -= -this_run; } } @@ -940,10 +488,10 @@ namespace LibMSPackSharp.Compression } // Re-align input bitstream - if (bits_left > 0) - lzx.ENSURE_BITS(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - if ((bits_left & 15) != 0) - lzx.REMOVE_BITS_MSB(bits_left & 15, ref bit_buffer, ref bits_left); + if (state.BitsLeft > 0) + lzx.ENSURE_BITS(16, state); + if ((state.BitsLeft & 15) != 0) + state.REMOVE_BITS_MSB(state.BitsLeft & 15); // Check that we've used all of the previous frame first if (lzx.OutputPointer != lzx.OutputEnd) @@ -955,59 +503,24 @@ namespace LibMSPackSharp.Compression // Does this intel block _really_ need decoding? if (lzx.IntelStarted && lzx.IntelFileSize != 0 && (lzx.Frame < 32768) && (frame_size > 10)) { - int data = 0; - int dataend = (int)(frame_size - 10); - int curpos = (int)lzx.Offset; - int filesize = lzx.IntelFileSize; - int abs_off, rel_off; - - // Copy e8 block to the e8 buffer and tweak if needed - lzx.OutputPointer = data; - Array.Copy(lzx.Window, lzx.FramePosition, lzx.e8_buf, data, frame_size); - - while (data < dataend) - { - if (lzx.e8_buf[data++] != 0xE8) - { - curpos++; - continue; - } - - abs_off = lzx.e8_buf[data + 0] | (lzx.e8_buf[data + 1] << 8) | (lzx.e8_buf[data + 2] << 16) | (lzx.e8_buf[data + 3] << 24); - if ((abs_off >= -curpos) && (abs_off < filesize)) - { - rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; - lzx.e8_buf[data + 0] = (byte)rel_off; - lzx.e8_buf[data + 1] = (byte)(rel_off >> 8); - lzx.e8_buf[data + 2] = (byte)(rel_off >> 16); - lzx.e8_buf[data + 3] = (byte)(rel_off >> 24); - } - - data += 4; - curpos += 5; - } - - lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size); - - // Write a frame - i = (int)((out_bytes < frame_size) ? out_bytes : frame_size); - try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); } - catch { return lzx.Error = Error.MSPACK_ERR_WRITE; } + lzx.UndoE8Preprocessing(frame_size); } else { + lzx.OutputIsE8 = false; lzx.OutputPointer = (int)lzx.FramePosition; - lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size); - - // Write a frame - i = (int)((out_bytes < frame_size) ? out_bytes : frame_size); - try { lzx.System.Write(lzx.OutputFileHandle, lzx.Window, lzx.OutputPointer, i); } - catch { return lzx.Error = Error.MSPACK_ERR_WRITE; } } - lzx.OutputPointer += i; - lzx.Offset += i; - out_bytes -= i; + lzx.OutputEnd = (int)(lzx.OutputPointer + frame_size); + + // Write a frame + int new_out_bytes = (int)((out_bytes < frame_size) ? out_bytes : frame_size); + try { lzx.System.Write(lzx.OutputFileHandle, lzx.OutputIsE8 ? lzx.E8Buffer : lzx.Window, lzx.OutputPointer, new_out_bytes); } + catch { return lzx.Error = Error.MSPACK_ERR_WRITE; } + + lzx.OutputPointer += new_out_bytes; + lzx.Offset += new_out_bytes; + out_bytes -= new_out_bytes; // Advance frame start position lzx.FramePosition += frame_size; @@ -1028,131 +541,9 @@ namespace LibMSPackSharp.Compression } // Store local state - lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + lzx.STORE_BITS(state); lzx.WindowPosition = window_posn; - lzx.R0 = R0; - lzx.R1 = R1; - lzx.R2 = R2; - - return Error.MSPACK_ERR_OK; - } - - private static Error BUILD_TABLE(LZXDStream lzx, ushort[] table, byte[] lengths, int tablebits, int maxsymbols) - { - if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table)) - { - Console.WriteLine($"Failed to build table"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - - return lzx.Error = Error.MSPACK_ERR_OK; - } - - private static Error BUILD_TABLE_MAYBE_EMPTY(LZXDStream lzx) - { - lzx.LENGTH_empty = 0; - if (!CompressionStream.MakeDecodeTableMSB(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, lzx.LENGTH_len, lzx.LENGTH_table)) - { - for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) - { - if (lzx.LENGTH_len[i] > 0) - { - Console.WriteLine("Failed to build table"); - return lzx.Error = Error.MSPACK_ERR_DECRUNCH; - } - } - - // Empty tree - allow it, but don't decode symbols with it - lzx.LENGTH_empty = 1; - } - - return lzx.Error = Error.MSPACK_ERR_OK; - } - - private static Error READ_LENGTHS(LZXDStream lzx, byte[] lengths, uint first, uint last, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) - { - lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); - - if (ReadLens(lzx, lengths, first, last) != Error.MSPACK_ERR_OK) - return lzx.Error; - - lzx.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); - return lzx.Error = Error.MSPACK_ERR_OK; - } - - private static Error ReadLens(LZXDStream lzx, byte[] lens, uint first, uint last) - { - uint x, y; - int z; - - lzx.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); - - // Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) - for (x = 0; x < 20; x++) - { - y = (uint)lzx.READ_BITS_MSB(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - lzx.PRETREE_len[x] = (byte)y; - } - - BUILD_TABLE(lzx, lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS); - if (lzx.Error != Error.MSPACK_ERR_OK) - return lzx.Error; - - for (x = first; x < last;) - { - z = (int)lzx.READ_HUFFSYM_MSB(lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - // Code = 17, run of ([read 4 bits]+4) zeros - if (z == 17) - { - y = (uint)lzx.READ_BITS_MSB(4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - y += 4; - while (y-- != 0) - { - lens[x++] = 0; - } - } - - // Code = 18, run of ([read 5 bits]+20) zeros - else if (z == 18) - { - y = (uint)lzx.READ_BITS_MSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - y += 20; - while (y-- != 0) - { - lens[x++] = 0; - } - } - - // Code = 19, run of ([read 1 bit]+4) [read huffman symbol] - else if (z == 19) - { - y = (uint)lzx.READ_BITS_MSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - y += 4; - z = (int)lzx.READ_HUFFSYM_MSB(lzx.PRETREE_table, lzx.PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - - z = lens[x] - z; - if (z < 0) - z += 17; - - while (y-- != 0) - { - lens[x++] = (byte)z; - } - } - - // Code = 0 to 16, delta current length entry - else - { - z = lens[x] - z; - if (z < 0) - z += 17; - - lens[x++] = (byte)z; - } - } - - lzx.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + lzx.R = R; return Error.MSPACK_ERR_OK; } diff --git a/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs b/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs index 3cb656cf..95676cfa 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs @@ -10,6 +10,9 @@ * For further details, see the file COPYING.LIB distributed with libmspack */ +using System; +using static LibMSPackSharp.Compression.Constants; + namespace LibMSPackSharp.Compression { public class LZXDStream : CompressionStream @@ -49,7 +52,7 @@ namespace LibMSPackSharp.Compression /// /// Decompression offset within window /// - public uint WindowPosition { get; set; } + public int WindowPosition { get; set; } /// /// Current frame offset within in window @@ -69,27 +72,17 @@ namespace LibMSPackSharp.Compression /// /// For the LRU offset system /// - public uint R0 { get; set; } - - /// - /// For the LRU offset system - /// - public uint R1 { get; set; } - - /// - /// For the LRU offset system - /// - public uint R2 { get; set; } + public uint[] R { get; set; } = new uint[3]; /// /// Uncompressed length of this LZX block /// - public uint BlockLength { get; set; } + public int BlockLength { get; set; } /// /// Uncompressed bytes still left to decode /// - public uint BlockRemaining { get; set; } + public int BlockRemaining { get; set; } /// /// Magic header value used for transform @@ -118,26 +111,564 @@ namespace LibMSPackSharp.Compression #region Huffman code lengths - public byte[] PRETREE_len { get; set; } = new byte[LZX.LZX_PRETREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY]; - public byte[] MAINTREE_len { get; set; } = new byte[LZX.LZX_MAINTREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY]; - public byte[] LENGTH_len { get; set; } = new byte[LZX.LZX_LENGTH_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY]; - public byte[] ALIGNED_len { get; set; } = new byte[LZX.LZX_ALIGNED_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY]; + public byte[] PRETREE_len { get; set; } = new byte[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + public byte[] MAINTREE_len { get; set; } = new byte[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + public byte[] LENGTH_len { get; set; } = new byte[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + public byte[] ALIGNED_len { get; set; } = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; #endregion #region Huffman decoding tables - public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX.LZX_PRETREE_TABLEBITS) + (LZX.LZX_PRETREE_MAXSYMBOLS * 2)]; - public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX.LZX_MAINTREE_TABLEBITS) + (LZX.LZX_MAINTREE_MAXSYMBOLS * 2)]; - public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX.LZX_LENGTH_TABLEBITS) + (LZX.LZX_LENGTH_MAXSYMBOLS * 2)]; - public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX.LZX_ALIGNED_TABLEBITS) + (LZX.LZX_ALIGNED_MAXSYMBOLS * 2)]; + public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS * 2)]; + public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS * 2)]; + public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS * 2)]; + public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS * 2)]; #endregion public byte LENGTH_empty { get; set; } // This is used purely for doing the intel E8 transform - public byte[] e8_buf { get; set; } = new byte[LZX.LZX_FRAME_SIZE]; + public byte[] E8Buffer { get; set; } = new byte[LZX_FRAME_SIZE]; + + /// + /// Is the output pointer referring to E8? + /// + public bool OutputIsE8 { get; set; } + + #endregion + + #region Specialty Methods + + public Error DecompressBlock(byte[] window, ref int window_posn, ref int this_run, ref uint[] R, BufferState state) + { + while (this_run > 0) + { + int main_element = (int)READ_HUFFSYM_MSB(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, state); + if (main_element < LZX_NUM_CHARS) + { + // Literal: 0 to LZX_NUM_CHARS-1 + window[window_posn++] = (byte)main_element; + this_run--; + } + else + { + // Match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) + main_element -= LZX_NUM_CHARS; + + // Get match length + int match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) + { + if (LENGTH_empty != 0) + { + Console.WriteLine("LENGTH symbol needed but tree is empty"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + int length_footer = (int)READ_HUFFSYM_MSB(LENGTH_table, LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, state); + match_length += length_footer; + } + + match_length += LZX_MIN_MATCH; + + // Get match offset + uint match_offset = (uint)(main_element >> 3); + switch (match_offset) + { + case 0: + match_offset = R[0]; + break; + + case 1: + match_offset = R[1]; + R[1] = R[0]; + R[0] = match_offset; + break; + + case 2: + match_offset = R[2]; + R[2] = R[0]; + R[0] = match_offset; + break; + + default: + if (BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM) + { + if (match_offset == 3) + { + match_offset = 1; + } + else + { + int extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset]; + int verbatim_bits = (int)READ_BITS_MSB(extra, state); + match_offset = (uint)(LZXPositionBase[match_offset] - 2 + verbatim_bits); + } + } + + // LZX_BLOCKTYPE_ALIGNED + else + { + int extra = (match_offset >= 36) ? 17 : LZXExtraBits[match_offset]; + match_offset = LZXPositionBase[match_offset] - 2; + + // >3: verbatim and aligned bits + if (extra > 3) + { + extra -= 3; + int verbatim_bits = (int)READ_BITS_MSB(extra, state); + match_offset += (uint)(verbatim_bits << 3); + + int aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, state); + match_offset += (uint)aligned_bits; + } + + // 3: aligned bits only + else if (extra == 3) + { + int aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, state); + match_offset += (uint)aligned_bits; + } + + // 1-2: verbatim bits only + else if (extra > 0) + { + int verbatim_bits = (int)READ_BITS_MSB(extra, state); + match_offset += (uint)verbatim_bits; + } + + // 0: not defined in LZX specification! + else + { + match_offset = 1; + } + } + + // Update repeated offset LRU queue + R[2] = R[1]; R[1] = R[0]; R[0] = match_offset; + break; + } + + // LZX DELTA uses max match length to signal even longer match + if (match_length == LZX_MAX_MATCH && IsDelta) + { + int extra_len; + + // 4 entry huffman tree + ENSURE_BITS(3, state); + + // '0' . 8 extra length bits + if (PEEK_BITS_MSB(1, state.BitBuffer) == 0) + { + state.REMOVE_BITS_MSB(1); + extra_len = (int)READ_BITS_MSB(8, state); + } + + // '10' . 10 extra length bits + 0x100 + else if (PEEK_BITS_MSB(2, state.BitBuffer) == 2) + { + state.REMOVE_BITS_MSB(2); + extra_len = (int)READ_BITS_MSB(10, state); + extra_len += 0x100; + } + + // '110' . 12 extra length bits + 0x500 + else if (PEEK_BITS_MSB(3, state.BitBuffer) == 6) + { + state.REMOVE_BITS_MSB(3); + extra_len = (int)READ_BITS_MSB(12, state); + extra_len += 0x500; + } + + // '111' . 15 extra length bits + else + { + state.REMOVE_BITS_MSB(3); + extra_len = (int)READ_BITS_MSB(15, state); + } + + match_length += extra_len; + } + + if ((window_posn + match_length) > WindowSize) + { + Console.WriteLine("Match ran over window wrap"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + // Copy match + int rundest = window_posn; + int i = match_length; + + // Does match offset wrap the window? + if (match_offset > window_posn) + { + if (match_offset > Offset && (match_offset - window_posn) > ReferenceDataSize) + { + Console.WriteLine("Match offset beyond LZX stream"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + // j = length from match offset to end of window + int j = (int)(match_offset - window_posn); + if (j > (int)WindowSize) + { + Console.WriteLine("Match offset beyond window boundaries"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + int runsrc = (int)(WindowSize - j); + if (j < i) + { + // If match goes over the window edge, do two copy runs + i -= j; + while (j-- > 0) + { + window[rundest++] = window[runsrc++]; + } + + runsrc = 0; + } + + while (i-- > 0) + { + window[rundest++] = window[runsrc++]; + } + } + else + { + int runsrc = (int)(rundest - match_offset); + while (i-- > 0) + { + window[rundest++] = window[runsrc++]; + } + } + + this_run -= match_length; + window_posn += match_length; + } + } + + return Error = Error.MSPACK_ERR_OK; + } + + public Error ReadBlockHeader(byte[] buffer, ref uint[] R, BufferState state) + { + ENSURE_BITS(4, state); + + // Read block type (3 bits) and block length (24 bits) + byte block_type = (byte)READ_BITS_MSB(3, state); + BlockType = (LZXBlockType)block_type; + + // Read the block size + int block_size; + if (READ_BITS_MSB(1, state) == 1) + { + block_size = LZX_FRAME_SIZE; + } + else + { + int tmp; + block_size = 0; + + tmp = (int)READ_BITS_MSB(8, state); + block_size |= tmp; + tmp = (int)READ_BITS_MSB(8, state); + block_size <<= 8; + block_size |= tmp; + + if (WindowSize >= 65536) + { + tmp = (int)READ_BITS_MSB(8, state); + block_size <<= 8; + block_size |= tmp; + } + } + + BlockRemaining = BlockLength = block_size; + Console.WriteLine($"New block t {BlockType} len {BlockLength}"); + + // Read individual block headers + switch (BlockType) + { + case LZXBlockType.LZX_BLOCKTYPE_ALIGNED: + // Read lengths of and build aligned huffman decoding tree + for (byte i = 0; i < 8; i++) + { + ALIGNED_len[i] = (byte)READ_BITS_MSB(3, state); + } + + BUILD_TABLE(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + // Read lengths of and build main huffman decoding tree + READ_LENGTHS(MAINTREE_len, 0, 256, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + // Read lengths of and build lengths huffman decoding tree + READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + BUILD_TABLE_MAYBE_EMPTY(); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + break; + + case LZXBlockType.LZX_BLOCKTYPE_VERBATIM: + // Read lengths of and build main huffman decoding tree + READ_LENGTHS(MAINTREE_len, 0, 256, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + // If the literal 0xE8 is anywhere in the block... + if (MAINTREE_len[0xE8] != 0) + IntelStarted = true; + + // Read lengths of and build lengths huffman decoding tree + READ_LENGTHS(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + BUILD_TABLE_MAYBE_EMPTY(); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + break; + + case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED: + // Read 1-16 (not 0-15) bits to align to bytes + if (state.BitsLeft == 0) + ENSURE_BITS(16, state); + + state.BitsLeft = 0; state.BitBuffer = 0; + + // Read 12 bytes of stored R[0] / R[1] / R[2] values + for (int rundest = 0, k = 0; k < 12; k++) + { + READ_IF_NEEDED(state); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + buffer[rundest++] = InputBuffer[state.InputPointer++]; + } + + // TODO: uint[] R should be a part of a state object + R[0] = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); + R[1] = (uint)(buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24)); + R[2] = (uint)(buffer[8] | (buffer[9] << 8) | (buffer[10] << 16) | (buffer[11] << 24)); + + break; + + default: + Console.WriteLine($"Bad block type: {BlockType}"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + return Error = Error.MSPACK_ERR_OK; + } + + public Error ReadLens(byte[] lens, uint first, uint last) + { + BufferState state = RESTORE_BITS(); + + // Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) + for (int i = 0; i < LZX_PRETREE_MAXSYMBOLS; i++) + { + uint y = (uint)READ_BITS_MSB(4, state); + PRETREE_len[i] = (byte)y; + } + + BUILD_TABLE(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS); + if (Error != Error.MSPACK_ERR_OK) + return Error; + + for (uint lensPtr = first; lensPtr < last;) + { + uint num_zeroes, num_same; + int tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, state); + switch (tree_code) + { + // Code = 17, run of ([read 4 bits]+4) zeros + case 17: + num_zeroes = (uint)READ_BITS_MSB(4, state); + num_zeroes += 4; + while (num_zeroes-- != 0) + { + lens[lensPtr++] = 0; + } + + break; + + // Code = 18, run of ([read 5 bits]+20) zeros + case 18: + num_zeroes = (uint)READ_BITS_MSB(5, state); + num_zeroes += 20; + while (num_zeroes-- != 0) + { + lens[lensPtr++] = 0; + } + + break; + + // Code = 19, run of ([read 1 bit]+4) [read huffman symbol] + case 19: + num_same = (uint)READ_BITS_MSB(1, state); + num_same += 4; + + tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, state); + tree_code = lens[lensPtr] - tree_code; + if (tree_code < 0) + tree_code += 17; + + while (num_same-- != 0) + { + lens[lensPtr++] = (byte)tree_code; + } + + break; + + // Code = 0 to 16, delta current length entry + default: + tree_code = lens[lensPtr] - tree_code; + if (tree_code < 0) + tree_code += 17; + + lens[lensPtr++] = (byte)tree_code; + break; + } + } + + STORE_BITS(state); + + return Error.MSPACK_ERR_OK; + } + + public void ResetState() + { + R[0] = 1; + R[1] = 1; + R[2] = 1; + HeaderRead = 0; + BlockRemaining = 0; + BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0; + + // Initialise tables to 0 (because deltas will be applied to them) + for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) + { + MAINTREE_len[i] = 0; + } + + for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) + { + LENGTH_len[i] = 0; + } + } + + public void UndoE8Preprocessing(uint frame_size) + { + int data = 0; + int dataend = (int)(frame_size - 10); + int curpos = (int)Offset; + int filesize = IntelFileSize; + int abs_off, rel_off; + + // Copy e8 block to the e8 buffer and tweak if needed + OutputIsE8 = true; + OutputPointer = data; + Array.Copy(Window, FramePosition, E8Buffer, data, frame_size); + + while (data < dataend) + { + if (E8Buffer[data++] != 0xE8) + { + curpos++; + continue; + } + + abs_off = E8Buffer[data + 0] | (E8Buffer[data + 1] << 8) | (E8Buffer[data + 2] << 16) | (E8Buffer[data + 3] << 24); + if ((abs_off >= -curpos) && (abs_off < filesize)) + { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + E8Buffer[data + 0] = (byte)rel_off; + E8Buffer[data + 1] = (byte)(rel_off >> 8); + E8Buffer[data + 2] = (byte)(rel_off >> 16); + E8Buffer[data + 3] = (byte)(rel_off >> 24); + } + + data += 4; + curpos += 5; + } + } + + private Error BUILD_TABLE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols) + { + if (!MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table)) + { + Console.WriteLine($"Failed to build table"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + return Error = Error.MSPACK_ERR_OK; + } + + private Error BUILD_TABLE_MAYBE_EMPTY() + { + LENGTH_empty = 0; + if (!MakeDecodeTableMSB(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, LENGTH_len, LENGTH_table)) + { + for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) + { + if (LENGTH_len[i] > 0) + { + Console.WriteLine("Failed to build table"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + } + + // Empty tree - allow it, but don't decode symbols with it + LENGTH_empty = 1; + } + + return Error = Error.MSPACK_ERR_OK; + } + + private Error READ_LENGTHS(byte[] lengths, uint first, uint last, BufferState state) + { + STORE_BITS(state); + + if (ReadLens(lengths, first, last) != Error.MSPACK_ERR_OK) + return Error; + + BufferState temp = RESTORE_BITS(); + state.InputPointer = temp.InputPointer; + state.InputEnd = temp.InputEnd; + state.BitBuffer = temp.BitBuffer; + state.BitsLeft = temp.BitsLeft; + + return Error = Error.MSPACK_ERR_OK; + } #endregion @@ -145,20 +676,20 @@ namespace LibMSPackSharp.Compression public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH; /// - public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public override void READ_BYTES(BufferState state) { - READ_IF_NEEDED(ref i_ptr, ref i_end); + READ_IF_NEEDED(state); if (Error != Error.MSPACK_ERR_OK) return; - byte b0 = InputBuffer[i_ptr++]; + byte b0 = InputBuffer[state.InputPointer++]; - READ_IF_NEEDED(ref i_ptr, ref i_end); + READ_IF_NEEDED(state); if (Error != Error.MSPACK_ERR_OK) return; - byte b1 = InputBuffer[i_ptr++]; - INJECT_BITS_MSB((b1 << 8) | b0, 16, ref bit_buffer, ref bits_left); + byte b1 = InputBuffer[state.InputPointer++]; + INJECT_BITS_MSB((b1 << 8) | b0, 16, state); } } } diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs index 6009411d..431b36ae 100644 --- a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs +++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs @@ -12,75 +12,12 @@ using System; using System.IO; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { public class MSZIP { - #region MSZIP (deflate) compression / (inflate) decompression definitions - - public const int MSZIP_FRAME_SIZE = 32768; // Size of LZ history window - public const int MSZIP_LITERAL_MAXSYMBOLS = 288; // literal/length huffman tree - public const int MSZIP_LITERAL_TABLEBITS = 9; - public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; // Distance huffman tree - public const int MSZIP_DISTANCE_TABLEBITS = 6; - - // If there are less direct lookup entries than symbols, the longer - // code pointers will be <= maxsymbols. This must not happen, or we - // will decode entries badly - - //public const int MSZIP_LITERAL_TABLESIZE = (MSZIP_LITERAL_MAXSYMBOLS * 4); - public const int MSZIP_LITERAL_TABLESIZE = ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2)); - - //public const int MSZIP_DISTANCE_TABLESIZE = (MSZIP_DISTANCE_MAXSYMBOLS * 4); - public const int MSZIP_DISTANCE_TABLESIZE = ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2)); - - /// - /// Match lengths for literal codes 257.. 285 - /// - private static readonly ushort[] lit_lengths = new ushort[29] - { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, - 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 - }; - - /// - /// Match offsets for distance codes 0 .. 29 - /// - private static readonly ushort[] dist_offsets = new ushort[30] - { - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, - 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 - }; - - /// - /// Extra bits required for literal codes 257.. 285 - /// - private static readonly byte[] lit_extrabits = new byte[29] - { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, - 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 - }; - - /// - /// Extra bits required for distance codes 0 .. 29 - /// - private static readonly byte[] dist_extrabits = new byte[30] - { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, - 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 - }; - - /// - /// The order of the bit length Huffman code lengths - /// - private static readonly byte[] bitlen_order = new byte[19] - { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 - }; - - #endregion - /// /// Allocates MS-ZIP decompression stream for decoding the given stream. /// @@ -120,12 +57,16 @@ namespace LibMSPackSharp.Compression RepairMode = repair_mode, FlushWindow = FlushWindow, - InputPointer = 0, - InputEnd = 0, OutputPointer = 0, OutputEnd = 0, - BitBuffer = 0, - BitsLeft = 0, + + BufferState = new BufferState() + { + InputPointer = 0, + InputEnd = 0, + BitBuffer = 0, + BitsLeft = 0, + } }; } @@ -159,7 +100,7 @@ namespace LibMSPackSharp.Compression int bits_left; int i_ptr, i_end; - int i, state; + int i, readState; Error error; // Easy answers @@ -188,32 +129,32 @@ namespace LibMSPackSharp.Compression while (out_bytes > 0) { // Unpack another block - zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); + BufferState state = zip.RESTORE_BITS(); // Skip to next read 'CK' header - i = bits_left & 7; + i = state.BitsLeft & 7; // Align to bytestream - zip.REMOVE_BITS_LSB(i, ref bit_buffer, ref bits_left); + state.REMOVE_BITS_LSB(i); - state = 0; + readState = 0; do { - i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + i = (int)zip.READ_BITS_LSB(8, state); if (i == 'C') - state = 1; - else if ((state == 1) && (i == 'K')) - state = 2; + readState = 1; + else if ((readState == 1) && (i == 'K')) + readState = 2; else - state = 0; - } while (state != 2); + readState = 0; + } while (readState != 2); // Inflate a block, repair and realign if necessary zip.WindowPosition = 0; zip.BytesOutput = 0; - zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + zip.STORE_BITS(state); if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK) { @@ -269,36 +210,31 @@ namespace LibMSPackSharp.Compression /// public static Error DecompressKWAJ(MSZIPDStream zip) { - // For the bit buffer - uint bit_buffer; - int bits_left; - int i_ptr, i_end; - int i, block_len; Error error; // Unpack blocks until block_len == 0 for (; ; ) { - zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); + BufferState state = zip.RESTORE_BITS(); // Align to bytestream, read block_len - i = bits_left & 7; - zip.REMOVE_BITS_LSB(i, ref bit_buffer, ref bits_left); + i = state.BitsLeft & 7; + state.REMOVE_BITS_LSB(i); - block_len = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + block_len = (int)zip.READ_BITS_LSB(8, state); + i = (int)zip.READ_BITS_LSB(8, state); block_len |= i << 8; if (block_len == 0) break; // Read "CK" header - i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + i = (int)zip.READ_BITS_LSB(8, state); if (i != 'C') return Error.MSPACK_ERR_DATAFORMAT; - i = (int)zip.READ_BITS_LSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + i = (int)zip.READ_BITS_LSB(8, state); if (i != 'K') return Error.MSPACK_ERR_DATAFORMAT; @@ -306,7 +242,7 @@ namespace LibMSPackSharp.Compression zip.WindowPosition = 0; zip.BytesOutput = 0; - zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + zip.STORE_BITS(state); if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK) { @@ -331,16 +267,16 @@ namespace LibMSPackSharp.Compression byte[] lens = new byte[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS]; uint lit_codes, dist_codes, code, last_code = 0, bitlen_codes, i, run; - zip.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); + BufferState state = zip.RESTORE_BITS(); // Read the number of codes - lit_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + lit_codes = (uint)zip.READ_BITS_LSB(5, state); lit_codes += 257; - dist_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + dist_codes = (uint)zip.READ_BITS_LSB(5, state); dist_codes += 1; - bitlen_codes = (uint)zip.READ_BITS_LSB(5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + bitlen_codes = (uint)zip.READ_BITS_LSB(5, state); bitlen_codes += 4; if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS) @@ -351,12 +287,12 @@ namespace LibMSPackSharp.Compression // Read in the bit lengths in their unusual order for (i = 0; i < bitlen_codes; i++) { - bl_len[bitlen_order[i]] = (byte)zip.READ_BITS_LSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + bl_len[BitLengthOrder[i]] = (byte)zip.READ_BITS_LSB(3, state); } while (i < 19) { - bl_len[bitlen_order[i++]] = 0; + bl_len[BitLengthOrder[i++]] = 0; } // Create decoding table with an immediate lookup @@ -368,9 +304,9 @@ namespace LibMSPackSharp.Compression { // Single-level huffman lookup - zip.ENSURE_BITS(7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - code = bl_table[zip.PEEK_BITS_LSB(7, bit_buffer)]; - zip.REMOVE_BITS_LSB(bl_len[code], ref bit_buffer, ref bits_left); + zip.ENSURE_BITS(7, state); + code = bl_table[zip.PEEK_BITS_LSB(7, state.BitBuffer)]; + state.REMOVE_BITS_LSB(bl_len[code]); if (code < 16) { @@ -381,19 +317,19 @@ namespace LibMSPackSharp.Compression switch (code) { case 16: - run = (uint)zip.READ_BITS_LSB(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + run = (uint)zip.READ_BITS_LSB(2, state); run += 3; code = last_code; break; case 17: - run = (uint)zip.READ_BITS_LSB(3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + run = (uint)zip.READ_BITS_LSB(3, state); run += 3; code = 0; break; case 18: - run = (uint)zip.READ_BITS_LSB(7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + run = (uint)zip.READ_BITS_LSB(7, state); run += 11; code = 0; break; @@ -430,7 +366,7 @@ namespace LibMSPackSharp.Compression zip.DISTANCE_len[i++] = 0; } - zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + zip.STORE_BITS(state); return 0; } @@ -444,15 +380,15 @@ namespace LibMSPackSharp.Compression Error err; ushort sym; - zip.RESTORE_BITS(out int i_ptr, out int i_end, out uint bit_buffer, out int bits_left); + BufferState state = zip.RESTORE_BITS(); do { // Read in last block bit - last_block = (uint)zip.READ_BITS_LSB(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + last_block = (uint)zip.READ_BITS_LSB(1, state); // Read in block type - block_type = (uint)zip.READ_BITS_LSB(2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + block_type = (uint)zip.READ_BITS_LSB(2, state); if (block_type == 0) { @@ -460,29 +396,29 @@ namespace LibMSPackSharp.Compression byte[] lens_buf = new byte[4]; // Go to byte boundary - i = (uint)(bits_left & 7); - zip.REMOVE_BITS_LSB((int)i, ref bit_buffer, ref bits_left); + i = (uint)(state.BitsLeft & 7); + state.REMOVE_BITS_LSB((int)i); // Read 4 bytes of data, emptying the bit-buffer if necessary - for (i = 0; (bits_left >= 8); i++) + for (i = 0; (state.BitsLeft >= 8); i++) { if (i == 4) return Error.INF_ERR_BITBUF; - lens_buf[i] = (byte)zip.PEEK_BITS_LSB(8, bit_buffer); - zip.REMOVE_BITS_LSB(8, ref bit_buffer, ref bits_left); + lens_buf[i] = (byte)zip.PEEK_BITS_LSB(8, state.BitBuffer); + state.REMOVE_BITS_LSB(8); } - if (bits_left != 0) + if (state.BitsLeft != 0) return Error.INF_ERR_BITBUF; while (i < 4) { - zip.READ_IF_NEEDED(ref i_ptr, ref i_end); + zip.READ_IF_NEEDED(state); if (zip.Error != Error.MSPACK_ERR_OK) return zip.Error; - lens_buf[i++] = zip.InputBuffer[i_ptr++]; + lens_buf[i++] = zip.InputBuffer[state.InputPointer++]; } // Get the length and its complement @@ -496,21 +432,21 @@ namespace LibMSPackSharp.Compression // Read and copy the uncompressed data into the window while (length > 0) { - zip.READ_IF_NEEDED(ref i_ptr, ref i_end); + zip.READ_IF_NEEDED(state); if (zip.Error != Error.MSPACK_ERR_OK) return zip.Error; this_run = length; - if (this_run > (uint)(i_end - i_ptr)) - this_run = (uint)(i_end - i_ptr); + if (this_run > (uint)(state.InputEnd - state.InputPointer)) + this_run = (uint)(state.InputEnd - state.InputPointer); if (this_run > (MSZIP_FRAME_SIZE - zip.WindowPosition)) this_run = MSZIP_FRAME_SIZE - zip.WindowPosition; - Array.Copy(zip.InputBuffer, i_ptr, zip.Window, zip.WindowPosition, this_run); + Array.Copy(zip.InputBuffer, state.InputPointer, zip.Window, zip.WindowPosition, this_run); zip.WindowPosition += this_run; - i_ptr += (int)this_run; + state.InputPointer += (int)this_run; length -= this_run; err = FLUSH_IF_NEEDED(zip); @@ -555,12 +491,12 @@ namespace LibMSPackSharp.Compression else { // Block with dynamic Huffman codes - zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + zip.STORE_BITS(state); if ((err = ReadLens(zip)) != Error.MSPACK_ERR_OK) return err; - zip.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); + state = zip.RESTORE_BITS(); } // Now huffman lengths are read for either kind of block, @@ -574,7 +510,7 @@ namespace LibMSPackSharp.Compression // Decode forever until end of block code for (; ; ) { - code = (uint)zip.READ_HUFFSYM_LSB(zip.LITERAL_table, zip.LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + code = (uint)zip.READ_HUFFSYM_LSB(zip.LITERAL_table, zip.LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS, state); if (code < 256) { @@ -594,16 +530,16 @@ namespace LibMSPackSharp.Compression if (code >= 29) return Error.INF_ERR_LITCODE; // Codes 286-287 are illegal - length = (uint)zip.READ_BITS_T_LSB(lit_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - length += lit_lengths[code]; + length = (uint)zip.READ_BITS_T_LSB(LiteralExtraBits[code], state); + length += LiteralLengths[code]; - code = (uint)zip.READ_HUFFSYM_LSB(zip.DISTANCE_table, zip.DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + code = (uint)zip.READ_HUFFSYM_LSB(zip.DISTANCE_table, zip.DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS, state); if (code >= 30) return Error.INF_ERR_DISTCODE; - distance = (uint)zip.READ_BITS_T_LSB(dist_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - distance += dist_offsets[code]; + distance = (uint)zip.READ_BITS_T_LSB(DistanceExtraBits[code], state); + distance += DistanceOffsets[code]; // Match position is window position minus distance. If distance // is more than window position numerically, it must 'wrap @@ -672,7 +608,7 @@ namespace LibMSPackSharp.Compression return Error.INF_ERR_FLUSH; } - zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + zip.STORE_BITS(state); // Return success return Error.MSPACK_ERR_OK; diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs b/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs index 52f9b7b9..ec1bbf75 100644 --- a/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs +++ b/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs @@ -11,6 +11,7 @@ */ using System; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { @@ -21,7 +22,7 @@ namespace LibMSPackSharp.Compression /// /// 32kb history window /// - public byte[] Window { get; set; } = new byte[MSZIP.MSZIP_FRAME_SIZE]; + public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE]; /// /// Offset within window @@ -39,15 +40,15 @@ namespace LibMSPackSharp.Compression #region Huffman code lengths - public byte[] LITERAL_len { get; set; } = new byte[MSZIP.MSZIP_LITERAL_MAXSYMBOLS]; - public byte[] DISTANCE_len { get; set; } = new byte[MSZIP.MSZIP_DISTANCE_MAXSYMBOLS]; + public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS]; + public byte[] DISTANCE_len { get; set; } = new byte[MSZIP_DISTANCE_MAXSYMBOLS]; #endregion #region Huffman decoding tables - public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP.MSZIP_LITERAL_TABLESIZE]; - public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP.MSZIP_DISTANCE_TABLESIZE]; + public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP_LITERAL_TABLESIZE]; + public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP_DISTANCE_TABLESIZE]; #endregion @@ -57,13 +58,13 @@ namespace LibMSPackSharp.Compression public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM; /// - public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public override void READ_BYTES(BufferState state) { - READ_IF_NEEDED(ref i_ptr, ref i_end); + READ_IF_NEEDED(state); if (Error != Error.MSPACK_ERR_OK) return; - INJECT_BITS_LSB(InputBuffer[i_ptr++], 8, ref bit_buffer, ref bits_left); + INJECT_BITS_LSB(InputBuffer[state.InputPointer++], 8, state); } } } diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.cs b/BurnOutSharp/External/libmspack/Compression/QTM.cs index 7f1ba42b..5b6cdfc9 100644 --- a/BurnOutSharp/External/libmspack/Compression/QTM.cs +++ b/BurnOutSharp/External/libmspack/Compression/QTM.cs @@ -22,67 +22,12 @@ using System; using System.IO; +using static LibMSPackSharp.Compression.Constants; namespace LibMSPackSharp.Compression { public class QTM { - public const int QTM_FRAME_SIZE = 32768; - - /* Quantum static data tables: - * - * Quantum uses 'position slots' to represent match offsets. For every - * match, a small 'position slot' number and a small offset from that slot - * are encoded instead of one large offset. - * - * position_base[] is an index to the position slot bases - * - * extra_bits[] states how many bits of offset-from-base data is needed. - * - * length_base[] and length_extra[] are equivalent in function, but are - * used for encoding selector 6 (variable length match) match lengths, - * instead of match offsets. - * - * They are generated with the following code: - * uint i, offset; - * for (i = 0, offset = 0; i < 42; i++) { - * position_base[i] = offset; - * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; - * offset += 1 << extra_bits[i]; - * } - * for (i = 0, offset = 0; i < 26; i++) { - * length_base[i] = offset; - * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; - * offset += 1 << length_extra[i]; - * } - * length_base[26] = 254; length_extra[26] = 0; - */ - - private static readonly uint[] position_base = new uint[42] - { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, - 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, - 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864 - }; - - private static readonly byte[] extra_bits = new byte[42] - { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, - 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 - }; - - private static readonly byte[] length_base = new byte[27] - { - 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, - 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254 - }; - - private static readonly byte[] length_extra = new byte[27] - { - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 - }; - /// /// allocates Quantum decompression state for decoding the given stream. /// @@ -126,13 +71,17 @@ namespace LibMSPackSharp.Compression HeaderRead = 0, Error = Error.MSPACK_ERR_OK, - InputPointer = 0, - InputEnd = 0, OutputPointer = 0, OutputEnd = 0, EndOfInput = 0, - BitsLeft = 0, - BitBuffer = 0, + + BufferState = new BufferState() + { + InputPointer = 0, + InputEnd = 0, + BitBuffer = 0, + BitsLeft = 0, + } }; // Initialise arithmetic coding models @@ -214,7 +163,7 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; // Restore local state - qtm.RESTORE_BITS(out i_ptr, out i_end, out bit_buffer, out bits_left); + BufferState state = qtm.RESTORE_BITS(); window = qtm.Window; window_posn = qtm.WindowPosition; frame_todo = qtm.FrameTODO; @@ -230,7 +179,7 @@ namespace LibMSPackSharp.Compression { H = 0xFFFF; L = 0; - C = (ushort)qtm.READ_BITS_MSB(16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + C = (ushort)qtm.READ_BITS_MSB(16, state); qtm.HeaderRead = 1; } @@ -244,7 +193,7 @@ namespace LibMSPackSharp.Compression while (window_posn < frame_end) { - selector = GET_SYMBOL(qtm, qtm.Model7, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + selector = GET_SYMBOL(qtm, qtm.Model7, ref H, ref L, ref C, state); if (selector < 4) { // Literal byte @@ -269,7 +218,7 @@ namespace LibMSPackSharp.Compression break; } - sym = GET_SYMBOL(qtm, mdl, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + sym = GET_SYMBOL(qtm, mdl, ref H, ref L, ref C, state); window[window_posn++] = (byte)sym; frame_todo--; } @@ -280,29 +229,29 @@ namespace LibMSPackSharp.Compression { // Selector 4 = fixed length match (3 bytes) case 4: - sym = GET_SYMBOL(qtm, qtm.Model4, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset = (uint)(position_base[sym] + extra + 1); + sym = GET_SYMBOL(qtm, qtm.Model4, ref H, ref L, ref C, state); + extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state); + match_offset = (uint)(QTMPositionBase[sym] + extra + 1); match_length = 3; break; // Selector 5 = fixed length match (4 bytes) case 5: - sym = GET_SYMBOL(qtm, qtm.Model5, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset = (uint)(position_base[sym] + extra + 1); + sym = GET_SYMBOL(qtm, qtm.Model5, ref H, ref L, ref C, state); + extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state); + match_offset = (uint)(QTMPositionBase[sym] + extra + 1); match_length = 4; break; // Selector 6 = variable length match case 6: - sym = GET_SYMBOL(qtm, qtm.Model6Len, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra = (int)qtm.READ_MANY_BITS_MSB(length_extra[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_length = length_base[sym] + extra + 5; + sym = GET_SYMBOL(qtm, qtm.Model6Len, ref H, ref L, ref C, state); + extra = (int)qtm.READ_MANY_BITS_MSB(QTMLengthExtra[sym], state); + match_length = QTMLengthBase[sym] + extra + 5; - sym = GET_SYMBOL(qtm, qtm.Model6, ref H, ref L, ref C, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - extra = (int)qtm.READ_MANY_BITS_MSB(extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - match_offset = (uint)(position_base[sym] + extra + 1); + sym = GET_SYMBOL(qtm, qtm.Model6, ref H, ref L, ref C, state); + extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym], state); + match_offset = (uint)(QTMPositionBase[sym] + extra + 1); break; default: @@ -422,15 +371,15 @@ namespace LibMSPackSharp.Compression if (frame_todo == 0) { // Re-align input - if ((bits_left & 7) != 0) - qtm.REMOVE_BITS_MSB(bits_left & 7, ref bit_buffer, ref bits_left); + if ((state.BitsLeft & 7) != 0) + state.REMOVE_BITS_MSB(state.BitsLeft & 7); // Special Quantum hack -- cabd.c injects a trailer byte to allow the // decompressor to realign itself. CAB Quantum blocks, unlike LZX // blocks, can have anything from 0 to 4 trailing null bytes. do { - i = (int)qtm.READ_BITS_MSB(8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); + i = (int)qtm.READ_BITS_MSB(8, state); } while (i != 0xFF); qtm.HeaderRead = 0; @@ -470,7 +419,7 @@ namespace LibMSPackSharp.Compression } // Store local state - qtm.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left); + qtm.STORE_BITS(state); qtm.WindowPosition = window_posn; qtm.FrameTODO = frame_todo; qtm.High = H; @@ -480,7 +429,7 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - private static ushort GET_SYMBOL(QTMDStream qtm, QTMDModel model, ref ushort H, ref ushort L, ref ushort C, ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + private static ushort GET_SYMBOL(QTMDStream qtm, QTMDModel model, ref ushort H, ref ushort L, ref ushort C, BufferState state) { uint range = (uint)((H - L) & 0xFFFF) + 1; ushort symf = (ushort)(((((C - L + 1) * model.Syms[0].CumulativeFrequency) - 1) / range) & 0xFFFF); @@ -527,9 +476,9 @@ namespace LibMSPackSharp.Compression L <<= 1; H = (ushort)((H << 1) | 1); - qtm.ENSURE_BITS(1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left); - C = (ushort)((C << 1) | (qtm.PEEK_BITS_MSB(1, bit_buffer))); - qtm.REMOVE_BITS_MSB(1, ref bit_buffer, ref bits_left); + qtm.ENSURE_BITS(1, state); + C = (ushort)((C << 1) | (qtm.PEEK_BITS_MSB(1, state.BitBuffer))); + state.REMOVE_BITS_MSB(1); } return temp; diff --git a/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs b/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs index dd73b778..2d7ebb1e 100644 --- a/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs +++ b/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs @@ -130,20 +130,20 @@ namespace LibMSPackSharp.Compression public override Error HUFF_ERROR() => throw new NotImplementedException(); /// - public override void READ_BYTES(ref int i_ptr, ref int i_end, ref uint bit_buffer, ref int bits_left) + public override void READ_BYTES(BufferState state) { - READ_IF_NEEDED(ref i_ptr, ref i_end); + READ_IF_NEEDED(state); if (Error != Error.MSPACK_ERR_OK) return; - byte b0 = InputBuffer[i_ptr++]; + byte b0 = InputBuffer[state.InputPointer++]; - READ_IF_NEEDED(ref i_ptr, ref i_end); + READ_IF_NEEDED(state); if (Error != Error.MSPACK_ERR_OK) return; - byte b1 = InputBuffer[i_ptr++]; - INJECT_BITS_MSB((b0 << 8) | b1, 16, ref bit_buffer, ref bits_left); + byte b1 = InputBuffer[state.InputPointer++]; + INJECT_BITS_MSB((b0 << 8) | b1, 16, state); } } }