From c14300ffbbaf561b1549423f2a5aa05f9a04de91 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 3 Jan 2023 22:11:57 -0800 Subject: [PATCH] Port MS-ZIP for MS-CAB --- BurnOutSharp.Compression/MSZIP.cs | 35 - .../MSZIP/Decompressor.cs | 588 ++++++- BurnOutSharp.Compression/MSZIP/HuffmanNode.cs | 10 +- BurnOutSharp.Compression/MSZIP/State.cs | 22 +- BurnOutSharp.Wrappers/MicrosoftCabinet.cs | 7 +- .../MicrosoftCabinet.fdi.MSZIP.cs | 444 ----- BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs | 1561 ----------------- BurnOutSharp/FileType/MicrosoftCAB.LZX.cs | 32 - BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs | 151 -- BurnOutSharp/FileType/MicrosoftCAB.cs | 43 +- README.md | 2 +- 11 files changed, 524 insertions(+), 2371 deletions(-) delete mode 100644 BurnOutSharp.Compression/MSZIP.cs delete mode 100644 BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.MSZIP.cs delete mode 100644 BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs delete mode 100644 BurnOutSharp/FileType/MicrosoftCAB.LZX.cs delete mode 100644 BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs diff --git a/BurnOutSharp.Compression/MSZIP.cs b/BurnOutSharp.Compression/MSZIP.cs deleted file mode 100644 index be76177e..00000000 --- a/BurnOutSharp.Compression/MSZIP.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using ICSharpCode.SharpZipLib.Zip.Compression; - -namespace BurnOutSharp.Compression -{ - public class MSZIP_zlib - { - #region Instance Variables - - /// - /// Inflater to be shared between blocks - /// - private readonly Inflater _inflater = new Inflater(noHeader: true); - - #endregion - - #region Decompressiom - - /// - /// Decompress MSZIP data block - /// - public byte[] DecompressMSZIPData(byte[] data, bool previousBlock = false) - { - if (previousBlock) - _inflater.Reset(); - - _inflater.SetInput(buffer: data, 2, data.Length - 2); - byte[] outputData = new byte[128 * 1024]; - int read = _inflater.Inflate(outputData); - return outputData.AsSpan(0, read).ToArray(); - } - - #endregion - } -} \ No newline at end of file diff --git a/BurnOutSharp.Compression/MSZIP/Decompressor.cs b/BurnOutSharp.Compression/MSZIP/Decompressor.cs index 742b547d..5673f01f 100644 --- a/BurnOutSharp.Compression/MSZIP/Decompressor.cs +++ b/BurnOutSharp.Compression/MSZIP/Decompressor.cs @@ -1,63 +1,69 @@ +using System; +using System.Runtime.InteropServices; using BurnOutSharp.Models.Compression.MSZIP; using static BurnOutSharp.Models.Compression.MSZIP.Constants; namespace BurnOutSharp.Compression.MSZIP { /// - public class Decompressor + public unsafe class Decompressor { /// /// Decompress a byte array using a given State /// public static bool Decompress(State state, int inlen, byte[] inbuf, int outlen, byte[] outbuf) { - state.InputPosition = 0; // inbuf[0]; - state.BitBuffer = state.BitCount = state.WindowPosition = 0; - if (outlen > ZIPWSIZE) - return false; - - // CK = Chris Kirmse, official Microsoft purloiner - if (inbuf[state.InputPosition + 0] != 0x43 || inbuf[state.InputPosition + 1] != 0x48) - return false; - - state.InputPosition += 2; - - int lastBlockFlag = 0; - do + fixed (byte* inpos = inbuf) { - if (InflateBlock(ref lastBlockFlag, state, inbuf, outbuf) != 0) + state.inpos = inpos; + state.bb = state.bk = state.window_posn = 0; + if (outlen > ZIPWSIZE) return false; - } while (lastBlockFlag == 0); - // Return success - return true; + // CK = Chris Kirmse, official Microsoft purloiner + if (state.inpos[0] != 0x43 || state.inpos[1] != 0x48) + return false; + + state.inpos += 2; + + int lastBlockFlag = 0; + do + { + if (InflateBlock(&lastBlockFlag, state, inbuf, outbuf) != 0) + return false; + } while (lastBlockFlag == 0); + + // Return success + return true; + } + } /// /// Decompress a deflated block /// - private static uint InflateBlock(ref int e, State state, byte[] inbuf, byte[] outbuf) + private static uint InflateBlock(int* e, State state, byte[] inbuf, byte[] outbuf) { // Make local bit buffer - uint bitBuffer = state.BitBuffer; - uint bitCount = state.BitCount; + uint b = state.bb; + uint k = state.bk; // Read the deflate block header var header = new DeflateBlockHeader(); // Read in last block bit - ZIPNEEDBITS(1, state, inbuf, ref bitBuffer, ref bitCount); - header.BFINAL = (e = (int)(bitBuffer & 1)) != 0; - ZIPDUMPBITS(1, ref bitBuffer, ref bitCount); + ZIPNEEDBITS(1, state, ref b, ref k); + header.BFINAL = (*e = (int)b & 1) != 0; + ZIPDUMPBITS(1, ref b, ref k); // Read in block type - ZIPNEEDBITS(2, state, inbuf, ref bitBuffer, ref bitCount); - header.BTYPE = (CompressionType)(bitBuffer & 3); - ZIPDUMPBITS(2, ref bitBuffer, ref bitCount); + ZIPNEEDBITS(2, state, ref b, ref k); + header.BTYPE = (CompressionType)(b & 3); + ZIPDUMPBITS(2, ref b, ref k); // Restore the global bit buffer - state.BitBuffer = bitBuffer; - state.BitCount = bitCount; + state.bb = b; + state.bk = k; // Inflate that block type switch (header.BTYPE) @@ -82,42 +88,42 @@ namespace BurnOutSharp.Compression.MSZIP private static int DecompressStored(State state, byte[] inbuf, byte[] outbuf) { // Make local copies of globals - uint bitBuffer = state.BitBuffer; - uint bitCount = state.BitCount; - uint windowPosition = state.WindowPosition; + uint b = state.bb; + uint k = state.bk; + uint w = state.window_posn; // Go to byte boundary - int n = (int)(bitCount & 7); - ZIPDUMPBITS(n, ref bitBuffer, ref bitCount); + int n = (int)(k & 7); + ZIPDUMPBITS(n, ref b, ref k); // Read the stored block header var header = new NonCompressedBlockHeader(); // Get the length and its compliment - ZIPNEEDBITS(16, state, inbuf, ref bitBuffer, ref bitCount); - header.LEN = (ushort)(bitBuffer & 0xffff); - ZIPDUMPBITS(16, ref bitBuffer, ref bitCount); + ZIPNEEDBITS(16, state, ref b, ref k); + header.LEN = (ushort)(b & 0xffff); + ZIPDUMPBITS(16, ref b, ref k); - ZIPNEEDBITS(16, state, inbuf, ref bitBuffer, ref bitCount); - header.NLEN = (ushort)(bitBuffer & 0xffff); + ZIPNEEDBITS(16, state, ref b, ref k); + header.NLEN = (ushort)(b & 0xffff); if (header.LEN != (~header.NLEN & 0xffff)) return 1; // Error in compressed data - ZIPDUMPBITS(16, ref bitBuffer, ref bitCount); + ZIPDUMPBITS(16, ref b, ref k); // Read and output the compressed data while (n-- > 0) { - ZIPNEEDBITS(8, state, inbuf, ref bitBuffer, ref bitCount); - outbuf[windowPosition++] = (byte)bitBuffer; - ZIPDUMPBITS(8, ref bitBuffer, ref bitCount); + ZIPNEEDBITS(8, state, ref b, ref k); + outbuf[w++] = (byte)b; + ZIPDUMPBITS(8, ref b, ref k); } // Restore the globals from the locals - state.WindowPosition = windowPosition; - state.BitBuffer = bitBuffer; - state.BitCount = bitCount; + state.window_posn = w; + state.bb = b; + state.bk = k; return 0; } @@ -128,78 +134,492 @@ namespace BurnOutSharp.Compression.MSZIP private static int DecompressFixed(State state, byte[] inbuf, byte[] outbuf) { // Create the block header - var header = new FixedHuffmanCompressedBlockHeader(); + FixedHuffmanCompressedBlockHeader header = new FixedHuffmanCompressedBlockHeader(); - // Assign the literal lengths - state.Lengths = header.LiteralLengths; - var literalLengthTable = new HuffmanNode[30]; - int literalLengthBitCount = 7; + fixed (uint* l = state.ll) + fixed (ushort* Zipcplens = CopyLengths) + fixed (ushort* Zipcplext = LiteralExtraBits) + fixed (ushort* Zipcpdist = CopyOffsets) + fixed (ushort* Zipcpdext = DistanceExtraBits) + { + // Assign the literal lengths + state.ll = header.LiteralLengths; + HuffmanNode* fixed_tl; + int fixed_bl = 7; - // Build the literal length tree - int i = BuildHuffmanTree(state.Lengths, 0, 30, 0, CopyOffsets, LiteralExtraBits, ref literalLengthTable, ref literalLengthBitCount, state); - if (i != 0) - return i; + // Build the literal length tree + int i = BuildHuffmanTree(l, 288, 257, Zipcplens, Zipcplext, &fixed_tl, &fixed_bl, state); + if (i != 0) + return i; - // Assign the distance codes - state.Lengths = header.DistanceCodes; - var distanceCodeTable = new HuffmanNode[30]; - int distanceCodeBitCount = 5; + // Assign the distance codes + state.ll = header.DistanceCodes; + HuffmanNode* fixed_td; + int fixed_bd = 5; - // Build the distance code tree - i = BuildHuffmanTree(state.Lengths, 0, 30, 0, CopyOffsets, DistanceExtraBits, ref distanceCodeTable, ref distanceCodeBitCount, state); - if (i != 0) - return i; + // Build the distance code tree + i = BuildHuffmanTree(l, 30, 0, Zipcpdist, Zipcpdext, &fixed_td, &fixed_bd, state); + if (i != 0) + return i; - // Decompress until an end-of-block code - return InflateCodes(literalLengthTable, distanceCodeTable, literalLengthBitCount, distanceCodeBitCount, state, inbuf, outbuf); + // Decompress until an end-of-block code + return InflateCodes(fixed_tl, fixed_td, fixed_bl, fixed_bd, state, inbuf, outbuf); + } } /// /// Decompress a block originally compressed with dynamic Huffman codes /// - /// INCORRECT IMPLEMENTATION TO SATISFY COMPILER FOR NOW private static int DecompressDynamic(State state, byte[] inbuf, byte[] outbuf) { + int i; /* temporary variables */ + uint j; + uint l; /* last length */ + uint m; /* mask for bit lengths table */ + uint n; /* number of lengths to get */ + HuffmanNode* tl; /* literal/length code table */ + HuffmanNode* td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + uint nb; /* number of bit length codes */ + uint nl; /* number of literal/length codes */ + uint nd; /* number of distance codes */ + uint b; /* bit buffer */ + uint k; /* number of bits in bit buffer */ - // TODO: Finish implementation - return 0; + /* make local bit buffer */ + b = state.bb; + k = state.bk; + + fixed (uint* ll = state.ll) + { + /* read in table lengths */ + ZIPNEEDBITS(5, state, ref b, ref k); + nl = 257 + (b & 0x1f); /* number of literal/length codes */ + ZIPDUMPBITS(5, ref b, ref k); + + ZIPNEEDBITS(5, state, ref b, ref k); + nd = 1 + (b & 0x1f); /* number of distance codes */ + ZIPDUMPBITS(5, ref b, ref k); + + ZIPNEEDBITS(4, state, ref b, ref k); + nb = 4 + (b & 0xf); /* number of bit length codes */ + ZIPDUMPBITS(4, ref b, ref k); + if (nl > 288 || nd > 32) + return 1; /* bad lengths */ + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) + { + ZIPNEEDBITS(3, state, ref b, ref k); + state.ll[BitLengthOrder[j]] = b & 7; + ZIPDUMPBITS(3, ref b, ref k); + } + for (; j < 19; j++) + state.ll[BitLengthOrder[j]] = 0; + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = BuildHuffmanTree(ll, 19, 19, null, null, &tl, &bl, state)) != 0) + return i; /* incomplete code set */ + + /* read in literal and distance code lengths */ + n = nl + nd; + m = BitMasks[bl]; + i = (int)(l = 0); + while ((uint)i < n) + { + ZIPNEEDBITS(bl, state, ref b, ref k); + j = (td = tl + (b & m))->b; + ZIPDUMPBITS((int)j, ref b, ref k); + j = td->n; + if (j < 16) /* length of code in bits (0..15) */ + { + state.ll[i++] = l = j; /* save last length in l */ + } + else if (j == 16) /* repeat last length 3 to 6 times */ + { + ZIPNEEDBITS(2, state, ref b, ref k); + j = 3 + (b & 3); + ZIPDUMPBITS(2, ref b, ref k); + if ((uint)i + j > n) + return 1; + while (j-- > 0) + { + state.ll[i++] = l; + } + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + ZIPNEEDBITS(3, state, ref b, ref k); + j = 3 + (b & 7); + ZIPDUMPBITS(3, ref b, ref k); + if ((uint)i + j > n) + return 1; + while (j-- > 0) + state.ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + ZIPNEEDBITS(7, state, ref b, ref k); + j = 11 + (b & 0x7f); + ZIPDUMPBITS(7, ref b, ref k); + if ((uint)i + j > n) + return 1; + while (j-- > 0) + state.ll[i++] = 0; + l = 0; + } + } + + /* restore the global bit buffer */ + state.bb = b; + state.bk = k; + + fixed (ushort* Zipcplens = CopyLengths) + fixed (ushort* Zipcplext = LiteralExtraBits) + fixed (ushort* Zipcpdist = CopyOffsets) + fixed (ushort* Zipcpdext = DistanceExtraBits) + { + /* build the decoding tables for literal/length and distance codes */ + bl = ZIPLBITS; + if ((i = BuildHuffmanTree(ll, nl, 257, Zipcplens, Zipcplext, &tl, &bl, state)) != 0) + { + return i; /* incomplete code set */ + } + bd = ZIPDBITS; + BuildHuffmanTree(ll + nl, nd, 0, Zipcpdist, Zipcpdext, &td, &bd, state); + + /* decompress until an end-of-block code */ + if (InflateCodes(tl, td, bl, bd, state, inbuf, outbuf) != 0) + return 1; + + return 0; + } + } } /// /// Build a Huffman tree from a set of lengths /// - /// INCORRECT IMPLEMENTATION TO SATISFY COMPILER FOR NOW - private static int BuildHuffmanTree(uint[] b, int bi, uint n, uint s, ushort[] d, ushort[] e, ref HuffmanNode[] t, ref int m, State state) + private static int BuildHuffmanTree(uint* b, uint n, uint s, ushort* d, ushort* e, HuffmanNode** t, int* m, State state) { - - // TODO: Finish implementation - return 0; + uint a; /* counter for codes of length k */ + uint el; /* length of EOB code (value 256) */ + uint f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + uint i; /* counter, current code */ + uint j; /* counter */ + int k; /* number of bits in current code */ + int* l; /* stack of bits per table */ + uint* p; /* pointer into state.c[],state.b[],state.v[] */ + HuffmanNode* q; /* points to current table */ + HuffmanNode r = new HuffmanNode(); /* table entry for structure assignment */ + int w; /* bits before this table == (l * h) */ + uint* xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uint z; /* number of entries in current table */ + + fixed (int* state_lx_ptr = state.lx) + { + l = state_lx_ptr + 1; + + /* Generate counts for each bit length */ + el = n > 256 ? b[256] : ZIPBMAX; /* set length of EOB code, if any */ + + for (i = 0; i < ZIPBMAX + 1; ++i) + state.c[i] = 0; + p = b; i = n; + do + { + state.c[*p]++; p++; /* assume all entries <= ZIPBMAX */ + } while (--i > 0); + + if (state.c[0] == n) /* null input--all zero length codes */ + { + *t = null; + *m = 0; + return 0; + } + + /* Find minimum and maximum length, bound *m by those */ + for (j = 1; j <= ZIPBMAX; j++) + { + if (state.c[j] > 0) + break; + } + + k = (int)j; /* minimum code length */ + if ((uint)*m < j) + *m = (int)j; + + for (i = ZIPBMAX; i > 0; i--) + { + if (state.c[i] > 0) + break; + } + + g = (int)i; /* maximum code length */ + if ((uint)*m > i) + *m = (int)i; + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << (int)j; j < i; j++, y <<= 1) + { + if ((y -= (int)state.c[j]) < 0) + return 2; /* bad input: more codes than bits */ + } + + if ((y -= (int)state.c[i]) < 0) + return 2; + + state.c[i] += (uint)y; + + /* Generate starting offsets LONGo the value table for each length */ + state.x[1] = j = 0; + + fixed (uint* state_c_ptr = state.c) + fixed (uint* state_x_ptr = state.x) + { + p = state_c_ptr + 1; + xp = state_x_ptr + 2; + while (--i > 0) + { + /* note that i == g from above */ + *xp++ = (j += *p++); + } + } + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do + { + if ((j = *p++) != 0) + state.v[state.x[j]++] = i; + } while (++i < n); + + /* Generate the Huffman codes and for each, make the table entries */ + state.x[0] = i = 0; /* first Huffman code is zero */ + + fixed (uint* state_v_ptr = state.v) + { + p = state_v_ptr; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = l[-1] = 0; /* no bits decoded yet */ + state.u[0] = default; /* just to keep compilers happy */ + q = null; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = state.c[k]; + while (a-- > 0) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l[h]) + { + w += l[h++]; /* add bits already decoded */ + + /* compute minimum size table less than or equal to *m bits */ + if ((z = (uint)(g - w)) > (uint)*m) /* upper limit */ + z = (uint)*m; + + if ((f = (uint)(1 << (int)(j = (uint)(k - w)))) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + fixed (uint* state_c_ptr = state.c) + { + xp = state_c_ptr + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + } + + if ((uint)w + j > el && (uint)w < el) + j = (uint)(el - w); /* make EOB code end at table */ + + z = (uint)(1 << (int)j); /* table entries for j-bit table */ + l[h] = (int)j; /* set table size in stack */ + + /* allocate and link in new table */ + q = (HuffmanNode*)Marshal.AllocHGlobal((int)((z + 1) * sizeof(HuffmanNode))); + *t = q + 1; /* link to list for HuffmanNode_free() */ + *(t = &(*q).t) = null; + state.u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h > 0) + { + state.x[h] = i; /* save pattern for backing up */ + r.b = (byte)l[h - 1]; /* bits to dump before this table */ + r.e = (byte)(16 + j); /* bits in this table */ + r.t = q; /* pointer to this table */ + j = (uint)((i & ((1 << w) - 1)) >> (w - l[h - 1])); + state.u[h - 1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.b = (byte)(k - w); + + fixed (uint* state_v_ptr_comp = state.v) + { + if (p >= state_v_ptr_comp + n) + { + r.e = 99; /* out of values--invalid code */ + } + else if (*p < s) + { + r.e = (byte)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.n = (ushort)*p++; /* simple code is just the value */ + } + else + { + r.e = (byte)e[*p - s]; /* non-simple--look up in lists */ + r.n = d[*p++ - s]; + } + } + + /* fill code-like entries with r */ + f = (uint)(1 << (k - w)); + for (j = i >> w; j < z; j += f) + { + q[j] = r; + } + + /* backwards increment the k-bit code i */ + for (j = (uint)(1 << (k - 1)); (i & j) != 0; j >>= 1) + { + i ^= j; + } + + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != state.x[h]) + w -= l[--h]; /* don't need to update q */ + } + } + } + + /* return actual size of base table */ + *m = l[0]; + } + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1 ? 1 : 0; } /// /// Inflate codes into Huffman trees /// - /// INCORRECT IMPLEMENTATION TO SATISFY COMPILER FOR NOW - private static int InflateCodes(HuffmanNode[] tl, HuffmanNode[] td, int bl, int bd, State state, byte[] inbuf, byte[] outbuf) + private static int InflateCodes(HuffmanNode* tl, HuffmanNode* td, int bl, int bd, State state, byte[] inbuf, byte[] outbuf) { + uint e; /* table entry flag/number of extra bits */ + uint n, d; /* length and index for copy */ + uint w; /* current window position */ + HuffmanNode* t; /* pointer to table entry */ + uint ml, md; /* masks for bl and bd bits */ + uint b; /* bit buffer */ + uint k; /* number of bits in bit buffer */ - // TODO: Finish implementation + /* make local copies of globals */ + b = state.bb; /* initialize bit buffer */ + k = state.bk; + w = state.window_posn; /* initialize window position */ + + /* inflate the coded data */ + ml = BitMasks[bl]; /* precompute masks for speed */ + md = BitMasks[bd]; + + for (; ; ) + { + ZIPNEEDBITS(bl, state, ref b, ref k); + if ((e = (t = tl + (b & ml))->e) > 16) + { + do + { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b, ref b, ref k); + e -= 16; + ZIPNEEDBITS((int)e, state, ref b, ref k); + } while ((e = (*(t = t->t + (b & BitMasks[e]))).e) > 16); + } + + ZIPDUMPBITS(t->b, ref b, ref k); + if (e == 16) /* then it's a literal */ + { + outbuf[w++] = (byte)t->n; + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + break; + + /* get length of block to copy */ + ZIPNEEDBITS((int)e, state, ref b, ref k); + n = t->n + (b & BitMasks[e]); + ZIPDUMPBITS((int)e, ref b, ref k); + + /* decode distance of block to copy */ + ZIPNEEDBITS(bd, state, ref b, ref k); + + if ((e = (*(t = td + (b & md))).e) > 16) + do + { + if (e == 99) + return 1; + ZIPDUMPBITS(t->b, ref b, ref k); + e -= 16; + ZIPNEEDBITS((int)e, state, ref b, ref k); + } while ((e = (*(t = t->t + (b & BitMasks[e]))).e) > 16); + + ZIPDUMPBITS(t->b, ref b, ref k); + + ZIPNEEDBITS((int)e, state, ref b, ref k); + d = w - t->n - (b & BitMasks[e]); + ZIPDUMPBITS((int)e, ref b, ref k); + + do + { + d &= ZIPWSIZE - 1; + e = ZIPWSIZE - Math.Max(d, w); + e = Math.Min(e, n); + n -= e; + do + { + outbuf[w++] = outbuf[d++]; + } while (--e > 0); + } while (n > 0); + } + } + + /* restore the globals from the locals */ + state.window_posn = w; /* restore global window pointer */ + state.bb = b; /* restore global bit buffer */ + state.bk = k; + + /* done */ return 0; } - /// - /// Free a single huffman node - /// - /// No-op because of garbage collection - private static void Free(HuffmanNode node) { } - #region Macros - private static void ZIPNEEDBITS(int n, State state, byte[] inbuf, ref uint bitBuffer, ref uint bitCount) + private static void ZIPNEEDBITS(int n, State state, ref uint bitBuffer, ref uint bitCount) { while (bitCount < n) { - int c = inbuf[state.InputPosition++]; + int c = *state.inpos++; bitBuffer |= (uint)(c << (int)bitCount); bitCount += 8; } diff --git a/BurnOutSharp.Compression/MSZIP/HuffmanNode.cs b/BurnOutSharp.Compression/MSZIP/HuffmanNode.cs index 475a9a6c..ebb42a52 100644 --- a/BurnOutSharp.Compression/MSZIP/HuffmanNode.cs +++ b/BurnOutSharp.Compression/MSZIP/HuffmanNode.cs @@ -1,28 +1,28 @@ namespace BurnOutSharp.Compression.MSZIP { - public class HuffmanNode + public unsafe struct HuffmanNode { /// /// Number of extra bits or operation /// - public byte ExtraBits; + public byte e; /// /// Number of bits in this code or subcode /// - public byte BitLength; + public byte b; #region v /// /// Literal, length base, or distance base /// - public ushort Base; + public ushort n; /// /// Pointer to next level of table /// - public HuffmanNode[] NextLevel; + public HuffmanNode* t; #endregion } diff --git a/BurnOutSharp.Compression/MSZIP/State.cs b/BurnOutSharp.Compression/MSZIP/State.cs index 57bcd237..022ba9df 100644 --- a/BurnOutSharp.Compression/MSZIP/State.cs +++ b/BurnOutSharp.Compression/MSZIP/State.cs @@ -3,54 +3,54 @@ using static BurnOutSharp.Models.Compression.MSZIP.Constants; namespace BurnOutSharp.Compression.MSZIP { /// - public class State + public unsafe class State { /// /// Current offset within the window /// - public uint WindowPosition; + public uint window_posn; /// /// Bit buffer /// - public uint BitBuffer; + public uint bb; /// /// Bits in bit buffer /// - public uint BitCount; + public uint bk; /// /// Literal/length and distance code lengths /// - public uint[] Lengths = new uint[288 + 32]; + public uint[] ll = new uint[288 + 32]; /// /// Bit length count table /// - public uint[] Counts = new uint[ZIPBMAX + 1]; + public uint[] c = new uint[ZIPBMAX + 1]; /// /// Memory for l[-1..ZIPBMAX-1] /// - public int[] LengthMemory = new int[ZIPBMAX + 1]; + public int[] lx = new int[ZIPBMAX + 1]; /// /// Table stack /// - public HuffmanNode[] TableStack = new HuffmanNode[ZIPBMAX]; + public HuffmanNode*[] u = new HuffmanNode*[ZIPBMAX]; /// /// Values in order of bit length /// - public uint[] Values = new uint[ZIPN_MAX]; + public uint[] v = new uint[ZIPN_MAX]; /// /// Bit offsets, then code stack /// - public uint[] BitOffsets = new uint[ZIPBMAX + 1]; + public uint[] x = new uint[ZIPBMAX + 1]; /// byte* - public int InputPosition; + public byte* inpos; } } \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs index 84630cb9..347b1126 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.cs @@ -218,11 +218,9 @@ namespace BurnOutSharp.Wrappers // 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 - Compression.MSZIP_zlib mszip = new Compression.MSZIP_zlib(); - bool hasLastBlock = false; + Compression.MSZIP.State mszip = new Compression.MSZIP.State(); // Setup Quantum decompression var qtm = new Compression.Quantum.State(); @@ -238,7 +236,7 @@ namespace BurnOutSharp.Wrappers decompressed = dataBlock.CompressedData; break; case Models.MicrosoftCabinet.CompressionType.TYPE_MSZIP: - decompressed = mszip.DecompressMSZIPData(dataBlock.CompressedData, hasLastBlock); + Compression.MSZIP.Decompressor.Decompress(mszip, dataBlock.CompressedSize, dataBlock.CompressedData, dataBlock.UncompressedSize, decompressed); break; case Models.MicrosoftCabinet.CompressionType.TYPE_QUANTUM: Compression.Quantum.Decompressor.Decompress(qtm, dataBlock.CompressedSize, dataBlock.CompressedData, dataBlock.UncompressedSize, decompressed); @@ -250,7 +248,6 @@ namespace BurnOutSharp.Wrappers return null; } - hasLastBlock = true; data.AddRange(decompressed ?? new byte[0]); } diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.MSZIP.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.MSZIP.cs deleted file mode 100644 index 656ae0de..00000000 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.MSZIP.cs +++ /dev/null @@ -1,444 +0,0 @@ -// using static BurnOutSharp.Wrappers.CabinetConstants; -// using static BurnOutSharp.Wrappers.FDIcConstants; -// using static BurnOutSharp.Wrappers.FDIConstants; -// using static BurnOutSharp.Models.Compression.MSZIP.Constants; -// using cab_LONG = System.Int32; -// using cab_off_t = System.UInt32; -// using cab_UBYTE = System.Byte; -// using uint = System.UInt32; -// using cab_UWORD = System.UInt16; - -// namespace BurnOutSharp.Wrappers -// { -// internal unsafe class MSZIPfdi -// { -// /// -// /// BuildHuffmanTree (internal) -// /// -// static cab_LONG BuildHuffmanTree(uint* b, uint n, uint s, cab_UWORD* d, cab_UWORD* e, ref Ziphuft[] t, cab_LONG* m, fdi_decomp_state decomp_state) -// { -// uint a; /* counter for codes of length k */ -// uint el; /* length of EOB code (value 256) */ -// uint f; /* i repeats in table every f entries */ -// cab_LONG g; /* maximum code length */ -// cab_LONG h; /* table level */ -// uint i; /* counter, current code */ -// uint j; /* counter */ -// cab_LONG k; /* number of bits in current code */ -// cab_LONG* l; /* stack of bits per table */ -// uint* p; /* pointer into decomp_state.zip.c[],decomp_state.zip.b[],decomp_state.zip.v[] */ -// Ziphuft* q; /* points to current table */ -// Ziphuft r; /* table entry for structure assignment */ -// cab_LONG w; /* bits before this table == (l * h) */ -// uint* xp; /* pointer into x */ -// cab_LONG y; /* number of dummy codes added */ -// uint z; /* number of entries in current table */ - -// l = decomp_state.zip.lx + 1; - -// // Generate counts for each bit length -// // set length of EOB code, if any -// el = n > 256 ? b[256] : ZIPBMAX; - -// for (i = 0; i < ZIPBMAX + 1; ++i) -// { -// decomp_state.zip.c[i] = 0; -// } - -// p = b; i = n; - -// // assume all entries <= ZIPBMAX -// do -// { -// decomp_state.zip.c[*p]++; p++; -// } while (--i != 0); - -// // null input--all zero length codes -// if (decomp_state.zip.c[0] == n) -// { -// t = null; -// *m = 0; -// return 0; -// } - -// // Find minimum and maximum length, bound *m by those -// for (j = 1; j <= ZIPBMAX; j++) -// { -// if (decomp_state.zip.c[j] != 0) -// break; -// } - -// // minimum code length -// k = (int)j; -// if (*m < j) -// *m = (int)j; - -// for (i = ZIPBMAX; i != 0; i--) -// { -// if (decomp_state.zip.c[i] != 0) -// break; -// } - -// // maximum code length -// g = (int)i; -// if (*m > i) -// *m = (int)i; - -// // Adjust last length count to fill out codes, if needed */ -// for (y = 1 << (int)j; j < i; j++, y <<= 1) -// { -// // bad input: more codes than bits -// if ((y -= (int)decomp_state.zip.c[j]) < 0) -// return 2; -// } - -// if ((y -= (int)decomp_state.zip.c[i]) < 0) -// return 2; - -// decomp_state.zip.c[i] += (uint)y; - -// // Generate starting offsets LONGo the value table for each length -// decomp_state.zip.x[1] = j = 0; -// p = decomp_state.zip.c + 1; xp = decomp_state.zip.x + 2; -// while (--i != 0) -// { -// // note that i == g from above -// *xp++ = (j += *p++); -// } - -// /* Make a table of values in order of bit lengths */ -// p = b; i = 0; -// do -// { -// if ((j = *p++) != 0) -// decomp_state.zip.v[decomp_state.zip.x[j]++] = i; -// } while (++i < n); - -// // Generate the Huffman codes and for each, make the table entries -// decomp_state.zip.x[0] = i = 0; /* first Huffman code is zero */ -// p = decomp_state.zip.v; /* grab values in bit order */ -// h = -1; /* no tables yet--level -1 */ -// w = l[-1] = 0; /* no bits decoded yet */ -// decomp_state.zip.u[0] = null; /* just to keep compilers happy */ -// q = null; /* ditto */ -// z = 0; /* ditto */ - -// /* go through the bit lengths (k already is bits in shortest code) */ -// for (; k <= g; k++) -// { -// a = decomp_state.zip.c[k]; -// while (a-- != 0) -// { -// // here i is the Huffman code of length k bits for value *p -// // make tables up to required level -// while (k > w + l[h]) -// { -// // add bits already decoded -// w += l[h++]; - -// // compute minimum size table less than or equal to *m bits -// if ((z = (uint)(g - w)) > *m) /* upper limit */ -// z = (uint)*m; - -// // try a k-w bit table -// if ((f = (uint)(1 << (int)(j = (uint)(k - w)))) > a + 1) -// { /* too few codes for k-w bit table */ -// f -= a + 1; /* deduct codes from patterns left */ -// xp = decomp_state.zip.c + k; -// while (++j < z) /* try smaller tables up to z bits */ -// { -// if ((f <<= 1) <= *++xp) -// break; /* enough codes to use up j bits */ -// f -= *xp; /* else deduct codes from patterns */ -// } -// } - -// if ((uint)w + j > el && (uint)w < el) -// j = el - w; /* make EOB code end at table */ - -// z = 1 << j; /* table entries for j-bit table */ -// l[h] = j; /* set table size in stack */ - -// /* allocate and link in new table */ -// if (!(q = decomp_state.fdi.alloc((z + 1) * sizeof(Ziphuft)))) -// { -// if (h) -// Free(decomp_state.fdi, decomp_state.zip.u[0]); -// return 3; /* not enough memory */ -// } - -// *t = q + 1; /* link to list for Ziphuft_free() */ -// *(t = &(q.v.t)) = null; -// decomp_state.zip.u[h] = ++q; /* table starts after link */ - -// // connect to last table, if there is one -// if (h != 0) -// { -// decomp_state.zip.x[h] = i; /* save pattern for backing up */ -// r.b = (cab_UBYTE)l[h - 1]; /* bits to dump before this table */ -// r.e = (cab_UBYTE)(16 + j); /* bits in this table */ -// r.t = q; /* pointer to this table */ -// j = (i & ((1 << w) - 1)) >> (w - l[h - 1]); -// decomp_state.zip.u[h - 1][j] = r; /* connect to last table */ -// } -// } - -// /* set up table entry in r */ -// r.b = (cab_UBYTE)(k - w); -// if (p >= decomp_state.zip.v + n) -// { -// r.e = 99; /* out of values--invalid code */ -// } -// else if (*p < s) -// { -// r.e = (cab_UBYTE)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ -// r.n = *p++; /* simple code is just the value */ -// } -// else -// { -// r.e = (cab_UBYTE)e[*p - s]; /* non-simple--look up in lists */ -// r.n = d[*p++ - s]; -// } - -// /* fill code-like entries with r */ -// f = 1 << (k - w); -// for (j = i >> w; j < z; j += f) -// q[j] = r; - -// /* backwards increment the k-bit code i */ -// for (j = 1 << (k - 1); i & j; j >>= 1) -// i ^= j; -// i ^= j; - -// /* backup over finished tables */ -// while ((i & ((1 << w) - 1)) != decomp_state.zip.x[h]) -// w -= l[--h]; /* don't need to update q */ -// } -// } - -// /* return actual size of base table */ -// *m = l[0]; - -// /* Return true (1) if we were given an incomplete table */ -// return y != 0 && g != 1; -// } - -// /// -// /// fdi_Zipinflate_codes (internal) -// /// -// static cab_LONG fdi_Zipinflate_codes(in Ziphuft tl, in Ziphuft td, cab_LONG bl, cab_LONG bd, fdi_decomp_state decomp_state) -// { -// uint e; /* table entry flag/number of extra bits */ -// uint n, d; /* length and index for copy */ -// uint w; /* current window position */ -// Ziphuft t; /* pointer to table entry */ -// uint ml, md; /* masks for bl and bd bits */ -// uint b; /* bit buffer */ -// uint k; /* number of bits in bit buffer */ - -// /* make local copies of globals */ -// b = decomp_state.zip.bb; /* initialize bit buffer */ -// k = decomp_state.zip.bk; -// w = decomp_state.zip.window_posn; /* initialize window position */ - -// /* inflate the coded data */ -// ml = BitMasks[bl]; /* precompute masks for speed */ -// md = BitMasks[bd]; - -// for (; ; ) -// { -// ZIPNEEDBITS((uint)bl) -// if ((e = (t = tl + (b & ml)).e) > 16) -// do -// { -// if (e == 99) -// return 1; -// ZIPDUMPBITS(t.b) -// e -= 16; -// ZIPNEEDBITS(e) -// } while ((e = (t = t.v.t + (b & BitMasks[e])).e) > 16); -// ZIPDUMPBITS(t.b) -// if (e == 16) /* then it's a literal */ -// decomp_state.outbuf[w++] = (cab_UBYTE)t.v.n; -// else /* it's an EOB or a length */ -// { -// /* exit if end of block */ -// if (e == 15) -// break; - -// /* get length of block to copy */ -// ZIPNEEDBITS(e) -// n = t.v.n + (b & BitMasks[e]); -// ZIPDUMPBITS(e); - -// /* decode distance of block to copy */ -// ZIPNEEDBITS((uint)bd) -// if ((e = (t = td + (b & md)).e) > 16) -// do -// { -// if (e == 99) -// return 1; -// ZIPDUMPBITS(t.b) -// e -= 16; -// ZIPNEEDBITS(e) -// } while ((e = (t = t.v.t + (b & BitMasks[e])).e) > 16); -// ZIPDUMPBITS(t.b) -// ZIPNEEDBITS(e) -// d = w - t.v.n - (b & BitMasks[e]); -// ZIPDUMPBITS(e) -// do -// { -// d &= ZIPWSIZE - 1; -// e = ZIPWSIZE - max(d, w); -// e = min(e, n); -// n -= e; -// do -// { -// decomp_state.outbuf[w++] = decomp_state.outbuf[d++]; -// } while (--e); -// } while (n); -// } -// } - -// /* restore the globals from the locals */ -// decomp_state.zip.window_posn = w; /* restore global window pointer */ -// decomp_state.zip.bb = b; /* restore global bit buffer */ -// decomp_state.zip.bk = k; - -// /* done */ -// return 0; -// } - -// /// -// /// fdi_Zipinflate_dynamic (internal) -// /// -// /// decompress an inflated type 2 (dynamic Huffman codes) block. -// /// -// static cab_LONG fdi_Zipinflate_dynamic(fdi_decomp_state decomp_state) -// { -// cab_LONG i; /* temporary variables */ -// uint j; -// uint l; /* last length */ -// uint m; /* mask for bit lengths table */ -// uint n; /* number of lengths to get */ -// Ziphuft tl; /* literal/length code table */ -// Ziphuft td; /* distance code table */ -// cab_LONG bl; /* lookup bits for tl */ -// cab_LONG bd; /* lookup bits for td */ -// uint nb; /* number of bit length codes */ -// uint nl; /* number of literal/length codes */ -// uint nd; /* number of distance codes */ -// uint bitBuffer; /* bit buffer */ -// uint bitCount; /* number of bits in bit buffer */ - -// /* make local bit buffer */ -// bitBuffer = decomp_state.zip.bb; -// bitCount = decomp_state.zip.bk; - -// /* read in table lengths */ -// ZIPNEEDBITS(5) -// nl = 257 + (bitBuffer & 0x1f); /* number of literal/length codes */ -// ZIPDUMPBITS(5) -// ZIPNEEDBITS(5) -// nd = 1 + (bitBuffer & 0x1f); /* number of distance codes */ -// ZIPDUMPBITS(5) -// ZIPNEEDBITS(4) -// nb = 4 + (bitBuffer & 0xf); /* number of bit length codes */ -// ZIPDUMPBITS(4) -// if (nl > 288 || nd > 32) -// return 1; /* bad lengths */ - -// /* read in bit-length-code lengths */ -// for (j = 0; j < nb; j++) -// { -// ZIPNEEDBITS(3) -// state.Lengths[BitLengthOrder[j]] = bitBuffer & 7; -// ZIPDUMPBITS(3) -// } -// for (; j < 19; j++) -// state.Lengths[BitLengthOrder[j]] = 0; - -// /* build decoding table for trees--single level, 7 bit lookup */ -// bl = 7; -// if ((i = BuildHuffmanTree(state.Lengths, 19, 19, null, null, &tl, &bl, decomp_state)) != 0) -// { -// if (i == 1) -// Free(decomp_state.fdi, tl); -// return i; /* incomplete code set */ -// } - -// /* read in literal and distance code lengths */ -// n = nl + nd; -// m = BitMasks[bl]; -// i = l = 0; -// while ((uint)i < n) -// { -// ZIPNEEDBITS((uint)bl) -// j = (td = tl + (bitBuffer & m)).b; -// ZIPDUMPBITS(j) -// j = td.v.n; -// if (j < 16) /* length of code in bits (0..15) */ -// state.Lengths[i++] = l = j; /* save last length in l */ -// else if (j == 16) /* repeat last length 3 to 6 times */ -// { -// ZIPNEEDBITS(2) -// j = 3 + (bitBuffer & 3); -// ZIPDUMPBITS(2) -// if ((uint)i + j > n) -// return 1; -// while (j--) -// state.Lengths[i++] = l; -// } -// else if (j == 17) /* 3 to 10 zero length codes */ -// { -// ZIPNEEDBITS(3) -// j = 3 + (bitBuffer & 7); -// ZIPDUMPBITS(3) -// if ((uint)i + j > n) -// return 1; -// while (j--) -// state.Lengths[i++] = 0; -// l = 0; -// } -// else /* j == 18: 11 to 138 zero length codes */ -// { -// ZIPNEEDBITS(7) -// j = 11 + (bitBuffer & 0x7f); -// ZIPDUMPBITS(7) -// if ((uint)i + j > n) -// return 1; -// while (j--) -// state.Lengths[i++] = 0; -// l = 0; -// } -// } - -// /* free decoding table for trees */ -// Free(decomp_state.fdi, tl); - -// /* restore the global bit buffer */ -// decomp_state.zip.bb = bitBuffer; -// decomp_state.zip.bk = bitCount; - -// /* build the decoding tables for literal/length and distance codes */ -// bl = ZIPLBITS; -// if ((i = BuildHuffmanTree(state.Lengths, nl, 257, CopyLengths, LiteralExtraBits, &tl, &bl, decomp_state)) != 0) -// { -// if (i == 1) -// Free(decomp_state.fdi, tl); -// return i; /* incomplete code set */ -// } -// bd = ZIPDBITS; -// BuildHuffmanTree(state.Lengths + nl, nd, 0, CopyOffsets, DistanceExtraBits, &td, &bd, decomp_state); - -// /* decompress until an end-of-block code */ -// if (fdi_Zipinflate_codes(tl, td, bl, bd, decomp_state)) -// return 1; - -// /* free the decoding tables, return */ -// Free(decomp_state.fdi, tl); -// Free(decomp_state.fdi, td); -// return 0; -// } -// } -// } \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs deleted file mode 100644 index 1c57ad2d..00000000 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.cs +++ /dev/null @@ -1,1561 +0,0 @@ -// using System; -// using System.Collections.Generic; -// using System.IO; -// using System.Linq; -// using BurnOutSharp.Compression; -// using BurnOutSharp.Compression.MSZIP; -// using static BurnOutSharp.Wrappers.CabinetConstants; -// using static BurnOutSharp.Wrappers.FDIcConstants; -// using static BurnOutSharp.Wrappers.FDIConstants; - -// namespace BurnOutSharp.Wrappers -// { -// public partial class MicrosoftCabinet -// { -// // TEMP AREA TO MAKE BUILDS WORK - -// private static void SetLastError(int error) { } -// private class HFDI : FDI_Int { } -// private const int ERROR_INVALID_HANDLE = 0; -// private const int FDIERROR_NONE = 0; -// private const int ERROR_BAD_ARGUMENTS = 0; -// private const int FDIERROR_ALLOC_FAIL = 0; -// private static void ERR(object o) { } - -// // END TEMP AREA - -// static void set_error(FDI_Int fdi, int oper, int err) -// { -// fdi.perf.erfOper = oper; -// fdi.perf.erfType = err; -// fdi.perf.fError = true; -// if (err != 0) SetLastError(err); -// } - -// static FDI_Int get_fdi_ptr(HFDI hfdi) -// { -// FDI_Int fdi = hfdi as FDI_Int; - -// if (fdi == null || fdi.magic != FDI_INT_MAGIC) -// { -// SetLastError(ERROR_INVALID_HANDLE); -// return null; -// } -// return fdi; -// } - -// /************************************************************************* -// * make_decode_table (internal) -// * -// * This function was coded by David Tritscher. It builds a fast huffman -// * decoding table out of just a canonical huffman code lengths table. -// * -// * PARAMS -// * 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 -// * OK: 0 -// * error: 1 -// */ -// static int make_decode_table(uint nsyms, uint nbits, byte[] length, ushort[] table) -// { -// ushort sym; -// uint leaf; -// byte bit_num = 1; -// uint fill; -// uint pos = 0; /* the current position in the decode table */ -// uint table_mask = (uint)(1 << (int)nbits); -// uint bit_mask = table_mask >> 1; /* don't do 0 length codes */ -// uint 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 = (ushort)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] = (ushort)next_symbol++; -// } -// /* follow the path and select either left or right for next bit */ -// leaf = (uint)(table[leaf] << 1); -// if (((pos >> (int)(15 - fill)) & 1) != 0) 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 (length[sym] != 0) return 1; -// return 0; -// } - -// /************************************************************************* -// * checksum (internal) -// */ -// static uint checksum(byte[] data, ushort bytes, uint csum) -// { -// int dataPtr = 0; // TODO: Confirm that it's always starting at the beginning of data - -// int len; -// uint ul = 0; - -// for (len = bytes >> 2; len-- > 0; dataPtr += 4) -// { -// csum ^= (uint)((data[dataPtr + 0]) | (data[dataPtr + 1] << 8) | (data[dataPtr + 2] << 16) | (data[dataPtr + 3] << 24)); -// } - -// switch (bytes & 3) -// { -// case 3: ul |= (uint)(data[dataPtr++] << 16); goto case 2; -// /* fall through */ -// case 2: ul |= (uint)(data[dataPtr++] << 8); goto case 1; -// /* fall through */ -// case 1: ul |= data[dataPtr]; break; -// } -// csum ^= ul; - -// return csum; -// } - -// /*********************************************************************** -// * FDICreate (CABINET.20) -// * -// * Provided with several callbacks (all of them are mandatory), -// * returns a handle which can be used to perform operations -// * on cabinet files. -// * -// * PARAMS -// * pfnalloc [I] A pointer to a function which allocates ram. Uses -// * the same interface as malloc. -// * pfnfree [I] A pointer to a function which frees ram. Uses the -// * same interface as free. -// * pfnopen [I] A pointer to a function which opens a file. Uses -// * the same interface as _open. -// * pfnread [I] A pointer to a function which reads from a file into -// * a caller-provided buffer. Uses the same interface -// * as _read -// * pfnwrite [I] A pointer to a function which writes to a file from -// * a caller-provided buffer. Uses the same interface -// * as _write. -// * pfnclose [I] A pointer to a function which closes a file handle. -// * Uses the same interface as _close. -// * pfnseek [I] A pointer to a function which seeks in a file. -// * Uses the same interface as _lseek. -// * cpuType [I] The type of CPU; ignored in wine (recommended value: -// * cpuUNKNOWN, aka -1). -// * perf [IO] A pointer to an ERF structure. When FDICreate -// * returns an error condition, error information may -// * be found here as well as from GetLastError. -// * -// * RETURNS -// * On success, returns an FDI handle of type HFDI. -// * On failure, the null file handle is returned. Error -// * info can be retrieved from perf. -// * -// * INCLUDES -// * fdi.h -// * -// */ -// HFDI FDICreate( -// Action pfnalloc, -// Action pfnfree, -// Action pfnopen, -// Action pfnread, -// Action pfnwrite, -// Action pfnclose, -// Action pfnseek, -// int cpuType, -// ERF perf) -// { -// FDI_Int fdi; - -// // System.Diagnostics.Debug.WriteLine("(pfnalloc == ^%p, pfnfree == ^%p, pfnopen == ^%p, pfnread == ^%p, pfnwrite == ^%p, " -// // "pfnclose == ^%p, pfnseek == ^%p, cpuType == %d, perf == ^%p)\n", -// // pfnalloc, pfnfree, pfnopen, pfnread, pfnwrite, pfnclose, pfnseek, -// // cpuType, perf); - -// if ((pfnalloc == null) || (pfnfree == null)) -// { -// perf.erfOper = FDIERROR_NONE; -// perf.erfType = ERROR_BAD_ARGUMENTS; -// perf.fError = true; - -// SetLastError(ERROR_BAD_ARGUMENTS); -// return null; -// } - -// if (((fdi = pfnalloc(sizeof(FDI_Int)))) == null) -// { -// perf.erfOper = FDIERROR_ALLOC_FAIL; -// perf.erfType = 0; -// perf.fError = true; -// return null; -// } - -// fdi.magic = FDI_INT_MAGIC; -// fdi.alloc = pfnalloc; -// fdi.free = pfnfree; -// fdi.open = pfnopen; -// fdi.read = pfnread; -// fdi.write = pfnwrite; -// fdi.close = pfnclose; -// fdi.seek = pfnseek; -// /* no-brainer: we ignore the cpu type; this is only used -// for the 16-bit versions in Windows anyhow... */ -// fdi.perf = perf; - -// return (HFDI)fdi; -// } - -// /******************************************************************* -// * FDI_getoffset (internal) -// * -// * returns the file pointer position of a file handle. -// */ -// static int FDI_getoffset(FDI_Int fdi, Stream hf) -// { -// return fdi.seek(hf, 0, SeekOrigin.Current); -// } - -// /********************************************************************** -// * FDI_read_string (internal) -// * -// * allocate and read an arbitrarily long string from the cabinet -// */ -// static string FDI_read_string(FDI_Int fdi, Stream hf, long cabsize) -// { -// int len = 256, -// @base = FDI_getoffset(fdi, hf), -// maxlen = (int)(cabsize - @base); -// bool ok = false; -// uint i; -// byte[] buf = null; - -// //System.Diagnostics.Debug.WriteLine("(fdi == %p, hf == %Id, cabsize == %ld)\n", fdi, hf, cabsize); - -// do -// { -// if (len > maxlen) len = maxlen; -// if ((buf = fdi.alloc(len)) == null) break; -// if (fdi.read(hf, buf, len) == 0) break; - -// /* search for a null terminator in what we've just read */ -// for (i = 0; i < len; i++) -// { -// if (buf[i] == 0) { ok = true; break; } -// } - -// if (!ok) -// { -// if (len == maxlen) -// { -// ERR("cabinet is truncated\n"); -// break; -// } -// /* The buffer is too small for the string. Reset the file to the point -// * where we started, free the buffer and increase the size for the next try -// */ -// fdi.seek(hf, @base, SeekOrigin.Begin); -// fdi.free(buf); -// buf = null; -// len *= 2; -// } -// } while (!ok); - -// if (!ok) -// { -// if (buf != null) -// fdi.free(buf); -// else -// ERR("out of memory!\n"); -// return null; -// } - -// /* otherwise, set the stream to just after the string and return */ -// fdi.seek(hf, @base + strlen((char*)buf) + 1, SeekOrigin.Begin); - -// return System.Text.Encoding.ASCII.GetString(buf); -// } - -// /****************************************************************** -// * FDI_read_entries (internal) -// * -// * process the cabinet header in the style of FDIIsCabinet, but -// * without the sanity checks (and bug) -// */ -// static bool FDI_read_entries( -// FDI_Int fdi, -// Stream hf, -// FDICABINETINFO pfdici, -// MORE_ISCAB_INFO pmii) -// { -// int num_folders, num_files, header_resv, folder_resv = 0; -// int cabsize; -// ushort setid, cabidx, flags; -// byte[] buf = new byte[64]; byte block_resv; -// string prevname = null, previnfo = null, nextname = null, nextinfo = null; - -// //System.Diagnostics.Debug.WriteLine("(fdi == ^%p, hf == %Id, pfdici == ^%p)\n", fdi, hf, pfdici); - -// /* read in the CFHEADER */ -// if (fdi.read(hf, buf, cfhead_SIZEOF) != cfhead_SIZEOF) -// { -// if (pmii != null) set_error(fdi, FDIERROR_NOT_A_CABINET, 0); -// return false; -// } - -// /* check basic MSCF signature */ -// if (EndGetI32(buf + cfhead_Signature) != 0x4643534d) -// { -// if (pmii != null) set_error(fdi, FDIERROR_NOT_A_CABINET, 0); -// return false; -// } - -// /* get the cabinet size */ -// cabsize = EndGetI32(buf + cfhead_CabinetSize); - -// /* get the number of folders */ -// num_folders = EndGetI16(buf + cfhead_NumFolders); - -// /* get the number of files */ -// num_files = EndGetI16(buf + cfhead_NumFiles); - -// /* setid */ -// setid = EndGetI16(buf + cfhead_SetID); - -// /* cabinet (set) index */ -// cabidx = EndGetI16(buf + cfhead_CabinetIndex); - -// /* check the header revision */ -// if ((buf[cfhead_MajorVersion] > 1) || -// (buf[cfhead_MajorVersion] == 1 && buf[cfhead_MinorVersion] > 3)) -// { -// WARN("cabinet format version > 1.3\n"); -// if (pmii != null) set_error(fdi, FDIERROR_UNKNOWN_CABINET_VERSION, 0 /* ? */ ); -// return false; -// } - -// /* pull the flags out */ -// flags = EndGetI16(buf + cfhead_Flags); - -// /* read the reserved-sizes part of header, if present */ -// if ((flags & cfheadRESERVE_PRESENT) != 0) -// { -// if (fdi.read(hf, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF) -// { -// ERR("bunk reserve-sizes?\n"); -// if (pmii != null) set_error(fdi, FDIERROR_CORRUPT_CABINET, 0 /* ? */ ); -// return false; -// } - -// header_resv = EndGetI16(buf + cfheadext_HeaderReserved); -// if (pmii != null) pmii.header_resv = header_resv; -// folder_resv = buf[cfheadext_FolderReserved]; -// if (pmii != null) pmii.folder_resv = folder_resv; -// block_resv = buf[cfheadext_DataReserved]; -// if (pmii != null) pmii.block_resv = block_resv; - -// if (header_resv > 60000) -// { -// WARN("WARNING; header reserved space > 60000\n"); -// } - -// /* skip the reserved header */ -// if ((header_resv) && (fdi.seek(hf, header_resv, SEEK_CUR) == -1)) -// { -// ERR("seek failure: header_resv\n"); -// if (pmii) set_error(fdi, FDIERROR_CORRUPT_CABINET, 0 /* ? */ ); -// return false; -// } -// } - -// if ((flags & cfheadPREV_CABINET) != 0) -// { -// prevname = FDI_read_string(fdi, hf, cabsize); -// if (prevname == null) -// { -// if (pmii != null) set_error(fdi, FDIERROR_CORRUPT_CABINET, 0 /* ? */ ); -// return false; -// } -// else -// if (pmii != null) -// pmii.prevname = prevname; -// else -// fdi.free(prevname); -// previnfo = FDI_read_string(fdi, hf, cabsize); -// if (previnfo != null) -// { -// if (pmii != null) -// pmii.previnfo = previnfo; -// else -// fdi.free(previnfo); -// } -// } - -// if ((flags & cfheadNEXT_CABINET) != 0) -// { -// if (pmii != null) -// pmii.hasnext = true; -// nextname = FDI_read_string(fdi, hf, cabsize); -// if (nextname == null) -// { -// if (((flags & cfheadPREV_CABINET) != 0) && pmii != null) -// { -// if (pmii.prevname != null) fdi.free(prevname); -// if (pmii.previnfo != null) fdi.free(previnfo); -// } -// set_error(fdi, FDIERROR_CORRUPT_CABINET, 0 /* ? */ ); -// return false; -// } -// else -// if (pmii != null) -// pmii.nextname = nextname; -// else -// fdi.free(nextname); -// nextinfo = FDI_read_string(fdi, hf, cabsize); -// if (nextinfo != null) -// { -// if (pmii != null) -// pmii.nextinfo = nextinfo; -// else -// fdi.free(nextinfo); -// } -// } - -// /* we could process the whole cabinet searching for problems; -// instead lets stop here. Now let's fill out the paperwork */ -// pfdici.cbCabinet = cabsize; -// pfdici.cFolders = (ushort)num_folders; -// pfdici.cFiles = (ushort)num_files; -// pfdici.setID = setid; -// pfdici.iCabinet = cabidx; -// pfdici.fReserve = (flags & cfheadRESERVE_PRESENT) != 0; -// pfdici.hasprev = (flags & cfheadPREV_CABINET) != 0; -// pfdici.hasnext = (flags & cfheadNEXT_CABINET) != 0; -// return true; -// } - -// /*********************************************************************** -// * FDIIsCabinet (CABINET.21) -// * -// * Informs the caller as to whether or not the provided file handle is -// * really a cabinet or not, filling out the provided PFDICABINETINFO -// * structure with information about the cabinet. Brief explanations of -// * the elements of this structure are available as comments accompanying -// * its definition in wine's include/fdi.h. -// * -// * PARAMS -// * hfdi [I] An HFDI from FDICreate -// * hf [I] The file handle about which the caller inquires -// * pfdici [IO] Pointer to a PFDICABINETINFO structure which will -// * be filled out with information about the cabinet -// * file indicated by hf if, indeed, it is determined -// * to be a cabinet. -// * -// * RETURNS -// * TRUE if the file is a cabinet. The info pointed to by pfdici will -// * be provided. -// * false if the file is not a cabinet, or if an error was encountered -// * while processing the cabinet. The PERF structure provided to -// * FDICreate can be queried for more error information. -// * -// * INCLUDES -// * fdi.c -// */ -// bool FDIIsCabinet(HFDI hfdi, Stream hf, FDICABINETINFO pfdici) -// { -// bool rv; -// FDI_Int fdi = get_fdi_ptr(hfdi); - -// //System.Diagnostics.Debug.WriteLine("(hfdi == ^%p, hf == ^%Id, pfdici == ^%p)\n", hfdi, hf, pfdici); - -// if (fdi == null) return false; - -// if (pfdici == null) -// { -// SetLastError(ERROR_BAD_ARGUMENTS); -// return false; -// } -// rv = FDI_read_entries(fdi, hf, pfdici, null); - -// if (rv) -// pfdici.hasnext = false; /* yuck. duplicate apparent cabinet.dll bug */ - -// return rv; -// } - -// /**************************************************** -// * NONEfdi_decomp(internal) -// */ -// static int NONEfdi_decomp(int inlen, int outlen, fdi_decomp_state decomp_state) -// { -// if (inlen != outlen) return DECR_ILLEGALDATA; -// if (outlen > CAB_BLOCKMAX) return DECR_DATAFORMAT; -// Array.Copy(decomp_state.inbuf, decomp_state.outbuf, inlen); -// return DECR_OK; -// } -// } - -// /// -// #region fdi.h - -// /// -// internal class ERF -// { -// /// -// /// FCI/FDI error code - see {FCI,FDI}ERROR_XXX for details. -// /// -// public int erfOper; - -// /// -// /// Optional error value filled in by FCI/FDI. -// /// -// public int erfType; - -// /// -// /// true => error present -// /// -// public bool fError; -// } - -// /**********************************************************************/ - -// /// -// internal static class FDIConstants -// { -// public const ushort CB_MAX_CHUNK = 32768; -// public const uint CB_MAX_DISK = 0x7fffffff; -// public const int CB_MAX_FILENAME = 256; -// public const int CB_MAX_CABINET_NAME = 256; -// public const int CB_MAX_CAB_PATH = 256; -// public const int CB_MAX_DISK_NAME = 256; - -// /**********************************************************************/ - -// /// -// /// Mask for compression type -// /// -// public const ushort tcompMASK_TYPE = 0x000F; - -// /// -// /// No compression -// /// -// public const ushort tcompTYPE_NONE = 0x0000; - -// /// -// /// MSZIP -// /// -// public const ushort tcompTYPE_MSZIP = 0x0001; - -// /// -// /// Quantum -// /// -// public const ushort tcompTYPE_QUANTUM = 0x0002; - -// /// -// /// LZX -// /// -// public const ushort tcompTYPE_LZX = 0x0003; - -// /// -// /// Unspecified compression type -// /// -// public const ushort tcompBAD = 0x000F; - -// /// -// /// Mask for LZX Compression Memory -// /// -// public const ushort tcompMASK_LZX_WINDOW = 0x1F00; - -// /// -// /// Lowest LZX Memory (15) -// /// -// public const ushort tcompLZX_WINDOW_LO = 0x0F00; - -// /// -// /// Highest LZX Memory (21) -// /// -// public const ushort tcompLZX_WINDOW_HI = 0x1500; - -// /// -// /// Amount to shift over to get int -// /// -// public const ushort tcompSHIFT_LZX_WINDOW = 8; - -// /// -// /// Reserved bits (high 3 bits) -// /// -// public const ushort tcompMASK_RESERVED = 0xE000; - -// /**********************************************************************/ - -// public const byte _A_NAME_IS_UTF = 0x80; - -// public const byte _A_EXEC = 0x40; - -// /**********************************************************************/ - -// /// -// /// FDI does detection -// /// -// public const int cpuUNKNOWN = -1; - -// /// -// /// '286 opcodes only -// /// -// public const int cpu80286 = 0; - -// /// -// /// '386 opcodes used -// /// -// public const int cpu80386 = 1; - -// /**********************************************************************/ -// } - -// /**********************************************************************/ - -// /// -// internal enum FDIERROR -// { -// FDIERROR_NONE, -// FDIERROR_CABINET_NOT_FOUND, -// FDIERROR_NOT_A_CABINET, -// FDIERROR_UNKNOWN_CABINET_VERSION, -// FDIERROR_CORRUPT_CABINET, -// FDIERROR_ALLOC_FAIL, -// FDIERROR_BAD_COMPR_TYPE, -// FDIERROR_MDI_FAIL, -// FDIERROR_TARGET_FILE, -// FDIERROR_RESERVE_MISMATCH, -// FDIERROR_WRONG_CABINET, -// FDIERROR_USER_ABORT, -// } - -// /**********************************************************************/ - -// /// -// internal class FDICABINETINFO -// { -// /// -// /// Total length of cabinet file -// /// -// public long cbCabinet; - -// /// -// /// Count of folders in cabinet -// /// -// public ushort cFolders; - -// /// -// /// Count of files in cabinet -// /// -// public ushort cFiles; - -// /// -// /// Cabinet set ID -// /// -// public ushort setID; - -// /// -// /// Cabinet number in set (0 based) -// /// -// public ushort iCabinet; - -// /// -// /// true => RESERVE present in cabinet -// /// -// public bool fReserve; - -// /// -// /// true => Cabinet is chained prev -// /// -// public bool hasprev; - -// /// -// /// true => Cabinet is chained next -// /// -// public bool hasnext; -// } - -// /**********************************************************************/ - -// /// -// internal enum FDIDECRYPTTYPE -// { -// /// -// /// New cabinet -// /// -// fdidtNEW_CABINET, - -// /// -// /// New folder -// /// -// fdidtNEW_FOLDER, - -// /// -// /// Decrypt a data block -// /// -// fdidtDECRYPT, -// } - -// /**********************************************************************/ - -// /// -// internal class FDIDECRYPT -// { -// /// -// /// Command type (selects union below) -// /// -// public FDIDECRYPTTYPE fdidt; - -// /// -// /// Decryption context -// /// -// public object pvUser; - -// #region DUMMYUNIONNAME - -// /// -// /// fdidtNEW_CABINET -// /// -// public DUMMYUNIONNAMEcabinet cabinet; - -// /// -// /// fdidtNEW_FOLDER -// /// -// public DUMMYUNIONNAMEfolder folder; - -// /// -// /// fdidtDECRYPT -// /// -// public DUMMYUNIONNAMEdecrypt decrypt; - -// /// -// /// fdidtNEW_CABINET -// /// -// public class DUMMYUNIONNAMEcabinet -// { -// /// -// /// RESERVE section from CFHEADER -// /// -// public object pHeaderReserve; - -// /// -// /// Size of pHeaderReserve -// /// -// public ushort cbHeaderReserve; - -// /// -// /// Cabinet set ID -// /// -// public ushort setID; - -// /// -// /// Cabinet number in set (0 based) -// /// -// public int iCabinet; -// } - -// /// -// /// fdidtNEW_FOLDER -// /// -// public class DUMMYUNIONNAMEfolder -// { -// /// -// /// RESERVE section from CFFOLDER -// /// -// public object pFolderReserve; - -// /// -// /// Size of pFolderReserve -// /// -// public ushort cbFolderReserve; - -// /// -// /// Folder number in cabinet (0 based) -// /// -// public ushort iFolder; -// } - -// /// -// /// fdidtDECRYPT -// /// -// public class DUMMYUNIONNAMEdecrypt -// { -// /// -// /// RESERVE section from CFDATA -// /// -// public object pDataReserve; - -// /// -// /// Size of pDataReserve -// /// -// public ushort cbDataReserve; - -// /// -// /// Data buffer -// /// -// public object pbData; - -// /// -// /// Size of data buffer -// /// -// public ushort cbData; - -// /// -// /// true if this is a split data block -// /// -// public bool fSplit; - -// /// -// /// 0 if this is not a split block, or the first piece of a split block; -// /// Greater than 0 if this is the second piece of a split block. -// /// -// public ushort cbPartial; -// } - -// #endregion -// } - -// /**********************************************************************/ - -// /// -// internal class FDINOTIFICATION -// { -// public long cb; -// public char[] psz1; -// public char[] psz2; - -// /// -// /// Points to a 256 character buffer -// /// -// public char[] psz3; - -// /// -// /// Value for client -// /// -// public object pv; - -// public Stream hf; - -// public ushort date; -// public ushort time; -// public ushort attribs; - -// /// -// /// Cabinet set ID -// /// -// public ushort setID; - -// /// -// /// Cabinet number (0-based) -// /// -// public ushort iCabinet; - -// /// -// /// Folder number (0-based) -// /// -// public ushort iFolder; - -// public FDIERROR fdie; -// } - -// /// -// internal enum FDINOTIFICATIONTYPE -// { -// /// -// /// General information about cabinet -// /// -// fdintCABINET_INFO, - -// /// -// /// First file in cabinet is continuation -// /// -// fdintPARTIAL_FILE, - -// /// -// /// File to be copied -// /// -// fdintCOPY_FILE, - -// /// -// /// Close the file, set relevant info -// /// -// fdintCLOSE_FILE_INFO, - -// /// -// /// File continued to next cabinet -// /// -// fdintNEXT_CABINET, - -// /// -// /// Enumeration status -// /// -// fdintENUMERATE, -// } - -// /// -// internal class FDINOTIFY -// { -// public FDINOTIFICATIONTYPE fdint; - -// public FDINOTIFICATION pfdin; -// } - -// /// -// internal class FDISPILLFILE -// { -// /// -// /// Set to { '*', '\0' } -// /// -// public char[] ach = new char[2]; - -// /// -// /// Required spill file size -// /// -// public long cbFile; -// } - -// #endregion - -// /// -// #region cabinet.h - -// /// -// internal static class CabinetConstants -// { -// public const int CAB_SPLITMAX = (10); - -// public const int CAB_SEARCH_SIZE = (32 * 1024); - -// /* structure offsets */ -// public const byte cfhead_Signature = (0x00); -// public const byte cfhead_CabinetSize = (0x08); -// public const byte cfhead_FileOffset = (0x10); -// public const byte cfhead_MinorVersion = (0x18); -// public const byte cfhead_MajorVersion = (0x19); -// public const byte cfhead_NumFolders = (0x1A); -// public const byte cfhead_NumFiles = (0x1C); -// public const byte cfhead_Flags = (0x1E); -// public const byte cfhead_SetID = (0x20); -// public const byte cfhead_CabinetIndex = (0x22); -// public const byte cfhead_SIZEOF = (0x24); -// public const byte cfheadext_HeaderReserved = (0x00); -// public const byte cfheadext_FolderReserved = (0x02); -// public const byte cfheadext_DataReserved = (0x03); -// public const byte cfheadext_SIZEOF = (0x04); -// public const byte cffold_DataOffset = (0x00); -// public const byte cffold_NumBlocks = (0x04); -// public const byte cffold_CompType = (0x06); -// public const byte cffold_SIZEOF = (0x08); -// public const byte cffile_UncompressedSize = (0x00); -// public const byte cffile_FolderOffset = (0x04); -// public const byte cffile_FolderIndex = (0x08); -// public const byte cffile_Date = (0x0A); -// public const byte cffile_Time = (0x0C); -// public const byte cffile_Attribs = (0x0E); -// public const byte cffile_SIZEOF = (0x10); -// public const byte cfdata_CheckSum = (0x00); -// public const byte cfdata_CompressedSize = (0x04); -// public const byte cfdata_UncompressedSize = (0x06); -// public const byte cfdata_SIZEOF = (0x08); - -// /* flags */ -// public const ushort cffoldCOMPTYPE_MASK = (0x000f); -// public const ushort cffoldCOMPTYPE_NONE = (0x0000); -// public const ushort cffoldCOMPTYPE_MSZIP = (0x0001); -// public const ushort cffoldCOMPTYPE_QUANTUM = (0x0002); -// public const ushort cffoldCOMPTYPE_LZX = (0x0003); -// public const ushort cfheadPREV_CABINET = (0x0001); -// public const ushort cfheadNEXT_CABINET = (0x0002); -// public const ushort cfheadRESERVE_PRESENT = (0x0004); -// public const ushort cffileCONTINUED_FROM_PREV = (0xFFFD); -// public const ushort cffileCONTINUED_TO_NEXT = (0xFFFE); -// public const ushort cffileCONTINUED_PREV_AND_NEXT = (0xFFFF); -// public const byte cffile_A_RDONLY = (0x01); -// public const byte cffile_A_HIDDEN = (0x02); -// public const byte cffile_A_SYSTEM = (0x04); -// public const byte cffile_A_ARCH = (0x20); -// public const byte cffile_A_EXEC = (0x40); -// public const byte cffile_A_NAME_IS_UTF = (0x80); - -// /****************************************************************************/ -// /* our archiver information / state */ - -// /* MSZIP stuff */ - -// /* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed -// * blocks have zero growth. MSZIP guarantees that it won't grow above -// * uncompressed size by more than 12 bytes. LZX guarantees it won't grow -// * more than 6144 bytes. -// */ -// public const int CAB_BLOCKMAX = (32768); -// public const int CAB_INPUTMAX = (CAB_BLOCKMAX + 6144); - -// /* SESSION Operation */ -// public const uint EXTRACT_FILLFILELIST = 0x00000001; -// public const uint EXTRACT_EXTRACTFILES = 0x00000002; -// } - -// /****************************************************************************/ - -// /// -// internal class cab_file -// { -// /// -// /// next file in sequence -// /// -// public cab_file next; - -// /// -// /// folder that contains this file -// /// -// public cab_folder folder; - -// /// -// /// output name of file -// /// -// public string filename; - -// /// -// /// open file handle or null -// /// -// public Stream fh; - -// /// -// /// uncompressed length of file -// /// -// public uint length; - -// /// -// /// uncompressed offset in folder -// /// -// public uint offset; - -// /// -// /// magic index number of folder -// /// -// public ushort index; - -// /// -// /// MS-DOS time/date/attributes -// /// -// public ushort time, date, attribs; -// } - -// /// -// internal class cab_folder -// { -// /// -// /// next folder in sequence -// /// -// public cab_folder next; - -// /// -// /// cabinet(s) this folder spans -// /// -// public cabinet[] cab = new cabinet[CAB_SPLITMAX]; - -// /// -// /// offset to data blocks -// /// -// public uint[] offset = new uint[CAB_SPLITMAX]; - -// /// -// /// compression format/window size -// /// -// public ushort comp_type; - -// /// -// /// compressed size of folder -// /// -// public uint comp_size; - -// /// -// /// number of split blocks + 1 -// /// -// public byte num_splits; - -// /// -// /// total number of blocks -// /// -// public ushort num_blocks; - -// /// -// /// the first split file -// /// -// public cab_file contfile; -// } - -// /// -// internal class cabinet -// { -// /// -// /// next cabinet in sequence -// /// -// public cabinet next; - -// /// -// /// input name of cabinet -// /// -// public string filename; - -// /// -// /// open file handle or null -// /// -// public Stream fh; - -// /// -// /// length of cabinet file -// /// -// public uint filelen; - -// /// -// /// offset to data blocks in file -// /// -// public uint blocks_off; - -// /// -// /// multipart cabinet chains -// /// -// public cabinet prevcab, nextcab; - -// /// -// /// and their filenames -// /// -// public string prevname, nextname; - -// /// -// /// and their visible names -// /// -// public string previnfo, nextinfo; - -// /// -// /// first folder in this cabinet -// /// -// public cab_folder folders; - -// /// -// /// first file in this cabinet -// /// -// public cab_file files; - -// /// -// /// reserved space in datablocks -// /// -// public byte block_resv; - -// /// -// /// header flags -// /// -// public byte flags; -// } - -// /// -// internal class cds_forward -// { -// /// -// /// current folder we're extracting from -// /// -// public cab_folder current; - -// /// -// /// uncompressed offset within folder -// /// -// public uint offset; - -// /// -// /// (high level) start of data to use up -// /// -// public byte* outpos; - -// /// -// /// (high level) amount of data to use up -// /// -// public ushort outlen; - -// /// -// /// at which split in current folder? -// /// -// public ushort split; - -// /// -// /// chosen compress fn -// /// -// public Func decompress; - -// /// -// /// +2 for lzx bitbuffer overflows! -// /// -// public byte[] inbuf = new byte[CAB_INPUTMAX + 2]; - -// public byte[] outbuf = new byte[CAB_INPUTMAX]; - -// public byte[] q_length_base = new byte[27], q_length_extra = new byte[27], q_extra_bits = new byte[42]; - -// public uint[] q_position_base = new uint[42]; - -// public uint[] lzx_position_base = new uint[51]; - -// public uint[] extra_bits = new uint[51]; - -// #region methods - -// public Compression.MSZIP.State zip; -// public Compression.Quantum.State qtm; -// public Compression.LZX.State lzx; - -// #endregion -// } - -// /* -// * the rest of these are somewhat kludgy macros which are shared between fdi.c -// * and cabextract.c. -// */ - -// /* Huffman macros */ - -// // #define TABLEBITS(tbl) (LZX_##tbl##_TABLEBITS) -// // #define MAXSYMBOLS(tbl) (LZX_##tbl##_MAXSYMBOLS) -// // #define SYMTABLE(tbl) (LZX(tbl##_table)) -// // #define LENTABLE(tbl) (LZX(tbl##_len)) - -// /* 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. -// */ -// // #define BUILD_TABLE(tbl) \ -// // if (make_decode_table( \ -// // MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl) \ -// // )) { return DECR_ILLEGALDATA; } - -// /// -// internal class FILELIST -// { -// public string FileName; - -// public FILELIST next; - -// public bool DoExtract; -// } - -// /// -// internal class SESSION -// { -// public int FileSize; - -// public ERF Error; - -// public FILELIST FileList; - -// public int FileCount; - -// public int Operation; - -// public char[] Destination = new char[CB_MAX_CAB_PATH]; - -// public char[] CurrentFile = new char[CB_MAX_CAB_PATH]; - -// public char[] Reserved = new char[CB_MAX_CAB_PATH]; - -// public FILELIST FilterList; -// } - -// #endregion - -// /// -// #region fdi.c - -// internal static class FDIcConstants -// { -// public const uint FDI_INT_MAGIC = 0xfdfdfd05; - -// public const int DECR_OK = (0); -// public const int DECR_DATAFORMAT = (1); -// public const int DECR_ILLEGALDATA = (2); -// public const int DECR_NOMEMORY = (3); -// public const int DECR_CHECKSUM = (4); -// public const int DECR_INPUT = (5); -// public const int DECR_OUTPUT = (6); -// public const int DECR_USERABORT = (7); -// } - -// /// -// internal class fdi_file -// { -// /// -// /// next file in sequence -// /// -// public fdi_file next; - -// /// -// /// output name of file -// /// -// public string filename; - -// /// -// /// open file handle or null -// /// -// public Stream fh; - -// /// -// /// uncompressed length of file -// /// -// public uint length; - -// /// -// /// uncompressed offset in folder -// /// -// public uint offset; - -// /// -// /// magic index number of folder -// /// -// public ushort index; - -// /// -// /// MS-DOS time/date/attributes -// /// -// public ushort time, date, attribs; - -// /// -// /// never to be processed -// /// -// public bool oppressed; -// } - -// /// -// internal class fdi_folder -// { -// /// -// /// next folder in sequence -// /// -// public fdi_folder next; - -// /// -// /// offset to data blocks (32 bit) -// /// -// public uint offset; - -// /// -// /// compression format/window size -// /// -// public ushort comp_type; - -// /// -// /// compressed size of folder -// /// -// public uint comp_size; - -// /// -// /// number of split blocks + 1 -// /// -// public byte num_splits; - -// /// -// /// total number of blocks -// /// -// public ushort num_blocks; -// } - -// /// -// /// this structure fills the gaps between what is available in a PFDICABINETINFO -// /// vs what is needed by FDICopy. Memory allocated for these becomes the responsibility -// /// of the caller to free. Yes, I am aware that this is totally, utterly inelegant. -// /// To make things even more unnecessarily confusing, we now attach these to the -// /// fdi_decomp_state. -// /// -// /// -// internal class MORE_ISCAB_INFO -// { -// public string prevname, previnfo; - -// public string nextname, nextinfo; - -// /// -// /// bug free indicator -// /// -// public bool hasnext; - -// public int folder_resv, header_resv; - -// public byte block_resv; -// } - -// /// -// internal class FDI_Int -// { -// public uint magic; - -// public Action alloc; - -// public Action free; - -// public Action open; - -// public Action read; - -// public Action write; - -// public Action close; - -// public Action seek; - -// public ERF perf; -// } - -// /* -// * ugh, well, this ended up being pretty damn silly... -// * now that I've conceded to build equivalent structures to struct cab.*, -// * I should have just used those, or, better yet, unified the two... sue me. -// * (Note to Microsoft: That's a joke. Please /don't/ actually sue me! -gmt). -// * Nevertheless, I've come this far, it works, so I'm not gonna change it -// * for now. This implementation has significant semantic differences anyhow. -// */ - -// /// Also known as fdi_cds_fwd -// /// -// internal class fdi_decomp_state -// { -// /// -// /// the hfdi we are using -// /// -// public FDI_Int fdi; - -// /// -// /// file handle we are using -// /// -// public Stream filehf, cabhf; - -// /// -// /// current folder we're extracting from -// /// -// public fdi_folder current; - -// /// -// /// uncompressed offset within folder -// /// -// public uint offset; - -// /// -// /// (high level) start of data to use up -// /// -// public byte* outpos; - -// /// -// /// (high level) amount of data to use up -// /// -// public ushort outlen; - -// /// -// /// chosen compress fn -// /// -// public Func decompress; - -// /// -// /// +2 for lzx bitbuffer overflows! -// /// -// public byte[] inbuf = new byte[CAB_INPUTMAX + 2]; - -// public byte[] outbuf = new byte[CAB_BLOCKMAX]; - -// #region methods - -// public Compression.MSZIP.State zip; -// public Compression.Quantum.State qtm; -// public Compression.LZX.State lzx; - -// #endregion - -// /* some temp variables for use during decompression */ -// public byte[] q_length_base = new byte[27], q_length_extra = new byte[27], q_extra_bits = new byte[42]; -// public uint[] q_position_base = new uint[42]; -// public uint[] lzx_position_base = new uint[51]; -// public byte[] extra_bits = new byte[51]; - -// /// -// /// Cabinet set ID -// /// -// public ushort setID; - -// /// -// /// Cabinet number in set (0 based) -// /// -// public ushort iCabinet; - -// public fdi_decomp_state decomp_cab; -// public MORE_ISCAB_INFO mii; -// public fdi_folder firstfol; -// public fdi_file firstfile; -// public fdi_decomp_state next; -// } - -// // #define ZIPNEEDBITS(n) {while(k<(n)){cab_int c=*(ZIP(inpos)++);\ -// // b|=((uint)c)<>=(n);k-=(n);} - -// // /* endian-neutral reading of little-endian data */ -// // #define EndGetI32(a) ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0])) -// // #define EndGetI16(a) ((((a)[1])<<8)|((a)[0])) - -// // #define CAB(decomp_state.x) (decomp_state.x) -// // #define ZIP(x) (decomp_state.methods.zip.x) -// // #define QTM(decomp_state.qtm.x) (decomp_state.methods.qtm.x) -// // #define LZX(x) (decomp_state.methods.lzx.x) - -// #endregion -// } \ No newline at end of file diff --git a/BurnOutSharp/FileType/MicrosoftCAB.LZX.cs b/BurnOutSharp/FileType/MicrosoftCAB.LZX.cs deleted file mode 100644 index e5a36291..00000000 --- a/BurnOutSharp/FileType/MicrosoftCAB.LZX.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; - -/// -/// -/// -/// -/// -/// -/// -/// -/// -namespace BurnOutSharp.FileType -{ - public class MSCABLZX - { - /// - /// The window size determines the number of window subdivisions, or position slots - /// - public static readonly Dictionary PositionSlots = new Dictionary() - { - [128 * 1024] = 34, // 128 KB - [256 * 1024] = 36, // 256 KB - [512 * 1024] = 38, // 512 KB - [1024 * 1024] = 42, // 1 MB - [2 * 1024 * 1024] = 50, // 2 MB - [4 * 1024 * 1024] = 66, // 4 MB - [8 * 1024 * 1024] = 98, // 8 MB - [16 * 1024 * 1024] = 162, // 16 MB - [32 * 1024 * 1024] = 290, // 32 MB - }; - } -} diff --git a/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs b/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs deleted file mode 100644 index bd7118b4..00000000 --- a/BurnOutSharp/FileType/MicrosoftCAB.Quantum.cs +++ /dev/null @@ -1,151 +0,0 @@ -using BurnOutSharp.Models.Compression.Quantum; - -/// -/// -/// -/// -/// -/// -namespace BurnOutSharp.FileType -{ - #region LZ Compression Tables - - public static class QuantumConstants - { - /// - /// Base position for each position slot (0..41) - /// Used by selectors 4, 5, and 6 - /// - public static readonly uint[] PositionBaseTable = new uint[] - { - 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, 0x000006, 0x000008, 0x00000c, - 0x000010, 0x000018, 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, 0x0000c0, - 0x000100, 0x000180, 0x000200, 0x000300, 0x000400, 0x000600, 0x000800, 0x000c00, - 0x001000, 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, 0x008000, 0x00c000, - 0x010000, 0x018000, 0x020000, 0x030000, 0x040000, 0x060000, 0x080000, 0x0c0000, - 0x100000, 0x180000, - }; - - /// - /// Extra bits for each position slot (0..41) - /// Used by selectors 4, 5, and 6 - /// - public static readonly int[] PositionExtraBitsTable = new int[] - { - 0, 0, 0, 0, 1, 1, 2, 2, - 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, - 11, 11, 12, 12, 13, 13, 14, 14, - 15, 15, 16, 16, 17, 17, 18, 18, - 19, 19, - }; - - /// - /// Base length for each length slot (0..26) - /// Used by selector 6 - /// - public static readonly byte[] LengthBaseTable = new byte[] - { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x0a, 0x0c, 0x0e, 0x12, 0x16, 0x1a, 0x1e, 0x26, - 0x2e, 0x36, 0x3e, 0x4e, 0x5e, 0x6e, 0x7e, 0x9e, - 0xbe, 0xde, 0xfe - }; - - /// - /// Extra bits for each length slot (0..26) - /// Used by selector 6 - /// - public static readonly int[] LengthExtraBitsTable = new int[] - { - 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 2, 2, 2, 2, 3, 3, - 3, 3, 4, 4, 4, 4, 5, 5, - 5, 5, 0, - }; - - /// - /// Number of position slots for (tsize - 10) - /// - public static readonly int[] NumberOfPositionSlots = new int[] - { - 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, - }; - } - - #endregion - - public static class QuantumCompressor - { - // TODO: Determine how these values are set - private static uint CS_C = 0; - private static uint CS_H = 0; - private static uint CS_L = 0; - - /// - /// Get frequency from code - /// - public static ushort GetFrequency(ushort totfreq) - { - uint range = ((CS_H - CS_L) & 0xFFFF) + 1; - uint freq = ((CS_C - CS_L + 1) * totfreq - 1) / range; - return (ushort)(freq & 0xFFFF); - } - - /// - /// The decoder renormalization loop - /// - public static int GetCode(int cumfreqm1, int cumfreq, int totfreq) - { - uint range = (CS_H - CS_L) + 1; - CS_H = CS_L + (uint)((cumfreqm1 * range) / totfreq) - 1; - CS_L = CS_L + (uint)((cumfreq * range) / totfreq); - - while (true) - { - if ((CS_L & 0x8000) != (CS_H & 0x8000)) - { - if ((CS_L & 0x4000) != 0 && (CS_H & 0x4000) == 0) - { - // Underflow case - CS_C ^= 0x4000; - CS_L &= 0x3FFF; - CS_H |= 0x4000; - } - else - { - break; - } - } - - CS_L <<= 1; - CS_H = (CS_H << 1) | 1; - CS_C = (CS_C << 1) | 0; // TODO: Figure out what `getbit()` is and replace the placeholder `0` - } - - // TODO: Figure out what is supposed to return here - return 0; - } - - public static int GetSymbol(Model model) - { - int freq = GetFrequency(model.Symbols[0].CumulativeFrequency); - - int i = 1; - for (; i < model.Entries; i++) - { - if (model.Symbols[i].CumulativeFrequency <= freq) - break; - } - - int sym = model.Symbols[i - 1].Symbol; - - GetCode(model.Symbols[i - 1].CumulativeFrequency, model.Symbols[i].CumulativeFrequency, model.Symbols[0].CumulativeFrequency); - - // TODO: Figure out what `update_model` does - //update_model(model, i); - - return sym; - } - } -} diff --git a/BurnOutSharp/FileType/MicrosoftCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.cs index c7a2fc3a..4d7aa2da 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.cs @@ -2,12 +2,7 @@ using System.Collections.Concurrent; using System.IO; using BurnOutSharp.Interfaces; -#if NET48 -using WixToolset.Dtf.Compression; -using WixToolset.Dtf.Compression.Cab; -#elif NET6_0_OR_GREATER using BurnOutSharp.Wrappers; -#endif using static BurnOutSharp.Utilities.Dictionary; namespace BurnOutSharp.FileType @@ -17,7 +12,7 @@ namespace BurnOutSharp.FileType /// /// Specification available at /// - public partial class MicrosoftCAB : IScannable + public class MicrosoftCAB : IScannable { /// public ConcurrentDictionary> Scan(Scanner scanner, string file) @@ -34,7 +29,6 @@ namespace BurnOutSharp.FileType /// public ConcurrentDictionary> Scan(Scanner scanner, Stream stream, string file) { -#if NET6_0_OR_GREATER // If the cab file itself fails try { @@ -87,41 +81,6 @@ namespace BurnOutSharp.FileType } return null; -#else - // If the cab file itself fails - try - { - string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempPath); - - CabInfo cabInfo = new CabInfo(file); - cabInfo.Unpack(tempPath); - - // Collect and format all found protections - var protections = scanner.GetProtections(tempPath); - - // If temp directory cleanup fails - try - { - Directory.Delete(tempPath, true); - } - catch (Exception ex) - { - if (scanner.IncludeDebug) Console.WriteLine(ex); - } - - // Remove temporary path references - StripFromKeys(protections, tempPath); - - return protections; - } - catch (Exception ex) - { - if (scanner.IncludeDebug) Console.WriteLine(ex); - } - - return null; -#endif } } } diff --git a/README.md b/README.md index b757d3dd..c4c08ebb 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` | +| Microsoft cabinet file | Yes | Yes | Yes | | | 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` |