diff --git a/libmspack/CAB/Decompressor.cs b/libmspack/CAB/Decompressor.cs index 8d32ee6..5a506e6 100644 --- a/libmspack/CAB/Decompressor.cs +++ b/libmspack/CAB/Decompressor.cs @@ -10,7 +10,7 @@ namespace SabreTools.Compression.libmspack.CAB /// public unsafe class Decompressor : BaseDecompressor { - public mscabd_decompress_state d { get; private set; } + public mscabd_decompress_state d { get; set; } public int buf_size { get; private set; } @@ -1092,13 +1092,8 @@ namespace SabreTools.Compression.libmspack.CAB // Allocate generic decompression state if (this.d == null) { - this.d = new mscabd_decompress_state(); - this.d.folder = null; - this.d.data = null; + this.d = new None.DecompressState(); this.d.sys = sys as CABSystem; - this.d.state = null; - this.d.infh = null; - this.d.incab = null; } // Do we need to change folder or reset the current folder? diff --git a/libmspack/kwaj.cs b/libmspack/KWAJ/Constants.cs similarity index 94% rename from libmspack/kwaj.cs rename to libmspack/KWAJ/Constants.cs index 39c4657..0dd210e 100644 --- a/libmspack/kwaj.cs +++ b/libmspack/KWAJ/Constants.cs @@ -1,6 +1,6 @@ -namespace SabreTools.Compression.libmspack +namespace SabreTools.Compression.libmspack.KWAJ { - public static class kwaj + public static class Constants { public const byte kwajh_Signature1 = 0x00; public const byte kwajh_Signature2 = 0x04; diff --git a/libmspack/KWAJ/Decompressor.cs b/libmspack/KWAJ/Decompressor.cs index 897fd59..7142daa 100644 --- a/libmspack/KWAJ/Decompressor.cs +++ b/libmspack/KWAJ/Decompressor.cs @@ -1,5 +1,4 @@ using System; -using static SabreTools.Compression.libmspack.kwaj; namespace SabreTools.Compression.libmspack.KWAJ { @@ -45,8 +44,8 @@ namespace SabreTools.Compression.libmspack.KWAJ /// passed directly to mspack_system::open(). /// /// A pointer to a mskwajd_header structure, or null on failure - /// - public mskwajd_header open(in string filename) + /// + public mskwajd_header Open(in string filename) { mspack_system sys = this.system; @@ -63,7 +62,7 @@ namespace SabreTools.Compression.libmspack.KWAJ MSPACK_ERR err; if ((err = ReadHeaders(sys, fh, hdr)) != MSPACK_ERR.MSPACK_ERR_OK) { - close(hdr); + Close(hdr); this.error = err; return null; } @@ -79,8 +78,8 @@ namespace SabreTools.Compression.libmspack.KWAJ /// used again. /// /// The KWAJ file to close - /// - public void close(mskwajd_header kwaj) + /// + public void Close(mskwajd_header kwaj) { if (this.system == null) return; @@ -150,7 +149,7 @@ namespace SabreTools.Compression.libmspack.KWAJ if (sys.read(fh, &buf[0], 2) != 2) return MSPACK_ERR.MSPACK_ERR_READ; i = BitConverter.ToUInt16(buf, 0); - if (sys.seek(fh, i, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR)) + if (sys.seek(fh, i, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) return MSPACK_ERR.MSPACK_ERR_SEEK; } @@ -160,8 +159,8 @@ namespace SabreTools.Compression.libmspack.KWAJ int len; // Allocate memory for maximum length filename - char* fn = (char*)sys.alloc(sys, 13); - if (!(hdr.filename = fn)) + char* fn = (char*)sys.alloc(13); + if ((hdr.extra = fn) == null) return MSPACK_ERR.MSPACK_ERR_NOMEMORY; // Copy filename if present @@ -170,8 +169,9 @@ namespace SabreTools.Compression.libmspack.KWAJ // Read and copy up to 9 bytes of a null terminated string if ((len = sys.read(fh, &buf[0], 9)) < 2) return MSPACK_ERR.MSPACK_ERR_READ; + for (i = 0; i < len; i++) - if (!(*fn++ = buf[i])) + if ((*fn++ = (char)buf[i]) == '\0') break; // If string was 9 bytes with no null terminator, reject it @@ -179,7 +179,7 @@ namespace SabreTools.Compression.libmspack.KWAJ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; // Seek to byte after string ended in file - if (sys.seek(fh, i + 1 - len, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR)) + if (sys.seek(fh, i + 1 - len, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) return MSPACK_ERR.MSPACK_ERR_SEEK; fn--; // Remove the null terminator @@ -195,7 +195,7 @@ namespace SabreTools.Compression.libmspack.KWAJ return MSPACK_ERR.MSPACK_ERR_READ; for (i = 0; i < len; i++) - if (!(*fn++ = buf[i])) + if ((*fn++ = (char)buf[i]) == '\0') break; // If string was 4 bytes with no null terminator, reject it @@ -203,7 +203,7 @@ namespace SabreTools.Compression.libmspack.KWAJ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; // Seek to byte after string ended in file - if (sys.seek(fh, i + 1 - len, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR)) + if (sys.seek(fh, i + 1 - len, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) return MSPACK_ERR.MSPACK_ERR_SEEK; fn--; // Remove the null terminator @@ -218,15 +218,15 @@ namespace SabreTools.Compression.libmspack.KWAJ return MSPACK_ERR.MSPACK_ERR_READ; i = BitConverter.ToUInt16(&buf[0]); - hdr.extra = (char*)sys.alloc(sys, i + 1); - if (!hdr.extra) + hdr.extra = (char*)sys.alloc(i + 1); + if (hdr.extra == null) return MSPACK_ERR.MSPACK_ERR_NOMEMORY; if (sys.read(fh, hdr.extra, i) != i) return MSPACK_ERR.MSPACK_ERR_READ; hdr.extra[i] = '\0'; - hdr.extra_length = i; + hdr.extra_length = (ushort)i; } return MSPACK_ERR.MSPACK_ERR_OK; @@ -244,7 +244,88 @@ namespace SabreTools.Compression.libmspack.KWAJ /// is passed directly to mspack_system::open(). /// /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR extract(mskwajd_header kwaj, in string filename) => throw new NotImplementedException(); + public MSPACK_ERR Extract(mskwajd_header kwaj, in string filename) + { + if (kwaj == null) + return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; + + mspack_system sys = this.system; + mspack_file fh = kwaj.fh; + + // Seek to the compressed data + if (sys.seek(fh, kwaj.data_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) + { + return this.error = MSPACK_ERR.MSPACK_ERR_SEEK; + } + + // Open file for output + mspack_file outfh; + if ((outfh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE)) == null) + { + return this.error = MSPACK_ERR.MSPACK_ERR_OPEN; + } + + this.error = MSPACK_ERR.MSPACK_ERR_OK; + + // Decompress based on format + if (kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_NONE || kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_XOR) + { + // NONE is a straight copy. XOR is a copy xored with 0xFF + byte* buf = (byte*)sys.alloc(KWAJ_INPUT_SIZE); + if (buf != null) + { + int read, i; + while ((read = sys.read(fh, buf, KWAJ_INPUT_SIZE)) > 0) + { + if (kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_XOR) + { + for (i = 0; i < read; i++) + buf[i] ^= 0xFF; + } + + if (sys.write(outfh, buf, read) != read) + { + this.error = MSPACK_ERR.MSPACK_ERR_WRITE; + break; + } + } + + if (read < 0) + this.error = MSPACK_ERR.MSPACK_ERR_READ; + + sys.free(buf); + } + else + { + this.error = MSPACK_ERR.MSPACK_ERR_NOMEMORY; + } + } + else if (kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_SZDD) + { + this.error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, LZSS_MODE.LZSS_MODE_QBASIC); + } + else if (kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_LZH) + { + kwajd_stream lzh = lzh_init(sys, fh, outfh); + this.error = (lzh != null) ? lzh_decompress(lzh) : MSPACK_ERR.MSPACK_ERR_NOMEMORY; + lzh_free(lzh); + } + else if (kwaj.comp_type == MSKWAJ_COMP.MSKWAJ_COMP_MSZIP) + { + mszipd_stream zip = mszipd_init(sys, fh, outfh, KWAJ_INPUT_SIZE, 0); + this.error = (zip != null) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR.MSPACK_ERR_NOMEMORY; + mszipd_free(zip); + } + else + { + this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; + } + + // Close output file + sys.close(outfh); + + return this.error; + } /// /// Decompresses an KWAJ file to an output file in one step. @@ -264,7 +345,16 @@ namespace SabreTools.Compression.libmspack.KWAJ /// is passed directly to mspack_system::open(). /// /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR decompress(in string input, in string output) => throw new NotImplementedException(); + public MSPACK_ERR Decompress(in string input, in string output) + { + mskwajd_header hdr; + if ((hdr = Open(input)) == null) + return this.error; + + MSPACK_ERR error = Extract(hdr, output); + Close(hdr); + return this.error = error; + } /// /// Returns the error code set by the most recently called method. @@ -273,8 +363,11 @@ namespace SabreTools.Compression.libmspack.KWAJ /// error code directly. /// /// The most recent error code - /// + /// /// - public MSPACK_ERR last_error() => throw new NotImplementedException(); + public MSPACK_ERR LastError() + { + return this.error; + } } } \ No newline at end of file diff --git a/libmspack/None/DecompressState.cs b/libmspack/None/DecompressState.cs new file mode 100644 index 0000000..c429a99 --- /dev/null +++ b/libmspack/None/DecompressState.cs @@ -0,0 +1,47 @@ +namespace SabreTools.Compression.libmspack.None +{ + public unsafe class DecompressState : mscabd_decompress_state + { + public DecompressState() + { + this.comp_type = MSCAB_COMP.MSCAB_COMP_NONE; + this.state = null; + } + + public DecompressState(mscabd_decompress_state oldstate) + { + this.comp_type = MSCAB_COMP.MSCAB_COMP_NONE; + this.state = null; + if (oldstate != null) + { + this.folder = oldstate.folder; + this.data = oldstate.data; + this.offset = oldstate.offset; + this.block = oldstate.block; + this.outlen = oldstate.outlen; + this.sys = oldstate.sys; + this.incab = oldstate.incab; + this.infh = oldstate.infh; + this.outfh = oldstate.outfh; + this.i_ptr = oldstate.i_ptr; + this.i_end = oldstate.i_end; + this.input = oldstate.input; + } + } + + /// + public override unsafe MSPACK_ERR decompress(object data, long bytes) + { + State s = data as State; + while (bytes > 0) + { + int run = (bytes > s.BufferSize) ? s.BufferSize : (int)bytes; + if (s.InternalSystem.read(s.Input, &s.Buffer[0], run) != run) return MSPACK_ERR.MSPACK_ERR_READ; + if (s.InternalSystem.write(s.Output, &s.Buffer[0], run) != run) return MSPACK_ERR.MSPACK_ERR_WRITE; + bytes -= run; + } + + return MSPACK_ERR.MSPACK_ERR_OK; + } + } +} \ No newline at end of file diff --git a/libmspack/None/State.cs b/libmspack/None/State.cs new file mode 100644 index 0000000..5e86557 --- /dev/null +++ b/libmspack/None/State.cs @@ -0,0 +1,34 @@ +namespace SabreTools.Compression.libmspack.None +{ + /// + /// The "not compressed" method decompressor + /// + public unsafe class State + { + public mspack_system InternalSystem { get; private set; } + + public mspack_file Input { get; private set; } + + public mspack_file Output { get; private set; } + + public byte* Buffer { get; private set; } + + public int BufferSize { get; private set; } + + public State(mspack_system sys, mspack_file infh, mspack_file outfh, int bufsize) + { + this.InternalSystem = sys; + this.Input = infh; + this.Output = outfh; + this.Buffer = system.CreateArray(bufsize); + this.BufferSize = bufsize; + } + + ~State() + { + mspack_system sys = this.InternalSystem; + sys.free(this.Buffer); + //sys.free(this); + } + } +} \ No newline at end of file diff --git a/libmspack/szdd.cs b/libmspack/SZDD/Constants.cs similarity index 85% rename from libmspack/szdd.cs rename to libmspack/SZDD/Constants.cs index d416f90..5679b8c 100644 --- a/libmspack/szdd.cs +++ b/libmspack/SZDD/Constants.cs @@ -7,9 +7,9 @@ * For further details, see the file COPYING.LIB distributed with libmspack */ -namespace SabreTools.Compression.libmspack +namespace SabreTools.Compression.libmspack.SZDD { - public static class szdd + public static class Constants { /// /// Input buffer size during decompression - not worth parameterising IMHO diff --git a/libmspack/SZDD/Decompressor.cs b/libmspack/SZDD/Decompressor.cs index 573915b..f323ab9 100644 --- a/libmspack/SZDD/Decompressor.cs +++ b/libmspack/SZDD/Decompressor.cs @@ -1,5 +1,5 @@ using System.Linq; -using static SabreTools.Compression.libmspack.szdd; +using static SabreTools.Compression.libmspack.SZDD.Constants; namespace SabreTools.Compression.libmspack.SZDD { diff --git a/libmspack/cab.cs b/libmspack/cab.cs index a41ede1..6a35523 100644 --- a/libmspack/cab.cs +++ b/libmspack/cab.cs @@ -19,8 +19,8 @@ namespace SabreTools.Compression.libmspack switch ((MSCAB_COMP)((int)ct & cffoldCOMPTYPE_MASK)) { case MSCAB_COMP.MSCAB_COMP_NONE: - self.d = new mscabd_noned_decompress_state(); - self.d.state = noned_init(self.d.sys, fh, fh, self.buf_size); + self.d = new None.DecompressState(self.d); + self.d.state = new None.State(self.d.sys, fh, fh, self.buf_size); break; case MSCAB_COMP.MSCAB_COMP_MSZIP: self.d = new mscabd_mszipd_decompress_state(); @@ -51,7 +51,6 @@ namespace SabreTools.Compression.libmspack switch ((MSCAB_COMP)((int)self.d.comp_type & cffoldCOMPTYPE_MASK)) { - case MSCAB_COMP.MSCAB_COMP_NONE: noned_free((noned_state)self.d.state); break; case MSCAB_COMP.MSCAB_COMP_MSZIP: mszipd_free((mszipd_stream)self.d.state); break; case MSCAB_COMP.MSCAB_COMP_QUANTUM: qtmd_free((qtmd_stream)self.d.state); break; case MSCAB_COMP.MSCAB_COMP_LZX: lzxd_free((lzxd_stream)self.d.state); break; @@ -62,32 +61,5 @@ namespace SabreTools.Compression.libmspack } #endregion - - #region noned_state - - public static noned_state noned_init(mspack_system sys, mspack_file @in, mspack_file @out, int bufsize) - { - noned_state state = new noned_state(); - - state.sys = sys; - state.i = @in; - state.o = @out; - state.buf = system.CreateArray(bufsize); - state.bufsize = bufsize; - return state; - } - - public static void noned_free(noned_state state) - { - mspack_system sys; - if (state != null) - { - sys = state.sys; - sys.free(state.buf); - //sys.free(state); - } - } - - #endregion } } \ No newline at end of file diff --git a/libmspack/kwajd_stream.cs b/libmspack/kwajd_stream.cs index 71def9f..8bf8599 100644 --- a/libmspack/kwajd_stream.cs +++ b/libmspack/kwajd_stream.cs @@ -1,4 +1,4 @@ -using static SabreTools.Compression.libmspack.kwaj; +using static SabreTools.Compression.libmspack.KWAJ.Constants; using static SabreTools.Compression.libmspack.lzss; namespace SabreTools.Compression.libmspack diff --git a/libmspack/mscabd_decompress_state.cs b/libmspack/mscabd_decompress_state.cs index 47badf8..bfc1143 100644 --- a/libmspack/mscabd_decompress_state.cs +++ b/libmspack/mscabd_decompress_state.cs @@ -3,7 +3,7 @@ using static SabreTools.Compression.libmspack.CAB.Constants; namespace SabreTools.Compression.libmspack { - public unsafe class mscabd_decompress_state + public unsafe abstract class mscabd_decompress_state { /// /// Current folder we're extracting from @@ -40,11 +40,6 @@ namespace SabreTools.Compression.libmspack /// public MSCAB_COMP comp_type { get; set; } - /// - /// Decompressor code - /// - public virtual MSPACK_ERR decompress(object data, long offset) => MSPACK_ERR.MSPACK_ERR_NOMEMORY; - /// /// Decompressor state /// @@ -79,26 +74,10 @@ namespace SabreTools.Compression.libmspack /// One input block of data /// public byte[] input { get; set; } = new byte[CAB_INPUTBUF]; - } - public unsafe class mscabd_noned_decompress_state : mscabd_decompress_state - { - /// - public override unsafe MSPACK_ERR decompress(object data, long bytes) - { - noned_state s = data as noned_state; - while (bytes > 0) - { - int run = (bytes > s.bufsize) ? s.bufsize : (int)bytes; - { - if (s.sys.read(s.i, &s.buf[0], run) != run) return MSPACK_ERR.MSPACK_ERR_READ; - if (s.sys.write(s.o, &s.buf[0], run) != run) return MSPACK_ERR.MSPACK_ERR_WRITE; - bytes -= run; - } - return MSPACK_ERR.MSPACK_ERR_OK; - } - - return MSPACK_ERR.MSPACK_ERR_DECRUNCH; - } + /// + /// Decompressor code + /// + public abstract MSPACK_ERR decompress(object data, long offset); } } \ No newline at end of file diff --git a/libmspack/mskwajd_header.cs b/libmspack/mskwajd_header.cs index 0740d3b..f87ffdf 100644 --- a/libmspack/mskwajd_header.cs +++ b/libmspack/mskwajd_header.cs @@ -5,7 +5,7 @@ namespace SabreTools.Compression.libmspack /// /// All fields are READ ONLY. /// - public class mskwajd_header + public unsafe class mskwajd_header { /// /// The compression type @@ -30,13 +30,13 @@ namespace SabreTools.Compression.libmspack /// /// Output filename, or null if not present /// - public string filename { get; set; } + public char* filename { get; set; } /// /// Extra uncompressed data (usually text) in the header. /// This data can contain nulls so use extra_length to get the size. /// - public string extra { get; set; } + public char* extra { get; set; } /// /// Length of extra uncompressed data in the header diff --git a/libmspack/noned_state.cs b/libmspack/noned_state.cs deleted file mode 100644 index d946d7c..0000000 --- a/libmspack/noned_state.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// The "not compressed" method decompressor - /// - public unsafe class noned_state - { - public mspack_system sys { get; set; } - - public mspack_file i { get; set; } - - public mspack_file o { get; set; } - - public byte* buf { get; set; } - - public int bufsize { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/readhuff.cs b/libmspack/readhuff.cs index 7d44321..fb2d027 100644 --- a/libmspack/readhuff.cs +++ b/libmspack/readhuff.cs @@ -1,7 +1,206 @@ namespace SabreTools.Compression.libmspack { - public unsafe abstract class readhuff + public unsafe static class readhuff { public const int HUFF_MAXBITS = 16; + + #region MSB + + /// + /// This function was originally coded by David Tritscher. + /// It builds a fast huffman decoding table from + /// 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 nsyms-1] + /// + /// The table to fill up with decoded symbols and pointers. + /// Should be ((1< + /// Returns 0 for OK or 1 for error + public static int make_decode_table_MSB(uint nsyms, uint nbits, byte* length, ushort* table) + { + ushort sym, next_symbol; + uint leaf, fill; + byte bit_num; + uint pos = 0; // The current position in the decode table + uint table_mask = (uint)(1 << (int)nbits); + uint bit_mask = table_mask >> 1; // Don't do 0 length codes + + // Fill entries for codes short enough for a direct mapping + for (bit_num = 1; bit_num <= nbits; bit_num++) + { + for (sym = 0; sym < nsyms; sym++) + { + if (length[sym] != bit_num) continue; + leaf = pos; + + if ((pos += bit_mask) > table_mask) return 1; // Table overrun + + // Fill all possible lookups of this symbol with the symbol itself + for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; + } + bit_mask >>= 1; + } + + // Exit with success if table is now complete + if (pos == table_mask) return 0; + + // Mark all remaining table entries as unused + for (sym = (ushort)pos; sym < table_mask; sym++) + { + table[sym] = 0xFFFF; + } + + // next_symbol = base of allocation for long codes + next_symbol = (ushort)(((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1)); + + // Give ourselves room for codes to grow by up to 16 more bits. + // codes now start at bit nbits+16 and end at (nbits+16-codelength) + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++) + { + for (sym = 0; sym < nsyms; sym++) + { + if (length[sym] != bit_num) continue; + if (pos >= table_mask) return 1; // Table overflow + + leaf = pos >> 16; + for (fill = 0; fill < (bit_num - nbits); fill++) + { + // If this path hasn't been taken yet, 'allocate' two entries + if (table[leaf] == 0xFFFF) + { + table[(next_symbol << 1)] = 0xFFFF; + table[(next_symbol << 1) + 1] = 0xFFFF; + table[leaf] = next_symbol++; + } + + // Follow the path and select either left or right for next bit + leaf = (uint)(table[leaf] << 1); + if (((pos >> (int)(15 - fill)) & 1) != 0) leaf++; + } + table[leaf] = sym; + pos += bit_mask; + } + bit_mask >>= 1; + } + + // Full table? + return (pos == table_mask) ? 0 : 1; + } + + #endregion + + #region LSB + + /// + /// This function was originally coded by David Tritscher. + /// It builds a fast huffman decoding table from + /// 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 nsyms-1] + /// + /// The table to fill up with decoded symbols and pointers. + /// Should be ((1< + /// Returns 0 for OK or 1 for error + public static int make_decode_table_LSB(uint nsyms, uint nbits, byte* length, ushort* table) + { + ushort sym, next_symbol; + uint leaf, fill; + uint reverse; + byte bit_num; + uint pos = 0; // The current position in the decode table + uint table_mask = (uint)(1 << (int)nbits); + uint bit_mask = table_mask >> 1; // Don't do 0 length codes + + // Fill entries for codes short enough for a direct mapping + for (bit_num = 1; bit_num <= nbits; bit_num++) + { + for (sym = 0; sym < nsyms; sym++) + { + if (length[sym] != bit_num) continue; + + // Reverse the significant bits + fill = length[sym]; reverse = pos >> (int)(nbits - fill); leaf = 0; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill > 0); + + if ((pos += bit_mask) > table_mask) return 1; // Table overrun + + // Fill all possible lookups of this symbol with the symbol itself + fill = bit_mask; next_symbol = (ushort)(1 << bit_num); + do { table[leaf] = sym; leaf += next_symbol; } while (--fill > 0); + } + bit_mask >>= 1; + } + + // Exit with success if table is now complete + if (pos == table_mask) return 0; + + // Mark all remaining table entries as unused + for (sym = (ushort)pos; sym < table_mask; sym++) + { + reverse = sym; leaf = 0; fill = nbits; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill > 0); + table[leaf] = 0xFFFF; + } + + // next_symbol = base of allocation for long codes + next_symbol = (ushort)(((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1)); + + // Give ourselves room for codes to grow by up to 16 more bits. + // codes now start at bit nbits+16 and end at (nbits+16-codelength) + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++) + { + for (sym = 0; sym < nsyms; sym++) + { + if (length[sym] != bit_num) continue; + if (pos >= table_mask) return 1; // Table overflow + + // leaf = the first nbits of the code, reversed + reverse = pos >> 16; leaf = 0; fill = nbits; + do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill > 0); + for (fill = 0; fill < (bit_num - nbits); fill++) + { + // If this path hasn't been taken yet, 'allocate' two entries + if (table[leaf] == 0xFFFF) + { + table[(next_symbol << 1)] = 0xFFFF; + table[(next_symbol << 1) + 1] = 0xFFFF; + table[leaf] = 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; + pos += bit_mask; + } + bit_mask >>= 1; + } + + // Full table? + return (pos == table_mask) ? 0 : 1; + } + + #endregion } } \ No newline at end of file