From 42b4c40d8717134a5e62d4253f65a935759a5de1 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 3 Jan 2023 19:00:21 -0800 Subject: [PATCH] Port LZX init and decompression --- BurnOutSharp.Compression/LZX/Decompressor.cs | 460 +++++++++++++++++- BurnOutSharp.Compression/LZX/State.cs | 29 ++ BurnOutSharp.Wrappers/MicrosoftCabinet.cs | 6 +- .../MicrosoftCabinet.fdi.LZX.cs | 382 --------------- BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs | 65 --- README.md | 2 +- 6 files changed, 489 insertions(+), 455 deletions(-) delete mode 100644 BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs diff --git a/BurnOutSharp.Compression/LZX/Decompressor.cs b/BurnOutSharp.Compression/LZX/Decompressor.cs index b89dad11..5ce8e38b 100644 --- a/BurnOutSharp.Compression/LZX/Decompressor.cs +++ b/BurnOutSharp.Compression/LZX/Decompressor.cs @@ -1,21 +1,470 @@ +using System; using BurnOutSharp.Models.Compression.LZX; using static BurnOutSharp.Models.Compression.LZX.Constants; +using static BurnOutSharp.Models.MicrosoftCabinet.Constants; namespace BurnOutSharp.Compression.LZX { /// public class Decompressor { + /// + /// Initialize an LZX decompressor state + /// + public static bool Init(int window, State state) + { + uint wndsize = (uint)(1 << window); + int 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) + return false; + + if (state.actual_size < wndsize) + state.window = null; + + if (state.window == null) + { + state.window = new byte[wndsize]; + state.actual_size = wndsize; + } + + state.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; + + /*posn_slots=i=0; while (i < wndsize) i += 1 << CAB(extra_bits)[posn_slots++]; */ + + state.R0 = state.R1 = state.R2 = 1; + state.main_elements = (ushort)(LZX_NUM_CHARS + (posn_slots << 3)); + state.header_read = 0; + state.frames_read = 0; + state.block_remaining = 0; + state.block_type = LZX_BLOCKTYPE_INVALID; + state.intel_curpos = 0; + state.intel_started = 0; + state.window_posn = 0; + + /* initialize tables to 0 (because deltas will be applied to them) */ + // memset(state.MAINTREE_len, 0, sizeof(state.MAINTREE_len)); + // memset(state.LENGTH_len, 0, sizeof(state.LENGTH_len)); + + return true; + } + /// /// Decompress a byte array using a given State /// public static bool Decompress(State state, int inlen, byte[] inbuf, int outlen, byte[] outbuf) { + int inpos = 0; // inbuf[0]; + int endinp = inpos + inlen; + int window = 0; // state.window[0]; + int runsrc, rundest; // byte* + ushort[] hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ - // TODO: Finish implementation - return false; + uint window_posn = state.window_posn; + uint window_size = state.window_size; + uint R0 = state.R0; + uint R1 = state.R1; + uint R2 = state.R2; + + uint match_offset, i, j, k; /* ijk used in READ_HUFFSYM macro */ + Bits lb = new Bits(); /* used in READ_LENGTHS macro */ + + int togo = outlen, this_run, main_element, aligned_bits; + int match_length, copy_length, length_footer, extra, verbatim_bits; + + INIT_BITSTREAM(out int bitsleft, out uint bitbuf); + + /* read header if necessary */ + if (state.header_read == 0) + { + i = j = 0; + k = READ_BITS(1, inbuf, ref inpos, ref bitsleft, ref bitbuf); + if (k != 0) + { + i = READ_BITS(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); + j = READ_BITS(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); + } + + state.intel_filesize = (int)((i << 16) | j); /* or 0 if not encoded */ + state.header_read = 1; + } + + /* main decoding loop */ + while (togo > 0) + { + /* last block finished, new block expected */ + if (state.block_remaining == 0) + { + if (state.block_type == LZX_BLOCKTYPE_UNCOMPRESSED) + { + if ((state.block_length & 1) != 0) + inpos++; /* realign bitstream to word */ + + INIT_BITSTREAM(out bitsleft, out bitbuf); + } + + state.block_type = (ushort)READ_BITS(3, inbuf, ref inpos, ref bitsleft, ref bitbuf); + i = READ_BITS(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); + j = READ_BITS(8, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.block_remaining = state.block_length = (i << 8) | j; + + switch (state.block_type) + { + case LZX_BLOCKTYPE_ALIGNED: + for (i = 0; i < 8; i++) + { + j = READ_BITS(3, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.tblALIGNED_len[i] = (byte)j; + } + + make_decode_table(LZX_ALIGNED_MAXSYMBOLS, LZX_ALIGNED_TABLEBITS, state.tblALIGNED_len, state.tblALIGNED_table); + + /* rest of aligned header is same as verbatim */ + goto case LZX_BLOCKTYPE_VERBATIM; + + case LZX_BLOCKTYPE_VERBATIM: + READ_LENGTHS(state.tblMAINTREE_len, 0, 256, lb, state, inbuf, ref inpos, ref bitsleft, ref bitbuf); + READ_LENGTHS(state.tblMAINTREE_len, 256, state.main_elements, lb, state, inbuf, ref inpos, ref bitsleft, ref bitbuf); + make_decode_table(LZX_MAINTREE_MAXSYMBOLS, LZX_MAINTREE_TABLEBITS, state.tblMAINTREE_len, state.tblMAINTREE_table); + if (state.tblMAINTREE_len[0xE8] != 0) + state.intel_started = 1; + + READ_LENGTHS(state.tblLENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS, lb, state, inbuf, ref inpos, ref bitsleft, ref bitbuf); + make_decode_table(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, state.tblLENGTH_len, state.tblLENGTH_table); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + state.intel_started = 1; /* because we can't assume otherwise */ + ENSURE_BITS(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); /* get up to 16 pad bits into the buffer */ + + /* and align the bitstream! */ + if (bitsleft > 16) + inpos -= 2; + + R0 = (uint)(inbuf[inpos + 0] | (inbuf[inpos + 1] << 8) | (inbuf[inpos + 2] << 16) | (inbuf[inpos + 3] << 24)); inpos += 4; + R1 = (uint)(inbuf[inpos + 0] | (inbuf[inpos + 1] << 8) | (inbuf[inpos + 2] << 16) | (inbuf[inpos + 3] << 24)); inpos += 4; + R2 = (uint)(inbuf[inpos + 0] | (inbuf[inpos + 1] << 8) | (inbuf[inpos + 2] << 16) | (inbuf[inpos + 3] << 24)); inpos += 4; + break; + + default: + return false; + } + } + + /* 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) + return false; + } + + while ((this_run = (int)state.block_remaining) > 0 && togo > 0) + { + if (this_run > togo) this_run = togo; + togo -= this_run; + state.block_remaining -= (uint)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 false; + + switch (state.block_type) + { + + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) + { + main_element = READ_HUFFSYM(state.tblMAINTREE_table, state.tblMAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + if (main_element < LZX_NUM_CHARS) + { + /* literal: 0 to LZX_NUM_CHARS-1 */ + state.window[window + window_posn++] = (byte)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) + { + length_footer = READ_HUFFSYM(state.tblLENGTH_table, state.tblLENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_length += length_footer; + } + + match_length += LZX_MIN_MATCH; + match_offset = (uint)(main_element >> 3); + + if (match_offset > 2) + { + /* not repeated offset */ + if (match_offset != 3) + { + extra = state.ExtraBits[match_offset]; + verbatim_bits = (int)READ_BITS(extra, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset = (uint)(state.PositionSlotBases[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 = (int)(window + window_posn); + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) + { + /* no wrap */ + runsrc = (int)(rundest - match_offset); + } + else + { + runsrc = (int)(rundest + (window_size - match_offset)); + copy_length = (int)(match_offset - window_posn); + if (copy_length < match_length) + { + match_length -= copy_length; + window_posn += (uint)copy_length; + while (copy_length-- > 0) + { + state.window[rundest++] = state.window[runsrc++]; + } + + runsrc = window; + } + } + + window_posn += (uint)match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + { + state.window[rundest++] = state.window[runsrc++]; + } + } + } + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) + { + main_element = READ_HUFFSYM(state.tblMAINTREE_table, state.tblMAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + + if (main_element < LZX_NUM_CHARS) + { + /* literal: 0 to LZX_NUM_CHARS-1 */ + state.window[window + window_posn++] = (byte)main_element; + this_run--; + } + else + { + /* mverbatim_bitsatch: 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) + { + length_footer = READ_HUFFSYM(state.tblLENGTH_table, state.tblLENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = (uint)(main_element >> 3); + + if (match_offset > 2) + { + /* not repeated offset */ + extra = state.ExtraBits[match_offset]; + match_offset = state.PositionSlotBases[match_offset] - 2; + if (extra > 3) + { + /* verbatim and aligned bits */ + extra -= 3; + verbatim_bits = (int)READ_BITS(extra, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset += (uint)(verbatim_bits << 3); + aligned_bits = READ_HUFFSYM(state.tblALIGNED_table, state.tblALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset += (uint)aligned_bits; + } + else if (extra == 3) + { + /* aligned bits only */ + aligned_bits = READ_HUFFSYM(state.tblALIGNED_table, state.tblALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset += (uint)aligned_bits; + } + else if (extra > 0) + { + /* extra==1, extra==2 */ + /* verbatim bits only */ + verbatim_bits = (int)READ_BITS(extra, inbuf, ref inpos, ref bitsleft, ref bitbuf); + match_offset += (uint)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 = (int)(window + window_posn); + this_run -= match_length; + + /* copy any wrapped around source data */ + if (window_posn >= match_offset) + { + /* no wrap */ + runsrc = (int)(rundest - match_offset); + } + else + { + runsrc = (int)(rundest + (window_size - match_offset)); + copy_length = (int)(match_offset - window_posn); + if (copy_length < match_length) + { + match_length -= copy_length; + window_posn += (uint)copy_length; + while (copy_length-- > 0) + { + state.window[rundest++] = state.window[runsrc++]; + } + + runsrc = window; + } + } + + window_posn += (uint)match_length; + + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + { + state.window[rundest++] = state.window[runsrc++]; + } + } + } + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + if ((inpos + this_run) > endinp) + return false; + + Array.Copy(inbuf, inpos, state.window, window + window_posn, this_run); + inpos += this_run; + window_posn += (uint)this_run; + break; + + default: + return false; /* might as well */ + } + + } + } + + if (togo != 0) + return false; + + Array.Copy(state.window, window + ((window_posn == 0) ? window_size : window_posn) - outlen, outbuf, 0, outlen); + + state.window_posn = window_posn; + state.R0 = R0; + state.R1 = R1; + state.R2 = R2; + + /* intel E8 decoding */ + if ((state.frames_read++ < 32768) && state.intel_filesize != 0) + { + if (outlen <= 6 || state.intel_started == 0) + { + state.intel_curpos += outlen; + } + else + { + int data = 0; // outbuf[0]; + int dataend = data + outlen - 10; + int curpos = state.intel_curpos; + int filesize = state.intel_filesize; + int abs_off, rel_off; + + state.intel_curpos = curpos + outlen; + + while (data < dataend) + { + if (outbuf[data++] != 0xE8) + { + curpos++; + continue; + } + + abs_off = outbuf[data + 0] | (outbuf[data + 1] << 8) | (outbuf[data + 2] << 16) | (outbuf[data + 3] << 24); + if ((abs_off >= -curpos) && (abs_off < filesize)) + { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + outbuf[data + 0] = (byte)rel_off; + outbuf[data + 1] = (byte)(rel_off >> 8); + outbuf[data + 2] = (byte)(rel_off >> 16); + outbuf[data + 3] = (byte)(rel_off >> 24); + } + data += 4; + curpos += 5; + } + } + } + + return true; } - /// /// Read and build the Huffman tree from the lengths /// @@ -118,7 +567,10 @@ namespace BurnOutSharp.Compression.LZX { while (bitsleft < n) { - bitbuf |= (uint)(((inbuf[inpos + 1] << 8) | inbuf[inpos + 0]) << (16 - bitsleft)); + byte b0 = inpos + 0 < inbuf.Length ? inbuf[inpos + 0] : (byte)0; + byte b1 = inpos + 1 < inbuf.Length ? inbuf[inpos + 1] : (byte)0; + + bitbuf |= (uint)(((b1 << 8) | b0) << (16 - bitsleft)); bitsleft += 16; inpos += 2; } diff --git a/BurnOutSharp.Compression/LZX/State.cs b/BurnOutSharp.Compression/LZX/State.cs index 212a83ed..e2f7610b 100644 --- a/BurnOutSharp.Compression/LZX/State.cs +++ b/BurnOutSharp.Compression/LZX/State.cs @@ -86,5 +86,34 @@ namespace BurnOutSharp.Compression.LZX public ushort[] tblALIGNED_table = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS << 1)]; public byte[] tblALIGNED_len = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; + + #region Decompression Tables + + /// + /// An index to the position slot bases + /// + public uint[] PositionSlotBases = new uint[] + { + 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 + }; + + /// + /// How many bits of offset-from-base data is needed + /// + public byte[] ExtraBits = new byte[] + { + 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 + }; + + #endregion } } \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs index 2ff348a7..84630cb9 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs @@ -216,6 +216,8 @@ namespace BurnOutSharp.Wrappers return null; // Setup LZX decompression + var lzx = new Compression.LZX.State(); + Compression.LZX.Decompressor.Init(((ushort)folder.CompressionType >> 8) & 0x1f, lzx); // TODO: Use this area for LZX // Setup MS-ZIP decompression @@ -242,9 +244,7 @@ namespace BurnOutSharp.Wrappers Compression.Quantum.Decompressor.Decompress(qtm, dataBlock.CompressedSize, dataBlock.CompressedData, dataBlock.UncompressedSize, decompressed); break; case Models.MicrosoftCabinet.CompressionType.TYPE_LZX: - // TODO: UNIMPLEMENTED - //decompressed = dataBlock.CompressedData; - decompressed = null; + Compression.LZX.Decompressor.Decompress(state: lzx, dataBlock.CompressedSize, dataBlock.CompressedData, dataBlock.UncompressedSize, decompressed); break; default: return null; diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs deleted file mode 100644 index f2d0a772..00000000 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs +++ /dev/null @@ -1,382 +0,0 @@ -// using static BurnOutSharp.Wrappers.CabinetConstants; -// using static BurnOutSharp.Wrappers.FDIcConstants; -// using static BurnOutSharp.Wrappers.FDIConstants; -// using cab_LONG = System.Int32; -// using cab_off_t = System.UInt32; -// using cab_UBYTE = System.Byte; -// using cab_ULONG = System.UInt32; -// using cab_UWORD = System.UInt16; - -// namespace BurnOutSharp.Wrappers -// { -// internal unsafe class LZXfdi -// { - -// /******************************************************* -// * LZXfdi_decomp(internal) -// */ -// static int LZXfdi_decomp(int inlen, int outlen, fdi_decomp_state* decomp_state) -// { -// cab_UBYTE* inpos = CAB(inbuf); -// const cab_UBYTE* endinp = inpos + inlen; -// cab_UBYTE* window = LZX(window); -// cab_UBYTE* runsrc, *rundest; -// cab_UWORD* hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ - -// cab_ULONG window_posn = LZX(window_posn); -// cab_ULONG window_size = LZX(window_size); -// cab_ULONG R0 = LZX(R0); -// cab_ULONG R1 = LZX(R1); -// cab_ULONG R2 = LZX(R2); - -// register cab_ULONG bitbuf; -// register int bitsleft; -// cab_ULONG match_offset, i, j, k; /* ijk used in READ_HUFFSYM macro */ -// struct lzx_bits lb; /* used in READ_LENGTHS macro */ - -// int togo = outlen, this_run, main_element, aligned_bits; -// int match_length, copy_length, length_footer, extra, verbatim_bits; - -// TRACE("(inlen == %d, outlen == %d)\n", inlen, outlen); - -// INIT_BITSTREAM; - -// /* read header if necessary */ -// if (!LZX(header_read)) -// { -// i = j = 0; -// READ_BITS(k, 1); if (k) { READ_BITS(i, 16); READ_BITS(j, 16); } -// LZX(intel_filesize) = (i << 16) | j; /* or 0 if not encoded */ -// LZX(header_read) = 1; -// } - -// /* main decoding loop */ -// while (togo > 0) -// { -// /* last block finished, new block expected */ -// if (LZX(block_remaining) == 0) -// { -// if (LZX(block_type) == LZX_BLOCKTYPE_UNCOMPRESSED) -// { -// if (LZX(block_length) & 1) inpos++; /* realign bitstream to word */ -// INIT_BITSTREAM; -// } - -// READ_BITS(LZX(block_type), 3); -// READ_BITS(i, 16); -// READ_BITS(j, 8); -// LZX(block_remaining) = LZX(block_length) = (i << 8) | j; - -// switch (LZX(block_type)) -// { -// case LZX_BLOCKTYPE_ALIGNED: -// for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; } -// BUILD_TABLE(ALIGNED); -// /* rest of aligned header is same as verbatim */ - -// case LZX_BLOCKTYPE_VERBATIM: -// READ_LENGTHS(MAINTREE, 0, 256, fdi_lzx_read_lens); -// READ_LENGTHS(MAINTREE, 256, LZX(main_elements), fdi_lzx_read_lens); -// BUILD_TABLE(MAINTREE); -// if (LENTABLE(MAINTREE)[0xE8] != 0) LZX(intel_started) = 1; - -// READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS, fdi_lzx_read_lens); -// BUILD_TABLE(LENGTH); -// break; - -// case LZX_BLOCKTYPE_UNCOMPRESSED: -// LZX(intel_started) = 1; /* because we can't assume otherwise */ -// ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */ -// if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */ -// R0 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); inpos += 4; -// R1 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); inpos += 4; -// R2 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); inpos += 4; -// break; - -// default: -// return DECR_ILLEGALDATA; -// } -// } - -// /* 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) return DECR_ILLEGALDATA; -// } - -// while ((this_run = LZX(block_remaining)) > 0 && togo > 0) -// { -// if (this_run > togo) this_run = togo; -// togo -= this_run; -// LZX(block_remaining) -= 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 (LZX(block_type)) -// { - -// case LZX_BLOCKTYPE_VERBATIM: -// while (this_run > 0) -// { -// READ_HUFFSYM(MAINTREE, main_element); - -// if (main_element < LZX_NUM_CHARS) -// { -// /* literal: 0 to LZX_NUM_CHARS-1 */ -// window[window_posn++] = 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) -// { -// READ_HUFFSYM(LENGTH, length_footer); -// match_length += length_footer; -// } -// match_length += LZX_MIN_MATCH; - -// match_offset = main_element >> 3; - -// if (match_offset > 2) -// { -// /* not repeated offset */ -// if (match_offset != 3) -// { -// extra = CAB(extra_bits)[match_offset]; -// READ_BITS(verbatim_bits, extra); -// match_offset = CAB(lzx_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 + window_posn; -// this_run -= match_length; - -// /* copy any wrapped around source data */ -// if (window_posn >= match_offset) -// { -// /* no wrap */ -// runsrc = rundest - match_offset; -// } -// else -// { -// runsrc = rundest + (window_size - match_offset); -// copy_length = match_offset - window_posn; -// if (copy_length < match_length) -// { -// match_length -= copy_length; -// window_posn += copy_length; -// while (copy_length-- > 0) *rundest++ = *runsrc++; -// runsrc = window; -// } -// } -// window_posn += match_length; - -// /* copy match data - no worries about destination wraps */ -// while (match_length-- > 0) *rundest++ = *runsrc++; -// } -// } -// break; - -// case LZX_BLOCKTYPE_ALIGNED: -// while (this_run > 0) -// { -// READ_HUFFSYM(MAINTREE, main_element); - -// if (main_element < LZX_NUM_CHARS) -// { -// /* literal: 0 to LZX_NUM_CHARS-1 */ -// window[window_posn++] = 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) -// { -// READ_HUFFSYM(LENGTH, length_footer); -// match_length += length_footer; -// } -// match_length += LZX_MIN_MATCH; - -// match_offset = main_element >> 3; - -// if (match_offset > 2) -// { -// /* not repeated offset */ -// extra = CAB(extra_bits)[match_offset]; -// match_offset = CAB(lzx_position_base)[match_offset] - 2; -// if (extra > 3) -// { -// /* verbatim and aligned bits */ -// extra -= 3; -// READ_BITS(verbatim_bits, extra); -// match_offset += (verbatim_bits << 3); -// READ_HUFFSYM(ALIGNED, aligned_bits); -// match_offset += aligned_bits; -// } -// else if (extra == 3) -// { -// /* aligned bits only */ -// READ_HUFFSYM(ALIGNED, aligned_bits); -// match_offset += aligned_bits; -// } -// else if (extra > 0) -// { /* extra==1, extra==2 */ -// /* verbatim bits only */ -// READ_BITS(verbatim_bits, extra); -// 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 + window_posn; -// this_run -= match_length; - -// /* copy any wrapped around source data */ -// if (window_posn >= match_offset) -// { -// /* no wrap */ -// runsrc = rundest - match_offset; -// } -// else -// { -// runsrc = rundest + (window_size - match_offset); -// copy_length = match_offset - window_posn; -// if (copy_length < match_length) -// { -// match_length -= copy_length; -// window_posn += copy_length; -// while (copy_length-- > 0) *rundest++ = *runsrc++; -// runsrc = window; -// } -// } -// window_posn += match_length; - -// /* copy match data - no worries about destination wraps */ -// while (match_length-- > 0) *rundest++ = *runsrc++; -// } -// } -// break; - -// case LZX_BLOCKTYPE_UNCOMPRESSED: -// if ((inpos + this_run) > endinp) return DECR_ILLEGALDATA; -// memcpy(window + window_posn, inpos, (size_t)this_run); -// inpos += this_run; window_posn += this_run; -// break; - -// default: -// return DECR_ILLEGALDATA; /* might as well */ -// } - -// } -// } - -// if (togo != 0) return DECR_ILLEGALDATA; -// memcpy(CAB(outbuf), window + ((!window_posn) ? window_size : window_posn) - -// outlen, (size_t)outlen); - -// LZX(window_posn) = window_posn; -// LZX(R0) = R0; -// LZX(R1) = R1; -// LZX(R2) = R2; - -// /* intel E8 decoding */ -// if ((LZX(frames_read)++ < 32768) && LZX(intel_filesize) != 0) -// { -// if (outlen <= 6 || !LZX(intel_started)) -// { -// LZX(intel_curpos) += outlen; -// } -// else -// { -// cab_UBYTE* data = CAB(outbuf); -// cab_UBYTE* dataend = data + outlen - 10; -// cab_LONG curpos = LZX(intel_curpos); -// cab_LONG filesize = LZX(intel_filesize); -// cab_LONG abs_off, rel_off; - -// LZX(intel_curpos) = curpos + outlen; - -// while (data < dataend) -// { -// if (*data++ != 0xE8) { curpos++; continue; } -// abs_off = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); -// if ((abs_off >= -curpos) && (abs_off < filesize)) -// { -// rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; -// data[0] = (cab_UBYTE)rel_off; -// data[1] = (cab_UBYTE)(rel_off >> 8); -// data[2] = (cab_UBYTE)(rel_off >> 16); -// data[3] = (cab_UBYTE)(rel_off >> 24); -// } -// data += 4; -// curpos += 5; -// } -// } -// } -// return DECR_OK; -// } -// } -// } \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs index 178728d1..1c57ad2d 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs @@ -536,71 +536,6 @@ // return rv; // } -// /************************************************************ -// * LZXfdi_init (internal) -// */ -// static int LZXfdi_init(int window, fdi_decomp_state decomp_state) -// { -// byte[] bits = -// { 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}; -// uint[] @base = -// { -// 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}; -// uint wndsize = (uint)(1 << window); -// int 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) return DECR_DATAFORMAT; -// if (decomp_state.lzx.actual_size < wndsize) -// { -// if (decomp_state.lzx.window != null) decomp_state.fdi.free(decomp_state.lzx.window); -// decomp_state.lzx.window = null; -// } -// if (decomp_state.lzx.window == null) -// { -// if ((decomp_state.lzx.window = decomp_state.fdi.alloc((int)wndsize)) == null) return DECR_NOMEMORY; -// decomp_state.lzx.actual_size = wndsize; -// } -// decomp_state.lzx.window_size = wndsize; - -// /* initialize static tables */ -// Array.Copy(bits, decomp_state.extra_bits, bits.Length); -// Array.Copy(@base, decomp_state.lzx_position_base, @base.Length); - -// /* calculate required position slots */ -// if (window == 20) posn_slots = 42; -// else if (window == 21) posn_slots = 50; -// else posn_slots = window << 1; - -// /*posn_slots=i=0; while (i < wndsize) i += 1 << CAB(extra_bits)[posn_slots++]; */ - -// decomp_state.lzx.R0 = decomp_state.lzx.R1 = decomp_state.lzx.R2 = 1; -// decomp_state.lzx.main_elements = (ushort)(LZX_NUM_CHARS + (posn_slots << 3)); -// decomp_state.lzx.header_read = 0; -// decomp_state.lzx.frames_read = 0; -// decomp_state.lzx.block_remaining = 0; -// decomp_state.lzx.block_type = LZX_BLOCKTYPE_INVALID; -// decomp_state.lzx.intel_curpos = 0; -// decomp_state.lzx.intel_started = 0; -// decomp_state.lzx.window_posn = 0; - -// /* initialize tables to 0 (because deltas will be applied to them) */ -// memset(decomp_state.lzx.MAINTREE_len, 0, sizeof(decomp_state.lzx.MAINTREE_len)); -// memset(decomp_state.lzx.LENGTH_len, 0, sizeof(decomp_state.lzx.LENGTH_len)); - -// return DECR_OK; -// } - // /**************************************************** // * NONEfdi_decomp(internal) // */ diff --git a/README.md b/README.md index 04aca8f6..b757d3dd 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ Below is a list of container formats that are supported in some way: | InstallShield Archive V3 (Z) | No | Yes | Yes | Via `UnshieldSharp` | | InstallShield CAB | No | Yes | Yes | Via `UnshieldSharp` | | Linear Executable | No | No | No | Skeleton only | -| Microsoft cabinet file | Yes | Yes | Yes | Via `WixToolset.Dtf` / `SharpZipLib` (Only uncompressed, MS-ZIP, and Quantum supported) | +| Microsoft cabinet file | Yes | Yes | Yes | Via `WixToolset.Dtf` / `SharpZipLib` | | Microsoft LZ-compressed files | No | Yes | Yes | | | MoPaQ game data archive (MPQ) | No | Yes | Yes | Via `StormLibSharp` | | Microsoft installation package (MSI) | No | Yes | Yes | Via `OpenMcdf` |