From 1c78dac79fa6385e97a05604d03af92b08d315d0 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 3 Jan 2023 16:11:30 -0800 Subject: [PATCH] Port LZX read lengths --- BurnOutSharp.Compression/LZX/Bits.cs | 2 +- BurnOutSharp.Compression/LZX/Decompressor.cs | 177 +++++++++++++++++- .../MicrosoftCabinet.fdi.LZX.cs | 49 ----- 3 files changed, 170 insertions(+), 58 deletions(-) diff --git a/BurnOutSharp.Compression/LZX/Bits.cs b/BurnOutSharp.Compression/LZX/Bits.cs index b5bfe65a..aac72c7a 100644 --- a/BurnOutSharp.Compression/LZX/Bits.cs +++ b/BurnOutSharp.Compression/LZX/Bits.cs @@ -7,6 +7,6 @@ namespace BurnOutSharp.Compression.LZX public int BitsLeft; - public int InitialPosition; //byte* + public int InputPosition; //byte* } } \ No newline at end of file diff --git a/BurnOutSharp.Compression/LZX/Decompressor.cs b/BurnOutSharp.Compression/LZX/Decompressor.cs index 5c061736..b89dad11 100644 --- a/BurnOutSharp.Compression/LZX/Decompressor.cs +++ b/BurnOutSharp.Compression/LZX/Decompressor.cs @@ -19,11 +19,73 @@ namespace BurnOutSharp.Compression.LZX /// /// Read and build the Huffman tree from the lengths /// - /// INCORRECT IMPLEMENTATION TO SATISFY COMPILER FOR NOW - private static int ReadLengths(byte[] lengths, uint first, uint last, Bits lb, State state, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + private static int ReadLengths(byte[] lengths, uint first, uint last, Bits lb, State state, byte[] inbuf) { + uint i, j, x, y; + int z; - // TODO: Finish implementation + uint bitbuf = lb.BitBuffer; + int bitsleft = lb.BitsLeft; + int inpos = lb.InputPosition; + ushort[] hufftbl; + + for (x = 0; x < 20; x++) + { + y = READ_BITS(4, inbuf, ref inpos, ref bitsleft, ref bitbuf); + state.tblPRETREE_len[x] = (byte)y; + } + + make_decode_table(LZX_PRETREE_MAXSYMBOLS, LZX_PRETREE_TABLEBITS, state.tblPRETREE_len, state.tblPRETREE_table); + + for (x = first; x < last;) + { + z = READ_HUFFSYM(state.tblPRETREE_table, state.tblPRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + if (z == 17) + { + y = READ_BITS(4, inbuf, ref inpos, ref bitsleft, ref bitbuf); + y += 4; + while (y-- > 0) + { + lengths[x++] = 0; + } + } + else if (z == 18) + { + y = READ_BITS(5, inbuf, ref inpos, ref bitsleft, ref bitbuf); + y += 20; + while (y-- > 0) + { + lengths[x++] = 0; + } + } + else if (z == 19) + { + y = READ_BITS(1, inbuf, ref inpos, ref bitsleft, ref bitbuf); + y += 4; + + z = READ_HUFFSYM(state.tblPRETREE_table, state.tblPRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS, inbuf, ref inpos, ref bitsleft, ref bitbuf); + z = lengths[x] - z; + if (z < 0) + z += 17; + + while (y-- > 0) + { + lengths[x++] = (byte)z; + } + } + else + { + z = lengths[x] - z; + if (z < 0) + z += 17; + + lengths[x++] = (byte)z; + } + } + + lb.BitBuffer = bitbuf; + lb.BitsLeft = bitsleft; + lb.InputPosition = inpos; return 0; } @@ -97,6 +159,105 @@ namespace BurnOutSharp.Compression.LZX #endregion + #region Huffman Methods + + /// + /// This function was coded by David Tritscher. It builds a fast huffman + /// decoding table out of just a canonical huffman code lengths table. + /// + /// Total number of symbols in this huffman tree. + /// + /// Any symbols with a code length of nbits or less can be decoded + /// in one lookup of the table. + /// + /// A table to get code lengths from [0 to syms-1] + /// The table to fill up with decoded symbols and pointers. + /// + /// OK: 0 + /// error: 1 + /// + private 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; + } + + #endregion + // Huffman macros #region Huffman Macros @@ -104,7 +265,7 @@ namespace BurnOutSharp.Compression.LZX /// Decodes one huffman symbol from the bitstream using the stated table and /// puts it in v. /// - private static int? READ_HUFFSYM(ushort[] hufftbl, byte[] lentable, int tablebits, int maxsymbols, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) + private static int READ_HUFFSYM(ushort[] hufftbl, byte[] lentable, int tablebits, int maxsymbols, byte[] inbuf, ref int inpos, ref int bitsleft, ref uint bitbuf) { int v = 0, i, j = 0; ENSURE_BITS(16, inbuf, ref inpos, ref bitsleft, ref bitbuf); @@ -117,7 +278,7 @@ namespace BurnOutSharp.Compression.LZX i <<= 1; i |= (bitbuf & j) != 0 ? 1 : 0; if (j == 0) - return null; + throw new System.Exception(); } while ((i = hufftbl[i]) >= maxsymbols); } @@ -134,14 +295,14 @@ namespace BurnOutSharp.Compression.LZX { lb.BitBuffer = bitbuf; lb.BitsLeft = bitsleft; - lb.InitialPosition = inpos; + lb.InputPosition = inpos; - if (ReadLengths(lentable, first, last, lb, state, inbuf, ref inpos, ref bitsleft, ref bitbuf) != 0) + if (ReadLengths(lentable, first, last, lb, state, inbuf) != 0) return false; bitbuf = lb.BitBuffer; bitsleft = lb.BitsLeft; - inpos = lb.InitialPosition; + inpos = lb.InputPosition; return true; } diff --git a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs index 4757bef4..f2d0a772 100644 --- a/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs +++ b/BurnOutSharp.Wrappers/MicrosoftCabinet.fdi.LZX.cs @@ -11,55 +11,6 @@ // { // internal unsafe class LZXfdi // { -// /************************************************************ -// * fdi_lzx_read_lens (internal) -// */ -// static int fdi_lzx_read_lens(cab_UBYTE* lens, cab_ULONG first, cab_ULONG last, struct lzx_bits *lb, -// fdi_decomp_state* decomp_state) { -// cab_ULONG i, j, x, y; -// int z; - -// register cab_ULONG bitbuf = lb->bb; -// register int bitsleft = lb->bl; -// cab_UBYTE* inpos = lb->ip; -// cab_UWORD* hufftbl; - -// for (x = 0; x< 20; x++) { -// READ_BITS(y, 4); -// LENTABLE(PRETREE)[x] = y; -// } -// BUILD_TABLE(PRETREE); - -// for (x = first; xbb = bitbuf; -// lb->bl = bitsleft; -// lb->ip = inpos; -// return 0; -// } // /******************************************************* // * LZXfdi_decomp(internal)