mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-29 01:50:24 +00:00
Partial classes
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,6 @@ namespace LibMSPackSharp.CHM
|
||||
/// <summary>
|
||||
/// LZX decompressor state
|
||||
/// </summary>
|
||||
public LZXDStream State { get; set; }
|
||||
public LZX State { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// i_ptr
|
||||
/// </summary>
|
||||
public int InputPointer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// i_end
|
||||
/// </summary>
|
||||
public int InputEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// bit_buffer
|
||||
/// </summary>
|
||||
public uint BitBuffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// bits_left
|
||||
/// </summary>
|
||||
public int BitsLeft { get; set; }
|
||||
|
||||
#region Common
|
||||
|
||||
/// <summary>
|
||||
/// Initialises bitstream state in state structure
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
InputPointer = 0;
|
||||
InputEnd = 0;
|
||||
BitBuffer = 0;
|
||||
BitsLeft = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MSB
|
||||
|
||||
/// <summary>
|
||||
/// Removes N bits from the bit buffer
|
||||
/// </summary>
|
||||
public void REMOVE_BITS_MSB(int nbits)
|
||||
{
|
||||
BitBuffer <<= nbits;
|
||||
BitsLeft -= nbits;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LSB
|
||||
|
||||
/// <summary>
|
||||
/// Removes N bits from the bit buffer
|
||||
/// </summary>
|
||||
public void REMOVE_BITS_LSB(int nbits)
|
||||
{
|
||||
BitBuffer >>= nbits;
|
||||
BitsLeft -= nbits;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
293
BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadBits.cs
vendored
Normal file
293
BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadBits.cs
vendored
Normal file
@@ -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
|
||||
* <limits.h> 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<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup
|
||||
* table instead of computing this. This adds two new macros,
|
||||
* PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS
|
||||
* and READ_BITS, except they use this lookup table. This is useful if
|
||||
* you need to look up a number of bits that are only known at
|
||||
* runtime, so the bit mask can't be turned into a constant by the
|
||||
* compiler.
|
||||
* The bit buffer datatype should be at least 32 bits wide: it must be
|
||||
* possible to ENSURE_BITS(17), so it must be possible to add 16 new bits
|
||||
* to the bit buffer when the bit buffer already has 1 to 15 bits left.
|
||||
*/
|
||||
public abstract partial class CompressionStream : BaseDecompressState
|
||||
{
|
||||
#region Common
|
||||
|
||||
/// <summary>
|
||||
/// Initialises bitstream state in state structure
|
||||
/// </summary>
|
||||
public void INIT_BITS()
|
||||
{
|
||||
InputPointer = 0;
|
||||
InputEnd = 0;
|
||||
BitBuffer = 0;
|
||||
BitsLeft = 0;
|
||||
EndOfInput = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure there are at least N bits in the bit buffer
|
||||
/// </summary>
|
||||
public void ENSURE_BITS(int nbits)
|
||||
{
|
||||
while (BitsLeft < nbits)
|
||||
{
|
||||
READ_BYTES();
|
||||
if (Error != Error.MSPACK_ERR_OK)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read from the input if the buffer is empty
|
||||
/// </summary>
|
||||
public void READ_IF_NEEDED()
|
||||
{
|
||||
if (InputPointer >= InputEnd)
|
||||
ReadInput();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes from the input into the bit buffer
|
||||
/// </summary>
|
||||
public abstract void READ_BYTES();
|
||||
|
||||
/// <summary>
|
||||
/// Read an input stream and fill the buffer
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Inject data into the bit buffer
|
||||
/// </summary>
|
||||
public void INJECT_BITS_MSB(int bitdata, int nbits)
|
||||
{
|
||||
BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - BitsLeft));
|
||||
BitsLeft += nbits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer
|
||||
/// </summary>
|
||||
public long PEEK_BITS_MSB(int nbits) => (BitBuffer >> (BITBUF_WIDTH - (nbits)));
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read multiple bits and put them in var
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes N bits from the bit buffer
|
||||
/// </summary>
|
||||
public void REMOVE_BITS_MSB(int nbits)
|
||||
{
|
||||
BitBuffer <<= nbits;
|
||||
BitsLeft -= nbits;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LSB
|
||||
|
||||
/// <summary>
|
||||
/// Inject data into the bit buffer
|
||||
/// </summary>
|
||||
public void INJECT_BITS_LSB(int bitdata, int nbits)
|
||||
{
|
||||
BitBuffer |= (uint)(bitdata << BitsLeft);
|
||||
BitsLeft += nbits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer
|
||||
/// </summary>
|
||||
public long PEEK_BITS_LSB(int nbits) => (BitBuffer & ((1 << (nbits)) - 1));
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer using a bit mask
|
||||
/// </summary>
|
||||
public long PEEK_BITS_T_LSB(int nbits) => BitBuffer & LSBBitMask[(nbits)];
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var using a bit mask
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes N bits from the bit buffer
|
||||
/// </summary>
|
||||
public void REMOVE_BITS_LSB(int nbits)
|
||||
{
|
||||
BitBuffer >>= nbits;
|
||||
BitsLeft -= nbits;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
342
BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadHuff.cs
vendored
Normal file
342
BurnOutSharp/External/libmspack/Compression/CompressionStream.ReadHuff.cs
vendored
Normal file
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Per compression error code for decoding failure
|
||||
/// </summary>
|
||||
public abstract Error HUFF_ERROR();
|
||||
|
||||
#endregion
|
||||
|
||||
#region MSB
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next huffman symbol from the input bitstream into var.
|
||||
/// Do not use this macro on a table unless build_decode_table() succeeded.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverse for a single symbol
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function was originally coded by David Tritscher.
|
||||
///
|
||||
/// It builds a fast huffman decoding table from
|
||||
/// a canonical huffman code lengths table.
|
||||
/// </summary>
|
||||
/// <param name="nsyms">total number of symbols in this huffman tree.</param>
|
||||
/// <param name="nbits">any symbols with a code length of nbits or less can be decoded in one lookup of the table.</param>
|
||||
/// <param name="length">A table to get code lengths from [0 to nsyms-1]</param>
|
||||
/// <param name="table">
|
||||
/// The table to fill up with decoded symbols and pointers.
|
||||
/// Should be ((1<<nbits) + (nsyms*2)) in length.
|
||||
/// </param>
|
||||
/// <returns>True for OK or false for error</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next huffman symbol from the input bitstream into var.
|
||||
/// Do not use this macro on a table unless build_decode_table() succeeded.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverse for a single symbol
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function was originally coded by David Tritscher.
|
||||
///
|
||||
/// It builds a fast huffman decoding table from
|
||||
/// a canonical huffman code lengths table.
|
||||
/// </summary>
|
||||
/// <param name="nsyms">total number of symbols in this huffman tree.</param>
|
||||
/// <param name="nbits">any symbols with a code length of nbits or less can be decoded in one lookup of the table.</param>
|
||||
/// <param name="length">A table to get code lengths from [0 to nsyms-1]</param>
|
||||
/// <param name="table">
|
||||
/// The table to fill up with decoded symbols and pointers.
|
||||
/// Should be ((1<<nbits) + (nsyms*2)) in length.
|
||||
/// </param>
|
||||
/// <returns>True for OK or false for error</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// i_ptr
|
||||
/// </summary>
|
||||
public int InputPointer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// i_end
|
||||
/// </summary>
|
||||
public int InputEnd { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// o_ptr
|
||||
/// </summary>
|
||||
public int OutputPointer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// o_end
|
||||
/// </summary>
|
||||
public int OutputEnd { get; set; }
|
||||
|
||||
internal BufferState BufferState { get; set; }
|
||||
/// <summary>
|
||||
/// bit_buffer
|
||||
/// </summary>
|
||||
public uint BitBuffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// bits_left
|
||||
/// </summary>
|
||||
public int BitsLeft { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Have we reached the end of input?
|
||||
/// </summary>
|
||||
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
|
||||
* <limits.h> 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<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup
|
||||
* table instead of computing this. This adds two new macros,
|
||||
* PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS
|
||||
* and READ_BITS, except they use this lookup table. This is useful if
|
||||
* you need to look up a number of bits that are only known at
|
||||
* runtime, so the bit mask can't be turned into a constant by the
|
||||
* compiler.
|
||||
* The bit buffer datatype should be at least 32 bits wide: it must be
|
||||
* possible to ENSURE_BITS(17), so it must be possible to add 16 new bits
|
||||
* to the bit buffer when the bit buffer already has 1 to 15 bits left.
|
||||
*/
|
||||
|
||||
#region Common
|
||||
|
||||
/// <summary>
|
||||
/// Initialises bitstream state in state structure
|
||||
/// </summary>
|
||||
public void INIT_BITS()
|
||||
{
|
||||
BufferState = new BufferState();
|
||||
BufferState.Init();
|
||||
EndOfInput = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores bitstream state in state structure
|
||||
/// </summary>
|
||||
public void STORE_BITS(BufferState state)
|
||||
{
|
||||
BufferState.InputPointer = state.InputPointer;
|
||||
BufferState.InputEnd = state.InputEnd;
|
||||
BufferState.BitBuffer = state.BitBuffer;
|
||||
BufferState.BitsLeft = state.BitsLeft;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores bitstream state from state structure
|
||||
/// </summary>
|
||||
public BufferState RESTORE_BITS()
|
||||
{
|
||||
return new BufferState()
|
||||
{
|
||||
InputPointer = BufferState.InputPointer,
|
||||
InputEnd = BufferState.InputEnd,
|
||||
BitBuffer = BufferState.BitBuffer,
|
||||
BitsLeft = BufferState.BitsLeft,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure there are at least N bits in the bit buffer
|
||||
/// </summary>
|
||||
public void ENSURE_BITS(int nbits, BufferState state)
|
||||
{
|
||||
while (state.BitsLeft < nbits)
|
||||
{
|
||||
READ_BYTES(state);
|
||||
if (Error != Error.MSPACK_ERR_OK)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read from the input if the buffer is empty
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes from the input into the bit buffer
|
||||
/// </summary>
|
||||
public abstract void READ_BYTES(BufferState state);
|
||||
|
||||
/// <summary>
|
||||
/// Read an input stream and fill the buffer
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Inject data into the bit buffer
|
||||
/// </summary>
|
||||
public void INJECT_BITS_MSB(int bitdata, int nbits, BufferState state)
|
||||
{
|
||||
state.BitBuffer |= (uint)(bitdata << (BITBUF_WIDTH - nbits - state.BitsLeft));
|
||||
state.BitsLeft += nbits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer
|
||||
/// </summary>
|
||||
public long PEEK_BITS_MSB(int nbits, uint bit_buffer) => (bit_buffer >> (BITBUF_WIDTH - (nbits)));
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read multiple bits and put them in var
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Inject data into the bit buffer
|
||||
/// </summary>
|
||||
public void INJECT_BITS_LSB(int bitdata, int nbits, BufferState state)
|
||||
{
|
||||
state.BitBuffer |= (uint)(bitdata << state.BitsLeft);
|
||||
state.BitsLeft += nbits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer
|
||||
/// </summary>
|
||||
public long PEEK_BITS_LSB(int nbits, uint bit_buffer) => (bit_buffer & ((1 << (nbits)) - 1));
|
||||
|
||||
/// <summary>
|
||||
/// Extracts without removing N bits from the bit buffer using a bit mask
|
||||
/// </summary>
|
||||
public long PEEK_BITS_T_LSB(int nbits, uint bit_buffer) => bit_buffer & LSBBitMask[(nbits)];
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes N bits from the buffer and puts them in var using a bit mask
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Per compression error code for decoding failure
|
||||
/// </summary>
|
||||
public abstract Error HUFF_ERROR();
|
||||
|
||||
#endregion
|
||||
|
||||
#region MSB
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next huffman symbol from the input bitstream into var.
|
||||
/// Do not use this macro on a table unless build_decode_table() succeeded.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverse for a single symbol
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function was originally coded by David Tritscher.
|
||||
///
|
||||
/// It builds a fast huffman decoding table from
|
||||
/// a canonical huffman code lengths table.
|
||||
/// </summary>
|
||||
/// <param name="nsyms">total number of symbols in this huffman tree.</param>
|
||||
/// <param name="nbits">any symbols with a code length of nbits or less can be decoded in one lookup of the table.</param>
|
||||
/// <param name="length">A table to get code lengths from [0 to nsyms-1]</param>
|
||||
/// <param name="table">
|
||||
/// The table to fill up with decoded symbols and pointers.
|
||||
/// Should be ((1<<nbits) + (nsyms*2)) in length.
|
||||
/// </param>
|
||||
/// <returns>True for OK or false for error</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next huffman symbol from the input bitstream into var.
|
||||
/// Do not use this macro on a table unless build_decode_table() succeeded.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traverse for a single symbol
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function was originally coded by David Tritscher.
|
||||
///
|
||||
/// It builds a fast huffman decoding table from
|
||||
/// a canonical huffman code lengths table.
|
||||
/// </summary>
|
||||
/// <param name="nsyms">total number of symbols in this huffman tree.</param>
|
||||
/// <param name="nbits">any symbols with a code length of nbits or less can be decoded in one lookup of the table.</param>
|
||||
/// <param name="length">A table to get code lengths from [0 to nsyms-1]</param>
|
||||
/// <param name="table">
|
||||
/// The table to fill up with decoded symbols and pointers.
|
||||
/// Should be ((1<<nbits) + (nsyms*2)) in length.
|
||||
/// </param>
|
||||
/// <returns>True for OK or false for error</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
293
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.Decompress.cs
vendored
Normal file
293
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.Decompress.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely read bits from the buffer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely read a symbol from a Huffman tree
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single byte to the output stream
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadBits.cs
vendored
Normal file
64
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadBits.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadHuff.cs
vendored
Normal file
17
BurnOutSharp/External/libmspack/Compression/LZHKWAJ.ReadHuff.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Safely read bits from the buffer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely read a symbol from a Huffman tree
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a single byte to the output stream
|
||||
/// </summary>
|
||||
public void WRITE_BYTE(int pos)
|
||||
{
|
||||
if (System.Write(OutputFileHandle, Window, pos, 1) != 1)
|
||||
Error = Error.MSPACK_ERR_WRITE;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
1045
BurnOutSharp/External/libmspack/Compression/LZX.Decompress.cs
vendored
Normal file
1045
BurnOutSharp/External/libmspack/Compression/LZX.Decompress.cs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
34
BurnOutSharp/External/libmspack/Compression/LZX.ReadBits.cs
vendored
Normal file
34
BurnOutSharp/External/libmspack/Compression/LZX.ReadBits.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
BurnOutSharp/External/libmspack/Compression/LZX.ReadHuff.cs
vendored
Normal file
20
BurnOutSharp/External/libmspack/Compression/LZX.ReadHuff.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
|
||||
}
|
||||
}
|
||||
590
BurnOutSharp/External/libmspack/Compression/LZX.cs
vendored
590
BurnOutSharp/External/libmspack/Compression/LZX.cs
vendored
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="system">
|
||||
/// an mspack_system structure used to read from
|
||||
/// the input stream and write to the output
|
||||
/// stream, also to allocate and free memory.
|
||||
/// </param>
|
||||
/// <param name="input">an input stream with the LZX data.</param>
|
||||
/// <param name="output">an output stream to write the decoded data to.</param>
|
||||
/// <param name="window_bits">
|
||||
/// 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.</param>
|
||||
/// <param name="reset_interval">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <param name="input_buffer_size">
|
||||
/// the number of bytes to use as an input
|
||||
/// bitstream buffer.
|
||||
/// </param>
|
||||
/// <param name="output_length">
|
||||
/// 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.
|
||||
/// </param>
|
||||
/// <param name="is_delta">
|
||||
/// should be zero for all regular LZX data,
|
||||
/// non-zero for LZX DELTA encoded data.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// a pointer to an initialised LZXDStream structure, or null if
|
||||
/// there was not enough memory or parameters to the function were wrong.
|
||||
/// </returns>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="lzx">the LZX stream to apply this reference data to</param>
|
||||
/// <param name="system">
|
||||
/// an mspack_system implementation to use with the
|
||||
/// input param. Only read() will be called.
|
||||
/// </param>
|
||||
/// <param name="input"> an input file handle to read reference data using system.read().</param>
|
||||
/// <param name="length">
|
||||
/// the length of the reference data. Cannot be longer
|
||||
/// than the LZX window size.
|
||||
/// </param>
|
||||
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="o">LZX decompression state, as allocated by lzxd_init().</param>
|
||||
/// <param name="out_bytes">the number of bytes of data to decompress.</param>
|
||||
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
|
||||
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];
|
||||
/// <summary>
|
||||
/// Window size
|
||||
/// </summary>
|
||||
public uint WindowSize { get; set; }
|
||||
|
||||
// Easy answers
|
||||
if (lzx == null || (out_bytes < 0))
|
||||
return Error.MSPACK_ERR_ARGS;
|
||||
/// <summary>
|
||||
/// LZX DELTA reference data size
|
||||
/// </summary>
|
||||
public uint ReferenceDataSize { get; set; }
|
||||
|
||||
if (lzx.Error != Error.MSPACK_ERR_OK)
|
||||
return lzx.Error;
|
||||
/// <summary>
|
||||
/// Number of match_offset entries in table
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// Decompression offset within window
|
||||
/// </summary>
|
||||
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; }
|
||||
/// <summary>
|
||||
/// Current frame offset within in window
|
||||
/// </summary>
|
||||
public uint FramePosition { get; set; }
|
||||
|
||||
lzx.OutputPointer += leftover_bytes;
|
||||
lzx.Offset += leftover_bytes;
|
||||
out_bytes -= leftover_bytes;
|
||||
}
|
||||
/// <summary>
|
||||
/// The number of 32kb frames processed
|
||||
/// </summary>
|
||||
public uint Frame { get; set; }
|
||||
|
||||
if (out_bytes == 0)
|
||||
return Error.MSPACK_ERR_OK;
|
||||
/// <summary>
|
||||
/// Which frame do we reset the compressor?
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// For the LRU offset system
|
||||
/// </summary>
|
||||
public uint[] R { get; set; } = new uint[3];
|
||||
|
||||
uint end_frame = (uint)((lzx.Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
|
||||
/// <summary>
|
||||
/// Uncompressed length of this LZX block
|
||||
/// </summary>
|
||||
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++;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Uncompressed bytes still left to decode
|
||||
/// </summary>
|
||||
public int BlockRemaining { get; set; }
|
||||
|
||||
// Re-read the intel header and reset the huffman lengths
|
||||
lzx.ResetState();
|
||||
R = lzx.R;
|
||||
}
|
||||
/// <summary>
|
||||
/// Magic header value used for transform
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
/// <summary>
|
||||
/// Has intel E8 decoding started?
|
||||
/// </summary>
|
||||
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);
|
||||
/// <summary>
|
||||
/// Type of the current block
|
||||
/// </summary>
|
||||
public LZXBlockType BlockType { get; set; }
|
||||
|
||||
// if (i != 0)
|
||||
// {
|
||||
// i = (int)lzx.READ_BITS_MSB(16, state);
|
||||
// j = (int)lzx.READ_BITS_MSB(16, state);
|
||||
// }
|
||||
/// <summary>
|
||||
/// Have we started decoding at all yet?
|
||||
/// </summary>
|
||||
public byte HeaderRead { get; set; }
|
||||
|
||||
// lzx.IntelFileSize = (i << 16) | j;
|
||||
// lzx.HeaderRead = 1;
|
||||
//}
|
||||
/// <summary>
|
||||
/// Does stream follow LZX DELTA spec?
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
/// <summary>
|
||||
/// Is the output pointer referring to E8?
|
||||
/// </summary>
|
||||
public bool OutputIsE8 { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes actually output
|
||||
/// </summary>
|
||||
public long Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overall decompressed length of stream
|
||||
/// </summary>
|
||||
public long Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Decoding window
|
||||
/// </summary>
|
||||
public byte[] Window { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Window size
|
||||
/// </summary>
|
||||
public uint WindowSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// LZX DELTA reference data size
|
||||
/// </summary>
|
||||
public uint ReferenceDataSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of match_offset entries in table
|
||||
/// </summary>
|
||||
public uint NumOffsets { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Decompression offset within window
|
||||
/// </summary>
|
||||
public int WindowPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current frame offset within in window
|
||||
/// </summary>
|
||||
public uint FramePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of 32kb frames processed
|
||||
/// </summary>
|
||||
public uint Frame { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Which frame do we reset the compressor?
|
||||
/// </summary>
|
||||
public uint ResetInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For the LRU offset system
|
||||
/// </summary>
|
||||
public uint[] R { get; set; } = new uint[3];
|
||||
|
||||
/// <summary>
|
||||
/// Uncompressed length of this LZX block
|
||||
/// </summary>
|
||||
public int BlockLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uncompressed bytes still left to decode
|
||||
/// </summary>
|
||||
public int BlockRemaining { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Magic header value used for transform
|
||||
/// </summary>
|
||||
public int IntelFileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has intel E8 decoding started?
|
||||
/// </summary>
|
||||
public bool IntelStarted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the current block
|
||||
/// </summary>
|
||||
public LZXBlockType BlockType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Have we started decoding at all yet?
|
||||
/// </summary>
|
||||
public byte HeaderRead { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does stream follow LZX DELTA spec?
|
||||
/// </summary>
|
||||
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];
|
||||
|
||||
/// <summary>
|
||||
/// Is the output pointer referring to E8?
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
617
BurnOutSharp/External/libmspack/Compression/MSZIP.Decompress.cs
vendored
Normal file
617
BurnOutSharp/External/libmspack/Compression/MSZIP.Decompress.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
|
||||
/// like mszipd_decompress(), but doesn't take an out_bytes parameter
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A clean implementation of RFC 1951 / inflate
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
BurnOutSharp/External/libmspack/Compression/MSZIP.ReadBits.cs
vendored
Normal file
27
BurnOutSharp/External/libmspack/Compression/MSZIP.ReadBits.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void READ_BYTES()
|
||||
{
|
||||
READ_IF_NEEDED();
|
||||
if (Error != Error.MSPACK_ERR_OK)
|
||||
return;
|
||||
|
||||
INJECT_BITS_LSB(InputBuffer[InputPointer++], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
BurnOutSharp/External/libmspack/Compression/MSZIP.ReadHuff.cs
vendored
Normal file
20
BurnOutSharp/External/libmspack/Compression/MSZIP.ReadHuff.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
|
||||
}
|
||||
}
|
||||
631
BurnOutSharp/External/libmspack/Compression/MSZIP.cs
vendored
631
BurnOutSharp/External/libmspack/Compression/MSZIP.cs
vendored
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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];
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
|
||||
/// like mszipd_decompress(), but doesn't take an out_bytes parameter
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A clean implementation of RFC 1951 / inflate
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// 32kb history window
|
||||
/// </summary>
|
||||
public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE];
|
||||
|
||||
/// <summary>
|
||||
/// Offset within window
|
||||
/// </summary>
|
||||
public uint WindowPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// inflate() will call this whenever the window should be emptied.
|
||||
/// </summary>
|
||||
public Func<MSZIPDStream, uint, Error> 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
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
546
BurnOutSharp/External/libmspack/Compression/QTM.Decompress.cs
vendored
Normal file
546
BurnOutSharp/External/libmspack/Compression/QTM.Decompress.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
BurnOutSharp/External/libmspack/Compression/QTM.ReadBits.cs
vendored
Normal file
34
BurnOutSharp/External/libmspack/Compression/QTM.ReadBits.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
BurnOutSharp/External/libmspack/Compression/QTM.ReadHuff.cs
vendored
Normal file
22
BurnOutSharp/External/libmspack/Compression/QTM.ReadHuff.cs
vendored
Normal file
@@ -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
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
580
BurnOutSharp/External/libmspack/Compression/QTM.cs
vendored
580
BurnOutSharp/External/libmspack/Compression/QTM.cs
vendored
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// Decompression offset within window
|
||||
/// </summary>
|
||||
public uint WindowPosition { get; set; }
|
||||
|
||||
uint bit_buffer;
|
||||
int bits_left;
|
||||
/// <summary>
|
||||
/// Bytes remaining for current frame
|
||||
/// </summary>
|
||||
public uint FrameTODO { get; set; }
|
||||
|
||||
// Easy answers
|
||||
if (qtm == null || (out_bytes < 0))
|
||||
return Error.MSPACK_ERR_ARGS;
|
||||
/// <summary>
|
||||
/// High: arith coding state
|
||||
/// </summary>
|
||||
public ushort High { get; set; }
|
||||
|
||||
if (qtm.Error != Error.MSPACK_ERR_OK)
|
||||
return qtm.Error;
|
||||
/// <summary>
|
||||
/// Low: arith coding state
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// Current: arith coding state
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// Have we started decoding a new frame?
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// For literals from 0 to 63 (selector = 0)
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// For literals from 64 to 127 (selector = 1)
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
/// <summary>
|
||||
/// For literals from 128 to 191 (selector = 2)
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// For literals from 129 to 255 (selector = 3)
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// For match with fixed length of 3 bytes
|
||||
/// </summary>
|
||||
public QTMDModel Model4 { get; set; }
|
||||
|
||||
case 2:
|
||||
mdl = qtm.Model2;
|
||||
break;
|
||||
/// <summary>
|
||||
/// For match with fixed length of 4 bytes
|
||||
/// </summary>
|
||||
public QTMDModel Model5 { get; set; }
|
||||
|
||||
case 3:
|
||||
default:
|
||||
mdl = qtm.Model3;
|
||||
break;
|
||||
}
|
||||
/// <summary>
|
||||
/// For variable length match, encoded with model6len model
|
||||
/// </summary>
|
||||
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;
|
||||
/// <summary>
|
||||
/// Selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6)
|
||||
/// </summary>
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Decoding window
|
||||
/// </summary>
|
||||
public byte[] Window { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Window size
|
||||
/// </summary>
|
||||
public uint WindowSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Decompression offset within window
|
||||
/// </summary>
|
||||
public uint WindowPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Bytes remaining for current frame
|
||||
/// </summary>
|
||||
public uint FrameTODO { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// High: arith coding state
|
||||
/// </summary>
|
||||
public ushort High { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Low: arith coding state
|
||||
/// </summary>
|
||||
public ushort Low { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Current: arith coding state
|
||||
/// </summary>
|
||||
public ushort Current { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Have we started decoding a new frame?
|
||||
/// </summary>
|
||||
public byte HeaderRead { get; set; }
|
||||
|
||||
// Four literal models, each representing 64 symbols
|
||||
|
||||
/// <summary>
|
||||
/// For literals from 0 to 63 (selector = 0)
|
||||
/// </summary>
|
||||
public QTMDModel Model0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For literals from 64 to 127 (selector = 1)
|
||||
/// </summary>
|
||||
public QTMDModel Model1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For literals from 128 to 191 (selector = 2)
|
||||
/// </summary>
|
||||
public QTMDModel Model2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For literals from 129 to 255 (selector = 3)
|
||||
/// </summary>
|
||||
public QTMDModel Model3 { get; set; }
|
||||
|
||||
// Three match models.
|
||||
|
||||
/// <summary>
|
||||
/// For match with fixed length of 3 bytes
|
||||
/// </summary>
|
||||
public QTMDModel Model4 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For match with fixed length of 4 bytes
|
||||
/// </summary>
|
||||
public QTMDModel Model5 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For variable length match, encoded with model6len model
|
||||
/// </summary>
|
||||
public QTMDModel Model6 { get; set; }
|
||||
|
||||
public QTMDModel Model6Len { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6)
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Error HUFF_ERROR() => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user