1492 lines
49 KiB
C#
1492 lines
49 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
// eww
|
|
using UBYTE = System.Byte; /* 8 bits exactly */
|
|
using UWORD = System.UInt16; /* 16 bits (or more) */
|
|
using ULONG = System.UInt32; /* 32 bits (or more) */
|
|
using LONG = System.Int32; /* 32 bits (or more) */
|
|
using System.IO;
|
|
|
|
namespace UniversalEditor.Compression.Modules.XMemLZX
|
|
{
|
|
public class XMemLZXCompressionModule : CompressionModule
|
|
{
|
|
internal sealed class Lzx
|
|
{
|
|
/* return codes */
|
|
public const int DECR_OK = 0;
|
|
public const int DECR_DATAFORMAT = 1;
|
|
public const int DECR_NOMEMORY = 3;
|
|
|
|
/* some constants defined by the LZX specification */
|
|
private const int LZX_MIN_MATCH = 2;
|
|
private const int LZX_MAX_MATCH = 257;
|
|
private const int LZX_NUM_CHARS = 256;
|
|
private const int LZX_PRETREE_NUM_ELEMENTS = 20;
|
|
private const int LZX_ALIGNED_NUM_ELEMENTS = 8; /* aligned offset tree #elements */
|
|
private const int LZX_NUM_PRIMARY_LENGTHS = 7; /* this one missing from spec! */
|
|
private const int LZX_NUM_SECONDARY_LENGTHS = 249; /* length tree #elements */
|
|
|
|
/* LZX huffman defines: tweak tablebits as desired */
|
|
private const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
|
|
private const int LZX_PRETREE_TABLEBITS = 6;
|
|
private const int LZX_MAINTREE_MAXSYMBOLS = (LZX_NUM_CHARS + 50 * 8);
|
|
private const int LZX_MAINTREE_TABLEBITS = 12;
|
|
private const int LZX_LENGTH_MAXSYMBOLS = (LZX_NUM_SECONDARY_LENGTHS + 1);
|
|
private const int LZX_LENGTH_TABLEBITS = 12;
|
|
private const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
|
|
private const int LZX_ALIGNED_TABLEBITS = 7;
|
|
|
|
private const int LZX_LENTABLE_SAFETY = 64; /* we allow length table decoding overruns */
|
|
|
|
internal sealed class LZXstate
|
|
{
|
|
public LZXstate() { }
|
|
|
|
public UBYTE[] window; /* the actual decoding window */
|
|
public ULONG window_size; /* window size (32Kb through 2Mb) */
|
|
public ULONG actual_size; /* window size when it was first allocated */
|
|
public ULONG window_posn; /* current offset within the window */
|
|
public ULONG R0, R1, R2; /* for the LRU offset system */
|
|
public UWORD main_elements; /* number of main tree elements */
|
|
public int header_read; /* have we started decoding at all yet? */
|
|
public LZX.LZXBlockType block_type; /* type of this block */
|
|
public ULONG block_length; /* uncompressed length of this block */
|
|
public ULONG block_remaining; /* uncompressed bytes still left to decode */
|
|
public ULONG frames_read; /* the number of CFDATA blocks processed */
|
|
public LONG intel_filesize; /* magic header value used for transform */
|
|
public LONG intel_curpos; /* current offset in transform space */
|
|
public int intel_started; /* have we seen any translatable data yet? */
|
|
|
|
/* LZX_DECLARE_TABLE(PRETREE); */
|
|
public UWORD[] PRETREE_table = new UWORD[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS << 1)];
|
|
public UBYTE[] PRETREE_len = new UBYTE[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
|
|
|
|
/* LZX_DECLARE_TABLE(MAINTREE); */
|
|
public UWORD[] MAINTREE_table = new UWORD[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS << 1)];
|
|
public UBYTE[] MAINTREE_len = new UBYTE[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
|
|
|
|
/* LZX_DECLARE_TABLE(LENGTH); */
|
|
public UWORD[] LENGTH_table = new UWORD[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS << 1)];
|
|
public UBYTE[] LENGTH_len = new UBYTE[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
|
|
|
|
/*LZX_DECLARE_TABLE(ALIGNED); */
|
|
public UWORD[] ALIGNED_table = new UWORD[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS << 1)];
|
|
public UBYTE[] ALIGNED_len = new UBYTE[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
|
|
};
|
|
|
|
/* LZX decruncher */
|
|
|
|
/* Microsoft's LZX document 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.
|
|
*/
|
|
|
|
|
|
/* LZX uses what it calls 'position slots' to represent match offsets.
|
|
* What this means is that a small 'position slot' number and a small
|
|
* offset from that slot are encoded instead of one large offset for
|
|
* every match.
|
|
* - position_base is an index to the position slot bases
|
|
* - extra_bits states how many bits of offset-from-base data is needed.
|
|
*/
|
|
private static UBYTE[] extra_bits = new UBYTE[] {
|
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
|
|
15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
|
|
17, 17, 17
|
|
};
|
|
|
|
private static ULONG[] position_base = new ULONG[] {
|
|
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192,
|
|
256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
|
|
65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
|
|
1835008, 1966080, 2097152
|
|
};
|
|
|
|
public static LZXstate LZXinit(int window)
|
|
{
|
|
LZXstate pState = null;
|
|
ULONG wndsize = (ULONG)(1 << window);
|
|
int i, posn_slots;
|
|
|
|
/* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
|
|
/* if a previously allocated window is big enough, keep it */
|
|
if (window < 15 || window > 21) throw new ArgumentOutOfRangeException("window must be between 15 and 21");
|
|
|
|
/* allocate state and associated window */
|
|
pState = new LZXstate();
|
|
pState.window = new UBYTE[wndsize];
|
|
pState.actual_size = wndsize;
|
|
pState.window_size = wndsize;
|
|
|
|
/* calculate required position slots */
|
|
if (window == 20) posn_slots = 42;
|
|
else if (window == 21) posn_slots = 50;
|
|
else posn_slots = window << 1;
|
|
|
|
/** alternatively **/
|
|
/* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
|
|
|
|
/* initialize other state */
|
|
pState.R0 = pState.R1 = pState.R2 = 1;
|
|
pState.main_elements = (UWORD)(LZX_NUM_CHARS + (posn_slots << 3));
|
|
pState.header_read = 0;
|
|
pState.frames_read = 0;
|
|
pState.block_remaining = 0;
|
|
pState.block_type = LZX.LZXBlockType.Invalid;
|
|
pState.intel_curpos = 0;
|
|
pState.intel_started = 0;
|
|
pState.window_posn = 0;
|
|
|
|
/* initialise tables to 0 (because deltas will be applied to them) */
|
|
for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) pState.MAINTREE_len[i] = 0;
|
|
for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) pState.LENGTH_len[i] = 0;
|
|
|
|
return pState;
|
|
}
|
|
|
|
public static void LZXteardown(LZXstate pState)
|
|
{
|
|
|
|
}
|
|
|
|
public static int LZXreset(LZXstate pState)
|
|
{
|
|
int i;
|
|
|
|
pState.R0 = pState.R1 = pState.R2 = 1;
|
|
pState.header_read = 0;
|
|
pState.frames_read = 0;
|
|
pState.block_remaining = 0;
|
|
pState.block_type = LZX.LZXBlockType.Invalid;
|
|
pState.intel_curpos = 0;
|
|
pState.intel_started = 0;
|
|
pState.window_posn = 0;
|
|
|
|
for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) pState.MAINTREE_len[i] = 0;
|
|
for (i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) pState.LENGTH_len[i] = 0;
|
|
|
|
return DECR_OK;
|
|
}
|
|
|
|
/* Bitstream reading macros:
|
|
*
|
|
* INIT_BITSTREAM should be used first to set up the system
|
|
* READ_BITS(var,n) takes N bits from the buffer and puts them in var
|
|
*
|
|
* ENSURE_BITS(n) ensures there are at least N bits in the bit buffer
|
|
* PEEK_BITS(n) extracts (without removing) N bits from the bit buffer
|
|
* REMOVE_BITS(n) removes N bits from the bit buffer
|
|
*
|
|
* These bit access routines work by using the area beyond the MSB and the
|
|
* LSB as a free source of zeroes. This avoids having to mask any bits.
|
|
* So we have to know the bit width of the bitbuffer variable. This is
|
|
* sizeof(ULONG) * 8, also defined as ULONG_BITS
|
|
*/
|
|
|
|
/* number of bits in ULONG. Note: This must be at multiple of 16, and at
|
|
* least 32 for the bitbuffer code to work (ie, it must be able to ensure
|
|
* up to 17 bits - that's adding 16 bits when there's one bit left, or
|
|
* adding 32 bits when there are no bits left. The code should work fine
|
|
* for machines where ULONG >= 32 bits.
|
|
*/
|
|
private static ULONG ULONG_BITS()
|
|
{
|
|
return sizeof(ULONG) << 3;
|
|
}
|
|
|
|
private static void INIT_BITSTREAM(ref int bitsleft, ref ULONG bitbuf)
|
|
{
|
|
bitsleft = 0;
|
|
bitbuf = 0;
|
|
}
|
|
|
|
private static void ENSURE_BITS(ref int bitsleft, ref ULONG bitbuf, ref UBYTE[] ipbuf,
|
|
ref long inpos, int n)
|
|
{
|
|
while (bitsleft < n)
|
|
{
|
|
bitbuf |= (ULONG)((ipbuf[inpos + 1] << 8) | ipbuf[inpos + 0]) << (int)(ULONG_BITS() - 16 - bitsleft);
|
|
bitsleft += 16; inpos += 2;
|
|
}
|
|
}
|
|
|
|
private static ULONG PEEK_BITS(ref ULONG bitbuf, int n)
|
|
{
|
|
return bitbuf >> (int)(ULONG_BITS() - n);
|
|
}
|
|
|
|
private static void REMOVE_BITS(ref int bitsleft, ref ULONG bitbuf, int n)
|
|
{
|
|
bitbuf <<= n;
|
|
bitsleft -= n;
|
|
}
|
|
|
|
private static void READ_BITS(ref ULONG v, int n, ref ULONG bitbuf, ref int bitsleft,
|
|
ref UBYTE[] ipbuf, ref long inpos)
|
|
{
|
|
ENSURE_BITS(ref bitsleft, ref bitbuf, ref ipbuf, ref inpos, n);
|
|
v = PEEK_BITS(ref bitbuf, n);
|
|
REMOVE_BITS(ref bitsleft, ref bitbuf, n);
|
|
}
|
|
|
|
/* Huffman macros */
|
|
|
|
/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths.
|
|
* In reality, it just calls make_decode_table() with the appropriate
|
|
* values - they're all fixed by some #defines anyway, so there's no point
|
|
* writing each call out in full by hand.
|
|
*/
|
|
private static bool BUILD_TABLE(ref UWORD[] tbl, ref UBYTE[] lentbl, int tablebits, int maxsymbols)
|
|
{
|
|
return make_decode_table((uint)maxsymbols, (uint)tablebits, ref lentbl, ref tbl) == 0;
|
|
}
|
|
|
|
/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
|
|
* bitstream using the stated table and puts it in var.
|
|
*/
|
|
private static bool READ_HUFFSYM(ref UWORD[] tbl, ref UWORD[] hufftbl, ref LONG bitsleft, ref ULONG bitbuf,
|
|
ref UBYTE[] ipbuf, ref long inpos, ref ULONG i, int tablebits, int maxsymbols, ref UBYTE[] tablelen,
|
|
ref ULONG j, ref int var)
|
|
{
|
|
ENSURE_BITS(ref bitsleft, ref bitbuf, ref ipbuf, ref inpos, 16);
|
|
hufftbl = tbl;
|
|
|
|
if ((i = hufftbl[PEEK_BITS(ref bitbuf, tablebits)]) >= maxsymbols)
|
|
{
|
|
j = (ULONG)(1 << (int)(ULONG_BITS() - tablebits));
|
|
do
|
|
{
|
|
j >>= 1; i <<= 1; i |= Convert.ToBoolean(bitbuf & j) ? (ULONG)1 : 0;
|
|
if (!Convert.ToBoolean(j)) { return false; /* DECR_ILLEGALDATA */ }
|
|
} while ((i = hufftbl[i]) >= maxsymbols);
|
|
}
|
|
|
|
j = tablelen[var = (int)i];
|
|
REMOVE_BITS(ref bitsleft, ref bitbuf, (int)j);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
|
|
* first to last in the given table. The code lengths are stored in their
|
|
* own special LZX way.
|
|
*/
|
|
private static bool READ_LENGTHS(ULONG first, ULONG last, lzx_bits lb, ref ULONG bitbuf,
|
|
ref LONG bitsleft, ref UBYTE[] ip, ref long inpos, LZXstate pState, ref UBYTE[] tablelen)
|
|
{
|
|
lb.bb = bitbuf;
|
|
lb.bl = bitsleft;
|
|
lb.ip = ip;
|
|
lb.ippos = inpos;
|
|
|
|
if (lzx_read_lens(pState, ref tablelen, first, last, lb) != 0)
|
|
return false;
|
|
|
|
bitbuf = lb.bb;
|
|
bitsleft = lb.bl;
|
|
inpos = lb.ippos;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* make_decode_table(nsyms, nbits, length[], table[])
|
|
*
|
|
* This function was coded by David Tritscher. It builds a fast huffman
|
|
* decoding table out of just a canonical huffman code lengths table.
|
|
*
|
|
* nsyms = total number of symbols in this huffman tree.
|
|
* nbits = any symbols with a code length of nbits or less can be decoded
|
|
* in one lookup of the table.
|
|
* length = A table to get code lengths from [0 to syms-1]
|
|
* table = The table to fill up with decoded symbols and pointers.
|
|
*
|
|
* Returns 0 for OK or 1 for error
|
|
*/
|
|
private static int make_decode_table(ULONG nsyms, ULONG nbits, ref UBYTE[] length, ref UWORD[] table)
|
|
{
|
|
UWORD sym;
|
|
ULONG leaf;
|
|
UBYTE bit_num = 1;
|
|
ULONG fill;
|
|
ULONG pos = 0; /* the current position in the decode table */
|
|
ULONG table_mask = (ULONG)(1 << (int)nbits);
|
|
ULONG bit_mask = table_mask >> 1; /* don't do 0 length codes */
|
|
ULONG next_symbol = bit_mask; /* base of allocation for long codes */
|
|
|
|
/* fill entries for codes short enough for a direct mapping */
|
|
while (bit_num <= nbits)
|
|
{
|
|
for (sym = 0; sym < nsyms; sym++)
|
|
{
|
|
if (length[sym] == bit_num)
|
|
{
|
|
leaf = pos;
|
|
|
|
if ((pos += bit_mask) > table_mask) return 1; /* table overrun */
|
|
|
|
/* fill all possible lookups of this symbol with the symbol itself */
|
|
fill = bit_mask;
|
|
while (fill-- > 0) table[leaf++] = sym;
|
|
}
|
|
}
|
|
|
|
bit_mask >>= 1;
|
|
bit_num++;
|
|
}
|
|
|
|
/* if there are any codes longer than nbits */
|
|
if (pos != table_mask)
|
|
{
|
|
/* clear the remainder of the table */
|
|
for (sym = (UWORD)pos; sym < table_mask; sym++) table[sym] = 0;
|
|
|
|
/* give ourselves room for codes to grow by up to 16 more bits */
|
|
pos <<= 16;
|
|
table_mask <<= 16;
|
|
bit_mask = 1 << 15;
|
|
|
|
while (bit_num <= 16)
|
|
{
|
|
for (sym = 0; sym < nsyms; sym++)
|
|
{
|
|
if (length[sym] == bit_num)
|
|
{
|
|
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] == 0)
|
|
{
|
|
table[(next_symbol << 1)] = 0;
|
|
table[(next_symbol << 1) + 1] = 0;
|
|
table[leaf] = (UWORD)next_symbol++;
|
|
}
|
|
/* follow the path and select either left or right for next bit */
|
|
leaf = (ULONG)(table[leaf] << 1);
|
|
if (Convert.ToBoolean((pos >> (int)(15 - fill)) & 1)) leaf++;
|
|
}
|
|
table[leaf] = sym;
|
|
|
|
if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
|
|
}
|
|
}
|
|
bit_mask >>= 1;
|
|
bit_num++;
|
|
}
|
|
}
|
|
|
|
/* full table? */
|
|
if (pos == table_mask) return 0;
|
|
|
|
/* either erroneous table, or all elements are 0 - let's find out. */
|
|
for (sym = 0; sym < nsyms; sym++) if (Convert.ToBoolean(length[sym])) return 1;
|
|
return 0;
|
|
}
|
|
|
|
private sealed class lzx_bits
|
|
{
|
|
public ULONG bb;
|
|
public int bl;
|
|
public UBYTE[] ip;
|
|
public long ippos;
|
|
};
|
|
|
|
private static int lzx_read_lens(LZXstate pState, ref UBYTE[] lens, ULONG first, ULONG last, lzx_bits lb)
|
|
{
|
|
ULONG i = 0, j = 0, x = 0, y = 0;
|
|
int z = 0;
|
|
|
|
ULONG bitbuf = lb.bb;
|
|
int bitsleft = lb.bl;
|
|
long inpos = lb.ippos;
|
|
UWORD[] hufftbl = null;
|
|
bool hr;
|
|
|
|
for (x = 0; x < 20; x++)
|
|
{
|
|
READ_BITS(ref y, 4, ref bitbuf, ref bitsleft, ref lb.ip, ref inpos);
|
|
pState.PRETREE_len[x] = (byte)y;
|
|
}
|
|
if (!BUILD_TABLE(ref pState.PRETREE_table, ref pState.PRETREE_len,
|
|
LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS))
|
|
throw new InvalidOperationException("BUILD_TABLE failed");
|
|
|
|
for (x = first; x < last;)
|
|
{
|
|
hr = READ_HUFFSYM(ref pState.PRETREE_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref lb.ip, ref inpos, ref i, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS,
|
|
ref pState.PRETREE_len, ref j, ref z);
|
|
if (!hr)
|
|
throw new InvalidOperationException("READ_HUFFSYM failed");
|
|
|
|
if (z == 17)
|
|
{
|
|
READ_BITS(ref y, 4, ref bitbuf, ref bitsleft, ref lb.ip, ref inpos); y += 4;
|
|
while (Convert.ToBoolean(y--)) lens[x++] = 0;
|
|
}
|
|
else if (z == 18)
|
|
{
|
|
READ_BITS(ref y, 5, ref bitbuf, ref bitsleft, ref lb.ip, ref inpos); y += 20;
|
|
while (Convert.ToBoolean(y--)) lens[x++] = 0;
|
|
}
|
|
else if (z == 19)
|
|
{
|
|
READ_BITS(ref y, 1, ref bitbuf, ref bitsleft, ref lb.ip, ref inpos); y += 4;
|
|
hr = READ_HUFFSYM(ref pState.PRETREE_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref lb.ip, ref inpos, ref i, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS,
|
|
ref pState.PRETREE_len, ref j, ref z);
|
|
if (!hr)
|
|
throw new InvalidOperationException("READ_HUFFSYM failed");
|
|
|
|
z = lens[x] - z; if (z < 0) z += 17;
|
|
while (Convert.ToBoolean(y--)) lens[x++] = (byte)z;
|
|
}
|
|
else
|
|
{
|
|
z = lens[x] - z; if (z < 0) z += 17;
|
|
lens[x++] = (byte)z;
|
|
}
|
|
}
|
|
|
|
lb.bb = bitbuf;
|
|
lb.bl = bitsleft;
|
|
lb.ippos = inpos;
|
|
return 0;
|
|
}
|
|
|
|
public static int LZXdecompress(LZXstate pState, ref UBYTE[] ip, long inpos, ref UBYTE[] op,
|
|
ulong outpos, int inlen, int outlen)
|
|
{
|
|
long endinp = inpos + inlen;
|
|
UBYTE[] window = pState.window;
|
|
long runsrc = 0;
|
|
long rundest = 0;
|
|
UWORD[] hufftbl = null; /* used in READ_HUFFSYM macro as chosen decoding table */
|
|
|
|
ULONG window_posn = pState.window_posn;
|
|
ULONG window_size = pState.window_size;
|
|
ULONG R0 = pState.R0;
|
|
ULONG R1 = pState.R1;
|
|
ULONG R2 = pState.R2;
|
|
|
|
ULONG bitbuf = 0;
|
|
int bitsleft = 0;
|
|
ULONG match_offset, i = 0, j = 0, k = 0; /* ijk used in READ_HUFFSYM macro */
|
|
lzx_bits lb = new lzx_bits(); /* used in READ_LENGTHS macro */
|
|
bool hr;
|
|
|
|
int togo = outlen, this_run, main_element = 0, aligned_bits = 0;
|
|
int match_length, length_footer = 0, extra;
|
|
uint verbatim_bits = 0;
|
|
|
|
INIT_BITSTREAM(ref bitsleft, ref bitbuf);
|
|
|
|
/* read header if necessary */
|
|
if (!Convert.ToBoolean(pState.header_read))
|
|
{
|
|
i = j = 0;
|
|
READ_BITS(ref k, 1, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
if (Convert.ToBoolean(k))
|
|
{
|
|
READ_BITS(ref i, 16, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
READ_BITS(ref j, 16, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
}
|
|
pState.intel_filesize = (LONG)((i << 16) | j); /* or 0 if not encoded */
|
|
pState.header_read = 1;
|
|
}
|
|
|
|
/* main decoding loop */
|
|
while (togo > 0)
|
|
{
|
|
/* last block finished, new block expected */
|
|
if (pState.block_remaining == 0)
|
|
{
|
|
if (pState.block_type == LZX.LZXBlockType.Uncompressed)
|
|
{
|
|
if (Convert.ToBoolean(pState.block_length & 1)) inpos++; /* realign bitstream to word */
|
|
INIT_BITSTREAM(ref bitsleft, ref bitbuf);
|
|
}
|
|
|
|
ULONG ulBlockType = (ULONG)pState.block_type;
|
|
READ_BITS(ref ulBlockType, 3, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
pState.block_type = (LZX.LZXBlockType)ulBlockType;
|
|
|
|
READ_BITS(ref i, 16, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
READ_BITS(ref j, 8, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
pState.block_remaining = pState.block_length = (i << 8) | j;
|
|
|
|
switch (pState.block_type)
|
|
{
|
|
case LZX.LZXBlockType.Aligned:
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
READ_BITS(ref j, 3, ref bitbuf, ref bitsleft, ref ip, ref inpos);
|
|
pState.ALIGNED_len[i] = (byte)j;
|
|
}
|
|
if (!BUILD_TABLE(ref pState.ALIGNED_table, ref pState.ALIGNED_len,
|
|
LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS))
|
|
throw new InvalidOperationException("BUILD_TABLE failed");
|
|
/* rest of aligned header is same as verbatim */
|
|
goto case LZX.LZXBlockType.Verbatim;
|
|
|
|
case LZX.LZXBlockType.Verbatim:
|
|
if (!READ_LENGTHS(0, 256, lb, ref bitbuf, ref bitsleft, ref ip, ref inpos,
|
|
pState, ref pState.MAINTREE_len))
|
|
throw new InvalidOperationException("READ_LENGTHS failed");
|
|
if (!READ_LENGTHS(256, pState.main_elements, lb, ref bitbuf, ref bitsleft,
|
|
ref ip, ref inpos, pState, ref pState.MAINTREE_len))
|
|
throw new InvalidOperationException("READ_LENGTHS failed");
|
|
if (!BUILD_TABLE(ref pState.MAINTREE_table, ref pState.MAINTREE_len,
|
|
LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS))
|
|
throw new InvalidOperationException("BUILD_TABLE failed");
|
|
if (pState.MAINTREE_len[0xE8] != 0) pState.intel_started = 1;
|
|
|
|
if (!READ_LENGTHS(0, LZX_NUM_SECONDARY_LENGTHS, lb, ref bitbuf, ref bitsleft,
|
|
ref ip, ref inpos, pState, ref pState.LENGTH_len))
|
|
throw new InvalidOperationException("READ_LENGTHS failed");
|
|
if (!BUILD_TABLE(ref pState.LENGTH_table, ref pState.LENGTH_len,
|
|
LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS))
|
|
throw new InvalidOperationException("BUILD_TABLE failed");
|
|
break;
|
|
|
|
case LZX.LZXBlockType.Uncompressed:
|
|
pState.intel_started = 1; /* because we can't assume otherwise */
|
|
ENSURE_BITS(ref bitsleft, ref bitbuf, ref ip, ref inpos, 16); /* get up to 16 pad bits into the buffer */
|
|
if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */
|
|
R0 = (ULONG)(ip[inpos + 0] | (ip[inpos + 1] << 8) | (ip[inpos + 2] << 16) | (ip[inpos + 3] << 24)); inpos += 4;
|
|
R1 = (ULONG)(ip[inpos + 0] | (ip[inpos + 1] << 8) | (ip[inpos + 2] << 16) | (ip[inpos + 3] << 24)); inpos += 4;
|
|
R2 = (ULONG)(ip[inpos + 0] | (ip[inpos + 1] << 8) | (ip[inpos + 2] << 16) | (ip[inpos + 3] << 24)); inpos += 4;
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException("unknown block type " + pState.block_type);
|
|
}
|
|
}
|
|
|
|
/* buffer exhaustion check */
|
|
if (inpos > endinp)
|
|
{
|
|
/* it's possible to have a file where the next run is less than
|
|
* 16 bits in size. In this case, the READ_HUFFSYM() macro used
|
|
* in building the tables will exhaust the buffer, so we should
|
|
* allow for this, but not allow those accidentally read bits to
|
|
* be used (so we check that there are at least 16 bits
|
|
* remaining - in this boundary case they aren't really part of
|
|
* the compressed data)
|
|
*/
|
|
if (inpos > (endinp + 2) || bitsleft < 16)
|
|
throw new InvalidOperationException("inpos > (endinp + 2) || bitsleft < 16");
|
|
}
|
|
|
|
while ((this_run = (LONG)pState.block_remaining) > 0 && togo > 0)
|
|
{
|
|
if (this_run > togo) this_run = togo;
|
|
togo -= this_run;
|
|
pState.block_remaining -= (ULONG)this_run;
|
|
|
|
/* apply 2^x-1 mask */
|
|
window_posn &= window_size - 1;
|
|
/* runs can't straddle the window wraparound */
|
|
if ((window_posn + this_run) > window_size)
|
|
return DECR_DATAFORMAT;
|
|
|
|
switch (pState.block_type)
|
|
{
|
|
|
|
case LZX.LZXBlockType.Verbatim:
|
|
while (this_run > 0)
|
|
{
|
|
hr = READ_HUFFSYM(ref pState.MAINTREE_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_MAINTREE_TABLEBITS,
|
|
LZX_MAINTREE_MAXSYMBOLS, ref pState.MAINTREE_len, ref j, ref main_element);
|
|
if (!hr)
|
|
throw new InvalidOperationException("READ_HUFFSYM failed");
|
|
|
|
if (main_element < LZX_NUM_CHARS)
|
|
{
|
|
/* literal: 0 to LZX_NUM_CHARS-1 */
|
|
window[window_posn++] = (UBYTE)main_element;
|
|
this_run--;
|
|
}
|
|
else
|
|
{
|
|
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
|
|
main_element -= LZX_NUM_CHARS;
|
|
|
|
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
|
|
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
|
|
{
|
|
hr = READ_HUFFSYM(ref pState.LENGTH_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_LENGTH_TABLEBITS,
|
|
LZX_LENGTH_MAXSYMBOLS, ref pState.LENGTH_len, ref j, ref length_footer);
|
|
if (!hr)
|
|
throw new InvalidOperationException("READ_HUFFSYM failed");
|
|
match_length += length_footer;
|
|
}
|
|
match_length += LZX_MIN_MATCH;
|
|
|
|
match_offset = (ULONG)main_element >> 3;
|
|
|
|
if (match_offset > 2)
|
|
{
|
|
/* not repeated offset */
|
|
if (match_offset != 3)
|
|
{
|
|
extra = extra_bits[match_offset];
|
|
READ_BITS(ref verbatim_bits, extra, ref bitbuf, ref bitsleft,
|
|
ref ip, ref inpos);
|
|
match_offset = position_base[match_offset] - 2 + verbatim_bits;
|
|
}
|
|
else
|
|
{
|
|
match_offset = 1;
|
|
}
|
|
|
|
/* update repeated offset LRU queue */
|
|
R2 = R1; R1 = R0; R0 = match_offset;
|
|
}
|
|
else if (match_offset == 0)
|
|
{
|
|
match_offset = R0;
|
|
}
|
|
else if (match_offset == 1)
|
|
{
|
|
match_offset = R1;
|
|
R1 = R0; R0 = match_offset;
|
|
}
|
|
else /* match_offset == 2 */
|
|
{
|
|
match_offset = R2;
|
|
R2 = R0; R0 = match_offset;
|
|
}
|
|
|
|
rundest = window_posn;
|
|
runsrc = rundest - match_offset;
|
|
window_posn += (ULONG)match_length;
|
|
if (window_posn > window_size) throw new InvalidOperationException("eieio"); ;
|
|
this_run -= match_length;
|
|
|
|
/* copy any wrapped around source data */
|
|
while ((runsrc < 0) && (match_length-- > 0))
|
|
{
|
|
window[rundest++] = window[runsrc + window_size]; runsrc++;
|
|
}
|
|
|
|
/* copy match data - no worries about destination wraps */
|
|
while (match_length-- > 0) window[rundest++] = window[runsrc++];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LZX.LZXBlockType.Aligned:
|
|
while (this_run > 0)
|
|
{
|
|
hr = READ_HUFFSYM(ref pState.MAINTREE_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_MAINTREE_TABLEBITS,
|
|
LZX_MAINTREE_MAXSYMBOLS, ref pState.MAINTREE_len, ref j, ref main_element);
|
|
if (!hr)
|
|
throw new InvalidOperationException("eieio"); ;
|
|
|
|
if (main_element < LZX_NUM_CHARS)
|
|
{
|
|
/* literal: 0 to LZX_NUM_CHARS-1 */
|
|
window[window_posn++] = (UBYTE)main_element;
|
|
this_run--;
|
|
}
|
|
else
|
|
{
|
|
/* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
|
|
main_element -= LZX_NUM_CHARS;
|
|
|
|
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
|
|
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
|
|
{
|
|
hr = READ_HUFFSYM(ref pState.LENGTH_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_LENGTH_TABLEBITS,
|
|
LZX_LENGTH_MAXSYMBOLS, ref pState.LENGTH_len, ref j, ref length_footer);
|
|
if (!hr)
|
|
throw new InvalidOperationException("eieio"); ;
|
|
match_length += length_footer;
|
|
}
|
|
match_length += LZX_MIN_MATCH;
|
|
|
|
match_offset = (ULONG)main_element >> 3;
|
|
|
|
if (match_offset > 2)
|
|
{
|
|
/* not repeated offset */
|
|
extra = extra_bits[match_offset];
|
|
match_offset = position_base[match_offset] - 2;
|
|
if (extra > 3)
|
|
{
|
|
/* verbatim and aligned bits */
|
|
extra -= 3;
|
|
READ_BITS(ref verbatim_bits, extra, ref bitbuf, ref bitsleft,
|
|
ref ip, ref inpos);
|
|
match_offset += (verbatim_bits << 3);
|
|
hr = READ_HUFFSYM(ref pState.ALIGNED_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_ALIGNED_TABLEBITS,
|
|
LZX_ALIGNED_MAXSYMBOLS, ref pState.ALIGNED_len, ref j, ref aligned_bits);
|
|
if (!hr)
|
|
throw new InvalidOperationException("eieio"); ;
|
|
match_offset += (ULONG)aligned_bits;
|
|
}
|
|
else if (extra == 3)
|
|
{
|
|
/* aligned bits only */
|
|
hr = READ_HUFFSYM(ref pState.ALIGNED_table, ref hufftbl, ref bitsleft,
|
|
ref bitbuf, ref ip, ref inpos, ref i, LZX_ALIGNED_TABLEBITS,
|
|
LZX_ALIGNED_MAXSYMBOLS, ref pState.ALIGNED_len, ref j, ref aligned_bits);
|
|
if (!hr)
|
|
throw new InvalidOperationException("eieio"); ;
|
|
match_offset += (ULONG)aligned_bits;
|
|
}
|
|
else if (extra > 0)
|
|
{ /* extra==1, extra==2 */
|
|
/* verbatim bits only */
|
|
READ_BITS(ref verbatim_bits, extra, ref bitbuf, ref bitsleft,
|
|
ref ip, ref inpos);
|
|
match_offset += verbatim_bits;
|
|
}
|
|
else /* extra == 0 */
|
|
{
|
|
/* ??? */
|
|
match_offset = 1;
|
|
}
|
|
|
|
/* update repeated offset LRU queue */
|
|
R2 = R1; R1 = R0; R0 = match_offset;
|
|
}
|
|
else if (match_offset == 0)
|
|
{
|
|
match_offset = R0;
|
|
}
|
|
else if (match_offset == 1)
|
|
{
|
|
match_offset = R1;
|
|
R1 = R0; R0 = match_offset;
|
|
}
|
|
else /* match_offset == 2 */
|
|
{
|
|
match_offset = R2;
|
|
R2 = R0; R0 = match_offset;
|
|
}
|
|
|
|
rundest = window_posn;
|
|
runsrc = rundest - match_offset;
|
|
window_posn += (ULONG)match_length;
|
|
if (window_posn > window_size) throw new InvalidOperationException("eieio"); ;
|
|
this_run -= match_length;
|
|
|
|
/* copy any wrapped around source data */
|
|
while ((runsrc < 0) && (match_length-- > 0))
|
|
{
|
|
window[rundest++] = window[runsrc + window_size]; runsrc++;
|
|
}
|
|
/* copy match data - no worries about destination wraps */
|
|
while (match_length-- > 0) window[rundest++] = window[runsrc++];
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LZX.LZXBlockType.Uncompressed:
|
|
if ((inpos + this_run) > endinp) throw new InvalidOperationException("eieio"); ;
|
|
Array.Copy(ip, inpos, window, window_posn, this_run);
|
|
inpos += this_run; window_posn += (ULONG)this_run;
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException("eieio"); ; /* might as well */
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (togo != 0) throw new InvalidOperationException("eieio"); ;
|
|
Array.Copy(
|
|
window,
|
|
(!Convert.ToBoolean(window_posn) ? window_size : window_posn) - outlen,
|
|
op,
|
|
(long)outpos,
|
|
outlen);
|
|
|
|
pState.window_posn = window_posn;
|
|
pState.R0 = R0;
|
|
pState.R1 = R1;
|
|
pState.R2 = R2;
|
|
|
|
/* intel E8 decoding */
|
|
if ((pState.frames_read++ < 32768) && pState.intel_filesize != 0)
|
|
{
|
|
if (outlen <= 6 || !Convert.ToBoolean(pState.intel_started))
|
|
{
|
|
pState.intel_curpos += outlen;
|
|
}
|
|
else
|
|
{
|
|
ulong data = outpos;
|
|
ulong dataend = outpos + (ulong)outlen - 10;
|
|
LONG curpos = pState.intel_curpos;
|
|
LONG filesize = pState.intel_filesize;
|
|
LONG abs_off, rel_off;
|
|
|
|
pState.intel_curpos = curpos + outlen;
|
|
|
|
while (data < dataend)
|
|
{
|
|
if (op[data++] != 0xE8) { curpos++; continue; }
|
|
abs_off = op[data + 0] | (op[data + 1] << 8) | (op[data + 2] << 16) | (op[data + 3] << 24);
|
|
if ((abs_off >= -curpos) && (abs_off < filesize))
|
|
{
|
|
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
|
|
op[data + 0] = (UBYTE)rel_off;
|
|
op[data + 1] = (UBYTE)(rel_off >> 8);
|
|
op[data + 2] = (UBYTE)(rel_off >> 16);
|
|
op[data + 3] = (UBYTE)(rel_off >> 24);
|
|
}
|
|
data += 4;
|
|
curpos += 5;
|
|
}
|
|
}
|
|
}
|
|
return DECR_OK;
|
|
}
|
|
}
|
|
|
|
public override string Name => "XMEMLZX";
|
|
|
|
protected override void CompressInternal(Stream inputStream, Stream outputStream)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
protected override void DecompressInternal(Stream inputStream, Stream outputStream, int inputLength, int outputLength)
|
|
{
|
|
Lzx.LZXstate state = Lzx.LZXinit(15);
|
|
|
|
byte[] ip = inputStream.ReadBytes(0, (int)inputStream.Length);
|
|
byte[] op = new byte[outputLength];
|
|
Lzx.LZXdecompress(state, ref ip, 0, ref op, 0, ip.Length, op.Length);
|
|
outputStream.Write(op);
|
|
}
|
|
}
|
|
|
|
public class DONOTUSE_XMemLZXCompressionModule : CompressionModule
|
|
{
|
|
static uint decrunch_method;
|
|
static uint decrunch_length;
|
|
static uint last_offset;
|
|
static uint global_control;
|
|
static int global_shift;
|
|
|
|
static byte[] offset_len = new byte[8];
|
|
static ushort[] offset_table = new ushort[128];
|
|
static byte[] huffman20_len = new byte[20];
|
|
static ushort[] huffman20_table = new ushort[96];
|
|
static byte[] literal_len = new byte[768];
|
|
static ushort[] literal_table = new ushort[5120];
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
//static unsigned int sum;
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static readonly byte[] table_one/*[32]*/ =
|
|
{
|
|
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14
|
|
};
|
|
|
|
static readonly uint[] table_two/*[32]*/ =
|
|
{
|
|
0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,
|
|
1536,2048,3072,4096,6144,8192,12288,16384,24576,32768,49152
|
|
};
|
|
|
|
static readonly uint[] table_three/*[16]*/ =
|
|
{
|
|
0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767
|
|
};
|
|
|
|
static readonly byte[] table_four /*[34]*/=
|
|
{
|
|
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
|
|
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
|
|
};
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Possible problems with 64 bit machines here. It kept giving warnings */
|
|
/* for people so I changed back to ~. */
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Build a fast huffman decode table from the symbol bit lengths. */
|
|
/* There is an alternate algorithm which is faster but also more complex. */
|
|
|
|
static bool make_decode_table(int number_symbols, int table_size, byte[] length, ushort[] table)
|
|
{
|
|
byte bit_num = 0;
|
|
int symbol;
|
|
uint leaf; /* could be a register */
|
|
uint table_mask, bit_mask, pos, fill, next_symbol, reverse;
|
|
bool myabort = false;
|
|
|
|
pos = 0; /* consistantly used as the current position in the decode table */
|
|
|
|
bit_mask = table_mask = (uint)(1 << table_size);
|
|
|
|
bit_mask >>= 1; /* don't do the first number */
|
|
bit_num++;
|
|
|
|
while ((!myabort) && (bit_num <= table_size))
|
|
{
|
|
for (symbol = 0; symbol < number_symbols; symbol++)
|
|
{
|
|
if (length[symbol] == bit_num)
|
|
{
|
|
reverse = pos; /* reverse the order of the position's bits */
|
|
leaf = 0;
|
|
fill = (uint)table_size;
|
|
do /* reverse the position */
|
|
{
|
|
leaf = (leaf << 1) + (reverse & 1);
|
|
reverse >>= 1;
|
|
} while (--fill != 0);
|
|
if ((pos += bit_mask) > table_mask)
|
|
{
|
|
myabort = true;
|
|
break; /* we will overrun the table! myabort! */
|
|
}
|
|
fill = bit_mask;
|
|
next_symbol = (uint)(1 << bit_num);
|
|
do
|
|
{
|
|
table[leaf] = (ushort)symbol;
|
|
leaf += next_symbol;
|
|
} while (--fill != 0);
|
|
}
|
|
}
|
|
bit_mask >>= 1;
|
|
bit_num++;
|
|
}
|
|
|
|
if ((!myabort) && (pos != table_mask))
|
|
{
|
|
for (symbol = (int)pos; symbol < table_mask; symbol++) /* clear the rest of the table */
|
|
{
|
|
reverse = (uint)symbol; /* reverse the order of the position's bits */
|
|
leaf = 0;
|
|
fill = (uint)table_size;
|
|
do /* reverse the position */
|
|
{
|
|
leaf = (leaf << 1) + (reverse & 1);
|
|
reverse >>= 1;
|
|
} while (--fill != 0);
|
|
table[leaf] = 0;
|
|
}
|
|
next_symbol = table_mask >> 1;
|
|
pos <<= 16;
|
|
table_mask <<= 16;
|
|
bit_mask = 32768;
|
|
|
|
while ((!myabort) && (bit_num <= 16))
|
|
{
|
|
for (symbol = 0; symbol < number_symbols; symbol++)
|
|
{
|
|
if (length[symbol] == bit_num)
|
|
{
|
|
reverse = pos >> 16; /* reverse the order of the position's bits */
|
|
leaf = 0;
|
|
fill = (uint)table_size;
|
|
do /* reverse the position */
|
|
{
|
|
leaf = (leaf << 1) + (reverse & 1);
|
|
reverse >>= 1;
|
|
} while (--fill != 0);
|
|
for (fill = 0; fill < bit_num - table_size; fill++)
|
|
{
|
|
if (table[leaf] == 0)
|
|
{
|
|
table[(next_symbol << 1)] = 0;
|
|
table[(next_symbol << 1) + 1] = 0;
|
|
table[leaf] = (ushort)(next_symbol++);
|
|
}
|
|
leaf = (uint)(table[leaf] << 1);
|
|
leaf += (uint)(((int)pos >> (int)(15 - fill)) & 1);
|
|
}
|
|
table[leaf] = (ushort)symbol;
|
|
if ((pos += bit_mask) > table_mask)
|
|
{
|
|
myabort = true;
|
|
break; /* we will overrun the table! myabort! */
|
|
}
|
|
}
|
|
}
|
|
bit_mask >>= 1;
|
|
bit_num++;
|
|
}
|
|
}
|
|
if (pos != table_mask) myabort = true; /* the table is incomplete! */
|
|
|
|
return (myabort);
|
|
} // DONE.
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/* Read and build the decrunch tables. There better be enough data in the */
|
|
/* source buffer or it's stuffed. */
|
|
|
|
static bool read_literal_table(System.IO.Stream inputStream, int inputLength, System.IO.Stream outputStream, int outputLength)
|
|
{
|
|
uint control;
|
|
int shift;
|
|
uint temp; /* could be a register */
|
|
uint symbol, pos, count, fix, max_symbol;
|
|
bool myabort = false;
|
|
|
|
control = global_control;
|
|
shift = global_shift;
|
|
|
|
if (shift < 0) /* fix the control word if necessary */
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
|
|
/* read the decrunch method */
|
|
|
|
decrunch_method = control & 7;
|
|
control >>= 3;
|
|
if ((shift -= 3) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
|
|
/* Read and build the offset huffman table */
|
|
|
|
if ((!myabort) && (decrunch_method == 3))
|
|
{
|
|
for (temp = 0; temp < 8; temp++)
|
|
{
|
|
offset_len[temp] = (byte)(control & 7);
|
|
control >>= 3;
|
|
if ((shift -= 3) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
}
|
|
myabort = make_decode_table(8, 7, offset_len, offset_table);
|
|
}
|
|
|
|
/* read decrunch length */
|
|
|
|
if (!myabort)
|
|
{
|
|
decrunch_length = (control & 255) << 16;
|
|
control >>= 8;
|
|
if ((shift -= 8) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
decrunch_length += (control & 255) << 8;
|
|
control >>= 8;
|
|
if ((shift -= 8) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
decrunch_length += (control & 255);
|
|
control >>= 8;
|
|
if ((shift -= 8) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
}
|
|
|
|
/* read and build the huffman literal table */
|
|
|
|
if ((!myabort) && (decrunch_method != 1))
|
|
{
|
|
pos = 0;
|
|
fix = 1;
|
|
max_symbol = 256;
|
|
|
|
do
|
|
{
|
|
for (temp = 0; temp < 20; temp++)
|
|
{
|
|
huffman20_len[temp] = (byte)(control & 15);
|
|
control >>= 4;
|
|
if ((shift -= 4) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
}
|
|
myabort = make_decode_table(20, 6, huffman20_len, huffman20_table);
|
|
|
|
if (myabort) break; /* argh! table is corrupt! */
|
|
|
|
do
|
|
{
|
|
if ((symbol = huffman20_table[control & 63]) >= 20)
|
|
{
|
|
do /* symbol is longer than 6 bits */
|
|
{
|
|
symbol = huffman20_table[((control >> 6) & 1) + (symbol << 1)];
|
|
if (shift-- == 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << 24;
|
|
// control += *source++ << 16;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
control >>= 1;
|
|
} while (symbol >= 20);
|
|
temp = 6;
|
|
}
|
|
else
|
|
{
|
|
temp = huffman20_len[symbol];
|
|
}
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
switch (symbol)
|
|
{
|
|
case 17:
|
|
case 18:
|
|
{
|
|
if (symbol == 17)
|
|
{
|
|
temp = 4;
|
|
count = 3;
|
|
}
|
|
else /* symbol == 18 */
|
|
{
|
|
temp = 6 - fix;
|
|
count = 19;
|
|
}
|
|
count += (control & table_three[temp]) + fix;
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
while ((pos < max_symbol) && (count-- != 0))
|
|
literal_len[pos++] = 0;
|
|
break;
|
|
}
|
|
case 19:
|
|
{
|
|
count = (control & 1) + 3 + fix;
|
|
if (shift-- == 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << 24;
|
|
// control += *source++ << 16;
|
|
control += (uint)(inputStream.ReadByte() << 24);
|
|
control += (uint)(inputStream.ReadByte() << 16);
|
|
}
|
|
control >>= 1;
|
|
if ((symbol = huffman20_table[control & 63]) >= 20)
|
|
{
|
|
do /* symbol is longer than 6 bits */
|
|
{
|
|
symbol = huffman20_table[((control >> 6) & 1) + (symbol << 1)];
|
|
if (shift-- == 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << 24;
|
|
// control += *source++ << 16;
|
|
control += (uint)(inputStream.ReadByte() << 24);
|
|
control += (uint)(inputStream.ReadByte() << 16);
|
|
}
|
|
control >>= 1;
|
|
} while (symbol >= 20);
|
|
temp = 6;
|
|
}
|
|
else
|
|
{
|
|
temp = huffman20_len[symbol];
|
|
}
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
symbol = table_four[literal_len[pos] + 17 - symbol];
|
|
while ((pos < max_symbol) && (count-- != 0))
|
|
literal_len[pos++] = (byte)symbol;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
symbol = table_four[literal_len[pos] + 17 - symbol];
|
|
literal_len[pos++] = (byte)symbol;
|
|
break;
|
|
}
|
|
}
|
|
} while (pos < max_symbol);
|
|
fix--;
|
|
max_symbol += 512;
|
|
} while (max_symbol == 768);
|
|
|
|
if (!myabort)
|
|
myabort = make_decode_table(768, 12, literal_len, literal_table);
|
|
}
|
|
|
|
global_control = control;
|
|
global_shift = shift;
|
|
|
|
return (myabort);
|
|
} // DONE.
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/// <summary>
|
|
/// Fill up the decrunch buffer. Needs lots of overrun for both destination
|
|
/// and source buffers. Most of the time is spent in this routine so it's
|
|
/// pretty damn optimized.
|
|
/// </summary>
|
|
static void decrunch(System.IO.Stream inputStream, System.IO.Stream outputStream)
|
|
{
|
|
uint control;
|
|
int shift;
|
|
uint temp; /* could be a register */
|
|
uint symbol, count;
|
|
|
|
control = global_control;
|
|
shift = global_shift;
|
|
|
|
do
|
|
{
|
|
if ((symbol = literal_table[control & 4095]) >= 768)
|
|
{
|
|
control >>= 12;
|
|
if ((shift -= 12) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
do /* literal is longer than 12 bits */
|
|
{
|
|
symbol = literal_table[(control & 1) + (symbol << 1)];
|
|
if (shift-- == 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << 24;
|
|
// control += *source++ << 16;
|
|
control += (uint)(inputStream.ReadByte() << 24);
|
|
control += (uint)(inputStream.ReadByte() << 16);
|
|
}
|
|
control >>= 1;
|
|
} while (symbol >= 768);
|
|
}
|
|
else
|
|
{
|
|
temp = literal_len[symbol];
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
}
|
|
if (symbol < 256)
|
|
{
|
|
// *destination++ = symbol;
|
|
outputStream.WriteByte((byte)symbol);
|
|
}
|
|
else
|
|
{
|
|
symbol -= 256;
|
|
count = table_two[temp = symbol & 31];
|
|
temp = table_one[temp];
|
|
if ((temp >= 3) && (decrunch_method == 3))
|
|
{
|
|
temp -= 3;
|
|
count += ((control & table_three[temp]) << 3);
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
count += (temp = offset_table[control & 127]);
|
|
temp = offset_len[temp];
|
|
}
|
|
else
|
|
{
|
|
count += control & table_three[temp];
|
|
if (count == 0) count = last_offset;
|
|
}
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
last_offset = count;
|
|
|
|
count = table_two[temp = (symbol >> 5) & 15] + 3;
|
|
temp = table_one[temp];
|
|
count += (control & table_three[temp]);
|
|
control >>= (int)temp;
|
|
if ((shift -= (int)temp) < 0)
|
|
{
|
|
shift += 16;
|
|
// control += *source++ << (8 + shift);
|
|
// control += *source++ << shift;
|
|
control += (uint)(inputStream.ReadByte() << (8 + shift));
|
|
control += (uint)(inputStream.ReadByte() << shift);
|
|
}
|
|
|
|
int str = (int)(outputStream.Length - last_offset); // string = destination - last_offset;
|
|
do
|
|
{
|
|
long pos = outputStream.Position;
|
|
outputStream.Position = str;
|
|
byte b = (byte)outputStream.ReadByte();
|
|
outputStream.Position = pos;
|
|
outputStream.WriteByte(b);
|
|
// *destination++ = *string++;
|
|
} while (--count != 0);
|
|
}
|
|
} while (!outputStream.get_EndOfStream() && !inputStream.get_EndOfStream());
|
|
|
|
global_control = control;
|
|
global_shift = shift;
|
|
} // DONE.
|
|
|
|
|
|
|
|
int unlzx(System.IO.Stream input, int inputLength, System.IO.Stream output, int outputLength)
|
|
{
|
|
global_control = 0;
|
|
global_shift = -16;
|
|
last_offset = 1;
|
|
if (read_literal_table(input, inputLength, output, outputLength)) return (-1);
|
|
decrunch(input, output);
|
|
return 0; // (destination - output);
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string Name
|
|
{
|
|
get { return "XMEMLZX"; }
|
|
}
|
|
|
|
protected override void CompressInternal(System.IO.Stream inputStream, System.IO.Stream outputStream)
|
|
{
|
|
}
|
|
protected override void DecompressInternal(System.IO.Stream inputStream, System.IO.Stream outputStream, int inputLength, int outputLength)
|
|
{
|
|
unlzx(inputStream, inputLength, outputStream, outputLength);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|