diff --git a/BurnOutSharp/External/libmspack/CAB/Decompressor.cs b/BurnOutSharp/External/libmspack/CAB/Decompressor.cs
index 65e7c14e..90d88e41 100644
--- a/BurnOutSharp/External/libmspack/CAB/Decompressor.cs
+++ b/BurnOutSharp/External/libmspack/CAB/Decompressor.cs
@@ -599,7 +599,7 @@ namespace LibMSPackSharp.CAB
{
// Special LZX hack -- on the last block, inform LZX of the
// size of the output data stream.
- LZX.SetOutputLength(self.State.DecompressorState as LZXDStream, self.State.Outlen);
+ (self.State.DecompressorState as LZX).SetOutputLength(self.State.Outlen);
}
}
}
diff --git a/BurnOutSharp/External/libmspack/CHM/DecompressState.cs b/BurnOutSharp/External/libmspack/CHM/DecompressState.cs
index ab76a42b..8330c5e2 100644
--- a/BurnOutSharp/External/libmspack/CHM/DecompressState.cs
+++ b/BurnOutSharp/External/libmspack/CHM/DecompressState.cs
@@ -31,6 +31,6 @@ namespace LibMSPackSharp.CHM
///
/// LZX decompressor state
///
- public LZXDStream State { get; set; }
+ public LZX State { get; set; }
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/BufferState.cs b/BurnOutSharp/External/libmspack/Compression/BufferState.cs
deleted file mode 100644
index 4d52c162..00000000
--- a/BurnOutSharp/External/libmspack/Compression/BufferState.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-/* 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.ReadBits.cs b/BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadBits.cs
new file mode 100644
index 00000000..ca26417c
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadBits.cs
@@ -0,0 +1,293 @@
+/* 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
+ */
+
+using System;
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ /* This header defines macros that read data streams by
+ * the individual bits
+ *
+ * INIT_BITS initialises bitstream state in state structure
+ * STORE_BITS stores bitstream state in state structure
+ * RESTORE_BITS restores bitstream state from state structure
+ * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer
+ * READ_BITS(var,n) takes N bits from the buffer and puts them in var
+ * PEEK_BITS(n) extracts without removing N bits from the bit buffer
+ * REMOVE_BITS(n) removes N bits from the bit buffer
+ *
+ * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS,
+ * which means it's limited to reading the number of bits you can
+ * ensure at any one time. It also fails if asked to read zero bits.
+ * If you need to read zero bits, or more bits than can be ensured in
+ * one go, use READ_MANY_BITS instead.
+ *
+ * These macros have variable names baked into them, so to use them
+ * you have to define some macros:
+ * - BITS_TYPE: the type name of your state structure
+ * - BITS_VAR: the variable that points to your state structure
+ * - define BITS_ORDER_MSB if bits are read from the MSB, or
+ * define BITS_ORDER_LSB if bits are read from the LSB
+ * - READ_BYTES: some code that reads more data into the bit buffer,
+ * it should use READ_IF_NEEDED (calls read_input if the byte buffer
+ * is empty), then INJECT_BITS(data,n) to put data from the byte
+ * buffer into the bit buffer.
+ *
+ * You also need to define some variables and structure members:
+ * - byte[] i_ptr; // current position in the byte buffer
+ * - byte[] i_end; // end of the byte buffer
+ * - uint bit_buffer; // the bit buffer itself
+ * - uint bits_left; // number of bits remaining
+ *
+ * If you use read_input() and READ_IF_NEEDED, they also expect these
+ * structure members:
+ * - struct mspack_system *sys; // to access sys->read()
+ * - uint error; // to record/return read errors
+ * - byte input_end; // to mark reaching the EOF
+ * - byte[] inbuf; // the input byte buffer
+ * - uint inbuf_size; // the size of the input byte buffer
+ *
+ * Your READ_BYTES implementation should read data from *i_ptr and
+ * put them in the bit buffer. READ_IF_NEEDED will call read_input()
+ * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to
+ * the start of inbuf and i_end to the end of inbuf.
+ *
+ * If you're reading in MSB order, the routines work by using the area
+ * beyond the MSB and the LSB of the bit buffer as a free source of
+ * zeroes when shifting. This avoids having to mask any bits. So we
+ * have to know the bit width of the bit buffer variable. We use
+ * and CHAR_BIT to find the size of the bit buffer in bits.
+ *
+ * If you are reading in LSB order, bits need to be masked. Normally
+ * this is done by computing the mask: N bits are masked by the value
+ * (1<
+ /// Initialises bitstream state in state structure
+ ///
+ public void INIT_BITS()
+ {
+ InputPointer = 0;
+ InputEnd = 0;
+ BitBuffer = 0;
+ BitsLeft = 0;
+ EndOfInput = 0;
+ }
+
+ ///
+ /// Ensure there are at least N bits in the bit buffer
+ ///
+ public void ENSURE_BITS(int nbits)
+ {
+ while (BitsLeft < nbits)
+ {
+ READ_BYTES();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+ }
+ }
+
+ ///
+ /// Read from the input if the buffer is empty
+ ///
+ public void READ_IF_NEEDED()
+ {
+ if (InputPointer >= InputEnd)
+ ReadInput();
+ }
+
+ ///
+ /// Read bytes from the input into the bit buffer
+ ///
+ public abstract void READ_BYTES();
+
+ ///
+ /// Read an input stream and fill the buffer
+ ///
+ protected virtual void ReadInput()
+ {
+ int read = System.Read(InputFileHandle, InputBuffer, 0, (int)InputBufferSize);
+ if (read < 0)
+ {
+ Error = Error.MSPACK_ERR_READ;
+ return;
+ }
+
+ // We might overrun the input stream by asking for bits we don't use,
+ // so fake 2 more bytes at the end of input
+ if (read == 0)
+ {
+ if (EndOfInput != 0)
+ {
+ Console.WriteLine("Out of input bytes");
+ Error = Error.MSPACK_ERR_READ;
+ return;
+ }
+ else
+ {
+ read = 2;
+ InputBuffer[0] = InputBuffer[1] = 0;
+ EndOfInput = 1;
+ }
+ }
+
+ // Update i_ptr and i_end
+ InputPointer = 0;
+ InputEnd = read;
+ }
+
+ #endregion
+
+ #region MSB
+
+ ///
+ /// Inject data into the bit buffer
+ ///
+ public void INJECT_BITS_MSB(int bitdata, int nbits)
+ {
+ BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - BitsLeft));
+ BitsLeft += nbits;
+ }
+
+ ///
+ /// Extracts without removing N bits from the bit buffer
+ ///
+ public long PEEK_BITS_MSB(int nbits) => (BitBuffer >> (BITBUF_WIDTH - (nbits)));
+
+ ///
+ /// Takes N bits from the buffer and puts them in var
+ ///
+ public long READ_BITS_MSB(int nbits)
+ {
+ ENSURE_BITS(nbits);
+ if (Error != Error.MSPACK_ERR_OK)
+ return -1;
+
+ long temp = PEEK_BITS_MSB(nbits);
+
+ REMOVE_BITS_MSB(nbits);
+ return temp;
+ }
+
+ ///
+ /// Read multiple bits and put them in var
+ ///
+ public long READ_MANY_BITS_MSB(int nbits)
+ {
+ byte needed = (byte)(nbits), bitrun;
+ long temp = 0;
+ while (needed > 0)
+ {
+ if (BitsLeft <= (BITBUF_WIDTH - 16))
+ {
+ READ_BYTES();
+ if (Error != Error.MSPACK_ERR_OK)
+ return -1;
+ }
+
+ bitrun = (byte)((BitsLeft < needed) ? BitsLeft : needed);
+ temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun);
+ REMOVE_BITS_MSB(bitrun);
+ needed -= bitrun;
+ }
+
+ return temp;
+ }
+
+ ///
+ /// Removes N bits from the bit buffer
+ ///
+ public void REMOVE_BITS_MSB(int nbits)
+ {
+ BitBuffer <<= nbits;
+ BitsLeft -= nbits;
+ }
+
+ #endregion
+
+ #region LSB
+
+ ///
+ /// Inject data into the bit buffer
+ ///
+ public void INJECT_BITS_LSB(int bitdata, int nbits)
+ {
+ BitBuffer |= (uint)(bitdata << BitsLeft);
+ BitsLeft += nbits;
+ }
+
+ ///
+ /// Extracts without removing N bits from the bit buffer
+ ///
+ public long PEEK_BITS_LSB(int nbits) => (BitBuffer & ((1 << (nbits)) - 1));
+
+ ///
+ /// Extracts without removing N bits from the bit buffer using a bit mask
+ ///
+ public long PEEK_BITS_T_LSB(int nbits) => BitBuffer & LSBBitMask[(nbits)];
+
+ ///
+ /// Takes N bits from the buffer and puts them in var
+ ///
+ public long READ_BITS_LSB(int nbits)
+ {
+ ENSURE_BITS(nbits);
+ if (Error != Error.MSPACK_ERR_OK)
+ return -1;
+
+ long temp = PEEK_BITS_LSB(nbits);
+
+ 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)
+ {
+ ENSURE_BITS(nbits);
+ if (Error != Error.MSPACK_ERR_OK)
+ return -1;
+
+ long temp = PEEK_BITS_T_LSB(nbits);
+
+ REMOVE_BITS_LSB(nbits);
+ return temp;
+ }
+
+ ///
+ /// 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.ReadHuff.cs b/BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadHuff.cs
new file mode 100644
index 00000000..e8e73b6d
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadHuff.cs
@@ -0,0 +1,342 @@
+/* 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
+ */
+
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ public abstract partial class CompressionStream : BaseDecompressState
+ {
+ #region Common
+
+ ///
+ /// Per compression error code for decoding failure
+ ///
+ public abstract Error HUFF_ERROR();
+
+ #endregion
+
+ #region MSB
+
+ ///
+ /// 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)
+ {
+ ENSURE_BITS(HUFF_MAXBITS);
+ ushort sym = table[PEEK_BITS_MSB(tablebits)];
+ if (sym >= maxsymbols)
+ HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols);
+
+ REMOVE_BITS_MSB(lengths[sym]);
+ return sym;
+ }
+
+ ///
+ /// Traverse for a single symbol
+ ///
+ private void HUFF_TRAVERSE_MSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols)
+ {
+ int i = 1 << (BITBUF_WIDTH - tablebits);
+ do
+ {
+ if ((i >>= 1) == 0)
+ {
+ Error = HUFF_ERROR();
+ return;
+ }
+
+ sym = table[(sym << 1) | ((BitBuffer & i) != 0 ? 1 : 0)];
+ } while (sym >= maxsymbols);
+ }
+
+ ///
+ /// This function was originally coded by David Tritscher.
+ ///
+ /// It builds a fast huffman decoding table from
+ /// a canonical huffman code lengths table.
+ ///
+ /// total number of symbols in this huffman tree.
+ /// any symbols with a code length of nbits or less can be decoded in one lookup of the table.
+ /// A table to get code lengths from [0 to nsyms-1]
+ ///
+ /// The table to fill up with decoded symbols and pointers.
+ /// Should be ((1<
+ /// True for OK or false for error
+ public static bool MakeDecodeTableMSB(int nsyms, int nbits, byte[] length, ushort[] table)
+ {
+ ushort sym, next_symbol;
+ uint leaf, fill;
+ byte bit_num;
+ uint pos = 0; // The current position in the decode table
+ uint table_mask = (uint)1 << nbits;
+ uint bit_mask = table_mask >> 1; // Don't do 0 length codes
+
+ // Fill entries for codes short enough for a direct mapping
+ for (bit_num = 1; bit_num <= nbits; bit_num++)
+ {
+ for (sym = 0; sym < nsyms; sym++)
+ {
+ if (length[sym] != bit_num)
+ continue;
+
+ leaf = pos;
+ if ((pos += bit_mask) > table_mask)
+ return false; // Table overrun
+
+ // Fill all possible lookups of this symbol with the symbol itself
+ for (fill = bit_mask; fill-- > 0;)
+ {
+ table[leaf++] = sym;
+ }
+ }
+
+ bit_mask >>= 1;
+ }
+
+ // Exit with success if table is now complete
+ if (pos == table_mask)
+ return true;
+
+ // Mark all remaining table entries as unused
+ for (sym = (ushort)pos; sym < table_mask; sym++)
+ {
+ table[sym] = 0xFFFF;
+ }
+
+ // next_symbol = base of allocation for long codes
+ next_symbol = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(table_mask >> 1);
+
+ // Give ourselves room for codes to grow by up to 16 more bits.
+ // codes now start at bit nbits+16 and end at (nbits+16-codelength)
+ pos <<= 16;
+ table_mask <<= 16;
+ bit_mask = 1 << 15;
+
+ for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++)
+ {
+ for (sym = 0; sym < nsyms; sym++)
+ {
+ if (length[sym] != bit_num)
+ continue;
+ if (pos >= table_mask)
+ return false; // Table overflow
+
+ leaf = pos >> 16;
+ for (fill = 0; fill < (bit_num - nbits); fill++)
+ {
+ // If this path hasn't been taken yet, 'allocate' two entries
+ if (table[leaf] == 0xFFFF)
+ {
+ table[(next_symbol << 1)] = 0xFFFF;
+ table[(next_symbol << 1) + 1] = 0xFFFF;
+ table[leaf] = next_symbol++;
+ }
+
+ // Follow the path and select either left or right for next bit
+ leaf = (uint)(table[leaf] << 1);
+ if (((pos >> (15 - (int)fill)) & 1) != 0)
+ leaf++;
+ }
+
+ table[leaf] = sym;
+ pos += bit_mask;
+ }
+
+ bit_mask >>= 1;
+ }
+
+ // Full table?
+ return pos == table_mask;
+ }
+
+ #endregion
+
+ #region LSB
+
+ ///
+ /// 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)
+ {
+ ENSURE_BITS(HUFF_MAXBITS);
+ ushort sym = table[PEEK_BITS_LSB(tablebits)];
+ if (sym >= maxsymbols)
+ HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols);
+
+ REMOVE_BITS_LSB(lengths[sym]);
+ return sym;
+ }
+
+ ///
+ /// Traverse for a single symbol
+ ///
+ private void HUFF_TRAVERSE_LSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols)
+ {
+ int i = tablebits - 1;
+ do
+ {
+ if (i++ > HUFF_MAXBITS)
+ {
+ Error = HUFF_ERROR();
+ return;
+ }
+
+ sym = table[(sym << 1) | ((BitBuffer >> i) & 1)];
+ } while (sym >= maxsymbols);
+ }
+
+ ///
+ /// This function was originally coded by David Tritscher.
+ ///
+ /// It builds a fast huffman decoding table from
+ /// a canonical huffman code lengths table.
+ ///
+ /// total number of symbols in this huffman tree.
+ /// any symbols with a code length of nbits or less can be decoded in one lookup of the table.
+ /// A table to get code lengths from [0 to nsyms-1]
+ ///
+ /// The table to fill up with decoded symbols and pointers.
+ /// Should be ((1<
+ /// True for OK or false for error
+ public static bool MakeDecodeTableLSB(int nsyms, int nbits, byte[] length, ushort[] table)
+ {
+ ushort sym, next_symbol;
+ uint leaf, fill;
+ uint reverse;
+ byte bit_num;
+ uint pos = 0; // The current position in the decode table
+ uint table_mask = (uint)1 << nbits;
+ uint bit_mask = table_mask >> 1; // Don't do 0 length codes
+
+ // Fill entries for codes short enough for a direct mapping
+ for (bit_num = 1; bit_num <= nbits; bit_num++)
+ {
+ for (sym = 0; sym < nsyms; sym++)
+ {
+ if (length[sym] != bit_num)
+ continue;
+
+ // Reverse the significant bits
+ fill = length[sym];
+ reverse = pos >> (int)(nbits - fill);
+ leaf = 0;
+
+ do
+ {
+ leaf <<= 1;
+ leaf |= reverse & 1;
+ reverse >>= 1;
+ } while (--fill != 0);
+
+ if ((pos += bit_mask) > table_mask)
+ return false; // Table overrun
+
+ // Fill all possible lookups of this symbol with the symbol itself
+ fill = bit_mask;
+ next_symbol = (ushort)(1 << bit_num);
+
+ do
+ {
+ table[leaf] = sym;
+ leaf += next_symbol;
+ } while (--fill != 0);
+ }
+
+ bit_mask >>= 1;
+ }
+
+ // Exit with success if table is now complete
+ if (pos == table_mask)
+ return true;
+
+ // Mark all remaining table entries as unused
+ for (sym = (ushort)pos; sym < table_mask; sym++)
+ {
+ reverse = sym;
+ leaf = 0;
+ fill = (uint)nbits;
+
+ do
+ {
+ leaf <<= 1;
+ leaf |= reverse & 1;
+ reverse >>= 1;
+ } while (--fill != 0);
+
+ table[leaf] = 0xFFFF;
+ }
+
+ // next_symbol = base of allocation for long codes
+ next_symbol = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(table_mask >> 1);
+
+ // Give ourselves room for codes to grow by up to 16 more bits.
+ // codes now start at bit nbits+16 and end at (nbits+16-codelength)
+ pos <<= 16;
+ table_mask <<= 16;
+ bit_mask = 1 << 15;
+
+ for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++)
+ {
+ for (sym = 0; sym < nsyms; sym++)
+ {
+ if (length[sym] != bit_num)
+ continue;
+ if (pos >= table_mask)
+ return false; // Table overflow
+
+ // leaf = the first nbits of the code, reversed
+ reverse = pos >> 16;
+ leaf = 0;
+ fill = (uint)nbits;
+
+ do
+ {
+ leaf <<= 1;
+ leaf |= reverse & 1;
+ reverse >>= 1;
+ } while (--fill != 0);
+
+ for (fill = 0; fill < (bit_num - nbits); fill++)
+ {
+ // If this path hasn't been taken yet, 'allocate' two entries
+ if (table[leaf] == 0xFFFF)
+ {
+ table[(next_symbol << 1)] = 0xFFFF;
+ table[(next_symbol << 1) + 1] = 0xFFFF;
+ table[leaf] = (ushort)next_symbol++;
+ }
+
+ // Follow the path and select either left or right for next bit
+ leaf = (uint)(table[leaf] << 1);
+ if (((pos >> (15 - (int)fill)) & 1) != 0)
+ leaf++;
+ }
+
+ table[leaf] = sym;
+ pos += bit_mask;
+ }
+
+ bit_mask >>= 1;
+ }
+
+ // Full table?
+ return pos == table_mask;
+ }
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs b/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs
index 1ddeac7d..c26e200f 100644
--- a/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs
+++ b/BurnOutSharp/External/libmspack/Compression/CompressionStream.cs
@@ -10,647 +10,47 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
-using System;
-using static LibMSPackSharp.Compression.Constants;
-
namespace LibMSPackSharp.Compression
{
- public abstract class CompressionStream : BaseDecompressState
+ public abstract partial class CompressionStream : BaseDecompressState
{
- #region I/O buffering
-
public byte[] InputBuffer { get; set; }
public uint InputBufferSize { get; set; }
+ ///
+ /// i_ptr
+ ///
+ public int InputPointer { get; set; }
+
+ ///
+ /// i_end
+ ///
+ public int InputEnd { get; set; }
+
+ ///
+ /// o_ptr
+ ///
public int OutputPointer { get; set; }
+ ///
+ /// o_end
+ ///
public int OutputEnd { get; set; }
- internal BufferState BufferState { get; set; }
+ ///
+ /// bit_buffer
+ ///
+ public uint BitBuffer { get; set; }
+
+ ///
+ /// bits_left
+ ///
+ public int BitsLeft { get; set; }
///
/// Have we reached the end of input?
///
public int EndOfInput { get; set; }
-
- #endregion
-
- #region ReadBits Methods
-
- /* This header defines macros that read data streams by
- * the individual bits
- *
- * INIT_BITS initialises bitstream state in state structure
- * STORE_BITS stores bitstream state in state structure
- * RESTORE_BITS restores bitstream state from state structure
- * ENSURE_BITS(n) ensure there are at least N bits in the bit buffer
- * READ_BITS(var,n) takes N bits from the buffer and puts them in var
- * PEEK_BITS(n) extracts without removing N bits from the bit buffer
- * REMOVE_BITS(n) removes N bits from the bit buffer
- *
- * READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS,
- * which means it's limited to reading the number of bits you can
- * ensure at any one time. It also fails if asked to read zero bits.
- * If you need to read zero bits, or more bits than can be ensured in
- * one go, use READ_MANY_BITS instead.
- *
- * These macros have variable names baked into them, so to use them
- * you have to define some macros:
- * - BITS_TYPE: the type name of your state structure
- * - BITS_VAR: the variable that points to your state structure
- * - define BITS_ORDER_MSB if bits are read from the MSB, or
- * define BITS_ORDER_LSB if bits are read from the LSB
- * - READ_BYTES: some code that reads more data into the bit buffer,
- * it should use READ_IF_NEEDED (calls read_input if the byte buffer
- * is empty), then INJECT_BITS(data,n) to put data from the byte
- * buffer into the bit buffer.
- *
- * You also need to define some variables and structure members:
- * - byte[] i_ptr; // current position in the byte buffer
- * - byte[] i_end; // end of the byte buffer
- * - uint bit_buffer; // the bit buffer itself
- * - uint bits_left; // number of bits remaining
- *
- * If you use read_input() and READ_IF_NEEDED, they also expect these
- * structure members:
- * - struct mspack_system *sys; // to access sys->read()
- * - uint error; // to record/return read errors
- * - byte input_end; // to mark reaching the EOF
- * - byte[] inbuf; // the input byte buffer
- * - uint inbuf_size; // the size of the input byte buffer
- *
- * Your READ_BYTES implementation should read data from *i_ptr and
- * put them in the bit buffer. READ_IF_NEEDED will call read_input()
- * if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to
- * the start of inbuf and i_end to the end of inbuf.
- *
- * If you're reading in MSB order, the routines work by using the area
- * beyond the MSB and the LSB of the bit buffer as a free source of
- * zeroes when shifting. This avoids having to mask any bits. So we
- * have to know the bit width of the bit buffer variable. We use
- * and CHAR_BIT to find the size of the bit buffer in bits.
- *
- * If you are reading in LSB order, bits need to be masked. Normally
- * this is done by computing the mask: N bits are masked by the value
- * (1<
- /// Initialises bitstream state in state structure
- ///
- public void INIT_BITS()
- {
- BufferState = new BufferState();
- BufferState.Init();
- EndOfInput = 0;
- }
-
- ///
- /// Stores bitstream state in state structure
- ///
- public void STORE_BITS(BufferState state)
- {
- BufferState.InputPointer = state.InputPointer;
- BufferState.InputEnd = state.InputEnd;
- BufferState.BitBuffer = state.BitBuffer;
- BufferState.BitsLeft = state.BitsLeft;
- }
-
- ///
- /// Restores bitstream state from state structure
- ///
- public BufferState RESTORE_BITS()
- {
- 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, BufferState state)
- {
- while (state.BitsLeft < nbits)
- {
- READ_BYTES(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
- }
- }
-
- ///
- /// Read from the input if the buffer is empty
- ///
- public void READ_IF_NEEDED(BufferState state)
- {
- if (state.InputPointer >= state.InputEnd)
- {
- ReadInput();
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- state.InputPointer = BufferState.InputPointer;
- state.InputEnd = BufferState.InputEnd;
- }
- }
-
- ///
- /// Read bytes from the input into the bit buffer
- ///
- public abstract void READ_BYTES(BufferState state);
-
- ///
- /// Read an input stream and fill the buffer
- ///
- protected virtual void ReadInput()
- {
- int read = System.Read(InputFileHandle, InputBuffer, 0, (int)InputBufferSize);
- if (read < 0)
- {
- Error = Error.MSPACK_ERR_READ;
- return;
- }
-
- // We might overrun the input stream by asking for bits we don't use,
- // so fake 2 more bytes at the end of input
- if (read == 0)
- {
- if (EndOfInput != 0)
- {
- Console.WriteLine("Out of input bytes");
- Error = Error.MSPACK_ERR_READ;
- return;
- }
- else
- {
- read = 2;
- InputBuffer[0] = InputBuffer[1] = 0;
- EndOfInput = 1;
- }
- }
-
- // Update i_ptr and i_end
- BufferState.InputPointer = 0;
- BufferState.InputEnd = read;
- }
-
- #endregion
-
- #region MSB
-
- ///
- /// Inject data into the bit buffer
- ///
- public void INJECT_BITS_MSB(int bitdata, int nbits, BufferState state)
- {
- state.BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - state.BitsLeft));
- state.BitsLeft += nbits;
- }
-
- ///
- /// Extracts without removing N bits from the bit buffer
- ///
- public long PEEK_BITS_MSB(int nbits, uint bit_buffer) => (bit_buffer >> (BITBUF_WIDTH - (nbits)));
-
- ///
- /// Takes N bits from the buffer and puts them in var
- ///
- public long READ_BITS_MSB(int nbits, BufferState state)
- {
- ENSURE_BITS(nbits, state);
- if (Error != Error.MSPACK_ERR_OK)
- return -1;
-
- long temp = PEEK_BITS_MSB(nbits, state.BitBuffer);
-
- state.REMOVE_BITS_MSB(nbits);
- return temp;
- }
-
- ///
- /// Read multiple bits and put them in var
- ///
- public long READ_MANY_BITS_MSB(int nbits, BufferState state)
- {
- byte needed = (byte)(nbits), bitrun;
- long temp = 0;
- while (needed > 0)
- {
- if (state.BitsLeft <= (BITBUF_WIDTH - 16))
- {
- READ_BYTES(state);
- if (Error != Error.MSPACK_ERR_OK)
- return -1;
- }
-
- 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;
- }
-
- #endregion
-
- #region LSB
-
- ///
- /// Inject data into the bit buffer
- ///
- public void INJECT_BITS_LSB(int bitdata, int nbits, BufferState state)
- {
- state.BitBuffer |= (uint)(bitdata << state.BitsLeft);
- state.BitsLeft += nbits;
- }
-
- ///
- /// Extracts without removing N bits from the bit buffer
- ///
- public long PEEK_BITS_LSB(int nbits, uint bit_buffer) => (bit_buffer & ((1 << (nbits)) - 1));
-
- ///
- /// 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 & LSBBitMask[(nbits)];
-
- ///
- /// Takes N bits from the buffer and puts them in var
- ///
- public long READ_BITS_LSB(int nbits, BufferState state)
- {
- ENSURE_BITS(nbits, state);
- if (Error != Error.MSPACK_ERR_OK)
- return -1;
-
- long temp = PEEK_BITS_LSB(nbits, state.BitBuffer);
-
- 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, BufferState state)
- {
- ENSURE_BITS(nbits, state);
- if (Error != Error.MSPACK_ERR_OK)
- return -1;
-
- long temp = PEEK_BITS_T_LSB(nbits, state.BitBuffer);
-
- state.REMOVE_BITS_LSB(nbits);
- return temp;
- }
-
- #endregion
-
- #endregion
-
- #region ReadHuff Methods
-
- #region Common
-
- ///
- /// Per compression error code for decoding failure
- ///
- public abstract Error HUFF_ERROR();
-
- #endregion
-
- #region MSB
-
- ///
- /// 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, BufferState state)
- {
- 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, state.BitBuffer);
-
- state.REMOVE_BITS_MSB(lengths[sym]);
- return sym;
- }
-
- ///
- /// Traverse for a single symbol
- ///
- private void HUFF_TRAVERSE_MSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols, uint bit_buffer)
- {
- int i = 1 << (BITBUF_WIDTH - tablebits);
- do
- {
- if ((i >>= 1) == 0)
- {
- Error = HUFF_ERROR();
- return;
- }
-
- sym = table[(sym << 1) | ((bit_buffer & i) != 0 ? 1 : 0)];
- } while (sym >= maxsymbols);
- }
-
- ///
- /// This function was originally coded by David Tritscher.
- ///
- /// It builds a fast huffman decoding table from
- /// a canonical huffman code lengths table.
- ///
- /// total number of symbols in this huffman tree.
- /// any symbols with a code length of nbits or less can be decoded in one lookup of the table.
- /// A table to get code lengths from [0 to nsyms-1]
- ///
- /// The table to fill up with decoded symbols and pointers.
- /// Should be ((1<
- /// True for OK or false for error
- public static bool MakeDecodeTableMSB(int nsyms, int nbits, byte[] length, ushort[] table)
- {
- ushort sym, next_symbol;
- uint leaf, fill;
- byte bit_num;
- uint pos = 0; // The current position in the decode table
- uint table_mask = (uint)1 << nbits;
- uint bit_mask = table_mask >> 1; // Don't do 0 length codes
-
- // Fill entries for codes short enough for a direct mapping
- for (bit_num = 1; bit_num <= nbits; bit_num++)
- {
- for (sym = 0; sym < nsyms; sym++)
- {
- if (length[sym] != bit_num)
- continue;
-
- leaf = pos;
- if ((pos += bit_mask) > table_mask)
- return false; // Table overrun
-
- // Fill all possible lookups of this symbol with the symbol itself
- for (fill = bit_mask; fill-- > 0;)
- {
- table[leaf++] = sym;
- }
- }
-
- bit_mask >>= 1;
- }
-
- // Exit with success if table is now complete
- if (pos == table_mask)
- return true;
-
- // Mark all remaining table entries as unused
- for (sym = (ushort)pos; sym < table_mask; sym++)
- {
- table[sym] = 0xFFFF;
- }
-
- // next_symbol = base of allocation for long codes
- next_symbol = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(table_mask >> 1);
-
- // Give ourselves room for codes to grow by up to 16 more bits.
- // codes now start at bit nbits+16 and end at (nbits+16-codelength)
- pos <<= 16;
- table_mask <<= 16;
- bit_mask = 1 << 15;
-
- for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++)
- {
- for (sym = 0; sym < nsyms; sym++)
- {
- if (length[sym] != bit_num)
- continue;
- if (pos >= table_mask)
- return false; // Table overflow
-
- leaf = pos >> 16;
- for (fill = 0; fill < (bit_num - nbits); fill++)
- {
- // If this path hasn't been taken yet, 'allocate' two entries
- if (table[leaf] == 0xFFFF)
- {
- table[(next_symbol << 1)] = 0xFFFF;
- table[(next_symbol << 1) + 1] = 0xFFFF;
- table[leaf] = next_symbol++;
- }
-
- // Follow the path and select either left or right for next bit
- leaf = (uint)(table[leaf] << 1);
- if (((pos >> (15 - (int)fill)) & 1) != 0)
- leaf++;
- }
-
- table[leaf] = sym;
- pos += bit_mask;
- }
-
- bit_mask >>= 1;
- }
-
- // Full table?
- return pos == table_mask;
- }
-
- #endregion
-
- #region LSB
-
- ///
- /// 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, BufferState state)
- {
- 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, state.BitBuffer);
-
- state.REMOVE_BITS_LSB(lengths[sym]);
- return sym;
- }
-
- ///
- /// Traverse for a single symbol
- ///
- private void HUFF_TRAVERSE_LSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols, uint bit_buffer)
- {
- int i = tablebits - 1;
- do
- {
- if (i++ > HUFF_MAXBITS)
- {
- Error = HUFF_ERROR();
- return;
- }
-
- sym = table[(sym << 1) | ((bit_buffer >> i) & 1)];
- } while (sym >= maxsymbols);
- }
-
- ///
- /// This function was originally coded by David Tritscher.
- ///
- /// It builds a fast huffman decoding table from
- /// a canonical huffman code lengths table.
- ///
- /// total number of symbols in this huffman tree.
- /// any symbols with a code length of nbits or less can be decoded in one lookup of the table.
- /// A table to get code lengths from [0 to nsyms-1]
- ///
- /// The table to fill up with decoded symbols and pointers.
- /// Should be ((1<
- /// True for OK or false for error
- public static bool MakeDecodeTableLSB(int nsyms, int nbits, byte[] length, ushort[] table)
- {
- ushort sym, next_symbol;
- uint leaf, fill;
- uint reverse;
- byte bit_num;
- uint pos = 0; // The current position in the decode table
- uint table_mask = (uint)1 << nbits;
- uint bit_mask = table_mask >> 1; // Don't do 0 length codes
-
- // Fill entries for codes short enough for a direct mapping
- for (bit_num = 1; bit_num <= nbits; bit_num++)
- {
- for (sym = 0; sym < nsyms; sym++)
- {
- if (length[sym] != bit_num)
- continue;
-
- // Reverse the significant bits
- fill = length[sym];
- reverse = pos >> (int)(nbits - fill);
- leaf = 0;
-
- do
- {
- leaf <<= 1;
- leaf |= reverse & 1;
- reverse >>= 1;
- } while (--fill != 0);
-
- if ((pos += bit_mask) > table_mask)
- return false; // Table overrun
-
- // Fill all possible lookups of this symbol with the symbol itself
- fill = bit_mask;
- next_symbol = (ushort)(1 << bit_num);
-
- do
- {
- table[leaf] = sym;
- leaf += next_symbol;
- } while (--fill != 0);
- }
-
- bit_mask >>= 1;
- }
-
- // Exit with success if table is now complete
- if (pos == table_mask)
- return true;
-
- // Mark all remaining table entries as unused
- for (sym = (ushort)pos; sym < table_mask; sym++)
- {
- reverse = sym;
- leaf = 0;
- fill = (uint)nbits;
-
- do
- {
- leaf <<= 1;
- leaf |= reverse & 1;
- reverse >>= 1;
- } while (--fill != 0);
-
- table[leaf] = 0xFFFF;
- }
-
- // next_symbol = base of allocation for long codes
- next_symbol = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(table_mask >> 1);
-
- // Give ourselves room for codes to grow by up to 16 more bits.
- // codes now start at bit nbits+16 and end at (nbits+16-codelength)
- pos <<= 16;
- table_mask <<= 16;
- bit_mask = 1 << 15;
-
- for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++)
- {
- for (sym = 0; sym < nsyms; sym++)
- {
- if (length[sym] != bit_num)
- continue;
- if (pos >= table_mask)
- return false; // Table overflow
-
- // leaf = the first nbits of the code, reversed
- reverse = pos >> 16;
- leaf = 0;
- fill = (uint)nbits;
-
- do
- {
- leaf <<= 1;
- leaf |= reverse & 1;
- reverse >>= 1;
- } while (--fill != 0);
-
- for (fill = 0; fill < (bit_num - nbits); fill++)
- {
- // If this path hasn't been taken yet, 'allocate' two entries
- if (table[leaf] == 0xFFFF)
- {
- table[(next_symbol << 1)] = 0xFFFF;
- table[(next_symbol << 1) + 1] = 0xFFFF;
- table[leaf] = (ushort)next_symbol++;
- }
-
- // Follow the path and select either left or right for next bit
- leaf = (uint)(table[leaf] << 1);
- if (((pos >> (15 - (int)fill)) & 1) != 0)
- leaf++;
- }
-
- table[leaf] = sym;
- pos += bit_mask;
- }
-
- bit_mask >>= 1;
- }
-
- // Full table?
- return pos == table_mask;
- }
-
- #endregion
-
- #endregion
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.Decompress.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.Decompress.cs
new file mode 100644
index 00000000..59ba4297
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.Decompress.cs
@@ -0,0 +1,293 @@
+/* This file is part of libmspack.
+ * (C) 2003-2010 Stuart Caie.
+ *
+ * libmspack is free software; 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
+ */
+
+using System.IO;
+using static LibMSPackSharp.Constants;
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ ///
+ /// In the KWAJ LZH format, there is no special 'eof' marker, it just
+ /// ends. Depending on how many bits are left in the final byte when
+ /// the stream ends, that might be enough to start another literal or
+ /// match. The only easy way to detect that we've come to an end is to
+ /// guard all bit-reading. We allow fake bits to be read once we reach
+ /// the end of the stream, but we check if we then consumed any of
+ /// those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
+ /// isn't how the default ReadInput works (it simply lets
+ /// 2 fake bytes in then stops), so we implement our own.
+ ///
+ public partial class LZHKWAJ
+ {
+ public static LZHKWAJ Init(SystemImpl sys, FileStream input, FileStream output)
+ {
+ if (sys == null || input == null || output == null)
+ return null;
+
+ return new LZHKWAJ()
+ {
+ System = sys,
+ InputFileHandle = input,
+ OutputFileHandle = output,
+ };
+ }
+
+ public Error Decompress()
+ {
+ int i;
+ int lit_run = 0;
+ int j, pos = 0, len, offset;
+ uint[] types = new uint[6];
+
+ // Reset global state
+ INIT_BITS();
+
+ for (i = 0; i < LZSS_WINDOW_SIZE; i++)
+ {
+ 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)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+ }
+
+ // Read huffman table symbol lengths and build huffman trees
+ BUILD_TREE(types[0], MATCHLEN1_table, MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS);
+ BUILD_TREE(types[1], MATCHLEN2_table, MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS);
+ BUILD_TREE(types[2], LITLEN_table, LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS);
+ BUILD_TREE(types[3], OFFSET_table, OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS);
+ BUILD_TREE(types[4], LITERAL_table, LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS);
+
+ while (EndOfInput == 0)
+ {
+ if (lit_run != 0)
+ {
+ len = (int)READ_HUFFSYM_SAFE(MATCHLEN2_table, MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+ }
+ else
+ {
+ len = (int)READ_HUFFSYM_SAFE(MATCHLEN1_table, MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+ }
+
+ if (len > 0)
+ {
+ len += 2;
+ lit_run = 0; // Not the end of a literal run
+
+ j = (int)READ_HUFFSYM_SAFE(OFFSET_table, OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ offset = j << 6;
+
+ j = (int)READ_BITS_SAFE(6);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ offset |= j;
+
+ // Copy match as output and into the ring buffer
+ while (len-- > 0)
+ {
+ Window[pos] = Window[(pos + 4096 - offset) & 4095];
+ WRITE_BYTE(pos);
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ pos++;
+ pos &= 4095;
+ }
+ }
+ else
+ {
+ len = (int)READ_HUFFSYM_SAFE(LITLEN_table, LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ len++;
+ lit_run = (len == 32) ? 0 : 1; // End of a literal run?
+ while (len-- > 0)
+ {
+ j = (int)READ_HUFFSYM_SAFE(LITERAL_table, LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ // Copy as output and into the ring buffer
+ Window[pos] = (byte)j;
+
+ WRITE_BYTE(pos);
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ pos++; pos &= 4095;
+ }
+ }
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ private Error BUILD_TREE(uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
+ {
+ Error err = ReadLens(type, (uint)maxsymbols, MATCHLEN1_len);
+ if (err != Error.MSPACK_ERR_OK)
+ return err;
+
+ if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
+ return Error.MSPACK_ERR_DATAFORMAT;
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ ///
+ /// Safely read bits from the buffer
+ ///
+ private long READ_BITS_SAFE(int nbits)
+ {
+ long val = READ_BITS_MSB(nbits);
+ if (EndOfInput != 0 && BitsLeft < EndOfInput)
+ Error = Error.MSPACK_ERR_NOMEMORY;
+ else
+ Error = Error.MSPACK_ERR_OK;
+
+ return val;
+ }
+
+ ///
+ /// Safely read a symbol from a Huffman tree
+ ///
+ private long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
+ {
+ long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols);
+ if (EndOfInput != 0 && BitsLeft < EndOfInput)
+ Error = Error.MSPACK_ERR_NOMEMORY;
+ else
+ Error = Error.MSPACK_ERR_OK;
+
+ return val;
+ }
+
+ ///
+ /// Write a single byte to the output stream
+ ///
+ private void WRITE_BYTE(int pos)
+ {
+ if (System.Write(OutputFileHandle, Window, pos, 1) != 1)
+ Error = Error.MSPACK_ERR_WRITE;
+ }
+
+ private Error ReadLens(uint type, uint numsyms, byte[] lens)
+ {
+ uint i, c, sel;
+
+ switch (type)
+ {
+ case 0:
+ i = numsyms;
+ c = (uint)((i == 16) ? 4 : (i == 32) ? 5 : (i == 64) ? 6 : (i == 256) ? 8 : 0);
+ for (i = 0; i < numsyms; i++)
+ {
+ lens[i] = (byte)c;
+ }
+
+ break;
+
+ case 1:
+ c = (uint)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ lens[0] = (byte)c;
+ for (i = 1; i < numsyms; i++)
+ {
+ sel = (uint)READ_BITS_SAFE(1);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ if (sel == 0)
+ {
+ lens[i] = (byte)c;
+ }
+ else
+ {
+ sel = (uint)READ_BITS_SAFE(1);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ if (sel == 0)
+ {
+ lens[i] = (byte)++c;
+ }
+ else
+ {
+ c = (uint)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ lens[i] = (byte)c;
+ }
+ }
+ }
+
+ break;
+
+ case 2:
+ c = (uint)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ lens[0] = (byte)c;
+ for (i = 1; i < numsyms; i++)
+ {
+ sel = (uint)READ_BITS_SAFE(2);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ if (sel == 3)
+ {
+ c = (uint)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+ }
+ else
+ {
+ c += (uint)((byte)sel - 1);
+ }
+
+ lens[i] = (byte)c;
+ }
+
+ break;
+
+ case 3:
+ for (i = 0; i < numsyms; i++)
+ {
+ c = (uint)READ_BITS_SAFE(4);
+ if (Error == Error.MSPACK_ERR_NOMEMORY)
+ return Error.MSPACK_ERR_OK;
+
+ lens[i] = (byte)c;
+ }
+
+ break;
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadBits.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadBits.cs
new file mode 100644
index 00000000..138ae39c
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadBits.cs
@@ -0,0 +1,64 @@
+/* This file is part of libmspack.
+ * (C) 2003-2010 Stuart Caie.
+ *
+ * libmspack is free software; 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
+ */
+
+using static LibMSPackSharp.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ public partial class LZHKWAJ : CompressionStream
+ {
+ ///
+ public override void READ_BYTES()
+ {
+ if (InputPointer >= InputEnd)
+ {
+ ReadInput();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ InputPointer = InputPointer;
+ InputEnd = InputEnd;
+ }
+
+ INJECT_BITS_MSB(InputBuffer[InputPointer++], 8);
+ }
+
+ ///
+ protected override void ReadInput()
+ {
+ int read;
+ if (EndOfInput != 0)
+ {
+ EndOfInput += 8;
+ InputBuffer[0] = 0;
+ read = 1;
+ }
+ else
+ {
+ read = System.Read(InputFileHandle, InputBuffer, 0, KWAJ_INPUT_SIZE);
+ if (read < 0)
+ {
+ Error = Error.MSPACK_ERR_READ;
+ return;
+ }
+
+ if (read == 0)
+ {
+ InputEnd = 8;
+ InputBuffer[0] = 0;
+ read = 1;
+ }
+ }
+
+ // Update InputPointer and InputLength
+ InputPointer = 0;
+ InputEnd = read;
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadHuff.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadHuff.cs
new file mode 100644
index 00000000..78c1e026
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadHuff.cs
@@ -0,0 +1,17 @@
+/* This file is part of libmspack.
+ * (C) 2003-2010 Stuart Caie.
+ *
+ * libmspack is free software; 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 partial class LZHKWAJ : CompressionStream
+ {
+ ///
+ public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs
index 13a0004c..442fd5a8 100644
--- a/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs
+++ b/BurnOutSharp/External/libmspack/Compression/LZHKWAJ.cs
@@ -7,263 +7,31 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
-using System.IO;
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
- ///
- /// In the KWAJ LZH format, there is no special 'eof' marker, it just
- /// ends. Depending on how many bits are left in the final byte when
- /// the stream ends, that might be enough to start another literal or
- /// match. The only easy way to detect that we've come to an end is to
- /// guard all bit-reading. We allow fake bits to be read once we reach
- /// the end of the stream, but we check if we then consumed any of
- /// those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
- /// isn't how the default ReadInput works (it simply lets
- /// 2 fake bytes in then stops), so we implement our own.
- ///
- public class LZHKWAJ
+ public partial class LZHKWAJ : CompressionStream
{
- public static LZHKWAJStream Init(SystemImpl sys, FileStream input, FileStream output)
- {
- if (sys == null || input == null || output == null)
- return null;
+ // Huffman code lengths
- return new LZHKWAJStream()
- {
- System = sys,
- InputFileHandle = input,
- OutputFileHandle = output,
- };
- }
+ public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS];
+ public byte[] MATCHLEN2_len { get; set; } = new byte[KWAJ_MATCHLEN2_SYMS];
+ public byte[] LITLEN_len { get; set; } = new byte[KWAJ_LITLEN_SYMS];
+ public byte[] OFFSET_len { get; set; } = new byte[KWAJ_OFFSET_SYMS];
+ public byte[] LITERAL_len { get; set; } = new byte[KWAJ_LITERAL_SYMS];
- public static Error Decompress(LZHKWAJStream lzh)
- {
- int i;
- int lit_run = 0;
- int j, pos = 0, len, offset;
- uint[] types = new uint[6];
+ // Huffman decoding tables
- // Reset global state
- lzh.INIT_BITS();
- BufferState state = lzh.RESTORE_BITS();
+ public ushort[] MATCHLEN1_table { get; set; } = new ushort[KWAJ_MATCHLEN1_TBLSIZE];
+ public ushort[] MATCHLEN2_table { get; set; } = new ushort[KWAJ_MATCHLEN2_TBLSIZE];
+ public ushort[] LITLEN_table { get; set; } = new ushort[KWAJ_LITLEN_TBLSIZE];
+ public ushort[] OFFSET_table { get; set; } = new ushort[KWAJ_OFFSET_TBLSIZE];
+ public ushort[] LITERAL_table { get; set; } = new ushort[KWAJ_LITERAL_TBLSIZE];
- for (i = 0; i < LZSS_WINDOW_SIZE; i++)
- {
- lzh.Window[i] = LZSS_WINDOW_FILL;
- }
+ // History window
- // 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, 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, 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, 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, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
- }
-
- if (len > 0)
- {
- 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, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- offset = j << 6;
-
- j = (int)lzh.READ_BITS_SAFE(6, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- offset |= j;
-
- // Copy match as output and into the ring buffer
- while (len-- > 0)
- {
- lzh.Window[pos] = lzh.Window[(pos + 4096 - offset) & 4095];
- lzh.WRITE_BYTE(pos);
- if (lzh.Error != Error.MSPACK_ERR_OK)
- return lzh.Error;
-
- pos++;
- pos &= 4095;
- }
- }
- else
- {
- 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;
-
- len++;
- 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, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- // Copy as output and into the ring buffer
- lzh.Window[pos] = (byte)j;
-
- lzh.WRITE_BYTE(pos);
- if (lzh.Error != Error.MSPACK_ERR_OK)
- return lzh.Error;
-
- pos++; pos &= 4095;
- }
- }
- }
-
- return Error.MSPACK_ERR_OK;
- }
-
- private static Error BUILD_TREE(LZHKWAJStream lzh, uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
- {
- lzh.STORE_BITS(state);
-
- Error err = ReadLens(lzh, type, (uint)maxsymbols, lzh.MATCHLEN1_len);
- if (err != Error.MSPACK_ERR_OK)
- return err;
-
- 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;
-
- return Error.MSPACK_ERR_OK;
- }
-
- private static Error ReadLens(LZHKWAJStream lzh, uint type, uint numsyms, byte[] lens)
- {
- uint i, c, sel;
-
- BufferState state = lzh.RESTORE_BITS();
-
- switch (type)
- {
- case 0:
- i = numsyms;
- c = (uint)((i == 16) ? 4 : (i == 32) ? 5 : (i == 64) ? 6 : (i == 256) ? 8 : 0);
- for (i = 0; i < numsyms; i++)
- {
- lens[i] = (byte)c;
- }
-
- break;
-
- case 1:
- 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, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- if (sel == 0)
- {
- lens[i] = (byte)c;
- }
- else
- {
- sel = (uint)lzh.READ_BITS_SAFE(1, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- if (sel == 0)
- {
- lens[i] = (byte)++c;
- }
- else
- {
- c = (uint)lzh.READ_BITS_SAFE(4, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- lens[i] = (byte)c;
- }
- }
- }
-
- break;
-
- case 2:
- 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, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- if (sel == 3)
- {
- c = (uint)lzh.READ_BITS_SAFE(4, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
- }
- else
- {
- c += (uint)((byte)sel - 1);
- }
-
- lens[i] = (byte)c;
- }
-
- break;
-
- case 3:
- for (i = 0; i < numsyms; i++)
- {
- c = (uint)lzh.READ_BITS_SAFE(4, state);
- if (lzh.Error == Error.MSPACK_ERR_NOMEMORY)
- return Error.MSPACK_ERR_OK;
-
- lens[i] = (byte)c;
- }
-
- break;
- }
-
- lzh.STORE_BITS(state);
-
- return Error.MSPACK_ERR_OK;
- }
+ public byte[] Window { get; set; } = new byte[LZSS_WINDOW_SIZE];
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs b/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs
deleted file mode 100644
index 094c33e1..00000000
--- a/BurnOutSharp/External/libmspack/Compression/LZHKWAJStream.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-/* This file is part of libmspack.
- * (C) 2003-2010 Stuart Caie.
- *
- * libmspack is free software; 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
- */
-
-using static LibMSPackSharp.Constants;
-using static LibMSPackSharp.Compression.Constants;
-
-namespace LibMSPackSharp.Compression
-{
- public class LZHKWAJStream : CompressionStream
- {
- #region Fields
-
- // Huffman code lengths
-
- public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS];
- public byte[] MATCHLEN2_len { get; set; } = new byte[KWAJ_MATCHLEN2_SYMS];
- public byte[] LITLEN_len { get; set; } = new byte[KWAJ_LITLEN_SYMS];
- public byte[] OFFSET_len { get; set; } = new byte[KWAJ_OFFSET_SYMS];
- public byte[] LITERAL_len { get; set; } = new byte[KWAJ_LITERAL_SYMS];
-
- // Huffman decoding tables
-
- public ushort[] MATCHLEN1_table { get; set; } = new ushort[KWAJ_MATCHLEN1_TBLSIZE];
- public ushort[] MATCHLEN2_table { get; set; } = new ushort[KWAJ_MATCHLEN2_TBLSIZE];
- public ushort[] LITLEN_table { get; set; } = new ushort[KWAJ_LITLEN_TBLSIZE];
- public ushort[] OFFSET_table { get; set; } = new ushort[KWAJ_OFFSET_TBLSIZE];
- public ushort[] LITERAL_table { get; set; } = new ushort[KWAJ_LITERAL_TBLSIZE];
-
- // History window
-
- public byte[] Window { get; set; } = new byte[LZSS_WINDOW_SIZE];
-
- #endregion
-
- #region Specialty Methods
-
- /* In the KWAJ LZH format, there is no special 'eof' marker, it just
- * ends. Depending on how many bits are left in the final byte when
- * the stream ends, that might be enough to start another literal or
- * match. The only easy way to detect that we've come to an end is to
- * guard all bit-reading. We allow fake bits to be read once we reach
- * the end of the stream, but we check if we then consumed any of
- * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
- * isn't how the default readbits.h read_input() works (it simply lets
- * 2 fake bytes in then stops), so we implement our own.
- */
-
- ///
- /// Safely read bits from the buffer
- ///
- public long READ_BITS_SAFE(int nbits, BufferState state)
- {
- long val = READ_BITS_MSB(nbits, state);
- if (EndOfInput != 0 && BufferState.BitsLeft < EndOfInput)
- Error = Error.MSPACK_ERR_NOMEMORY;
- else
- Error = Error.MSPACK_ERR_OK;
-
- return val;
- }
-
- ///
- /// Safely read a symbol from a Huffman tree
- ///
- public long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols, BufferState state)
- {
- 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;
-
- return val;
- }
-
- ///
- /// Write a single byte to the output stream
- ///
- public void WRITE_BYTE(int pos)
- {
- if (System.Write(OutputFileHandle, Window, pos, 1) != 1)
- Error = Error.MSPACK_ERR_WRITE;
- }
-
- #endregion
-
- ///
- public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
-
- ///
- public override void READ_BYTES(BufferState state)
- {
- if (state.InputPointer >= state.InputEnd)
- {
- ReadInput();
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- state.InputPointer = BufferState.InputPointer;
- state.InputEnd = BufferState.InputEnd;
- }
-
- INJECT_BITS_MSB(InputBuffer[state.InputPointer++], 8, state);
- }
-
- ///
- protected override void ReadInput()
- {
- int read;
- if (EndOfInput != 0)
- {
- EndOfInput += 8;
- InputBuffer[0] = 0;
- read = 1;
- }
- else
- {
- read = System.Read(InputFileHandle, InputBuffer, 0, KWAJ_INPUT_SIZE);
- if (read < 0)
- {
- Error = Error.MSPACK_ERR_READ;
- return;
- }
-
- if (read == 0)
- {
- BufferState.InputEnd = 8;
- InputBuffer[0] = 0;
- read = 1;
- }
- }
-
- // Update InputPointer and InputLength
- BufferState.InputPointer = 0;
- BufferState.InputEnd = read;
- }
- }
-}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.Decompress.cs b/BurnOutSharp/External/libmspack/Compression/LZX.Decompress.cs
new file mode 100644
index 00000000..23ab2ca6
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZX.Decompress.cs
@@ -0,0 +1,1045 @@
+/* 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; 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
+ */
+
+/* Microsoft's LZX document (in cab-sdk.exe) and their implementation
+ * of the com.ms.util.cab Java package do not concur.
+ *
+ * In the LZX document, there is a table showing the correlation between
+ * window size and the number of position slots. It states that the 1MB
+ * window = 40 slots and the 2MB window = 42 slots. In the implementation,
+ * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
+ * first slot whose position base is equal to or more than the required
+ * window size'. This would explain why other tables in the document refer
+ * to 50 slots rather than 42.
+ *
+ * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
+ * is not defined in the specification.
+ *
+ * The LZX document does not state the uncompressed block has an
+ * uncompressed length field. Where does this length field come from, so
+ * we can know how large the block is? The implementation has it as the 24
+ * bits following after the 3 blocktype bits, before the alignment
+ * padding.
+ *
+ * The LZX document states that aligned offset blocks have their aligned
+ * offset huffman tree AFTER the main and length trees. The implementation
+ * suggests that the aligned offset tree is BEFORE the main and length
+ * trees.
+ *
+ * The LZX document decoding algorithm states that, in an aligned offset
+ * block, if an extra_bits value is 1, 2 or 3, then that number of bits
+ * should be read and the result added to the match offset. This is
+ * correct for 1 and 2, but not 3, where just a huffman symbol (using the
+ * aligned tree) should be read.
+ *
+ * Regarding the E8 preprocessing, the LZX document states 'No translation
+ * may be performed on the last 6 bytes of the input block'. This is
+ * correct. However, the pseudocode provided checks for the *E8 leader*
+ * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
+ * from the end, this would cause the next four bytes to be modified, at
+ * least one of which would be in the last 6 bytes, which is not allowed
+ * according to the spec.
+ *
+ * The specification states that the huffman trees must always contain at
+ * least one element. However, many CAB files contain blocks where the
+ * length tree is completely empty (because there are no matches), and
+ * this is expected to succeed.
+ *
+ * The errors in LZX documentation appear have been corrected in the
+ * new documentation for the LZX DELTA format.
+ *
+ * http://msdn.microsoft.com/en-us/library/cc483133.aspx
+ *
+ * However, this is a different format, an extension of regular LZX.
+ * I have noticed the following differences, there may be more:
+ *
+ * The maximum window size has increased from 2MB to 32MB. This also
+ * increases the maximum number of position slots, etc.
+ *
+ * If the match length is 257 (the maximum possible), this signals
+ * a further length decoding step, that allows for matches up to
+ * 33024 bytes long.
+ *
+ * The format now allows for "reference data", supplied by the caller.
+ * If match offsets go further back than the number of bytes
+ * decompressed so far, that is them accessing the reference data.
+ */
+
+using System;
+using System.IO;
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ public partial class LZX
+ {
+ ///
+ /// Allocates and initialises LZX decompression state for decoding an LZX
+ /// stream.
+ ///
+ /// This routine uses system.alloc() to allocate memory. If memory
+ /// allocation fails, or the parameters to this function are invalid,
+ /// null is returned.
+ ///
+ /// A SystemImpl structure used to read from the input stream and write to the output stream, also to allocate and free memory.
+ /// an input stream with the LZX data.
+ /// an output stream to write the decoded data to.
+ ///
+ /// the size of the decoding window, which must be
+ /// between 15 and 21 inclusive for regular LZX
+ /// data, or between 17 and 25 inclusive for
+ /// LZX DELTA data.
+ ///
+ /// the interval at which the LZX bitstream is
+ /// reset, in multiples of LZX frames (32678
+ /// bytes), e.g. a value of 2 indicates the input
+ /// stream resets after every 65536 output bytes.
+ /// A value of 0 indicates that the bitstream never
+ /// resets, such as in CAB LZX streams.
+ ///
+ ///
+ /// the number of bytes to use as an input
+ /// bitstream buffer.
+ ///
+ ///
+ /// the length in bytes of the entirely
+ /// decompressed output stream, if known in
+ /// advance. It is used to correctly perform the
+ /// Intel E8 transformation, which must stop 6
+ /// bytes before the very end of the
+ /// decompressed stream. It is not otherwise used
+ /// or adhered to. If the full decompressed
+ /// length is known in advance, set it here.
+ /// If it is NOT known, use the value 0, and call
+ /// lzxd_set_outputLength() once it is
+ /// known. If never set, 4 of the final 6 bytes
+ /// of the output stream may be incorrect.
+ ///
+ ///
+ /// should be zero for all regular LZX data,
+ /// non-zero for LZX DELTA encoded data.
+ ///
+ ///
+ /// a pointer to an initialised LZX structure, or null if
+ /// there was not enough memory or parameters to the function were wrong.
+ ///
+ public static LZX Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int reset_interval, int input_buffer_size, long output_length, bool is_delta)
+ {
+ if (system == null)
+ return null;
+
+ // LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB),
+ // regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB)
+ if (is_delta)
+ {
+ if (window_bits < 17 || window_bits > 25)
+ return null;
+ }
+ else
+ {
+ if (window_bits < 15 || window_bits > 21)
+ return null;
+ }
+
+ if (reset_interval < 0 || output_length < 0)
+ {
+ Console.WriteLine("Reset interval or output length < 0");
+ return null;
+ }
+
+ // Round up input buffer size to multiple of two
+ input_buffer_size = (input_buffer_size + 1) & -2;
+ if (input_buffer_size < 2)
+ return null;
+
+ // Allocate decompression state
+ LZX lzx = new LZX()
+ {
+ // Allocate decompression window and input buffer
+ Window = new byte[1 << window_bits],
+ InputBuffer = new byte[input_buffer_size],
+
+ System = system,
+ InputFileHandle = input,
+ OutputFileHandle = output,
+ Offset = 0,
+ Length = output_length,
+
+ InputBufferSize = (uint)input_buffer_size,
+ WindowSize = (uint)(1 << window_bits),
+ ReferenceDataSize = 0,
+ WindowPosition = 0,
+ FramePosition = 0,
+ Frame = 0,
+ ResetInterval = (uint)reset_interval,
+ IntelFileSize = 0,
+ IntelStarted = false,
+ Error = Error.MSPACK_ERR_OK,
+ NumOffsets = LZXPositionSlots[window_bits - 15] << 3,
+ IsDelta = is_delta,
+
+ OutputPointer = 0,
+ OutputEnd = 0,
+ OutputIsE8 = true,
+ };
+
+ lzx.ResetState();
+ lzx.INIT_BITS();
+
+ return lzx;
+ }
+
+ ///
+ /// Reads LZX DELTA reference data into the window and allows
+ /// lzxd_decompress() to reference it.
+ ///
+ /// Call this before the first call to lzxd_decompress().
+ ///
+ ///
+ /// an mspack_system implementation to use with the
+ /// input param. Only read() will be called.
+ ///
+ /// an input file handle to read reference data using system.read().
+ /// The length of the reference data. Cannot be longer than the LZX window size.
+ /// An error code, or MSPACK_ERR_OK if successful
+ public Error SetReferenceData(SystemImpl system, FileStream input, uint length)
+ {
+ if (!IsDelta)
+ {
+ Console.WriteLine("Only LZX DELTA streams support reference data");
+ return Error.MSPACK_ERR_ARGS;
+ }
+
+ if (Offset != 0)
+ {
+ Console.WriteLine("Too late to set reference data after decoding starts");
+ return Error.MSPACK_ERR_ARGS;
+ }
+
+ if (length > WindowSize)
+ {
+ Console.WriteLine($"Reference length ({length}) is longer than the window");
+ return Error.MSPACK_ERR_ARGS;
+ }
+
+ if (length > 0 && (system == null || input == null))
+ {
+ Console.WriteLine("Length > 0 but no system or input");
+ return Error.MSPACK_ERR_ARGS;
+ }
+
+ ReferenceDataSize = length;
+ if (length > 0)
+ {
+ // Copy reference data
+ int pos = (int)(WindowSize - length);
+ int bytes = system.Read(input, Window, pos, (int)length);
+
+ // Length can't be more than 2^25, so no signedness problem
+ if (bytes < (int)length)
+ return Error.MSPACK_ERR_READ;
+ }
+
+ ReferenceDataSize = length;
+ return Error.MSPACK_ERR_OK;
+ }
+
+ // See description of outputLength in Init()
+ public void SetOutputLength(long outputLength)
+ {
+ if (outputLength > 0)
+ Length = outputLength;
+ }
+
+ ///
+ /// Decompresses entire or partial LZX streams.
+ ///
+ /// The number of bytes of data that should be decompressed is given as the
+ /// out_bytes parameter. If more bytes are decoded than are needed, they
+ /// will be kept over for a later invocation.
+ ///
+ /// The output bytes will be passed to the system.write() function given in
+ /// lzxd_init(), using the output file handle given in lzxd_init(). More than
+ /// one call may be made to system.write().
+ /// Input bytes will be read in as necessary using the system.read()
+ /// function given in lzxd_init(), using the input file handle given in
+ /// lzxd_init(). This will continue until system.read() returns 0 bytes,
+ /// or an error. Errors will be passed out of the function as
+ /// MSPACK_ERR_READ errors. Input streams should convey an "end of input
+ /// stream" by refusing to supply all the bytes that LZX asks for when they
+ /// reach the end of the stream, rather than return an error code.
+ ///
+ /// If any error code other than MSPACK_ERR_OK is returned, the stream
+ /// should be considered unusable and lzxd_decompress() should not be
+ /// called again on this stream.
+ ///
+ /// LZX decompression state, as allocated by lzxd_init().
+ /// the number of bytes of data to decompress.
+ /// an error code, or MSPACK_ERR_OK if successful
+ public static Error Decompress(object o, long out_bytes)
+ {
+ LZX lzx = o as LZX;
+ if (lzx == null)
+ return Error.MSPACK_ERR_ARGS;
+
+ int warned = 0;
+ byte[] buf = new byte[12];
+
+ // Easy answers
+ if (lzx == null || (out_bytes < 0))
+ return Error.MSPACK_ERR_ARGS;
+
+ if (lzx.Error != Error.MSPACK_ERR_OK)
+ return lzx.Error;
+
+ // Flush out any stored-up bytes before we begin
+ int leftover_bytes = lzx.OutputEnd - lzx.OutputPointer;
+ if (leftover_bytes > out_bytes)
+ leftover_bytes = (int)out_bytes;
+
+ if (leftover_bytes != 0)
+ {
+ 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 += leftover_bytes;
+ lzx.Offset += leftover_bytes;
+ out_bytes -= leftover_bytes;
+ }
+
+ if (out_bytes == 0)
+ return Error.MSPACK_ERR_OK;
+
+ uint end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
+
+ while (lzx.Frame < end_frame)
+ {
+ // Have we reached the reset interval? (if there is one?)
+ if (lzx.ResetInterval != 0 && ((lzx.Frame % lzx.ResetInterval) == 0))
+ {
+ if (lzx.BlockRemaining != 0)
+ {
+ // This is a file format error, we can make a best effort to extract what we can
+ Console.WriteLine($"{lzx.BlockRemaining} bytes remaining at reset interval");
+ if (warned == 0)
+ {
+ lzx.System.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
+ warned++;
+ }
+ }
+
+ // Re-read the intel header and reset the huffman lengths
+ lzx.ResetState();
+ }
+
+ // LZX DELTA format has chunk_size, not present in LZX format
+ if (lzx.IsDelta)
+ {
+ lzx.ENSURE_BITS(16);
+ lzx.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.
+ 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
+ int bytes_todo = (int)(lzx.FramePosition + frame_size - lzx.WindowPosition);
+ while (bytes_todo > 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();
+ if (lzx.Error != Error.MSPACK_ERR_OK)
+ return lzx.Error;
+
+ lzx.InputPointer++;
+ }
+
+ lzx.ReadBlockHeader(buf);
+ if (lzx.Error != Error.MSPACK_ERR_OK)
+ return lzx.Error;
+
+ // Decode more of the block:
+ int this_run = Math.Min(lzx.BlockRemaining, bytes_todo);
+
+ // Assume we decode exactly this_run bytes, for now
+ bytes_todo -= 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:
+ lzx.DecompressBlock(ref this_run);
+ 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;
+
+ 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)
+ int rundest = lzx.WindowPosition;
+ lzx.WindowPosition += this_run;
+ while (this_run > 0)
+ {
+ int i = lzx.InputEnd - lzx.InputPointer;
+ if (i == 0)
+ {
+ lzx.READ_IF_NEEDED();
+ if (lzx.Error != Error.MSPACK_ERR_OK)
+ return lzx.Error;
+ }
+ else
+ {
+ if (i > this_run)
+ i = this_run;
+
+ Array.Copy(lzx.InputBuffer, lzx.InputPointer, lzx.Window, rundest, i);
+
+ rundest += i;
+ lzx.InputPointer += i;
+ this_run -= i;
+ }
+ }
+
+ // Because we can't assume otherwise
+ lzx.IntelStarted = true;
+
+ break;
+
+ default:
+ return lzx.Error = Error.MSPACK_ERR_DECRUNCH; // Might as well
+ }
+
+ // Did the final match overrun our desired this_run length?
+ if (this_run < 0)
+ {
+ if ((uint)(-this_run) > lzx.BlockRemaining)
+ {
+ Console.WriteLine($"Overrun went past end of block by {-this_run} ({lzx.BlockRemaining} remaining)");
+ return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ lzx.BlockRemaining -= -this_run;
+ }
+ }
+
+ // Streams don't extend over frame boundaries
+ if ((lzx.WindowPosition - lzx.FramePosition) != frame_size)
+ {
+ Console.WriteLine($"Decode beyond output frame limits! {lzx.WindowPosition - lzx.FramePosition} != {frame_size}");
+ return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ // Re-align input bitstream
+ if (lzx.BitsLeft > 0)
+ lzx.ENSURE_BITS(16);
+ if ((lzx.BitsLeft & 15) != 0)
+ lzx.REMOVE_BITS_MSB(lzx.BitsLeft & 15);
+
+ // Check that we've used all of the previous frame first
+ if (lzx.OutputPointer != lzx.OutputEnd)
+ {
+ Console.WriteLine($"{lzx.OutputEnd - lzx.OutputPointer} avail bytes, new {frame_size} frame");
+ return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ // Does this intel block _really_ need decoding?
+ if (lzx.IntelStarted && lzx.IntelFileSize != 0 && (lzx.Frame < 32768) && (frame_size > 10))
+ {
+ lzx.UndoE8Preprocessing(frame_size);
+ }
+ else
+ {
+ lzx.OutputIsE8 = false;
+ lzx.OutputPointer = (int)lzx.FramePosition;
+ }
+
+ 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;
+ lzx.Frame++;
+
+ // Wrap window / frame position pointers
+ if (lzx.WindowPosition == lzx.WindowSize)
+ lzx.WindowPosition = 0;
+ if (lzx.FramePosition == lzx.WindowSize)
+ lzx.FramePosition = 0;
+
+ }
+
+ if (out_bytes != 0)
+ {
+ Console.WriteLine("Bytes left to output");
+ return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ 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)
+ {
+ if (ReadLens(lengths, first, last) != Error.MSPACK_ERR_OK)
+ return Error;
+
+ return Error = Error.MSPACK_ERR_OK;
+ }
+
+ private Error DecompressBlock(ref int this_run)
+ {
+ while (this_run > 0)
+ {
+ int main_element = (int)READ_HUFFSYM_MSB(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
+ if (main_element < LZX_NUM_CHARS)
+ {
+ // Literal: 0 to LZX_NUM_CHARS-1
+ Window[WindowPosition++] = (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);
+ 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);
+ 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);
+ match_offset += (uint)(verbatim_bits << 3);
+
+ int aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
+ 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);
+ match_offset += (uint)aligned_bits;
+ }
+
+ // 1-2: verbatim bits only
+ else if (extra > 0)
+ {
+ int verbatim_bits = (int)READ_BITS_MSB(extra);
+ 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);
+
+ // '0' . 8 extra length bits
+ if (PEEK_BITS_MSB(1) == 0)
+ {
+ REMOVE_BITS_MSB(1);
+ extra_len = (int)READ_BITS_MSB(8);
+ }
+
+ // '10' . 10 extra length bits + 0x100
+ else if (PEEK_BITS_MSB(2) == 2)
+ {
+ REMOVE_BITS_MSB(2);
+ extra_len = (int)READ_BITS_MSB(10);
+ extra_len += 0x100;
+ }
+
+ // '110' . 12 extra length bits + 0x500
+ else if (PEEK_BITS_MSB(3) == 6)
+ {
+ REMOVE_BITS_MSB(3);
+ extra_len = (int)READ_BITS_MSB(12);
+ extra_len += 0x500;
+ }
+
+ // '111' . 15 extra length bits
+ else
+ {
+ REMOVE_BITS_MSB(3);
+ extra_len = (int)READ_BITS_MSB(15);
+ }
+
+ match_length += extra_len;
+ }
+
+ if ((WindowPosition + match_length) > WindowSize)
+ {
+ Console.WriteLine("Match ran over window wrap");
+ return Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ // Copy match
+ int rundest = WindowPosition;
+ int i = match_length;
+
+ // Does match offset wrap the window?
+ if (match_offset > WindowPosition)
+ {
+ if (match_offset > Offset && (match_offset - WindowPosition) > 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 - WindowPosition);
+ 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;
+ WindowPosition += match_length;
+ }
+ }
+
+ return Error = Error.MSPACK_ERR_OK;
+ }
+
+ private Error ReadBlockHeader(byte[] buffer)
+ {
+ ENSURE_BITS(4);
+
+ // Read block type (3 bits) and block length (24 bits)
+ byte block_type = (byte)READ_BITS_MSB(3);
+ BlockType = (LZXBlockType)block_type;
+
+ // Read the block size
+ int block_size;
+ if (READ_BITS_MSB(1) == 1)
+ {
+ block_size = LZX_FRAME_SIZE;
+ }
+ else
+ {
+ int tmp;
+ block_size = 0;
+
+ tmp = (int)READ_BITS_MSB(8);
+ block_size |= tmp;
+ tmp = (int)READ_BITS_MSB(8);
+ block_size <<= 8;
+ block_size |= tmp;
+
+ if (WindowSize >= 65536)
+ {
+ tmp = (int)READ_BITS_MSB(8);
+ 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);
+ }
+
+ 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);
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
+ 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);
+ 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);
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ READ_LENGTHS(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
+ 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);
+ 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 (BitsLeft == 0)
+ ENSURE_BITS(16);
+
+ BitsLeft = 0; 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();
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ buffer[rundest++] = InputBuffer[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;
+ }
+
+ private Error ReadLens(byte[] lens, uint first, uint last)
+ {
+ // 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);
+ 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);
+ switch (tree_code)
+ {
+ // Code = 17, run of ([read 4 bits]+4) zeros
+ case 17:
+ num_zeroes = (uint)READ_BITS_MSB(4);
+ 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);
+ 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);
+ num_same += 4;
+
+ tree_code = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
+ 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;
+ }
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ private 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;
+ }
+ }
+
+ private 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;
+ }
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.ReadBits.cs b/BurnOutSharp/External/libmspack/Compression/LZX.ReadBits.cs
new file mode 100644
index 00000000..370a3297
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZX.ReadBits.cs
@@ -0,0 +1,34 @@
+/* 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 partial class LZX : CompressionStream
+ {
+ ///
+ public override void READ_BYTES()
+ {
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ byte b0 = InputBuffer[InputPointer++];
+
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ byte b1 = InputBuffer[InputPointer++];
+ INJECT_BITS_MSB((b1 << 8) | b0, 16);
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.ReadHuff.cs b/BurnOutSharp/External/libmspack/Compression/LZX.ReadHuff.cs
new file mode 100644
index 00000000..12a42af9
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/LZX.ReadHuff.cs
@@ -0,0 +1,20 @@
+/* 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 partial class LZX : CompressionStream
+ {
+ ///
+ public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.cs b/BurnOutSharp/External/libmspack/Compression/LZX.cs
index 680311a0..acc76f04 100644
--- a/BurnOutSharp/External/libmspack/Compression/LZX.cs
+++ b/BurnOutSharp/External/libmspack/Compression/LZX.cs
@@ -4,548 +4,134 @@
* The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation.
*
- * libmspack is free software; you can redistribute it and/or modify it under
+ * 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
*/
-/* Microsoft's LZX document (in cab-sdk.exe) and their implementation
- * of the com.ms.util.cab Java package do not concur.
- *
- * In the LZX document, there is a table showing the correlation between
- * window size and the number of position slots. It states that the 1MB
- * window = 40 slots and the 2MB window = 42 slots. In the implementation,
- * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
- * first slot whose position base is equal to or more than the required
- * window size'. This would explain why other tables in the document refer
- * to 50 slots rather than 42.
- *
- * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
- * is not defined in the specification.
- *
- * The LZX document does not state the uncompressed block has an
- * uncompressed length field. Where does this length field come from, so
- * we can know how large the block is? The implementation has it as the 24
- * bits following after the 3 blocktype bits, before the alignment
- * padding.
- *
- * The LZX document states that aligned offset blocks have their aligned
- * offset huffman tree AFTER the main and length trees. The implementation
- * suggests that the aligned offset tree is BEFORE the main and length
- * trees.
- *
- * The LZX document decoding algorithm states that, in an aligned offset
- * block, if an extra_bits value is 1, 2 or 3, then that number of bits
- * should be read and the result added to the match offset. This is
- * correct for 1 and 2, but not 3, where just a huffman symbol (using the
- * aligned tree) should be read.
- *
- * Regarding the E8 preprocessing, the LZX document states 'No translation
- * may be performed on the last 6 bytes of the input block'. This is
- * correct. However, the pseudocode provided checks for the *E8 leader*
- * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
- * from the end, this would cause the next four bytes to be modified, at
- * least one of which would be in the last 6 bytes, which is not allowed
- * according to the spec.
- *
- * The specification states that the huffman trees must always contain at
- * least one element. However, many CAB files contain blocks where the
- * length tree is completely empty (because there are no matches), and
- * this is expected to succeed.
- *
- * The errors in LZX documentation appear have been corrected in the
- * new documentation for the LZX DELTA format.
- *
- * http://msdn.microsoft.com/en-us/library/cc483133.aspx
- *
- * However, this is a different format, an extension of regular LZX.
- * I have noticed the following differences, there may be more:
- *
- * The maximum window size has increased from 2MB to 32MB. This also
- * increases the maximum number of position slots, etc.
- *
- * If the match length is 257 (the maximum possible), this signals
- * a further length decoding step, that allows for matches up to
- * 33024 bytes long.
- *
- * The format now allows for "reference data", supplied by the caller.
- * If match offsets go further back than the number of bytes
- * decompressed so far, that is them accessing the reference data.
- */
-
-using System;
-using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
- public class LZX
+ public partial class LZX : CompressionStream
{
///
- /// Allocates and initialises LZX decompression state for decoding an LZX
- /// stream.
- ///
- /// This routine uses system.alloc() to allocate memory. If memory
- /// allocation fails, or the parameters to this function are invalid,
- /// null is returned.
+ /// Number of bytes actually output
///
- ///
- /// an mspack_system structure used to read from
- /// the input stream and write to the output
- /// stream, also to allocate and free memory.
- ///
- /// an input stream with the LZX data.
- /// an output stream to write the decoded data to.
- ///
- /// the size of the decoding window, which must be
- /// between 15 and 21 inclusive for regular LZX
- /// data, or between 17 and 25 inclusive for
- /// LZX DELTA data.
- ///
- /// the interval at which the LZX bitstream is
- /// reset, in multiples of LZX frames (32678
- /// bytes), e.g. a value of 2 indicates the input
- /// stream resets after every 65536 output bytes.
- /// A value of 0 indicates that the bitstream never
- /// resets, such as in CAB LZX streams.
- ///
- ///
- /// the number of bytes to use as an input
- /// bitstream buffer.
- ///
- ///
- /// the length in bytes of the entirely
- /// decompressed output stream, if known in
- /// advance. It is used to correctly perform the
- /// Intel E8 transformation, which must stop 6
- /// bytes before the very end of the
- /// decompressed stream. It is not otherwise used
- /// or adhered to. If the full decompressed
- /// length is known in advance, set it here.
- /// If it is NOT known, use the value 0, and call
- /// lzxd_set_outputLength() once it is
- /// known. If never set, 4 of the final 6 bytes
- /// of the output stream may be incorrect.
- ///
- ///
- /// should be zero for all regular LZX data,
- /// non-zero for LZX DELTA encoded data.
- ///
- ///
- /// a pointer to an initialised LZXDStream structure, or null if
- /// there was not enough memory or parameters to the function were wrong.
- ///
- 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)
- {
- if (system == null)
- return null;
-
- // LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB),
- // regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB)
- if (is_delta)
- {
- if (window_bits < 17 || window_bits > 25)
- return null;
- }
- else
- {
- if (window_bits < 15 || window_bits > 21)
- return null;
- }
-
- if (reset_interval < 0 || output_length < 0)
- {
- Console.WriteLine("Reset interval or output length < 0");
- return null;
- }
-
- // Round up input buffer size to multiple of two
- input_buffer_size = (input_buffer_size + 1) & -2;
- if (input_buffer_size < 2)
- return null;
-
- // Allocate decompression state
- LZXDStream lzx = new LZXDStream()
- {
- // Allocate decompression window and input buffer
- Window = new byte[1 << window_bits],
- InputBuffer = new byte[input_buffer_size],
-
- System = system,
- InputFileHandle = input,
- OutputFileHandle = output,
- Offset = 0,
- Length = output_length,
-
- InputBufferSize = (uint)input_buffer_size,
- WindowSize = (uint)(1 << window_bits),
- ReferenceDataSize = 0,
- WindowPosition = 0,
- FramePosition = 0,
- Frame = 0,
- ResetInterval = (uint)reset_interval,
- IntelFileSize = 0,
- IntelStarted = false,
- Error = Error.MSPACK_ERR_OK,
- NumOffsets = LZXPositionSlots[window_bits - 15] << 3,
- IsDelta = is_delta,
-
- OutputPointer = 0,
- OutputEnd = 0,
- OutputIsE8 = true,
- };
-
- lzx.ResetState();
- lzx.INIT_BITS();
-
- return lzx;
- }
+ public long Offset { get; set; }
///
- /// Reads LZX DELTA reference data into the window and allows
- /// lzxd_decompress() to reference it.
- ///
- /// Call this before the first call to lzxd_decompress().
+ /// Overall decompressed length of stream
///
- /// the LZX stream to apply this reference data to
- ///
- /// an mspack_system implementation to use with the
- /// input param. Only read() will be called.
- ///
- /// an input file handle to read reference data using system.read().
- ///
- /// the length of the reference data. Cannot be longer
- /// than the LZX window size.
- ///
- /// An error code, or MSPACK_ERR_OK if successful
- public static Error SetReferenceData(LZXDStream lzx, SystemImpl system, FileStream input, uint length)
- {
- if (lzx == null)
- return Error.MSPACK_ERR_ARGS;
-
- if (!lzx.IsDelta)
- {
- Console.WriteLine("Only LZX DELTA streams support reference data");
- return Error.MSPACK_ERR_ARGS;
- }
-
- if (lzx.Offset != 0)
- {
- Console.WriteLine("Too late to set reference data after decoding starts");
- return Error.MSPACK_ERR_ARGS;
- }
-
- if (length > lzx.WindowSize)
- {
- Console.WriteLine($"Reference length ({length}) is longer than the window");
- return Error.MSPACK_ERR_ARGS;
- }
-
- if (length > 0 && (system == null || input == null))
- {
- Console.WriteLine("Length > 0 but no system or input");
- return Error.MSPACK_ERR_ARGS;
- }
-
- lzx.ReferenceDataSize = length;
- if (length > 0)
- {
- // Copy reference data
- int pos = (int)(lzx.WindowSize - length);
- int bytes = system.Read(input, lzx.Window, pos, (int)length);
-
- // Length can't be more than 2^25, so no signedness problem
- if (bytes < (int)length)
- return Error.MSPACK_ERR_READ;
- }
-
- lzx.ReferenceDataSize = length;
- return Error.MSPACK_ERR_OK;
- }
-
- // See description of outputLength in Init()
- public static void SetOutputLength(LZXDStream lzx, long outputLength)
- {
- if (lzx != null && outputLength > 0)
- lzx.Length = outputLength;
- }
+ public long Length { get; set; }
///
- /// Decompresses entire or partial LZX streams.
- ///
- /// The number of bytes of data that should be decompressed is given as the
- /// out_bytes parameter. If more bytes are decoded than are needed, they
- /// will be kept over for a later invocation.
- ///
- /// The output bytes will be passed to the system.write() function given in
- /// lzxd_init(), using the output file handle given in lzxd_init(). More than
- /// one call may be made to system.write().
- /// Input bytes will be read in as necessary using the system.read()
- /// function given in lzxd_init(), using the input file handle given in
- /// lzxd_init(). This will continue until system.read() returns 0 bytes,
- /// or an error. Errors will be passed out of the function as
- /// MSPACK_ERR_READ errors. Input streams should convey an "end of input
- /// stream" by refusing to supply all the bytes that LZX asks for when they
- /// reach the end of the stream, rather than return an error code.
- ///
- /// If any error code other than MSPACK_ERR_OK is returned, the stream
- /// should be considered unusable and lzxd_decompress() should not be
- /// called again on this stream.
+ /// Decoding window
///
- /// LZX decompression state, as allocated by lzxd_init().
- /// the number of bytes of data to decompress.
- /// an error code, or MSPACK_ERR_OK if successful
- public static Error Decompress(object o, long out_bytes)
- {
- LZXDStream lzx = o as LZXDStream;
- if (lzx == null)
- return Error.MSPACK_ERR_ARGS;
+ public byte[] Window { get; set; }
- int warned = 0;
- byte[] buf = new byte[12];
+ ///
+ /// Window size
+ ///
+ public uint WindowSize { get; set; }
- // Easy answers
- if (lzx == null || (out_bytes < 0))
- return Error.MSPACK_ERR_ARGS;
+ ///
+ /// LZX DELTA reference data size
+ ///
+ public uint ReferenceDataSize { get; set; }
- if (lzx.Error != Error.MSPACK_ERR_OK)
- return lzx.Error;
+ ///
+ /// Number of match_offset entries in table
+ ///
+ public uint NumOffsets { get; set; }
- // Flush out any stored-up bytes before we begin
- int leftover_bytes = lzx.OutputEnd - lzx.OutputPointer;
- if (leftover_bytes > out_bytes)
- leftover_bytes = (int)out_bytes;
+ ///
+ /// Decompression offset within window
+ ///
+ public int WindowPosition { get; set; }
- if (leftover_bytes != 0)
- {
- try { lzx.System.Write(lzx.OutputFileHandle, lzx.OutputIsE8 ? lzx.E8Buffer : lzx.Window, lzx.OutputPointer, leftover_bytes); }
- catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
+ ///
+ /// Current frame offset within in window
+ ///
+ public uint FramePosition { get; set; }
- lzx.OutputPointer += leftover_bytes;
- lzx.Offset += leftover_bytes;
- out_bytes -= leftover_bytes;
- }
+ ///
+ /// The number of 32kb frames processed
+ ///
+ public uint Frame { get; set; }
- if (out_bytes == 0)
- return Error.MSPACK_ERR_OK;
+ ///
+ /// Which frame do we reset the compressor?
+ ///
+ public uint ResetInterval { get; set; }
- // Restore local state
- BufferState state = lzx.RESTORE_BITS();
- byte[] window = lzx.Window;
- int window_posn = lzx.WindowPosition;
- uint[] R = lzx.R;
+ ///
+ /// For the LRU offset system
+ ///
+ public uint[] R { get; set; } = new uint[3];
- uint end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
+ ///
+ /// Uncompressed length of this LZX block
+ ///
+ public int BlockLength { get; set; }
- while (lzx.Frame < end_frame)
- {
- // Have we reached the reset interval? (if there is one?)
- if (lzx.ResetInterval != 0 && ((lzx.Frame % lzx.ResetInterval) == 0))
- {
- if (lzx.BlockRemaining != 0)
- {
- // This is a file format error, we can make a best effort to extract what we can
- Console.WriteLine($"{lzx.BlockRemaining} bytes remaining at reset interval");
- if (warned == 0)
- {
- lzx.System.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
- warned++;
- }
- }
+ ///
+ /// Uncompressed bytes still left to decode
+ ///
+ public int BlockRemaining { get; set; }
- // Re-read the intel header and reset the huffman lengths
- lzx.ResetState();
- R = lzx.R;
- }
+ ///
+ /// Magic header value used for transform
+ ///
+ public int IntelFileSize { get; set; }
- // LZX DELTA format has chunk_size, not present in LZX format
- if (lzx.IsDelta)
- {
- lzx.ENSURE_BITS(16, state);
- state.REMOVE_BITS_MSB(16);
- }
+ ///
+ /// Has intel E8 decoding started?
+ ///
+ public bool IntelStarted { get; set; }
- //// 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);
+ ///
+ /// Type of the current block
+ ///
+ public LZXBlockType BlockType { get; set; }
- // if (i != 0)
- // {
- // i = (int)lzx.READ_BITS_MSB(16, state);
- // j = (int)lzx.READ_BITS_MSB(16, state);
- // }
+ ///
+ /// Have we started decoding at all yet?
+ ///
+ public byte HeaderRead { get; set; }
- // lzx.IntelFileSize = (i << 16) | j;
- // lzx.HeaderRead = 1;
- //}
+ ///
+ /// Does stream follow LZX DELTA spec?
+ ///
+ public bool IsDelta { get; set; }
- // 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.
- uint frame_size = LZX_FRAME_SIZE;
- if (lzx.Length != 0 && (lzx.Length - lzx.Offset) < frame_size)
- frame_size = (uint)(lzx.Length - lzx.Offset);
+ #region Huffman code lengths
- // Decode until one more frame is available
- int bytes_todo = (int)(lzx.FramePosition + frame_size - window_posn);
- while (bytes_todo > 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(state);
- if (lzx.Error != Error.MSPACK_ERR_OK)
- return lzx.Error;
+ 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];
- state.InputPointer++;
- }
+ #endregion
- lzx.ReadBlockHeader(buf, ref R, state);
- if (lzx.Error != Error.MSPACK_ERR_OK)
- return lzx.Error;
+ #region Huffman decoding tables
- // Decode more of the block:
- int this_run = Math.Min(lzx.BlockRemaining, bytes_todo);
+ 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)];
- // Assume we decode exactly this_run bytes, for now
- bytes_todo -= this_run;
- lzx.BlockRemaining -= this_run;
+ #endregion
+
+ public byte LENGTH_empty { get; set; }
- // Decode at least this_run bytes
- switch (lzx.BlockType)
- {
- case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
- case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
- lzx.DecompressBlock(window, ref window_posn, ref this_run, ref R, state);
- if (lzx.Error != Error.MSPACK_ERR_OK)
- return lzx.Error;
+ // This is used purely for doing the intel E8 transform
+ public byte[] E8Buffer { get; set; } = new byte[LZX_FRAME_SIZE];
- // 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)
- int rundest = window_posn;
- window_posn += this_run;
- while (this_run > 0)
- {
- int i = state.InputEnd - state.InputPointer;
- if (i == 0)
- {
- lzx.READ_IF_NEEDED(state);
- if (lzx.Error != Error.MSPACK_ERR_OK)
- return lzx.Error;
- }
- else
- {
- if (i > this_run)
- i = this_run;
-
- Array.Copy(lzx.InputBuffer, state.InputPointer, window, rundest, i);
-
- rundest += i;
- state.InputPointer += i;
- this_run -= i;
- }
- }
-
- // Because we can't assume otherwise
- lzx.IntelStarted = true;
-
- break;
-
- default:
- return lzx.Error = Error.MSPACK_ERR_DECRUNCH; // Might as well
- }
-
- // Did the final match overrun our desired this_run length?
- if (this_run < 0)
- {
- if ((uint)(-this_run) > lzx.BlockRemaining)
- {
- Console.WriteLine($"Overrun went past end of block by {-this_run} ({lzx.BlockRemaining} remaining)");
- return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- lzx.BlockRemaining -= -this_run;
- }
- }
-
- // Streams don't extend over frame boundaries
- if ((window_posn - lzx.FramePosition) != frame_size)
- {
- Console.WriteLine($"Decode beyond output frame limits! {window_posn - lzx.FramePosition} != {frame_size}");
- return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- // Re-align input bitstream
- 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)
- {
- Console.WriteLine($"{lzx.OutputEnd - lzx.OutputPointer} avail bytes, new {frame_size} frame");
- return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- // Does this intel block _really_ need decoding?
- if (lzx.IntelStarted && lzx.IntelFileSize != 0 && (lzx.Frame < 32768) && (frame_size > 10))
- {
- lzx.UndoE8Preprocessing(frame_size);
- }
- else
- {
- lzx.OutputIsE8 = false;
- lzx.OutputPointer = (int)lzx.FramePosition;
- }
-
- 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;
- lzx.Frame++;
-
- // Wrap window / frame position pointers
- if (window_posn == lzx.WindowSize)
- window_posn = 0;
- if (lzx.FramePosition == lzx.WindowSize)
- lzx.FramePosition = 0;
-
- }
-
- if (out_bytes != 0)
- {
- Console.WriteLine("Bytes left to output");
- return lzx.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- // Store local state
- lzx.STORE_BITS(state);
- lzx.WindowPosition = window_posn;
- lzx.R = R;
-
- return Error.MSPACK_ERR_OK;
- }
+ ///
+ /// Is the output pointer referring to E8?
+ ///
+ public bool OutputIsE8 { get; set; }
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs b/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs
deleted file mode 100644
index 95676cfa..00000000
--- a/BurnOutSharp/External/libmspack/Compression/LZXDStream.cs
+++ /dev/null
@@ -1,695 +0,0 @@
-/* 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
- */
-
-using System;
-using static LibMSPackSharp.Compression.Constants;
-
-namespace LibMSPackSharp.Compression
-{
- public class LZXDStream : CompressionStream
- {
- #region Fields
-
- ///
- /// Number of bytes actually output
- ///
- public long Offset { get; set; }
-
- ///
- /// Overall decompressed length of stream
- ///
- public long Length { get; set; }
-
- ///
- /// Decoding window
- ///
- public byte[] Window { get; set; }
-
- ///
- /// Window size
- ///
- public uint WindowSize { get; set; }
-
- ///
- /// LZX DELTA reference data size
- ///
- public uint ReferenceDataSize { get; set; }
-
- ///
- /// Number of match_offset entries in table
- ///
- public uint NumOffsets { get; set; }
-
- ///
- /// Decompression offset within window
- ///
- public int WindowPosition { get; set; }
-
- ///
- /// Current frame offset within in window
- ///
- public uint FramePosition { get; set; }
-
- ///
- /// The number of 32kb frames processed
- ///
- public uint Frame { get; set; }
-
- ///
- /// Which frame do we reset the compressor?
- ///
- public uint ResetInterval { get; set; }
-
- ///
- /// For the LRU offset system
- ///
- public uint[] R { get; set; } = new uint[3];
-
- ///
- /// Uncompressed length of this LZX block
- ///
- public int BlockLength { get; set; }
-
- ///
- /// Uncompressed bytes still left to decode
- ///
- public int BlockRemaining { get; set; }
-
- ///
- /// Magic header value used for transform
- ///
- public int IntelFileSize { get; set; }
-
- ///
- /// Has intel E8 decoding started?
- ///
- public bool IntelStarted { get; set; }
-
- ///
- /// Type of the current block
- ///
- public LZXBlockType BlockType { get; set; }
-
- ///
- /// Have we started decoding at all yet?
- ///
- public byte HeaderRead { get; set; }
-
- ///
- /// Does stream follow LZX DELTA spec?
- ///
- public bool IsDelta { get; set; }
-
- #region Huffman code lengths
-
- 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_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[] 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
-
- ///
- public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
-
- ///
- public override void READ_BYTES(BufferState state)
- {
- READ_IF_NEEDED(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- byte b0 = InputBuffer[state.InputPointer++];
-
- READ_IF_NEEDED(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- byte b1 = InputBuffer[state.InputPointer++];
- INJECT_BITS_MSB((b1 << 8) | b0, 16, state);
- }
- }
-}
diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.Decompress.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.Decompress.cs
new file mode 100644
index 00000000..b507bc80
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.Decompress.cs
@@ -0,0 +1,617 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The deflate method was created by Phil Katz. MSZIP is equivalent to the
+ * deflate method.
+ *
+ * libmspack is free software; 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
+ */
+
+using System;
+using System.IO;
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ public partial class MSZIP
+ {
+ ///
+ /// Allocates MS-ZIP decompression stream for decoding the given stream.
+ ///
+ /// - uses system.alloc() to allocate memory
+ ///
+ /// - returns null if not enough memory
+ ///
+ /// - input_buffer_size is how many bytes to use as an input bitstream buffer
+ ///
+ /// - if RepairMode is non-zero, errors in decompression will be skipped
+ /// and 'holes' left will be filled with zero bytes. This allows at least
+ /// a partial recovery of erroneous data.
+ ///
+ public static MSZIP Init(SystemImpl system, FileStream input, FileStream output, int input_buffer_size, bool repair_mode)
+ {
+ if (system == null)
+ return null;
+
+ // Round up input buffer size to multiple of two
+ input_buffer_size = (input_buffer_size + 1) & -2;
+ if (input_buffer_size < 2)
+ return null;
+
+ // Allocate decompression state
+ return new MSZIP
+ {
+ // Allocate input buffer
+ InputBuffer = new byte[input_buffer_size],
+
+ // Initialise decompression state
+ System = system,
+ InputFileHandle = input,
+ OutputFileHandle = output,
+ InputBufferSize = (uint)input_buffer_size,
+ EndOfInput = 0,
+ Error = Error.MSPACK_ERR_OK,
+ RepairMode = repair_mode,
+
+ InputPointer = 0,
+ InputEnd = 0,
+ OutputPointer = 0,
+ OutputEnd = 0,
+ BitBuffer = 0,
+ BitsLeft = 0,
+ };
+ }
+
+ ///
+ /// Decompresses, or decompresses more of, an MS-ZIP stream.
+ ///
+ /// - out_bytes of data will be decompressed and the function will return
+ /// with an MSPACK_ERR_OK return code.
+ ///
+ /// - decompressing will stop as soon as out_bytes is reached. if the true
+ /// amount of bytes decoded spills over that amount, they will be kept for
+ /// a later invocation of mszipd_decompress().
+ ///
+ /// - the output bytes will be passed to the system.write() function given in
+ /// mszipd_init(), using the output file handle given in mszipd_init(). More
+ /// than one call may be made to system.write()
+ ///
+ /// - MS-ZIP will read input bytes as necessary using the system.read()
+ /// function given in mszipd_init(), using the input file handle given in
+ /// mszipd_init(). This will continue until system.read() returns 0 bytes,
+ /// or an error.
+ ///
+ public static Error Decompress(object o, long out_bytes)
+ {
+ MSZIP zip = o as MSZIP;
+ if (zip == null)
+ return Error.MSPACK_ERR_ARGS;
+
+ int i, readState;
+ Error error;
+
+ // Easy answers
+ if (zip == null || (out_bytes < 0))
+ return Error.MSPACK_ERR_ARGS;
+ if (zip.Error != Error.MSPACK_ERR_OK)
+ return zip.Error;
+
+ // Flush out any stored-up bytes before we begin
+ i = zip.OutputEnd - zip.OutputPointer;
+ if (i > out_bytes)
+ i = (int)out_bytes;
+
+ if (i != 0)
+ {
+ if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
+ return zip.Error = Error.MSPACK_ERR_WRITE;
+
+ zip.OutputPointer += i;
+ out_bytes -= i;
+ }
+
+ if (out_bytes == 0)
+ return Error.MSPACK_ERR_OK;
+
+ while (out_bytes > 0)
+ {
+ // Skip to next read 'CK' header
+ i = zip.BitsLeft & 7;
+
+ // Align to bytestream
+ zip.REMOVE_BITS_LSB(i);
+
+ readState = 0;
+ do
+ {
+ i = (int)zip.READ_BITS_LSB(8);
+
+ if (i == 'C')
+ readState = 1;
+ else if ((readState == 1) && (i == 'K'))
+ readState = 2;
+ else
+ readState = 0;
+ } while (readState != 2);
+
+ // Inflate a block, repair and realign if necessary
+ zip.WindowPosition = 0;
+ zip.BytesOutput = 0;
+
+ if ((error = zip.Inflate()) != Error.MSPACK_ERR_OK)
+ {
+ Console.WriteLine($"Inflate error {error}");
+ if (zip.RepairMode)
+ {
+ // Recover partially-inflated buffers
+ if (zip.BytesOutput == 0 && zip.WindowPosition > 0)
+ zip.FlushWindow(zip.WindowPosition);
+
+ zip.System.Message(null, $"MSZIP error, {MSZIP_FRAME_SIZE - zip.BytesOutput} bytes of data lost.");
+ for (i = zip.BytesOutput; i < MSZIP_FRAME_SIZE; i++)
+ {
+ zip.Window[i] = 0x00;
+ }
+
+ zip.BytesOutput = MSZIP_FRAME_SIZE;
+ }
+ else
+ {
+ return zip.Error = error;
+ }
+ }
+
+ zip.OutputPointer = 0;
+ zip.OutputEnd = zip.BytesOutput;
+
+ // Write a frame
+ i = (out_bytes < zip.BytesOutput) ? (int)out_bytes : zip.BytesOutput;
+ if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
+ return zip.Error = Error.MSPACK_ERR_WRITE;
+
+ // mspack errors (i.e. read errors) are fatal and can't be recovered
+ if ((error > 0) && zip.RepairMode)
+ return error;
+
+ zip.OutputPointer += i;
+ out_bytes -= i;
+ }
+
+ if (out_bytes != 0)
+ {
+ Console.WriteLine($"Bytes left to output: {out_bytes}");
+ return zip.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ ///
+ /// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
+ /// like mszipd_decompress(), but doesn't take an out_bytes parameter
+ ///
+ public Error DecompressKWAJ()
+ {
+ int i, block_len;
+ Error error;
+
+ // Unpack blocks until block_len == 0
+ for (; ; )
+ {
+ // Align to bytestream, read block_len
+ i = BitsLeft & 7;
+ REMOVE_BITS_LSB(i);
+
+ block_len = (int)READ_BITS_LSB(8);
+ i = (int)READ_BITS_LSB(8);
+
+ block_len |= i << 8;
+ if (block_len == 0)
+ break;
+
+ // Read "CK" header
+ i = (int)READ_BITS_LSB(8);
+ if (i != 'C')
+ return Error.MSPACK_ERR_DATAFORMAT;
+
+ i = (int)READ_BITS_LSB(8);
+ if (i != 'K')
+ return Error.MSPACK_ERR_DATAFORMAT;
+
+ // Inflate block
+ WindowPosition = 0;
+ BytesOutput = 0;
+
+ if ((error = Inflate()) != Error.MSPACK_ERR_OK)
+ {
+ Console.WriteLine($"Inflate error {error}");
+ return Error = (error > 0) ? error : Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ // Write inflated block
+ try { System.Write(OutputFileHandle, Window, 0, BytesOutput); }
+ catch { return Error = Error.MSPACK_ERR_WRITE; }
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ private Error ReadLens()
+ {
+ // bitlen Huffman codes -- immediate lookup, 7 bit max code length
+ ushort[] bl_table = new ushort[(1 << 7)];
+ byte[] bl_len = new byte[19];
+
+ byte[] lens = new byte[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS];
+ uint lit_codes, dist_codes, code, last_code = 0, bitlen_codes, i, run;
+
+ // Read the number of codes
+ lit_codes = (uint)READ_BITS_LSB(5);
+ lit_codes += 257;
+
+ dist_codes = (uint)READ_BITS_LSB(5);
+ dist_codes += 1;
+
+ bitlen_codes = (uint)READ_BITS_LSB(5);
+ bitlen_codes += 4;
+
+ if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS)
+ return Error.INF_ERR_SYMLENS;
+ if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS)
+ return Error.INF_ERR_SYMLENS;
+
+ // Read in the bit lengths in their unusual order
+ for (i = 0; i < bitlen_codes; i++)
+ {
+ bl_len[BitLengthOrder[i]] = (byte)READ_BITS_LSB(3);
+ }
+
+ while (i < 19)
+ {
+ bl_len[BitLengthOrder[i++]] = 0;
+ }
+
+ // Create decoding table with an immediate lookup
+ if (!CompressionStream.MakeDecodeTableLSB(19, 7, bl_len, bl_table))
+ return Error.INF_ERR_BITLENTBL;
+
+ // Read literal / distance code lengths
+ for (i = 0; i < (lit_codes + dist_codes); i++)
+ {
+ // Single-level huffman lookup
+
+ ENSURE_BITS(7);
+ code = bl_table[PEEK_BITS_LSB(7)];
+ REMOVE_BITS_LSB(bl_len[code]);
+
+ if (code < 16)
+ {
+ lens[i] = (byte)(last_code = code);
+ }
+ else
+ {
+ switch (code)
+ {
+ case 16:
+ run = (uint)READ_BITS_LSB(2);
+ run += 3;
+ code = last_code;
+ break;
+
+ case 17:
+ run = (uint)READ_BITS_LSB(3);
+ run += 3;
+ code = 0;
+ break;
+
+ case 18:
+ run = (uint)READ_BITS_LSB(7);
+ run += 11;
+ code = 0;
+ break;
+
+ default:
+ Console.WriteLine($"Bad code!: {code}");
+ return Error.INF_ERR_BADBITLEN;
+ }
+
+ if ((i + run) > (lit_codes + dist_codes))
+ return Error.INF_ERR_BITOVERRUN;
+
+ while (run-- != 0)
+ {
+ lens[i++] = (byte)code;
+ }
+
+ i--;
+ }
+ }
+
+ // Copy LITERAL code lengths and clear any remaining
+ i = lit_codes;
+ Array.Copy(lens, 0, LITERAL_len, 0, i);
+ while (i < MSZIP_LITERAL_MAXSYMBOLS)
+ {
+ LITERAL_len[i++] = 0;
+ }
+
+ i = dist_codes;
+ Array.Copy(lens, lit_codes, DISTANCE_len, 0, i);
+ while (i < MSZIP_DISTANCE_MAXSYMBOLS)
+ {
+ DISTANCE_len[i++] = 0;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// A clean implementation of RFC 1951 / inflate
+ ///
+ private Error Inflate()
+ {
+ uint last_block, block_type, distance, length, this_run, i;
+ Error err;
+ ushort sym;
+
+ do
+ {
+ // Read in last block bit
+ last_block = (uint)READ_BITS_LSB(1);
+
+ // Read in block type
+ block_type = (uint)READ_BITS_LSB(2);
+
+ if (block_type == 0)
+ {
+ // Uncompressed block
+ byte[] lens_buf = new byte[4];
+
+ // Go to byte boundary
+ i = (uint)(BitsLeft & 7);
+ REMOVE_BITS_LSB((int)i);
+
+ // Read 4 bytes of data, emptying the bit-buffer if necessary
+ for (i = 0; (BitsLeft >= 8); i++)
+ {
+ if (i == 4)
+ return Error.INF_ERR_BITBUF;
+
+ lens_buf[i] = (byte)PEEK_BITS_LSB(8);
+ REMOVE_BITS_LSB(8);
+ }
+
+ if (BitsLeft != 0)
+ return Error.INF_ERR_BITBUF;
+
+ while (i < 4)
+ {
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ lens_buf[i++] = InputBuffer[InputPointer++];
+ }
+
+ // Get the length and its complement
+ length = (ushort)(lens_buf[0] | (lens_buf[1] << 8));
+ i = (ushort)(lens_buf[2] | (lens_buf[3] << 8));
+
+ ushort compl = (ushort)(~i & 0xFFFF);
+ if (length != compl)
+ return Error.INF_ERR_COMPLEMENT;
+
+ // Read and copy the uncompressed data into the window
+ while (length > 0)
+ {
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return Error;
+
+ this_run = length;
+ if (this_run > (uint)(InputEnd - InputPointer))
+ this_run = (uint)(InputEnd - InputPointer);
+
+ if (this_run > (MSZIP_FRAME_SIZE - WindowPosition))
+ this_run = MSZIP_FRAME_SIZE - WindowPosition;
+
+ Array.Copy(InputBuffer, InputPointer, Window, WindowPosition, this_run);
+
+ WindowPosition += this_run;
+ InputPointer += (int)this_run;
+ length -= this_run;
+
+ err = FLUSH_IF_NEEDED();
+ if (err != Error.MSPACK_ERR_OK)
+ return err;
+ }
+ }
+ else if ((block_type == 1) || (block_type == 2))
+ {
+ // Huffman-compressed LZ77 block
+ uint match_posn, code;
+
+ if (block_type == 1)
+ {
+ // Block with fixed Huffman codes
+ i = 0;
+ while (i < 144)
+ {
+ LITERAL_len[i++] = 8;
+ }
+
+ while (i < 256)
+ {
+ LITERAL_len[i++] = 9;
+ }
+
+ while (i < 280)
+ {
+ LITERAL_len[i++] = 7;
+ }
+
+ while (i < 288)
+ {
+ LITERAL_len[i++] = 8;
+ }
+
+ for (i = 0; i < 32; i++)
+ {
+ DISTANCE_len[i] = 5;
+ }
+ }
+ else
+ {
+ // Block with dynamic Huffman codes
+ if ((err = ReadLens()) != Error.MSPACK_ERR_OK)
+ return err;
+ }
+
+ // Now huffman lengths are read for either kind of block,
+ // create huffman decoding tables
+ if (!CompressionStream.MakeDecodeTableLSB(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, LITERAL_len, LITERAL_table))
+ return Error.INF_ERR_LITERALTBL;
+
+ if (!CompressionStream.MakeDecodeTableLSB(MSZIP_DISTANCE_MAXSYMBOLS, MSZIP_DISTANCE_TABLEBITS, DISTANCE_len, DISTANCE_table))
+ return Error.INF_ERR_DISTANCETBL;
+
+ // Decode forever until end of block code
+ for (; ; )
+ {
+ code = (uint)READ_HUFFSYM_LSB(LITERAL_table, LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS);
+
+ if (code < 256)
+ {
+ Window[WindowPosition++] = (byte)code;
+ err = FLUSH_IF_NEEDED();
+ if (err != Error.MSPACK_ERR_OK)
+ return err;
+ }
+ else if (code == 256)
+ {
+ // END OF BLOCK CODE: loop break point
+ break;
+ }
+ else
+ {
+ code -= 257; // Codes 257-285 are matches
+ if (code >= 29)
+ return Error.INF_ERR_LITCODE; // Codes 286-287 are illegal
+
+ length = (uint)READ_BITS_T_LSB(LiteralExtraBits[code]);
+ length += LiteralLengths[code];
+
+ code = (uint)READ_HUFFSYM_LSB(DISTANCE_table, DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS);
+
+ if (code >= 30)
+ return Error.INF_ERR_DISTCODE;
+
+ distance = (uint)READ_BITS_T_LSB(DistanceExtraBits[code]);
+ distance += DistanceOffsets[code];
+
+ // Match position is window position minus distance. If distance
+ // is more than window position numerically, it must 'wrap
+ // around' the frame size.
+ match_posn = (uint)((distance > WindowPosition) ? MSZIP_FRAME_SIZE : 0) + WindowPosition - distance;
+
+ // Copy match
+ if (length < 12)
+ {
+ // Short match, use slower loop but no loop setup code
+ while (length-- != 0)
+ {
+ Window[WindowPosition++] = Window[match_posn++];
+ match_posn &= MSZIP_FRAME_SIZE - 1;
+ err = FLUSH_IF_NEEDED();
+ if (err != Error.MSPACK_ERR_OK)
+ return err;
+ }
+ }
+ else
+ {
+ // Longer match, use faster loop but with setup expense */
+ int runsrc, rundest;
+ do
+ {
+ this_run = length;
+ if ((match_posn + this_run) > MSZIP_FRAME_SIZE)
+ this_run = MSZIP_FRAME_SIZE - match_posn;
+ if ((WindowPosition + this_run) > MSZIP_FRAME_SIZE)
+ this_run = MSZIP_FRAME_SIZE - WindowPosition;
+
+ rundest = (int)WindowPosition;
+ WindowPosition += this_run;
+ runsrc = (int)match_posn;
+ match_posn += this_run;
+ length -= this_run;
+ while (this_run-- != 0)
+ {
+ Window[rundest++] = Window[runsrc++];
+ }
+
+ if (match_posn == MSZIP_FRAME_SIZE)
+ match_posn = 0;
+
+ err = FLUSH_IF_NEEDED();
+ if (err != Error.MSPACK_ERR_OK)
+ return err;
+ } while (length > 0);
+ }
+
+ }
+
+ }
+ }
+ else
+ {
+ // block_type == 3 -- bad block type
+ return Error.INF_ERR_BLOCKTYPE;
+ }
+ } while (last_block == 0);
+
+ // Flush the remaining data
+ if (WindowPosition != 0)
+ {
+ if (FlushWindow(WindowPosition) != Error.MSPACK_ERR_OK)
+ return Error.INF_ERR_FLUSH;
+ }
+
+ // Return success
+ return Error.MSPACK_ERR_OK;
+ }
+
+ private Error FLUSH_IF_NEEDED()
+ {
+ if (WindowPosition == MSZIP_FRAME_SIZE)
+ {
+ if (FlushWindow(MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
+ return Error.INF_ERR_FLUSH;
+
+ WindowPosition = 0;
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ ///
+ /// inflate() calls this whenever the window should be flushed. As
+ /// MSZIP only expands to the size of the window, the implementation used
+ /// simply keeps track of the amount of data flushed, and if more than 32k
+ /// is flushed, an error is raised.
+ ///
+ private Error FlushWindow(uint data_flushed)
+ {
+ BytesOutput += (int)data_flushed;
+ if (BytesOutput > MSZIP_FRAME_SIZE)
+ {
+ Console.WriteLine($"Overflow: {data_flushed} bytes flushed, total is now {BytesOutput}");
+ return Error.MSPACK_ERR_ARGS;
+ }
+
+ return Error.MSPACK_ERR_OK;
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadBits.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadBits.cs
new file mode 100644
index 00000000..c74fde69
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadBits.cs
@@ -0,0 +1,27 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The deflate method was created by Phil Katz. MSZIP is equivalent to the
+ * deflate method.
+ *
+ * libmspack is free software; 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 partial class MSZIP : CompressionStream
+ {
+ ///
+ public override void READ_BYTES()
+ {
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ INJECT_BITS_LSB(InputBuffer[InputPointer++], 8);
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadHuff.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadHuff.cs
new file mode 100644
index 00000000..dad81278
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.ReadHuff.cs
@@ -0,0 +1,20 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The deflate method was created by Phil Katz. MSZIP is equivalent to the
+ * deflate method.
+ *
+ * libmspack is free software; 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 partial class MSZIP : CompressionStream
+ {
+ ///
+ public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs
index 431b36ae..9486a19a 100644
--- a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs
+++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs
@@ -10,639 +10,38 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
-using System;
-using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
- public class MSZIP
+ public partial class MSZIP : CompressionStream
{
///
- /// Allocates MS-ZIP decompression stream for decoding the given stream.
- ///
- /// - uses system.alloc() to allocate memory
- ///
- /// - returns null if not enough memory
- ///
- /// - input_buffer_size is how many bytes to use as an input bitstream buffer
- ///
- /// - if RepairMode is non-zero, errors in decompression will be skipped
- /// and 'holes' left will be filled with zero bytes. This allows at least
- /// a partial recovery of erroneous data.
+ /// 32kb history window
///
- public static MSZIPDStream Init(SystemImpl system, FileStream input, FileStream output, int input_buffer_size, bool repair_mode)
- {
- if (system == null)
- return null;
-
- // Round up input buffer size to multiple of two
- input_buffer_size = (input_buffer_size + 1) & -2;
- if (input_buffer_size < 2)
- return null;
-
- // Allocate decompression state
- return new MSZIPDStream
- {
- // Allocate input buffer
- InputBuffer = new byte[input_buffer_size],
-
- // Initialise decompression state
- System = system,
- InputFileHandle = input,
- OutputFileHandle = output,
- InputBufferSize = (uint)input_buffer_size,
- EndOfInput = 0,
- Error = Error.MSPACK_ERR_OK,
- RepairMode = repair_mode,
- FlushWindow = FlushWindow,
-
- OutputPointer = 0,
- OutputEnd = 0,
-
- BufferState = new BufferState()
- {
- InputPointer = 0,
- InputEnd = 0,
- BitBuffer = 0,
- BitsLeft = 0,
- }
- };
- }
+ public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE];
///
- /// Decompresses, or decompresses more of, an MS-ZIP stream.
- ///
- /// - out_bytes of data will be decompressed and the function will return
- /// with an MSPACK_ERR_OK return code.
- ///
- /// - decompressing will stop as soon as out_bytes is reached. if the true
- /// amount of bytes decoded spills over that amount, they will be kept for
- /// a later invocation of mszipd_decompress().
- ///
- /// - the output bytes will be passed to the system.write() function given in
- /// mszipd_init(), using the output file handle given in mszipd_init(). More
- /// than one call may be made to system.write()
- ///
- /// - MS-ZIP will read input bytes as necessary using the system.read()
- /// function given in mszipd_init(), using the input file handle given in
- /// mszipd_init(). This will continue until system.read() returns 0 bytes,
- /// or an error.
+ /// Offset within window
///
- public static Error Decompress(object o, long out_bytes)
- {
- MSZIPDStream zip = o as MSZIPDStream;
- if (zip == null)
- return Error.MSPACK_ERR_ARGS;
+ public uint WindowPosition { get; set; }
- // For the bit buffer
- uint bit_buffer;
- int bits_left;
- int i_ptr, i_end;
+ public bool RepairMode { get; set; }
- int i, readState;
- Error error;
+ public int BytesOutput { get; set; }
- // Easy answers
- if (zip == null || (out_bytes < 0))
- return Error.MSPACK_ERR_ARGS;
- if (zip.Error != Error.MSPACK_ERR_OK)
- return zip.Error;
+ #region Huffman code lengths
- // Flush out any stored-up bytes before we begin
- i = zip.OutputEnd - zip.OutputPointer;
- if (i > out_bytes)
- i = (int)out_bytes;
+ public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS];
+ public byte[] DISTANCE_len { get; set; } = new byte[MSZIP_DISTANCE_MAXSYMBOLS];
- if (i != 0)
- {
- if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
- return zip.Error = Error.MSPACK_ERR_WRITE;
+ #endregion
- zip.OutputPointer += i;
- out_bytes -= i;
- }
+ #region Huffman decoding tables
- if (out_bytes == 0)
- return Error.MSPACK_ERR_OK;
+ public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP_LITERAL_TABLESIZE];
+ public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP_DISTANCE_TABLESIZE];
- while (out_bytes > 0)
- {
- // Unpack another block
- BufferState state = zip.RESTORE_BITS();
-
- // Skip to next read 'CK' header
- i = state.BitsLeft & 7;
-
- // Align to bytestream
- state.REMOVE_BITS_LSB(i);
-
- readState = 0;
- do
- {
- i = (int)zip.READ_BITS_LSB(8, state);
-
- if (i == 'C')
- readState = 1;
- else if ((readState == 1) && (i == 'K'))
- readState = 2;
- else
- readState = 0;
- } while (readState != 2);
-
- // Inflate a block, repair and realign if necessary
- zip.WindowPosition = 0;
- zip.BytesOutput = 0;
-
- zip.STORE_BITS(state);
-
- if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK)
- {
- Console.WriteLine($"Inflate error {error}");
- if (zip.RepairMode)
- {
- // Recover partially-inflated buffers
- if (zip.BytesOutput == 0 && zip.WindowPosition > 0)
- zip.FlushWindow(zip, zip.WindowPosition);
-
- zip.System.Message(null, $"MSZIP error, {MSZIP_FRAME_SIZE - zip.BytesOutput} bytes of data lost.");
- for (i = zip.BytesOutput; i < MSZIP_FRAME_SIZE; i++)
- {
- zip.Window[i] = 0x00;
- }
-
- zip.BytesOutput = MSZIP_FRAME_SIZE;
- }
- else
- {
- return zip.Error = error;
- }
- }
-
- zip.OutputPointer = 0;
- zip.OutputEnd = zip.BytesOutput;
-
- // Write a frame
- i = (out_bytes < zip.BytesOutput) ? (int)out_bytes : zip.BytesOutput;
- if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
- return zip.Error = Error.MSPACK_ERR_WRITE;
-
- // mspack errors (i.e. read errors) are fatal and can't be recovered
- if ((error > 0) && zip.RepairMode)
- return error;
-
- zip.OutputPointer += i;
- out_bytes -= i;
- }
-
- if (out_bytes != 0)
- {
- Console.WriteLine($"Bytes left to output: {out_bytes}");
- return zip.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- return Error.MSPACK_ERR_OK;
- }
-
- ///
- /// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
- /// like mszipd_decompress(), but doesn't take an out_bytes parameter
- ///
- public static Error DecompressKWAJ(MSZIPDStream zip)
- {
- int i, block_len;
- Error error;
-
- // Unpack blocks until block_len == 0
- for (; ; )
- {
- BufferState state = zip.RESTORE_BITS();
-
- // Align to bytestream, read block_len
- i = state.BitsLeft & 7;
- state.REMOVE_BITS_LSB(i);
-
- 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, state);
- if (i != 'C')
- return Error.MSPACK_ERR_DATAFORMAT;
-
- i = (int)zip.READ_BITS_LSB(8, state);
- if (i != 'K')
- return Error.MSPACK_ERR_DATAFORMAT;
-
- // Inflate block
- zip.WindowPosition = 0;
- zip.BytesOutput = 0;
-
- zip.STORE_BITS(state);
-
- if ((error = Inflate(zip)) != Error.MSPACK_ERR_OK)
- {
- Console.WriteLine($"Inflate error {error}");
- return zip.Error = (error > 0) ? error : Error.MSPACK_ERR_DECRUNCH;
- }
-
- // Write inflated block
- try { zip.System.Write(zip.OutputFileHandle, zip.Window, 0, zip.BytesOutput); }
- catch { return zip.Error = Error.MSPACK_ERR_WRITE; }
- }
-
- return Error.MSPACK_ERR_OK;
- }
-
- private static Error ReadLens(MSZIPDStream zip)
- {
- // bitlen Huffman codes -- immediate lookup, 7 bit max code length
- ushort[] bl_table = new ushort[(1 << 7)];
- byte[] bl_len = new byte[19];
-
- byte[] lens = new byte[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS];
- uint lit_codes, dist_codes, code, last_code = 0, bitlen_codes, i, run;
-
- BufferState state = zip.RESTORE_BITS();
-
- // Read the number of codes
- lit_codes = (uint)zip.READ_BITS_LSB(5, state);
- lit_codes += 257;
-
- dist_codes = (uint)zip.READ_BITS_LSB(5, state);
- dist_codes += 1;
-
- bitlen_codes = (uint)zip.READ_BITS_LSB(5, state);
- bitlen_codes += 4;
-
- if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS)
- return Error.INF_ERR_SYMLENS;
- if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS)
- return Error.INF_ERR_SYMLENS;
-
- // Read in the bit lengths in their unusual order
- for (i = 0; i < bitlen_codes; i++)
- {
- bl_len[BitLengthOrder[i]] = (byte)zip.READ_BITS_LSB(3, state);
- }
-
- while (i < 19)
- {
- bl_len[BitLengthOrder[i++]] = 0;
- }
-
- // Create decoding table with an immediate lookup
- if (!CompressionStream.MakeDecodeTableLSB(19, 7, bl_len, bl_table))
- return Error.INF_ERR_BITLENTBL;
-
- // Read literal / distance code lengths
- for (i = 0; i < (lit_codes + dist_codes); i++)
- {
- // Single-level huffman lookup
-
- 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)
- {
- lens[i] = (byte)(last_code = code);
- }
- else
- {
- switch (code)
- {
- case 16:
- run = (uint)zip.READ_BITS_LSB(2, state);
- run += 3;
- code = last_code;
- break;
-
- case 17:
- run = (uint)zip.READ_BITS_LSB(3, state);
- run += 3;
- code = 0;
- break;
-
- case 18:
- run = (uint)zip.READ_BITS_LSB(7, state);
- run += 11;
- code = 0;
- break;
-
- default:
- Console.WriteLine($"Bad code!: {code}");
- return Error.INF_ERR_BADBITLEN;
- }
-
- if ((i + run) > (lit_codes + dist_codes))
- return Error.INF_ERR_BITOVERRUN;
-
- while (run-- != 0)
- {
- lens[i++] = (byte)code;
- }
-
- i--;
- }
- }
-
- // Copy LITERAL code lengths and clear any remaining
- i = lit_codes;
- Array.Copy(lens, 0, zip.LITERAL_len, 0, i);
- while (i < MSZIP_LITERAL_MAXSYMBOLS)
- {
- zip.LITERAL_len[i++] = 0;
- }
-
- i = dist_codes;
- Array.Copy(lens, lit_codes, zip.DISTANCE_len, 0, i);
- while (i < MSZIP_DISTANCE_MAXSYMBOLS)
- {
- zip.DISTANCE_len[i++] = 0;
- }
-
- zip.STORE_BITS(state);
-
- return 0;
- }
-
- ///
- /// A clean implementation of RFC 1951 / inflate
- ///
- private static Error Inflate(MSZIPDStream zip)
- {
- uint last_block, block_type, distance, length, this_run, i;
- Error err;
- ushort sym;
-
- BufferState state = zip.RESTORE_BITS();
-
- do
- {
- // Read in last block bit
- last_block = (uint)zip.READ_BITS_LSB(1, state);
-
- // Read in block type
- block_type = (uint)zip.READ_BITS_LSB(2, state);
-
- if (block_type == 0)
- {
- // Uncompressed block
- byte[] lens_buf = new byte[4];
-
- // Go to byte boundary
- 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; (state.BitsLeft >= 8); i++)
- {
- if (i == 4)
- return Error.INF_ERR_BITBUF;
-
- lens_buf[i] = (byte)zip.PEEK_BITS_LSB(8, state.BitBuffer);
- state.REMOVE_BITS_LSB(8);
- }
-
- if (state.BitsLeft != 0)
- return Error.INF_ERR_BITBUF;
-
- while (i < 4)
- {
- zip.READ_IF_NEEDED(state);
- if (zip.Error != Error.MSPACK_ERR_OK)
- return zip.Error;
-
- lens_buf[i++] = zip.InputBuffer[state.InputPointer++];
- }
-
- // Get the length and its complement
- length = (ushort)(lens_buf[0] | (lens_buf[1] << 8));
- i = (ushort)(lens_buf[2] | (lens_buf[3] << 8));
-
- ushort compl = (ushort)(~i & 0xFFFF);
- if (length != compl)
- return Error.INF_ERR_COMPLEMENT;
-
- // Read and copy the uncompressed data into the window
- while (length > 0)
- {
- zip.READ_IF_NEEDED(state);
- if (zip.Error != Error.MSPACK_ERR_OK)
- return zip.Error;
-
- this_run = length;
- 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, state.InputPointer, zip.Window, zip.WindowPosition, this_run);
-
- zip.WindowPosition += this_run;
- state.InputPointer += (int)this_run;
- length -= this_run;
-
- err = FLUSH_IF_NEEDED(zip);
- if (err != Error.MSPACK_ERR_OK)
- return err;
- }
- }
- else if ((block_type == 1) || (block_type == 2))
- {
- // Huffman-compressed LZ77 block
- uint match_posn, code;
-
- if (block_type == 1)
- {
- // Block with fixed Huffman codes
- i = 0;
- while (i < 144)
- {
- zip.LITERAL_len[i++] = 8;
- }
-
- while (i < 256)
- {
- zip.LITERAL_len[i++] = 9;
- }
-
- while (i < 280)
- {
- zip.LITERAL_len[i++] = 7;
- }
-
- while (i < 288)
- {
- zip.LITERAL_len[i++] = 8;
- }
-
- for (i = 0; i < 32; i++)
- {
- zip.DISTANCE_len[i] = 5;
- }
- }
- else
- {
- // Block with dynamic Huffman codes
- zip.STORE_BITS(state);
-
- if ((err = ReadLens(zip)) != Error.MSPACK_ERR_OK)
- return err;
-
- state = zip.RESTORE_BITS();
- }
-
- // Now huffman lengths are read for either kind of block,
- // create huffman decoding tables
- if (!CompressionStream.MakeDecodeTableLSB(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, zip.LITERAL_len, zip.LITERAL_table))
- return Error.INF_ERR_LITERALTBL;
-
- if (!CompressionStream.MakeDecodeTableLSB(MSZIP_DISTANCE_MAXSYMBOLS, MSZIP_DISTANCE_TABLEBITS, zip.DISTANCE_len, zip.DISTANCE_table))
- return Error.INF_ERR_DISTANCETBL;
-
- // 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, state);
-
- if (code < 256)
- {
- zip.Window[zip.WindowPosition++] = (byte)code;
- err = FLUSH_IF_NEEDED(zip);
- if (err != Error.MSPACK_ERR_OK)
- return err;
- }
- else if (code == 256)
- {
- // END OF BLOCK CODE: loop break point
- break;
- }
- else
- {
- code -= 257; // Codes 257-285 are matches
- if (code >= 29)
- return Error.INF_ERR_LITCODE; // Codes 286-287 are illegal
-
- 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, state);
-
- if (code >= 30)
- return Error.INF_ERR_DISTCODE;
-
- 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
- // around' the frame size.
- match_posn = (uint)((distance > zip.WindowPosition) ? MSZIP_FRAME_SIZE : 0) + zip.WindowPosition - distance;
-
- // Copy match
- if (length < 12)
- {
- // Short match, use slower loop but no loop setup code
- while (length-- != 0)
- {
- zip.Window[zip.WindowPosition++] = zip.Window[match_posn++];
- match_posn &= MSZIP_FRAME_SIZE - 1;
- err = FLUSH_IF_NEEDED(zip);
- if (err != Error.MSPACK_ERR_OK)
- return err;
- }
- }
- else
- {
- // Longer match, use faster loop but with setup expense */
- int runsrc, rundest;
- do
- {
- this_run = length;
- if ((match_posn + this_run) > MSZIP_FRAME_SIZE)
- this_run = MSZIP_FRAME_SIZE - match_posn;
- if ((zip.WindowPosition + this_run) > MSZIP_FRAME_SIZE)
- this_run = MSZIP_FRAME_SIZE - zip.WindowPosition;
-
- rundest = (int)zip.WindowPosition;
- zip.WindowPosition += this_run;
- runsrc = (int)match_posn;
- match_posn += this_run;
- length -= this_run;
- while (this_run-- != 0)
- {
- zip.Window[rundest++] = zip.Window[runsrc++];
- }
-
- if (match_posn == MSZIP_FRAME_SIZE)
- match_posn = 0;
-
- err = FLUSH_IF_NEEDED(zip);
- if (err != Error.MSPACK_ERR_OK)
- return err;
- } while (length > 0);
- }
-
- }
-
- }
- }
- else
- {
- // block_type == 3 -- bad block type
- return Error.INF_ERR_BLOCKTYPE;
- }
- } while (last_block == 0);
-
- // Flush the remaining data
- if (zip.WindowPosition != 0)
- {
- if (zip.FlushWindow(zip, zip.WindowPosition) != Error.MSPACK_ERR_OK)
- return Error.INF_ERR_FLUSH;
- }
-
- zip.STORE_BITS(state);
-
- // Return success
- return Error.MSPACK_ERR_OK;
- }
-
- private static Error FLUSH_IF_NEEDED(MSZIPDStream zip)
- {
- if (zip.WindowPosition == MSZIP_FRAME_SIZE)
- {
- if (zip.FlushWindow(zip, MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
- return Error.INF_ERR_FLUSH;
-
- zip.WindowPosition = 0;
- }
-
- return Error.MSPACK_ERR_OK;
- }
-
- ///
- /// inflate() calls this whenever the window should be flushed. As
- /// MSZIP only expands to the size of the window, the implementation used
- /// simply keeps track of the amount of data flushed, and if more than 32k
- /// is flushed, an error is raised.
- ///
- private static Error FlushWindow(MSZIPDStream zip, uint data_flushed)
- {
- zip.BytesOutput += (int)data_flushed;
- if (zip.BytesOutput > MSZIP_FRAME_SIZE)
- {
- Console.WriteLine($"Overflow: {data_flushed} bytes flushed, total is now {zip.BytesOutput}");
- return Error.MSPACK_ERR_ARGS;
- }
-
- return Error.MSPACK_ERR_OK;
- }
+ #endregion
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs b/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs
deleted file mode 100644
index ec1bbf75..00000000
--- a/BurnOutSharp/External/libmspack/Compression/MSZIPDStream.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-/* This file is part of libmspack.
- * (C) 2003-2004 Stuart Caie.
- *
- * The deflate method was created by Phil Katz. MSZIP is equivalent to the
- * deflate method.
- *
- * libmspack is free software; 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
- */
-
-using System;
-using static LibMSPackSharp.Compression.Constants;
-
-namespace LibMSPackSharp.Compression
-{
- public class MSZIPDStream : CompressionStream
- {
- #region Fields
-
- ///
- /// 32kb history window
- ///
- public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE];
-
- ///
- /// Offset within window
- ///
- public uint WindowPosition { get; set; }
-
- ///
- /// inflate() will call this whenever the window should be emptied.
- ///
- public Func FlushWindow;
-
- public bool RepairMode { get; set; }
-
- public int BytesOutput { get; set; }
-
- #region Huffman code lengths
-
- 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_LITERAL_TABLESIZE];
- public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP_DISTANCE_TABLESIZE];
-
- #endregion
-
- #endregion
-
- ///
- public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
-
- ///
- public override void READ_BYTES(BufferState state)
- {
- READ_IF_NEEDED(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- INJECT_BITS_LSB(InputBuffer[state.InputPointer++], 8, state);
- }
- }
-}
diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.Decompress.cs b/BurnOutSharp/External/libmspack/Compression/QTM.Decompress.cs
new file mode 100644
index 00000000..c9187f6b
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/QTM.Decompress.cs
@@ -0,0 +1,546 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The Quantum method was created by David Stafford, adapted by Microsoft
+ * Corporation.
+ *
+ * This decompressor is based on an implementation by Matthew Russotto, used
+ * with permission.
+ *
+ * libmspack is free software; 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
+ */
+
+/* Quantum decompression implementation */
+
+/* This decompressor was researched and implemented by Matthew Russotto. It
+ * has since been tidied up by Stuart Caie. More information can be found at
+ * http://www.speakeasy.org/~russotto/quantumcomp.html
+ */
+
+using System;
+using System.IO;
+using static LibMSPackSharp.Compression.Constants;
+
+namespace LibMSPackSharp.Compression
+{
+ public partial class QTM
+ {
+ ///
+ /// allocates Quantum decompression state for decoding the given stream.
+ ///
+ /// - returns null if window_bits is outwith the range 10 to 21 (inclusive).
+ /// - uses system.alloc() to allocate memory
+ /// - returns null if not enough memory
+ /// - window_bits is the size of the Quantum window, from 1Kb(10) to 2Mb(21).
+ /// - input_buffer_size is the number of bytes to use to store bitstream data.
+ ///
+ public static QTM Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int input_buffer_size)
+ {
+ uint window_size = (uint)(1 << window_bits);
+
+ if (system == null)
+ return null;
+
+ // Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb)
+ if (window_bits < 10 || window_bits > 21)
+ return null;
+
+ // Round up input buffer size to multiple of two
+ input_buffer_size = (input_buffer_size + 1) & -2;
+ if (input_buffer_size < 2)
+ return null;
+
+ // Allocate decompression state
+ QTM qtm = new QTM()
+ {
+ // Allocate decompression window and input buffer
+ Window = new byte[window_size],
+ InputBuffer = new byte[input_buffer_size],
+
+ // Initialise decompression state
+ System = system,
+ InputFileHandle = input,
+ OutputFileHandle = output,
+ InputBufferSize = (uint)input_buffer_size,
+ WindowSize = window_size,
+ WindowPosition = 0,
+ FrameTODO = QTM_FRAME_SIZE,
+ HeaderRead = 0,
+ Error = Error.MSPACK_ERR_OK,
+
+ InputPointer = 0,
+ InputEnd = 0,
+ OutputPointer = 0,
+ OutputEnd = 0,
+ BitBuffer = 0,
+ BitsLeft = 0,
+ EndOfInput = 0,
+ };
+
+ // Initialise arithmetic coding models
+ // - model 4 depends on window size, ranges from 20 to 24
+ // - model 5 depends on window size, ranges from 20 to 36
+ // - model 6pos depends on window size, ranges from 20 to 42
+
+ int i = window_bits * 2;
+ qtm.InitModel(qtm.Model0, qtm.Model0Symbols, 0, 64);
+ qtm.InitModel(qtm.Model1, qtm.Model1Symbols, 64, 64);
+ qtm.InitModel(qtm.Model2, qtm.Model2Symbols, 128, 64);
+ qtm.InitModel(qtm.Model3, qtm.Model3Symbols, 192, 64);
+ qtm.InitModel(qtm.Model4, qtm.Model4Symbols, 0, (i > 24) ? 24 : i);
+ qtm.InitModel(qtm.Model5, qtm.Model5Symbols, 0, (i > 36) ? 36 : i);
+ qtm.InitModel(qtm.Model6, qtm.Model6Symbols, 0, i);
+ qtm.InitModel(qtm.Model6Len, qtm.Model6LenSymbols, 0, 27);
+ qtm.InitModel(qtm.Model7, qtm.Model7Symbols, 0, 7);
+
+ // All ok
+ return qtm;
+ }
+
+ ///
+ /// Decompresses, or decompresses more of, a Quantum stream.
+ ///
+ /// - out_bytes of data will be decompressed and the function will return
+ /// with an MSPACK_ERR_OK return code.
+ ///
+ /// - decompressing will stop as soon as out_bytes is reached. if the true
+ /// amount of bytes decoded spills over that amount, they will be kept for
+ /// a later invocation of qtmd_decompress().
+ ///
+ /// - the output bytes will be passed to the system.write() function given in
+ /// qtmd_init(), using the output file handle given in qtmd_init(). More
+ /// than one call may be made to system.write()
+ ///
+ /// - Quantum will read input bytes as necessary using the system.read()
+ /// function given in qtmd_init(), using the input file handle given in
+ /// qtmd_init(). This will continue until system.read() returns 0 bytes,
+ /// or an error.
+ ///
+ public static Error Decompress(object o, long out_bytes)
+ {
+ QTM qtm = o as QTM;
+ if (qtm == null)
+ return Error.MSPACK_ERR_ARGS;
+
+ uint frame_todo, frame_end, window_posn, match_offset, range;
+ byte[] window;
+ int runsrc, rundest;
+ int i, j, selector, extra, sym, match_length;
+ ushort H, L, C, symf;
+
+ // Easy answers
+ if (qtm == null || (out_bytes < 0))
+ return Error.MSPACK_ERR_ARGS;
+
+ if (qtm.Error != Error.MSPACK_ERR_OK)
+ return qtm.Error;
+
+ // Flush out any stored-up bytes before we begin
+ i = qtm.OutputEnd - qtm.OutputPointer;
+ if (i > out_bytes)
+ i = (int)out_bytes;
+
+ if (i != 0)
+ {
+ if (qtm.System.Write(qtm.OutputFileHandle, qtm.Window, qtm.OutputPointer, i) != i)
+ return qtm.Error = Error.MSPACK_ERR_WRITE;
+
+ qtm.OutputPointer += i;
+ out_bytes -= i;
+ }
+
+ if (out_bytes == 0)
+ return Error.MSPACK_ERR_OK;
+
+ // Restore local state
+ window = qtm.Window;
+ window_posn = qtm.WindowPosition;
+ frame_todo = qtm.FrameTODO;
+ H = qtm.High;
+ L = qtm.Low;
+ C = qtm.Current;
+
+ // While we do not have enough decoded bytes in reserve
+ while ((qtm.OutputEnd - qtm.OutputPointer) < out_bytes)
+ {
+ // Read header if necessary. Initialises H, L and C
+ if (qtm.HeaderRead != 0)
+ {
+ H = 0xFFFF;
+ L = 0;
+ C = (ushort)qtm.READ_BITS_MSB(16);
+ qtm.HeaderRead = 1;
+ }
+
+ // Decode more, up to the number of bytes needed, the frame boundary,
+ // or the window boundary, whichever comes first
+ frame_end = (uint)(window_posn + (out_bytes - (qtm.OutputEnd - qtm.OutputPointer)));
+ if ((window_posn + frame_todo) < frame_end)
+ frame_end = window_posn + frame_todo;
+ if (frame_end > qtm.WindowSize)
+ frame_end = qtm.WindowSize;
+
+ while (window_posn < frame_end)
+ {
+ selector = qtm.GET_SYMBOL(qtm.Model7, ref H, ref L, ref C);
+ if (selector < 4)
+ {
+ // Literal byte
+ QTMDModel mdl;
+ switch (selector)
+ {
+ case 0:
+ mdl = qtm.Model0;
+ break;
+
+ case 1:
+ mdl = qtm.Model1;
+ break;
+
+ case 2:
+ mdl = qtm.Model2;
+ break;
+
+ case 3:
+ default:
+ mdl = qtm.Model3;
+ break;
+ }
+
+ sym = qtm.GET_SYMBOL(mdl, ref H, ref L, ref C);
+ window[window_posn++] = (byte)sym;
+ frame_todo--;
+ }
+ else
+ {
+ // Match repeated string
+ switch (selector)
+ {
+ // Selector 4 = fixed length match (3 bytes)
+ case 4:
+ sym = qtm.GET_SYMBOL(qtm.Model4, ref H, ref L, ref C);
+ extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym]);
+ match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
+ match_length = 3;
+ break;
+
+ // Selector 5 = fixed length match (4 bytes)
+ case 5:
+ sym = qtm.GET_SYMBOL(qtm.Model5, ref H, ref L, ref C);
+ extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym]);
+ match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
+ match_length = 4;
+ break;
+
+ // Selector 6 = variable length match
+ case 6:
+ sym = qtm.GET_SYMBOL(qtm.Model6Len, ref H, ref L, ref C);
+ extra = (int)qtm.READ_MANY_BITS_MSB(QTMLengthExtra[sym]);
+ match_length = QTMLengthBase[sym] + extra + 5;
+
+ sym = qtm.GET_SYMBOL(qtm.Model6, ref H, ref L, ref C);
+ extra = (int)qtm.READ_MANY_BITS_MSB(QTMExtraBits[sym]);
+ match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
+ break;
+
+ default:
+ // Should be impossible, model7 can only return 0-6
+ Console.WriteLine($"Got {selector} from selector");
+ return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ rundest = (int)window_posn;
+ frame_todo -= (uint)match_length;
+
+ // Does match destination wrap the window? This situation is possible
+ // where the window size is less than the 32k frame size, but matches
+ // must not go beyond a frame boundary
+ if ((window_posn + match_length) > qtm.WindowSize)
+ {
+ // Copy first part of match, before window end
+ i = (int)(qtm.WindowSize - window_posn);
+ j = (int)(window_posn - match_offset);
+
+ while (i-- != 0)
+ {
+ window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
+ }
+
+ // Flush currently stored data
+ i = (int)(qtm.WindowSize - qtm.OutputPointer);
+
+ // This should not happen, but if it does then this code
+ // can't handle the situation (can't flush up to the end of
+ // the window, but can't break out either because we haven't
+ // finished writing the match). Bail out in this case
+ if (i > out_bytes)
+ {
+ Console.WriteLine($"During window-wrap match; {i} bytes to flush but only need {out_bytes}");
+ return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
+ return qtm.Error = Error.MSPACK_ERR_WRITE;
+
+ out_bytes -= i;
+ qtm.OutputPointer = 0;
+ qtm.OutputEnd = 0;
+
+ // Copy second part of match, after window wrap
+ rundest = 0;
+ i = (int)(match_length - (qtm.WindowSize - window_posn));
+ while (i-- != 0)
+ {
+ window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
+ }
+
+ window_posn = (uint)(window_posn + match_length - qtm.WindowSize);
+
+ break; // Because "window_posn < frame_end" has now failed
+ }
+ else
+ {
+ // Normal match - output won't wrap window or frame end
+ i = match_length;
+
+ // Does match _offset_ wrap the window?
+ if (match_offset > window_posn)
+ {
+ // j = length from match offset to end of window
+ j = (int)(match_offset - window_posn);
+ if (j > (int)qtm.WindowSize)
+ {
+ Console.WriteLine("Match offset beyond window boundaries");
+ return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ runsrc = (int)(qtm.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++];
+ }
+ }
+
+ window_posn += (uint)match_length;
+ }
+ }
+ }
+
+ qtm.OutputEnd = (int)window_posn;
+
+ // If we subtracted too much from frame_todo, it will
+ // wrap around past zero and go above its max value
+ if (frame_todo > QTM_FRAME_SIZE)
+ {
+ Console.WriteLine("Overshot frame alignment");
+ return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
+ }
+
+ // Another frame completed?
+ if (frame_todo == 0)
+ {
+ // Re-align input
+ if ((qtm.BitsLeft & 7) != 0)
+ qtm.REMOVE_BITS_MSB(qtm.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);
+ } while (i != 0xFF);
+
+ qtm.HeaderRead = 0;
+
+ frame_todo = QTM_FRAME_SIZE;
+ }
+
+ // Window wrap?
+ if (window_posn == qtm.WindowSize)
+ {
+ // Flush all currently stored data
+ i = (qtm.OutputEnd - qtm.OutputPointer);
+
+ // Break out if we have more than enough to finish this request
+ if (i >= out_bytes)
+ break;
+
+ if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
+ return qtm.Error = Error.MSPACK_ERR_WRITE;
+
+ out_bytes -= i;
+ qtm.OutputPointer = 0;
+ qtm.OutputEnd = 0;
+ window_posn = 0;
+ }
+
+ }
+
+ if (out_bytes != 0)
+ {
+ i = (int)out_bytes;
+
+ if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
+ return qtm.Error = Error.MSPACK_ERR_WRITE;
+
+ qtm.OutputPointer += i;
+ }
+
+ // Store local state
+ qtm.WindowPosition = window_posn;
+ qtm.FrameTODO = frame_todo;
+ qtm.High = H;
+ qtm.Low = L;
+ qtm.Current = C;
+
+ return Error.MSPACK_ERR_OK;
+ }
+
+ private ushort GET_SYMBOL(QTMDModel model, ref ushort H, ref ushort L, ref ushort C)
+ {
+ uint range = (uint)((H - L) & 0xFFFF) + 1;
+ ushort symf = (ushort)(((((C - L + 1) * model.Syms[0].CumulativeFrequency) - 1) / range) & 0xFFFF);
+
+ int i = 1;
+ for (; i < model.Entries; i++)
+ {
+ if (model.Syms[i].CumulativeFrequency <= symf)
+ break;
+ }
+
+ ushort temp = model.Syms[i - 1].Sym;
+
+ range = (uint)(H - L) + 1;
+ symf = model.Syms[0].CumulativeFrequency;
+ H = (ushort)(L + ((model.Syms[i - 1].CumulativeFrequency * range) / symf) - 1);
+ L = (ushort)(L + ((model.Syms[i].CumulativeFrequency * range) / symf));
+
+ do
+ {
+ model.Syms[--i].CumulativeFrequency += 8;
+ } while (i > 0);
+
+ if (model.Syms[0].CumulativeFrequency > 3800)
+ UpdateModel(model);
+
+ while (true)
+ {
+ if ((L & 0x8000) != (H & 0x8000))
+ {
+ if ((L & 0x4000) != 0 && (H & 0x4000) == 0)
+ {
+ // Underflow case
+ C ^= 0x4000;
+ L &= 0x3FFF;
+ H |= 0x4000;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ L <<= 1;
+ H = (ushort)((H << 1) | 1);
+
+ ENSURE_BITS(1);
+ C = (ushort)((C << 1) | (PEEK_BITS_MSB(1)));
+ REMOVE_BITS_MSB(1);
+ }
+
+ return temp;
+ }
+
+ private void UpdateModel(QTMDModel model)
+ {
+ QTMDModelSym tmp;
+ int i, j;
+
+ if (--model.ShiftsLeft != 0)
+ {
+ for (i = model.Entries - 1; i >= 0; i--)
+ {
+ // -1, not -2; the 0 entry saves this
+ model.Syms[i].CumulativeFrequency >>= 1;
+ if (model.Syms[i].CumulativeFrequency <= model.Syms[i + 1].CumulativeFrequency)
+ model.Syms[i].CumulativeFrequency = (ushort)(model.Syms[i + 1].CumulativeFrequency + 1);
+ }
+ }
+ else
+ {
+ model.ShiftsLeft = 50;
+ for (i = 0; i < model.Entries; i++)
+ {
+ // No -1, want to include the 0 entry
+
+ // This converts CumFreqs into frequencies, then shifts right
+ model.Syms[i].CumulativeFrequency -= model.Syms[i + 1].CumulativeFrequency;
+ model.Syms[i].CumulativeFrequency++; // Avoid losing things entirely
+ model.Syms[i].CumulativeFrequency >>= 1;
+ }
+
+ // Now sort by frequencies, decreasing order -- this must be an
+ // inplace selection sort, or a sort with the same (in)stability
+ // characteristics
+ for (i = 0; i < model.Entries - 1; i++)
+ {
+ for (j = i + 1; j < model.Entries; j++)
+ {
+ if (model.Syms[i].CumulativeFrequency < model.Syms[j].CumulativeFrequency)
+ {
+ tmp = model.Syms[i];
+ model.Syms[i] = model.Syms[j];
+ model.Syms[j] = tmp;
+ }
+ }
+ }
+
+ // Then convert frequencies back to CumFreq
+ for (i = model.Entries - 1; i >= 0; i--)
+ {
+ model.Syms[i].CumulativeFrequency += model.Syms[i + 1].CumulativeFrequency;
+ }
+ }
+ }
+
+ private void InitModel(QTMDModel model, QTMDModelSym[] syms, int start, int len)
+ {
+ model.ShiftsLeft = 4;
+ model.Entries = len;
+ model.Syms = syms;
+
+ for (int i = 0; i <= len; i++)
+ {
+ // Actual symbol
+ syms[i].Sym = (ushort)(start + i);
+
+ // Current frequency of that symbol
+ syms[i].CumulativeFrequency = (ushort)(len - i);
+ }
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.ReadBits.cs b/BurnOutSharp/External/libmspack/Compression/QTM.ReadBits.cs
new file mode 100644
index 00000000..83ce7f6c
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/QTM.ReadBits.cs
@@ -0,0 +1,34 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The Quantum method was created by David Stafford, adapted by Microsoft
+ * Corporation.
+ *
+ * libmspack is free software; 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 partial class QTM : CompressionStream
+ {
+ ///
+ public override void READ_BYTES()
+ {
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ byte b0 = InputBuffer[InputPointer++];
+
+ READ_IF_NEEDED();
+ if (Error != Error.MSPACK_ERR_OK)
+ return;
+
+ byte b1 = InputBuffer[InputPointer++];
+ INJECT_BITS_MSB((b0 << 8) | b1, 16);
+ }
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.ReadHuff.cs b/BurnOutSharp/External/libmspack/Compression/QTM.ReadHuff.cs
new file mode 100644
index 00000000..96b62bb9
--- /dev/null
+++ b/BurnOutSharp/External/libmspack/Compression/QTM.ReadHuff.cs
@@ -0,0 +1,22 @@
+/* This file is part of libmspack.
+ * (C) 2003-2004 Stuart Caie.
+ *
+ * The Quantum method was created by David Stafford, adapted by Microsoft
+ * Corporation.
+ *
+ * libmspack is free software; 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
+ */
+
+using System;
+
+namespace LibMSPackSharp.Compression
+{
+ public partial class QTM : CompressionStream
+ {
+ ///
+ public override Error HUFF_ERROR() => throw new NotImplementedException();
+ }
+}
diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.cs b/BurnOutSharp/External/libmspack/Compression/QTM.cs
index 5b6cdfc9..33bc9f3e 100644
--- a/BurnOutSharp/External/libmspack/Compression/QTM.cs
+++ b/BurnOutSharp/External/libmspack/Compression/QTM.cs
@@ -4,552 +4,120 @@
* The Quantum method was created by David Stafford, adapted by Microsoft
* Corporation.
*
- * This decompressor is based on an implementation by Matthew Russotto, used
- * with permission.
- *
* libmspack is free software; 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
*/
-/* Quantum decompression implementation */
-
-/* This decompressor was researched and implemented by Matthew Russotto. It
- * has since been tidied up by Stuart Caie. More information can be found at
- * http://www.speakeasy.org/~russotto/quantumcomp.html
- */
-
-using System;
-using System.IO;
-using static LibMSPackSharp.Compression.Constants;
-
namespace LibMSPackSharp.Compression
{
- public class QTM
+ public partial class QTM : CompressionStream
{
///
- /// allocates Quantum decompression state for decoding the given stream.
- ///
- /// - returns null if window_bits is outwith the range 10 to 21 (inclusive).
- /// - uses system.alloc() to allocate memory
- /// - returns null if not enough memory
- /// - window_bits is the size of the Quantum window, from 1Kb(10) to 2Mb(21).
- /// - input_buffer_size is the number of bytes to use to store bitstream data.
+ /// Decoding window
///
- public static QTMDStream Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int input_buffer_size)
- {
- uint window_size = (uint)(1 << window_bits);
-
- if (system == null)
- return null;
-
- // Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb)
- if (window_bits < 10 || window_bits > 21)
- return null;
-
- // Round up input buffer size to multiple of two
- input_buffer_size = (input_buffer_size + 1) & -2;
- if (input_buffer_size < 2)
- return null;
-
- // Allocate decompression state
- QTMDStream qtm = new QTMDStream()
- {
- // Allocate decompression window and input buffer
- Window = new byte[window_size],
- InputBuffer = new byte[input_buffer_size],
-
- // Initialise decompression state
- System = system,
- InputFileHandle = input,
- OutputFileHandle = output,
- InputBufferSize = (uint)input_buffer_size,
- WindowSize = window_size,
- WindowPosition = 0,
- FrameTODO = QTM_FRAME_SIZE,
- HeaderRead = 0,
- Error = Error.MSPACK_ERR_OK,
-
- OutputPointer = 0,
- OutputEnd = 0,
- EndOfInput = 0,
-
- BufferState = new BufferState()
- {
- InputPointer = 0,
- InputEnd = 0,
- BitBuffer = 0,
- BitsLeft = 0,
- }
- };
-
- // Initialise arithmetic coding models
- // - model 4 depends on window size, ranges from 20 to 24
- // - model 5 depends on window size, ranges from 20 to 36
- // - model 6pos depends on window size, ranges from 20 to 42
-
- int i = window_bits * 2;
- InitModel(qtm.Model0, qtm.Model0Symbols, 0, 64);
- InitModel(qtm.Model1, qtm.Model1Symbols, 64, 64);
- InitModel(qtm.Model2, qtm.Model2Symbols, 128, 64);
- InitModel(qtm.Model3, qtm.Model3Symbols, 192, 64);
- InitModel(qtm.Model4, qtm.Model4Symbols, 0, (i > 24) ? 24 : i);
- InitModel(qtm.Model5, qtm.Model5Symbols, 0, (i > 36) ? 36 : i);
- InitModel(qtm.Model6, qtm.Model6Symbols, 0, i);
- InitModel(qtm.Model6Len, qtm.Model6LenSymbols, 0, 27);
- InitModel(qtm.Model7, qtm.Model7Symbols, 0, 7);
-
- // All ok
- return qtm;
- }
+ public byte[] Window { get; set; }
///
- /// Decompresses, or decompresses more of, a Quantum stream.
- ///
- /// - out_bytes of data will be decompressed and the function will return
- /// with an MSPACK_ERR_OK return code.
- ///
- /// - decompressing will stop as soon as out_bytes is reached. if the true
- /// amount of bytes decoded spills over that amount, they will be kept for
- /// a later invocation of qtmd_decompress().
- ///
- /// - the output bytes will be passed to the system.write() function given in
- /// qtmd_init(), using the output file handle given in qtmd_init(). More
- /// than one call may be made to system.write()
- ///
- /// - Quantum will read input bytes as necessary using the system.read()
- /// function given in qtmd_init(), using the input file handle given in
- /// qtmd_init(). This will continue until system.read() returns 0 bytes,
- /// or an error.
+ /// Window size
///
- public static Error Decompress(object o, long out_bytes)
- {
- QTMDStream qtm = o as QTMDStream;
- if (qtm == null)
- return Error.MSPACK_ERR_ARGS;
+ public uint WindowSize { get; set; }
- uint frame_todo, frame_end, window_posn, match_offset, range;
- byte[] window;
- int i_ptr, i_end, runsrc, rundest;
- int i, j, selector, extra, sym, match_length;
- ushort H, L, C, symf;
+ ///
+ /// Decompression offset within window
+ ///
+ public uint WindowPosition { get; set; }
- uint bit_buffer;
- int bits_left;
+ ///
+ /// Bytes remaining for current frame
+ ///
+ public uint FrameTODO { get; set; }
- // Easy answers
- if (qtm == null || (out_bytes < 0))
- return Error.MSPACK_ERR_ARGS;
+ ///
+ /// High: arith coding state
+ ///
+ public ushort High { get; set; }
- if (qtm.Error != Error.MSPACK_ERR_OK)
- return qtm.Error;
+ ///
+ /// Low: arith coding state
+ ///
+ public ushort Low { get; set; }
- // Flush out any stored-up bytes before we begin
- i = qtm.OutputEnd - qtm.OutputPointer;
- if (i > out_bytes)
- i = (int)out_bytes;
+ ///
+ /// Current: arith coding state
+ ///
+ public ushort Current { get; set; }
- if (i != 0)
- {
- if (qtm.System.Write(qtm.OutputFileHandle, qtm.Window, qtm.OutputPointer, i) != i)
- return qtm.Error = Error.MSPACK_ERR_WRITE;
+ ///
+ /// Have we started decoding a new frame?
+ ///
+ public byte HeaderRead { get; set; }
- qtm.OutputPointer += i;
- out_bytes -= i;
- }
+ // Four literal models, each representing 64 symbols
- if (out_bytes == 0)
- return Error.MSPACK_ERR_OK;
+ ///
+ /// For literals from 0 to 63 (selector = 0)
+ ///
+ public QTMDModel Model0 { get; set; }
- // Restore local state
- BufferState state = qtm.RESTORE_BITS();
- window = qtm.Window;
- window_posn = qtm.WindowPosition;
- frame_todo = qtm.FrameTODO;
- H = qtm.High;
- L = qtm.Low;
- C = qtm.Current;
+ ///
+ /// For literals from 64 to 127 (selector = 1)
+ ///
+ public QTMDModel Model1 { get; set; }
- // While we do not have enough decoded bytes in reserve
- while ((qtm.OutputEnd - qtm.OutputPointer) < out_bytes)
- {
- // Read header if necessary. Initialises H, L and C
- if (qtm.HeaderRead != 0)
- {
- H = 0xFFFF;
- L = 0;
- C = (ushort)qtm.READ_BITS_MSB(16, state);
- qtm.HeaderRead = 1;
- }
+ ///
+ /// For literals from 128 to 191 (selector = 2)
+ ///
+ public QTMDModel Model2 { get; set; }
- // Decode more, up to the number of bytes needed, the frame boundary,
- // or the window boundary, whichever comes first
- frame_end = (uint)(window_posn + (out_bytes - (qtm.OutputEnd - qtm.OutputPointer)));
- if ((window_posn + frame_todo) < frame_end)
- frame_end = window_posn + frame_todo;
- if (frame_end > qtm.WindowSize)
- frame_end = qtm.WindowSize;
+ ///
+ /// For literals from 129 to 255 (selector = 3)
+ ///
+ public QTMDModel Model3 { get; set; }
- while (window_posn < frame_end)
- {
- selector = GET_SYMBOL(qtm, qtm.Model7, ref H, ref L, ref C, state);
- if (selector < 4)
- {
- // Literal byte
- QTMDModel mdl;
- switch (selector)
- {
- case 0:
- mdl = qtm.Model0;
- break;
+ // Three match models.
- case 1:
- mdl = qtm.Model1;
- break;
+ ///
+ /// For match with fixed length of 3 bytes
+ ///
+ public QTMDModel Model4 { get; set; }
- case 2:
- mdl = qtm.Model2;
- break;
+ ///
+ /// For match with fixed length of 4 bytes
+ ///
+ public QTMDModel Model5 { get; set; }
- case 3:
- default:
- mdl = qtm.Model3;
- break;
- }
+ ///
+ /// For variable length match, encoded with model6len model
+ ///
+ public QTMDModel Model6 { get; set; }
- sym = GET_SYMBOL(qtm, mdl, ref H, ref L, ref C, state);
- window[window_posn++] = (byte)sym;
- frame_todo--;
- }
- else
- {
- // Match repeated string
- switch (selector)
- {
- // Selector 4 = fixed length match (3 bytes)
- case 4:
- 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;
+ public QTMDModel Model6Len { get; set; }
- // Selector 5 = fixed length match (4 bytes)
- case 5:
- 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 model. 0-6 to say literal (0,1,2,3) or match (4,5,6)
+ ///
+ public QTMDModel Model7 { get; set; }
- // Selector 6 = variable length match
- case 6:
- 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;
+ // Symbol arrays for all models
- 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;
+ public QTMDModelSym[] Model0Symbols { get; set; } = new QTMDModelSym[64 + 1];
- default:
- // Should be impossible, model7 can only return 0-6
- Console.WriteLine($"Got {selector} from selector");
- return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
- }
+ public QTMDModelSym[] Model1Symbols { get; set; } = new QTMDModelSym[64 + 1];
- rundest = (int)window_posn;
- frame_todo -= (uint)match_length;
+ public QTMDModelSym[] Model2Symbols { get; set; } = new QTMDModelSym[64 + 1];
- // Does match destination wrap the window? This situation is possible
- // where the window size is less than the 32k frame size, but matches
- // must not go beyond a frame boundary
- if ((window_posn + match_length) > qtm.WindowSize)
- {
- // Copy first part of match, before window end
- i = (int)(qtm.WindowSize - window_posn);
- j = (int)(window_posn - match_offset);
+ public QTMDModelSym[] Model3Symbols { get; set; } = new QTMDModelSym[64 + 1];
- while (i-- != 0)
- {
- window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
- }
+ public QTMDModelSym[] Model4Symbols { get; set; } = new QTMDModelSym[24 + 1];
- // Flush currently stored data
- i = (int)(qtm.WindowSize - qtm.OutputPointer);
+ public QTMDModelSym[] Model5Symbols { get; set; } = new QTMDModelSym[36 + 1];
- // This should not happen, but if it does then this code
- // can't handle the situation (can't flush up to the end of
- // the window, but can't break out either because we haven't
- // finished writing the match). Bail out in this case
- if (i > out_bytes)
- {
- Console.WriteLine($"During window-wrap match; {i} bytes to flush but only need {out_bytes}");
- return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
- }
+ public QTMDModelSym[] Model6Symbols { get; set; } = new QTMDModelSym[42 + 1];
- if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
- return qtm.Error = Error.MSPACK_ERR_WRITE;
+ public QTMDModelSym[] Model6LenSymbols { get; set; } = new QTMDModelSym[27 + 1];
- out_bytes -= i;
- qtm.OutputPointer = 0;
- qtm.OutputEnd = 0;
-
- // Copy second part of match, after window wrap
- rundest = 0;
- i = (int)(match_length - (qtm.WindowSize - window_posn));
- while (i-- != 0)
- {
- window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
- }
-
- window_posn = (uint)(window_posn + match_length - qtm.WindowSize);
-
- break; // Because "window_posn < frame_end" has now failed
- }
- else
- {
- // Normal match - output won't wrap window or frame end
- i = match_length;
-
- // Does match _offset_ wrap the window?
- if (match_offset > window_posn)
- {
- // j = length from match offset to end of window
- j = (int)(match_offset - window_posn);
- if (j > (int)qtm.WindowSize)
- {
- Console.WriteLine("Match offset beyond window boundaries");
- return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- runsrc = (int)(qtm.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++];
- }
- }
-
- window_posn += (uint)match_length;
- }
- }
- }
-
- qtm.OutputEnd = (int)window_posn;
-
- // If we subtracted too much from frame_todo, it will
- // wrap around past zero and go above its max value
- if (frame_todo > QTM_FRAME_SIZE)
- {
- Console.WriteLine("Overshot frame alignment");
- return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
- }
-
- // Another frame completed?
- if (frame_todo == 0)
- {
- // Re-align input
- 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, state);
- } while (i != 0xFF);
-
- qtm.HeaderRead = 0;
-
- frame_todo = QTM_FRAME_SIZE;
- }
-
- // Window wrap?
- if (window_posn == qtm.WindowSize)
- {
- // Flush all currently stored data
- i = (qtm.OutputEnd - qtm.OutputPointer);
-
- // Break out if we have more than enough to finish this request
- if (i >= out_bytes)
- break;
-
- if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
- return qtm.Error = Error.MSPACK_ERR_WRITE;
-
- out_bytes -= i;
- qtm.OutputPointer = 0;
- qtm.OutputEnd = 0;
- window_posn = 0;
- }
-
- }
-
- if (out_bytes != 0)
- {
- i = (int)out_bytes;
-
- if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
- return qtm.Error = Error.MSPACK_ERR_WRITE;
-
- qtm.OutputPointer += i;
- }
-
- // Store local state
- qtm.STORE_BITS(state);
- qtm.WindowPosition = window_posn;
- qtm.FrameTODO = frame_todo;
- qtm.High = H;
- qtm.Low = L;
- qtm.Current = C;
-
- return Error.MSPACK_ERR_OK;
- }
-
- 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);
-
- int i = 1;
- for (; i < model.Entries; i++)
- {
- if (model.Syms[i].CumulativeFrequency <= symf)
- break;
- }
-
- ushort temp = model.Syms[i - 1].Sym;
-
- range = (uint)(H - L) + 1;
- symf = model.Syms[0].CumulativeFrequency;
- H = (ushort)(L + ((model.Syms[i - 1].CumulativeFrequency * range) / symf) - 1);
- L = (ushort)(L + ((model.Syms[i].CumulativeFrequency * range) / symf));
-
- do
- {
- model.Syms[--i].CumulativeFrequency += 8;
- } while (i > 0);
-
- if (model.Syms[0].CumulativeFrequency > 3800)
- UpdateModel(model);
-
- while (true)
- {
- if ((L & 0x8000) != (H & 0x8000))
- {
- if ((L & 0x4000) != 0 && (H & 0x4000) == 0)
- {
- // Underflow case
- C ^= 0x4000;
- L &= 0x3FFF;
- H |= 0x4000;
- }
- else
- {
- break;
- }
- }
-
- L <<= 1;
- H = (ushort)((H << 1) | 1);
-
- qtm.ENSURE_BITS(1, state);
- C = (ushort)((C << 1) | (qtm.PEEK_BITS_MSB(1, state.BitBuffer)));
- state.REMOVE_BITS_MSB(1);
- }
-
- return temp;
- }
-
- private static void UpdateModel(QTMDModel model)
- {
- QTMDModelSym tmp;
- int i, j;
-
- if (--model.ShiftsLeft != 0)
- {
- for (i = model.Entries - 1; i >= 0; i--)
- {
- // -1, not -2; the 0 entry saves this
- model.Syms[i].CumulativeFrequency >>= 1;
- if (model.Syms[i].CumulativeFrequency <= model.Syms[i + 1].CumulativeFrequency)
- model.Syms[i].CumulativeFrequency = (ushort)(model.Syms[i + 1].CumulativeFrequency + 1);
- }
- }
- else
- {
- model.ShiftsLeft = 50;
- for (i = 0; i < model.Entries; i++)
- {
- // No -1, want to include the 0 entry
-
- // This converts CumFreqs into frequencies, then shifts right
- model.Syms[i].CumulativeFrequency -= model.Syms[i + 1].CumulativeFrequency;
- model.Syms[i].CumulativeFrequency++; // Avoid losing things entirely
- model.Syms[i].CumulativeFrequency >>= 1;
- }
-
- // Now sort by frequencies, decreasing order -- this must be an
- // inplace selection sort, or a sort with the same (in)stability
- // characteristics
- for (i = 0; i < model.Entries - 1; i++)
- {
- for (j = i + 1; j < model.Entries; j++)
- {
- if (model.Syms[i].CumulativeFrequency < model.Syms[j].CumulativeFrequency)
- {
- tmp = model.Syms[i];
- model.Syms[i] = model.Syms[j];
- model.Syms[j] = tmp;
- }
- }
- }
-
- // Then convert frequencies back to CumFreq
- for (i = model.Entries - 1; i >= 0; i--)
- {
- model.Syms[i].CumulativeFrequency += model.Syms[i + 1].CumulativeFrequency;
- }
- }
- }
-
- private static void InitModel(QTMDModel model, QTMDModelSym[] syms, int start, int len)
- {
- model.ShiftsLeft = 4;
- model.Entries = len;
- model.Syms = syms;
-
- for (int i = 0; i <= len; i++)
- {
- // Actual symbol
- syms[i].Sym = (ushort)(start + i);
-
- // Current frequency of that symbol
- syms[i].CumulativeFrequency = (ushort)(len - i);
- }
- }
+ public QTMDModelSym[] Model7Symbols { get; set; } = new QTMDModelSym[7 + 1];
}
}
diff --git a/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs b/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs
deleted file mode 100644
index 2d7ebb1e..00000000
--- a/BurnOutSharp/External/libmspack/Compression/QTMDStream.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-/* This file is part of libmspack.
- * (C) 2003-2004 Stuart Caie.
- *
- * The Quantum method was created by David Stafford, adapted by Microsoft
- * Corporation.
- *
- * libmspack is free software; 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
- */
-
-using System;
-
-namespace LibMSPackSharp.Compression
-{
- public class QTMDStream : CompressionStream
- {
- #region Fields
-
- ///
- /// Decoding window
- ///
- public byte[] Window { get; set; }
-
- ///
- /// Window size
- ///
- public uint WindowSize { get; set; }
-
- ///
- /// Decompression offset within window
- ///
- public uint WindowPosition { get; set; }
-
- ///
- /// Bytes remaining for current frame
- ///
- public uint FrameTODO { get; set; }
-
- ///
- /// High: arith coding state
- ///
- public ushort High { get; set; }
-
- ///
- /// Low: arith coding state
- ///
- public ushort Low { get; set; }
-
- ///
- /// Current: arith coding state
- ///
- public ushort Current { get; set; }
-
- ///
- /// Have we started decoding a new frame?
- ///
- public byte HeaderRead { get; set; }
-
- // Four literal models, each representing 64 symbols
-
- ///
- /// For literals from 0 to 63 (selector = 0)
- ///
- public QTMDModel Model0 { get; set; }
-
- ///
- /// For literals from 64 to 127 (selector = 1)
- ///
- public QTMDModel Model1 { get; set; }
-
- ///
- /// For literals from 128 to 191 (selector = 2)
- ///
- public QTMDModel Model2 { get; set; }
-
- ///
- /// For literals from 129 to 255 (selector = 3)
- ///
- public QTMDModel Model3 { get; set; }
-
- // Three match models.
-
- ///
- /// For match with fixed length of 3 bytes
- ///
- public QTMDModel Model4 { get; set; }
-
- ///
- /// For match with fixed length of 4 bytes
- ///
- public QTMDModel Model5 { get; set; }
-
- ///
- /// For variable length match, encoded with model6len model
- ///
- public QTMDModel Model6 { get; set; }
-
- public QTMDModel Model6Len { get; set; }
-
- ///
- /// Selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6)
- ///
- public QTMDModel Model7 { get; set; }
-
- // Symbol arrays for all models
-
- public QTMDModelSym[] Model0Symbols { get; set; } = new QTMDModelSym[64 + 1];
-
- public QTMDModelSym[] Model1Symbols { get; set; } = new QTMDModelSym[64 + 1];
-
- public QTMDModelSym[] Model2Symbols { get; set; } = new QTMDModelSym[64 + 1];
-
- public QTMDModelSym[] Model3Symbols { get; set; } = new QTMDModelSym[64 + 1];
-
- public QTMDModelSym[] Model4Symbols { get; set; } = new QTMDModelSym[24 + 1];
-
- public QTMDModelSym[] Model5Symbols { get; set; } = new QTMDModelSym[36 + 1];
-
- public QTMDModelSym[] Model6Symbols { get; set; } = new QTMDModelSym[42 + 1];
-
- public QTMDModelSym[] Model6LenSymbols { get; set; } = new QTMDModelSym[27 + 1];
-
- public QTMDModelSym[] Model7Symbols { get; set; } = new QTMDModelSym[7 + 1];
-
- #endregion
-
- ///
- public override Error HUFF_ERROR() => throw new NotImplementedException();
-
- ///
- public override void READ_BYTES(BufferState state)
- {
- READ_IF_NEEDED(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- byte b0 = InputBuffer[state.InputPointer++];
-
- READ_IF_NEEDED(state);
- if (Error != Error.MSPACK_ERR_OK)
- return;
-
- byte b1 = InputBuffer[state.InputPointer++];
- INJECT_BITS_MSB((b0 << 8) | b1, 16, state);
- }
- }
-}
diff --git a/BurnOutSharp/External/libmspack/KWAJ/Decompressor.cs b/BurnOutSharp/External/libmspack/KWAJ/Decompressor.cs
index b1e7a95a..8d85f023 100644
--- a/BurnOutSharp/External/libmspack/KWAJ/Decompressor.cs
+++ b/BurnOutSharp/External/libmspack/KWAJ/Decompressor.cs
@@ -155,13 +155,13 @@ namespace LibMSPackSharp.KWAJ
}
else if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_LZH)
{
- LZHKWAJStream lzh = LZHKWAJ.Init(System, fh, outfh);
- Error = (lzh != null) ? LZHKWAJ.Decompress(lzh) : Error.MSPACK_ERR_NOMEMORY;
+ LZHKWAJ lzh = LZHKWAJ.Init(System, fh, outfh);
+ Error = (lzh != null) ? lzh.Decompress() : Error.MSPACK_ERR_NOMEMORY;
}
else if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP)
{
- MSZIPDStream zip = MSZIP.Init(System, fh, outfh, KWAJ_INPUT_SIZE, false);
- Error = (zip != null) ? MSZIP.DecompressKWAJ(zip) : Error.MSPACK_ERR_NOMEMORY;
+ MSZIP zip = MSZIP.Init(System, fh, outfh, KWAJ_INPUT_SIZE, false);
+ Error = (zip != null) ? zip.DecompressKWAJ() : Error.MSPACK_ERR_NOMEMORY;
}
else
{
diff --git a/BurnOutSharp/External/libmspack/OAB/Decompressor.cs b/BurnOutSharp/External/libmspack/OAB/Decompressor.cs
index 94db15b5..1a4f6bfb 100644
--- a/BurnOutSharp/External/libmspack/OAB/Decompressor.cs
+++ b/BurnOutSharp/External/libmspack/OAB/Decompressor.cs
@@ -51,7 +51,7 @@ namespace LibMSPackSharp.OAB
public Error Decompress(string input, string output)
{
byte[] hdrbuf = new byte[oabhead_SIZEOF];
- LZXDStream lzx = null;
+ LZX lzx = null;
Error ret = Error.MSPACK_ERR_OK;
FileStream infh = System.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
@@ -235,7 +235,7 @@ namespace LibMSPackSharp.OAB
public Error DecompressIncremental(string input, string basePath, string output)
{
byte[] hdrbuf = new byte[patchhead_SIZEOF];
- LZXDStream lzx = null;
+ LZX lzx = null;
int window_bits;
uint window_size;
Error ret = Error.MSPACK_ERR_OK;
@@ -348,7 +348,7 @@ namespace LibMSPackSharp.OAB
return ret;
}
- ret = LZX.SetReferenceData(lzx, System, basefh, blk_ssize);
+ ret = lzx.SetReferenceData(System, basefh, blk_ssize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);