From 5477afaf1e54acd178f703d8b541b3a017dd9f0e Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 21 Sep 2023 21:31:45 -0400 Subject: [PATCH] Remove partially ported libmspack code --- libmspack/BaseCompressor.cs | 16 - libmspack/BaseDecompressor.cs | 16 - libmspack/BitStream.cs | 49 - libmspack/CAB/CABSystem.cs | 231 ---- libmspack/CAB/Compressor.cs | 17 - libmspack/CAB/Constants.cs | 68 -- libmspack/CAB/Decompressor.cs | 1242 ---------------------- libmspack/CAB/cab.cs | 65 -- libmspack/CAB/mscabd_cabinet.cs | 125 --- libmspack/CAB/mscabd_decompress_state.cs | 83 -- libmspack/CAB/mscabd_file.cs | 74 -- libmspack/CAB/mscabd_folder.cs | 53 - libmspack/CAB/mscabd_folder_data.cs | 20 - libmspack/CHM/Compressor.cs | 169 --- libmspack/CHM/Constants.cs | 82 -- libmspack/CHM/Decompressor.cs | 922 ---------------- libmspack/CHM/mschmc_file.cs | 37 - libmspack/CHM/mschmd_decompress_state.cs | 45 - libmspack/CHM/mschmd_file.cs | 37 - libmspack/CHM/mschmd_header.cs | 120 --- libmspack/CHM/mschmd_sec_mscompressed.cs | 31 - libmspack/CHM/mschmd_sec_uncompressed.cs | 15 - libmspack/CHM/mschmd_section.cs | 25 - libmspack/Constants.cs | 10 - libmspack/Enums.cs | 422 -------- libmspack/FixedArray.cs | 86 -- libmspack/HLP/Compressor.cs | 17 - libmspack/HLP/Decompressor.cs | 17 - libmspack/KWAJ/Compressor.cs | 111 -- libmspack/KWAJ/Constants.cs | 37 - libmspack/KWAJ/Decompressor.cs | 375 ------- libmspack/KWAJ/kwajd_stream.cs | 60 -- libmspack/KWAJ/mskwajd_header.cs | 48 - libmspack/LIT/Compressor.cs | 17 - libmspack/LIT/Decompressor.cs | 17 - libmspack/LZSS/lzss.cs | 39 - libmspack/LZX/lzx.cs | 147 --- libmspack/LZX/lzxd_stream.cs | 148 --- libmspack/MSZIP/mszip.cs | 91 -- libmspack/MSZIP/mszipd_stream.cs | 46 - libmspack/None/DecompressState.cs | 47 - libmspack/None/State.cs | 34 - libmspack/OAB/Compressor.cs | 70 -- libmspack/OAB/Decompressor.cs | 400 ------- libmspack/OAB/OABSystem.cs | 34 - libmspack/OAB/oab.cs | 32 - libmspack/OAB/oabd_file.cs | 13 - libmspack/Quantum/qtm.cs | 241 ----- libmspack/Quantum/qtmd_model.cs | 27 - libmspack/Quantum/qtmd_modelsym.cs | 9 - libmspack/Quantum/qtmd_stream.cs | 126 --- libmspack/SZDD/Compressor.cs | 88 -- libmspack/SZDD/Constants.cs | 19 - libmspack/SZDD/Decompressor.cs | 231 ---- libmspack/SZDD/msszddd_header.cs | 31 - libmspack/crc32.cs | 118 -- libmspack/macros.cs | 35 - libmspack/mspack_default_system.cs | 140 --- libmspack/mspack_file.cs | 9 - libmspack/mspack_file_p.cs | 11 - libmspack/mspack_system.cs | 170 --- libmspack/readbits.cs | 245 ----- libmspack/readhuff.cs | 206 ---- libmspack/system.cs | 42 - 64 files changed, 7608 deletions(-) delete mode 100644 libmspack/BaseCompressor.cs delete mode 100644 libmspack/BaseDecompressor.cs delete mode 100644 libmspack/BitStream.cs delete mode 100644 libmspack/CAB/CABSystem.cs delete mode 100644 libmspack/CAB/Compressor.cs delete mode 100644 libmspack/CAB/Constants.cs delete mode 100644 libmspack/CAB/Decompressor.cs delete mode 100644 libmspack/CAB/cab.cs delete mode 100644 libmspack/CAB/mscabd_cabinet.cs delete mode 100644 libmspack/CAB/mscabd_decompress_state.cs delete mode 100644 libmspack/CAB/mscabd_file.cs delete mode 100644 libmspack/CAB/mscabd_folder.cs delete mode 100644 libmspack/CAB/mscabd_folder_data.cs delete mode 100644 libmspack/CHM/Compressor.cs delete mode 100644 libmspack/CHM/Constants.cs delete mode 100644 libmspack/CHM/Decompressor.cs delete mode 100644 libmspack/CHM/mschmc_file.cs delete mode 100644 libmspack/CHM/mschmd_decompress_state.cs delete mode 100644 libmspack/CHM/mschmd_file.cs delete mode 100644 libmspack/CHM/mschmd_header.cs delete mode 100644 libmspack/CHM/mschmd_sec_mscompressed.cs delete mode 100644 libmspack/CHM/mschmd_sec_uncompressed.cs delete mode 100644 libmspack/CHM/mschmd_section.cs delete mode 100644 libmspack/Constants.cs delete mode 100644 libmspack/Enums.cs delete mode 100644 libmspack/FixedArray.cs delete mode 100644 libmspack/HLP/Compressor.cs delete mode 100644 libmspack/HLP/Decompressor.cs delete mode 100644 libmspack/KWAJ/Compressor.cs delete mode 100644 libmspack/KWAJ/Constants.cs delete mode 100644 libmspack/KWAJ/Decompressor.cs delete mode 100644 libmspack/KWAJ/kwajd_stream.cs delete mode 100644 libmspack/KWAJ/mskwajd_header.cs delete mode 100644 libmspack/LIT/Compressor.cs delete mode 100644 libmspack/LIT/Decompressor.cs delete mode 100644 libmspack/LZSS/lzss.cs delete mode 100644 libmspack/LZX/lzx.cs delete mode 100644 libmspack/LZX/lzxd_stream.cs delete mode 100644 libmspack/MSZIP/mszip.cs delete mode 100644 libmspack/MSZIP/mszipd_stream.cs delete mode 100644 libmspack/None/DecompressState.cs delete mode 100644 libmspack/None/State.cs delete mode 100644 libmspack/OAB/Compressor.cs delete mode 100644 libmspack/OAB/Decompressor.cs delete mode 100644 libmspack/OAB/OABSystem.cs delete mode 100644 libmspack/OAB/oab.cs delete mode 100644 libmspack/OAB/oabd_file.cs delete mode 100644 libmspack/Quantum/qtm.cs delete mode 100644 libmspack/Quantum/qtmd_model.cs delete mode 100644 libmspack/Quantum/qtmd_modelsym.cs delete mode 100644 libmspack/Quantum/qtmd_stream.cs delete mode 100644 libmspack/SZDD/Compressor.cs delete mode 100644 libmspack/SZDD/Constants.cs delete mode 100644 libmspack/SZDD/Decompressor.cs delete mode 100644 libmspack/SZDD/msszddd_header.cs delete mode 100644 libmspack/crc32.cs delete mode 100644 libmspack/macros.cs delete mode 100644 libmspack/mspack_default_system.cs delete mode 100644 libmspack/mspack_file.cs delete mode 100644 libmspack/mspack_file_p.cs delete mode 100644 libmspack/mspack_system.cs delete mode 100644 libmspack/readbits.cs delete mode 100644 libmspack/readhuff.cs delete mode 100644 libmspack/system.cs diff --git a/libmspack/BaseCompressor.cs b/libmspack/BaseCompressor.cs deleted file mode 100644 index 42ea3b7..0000000 --- a/libmspack/BaseCompressor.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// Base class for all compressor implementations - /// - public abstract class BaseCompressor : mspack_file - { -#if NET48 - public mspack_system system { get; set; } -#else - public mspack_system? system { get; set; } -#endif - - public MSPACK_ERR error { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/BaseDecompressor.cs b/libmspack/BaseDecompressor.cs deleted file mode 100644 index 1de341a..0000000 --- a/libmspack/BaseDecompressor.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// Base class for all decompressor implementations - /// - public abstract class BaseDecompressor : mspack_file - { -#if NET48 - public mspack_system system { get; set; } -#else - public mspack_system? system { get; set; } -#endif - - public MSPACK_ERR error { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/BitStream.cs b/libmspack/BitStream.cs deleted file mode 100644 index 962ef18..0000000 --- a/libmspack/BitStream.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public unsafe abstract class BitStream - { - /// - /// I/O routines - /// - public mspack_system sys { get; set; } - - /// - /// Input file handle - /// - public mspack_file input { get; set; } - - /// - /// Output file handle - /// - public mspack_file output { get; set; } - - /// - /// Decompression offset within window - /// - public uint window_posn { get; set; } - - #region I/O buffering - - public byte* inbuf { get; set; } - - public byte* i_ptr { get; set; } - - public byte* i_end { get; set; } - - public byte* o_ptr { get; set; } - - public byte* o_end { get; set; } - - public int input_end { get; set; } - - public uint bit_buffer { get; set; } - - public uint bits_left { get; set; } - - public uint inbuf_size { get; set; } - - #endregion - - public MSPACK_ERR error { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CAB/CABSystem.cs b/libmspack/CAB/CABSystem.cs deleted file mode 100644 index 9442bb2..0000000 --- a/libmspack/CAB/CABSystem.cs +++ /dev/null @@ -1,231 +0,0 @@ -using static SabreTools.Compression.libmspack.macros; -using static SabreTools.Compression.libmspack.CAB.Constants; - -namespace SabreTools.Compression.libmspack.CAB -{ - public unsafe class CABSystem : mspack_default_system - { - /// - /// cabd_sys_read is the internal reader function which the decompressors - /// use. will read data blocks (and merge split blocks) from the cabinet - /// and serve the read bytes to the decompressors - /// - public override int read(mspack_file file, void* buffer, int bytes) - { - Decompressor self = (Decompressor)file; - byte* buf = (byte*)buffer; - mspack_system sys = self.system; - int avail, todo, outlen = 0, ignore_cksum, ignore_blocksize; - - ignore_cksum = self.salvage != 0 || (self.fix_mszip != 0 && ((MSCAB_COMP)((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == MSCAB_COMP.MSCAB_COMP_MSZIP)) == true ? 1 : 0; - ignore_blocksize = self.salvage; - - todo = bytes; - while (todo > 0) - { - avail = (int)(self.d.i_end - self.d.i_ptr); - - // If out of input data, read a new block - if (avail != 0) - { - // Copy as many input bytes available as possible - if (avail > todo) avail = todo; - sys.copy(self.d.i_ptr, buf, avail); - self.d.i_ptr += avail; - buf += avail; - todo -= avail; - } - else - { - // Out of data, read a new block - - // Check if we're out of input blocks, advance block counter - if (self.d.block++ >= self.d.folder.num_blocks) - { - if (self.salvage == 0) - { - self.read_error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - else - { - System.Console.Error.WriteLine("Ran out of CAB input blocks prematurely"); - } - break; - } - - // Read a block - self.read_error = ReadBlock(sys, self.d, ref outlen, ignore_cksum, ignore_blocksize); - if (self.read_error != MSPACK_ERR.MSPACK_ERR_OK) return -1; - self.d.outlen += outlen; - - // Special Quantum hack -- trailer byte to allow the decompressor - // to realign itself. CAB Quantum blocks, unlike LZX blocks, can have - // anything from 0 to 4 trailing null bytes. - if ((MSCAB_COMP)((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == MSCAB_COMP.MSCAB_COMP_QUANTUM) - { - *self.d.i_end++ = 0xFF; - } - - // Is this the last block? - if (self.d.block >= self.d.folder.num_blocks) - { - if ((MSCAB_COMP)((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == MSCAB_COMP.MSCAB_COMP_LZX) - { - // Special LZX hack -- on the last block, inform LZX of the - // size of the output data stream. - lzxd_set_output_length((lzxd_stream)self.d.state, self.d.outlen); - } - } - } /* if (avail) */ - } /* while (todo > 0) */ - return bytes - todo; - } - - /// - /// cabd_sys_write is the internal writer function which the decompressors - /// use. it either writes data to disk (self.d.outfh) with the real - /// sys.write() function, or does nothing with the data when - /// self.d.outfh == null. advances self.d.offset - /// - public override int write(mspack_file file, void* buffer, int bytes) - { - Decompressor self = (Decompressor)file; - self.d.offset += (uint)bytes; - if (self.d.outfh != null) - { - return self.system.write(self.d.outfh, buffer, bytes); - } - return bytes; - } - - /// - /// Reads a whole data block from a cab file. the block may span more than - /// one cab file, if it does then the fragments will be reassembled - /// - private static MSPACK_ERR ReadBlock(mspack_system sys, mscabd_decompress_state d, ref int @out, int ignore_cksum, int ignore_blocksize) - { - FixedArray hdr = new FixedArray(cfdata_SIZEOF); - uint cksum; - int len, full_len; - - // Reset the input block pointer and end of block pointer - d.i_ptr = d.i_end = d.input; - - do - { - // Read the block header - if (sys.read(d.infh, hdr, cfdata_SIZEOF) != cfdata_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Skip any reserved block headers - if (d.data.cab.block_resv != 0 && - sys.seek(d.infh, d.data.cab.block_resv, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Blocks must not be over CAB_INPUTMAX in size - len = EndGetI16(hdr, cfdata_CompressedSize); - full_len = (int)(d.i_end - d.i_ptr + len); // Include cab-spanning blocks */ - if (full_len > CAB_INPUTMAX) - { - System.Console.Error.WriteLine($"Block size {full_len} > CAB_INPUTMAX"); - // In salvage mode, blocks can be 65535 bytes but no more than that - if (ignore_blocksize == 0 || full_len > CAB_INPUTMAX_SALVAGE) - { - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - } - - // Blocks must not expand to more than CAB_BLOCKMAX - if (EndGetI16(hdr, cfdata_UncompressedSize) > CAB_BLOCKMAX) - { - System.Console.Error.WriteLine("block size > CAB_BLOCKMAX"); - if (ignore_blocksize == 0) return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Read the block data - if (sys.read(d.infh, d.i_end, len) != len) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Perform checksum test on the block (if one is stored) - if ((cksum = EndGetI32(hdr, cfdata_CheckSum)) != 0) - { - uint sum2 = Checksum(d.i_end, (uint)len, 0); - if (Checksum(hdr, 4, 4, sum2) != cksum) - { - if (ignore_cksum == 0) return MSPACK_ERR.MSPACK_ERR_CHECKSUM; - sys.message(d.infh, "WARNING; bad block checksum found"); - } - } - - // Advance end of block pointer to include newly read data - d.i_end += len; - - // Uncompressed size == 0 means this block was part of a split block - // and it continues as the first block of the next cabinet in the set. - // otherwise, this is the last part of the block, and no more block - // reading needs to be done. - - // EXIT POINT OF LOOP -- uncompressed size != 0 - if ((@out = EndGetI16(hdr, cfdata_UncompressedSize)) != 0) - { - return MSPACK_ERR.MSPACK_ERR_OK; - } - - // Otherwise, advance to next cabinet - - // Close current file handle - sys.close(d.infh); - d.infh = null; - - // Aadvance to next member in the cabinet set - if ((d.data = d.data.next) == null) - { - sys.message(d.infh, "WARNING; ran out of cabinets in set. Are any missing?"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Open next cab file - d.incab = d.data.cab; - if ((d.infh = sys.open(d.incab.filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) == null) - { - return MSPACK_ERR.MSPACK_ERR_OPEN; - } - - // Seek to start of data blocks - if (sys.seek(d.infh, d.data.offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - } while (true); - - // Not reached - return MSPACK_ERR.MSPACK_ERR_OK; - } - - private static uint Checksum(FixedArray data, int ptr, uint bytes, uint cksum) - { - uint len, ul = 0; - - for (len = bytes >> 2; len-- > 0; ptr += 4) - { - cksum ^= EndGetI32(data, ptr); - } - - switch (bytes & 3) - { - case 3: ul |= (uint)(data[ptr++] << 16); goto case 2; - case 2: ul |= (uint)(data[ptr++] << 8); goto case 1; - case 1: ul |= data[ptr]; break; - } - cksum ^= ul; - - return cksum; - } - } -} \ No newline at end of file diff --git a/libmspack/CAB/Compressor.cs b/libmspack/CAB/Compressor.cs deleted file mode 100644 index 0fdaadc..0000000 --- a/libmspack/CAB/Compressor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SabreTools.Compression.libmspack.CAB -{ - /// - /// TODO - /// - public class Compressor : BaseCompressor - { - /// - /// Creates a new CAB compressor - /// - public Compressor() - { - this.system = new CABSystem(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file diff --git a/libmspack/CAB/Constants.cs b/libmspack/CAB/Constants.cs deleted file mode 100644 index 2ea4b30..0000000 --- a/libmspack/CAB/Constants.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace SabreTools.Compression.libmspack.CAB -{ - public static class Constants - { - /* 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 cffileCONTINUED_FROM_PREV = 0xFFFD; - public const ushort cffileCONTINUED_TO_NEXT = 0xFFFE; - public const ushort cffileCONTINUED_PREV_AND_NEXT = 0xFFFF; - - /* 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. Quantum has no documentation, but the largest - * block seen in the wild is 337 bytes above uncompressed size. - */ - public const int CAB_BLOCKMAX = 32768; - public const int CAB_INPUTMAX = CAB_BLOCKMAX + 6144; - - /* input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block - * plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment. - * - * When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be - * up to 65535 bytes, so max input buffer size needed is 65535 + 1 - */ - public const int CAB_INPUTMAX_SALVAGE = 65535; - public const int CAB_INPUTBUF = CAB_INPUTMAX_SALVAGE + 1; - - /* There are no more than 65535 data blocks per folder, so a folder cannot - * be more than 32768*65535 bytes in length. As files cannot span more than - * one folder, this is also their max offset, length and offset+length limit. - */ - public const int CAB_FOLDERMAX = 65535; - public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX; - } -} \ No newline at end of file diff --git a/libmspack/CAB/Decompressor.cs b/libmspack/CAB/Decompressor.cs deleted file mode 100644 index 6688a7e..0000000 --- a/libmspack/CAB/Decompressor.cs +++ /dev/null @@ -1,1242 +0,0 @@ -using static SabreTools.Compression.libmspack.cab; -using static SabreTools.Compression.libmspack.macros; -using static SabreTools.Compression.libmspack.CAB.Constants; - -namespace SabreTools.Compression.libmspack.CAB -{ - /// - /// A decompressor for .CAB (Microsoft Cabinet) files - /// - /// All fields are READ ONLY. - /// - public unsafe class Decompressor : BaseDecompressor - { - public mscabd_decompress_state d { get; set; } - - public int buf_size { get; private set; } - - public int searchbuf_size { get; private set; } - - public int fix_mszip { get; private set; } - - public int salvage { get; private set; } - - public MSPACK_ERR read_error { get; set; } - - /// - /// Creates a new CAB decompressor - /// - public Decompressor() - { - this.system = new CABSystem(); - this.d = null; - this.error = MSPACK_ERR.MSPACK_ERR_OK; - - this.searchbuf_size = 32768; - this.fix_mszip = 0; - this.buf_size = 4096; - this.salvage = 0; - } - - /// - /// Destroys an existing CAB decompressor. - /// - ~Decompressor() - { - mspack_system sys = this.system; - if (this.d != null) - { - if (this.d.infh != null) sys.close(this.d.infh); - cab.cabd_free_decomp(this); - //sys.free(this.d); - } - - //sys.free(this); - } - - /// - /// Opens a cabinet file and reads its contents. - /// - /// If the file opened is a valid cabinet file, all headers will be read - /// and a mscabd_cabinet structure will be returned, with a full list of - /// folders and files. - /// - /// In the case of an error occuring, null is returned and the error code - /// is available from last_error(). - /// - /// The filename pointer should be considered "in use" until close() is - /// called on the cabinet. - /// - /// - /// The filename of the cabinet file. This is passed - /// directly to mspack_system::open(). - /// - /// A pointer to a mscabd_cabinet structure, or null on failure - /// - /// - /// - public mscabd_cabinet open(in string filename) - { - mscabd_cabinet cab = null; - mspack_file fh; - - if ((fh = this.system.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) != null) - { - cab = new mscabd_cabinet(); - cab.filename = filename; - MSPACK_ERR error = ReadHeaders(this.system, fh, cab, 0, this.salvage, 0); - if (error != MSPACK_ERR.MSPACK_ERR_OK) - { - close(cab); - cab = null; - } - this.error = error; - this.system.close(fh); - } - else - { - this.error = MSPACK_ERR.MSPACK_ERR_OPEN; - } - - return cab; - } - - /// - /// Closes a previously opened cabinet or cabinet set. - /// - /// This closes a cabinet, all cabinets associated with it via the - /// mscabd_cabinet::next, mscabd_cabinet::prevcab and - /// mscabd_cabinet::nextcab pointers, and all folders and files. All - /// memory used by these entities is freed. - /// - /// The cabinet pointer is now invalid and cannot be used again. All - /// mscabd_folder and mscabd_file pointers from that cabinet or cabinet - /// set are also now invalid, and cannot be used again. - /// - /// If the cabinet pointer given was created using search(), it MUST be - /// the cabinet pointer returned by search() and not one of the later - /// cabinet pointers further along the mscabd_cabinet::next chain. - /// - /// If extra cabinets have been added using append() or prepend(), these - /// will all be freed, even if the cabinet pointer given is not the first - /// cabinet in the set. Do NOT close() more than one cabinet in the set. - /// - /// The mscabd_cabinet::filename is not freed by the library, as it is - /// not allocated by the library. The caller should free this itself if - /// necessary, before it is lost forever. - /// - /// The cabinet to close - /// - /// - /// - /// - public void close(mscabd_cabinet origcab) - { - mscabd_folder_data dat, ndat; - mscabd_cabinet cab, ncab; - mscabd_folder fol, nfol; - mscabd_file fi, nfi; - - mspack_system sys = this.system; - - this.error = MSPACK_ERR.MSPACK_ERR_OK; - - while (origcab != null) - { - // Free files - for (fi = origcab.files; fi != null; fi = nfi) - { - nfi = fi.next; - //sys.free(fi.filename); - //sys.free(fi); - } - - // Free folders - for (fol = origcab.folders; fol != null; fol = nfol) - { - nfol = fol.next; - - // Free folder decompression state if it has been decompressed - if (this.d != null && (this.d.folder == fol)) - { - if (this.d.infh != null) sys.close(this.d.infh); - cabd_free_decomp(this); - //sys.free(this.d); - this.d = null; - } - - // Free folder data segments - for (dat = fol.data.next; dat != null; dat = ndat) - { - ndat = dat.next; - //sys.free(dat); - } - - //sys.free(fol); - } - - // Free predecessor cabinets (and the original cabinet's strings) - for (cab = origcab; cab != null; cab = ncab) - { - ncab = cab.prevcab; - //sys.free(cab.prevname); - //sys.free(cab.nextname); - //sys.free(cab.previnfo); - //sys.free(cab.nextinfo); - //if (cab != origcab) sys.free(cab); - } - - // Free successor cabinets - for (cab = origcab.nextcab; cab != null; cab = ncab) - { - ncab = cab.nextcab; - //sys.free(cab.prevname); - //sys.free(cab.nextname); - //sys.free(cab.previnfo); - //sys.free(cab.nextinfo); - //sys.free(cab); - } - - // Free actual cabinet structure - cab = origcab.next; - //sys.free(origcab); - - // Repeat full procedure again with the cab.next pointer (if set) - origcab = cab; - } - } - - /// - /// Reads the cabinet file header, folder list and file list. - /// Fills out a pre-existing mscabd_cabinet structure, allocates memory - /// for folders and files as necessary - /// - private static MSPACK_ERR ReadHeaders(mspack_system sys, mspack_file fh, mscabd_cabinet cab, long offset, int salvage, int quiet) - { - int num_folders, num_files, folder_resv, i, x, fidx; - MSPACK_ERR err; - mscabd_folder fol, linkfol = null; - mscabd_file file, linkfile = null; - FixedArray buf = new FixedArray(64); - - // Initialise pointers - cab.next = null; - cab.files = null; - cab.folders = null; - cab.prevcab = cab.nextcab = null; - cab.prevname = cab.nextname = null; - cab.previnfo = cab.nextinfo = null; - - cab.base_offset = offset; - - // Seek to CFHEADER - if (sys.seek(fh, offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Read in the CFHEADER - if (sys.read(fh, buf, cfhead_SIZEOF) != cfhead_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Check for "MSCF" signature - if (EndGetI32(buf, cfhead_Signature) != 0x4643534D) - { - return MSPACK_ERR.MSPACK_ERR_SIGNATURE; - } - - // Some basic header fields - cab.length = EndGetI32(buf, cfhead_CabinetSize); - cab.set_id = EndGetI16(buf, cfhead_SetID); - cab.set_index = EndGetI16(buf, cfhead_CabinetIndex); - - // Get the number of folders - num_folders = EndGetI16(buf, cfhead_NumFolders); - if (num_folders == 0) - { - if (quiet == 0) sys.message(fh, "No folders in cabinet."); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Get the number of files - num_files = EndGetI16(buf, cfhead_NumFiles); - if (num_files == 0) - { - if (quiet == 0) sys.message(fh, "no files in cabinet."); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Check cabinet version - if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) - { - if (quiet == 0) sys.message(fh, "WARNING; cabinet version is not 1.3"); - } - - // Read the reserved-sizes part of header, if present - cab.flags = (MSCAB_HDR)EndGetI16(buf, cfhead_Flags); - - if (cab.flags.HasFlag(MSCAB_HDR.MSCAB_HDR_RESV)) - { - if (sys.read(fh, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - cab.header_resv = EndGetI16(buf, cfheadext_HeaderReserved); - folder_resv = buf[cfheadext_FolderReserved]; - cab.block_resv = buf[cfheadext_DataReserved]; - - if (cab.header_resv > 60000) - { - if (quiet == 0) sys.message(fh, "WARNING; reserved header > 60000."); - } - - // Skip the reserved header - if (cab.header_resv != 0) - { - if (sys.seek(fh, cab.header_resv, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - } - } - else - { - cab.header_resv = 0; - folder_resv = 0; - cab.block_resv = 0; - } - - // Read name and info of preceeding cabinet in set, if present - if (cab.flags.HasFlag(MSCAB_HDR.MSCAB_HDR_PREVCAB)) - { - cab.prevname = ReadString(sys, fh, 0, out err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) return err; - cab.previnfo = ReadString(sys, fh, 1, out err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) return err; - } - - // Read name and info of next cabinet in set, if present - if (cab.flags.HasFlag(MSCAB_HDR.MSCAB_HDR_NEXTCAB)) - { - cab.nextname = ReadString(sys, fh, 0, out err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) return err; - cab.nextinfo = ReadString(sys, fh, 1, out err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) return err; - } - - // Read folders - for (i = 0; i < num_folders; i++) - { - if (sys.read(fh, buf, cffold_SIZEOF) != cffold_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - if (folder_resv != 0) - { - if (sys.seek(fh, folder_resv, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - } - - fol = new mscabd_folder(); - - fol.next = null; - fol.comp_type = (MSCAB_COMP)EndGetI16(buf, cffold_CompType); - fol.num_blocks = (uint)EndGetI16(buf, cffold_NumBlocks); - fol.data.next = null; - fol.data.cab = cab; - fol.data.offset = offset + (long)((uint)EndGetI32(buf, cffold_DataOffset)); - fol.merge_prev = null; - fol.merge_next = null; - - // Link folder into list of folders - if (linkfol == null) cab.folders = fol; - else linkfol.next = fol; - linkfol = fol; - } - - // Read files - for (i = 0; i < num_files; i++) - { - if (sys.read(fh, buf, cffile_SIZEOF) != cffile_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - file = new mscabd_file(); - - file.next = null; - file.length = EndGetI32(buf, cffile_UncompressedSize); - file.attribs = (MSCAB_ATTRIB)EndGetI16(buf, cffile_Attribs); - file.offset = EndGetI32(buf, cffile_FolderOffset); - - // Set folder pointer - fidx = EndGetI16(buf, cffile_FolderIndex); - if (fidx < cffileCONTINUED_FROM_PREV) - { - /* normal folder index; count up to the correct folder */ - if (fidx < num_folders) - { - mscabd_folder ifol = cab.folders; - while (fidx-- > 0) if (ifol != null) ifol = ifol.next; - file.folder = ifol; - } - else - { - System.Console.Error.WriteLine("Invalid folder index"); - file.folder = null; - } - } - else - { - // either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or CONTINUED_PREV_AND_NEXT - if ((fidx == cffileCONTINUED_TO_NEXT) || (fidx == cffileCONTINUED_PREV_AND_NEXT)) - { - // Get last folder - mscabd_folder ifol = cab.folders; - while (ifol.next != null) ifol = ifol.next; - file.folder = ifol; - - // Set "merge next" pointer - fol = ifol; - if (fol.merge_next == null) fol.merge_next = file; - } - - if ((fidx == cffileCONTINUED_FROM_PREV) || (fidx == cffileCONTINUED_PREV_AND_NEXT)) - { - // Get first folder - file.folder = cab.folders; - - // Set "merge prev" pointer - fol = file.folder; - if (fol.merge_prev == null) fol.merge_prev = file; - } - } - - // Get time - x = EndGetI16(buf, cffile_Time); - file.time_h = (char)(x >> 11); - file.time_m = (char)((x >> 5) & 0x3F); - file.time_s = (char)((x << 1) & 0x3E); - - // Get date - x = EndGetI16(buf, cffile_Date); - file.date_d = (char)(x & 0x1F); - file.date_m = (char)((x >> 5) & 0xF); - file.date_y = (x >> 9) + 1980; - - // Get filename - file.filename = ReadString(sys, fh, 0, out err); - - // If folder index or filename are bad, either skip it or fail - if (err != MSPACK_ERR.MSPACK_ERR_OK || file.folder == null) - { - //sys.free(file.filename); - //sys.free(file); - if (salvage != 0) continue; - return err != MSPACK_ERR.MSPACK_ERR_OK ? err : MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Link file entry into file list - if (linkfile == null) cab.files = file; - else linkfile.next = file; - linkfile = file; - } - - if (cab.files == null) - { - // We never actually added any files to the file list. Something went wrong. - // The file header may have been invalid - - System.Console.Error.WriteLine($"No files found, even though header claimed to have {num_files} files"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - private static string ReadString(mspack_system sys, mspack_file fh, int permit_empty, out MSPACK_ERR error) - { - long @base = sys.tell(fh); - FixedArray buf = new FixedArray(256); - string str; - int len, i, ok; - - // Read up to 256 bytes - if ((len = sys.read(fh, buf, 256)) <= 0) - { - error = MSPACK_ERR.MSPACK_ERR_READ; - return null; - } - - // Search for a null terminator in the buffer - for (i = 0, ok = 0; i < len; i++) if (buf[i] == 0) { ok = 1; break; } - /* optionally reject empty strings */ - if (i == 0 && permit_empty == 0) ok = 0; - - if (ok == 0) - { - error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - return null; - } - - len = i + 1; - - /* set the data stream to just after the string and return */ - if (sys.seek(fh, @base + len, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - error = MSPACK_ERR.MSPACK_ERR_SEEK; - return null; - } - - FixedArray strchr = new FixedArray(len); - sys.copy(buf, (void*)strchr, len); - str = new string(strchr.ToArray()); - error = MSPACK_ERR.MSPACK_ERR_OK; - return str; - } - - /// - /// Searches a regular file for embedded cabinets. - /// - /// This opens a normal file with the given filename and will search the - /// entire file for embedded cabinet files - /// - /// If any cabinets are found, the equivalent of open() is called on each - /// potential cabinet file at the offset it was found. All successfully - /// open()ed cabinets are kept in a list. - /// - /// The first cabinet found will be returned directly as the result of - /// this method. Any further cabinets found will be chained in a list - /// using the mscabd_cabinet::next field. - /// - /// In the case of an error occuring anywhere other than the simulated - /// open(), null is returned and the error code is available from - /// last_error(). - /// - /// If no error occurs, but no cabinets can be found in the file, null is - /// returned and last_error() returns MSPACK_ERR.MSPACK_ERR_OK. - /// - /// The filename pointer should be considered in use until close() is - /// called on the cabinet. - /// - /// close() should only be called on the result of search(), not on any - /// subsequent cabinets in the mscabd_cabinet::next chain. - /// - /// - /// The filename of the file to search for cabinets. This - /// is passed directly to mspack_system::open(). - /// - /// A pointer to a mscabd_cabinet structure, or null - /// - /// - /// - public mscabd_cabinet search(in string filename) - { - mscabd_cabinet cab = null; - mspack_system sys; - FixedArray search_buf; - mspack_file fh; - long filelen, firstlen = 0; - - sys = this.system; - - // Allocate a search buffer - search_buf = new FixedArray(this.searchbuf_size); - if (search_buf == null) - { - this.error = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - return null; - } - - // Open file and get its full file length - if ((fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) != null) - { - if ((this.error = libmspack.system.mspack_sys_filelen(sys, fh, &filelen)) == MSPACK_ERR.MSPACK_ERR_OK) - { - this.error = Find(search_buf, fh, filename, filelen, ref firstlen, cab); - } - - // Truncated / extraneous data warning: - if (firstlen != 0 && (firstlen != filelen) && cab == null || (cab.base_offset == 0)) - { - if (firstlen < filelen) - { - sys.message(fh, $"WARNING; possible {filelen - firstlen} extra bytes at end of file"); - } - else - { - sys.message(fh, $"WARNING; file possibly truncated by {firstlen - filelen} bytes."); - } - } - - sys.close(fh); - } - else - { - this.error = MSPACK_ERR.MSPACK_ERR_OPEN; - } - - // Free the search buffer - sys.free(search_buf); - - return cab; - } - - /// - /// Find is the inner loop of , to make it easier to - /// break out of the loop and be sure that all resources are freed - /// - private MSPACK_ERR Find(FixedArray buf, mspack_file fh, in string filename, long flen, ref long firstlen, mscabd_cabinet firstcab) - { - mscabd_cabinet cab, link = null; - long caboff, offset, length; - mspack_system sys = this.system; - byte* p, pend; - byte state = 0; - uint cablen_u32 = 0, foffset_u32 = 0; - int false_cabs = 0; - - // Search through the full file length - for (offset = 0; offset < flen; offset += length) - { - // Search length is either the full length of the search buffer, or the - // amount of data remaining to the end of the file, whichever is less. - length = flen - offset; - if (length > this.searchbuf_size) - { - length = this.searchbuf_size; - } - - // Fill the search buffer with data from disk */ - if (sys.read(fh, buf, (int)length) != (int)length) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // FAQ avoidance strategy - if ((offset == 0) && (EndGetI32(buf, 0) == 0x28635349)) - { - sys.message(fh, "WARNING; found InstallShield header. Use unshield (https://github.com/twogood/unshield) to unpack this file"); - } - - // Read through the entire buffer. - for (p = buf, pend = (byte*)buf.Pointer + length; p < pend;) - { - switch (state) - { - // Starting state - case 0: - // we spend most of our time in this while loop, looking for - // a leading 'M' of the 'MSCF' signature - while (p < pend && *p != 0x4D) p++; - // If we found tht 'M', advance state - if (p++ < pend) state = 1; - break; - - // Verify that the next 3 bytes are 'S', 'C' and 'F' - case 1: state = (byte)((*p++ == 0x53) ? 2 : 0); break; - case 2: state = (byte)((*p++ == 0x43) ? 3 : 0); break; - case 3: state = (byte)((*p++ == 0x46) ? 4 : 0); break; - - // We don't care about bytes 4-7 (see default: for action) - - // Bytes 8-11 are the overall length of the cabinet - case 8: cablen_u32 = *p++; state++; break; - case 9: cablen_u32 |= (uint)(*p++ << 8); state++; break; - case 10: cablen_u32 |= (uint)(*p++ << 16); state++; break; - case 11: cablen_u32 |= (uint)(*p++ << 24); state++; break; - - // We don't care about bytes 12-15 (see default: for action) - - // Bytes 16-19 are the offset within the cabinet of the filedata - case 16: foffset_u32 = *p++; state++; break; - case 17: foffset_u32 |= (uint)(*p++ << 8); state++; break; - case 18: foffset_u32 |= (uint)(*p++ << 16); state++; break; - case 19: - foffset_u32 |= (uint)(*p++ << 24); - // Now we have recieved 20 bytes of potential cab header. work out - // the offset in the file of this potential cabinet */ - caboff = offset + (p - buf) - 20; - - // Should reading cabinet fail, restart search just after 'MSCF' - offset = caboff + 4; - - // Capture the "length of cabinet" field if there is a cabinet at - // offset 0 in the file, regardless of whether the cabinet can be - // read correctly or not - if (caboff == 0) firstlen = cablen_u32; - - // Check that the files offset is less than the alleged length of - // the cabinet, and that the offset + the alleged length are - // 'roughly' within the end of overall file length. In salvage - // mode, don't check the alleged length, allow it to be garbage - if ((foffset_u32 < cablen_u32) && - ((caboff + (long)foffset_u32) < (flen + 32)) && - (((caboff + (long)cablen_u32) < (flen + 32)) || this.salvage != 0)) - { - // Likely cabinet found -- try reading it - cab = new mscabd_cabinet(); - cab.filename = filename; - if (ReadHeaders(sys, fh, cab, caboff, this.salvage, 1) != MSPACK_ERR.MSPACK_ERR_OK) - { - // Destroy the failed cabinet - close(cab); - false_cabs++; - } - else - { - // Cabinet read correctly! - - // Link the cab into the list - if (link == null) firstcab = cab; - else link.next = cab; - link = cab; - - // Cause the search to restart after this cab's data. - offset = caboff + (long)cablen_u32; - } - } - - // Restart search - if (offset >= flen) return MSPACK_ERR.MSPACK_ERR_OK; - if (sys.seek(fh, offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - length = 0; - p = pend; - state = 0; - break; - - /* for bytes 4-7 and 12-15, just advance state/pointer */ - default: - p++; state++; - break; - } /* switch(state) */ - } /* for (... p < pend ...) */ - } /* for (... offset < length ...) */ - - if (false_cabs != 0) - { - System.Console.Error.WriteLine("%d false cabinets found", false_cabs); - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Appends one mscabd_cabinet to another, forming or extending a cabinet - /// set. - /// - /// This will attempt to append one cabinet to another such that - /// (cab.nextcab == nextcab) && (nextcab.prevcab == cab) and - /// any folders split between the two cabinets are merged. - /// - /// The cabinets MUST be part of a cabinet set -- a cabinet set is a - /// cabinet that spans more than one physical cabinet file on disk -- and - /// must be appropriately matched. - /// - /// It can be determined if a cabinet has further parts to load by - /// examining the mscabd_cabinet::flags field: - /// - /// - if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a - /// predecessor cabinet to open() and prepend(). Its MS-DOS - /// case-insensitive filename is mscabd_cabinet::prevname - /// - if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a - /// successor cabinet to open() and append(). Its MS-DOS case-insensitive - /// filename is mscabd_cabinet::nextname - /// - /// If the cabinets do not match, an error code will be returned. Neither - /// cabinet has been altered, and both should be closed seperately. - /// - /// Files and folders in a cabinet set are a single entity. All cabinets - /// in a set use the same file list, which is updated as cabinets in the - /// set are added. All pointers to mscabd_folder and mscabd_file - /// structures in either cabinet must be discarded and re-obtained after - /// merging. - /// - /// The cabinet which will be appended to, predecessor of nextcab - /// The cabinet which will be appended, successor of cab - /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful - /// - /// - /// - public MSPACK_ERR append(mscabd_cabinet cab, mscabd_cabinet nextcab) - { - return Merge(cab, nextcab); - } - - /// - /// Prepends one mscabd_cabinet to another, forming or extending a - /// cabinet set. - /// - /// This will attempt to prepend one cabinet to another, such that - /// (cab.prevcab == prevcab) && (prevcab.nextcab == cab). In - /// all other respects, it is identical to append(). See append() for the - /// full documentation. - /// - /// The cabinet which will be prepended to, successor of prevcab - /// The cabinet which will be prepended, predecessor of cab - /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful - /// - /// - /// - public MSPACK_ERR prepend(mscabd_cabinet cab, mscabd_cabinet prevcab) - { - return Merge(prevcab, cab); - } - - /// - /// Joins cabinets together, also merges split folders between these two - /// cabinets only. This includes freeing the duplicate folder and file(s) - /// and allocating a further mscabd_folder_data structure to append to the - /// merged folder's data parts list. - /// - private MSPACK_ERR Merge(mscabd_cabinet lcab, mscabd_cabinet rcab) - { - mscabd_folder_data data, ndata; - mscabd_folder lfol, rfol; - mscabd_file fi, rfi, lfi; - mscabd_cabinet cab; - - mspack_system sys = this.system; - - // Basic args check - if (lcab == null || rcab == null || (lcab == rcab)) - { - System.Console.Error.WriteLine("lcab null, rcab null or lcab = rcab"); - return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - } - - // Check there's not already a cabinet attached - if (lcab.nextcab != null || rcab.prevcab != null) - { - System.Console.Error.WriteLine("cabs already joined"); - return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - } - - // Do not create circular cabinet chains - for (cab = lcab.prevcab; cab != null; cab = cab.prevcab) - { - if (cab == rcab) - { - System.Console.Error.WriteLine("circular!"); - return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - } - } - for (cab = rcab.nextcab; cab != null; cab = cab.nextcab) - { - if (cab == lcab) - { - System.Console.Error.WriteLine("circular!"); - return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - } - } - - // Warn about odd set IDs or indices - if (lcab.set_id != rcab.set_id) - { - sys.message(null, "WARNING; merged cabinets with differing Set IDs."); - } - - if (lcab.set_index > rcab.set_index) - { - sys.message(null, "WARNING; merged cabinets with odd order."); - } - - // Merging the last folder in lcab with the first folder in rcab - lfol = lcab.folders; - rfol = rcab.folders; - while (lfol.next != null) - lfol = lfol.next; - - // Do we need to merge folders? - if (lfol.merge_next == null && rfol.merge_prev == null) - { - // No, at least one of the folders is not for merging - - // Attach cabs - lcab.nextcab = rcab; - rcab.prevcab = lcab; - - // Attach folders - lfol.next = rfol; - - // Attach files - fi = lcab.files; - while (fi.next != null) - fi = fi.next; - fi.next = rcab.files; - } - else - { - // Folder merge required - do the files match? - if (CanMergeFolders(sys, lfol, rfol) == 0) - { - return this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Allocate a new folder data structure - data = new mscabd_folder_data(); - - // Attach cabs - lcab.nextcab = rcab; - rcab.prevcab = lcab; - - // Append rfol's data to lfol - ndata = lfol.data; - while (ndata.next != null) - ndata = ndata.next; - ndata.next = data; - data = rfol.data; - rfol.data.next = null; - - // lfol becomes rfol. - // NOTE: special case, don't merge if rfol is merge prev and next, - // rfol.merge_next is going to be deleted, so keep lfol's version - // instead - lfol.num_blocks += rfol.num_blocks - 1; - if ((rfol.merge_next == null) || (rfol.merge_next.folder != rfol)) - { - lfol.merge_next = rfol.merge_next; - } - - // Attach the rfol's folder (except the merge folder) - while (lfol.next != null) - lfol = lfol.next; - lfol.next = rfol.next; - - // Free disused merge folder - //sys.free(rfol); - - // Attach rfol's files - fi = lcab.files; - while (fi.next != null) - fi = fi.next; - fi.next = rcab.files; - - // Delete all files from rfol's merge folder - lfi = null; - for (fi = lcab.files; fi != null; fi = rfi) - { - rfi = fi.next; - // If file's folder matches the merge folder, unlink and free it - if (fi.folder == rfol) - { - if (lfi != null) - lfi.next = rfi; - else - lcab.files = rfi; - //sys.free(fi.filename); - //sys.free(fi); - } - else - lfi = fi; - } - } - - // All done! fix files and folders pointers in all cabs so they all - // point to the same list - for (cab = lcab.prevcab; cab != null; cab = cab.prevcab) - { - cab.files = lcab.files; - cab.folders = lcab.folders; - } - - for (cab = lcab.nextcab; cab != null; cab = cab.nextcab) - { - cab.files = lcab.files; - cab.folders = lcab.folders; - } - - return this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Decides if two folders are OK to merge - /// - private int CanMergeFolders(mspack_system sys, mscabd_folder lfol, mscabd_folder rfol) - { - mscabd_file lfi, rfi, l, r; - int matching = 1; - - // Check that both folders use the same compression method/settings - if (lfol.comp_type != rfol.comp_type) - { - System.Console.Error.WriteLine("folder merge: compression type mismatch"); - return 0; - } - - /* check there are not too many data blocks after merging */ - if ((lfol.num_blocks + rfol.num_blocks) > CAB_FOLDERMAX) - { - System.Console.Error.WriteLine("folder merge: too many data blocks in merged folders"); - return 0; - } - - if ((lfi = lfol.merge_next) == null || (rfi = rfol.merge_prev) == null) - { - System.Console.Error.WriteLine("folder merge: one cabinet has no files to merge"); - return 0; - } - - // For all files in lfol (which is the last folder in whichever cab and - // only has files to merge), compare them to the files from rfol. They - // should be identical in number and order. to verify this, check the - // offset and length of each file. - for (l = lfi, r = rfi; l != null; l = l.next, r = r.next) - { - if (r == null || (l.offset != r.offset) || (l.length != r.length)) - { - matching = 0; - break; - } - } - - if (matching != 0) - return 1; - - // If rfol does not begin with an identical copy of the files in lfol, make - // make a judgement call; if at least ONE file from lfol is in rfol, allow - // the merge with a warning about missing files. - matching = 0; - for (l = lfi; l != null; l = l.next) - { - for (r = rfi; r != null; r = r.next) - { - if (l.offset == r.offset && l.length == r.length) - break; - } - if (r != null) - matching = 1; - else - sys.message(null, $"WARNING; merged file {l.filename} not listed in both cabinets"); - } - return matching; - } - - /// - /// Extracts a file from a cabinet or cabinet set. - /// - /// This extracts a compressed file in a cabinet and writes it to the given - /// filename. - /// - /// The MS-DOS filename of the file, mscabd_file::filename, is NOT USED - /// by extract(). The caller must examine this MS-DOS filename, copy and - /// change it as necessary, create directories as necessary, and provide - /// the correct filename as a parameter, which will be passed unchanged - /// to the decompressor's mspack_system::open() - /// - /// If the file belongs to a split folder in a multi-part cabinet set, - /// and not enough parts of the cabinet set have been loaded and appended - /// or prepended, an error will be returned immediately. - /// - /// The file to be decompressed - /// The filename of the file being written to - /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful - public MSPACK_ERR extract(mscabd_file file, in string filename) - { - mspack_file fh; - uint filelen; - - if (file == null) - return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - - mspack_system sys = this.system; - mscabd_folder fol = file.folder; - - // If offset is beyond 2GB, nothing can be extracted - if (file.offset > CAB_LENGTHMAX) - { - return this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - /* if file claims to go beyond 2GB either error out, - * or in salvage mode reduce file length so it fits 2GB limit - */ - filelen = file.length; - if (filelen > (CAB_LENGTHMAX - file.offset)) - { - if (this.salvage != 0) - { - filelen = CAB_LENGTHMAX - file.offset; - } - else - { - return this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - } - - // Extraction impossible if no folder, or folder needs predecessor - if (fol == null || fol.merge_prev != null) - { - sys.message(null, $"ERROR; file \"{file.filename}\" cannot be extracted, cabinet set is incomplete"); - return this.error = MSPACK_ERR.MSPACK_ERR_DECRUNCH; - } - - // If file goes beyond what can be decoded, given an error. - // In salvage mode, don't assume block sizes, just try decoding - if (this.salvage == 0) - { - uint maxlen = fol.num_blocks * CAB_BLOCKMAX; - if (file.offset > maxlen || filelen > (maxlen - file.offset)) - { - sys.message(null, $"ERROR; file \"{file.filename}\" cannot be extracted, cabinet set is incomplete"); - return this.error = MSPACK_ERR.MSPACK_ERR_DECRUNCH; - } - } - - // Allocate generic decompression state - if (this.d == null) - { - this.d = new None.DecompressState(); - this.d.sys = sys as CABSystem; - } - - // Do we need to change folder or reset the current folder? - if ((this.d.folder != fol) || (this.d.offset > file.offset) || this.d.state == null) - { - // Free any existing decompressor - cabd_free_decomp(this); - - // Do we need to open a new cab file? - if (this.d.infh == null || (fol.data.cab != this.d.incab)) - { - // Close previous file handle if from a different cab - if (this.d.infh != null) - sys.close(this.d.infh); - this.d.incab = fol.data.cab; - this.d.infh = sys.open(fol.data.cab.filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - if (this.d.infh == null) - return this.error = MSPACK_ERR.MSPACK_ERR_OPEN; - } - // Seek to start of data blocks - if (sys.seek(this.d.infh, fol.data.offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return this.error = MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Set up decompressor - if (cabd_init_decomp(this, fol.comp_type) != MSPACK_ERR.MSPACK_ERR_OK) - { - return this.error; - } - - // Initialise new folder state - this.d.folder = fol; - this.d.data = fol.data; - this.d.offset = 0; - this.d.block = 0; - this.d.outlen = 0; - this.d.i_ptr = this.d.i_end = d.input; - - // Read_error lasts for the lifetime of a decompressor - this.read_error = MSPACK_ERR.MSPACK_ERR_OK; - } - - // Open file for output - if ((fh = 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; - - // If file has more than 0 bytes - if (filelen != 0) - { - long bytes; - MSPACK_ERR error; - // Get to correct offset. - // - use null fh to say 'no writing' to cabd_sys_write() - // - if cabd_sys_read() has an error, it will set this.read_error - // and pass back MSPACK_ERR_READ - this.d.outfh = null; - if ((bytes = file.offset - this.d.offset) != 0) - { - error = this.d.decompress(this.d.state, bytes); - this.error = (error == MSPACK_ERR.MSPACK_ERR_READ) ? this.read_error : error; - } - - // If getting to the correct offset was error free, unpack file - if (this.error == MSPACK_ERR.MSPACK_ERR_OK) - { - this.d.outfh = fh; - error = this.d.decompress(this.d.state, filelen); - this.error = (error == MSPACK_ERR.MSPACK_ERR_READ) ? this.read_error : error; - } - } - - // Close output file - sys.close(fh); - this.d.outfh = null; - - return this.error; - } - - /// - /// Sets a CAB decompression engine parameter. - /// - /// The following parameters are defined: - /// - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a - /// buffer when using search()? The minimum value is 4. The default - /// value is 32768. - /// - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad - /// checksums and recover from decompression errors in MS-ZIP - /// compressed folders. The default value is 0 (don't recover). - /// - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input - /// bit buffer by decompressors? The minimum value is 4. The default - /// value is 4096. - /// - /// The parameter to set - /// The value to set the parameter to - /// - /// MSPACK_ERR.MSPACK_ERR_OK if all is OK, or MSPACK_ERR.MSPACK_ERR_ARGS if there - /// is a problem with either parameter or value. - /// - /// - /// - public MSPACK_ERR set_param(MSCABD_PARAM param, int value) - { - switch (param) - { - case MSCABD_PARAM.MSCABD_PARAM_SEARCHBUF: - if (value < 4) return MSPACK_ERR.MSPACK_ERR_ARGS; - this.searchbuf_size = value; - break; - case MSCABD_PARAM.MSCABD_PARAM_FIXMSZIP: - this.fix_mszip = value; - break; - case MSCABD_PARAM.MSCABD_PARAM_DECOMPBUF: - if (value < 4) return MSPACK_ERR.MSPACK_ERR_ARGS; - this.buf_size = value; - break; - case MSCABD_PARAM.MSCABD_PARAM_SALVAGE: - this.salvage = value; - break; - default: - return MSPACK_ERR.MSPACK_ERR_ARGS; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Returns the error code set by the most recently called method. - /// - /// This is useful for open() and search(), which do not return an error - /// code directly. - /// - /// The most recent error code - /// - /// - public MSPACK_ERR last_error() - { - return this.error; - } - } -} \ No newline at end of file diff --git a/libmspack/CAB/cab.cs b/libmspack/CAB/cab.cs deleted file mode 100644 index 6a35523..0000000 --- a/libmspack/CAB/cab.cs +++ /dev/null @@ -1,65 +0,0 @@ -using static SabreTools.Compression.libmspack.CAB.Constants; - -namespace SabreTools.Compression.libmspack -{ - public unsafe static class cab - { - #region decomp - - /// - /// cabd_free_decomp frees decompression state, according to which method - /// was used. - /// - public static MSPACK_ERR cabd_init_decomp(CAB.Decompressor self, MSCAB_COMP ct) - { - mspack_file fh = self; - - self.d.comp_type = ct; - - switch ((MSCAB_COMP)((int)ct & cffoldCOMPTYPE_MASK)) - { - case MSCAB_COMP.MSCAB_COMP_NONE: - 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(); - self.d.state = mszipd_init(self.d.sys, fh, fh, self.buf_size, self.fix_mszip); - break; - case MSCAB_COMP.MSCAB_COMP_QUANTUM: - self.d = new mscabd_qtmd_decompress_state(); - self.d.state = qtmd_init(self.d.sys, fh, fh, ((int)ct >> 8) & 0x1f, self.buf_size); - break; - case MSCAB_COMP.MSCAB_COMP_LZX: - self.d = new mscabd_lzxd_decompress_state(); - self.d.state = lzxd_init(self.d.sys, fh, fh, ((int)ct >> 8) & 0x1f, 0, self.buf_size, 0, 0); - break; - default: - return self.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - return self.error = (self.d.state != null) ? MSPACK_ERR.MSPACK_ERR_OK : MSPACK_ERR.MSPACK_ERR_NOMEMORY; - } - - /// - /// cabd_init_decomp initialises decompression state, according to which - /// decompression method was used. relies on self.d.folder being the same - /// as when initialised. - /// - public static void cabd_free_decomp(CAB.Decompressor self) - { - if (self == null || self.d == null || self.d.state == null) return; - - switch ((MSCAB_COMP)((int)self.d.comp_type & cffoldCOMPTYPE_MASK)) - { - 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; - } - - //self.d.decompress = null; - self.d.state = null; - } - - #endregion - } -} \ No newline at end of file diff --git a/libmspack/CAB/mscabd_cabinet.cs b/libmspack/CAB/mscabd_cabinet.cs deleted file mode 100644 index dfb5588..0000000 --- a/libmspack/CAB/mscabd_cabinet.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a single cabinet file. - /// - /// All fields are READ ONLY. - /// - /// If this cabinet is part of a merged cabinet set, the #files and #folders - /// fields are common to all cabinets in the set, and will be identical. - /// - /// - /// - /// - public unsafe class mscabd_cabinet - { - /// - /// The next cabinet in a chained list, if this cabinet was opened with - /// mscab_decompressor::search(). May be null to mark the end of the - /// list. - /// - public mscabd_cabinet next { get; set; } - - /// - /// The filename of the cabinet. More correctly, the filename of the - /// physical file that the cabinet resides in. This is given by the - /// library user and may be in any format. - /// - public string filename { get; set; } - - /// - /// The file offset of cabinet within the physical file it resides in. - /// - public long base_offset { get; set; } - - /// - /// The length of the cabinet file in bytes. - /// - public uint length { get; set; } - - /// - /// The previous cabinet in a cabinet set, or null. - /// - public mscabd_cabinet prevcab { get; set; } - - /// - /// The next cabinet in a cabinet set, or null. - /// - public mscabd_cabinet nextcab { get; set; } - - /// - /// The filename of the previous cabinet in a cabinet set, or null. - /// - public string prevname { get; set; } - - /// - /// The filename of the next cabinet in a cabinet set, or null. - /// - public string nextname { get; set; } - - /// - /// The name of the disk containing the previous cabinet in a cabinet - /// set, or null. - /// - public string previnfo { get; set; } - - /// - /// The name of the disk containing the next cabinet in a cabinet set, - /// or null. - /// - public string nextinfo { get; set; } - - /// - /// A list of all files in the cabinet or cabinet set. - /// - public mscabd_file files { get; set; } - - /// - /// A list of all folders in the cabinet or cabinet set. - /// - public mscabd_folder folders { get; set; } - - /// - /// The set ID of the cabinet. All cabinets in the same set should have - /// the same set ID. - /// - public ushort set_id { get; set; } - - /// - /// The index number of the cabinet within the set. Numbering should - /// start from 0 for the first cabinet in the set, and increment by 1 for - /// each following cabinet. - /// - public ushort set_index { get; set; } - - /// - /// The number of bytes reserved in the header area of the cabinet. - /// - /// If this is non-zero and flags has MSCAB_HDR_RESV set, this data can - /// be read by the calling application. It is of the given length, - /// located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the - /// cabinet file. - /// - public ushort header_resv { get; set; } - - /// - /// Header flags. - /// - /// - /// - /// - /// - /// - public MSCAB_HDR flags { get; set; } - - /// - /// Offset to data blocks - /// - public long blocks_off { get; set; } - - /// - /// Reserved space in data blocks - /// - public int block_resv { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CAB/mscabd_decompress_state.cs b/libmspack/CAB/mscabd_decompress_state.cs deleted file mode 100644 index de01f6d..0000000 --- a/libmspack/CAB/mscabd_decompress_state.cs +++ /dev/null @@ -1,83 +0,0 @@ -using SabreTools.Compression.libmspack.CAB; -using static SabreTools.Compression.libmspack.CAB.Constants; - -namespace SabreTools.Compression.libmspack -{ - public unsafe abstract class mscabd_decompress_state - { - /// - /// Current folder we're extracting from - /// - public mscabd_folder folder { get; set; } - - /// - /// Current folder split we're in - /// - public mscabd_folder_data data { get; set; } - - /// - /// Uncompressed offset within folder - /// - public uint offset { get; set; } - - /// - /// Which block are we decompressing? - /// - public uint block { get; set; } - - /// - /// Cumulative sum of block output sizes - /// - public long outlen { get; set; } - - /// - /// Special I/O code for decompressor - /// - public CABSystem sys { get; set; } - - /// - /// Type of compression used by folder - /// - public MSCAB_COMP comp_type { get; set; } - - /// - /// Decompressor state - /// - public object state { get; set; } - - /// - /// Cabinet where input data comes from - /// - public mscabd_cabinet incab { get; set; } - - /// - /// Input file handle - /// - public mspack_file infh { get; set; } - - /// - /// Output file handle - /// - public mspack_file outfh { get; set; } - - /// - /// Input data consumed - /// - public byte* i_ptr { get; set; } - - /// - /// Input data end - /// - public byte* i_end { get; set; } - - /// - /// One input block of data - /// - public FixedArray input { get; set; } = new FixedArray(CAB_INPUTBUF); - - /// - /// Decompressor code - /// - public abstract MSPACK_ERR decompress(object data, long offset); - } -} \ No newline at end of file diff --git a/libmspack/CAB/mscabd_file.cs b/libmspack/CAB/mscabd_file.cs deleted file mode 100644 index 57682ed..0000000 --- a/libmspack/CAB/mscabd_file.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a single file in a cabinet or cabinet set. - /// - /// All fields are READ ONLY. - /// - public class mscabd_file - { - /// - /// The next file in the cabinet or cabinet set, or null if this is the - /// final file. - /// - public mscabd_file next { get; set; } - - /// - /// The filename of the file. - /// - /// A null terminated string of up to 255 bytes in length, it may be in - /// either ISO-8859-1 or UTF8 format, depending on the file attributes. - /// - public string filename { get; set; } - - /// - /// The uncompressed length of the file, in bytes. - /// - public uint length { get; set; } - - /// - /// File attributes. - /// - public MSCAB_ATTRIB attribs { get; set; } - - /// - /// File's last modified time, hour field. - /// - public char time_h { get; set; } - - /// - /// File's last modified time, minute field. - /// - public char time_m { get; set; } - - /// - /// File's last modified time, second field. - /// - public char time_s { get; set; } - - /// - /// File's last modified date, day field. - /// - public char date_d { get; set; } - - /// - /// File's last modified date, month field. - /// - public char date_m { get; set; } - - /// - /// File's last modified date, year field. - /// - public int date_y; - - /// - /// A pointer to the folder that contains this file. - /// - public mscabd_folder folder { get; set; } - - /// - /// The uncompressed offset of this file in its folder. - /// - public uint offset { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CAB/mscabd_folder.cs b/libmspack/CAB/mscabd_folder.cs deleted file mode 100644 index 31d17ff..0000000 --- a/libmspack/CAB/mscabd_folder.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a single folder in a cabinet or cabinet set. - /// - /// All fields are READ ONLY. - /// - /// A folder is a single compressed stream of data. When uncompressed, it - /// holds the data of one or more files. A folder may be split across more - /// than one cabinet. - /// - public class mscabd_folder - { - /// - /// A pointer to the next folder in this cabinet or cabinet set, or null - /// if this is the final folder. - /// - public mscabd_folder next { get; set; } - - /// - /// The compression format used by this folder. - /// - /// The macro MSCABD_COMP_METHOD() should be used on this field to get - /// the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get - /// the "compression level". - /// - /// - /// - public MSCAB_COMP comp_type { get; set; } - - /// - /// The total number of data blocks used by this folder. This includes - /// data blocks present in other files, if this folder spans more than - /// one cabinet. - /// - public uint num_blocks { get; set; } - - /// - /// Where are the data blocks? - /// - public mscabd_folder_data data { get; set; } - - /// - /// First file needing backwards merge - /// - public mscabd_file merge_prev { get; set; } - - /// - /// First file needing forwards merge - /// - public mscabd_file merge_next { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CAB/mscabd_folder_data.cs b/libmspack/CAB/mscabd_folder_data.cs deleted file mode 100644 index 91a2442..0000000 --- a/libmspack/CAB/mscabd_folder_data.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// There is one of these for every cabinet a folder spans - /// - public class mscabd_folder_data - { - public mscabd_folder_data next { get; set; } - - /// - /// Cabinet file of this folder span - /// - public mscabd_cabinet cab { get; set; } - - /// - /// Cabinet offset of first datablock - /// - public long offset { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/Compressor.cs b/libmspack/CHM/Compressor.cs deleted file mode 100644 index d090cbb..0000000 --- a/libmspack/CHM/Compressor.cs +++ /dev/null @@ -1,169 +0,0 @@ -namespace SabreTools.Compression.libmspack.CHM -{ - /// - /// A compressor for .CHM (Microsoft HTMLHelp) files. - /// - /// All fields are READ ONLY. - /// - /// - public class Compressor : BaseCompressor - { - public string temp_file { get; private set; } - - public int use_temp_file { get; private set; } - - /// - /// Creates a new CHM compressor - /// - public Compressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Generates a CHM help file. - /// - /// The help file will contain up to two sections, an Uncompressed - /// section and potentially an MSCompressed (LZX compressed) - /// section. - /// - /// While the contents listing of a CHM file is always in lexical order, - /// the file list passed in will be taken as the correct order for files - /// within the sections. It is in your interest to place similar files - /// together for better compression. - /// - /// There are two modes of generation, to use a temporary file or not to - /// use one. See use_temporary_file() for the behaviour of generate() in - /// these two different modes. - /// - /// - /// An array of mschmc_file structures, terminated - /// with an entry whose mschmc_file::section field is - /// #MSCHMC_ENDLIST. The order of the list is - /// preserved within each section. The length of any - /// mschmc_file::chm_filename string cannot exceed - /// roughly 4096 bytes. Each source file must be able - /// to supply as many bytes as given in the - /// mschmc_file::length field. - /// - /// - /// The file to write the generated CHM helpfile to. - /// This is passed directly to mspack_system::open() - /// - /// An error code, or MSPACK_ERR_OK if successful - /// - /// - public MSPACK_ERR generate(mschmc_file[] file_list, in string output_file) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Specifies whether a temporary file is used during CHM generation. - /// - /// The CHM file format includes data about the compressed section (such - /// as its overall size) that is stored in the output CHM file prior to - /// the compressed section itself. This unavoidably requires that the - /// compressed section has to be generated, before these details can be - /// set. There are several ways this can be handled. Firstly, the - /// compressed section could be generated entirely in memory before - /// writing any of the output CHM file. This approach is not used in - /// libmspack, as the compressed section can exceed the addressable - /// memory space on most architectures. - /// - /// libmspack has two options, either to write these unknowable sections - /// with blank data, generate the compressed section, then re-open the - /// output file for update once the compressed section has been - /// completed, or to write the compressed section to a temporary file, - /// then write the entire output file at once, performing a simple - /// file-to-file copy for the compressed section. - /// - /// The simple solution of buffering the entire compressed section in - /// memory can still be used, if desired. As the temporary file's - /// filename is passed directly to mspack_system::open(), it is possible - /// for a custom mspack_system implementation to hold this file in memory, - /// without writing to a disk. - /// - /// If a temporary file is set, generate() performs the following - /// sequence of events: the temporary file is opened for writing, the - /// compression algorithm writes to the temporary file, the temporary - /// file is closed. Then the output file is opened for writing and the - /// temporary file is re-opened for reading. The output file is written - /// and the temporary file is read from. Both files are then closed. The - /// temporary file itself is not deleted. If that is desired, the - /// temporary file should be deleted after the completion of generate(), - /// if it exists. - /// - /// If a temporary file is set not to be used, generate() performs the - /// following sequence of events: the output file is opened for writing, - /// then it is written and closed. The output file is then re-opened for - /// update, the appropriate sections are seek()ed to and re-written, then - /// the output file is closed. - /// - /// - /// Non-zero if the temporary file should be used, - /// zero if the temporary file should not be used. - /// - /// - /// A file to temporarily write compressed data to, - /// before opening it for reading and copying the - /// contents to the output file. This is passed - /// directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR_OK if successful - /// - public MSPACK_ERR use_temporary_file(int use_temp_file, in string temp_file) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Sets a CHM compression engine parameter. - /// - /// The following parameters are defined: - /// - /// - #MSCHMC_PARAM_TIMESTAMP: Sets the "timestamp" of the CHM file - /// generated. This is not a timestamp, see mschmd_header::timestamp - /// for a description. If this timestamp is 0, generate() will use its - /// own algorithm for making a unique ID, based on the lengths and - /// names of files in the CHM itself. Defaults to 0, any value between - /// 0 and (2^32)-1 is valid. - /// - #MSCHMC_PARAM_LANGUAGE: Sets the "language" of the CHM file - /// generated. This is not the language used in the CHM file, but the - /// language setting of the user who ran the HTMLHelp compiler. It - /// defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F. - /// - #MSCHMC_PARAM_LZXWINDOW: Sets the size of the LZX history window, - /// which is also the interval at which the compressed data stream can be - /// randomly accessed. The value is not a size in bytes, but a power of - /// two. The default value is 16 (which makes the window 2^16 bytes, or - /// 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2 - /// megabytes). - /// - #MSCHMC_PARAM_DENSITY: Sets the "density" of quick reference - /// entries stored at the end of directory listing chunk. Each chunk is - /// 4096 bytes in size, and contains as many file entries as there is - /// room for. At the other end of the chunk, a list of "quick reference" - /// pointers is included. The offset of every 'N'th file entry is given a - /// quick reference, where N = (2^density) + 1. The default density is - /// 2. The smallest density is 0 (N=2), the maximum is 10 (N=1025). As - /// each file entry requires at least 5 bytes, the maximum number of - /// entries in a single chunk is roughly 800, so the maximum value 10 - /// can be used to indicate there are no quickrefs at all. - /// - #MSCHMC_PARAM_INDEX: Sets whether or not to include quick lookup - /// index chunk(s), in addition to normal directory listing chunks. A - /// value of zero means no index chunks will be created, a non-zero value - /// means index chunks will be created. The default is zero, "don't - /// create an index". - /// - /// The parameter to set - /// The value to set the parameter to - /// - /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there - /// is a problem with either parameter or value. - /// - /// - public MSPACK_ERR set_param(MSCHMC_PARAM param, int value) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Returns the error code set by the most recently called method. - /// - /// The most recent error code - /// - /// - public MSPACK_ERR last_error() => MSPACK_ERR.MSPACK_ERR_OK; - } -} \ No newline at end of file diff --git a/libmspack/CHM/Constants.cs b/libmspack/CHM/Constants.cs deleted file mode 100644 index 828c227..0000000 --- a/libmspack/CHM/Constants.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace SabreTools.Compression.libmspack.CHM -{ - public static class Constants - { - public const ushort chmhead_Signature = 0x0000; - public const ushort chmhead_Version = 0x0004; - public const ushort chmhead_HeaderLen = 0x0008; - public const ushort chmhead_Unknown1 = 0x000C; - public const ushort chmhead_Timestamp = 0x0010; - public const ushort chmhead_LanguageID = 0x0014; - public const ushort chmhead_GUID1 = 0x0018; - public const ushort chmhead_GUID2 = 0x0028; - public const ushort chmhead_SIZEOF = 0x0038; - - public const ushort chmhst_OffsetHS0 = 0x0000; - public const ushort chmhst_LengthHS0 = 0x0008; - public const ushort chmhst_OffsetHS1 = 0x0010; - public const ushort chmhst_LengthHS1 = 0x0018; - public const ushort chmhst_SIZEOF = 0x0020; - public const ushort chmhst3_OffsetCS0 = 0x0020; - public const ushort chmhst3_SIZEOF = 0x0028; - - public const ushort chmhs0_Unknown1 = 0x0000; - public const ushort chmhs0_Unknown2 = 0x0004; - public const ushort chmhs0_FileLen = 0x0008; - public const ushort chmhs0_Unknown3 = 0x0010; - public const ushort chmhs0_Unknown4 = 0x0014; - public const ushort chmhs0_SIZEOF = 0x0018; - - public const ushort chmhs1_Signature = 0x0000; - public const ushort chmhs1_Version = 0x0004; - public const ushort chmhs1_HeaderLen = 0x0008; - public const ushort chmhs1_Unknown1 = 0x000C; - public const ushort chmhs1_ChunkSize = 0x0010; - public const ushort chmhs1_Density = 0x0014; - public const ushort chmhs1_Depth = 0x0018; - public const ushort chmhs1_IndexRoot = 0x001C; - public const ushort chmhs1_FirstPMGL = 0x0020; - public const ushort chmhs1_LastPMGL = 0x0024; - public const ushort chmhs1_Unknown2 = 0x0028; - public const ushort chmhs1_NumChunks = 0x002C; - public const ushort chmhs1_LanguageID = 0x0030; - public const ushort chmhs1_GUID = 0x0034; - public const ushort chmhs1_Unknown3 = 0x0044; - public const ushort chmhs1_Unknown4 = 0x0048; - public const ushort chmhs1_Unknown5 = 0x004C; - public const ushort chmhs1_Unknown6 = 0x0050; - public const ushort chmhs1_SIZEOF = 0x0054; - - public const ushort pmgl_Signature = 0x0000; - public const ushort pmgl_QuickRefSize = 0x0004; - public const ushort pmgl_Unknown1 = 0x0008; - public const ushort pmgl_PrevChunk = 0x000C; - public const ushort pmgl_NextChunk = 0x0010; - public const ushort pmgl_Entries = 0x0014; - public const ushort pmgl_headerSIZEOF = 0x0014; - - public const ushort pmgi_Signature = 0x0000; - public const ushort pmgi_QuickRefSize = 0x0004; - public const ushort pmgi_Entries = 0x0008; - public const ushort pmgi_headerSIZEOF = 0x000C; - - public const ushort lzxcd_Length = 0x0000; - public const ushort lzxcd_Signature = 0x0004; - public const ushort lzxcd_Version = 0x0008; - public const ushort lzxcd_ResetInterval = 0x000C; - public const ushort lzxcd_WindowSize = 0x0010; - public const ushort lzxcd_CacheSize = 0x0014; - public const ushort lzxcd_Unknown1 = 0x0018; - public const ushort lzxcd_SIZEOF = 0x001C; - - public const ushort lzxrt_Unknown1 = 0x0000; - public const ushort lzxrt_NumEntries = 0x0004; - public const ushort lzxrt_EntrySize = 0x0008; - public const ushort lzxrt_TableOffset = 0x000C; - public const ushort lzxrt_UncompLen = 0x0010; - public const ushort lzxrt_CompLen = 0x0018; - public const ushort lzxrt_FrameLen = 0x0020; - public const ushort lzxrt_Entries = 0x0028; - public const ushort lzxrt_headerSIZEOF = 0x0028; - } -} \ No newline at end of file diff --git a/libmspack/CHM/Decompressor.cs b/libmspack/CHM/Decompressor.cs deleted file mode 100644 index da62113..0000000 --- a/libmspack/CHM/Decompressor.cs +++ /dev/null @@ -1,922 +0,0 @@ -using System; -using System.Linq; -using System.Runtime.InteropServices; -using static SabreTools.Compression.libmspack.CHM.Constants; -using static SabreTools.Compression.libmspack.macros; -using static SabreTools.Compression.libmspack.system; - -namespace SabreTools.Compression.libmspack.CHM -{ - /// - /// A decompressor for .CHM (Microsoft HTMLHelp) files - /// - /// All fields are READ ONLY. - /// - /// - public unsafe class Decompressor : BaseDecompressor - { - public mschmd_decompress_state d { get; private set; } - - // Filenames of the system files used for decompression. - // Content and ControlData are essential. - // ResetTable is preferred, but SpanInfo can be used if not available - private const string content_name = "::DataSpace/Storage/MSCompressed/Content"; - private const string control_name = "::DataSpace/Storage/MSCompressed/ControlData"; - private const string spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo"; - private const string rtable_name = "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable"; - - // The GUIDs found in CHM header - private static readonly byte[] guids = new byte[32] - { - // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} - 0x10, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11, - 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC, - - // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} - 0x11, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11, - 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC - }; - - /// - /// Creates a new CHM decompressor - /// - public Decompressor() - { - this.system = new mspack_default_system(); - error = MSPACK_ERR.MSPACK_ERR_OK; - d = null; - } - - /// - /// Destroys an existing CHM decompressor - /// - ~Decompressor() - { - mspack_system sys = this.system; - if (this.d != null) - { - if (this.d.infh != null) sys.close(this.d.infh); - if (this.d.state != null) lzxd_free(this.d.state); - //sys.free(this.d); - } - //sys.free(this); - } - - /// - /// Opens a CHM helpfile and reads its contents. - /// - /// If the file opened is a valid CHM helpfile, all headers will be read - /// and a mschmd_header structure will be returned, with a full list of - /// files. - /// - /// In the case of an error occuring, null is returned and the error code - /// is available from last_error(). - /// - /// The filename pointer should be considered "in use" until close() is - /// called on the CHM helpfile. - /// - /// - /// The filename of the CHM helpfile. This is passed - /// directly to mspack_system::open(). - /// - /// A pointer to a mschmd_header structure, or null on failure - /// - public mschmd_header open(in string filename) - { - return chmd_real_open(filename, 1); - } - - /// - /// Closes a previously opened CHM helpfile. - /// - /// This closes a CHM helpfile, frees the mschmd_header and all - /// mschmd_file structures associated with it (if any). This works on - /// both helpfiles opened with open() and helpfiles opened with - /// fast_open(). - /// - /// The CHM header pointer is now invalid and cannot be used again. All - /// mschmd_file pointers referencing that CHM are also now invalid, and - /// cannot be used again. - /// - /// The CHM helpfile to close - /// - /// - public void close(mschmd_header chm) - { - mschmd_file fi, nfi; - mspack_system sys; - uint i; - - sys = this.system; - - this.error = MSPACK_ERR.MSPACK_ERR_OK; - - // Free files - for (fi = chm.files; fi != null; fi = nfi) - { - nfi = fi.next; - //sys.free(fi); - } - for (fi = chm.sysfiles; fi != null; fi = nfi) - { - nfi = fi.next; - //sys.free(fi); - } - - // If this CHM was being decompressed, free decompression state - if (this.d != null && (this.d.chm == chm)) - { - if (this.d.infh != null) sys.close(this.d.infh); - if (this.d.state != null) lzxd_free(this.d.state); - //sys.free(this.d); - this.d = null; - } - - // If this CHM had a chunk cache, free it and contents - if (chm.chunk_cache != null) - { - for (i = 0; i < chm.num_chunks; i++) sys.free(chm.chunk_cache[i]); - sys.free(chm.chunk_cache); - } - - //sys.free(chm); - } - - /// - /// Reads the basic CHM file headers. If the "entire" parameter is - /// non-zero, all file entries will also be read. fills out a pre-existing - /// mschmd_header structure, allocates memory for files as necessary - /// - private MSPACK_ERR chmd_read_headers(mspack_system sys, mspack_file fh, mschmd_header chm, int entire) - { - uint errors, num_chunks; - FixedArray buf = new FixedArray(0x54); - FixedArray chunk = null; - byte* name, p, end; - mschmd_file fi, link = null; - long offset_hs0, filelen; - int num_entries; - MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK; - - // Initialise pointers - chm.files = null; - chm.sysfiles = null; - chm.chunk_cache = null; - chm.sec0.chm = chm; - chm.sec0.id = 0; - chm.sec1.chm = chm; - chm.sec1.id = 1; - chm.sec1.content = null; - chm.sec1.control = null; - chm.sec1.spaninfo = null; - chm.sec1.rtable = null; - - // Read the first header - if (sys.read(fh, buf, chmhead_SIZEOF) != chmhead_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Check ITSF signature - if (EndGetI32(buf, chmhead_Signature) != 0x46535449) - { - return MSPACK_ERR.MSPACK_ERR_SIGNATURE; - } - - // Check both header GUIDs - if (!buf.ToArray().Skip(chmhead_GUID1).Take(guids.Length).SequenceEqual(guids)) - { - Console.Error.WriteLine("Incorrect GUIDs"); - return MSPACK_ERR.MSPACK_ERR_SIGNATURE; - } - - chm.version = EndGetI32(buf, chmhead_Version); - chm.timestamp = EndGetM32(buf, chmhead_Timestamp); - chm.language = EndGetI32(buf, chmhead_LanguageID); - if (chm.version > 3) - { - sys.message(fh, "WARNING; CHM version > 3"); - } - - // Read the header section table - if (sys.read(fh, buf, chmhst3_SIZEOF) != chmhst3_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files. - // The offset will be corrected later, once HS1 is read. - if (read_off64(&offset_hs0, &buf[chmhst_OffsetHS0], sys, fh) || - read_off64(&chm.dir_offset, &buf[chmhst_OffsetHS1], sys, fh) || - read_off64(&chm.sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh)) - { - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Seek to header section 0 - if (sys.seek(fh, offset_hs0, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Read header section 0 - if (sys.read(fh, buf, chmhs0_SIZEOF) != chmhs0_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - if (read_off64(&chm.length, &buf[chmhs0_FileLen], sys, fh)) - { - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Compare declared CHM file size against actual size - if (mspack_sys_filelen(sys, fh, &filelen) == 0) - { - if (chm.length > filelen) - { - sys.message(fh, $"WARNING; file possibly truncated by {chm.length - filelen} bytes"); - } - else if (chm.length < filelen) - { - sys.message(fh, $"WARNING; possible {filelen - chm.length} extra bytes at end of file"); - } - } - - // Seek to header section 1 - if (sys.seek(fh, chm.dir_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Read header section 1 - if (sys.read(fh, buf, chmhs1_SIZEOF) != chmhs1_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - chm.dir_offset = sys.tell(fh); - chm.chunk_size = EndGetI32(buf, chmhs1_ChunkSize); - chm.density = EndGetI32(buf, chmhs1_Density); - chm.depth = EndGetI32(buf, chmhs1_Depth); - chm.index_root = EndGetI32(buf, chmhs1_IndexRoot); - chm.num_chunks = EndGetI32(buf, chmhs1_NumChunks); - chm.first_pmgl = EndGetI32(buf, chmhs1_FirstPMGL); - chm.last_pmgl = EndGetI32(buf, chmhs1_LastPMGL); - - if (chm.version < 3) - { - // Versions before 3 don't have chmhst3_OffsetCS0 - chm.sec0.offset = chm.dir_offset + (chm.chunk_size * chm.num_chunks); - } - - // Check if content offset or file size is wrong - if (chm.sec0.offset > chm.length) - { - Console.Error.WriteLine("content section begins after file has ended"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Ensure there are chunks and that chunk size is - // large enough for signature and num_entries - if (chm.chunk_size < (pmgl_Entries + 2)) - { - Console.Error.WriteLine("chunk size not large enough"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - if (chm.num_chunks == 0) - { - Console.Error.WriteLine("no chunks"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // The chunk_cache data structure is not great; large values for num_chunks - // or num_chunks*chunk_size can exhaust all memory. Until a better chunk - // cache is implemented, put arbitrary limits on num_chunks and chunk size. - if (chm.num_chunks > 100000) - { - Console.Error.WriteLine("more than 100,000 chunks"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - if (chm.chunk_size > 8192) - { - Console.Error.WriteLine("chunk size over 8192 (get in touch if this is valid)"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - if ((long)chm.chunk_size * (long)chm.num_chunks > chm.length) - { - Console.Error.WriteLine("chunks larger than entire file"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // Common sense checks on header section 1 fields - if (chm.chunk_size != 4096) - { - sys.message(fh, "WARNING; chunk size is not 4096"); - } - if (chm.first_pmgl != 0) - { - sys.message(fh, "WARNING; first PMGL chunk is not zero"); - } - if (chm.first_pmgl > chm.last_pmgl) - { - Console.Error.WriteLine("first pmgl chunk is after last pmgl chunk"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - if (chm.index_root != 0xFFFFFFFF && chm.index_root >= chm.num_chunks) - { - Console.Error.WriteLine("index_root outside valid range"); - return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - // If we are doing a quick read, stop here! - if (entire == 0) - { - return MSPACK_ERR.MSPACK_ERR_OK; - } - - // Seek to the first PMGL chunk, and reduce the number of chunks to read - if (chm.first_pmgl != 0) - { - long pmgl_offset = (long)chm.first_pmgl * (long)chm.chunk_size; - if (sys.seek(fh, pmgl_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - } - num_chunks = chm.last_pmgl - chm.first_pmgl + 1; - - chunk = new FixedArray((int)chm.chunk_size); - - // Read and process all chunks from FirstPMGL to LastPMGL - errors = 0; - while (num_chunks-- > 0) - { - // Read next chunk - if (sys.read(fh, chunk, (int)chm.chunk_size) != (int)chm.chunk_size) - { - sys.free(chunk); - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Process only directory (PMGL) chunks - if (EndGetI32(chunk, pmgl_Signature) != 0x4C474D50) continue; - - if (EndGetI32(chunk, pmgl_QuickRefSize) < 2) - { - sys.message(fh, "WARNING; PMGL quickref area is too small"); - } - if (EndGetI32(chunk, pmgl_QuickRefSize) > - (chm.chunk_size - pmgl_Entries)) - { - sys.message(fh, "WARNING; PMGL quickref area is too large"); - } - - p = (byte*)chunk.Pointer + pmgl_Entries; - end = (byte*)chunk.Pointer + chm.chunk_size - 2; - num_entries = EndGetI16(chunk, (int)(chm.chunk_size - 2)); - - while (num_entries-- > 0) - { - uint name_len, section; - long offset, length; - name_len = read_encint(&p, end, &err); - if (err != MSPACK_ERR.MSPACK_ERR_OK || (name_len > (uint)(end - p))) goto encint_err; - name = p; p += name_len; - section = read_encint(&p, end, &err); - offset = read_encint(&p, end, &err); - length = read_encint(&p, end, &err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err; - - // Ignore blank or one-char (e.g. "/") filenames we'd return as blank */ - if (name_len < 2 || name[0] == 0x00 || name[1] == 0x00) continue; - - // Empty files and directory names are stored as a file entry at - // offset 0 with length 0. We want to keep empty files, but not - // directory names, which end with a "/" - if ((offset == 0) && (length == 0)) - { - if ((name_len > 0) && (name[name_len - 1] == '/')) continue; - } - - if (section > 1) - { - sys.message(fh, $"Invalid section number '{section}'."); - continue; - } - - fi = new mschmd_file(); - fi.next = null; - fi.section = (section == 0 ? (mschmd_section)chm.sec0 : (mschmd_section)chm.sec1); - fi.offset = offset; - fi.length = length; - - char[] filenameArr = new char[name_len]; - Marshal.Copy((IntPtr)name, filenameArr, 0, (int)name_len); - filenameArr[(int)name_len] = '\0'; - fi.filename = new string(filenameArr); - - if (name[0] == ':' && name[1] == ':') - { - // System file - if (name_len == 40 && fi.filename.StartsWith(content_name)) - { - chm.sec1.content = fi; - } - else if (name_len == 44 && fi.filename.StartsWith(control_name)) - { - chm.sec1.control = fi; - } - else if (name_len == 41 && fi.filename.StartsWith(spaninfo_name)) - { - chm.sec1.spaninfo = fi; - } - else if (name_len == 105 && fi.filename.StartsWith(rtable_name)) - { - chm.sec1.rtable = fi; - } - fi.next = chm.sysfiles; - chm.sysfiles = fi; - } - else - { - // Normal file - if (link != null) link.next = fi; else chm.files = fi; - link = fi; - } - } - - // This is reached either when num_entries runs out, or if - // an ENCINT is badly encoded - encint_err: - if (num_entries >= 0) - { - Console.Error.WriteLine("bad encint before all entries could be read"); - errors++; - } - } - - sys.free(chunk); - return (errors > 0) ? MSPACK_ERR.MSPACK_ERR_DATAFORMAT : MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Extracts a file from a CHM helpfile. - /// - /// This extracts a file from a CHM helpfile and writes it to the given - /// filename. The filename of the file, mscabd_file::filename, is not - /// used by extract(), but can be used by the caller as a guide for - /// constructing an appropriate filename. - /// - /// This method works both with files found in the mschmd_header::files - /// and mschmd_header::sysfiles list and mschmd_file structures generated - /// on the fly by fast_find(). - /// - /// The file to be decompressed - /// The filename of the file being written to - /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR extract(mschmd_file file, in string filename) => throw new NotImplementedException(); - - /// - /// Returns the error code set by the most recently called method. - /// - /// This is useful for open() and fast_open(), which do not return an - /// error code directly. - /// - /// The most recent error code - /// - /// - public MSPACK_ERR last_error() => throw new NotImplementedException(); - - /// - /// Opens a CHM helpfile quickly. - /// - /// If the file opened is a valid CHM helpfile, only essential headers - /// will be read. A mschmd_header structure will be still be returned, as - /// with open(), but the mschmd_header::files field will be null. No - /// files details will be automatically read. The fast_find() method - /// must be used to obtain file details. - /// - /// In the case of an error occuring, null is returned and the error code - /// is available from last_error(). - /// - /// The filename pointer should be considered "in use" until close() is - /// called on the CHM helpfile. - /// - /// - /// The filename of the CHM helpfile. This is passed - /// directly to mspack_system::open(). - /// - /// A pointer to a mschmd_header structure, or null on failure - /// - /// - /// - /// - public mschmd_header fast_open(in string filename) - { - return chmd_real_open(filename, 0); - } - - /// - /// The real implementation of chmd_open() and chmd_fast_open(). It simply - /// passes the "entire" parameter to chmd_read_headers(), which will then - /// either read all headers, or a bare mininum. - /// - private mschmd_header chmd_real_open(in string filename, int entire) - { - mschmd_header chm = null; - MSPACK_ERR error; - - mspack_system sys = this.system; - - mspack_file fh; - if ((fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) != null) - { - chm = new mschmd_header(); - chm.filename = filename; - error = chmd_read_headers(sys, fh, chm, entire); - if (error != MSPACK_ERR.MSPACK_ERR_OK) - { - // If the error is DATAFORMAT, and there are some results, return - // partial results with a warning, rather than nothing - if (error == MSPACK_ERR.MSPACK_ERR_DATAFORMAT && (chm.files != null || chm.sysfiles != null)) - { - sys.message(fh, "WARNING; contents are corrupt"); - error = MSPACK_ERR.MSPACK_ERR_OK; - } - else - { - close(chm); - chm = null; - } - } - - this.error = error; - sys.close(fh); - } - else - { - this.error = MSPACK_ERR.MSPACK_ERR_OPEN; - } - return chm; - } - - /// - /// Finds file details quickly. - /// - /// Instead of reading all CHM helpfile headers and building a list of - /// files, fast_open() and fast_find() are intended for finding file - /// details only when they are needed. The CHM file format includes an - /// on-disk file index to allow this. - /// - /// Given a case-sensitive filename, fast_find() will search the on-disk - /// index for that file. - /// - /// If the file was found, the caller-provided mschmd_file structure will - /// be filled out like so: - /// - section: the correct value for the found file - /// - offset: the correct value for the found file - /// - length: the correct value for the found file - /// - all other structure elements: null or 0 - /// - /// If the file was not found, MSPACK_ERR_OK will still be returned as the - /// result, but the caller-provided structure will be filled out like so: - /// - section: null - /// - offset: 0 - /// - length: 0 - /// - all other structure elements: null or 0 - /// - /// This method is intended to be used in conjunction with CHM helpfiles - /// opened with fast_open(), but it also works with helpfiles opened - /// using the regular open(). - /// - /// The CHM helpfile to search for the file - /// The filename of the file to search for - /// A pointer to a caller-provded mschmd_file structure - /// sizeof(mschmd_file) - /// An error code, or MSPACK_ERR_OK if successful - /// - /// - /// - /// - public MSPACK_ERR fast_find(mschmd_header chm, in string filename, ref mschmd_file f_ptr, int f_size) - { - mspack_system sys; - mspack_file fh; - - // p and end are initialised to prevent MSVC warning about "potentially" - // uninitialised usage. This is provably untrue, but MS won't fix: - // https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html - FixedArray chunk; - byte* p = null, end = null; - MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK; - int result = -1; - uint n, sec; - - if (chm == null || f_ptr == null) - { - return MSPACK_ERR.MSPACK_ERR_ARGS; - } - - sys = this.system; - - // Clear the results structure - f_ptr = new mschmd_file(); - - if ((fh = sys.open(chm.filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) == null) - { - return MSPACK_ERR.MSPACK_ERR_OPEN; - } - - // Go through PMGI chunk hierarchy to reach PMGL chunk - if (chm.index_root < chm.num_chunks) - { - n = chm.index_root; - for (; ; ) - { - if ((chunk = read_chunk(chm, fh, n)) == null) - { - sys.close(fh); - return this.error; - } - - // Search PMGI/PMGL chunk. exit early if no entry found - if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0) - { - break; - } - - // Found result. loop around for next chunk if this is PMGI - if (chunk[3] == 0x4C) break; - - n = read_encint(&p, end, &err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err; - } - } - else - { - // PMGL chunks only, search from first_pmgl to last_pmgl - for (n = chm.first_pmgl; n <= chm.last_pmgl; n = EndGetI32(chunk, pmgl_NextChunk)) - { - if ((chunk = read_chunk(chm, fh, n)) == null) - { - err = this.error; - break; - } - - // Search PMGL chunk. exit if file found - if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0) - { - break; - } - - // Stop simple infinite loops: can't visit the same chunk twice - if (n == EndGetI32(chunk, pmgl_NextChunk)) - { - break; - } - } - } - - // If we found a file, read it - if (result > 0) - { - sec = read_encint(&p, end, &err); - f_ptr.section = (sec == 0) ? (mschmd_section)chm.sec0 : (mschmd_section)chm.sec1; - f_ptr.offset = read_encint(&p, end, &err); - f_ptr.length = read_encint(&p, end, &err); - if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err; - } - else if (result < 0) - { - err = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - sys.close(fh); - return this.error = err; - - encint_err: - Console.Error.WriteLine("Bad encint in PGMI/PGML chunk"); - sys.close(fh); - return this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - } - - /// - /// Reads the given chunk into memory, storing it in a chunk cache - /// so it doesn't need to be read from disk more than once - /// - /// - private FixedArray read_chunk(mschmd_header chm, mspack_file fh, uint chunk_num) - { - mspack_system sys = this.system; - FixedArray buf; - - // Check arguments - most are already checked by chmd_fast_find - if (chunk_num >= chm.num_chunks) return null; - - // Ensure chunk cache is available - if (chm.chunk_cache == null) - { - chm.chunk_cache = new FixedArray[chm.num_chunks]; - if (chm.chunk_cache == null) - { - this.error = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - return null; - } - } - - // Try to answer out of chunk cache - if (chm.chunk_cache[chunk_num] != null) return chm.chunk_cache[chunk_num]; - - // Need to read chunk - allocate memory for it - buf = new FixedArray((int)chm.chunk_size); - - // Seek to block and read it - if (sys.seek(fh, chm.dir_offset + (chunk_num * chm.chunk_size), MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - this.error = MSPACK_ERR.MSPACK_ERR_SEEK; - sys.free(buf); - return null; - } - if (sys.read(fh, buf, (int)chm.chunk_size) != (int)chm.chunk_size) - { - this.error = MSPACK_ERR.MSPACK_ERR_READ; - sys.free(buf); - return null; - } - - // Check the signature. Is is PMGL or PMGI? - if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && ((buf[3] == 0x4C) || (buf[3] == 0x49)))) - { - this.error = MSPACK_ERR.MSPACK_ERR_SEEK; - sys.free(buf); - return null; - } - - // All OK. Store chunk in cache and return it - return chm.chunk_cache[chunk_num] = buf; - } - - /// - /// Searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on - /// data format error, 0 if entry definitely not found, 1 if entry - /// found. In the latter case, *result and *result_end are set pointing - /// to that entry's data (either the "next chunk" ENCINT for a PMGI or - /// the section, offset and length ENCINTs for a PMGL). - /// - /// In the case of PMGL chunks, the entry has definitely been - /// found. In the case of PMGI chunks, the entry which points to the - /// chunk that may eventually contain that entry has been found. - /// - /// - private int search_chunk(mschmd_header chm, in FixedArray chunk, in string filename, byte** result, byte** result_end) - { - byte* p; - uint qr_size, num_entries, qr_entries, qr_density, name_len; - uint L, R, M, entries_off, is_pmgl; - int cmp; - MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK; - - uint fname_len = (uint)filename.Length; - - // PMGL chunk or PMGI chunk? (note: read_chunk() has already - // checked the rest of the characters in the chunk signature) - if (chunk[3] == 0x4C) - { - is_pmgl = 1; - entries_off = pmgl_Entries; - } - else - { - is_pmgl = 0; - entries_off = pmgi_Entries; - } - - // Step 1: binary search first filename of each QR entry - // - target filename == entry - // found file - // - target filename < all entries - // file not found - // - target filename > all entries - // proceed to step 2 using final entry - // - target filename between two searched entries - // proceed to step 2 - qr_size = EndGetI32(chunk, pmgl_QuickRefSize); - int start = (int)(chm.chunk_size - 2); - int end = (int)(chm.chunk_size - qr_size); - num_entries = EndGetI16(chunk, (int)(chm.chunk_size - 2)); - qr_density = (uint)(1 + (1 << (int)chm.density)); - qr_entries = (num_entries + qr_density - 1) / qr_density; - - if (num_entries == 0) - { - Console.Error.WriteLine("Chunk has no entries"); - return -1; - } - - if (qr_size > chm.chunk_size) - { - Console.Error.WriteLine("Quickref size > chunk size"); - return -1; - } - - *result_end = &chunk[end]; - - if (((int)qr_entries * 2) > (start - end)) - { - Console.Error.WriteLine("WARNING; more quickrefs than quickref space"); - qr_entries = 0; // But we can live with it - } - - if (qr_entries > 0) - { - L = 0; - R = qr_entries - 1; - do - { - // Pick new midpoint - M = (L + R) >> 1; - - // Compare filename with entry QR points to - p = &chunk[entries_off + (M != 0 ? EndGetI16(chunk, start - (int)(M << 1)) : 0)]; - name_len = read_encint(&p, end, &err); - if (err != MSPACK_ERR.MSPACK_ERR_OK || (name_len > (uint)(end - p))) goto encint_err; - cmp = compare(filename, (char*)p, fname_len, name_len); - - if (cmp == 0) break; - else if (cmp < 0) { if (M) R = M - 1; else return 0; } - else if (cmp > 0) L = M + 1; - } while (L <= R); - M = (L + R) >> 1; - - if (cmp == 0) - { - /* exact match! */ - p += name_len; - *result = p; - return 1; - } - - /* otherwise, read the group of entries for QR entry M */ - p = &chunk[entries_off + (M ? EndGetI16(chunk, start - (M << 1)) : 0)]; - num_entries -= (M * qr_density); - if (num_entries > qr_density) num_entries = qr_density; - } - else - { - p = &chunk[entries_off]; - } - - /* Step 2: linear search through the set of entries reached in step 1. - * - filename == any entry - * found entry - * - filename < all entries (PMGI) or any entry (PMGL) - * entry not found, stop now - * - filename > all entries - * entry not found (PMGL) / maybe found (PMGI) - * - - */ - *result = null; - while (num_entries-- > 0) - { - name_len = read_encint(&p, end, &err); - if (err || (name_len > (uint)(end - p))) goto encint_err; - cmp = compare(filename, (char*)p, fname_len, name_len); - p += name_len; - - if (cmp == 0) - { - /* entry found */ - *result = p; - return 1; - } - - if (cmp < 0) - { - /* entry not found (PMGL) / maybe found (PMGI) */ - break; - } - - /* read and ignore the rest of this entry */ - if (is_pmgl) - { - while (p < end && (*p++ & 0x80)) ; /* skip section ENCINT */ - while (p < end && (*p++ & 0x80)) ; /* skip offset ENCINT */ - while (p < end && (*p++ & 0x80)) ; /* skip length ENCINT */ - } - else - { - *result = p; /* store potential final result */ - while (p < end && (*p++ & 0x80)) ; /* skip chunk number ENCINT */ - } - } - - /* PMGL? not found. PMGI? maybe found */ - return (is_pmgl) ? 0 : (*result ? 1 : 0); - - encint_err: - Console.Error.WriteLine("bad encint while searching"); - return -1; - } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmc_file.cs b/libmspack/CHM/mschmc_file.cs deleted file mode 100644 index f633c69..0000000 --- a/libmspack/CHM/mschmc_file.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a file to be placed in a CHM helpfile. - /// - /// A contiguous array of these structures should be passed to - /// mschm_compressor::generate(). The array list is terminated with an - /// entry whose mschmc_file::section field is set to #MSCHMC_ENDLIST, the - /// other fields in this entry are ignored. - /// - public class mschmc_file - { - /// - /// One of values. - /// - public MSCHMC section { get; set; } - - /// - /// The filename of the source file that will be added to the CHM. This - /// is passed directly to mspack_system::open(). - /// - public string filename { get; set; } - - /// - /// The full path and filename of the file within the CHM helpfile, a - /// UTF-1 encoded null-terminated string. - /// - public string chm_filename { get; set; } - - /// - /// The length of the file, in bytes. This will be adhered to strictly - /// and a read error will be issued if this many bytes cannot be read - /// from the real file at CHM generation time. - /// - public long length { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_decompress_state.cs b/libmspack/CHM/mschmd_decompress_state.cs deleted file mode 100644 index aefd3ce..0000000 --- a/libmspack/CHM/mschmd_decompress_state.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public class mschmd_decompress_state - { - /// - /// CHM file being decompressed - /// - public mschmd_header chm { get; set; } - - /// - /// Uncompressed length of LZX stream - /// - public long length { get; set; } - - /// - /// Uncompressed offset within stream - /// - public long offset { get; set; } - - /// - /// Offset in input file - /// - public long inoffset { get; set; } - - /// - /// LZX decompressor state - /// - public lzxd_stream state { get; set; } - - /// - /// Special I/O code for decompressor - /// - public mspack_system sys { get; set; } - - /// - /// Input file handle - /// - public mspack_file infh { get; set; } - - /// - /// Output file handle - /// - public mspack_file outfh { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_file.cs b/libmspack/CHM/mschmd_file.cs deleted file mode 100644 index 0b92633..0000000 --- a/libmspack/CHM/mschmd_file.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a file stored in a CHM helpfile. - /// - /// All fields are READ ONLY. - /// - public class mschmd_file - { - /// - /// A pointer to the next file in the list, or null if this is the final - /// file. - /// - public mschmd_file next { get; set; } - - /// - /// A pointer to the section that this file is located in. Indirectly, - /// it also points to the CHM helpfile the file is located in. - /// - public mschmd_section section { get; set; } - - /// - /// The offset within the section data that this file is located at. - /// - public long offset { get; set; } - - /// - /// The length of this file, in bytes - /// - public long length { get; set; } - - /// - /// The filename of this file -- a null terminated string in UTF-8. - /// - public string filename { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_header.cs b/libmspack/CHM/mschmd_header.cs deleted file mode 100644 index 844e5bc..0000000 --- a/libmspack/CHM/mschmd_header.cs +++ /dev/null @@ -1,120 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a CHM helpfile. - /// - /// All fields are READ ONLY. - /// - public unsafe class mschmd_header - { - /// - /// The version of the CHM file format used in this file. - /// - public uint version { get; set; } - - /// - /// The "timestamp" of the CHM helpfile. - /// - /// It is the lower 32 bits of a 64-bit value representing the number of - /// centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful - /// as a timestamp, but it is useful as a semi-unique ID. - /// - public uint timestamp { get; set; } - - /// - /// The default Language and Country ID (LCID) of the user who ran the - /// HTMLHelp Compiler. This is not the language of the CHM file itself. - /// - public uint language { get; set; } - - /// - /// The filename of the CHM helpfile. This is given by the library user - /// and may be in any format. - /// - public string filename { get; set; } - - /// - /// The length of the CHM helpfile, in bytes. - /// - public long length { get; set; } - - /// - /// A list of all non-system files in the CHM helpfile. - /// - public mschmd_file files { get; set; } - - /// - /// A list of all system files in the CHM helpfile. - /// - /// System files are files which begin with "::". They are meta-files - /// generated by the CHM creation process. - /// - public mschmd_file sysfiles { get; set; } - - /// - /// The section 0 (uncompressed) data in this CHM helpfile. - /// - public mschmd_sec_uncompressed sec0 { get; set; } - - /// - /// The section 1 (MSCompressed) data in this CHM helpfile. - /// - public mschmd_sec_mscompressed sec1 { get; set; } - - /// - /// The file offset of the first PMGL/PMGI directory chunk. - /// - public long dir_offset { get; set; } - - /// - /// The number of PMGL/PMGI directory chunks in this CHM helpfile. - /// - public uint num_chunks { get; set; } - - /// - /// The size of each PMGL/PMGI chunk, in bytes. - /// - public uint chunk_size { get; set; } - - /// - /// The "density" of the quick-reference section in PMGL/PMGI chunks. - /// - public uint density { get; set; } - - /// - /// The depth of the index tree. - /// - /// - if 1, there are no PMGI chunks, only PMGL chunks. - /// - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. - /// - if 3, the root PMGI chunk points to secondary PMGI chunks, which in - /// turn point to PMGL chunks. - /// - and so on... - /// - public uint depth { get; set; } - - /// - /// The number of the root PMGI chunk. - /// - /// If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. - /// - public uint index_root { get; set; } - - /// - /// The number of the first PMGL chunk. Usually zero. - /// Available only in CHM decoder version 2 and above. - /// - public uint first_pmgl { get; set; } - - /// - /// The number of the last PMGL chunk. Usually num_chunks-1. - /// Available only in CHM decoder version 2 and above. - /// - public uint last_pmgl { get; set; } - - /// - /// A cache of loaded chunks, filled in by mschm_decoder::fast_find(). - /// Available only in CHM decoder version 2 and above. - /// - public FixedArray[] chunk_cache { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_sec_mscompressed.cs b/libmspack/CHM/mschmd_sec_mscompressed.cs deleted file mode 100644 index fc990d4..0000000 --- a/libmspack/CHM/mschmd_sec_mscompressed.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents the LZX compressed section of a CHM helpfile. - /// - /// All fields are READ ONLY. - /// - public class mschmd_sec_mscompressed : mschmd_section - { - /// - /// A pointer to the meta-file which represents all LZX compressed data. - /// - public mschmd_file content { get; set; } - - /// - /// A pointer to the file which contains the LZX control data. - /// - public mschmd_file control { get; set; } - - /// - /// A pointer to the file which contains the LZX reset table. - /// - public mschmd_file rtable { get; set; } - - /// - /// A pointer to the file which contains the LZX span information. - /// Available only in CHM decoder version 2 and above. - /// - public mschmd_file spaninfo { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_sec_uncompressed.cs b/libmspack/CHM/mschmd_sec_uncompressed.cs deleted file mode 100644 index ef5e8ee..0000000 --- a/libmspack/CHM/mschmd_sec_uncompressed.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents the uncompressed section of a CHM helpfile. - /// - /// All fields are READ ONLY. - /// - public class mschmd_sec_uncompressed : mschmd_section - { - /// - /// The file offset of where this section begins in the CHM helpfile. - /// - public long offset { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/CHM/mschmd_section.cs b/libmspack/CHM/mschmd_section.cs deleted file mode 100644 index 28f4265..0000000 --- a/libmspack/CHM/mschmd_section.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents a section of a CHM helpfile. - /// - /// All fields are READ ONLY. - /// - /// Not used directly, but used as a generic base type for - /// mschmd_sec_uncompressed and mschmd_sec_mscompressed. - /// - public class mschmd_section - { - /// - /// A pointer to the CHM helpfile that contains this section. - /// - public mschmd_header chm { get; set; } - - /// - /// The section ID. Either 0 for the uncompressed section - /// mschmd_sec_uncompressed, or 1 for the LZX compressed section - /// mschmd_sec_mscompressed. No other section IDs are known. - /// - public uint id { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/Constants.cs b/libmspack/Constants.cs deleted file mode 100644 index 4e6f91c..0000000 --- a/libmspack/Constants.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class Constants - { - /// - /// Offset from start of cabinet to the reserved header data (if present). - /// - public const int MSCAB_HDR_RESV_OFFSET = 0x28; - } -} \ No newline at end of file diff --git a/libmspack/Enums.cs b/libmspack/Enums.cs deleted file mode 100644 index 058c74c..0000000 --- a/libmspack/Enums.cs +++ /dev/null @@ -1,422 +0,0 @@ -using System; - -namespace SabreTools.Compression.libmspack -{ - #region mspack.h - - /// - /// mspack_system::open() mode - /// - public enum MSPACK_SYS_OPEN : int - { - /// - /// Open existing file for reading - /// - MSPACK_SYS_OPEN_READ = 0, - - /// - /// Open new file for writing - /// - MSPACK_SYS_OPEN_WRITE = 1, - - /// - /// Open existing file for writing - /// - MSPACK_SYS_OPEN_UPDATE = 2, - - /// - /// Open existing file for writing - /// - MSPACK_SYS_OPEN_APPEND = 3, - } - - /// - /// mspack_system::seek() mode - /// - public enum MSPACK_SYS_SEEK : int - { - /// - /// Seek relative to start of file - /// - MSPACK_SYS_SEEK_START = 0, - - /// - /// Seek relative to current offset - /// - MSPACK_SYS_SEEK_CUR = 1, - - /// - /// Seek relative to end of file - /// - MSPACK_SYS_SEEK_END = 2, - } - - /// - /// Error code - /// - public enum MSPACK_ERR : int - { - MSPACK_ERR_OK = 0, - - /// - /// Bad arguments to method - /// - MSPACK_ERR_ARGS = 1, - - /// - /// Error opening file - /// - MSPACK_ERR_OPEN = 2, - - /// - /// Error reading file - /// - MSPACK_ERR_READ = 3, - - /// - /// Error writing file - /// - MSPACK_ERR_WRITE = 4, - - /// - /// Seek error - /// - MSPACK_ERR_SEEK = 5, - - /// - /// Out of memory - /// - MSPACK_ERR_NOMEMORY = 6, - - /// - /// Bad "magic id" in file - /// - MSPACK_ERR_SIGNATURE = 7, - - /// - /// Bad or corrupt file format - /// - MSPACK_ERR_DATAFORMAT = 8, - - /// - /// Bad checksum or CRC - /// - MSPACK_ERR_CHECKSUM = 9, - - /// - /// Error during compression - /// - MSPACK_ERR_CRUNCH = 10, - - /// - /// Error during decompression - /// - MSPACK_ERR_DECRUNCH = 11, - } - - /// - /// Cabinet header flag - /// - [Flags] - public enum MSCAB_HDR : int - { - /// - /// Cabinet has a predecessor - /// - MSCAB_HDR_PREVCAB = 0x01, - - /// - /// Cabinet has a successor - /// - MSCAB_HDR_NEXTCAB = 0x02, - - /// - /// Cabinet has reserved header space - /// - MSCAB_HDR_RESV = 0x04, - } - - /// - /// Compression mode - /// - public enum MSCAB_COMP : int - { - /// - /// No compression - /// - MSCAB_COMP_NONE = 0, - - /// - /// MSZIP (deflate) compression - /// - MSCAB_COMP_MSZIP = 1, - - /// - /// Quantum compression - /// - MSCAB_COMP_QUANTUM = 2, - - /// - /// LZX compression - /// - MSCAB_COMP_LZX = 3, - } - - /// - /// mscabd_file::attribs attribute - /// - [Flags] - public enum MSCAB_ATTRIB : int - { - /// - /// File is read-only - /// - MSCAB_ATTRIB_RDONLY = 0x01, - - /// - /// File is hidden - /// - MSCAB_ATTRIB_HIDDEN = 0x02, - - /// - /// File is an operating system file - /// - MSCAB_ATTRIB_SYSTEM = 0x04, - - /// - /// File is "archived" - /// - MSCAB_ATTRIB_ARCH = 0x20, - - /// - /// File is an executable program - /// - MSCAB_ATTRIB_EXEC = 0x40, - - /// - /// Filename is UTF8, not ISO-8859-1 - /// - MSCAB_ATTRIB_UTF_NAME = 0x80, - } - - /// - /// mschmc_file::section value - /// - public enum MSCHMC : int - { - /// - /// End of CHM file list - /// - MSCHMC_ENDLIST = 0, - - /// - /// This file is in the Uncompressed section - /// - MSCHMC_UNCOMP = 1, - - /// - /// This file is in the MSCompressed section - /// - MSCHMC_MSCOMP = 2, - } - - /// - /// msszddd_header::format value - /// - public enum MSSZDD_FMT : int - { - /// - /// A regular SZDD file - /// - MSSZDD_FMT_NORMAL = 0, - - /// - /// A special QBasic SZDD file - /// - MSSZDD_FMT_QBASIC = 1, - } - - /// - /// WAJ compression type - /// - public enum MSKWAJ_COMP : int - { - /// - /// No compression - /// - MSKWAJ_COMP_NONE = 0, - - /// - /// No compression, 0xFF XOR "encryption" - /// - MSKWAJ_COMP_XOR = 1, - - /// - /// LZSS (same method as SZDD) - /// - MSKWAJ_COMP_SZDD = 2, - - /// - /// LZ+Huffman compression - /// - MSKWAJ_COMP_LZH = 3, - - /// - /// MSZIP - /// - MSKWAJ_COMP_MSZIP = 4, - } - - /// - /// KWAJ optional header flag - /// - [Flags] - public enum MSKWAJ_HDR : int - { - /// - /// Decompressed file length is included - /// - MSKWAJ_HDR_HASLENGTH = 0x01, - - /// - /// Unknown 2-byte structure is included - /// - MSKWAJ_HDR_HASUNKNOWN1 = 0x02, - - /// - /// Unknown multi-sized structure is included - /// - MSKWAJ_HDR_HASUNKNOWN2 = 0x04, - - /// - /// File name (no extension) is included - /// - MSKWAJ_HDR_HASFILENAME = 0x08, - - /// - /// File extension is included - /// - MSKWAJ_HDR_HASFILEEXT = 0x10, - - /// - /// Extra text is included - /// - MSKWAJ_HDR_HASEXTRATEXT = 0x20, - } - - #region Parameters - - /// - /// mscab_decompressor::set_param() parameter - /// - public enum MSCABD_PARAM : int - { - /// - /// Search buffer size - /// - MSCABD_PARAM_SEARCHBUF = 0, - - /// - /// Repair MS-ZIP streams? - /// - MSCABD_PARAM_FIXMSZIP = 1, - - /// - /// Size of decompression buffer - /// - MSCABD_PARAM_DECOMPBUF = 2, - - /// - /// Salvage data from bad cabinets? - /// If enabled, open() will skip file with bad folder indices or filenames - /// rather than reject the whole cabinet, and extract() will limit rather than - /// reject files with invalid offsets and lengths, and bad data block checksums - /// will be ignored. Available only in CAB decoder version 2 and above. - /// - MSCABD_PARAM_SALVAGE = 3, - } - - /// - /// mschm_compressor::set_param() parameter - /// - public enum MSCHMC_PARAM : int - { - /// - /// "timestamp" header - /// - MSCHMC_PARAM_TIMESTAMP = 0, - - /// - /// "language" header - /// - MSCHMC_PARAM_LANGUAGE = 1, - - /// - /// LZX window size - /// - MSCHMC_PARAM_LZXWINDOW = 2, - - /// - /// Intra-chunk quickref density - /// - MSCHMC_PARAM_DENSITY = 3, - - /// - /// Whether to create indices - /// - MSCHMC_PARAM_INDEX = 4, - } - - /// - /// msszdd_compressor::set_param() parameter - /// - public enum MSSZDDC_PARAM : int - { - /// - /// The missing character - /// - MSSZDDC_PARAM_MISSINGCHAR = 0, - } - - /// - /// mskwaj_compressor::set_param() parameter - /// - public enum MSKWAJC_PARAM : int - { - /// - /// Compression type - /// - MSKWAJC_PARAM_COMP_TYPE = 0, - - /// - /// Include the length of the uncompressed file in the header? - /// - MSKWAJC_PARAM_INCLUDE_LENGTH = 1, - } - - /// - /// msoab_decompressor::set_param() parameter - /// - public enum MSOABD_PARAM : int - { - /// - /// Size of decompression buffer - /// - MSOABD_PARAM_DECOMPBUF = 0, - } - - #endregion - - #endregion - - #region lzss.h - - public enum LZSS_MODE : int - { - LZSS_MODE_EXPAND = 0, - LZSS_MODE_MSHELP = 1, - LZSS_MODE_QBASIC = 2, - } - - #endregion -} \ No newline at end of file diff --git a/libmspack/FixedArray.cs b/libmspack/FixedArray.cs deleted file mode 100644 index 9493f11..0000000 --- a/libmspack/FixedArray.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace SabreTools.Compression.libmspack -{ - public unsafe class FixedArray where T : struct - { - /// - /// Direct access to the internal pointer - /// - public IntPtr Pointer { get; private set; } - - /// - /// Size of the T object - /// - private int sizeofT { get { return Marshal.SizeOf(typeof(T)); } } - - /// - /// Length of the fixed array - /// - private int _length; - - public T this[int i] - { - get - { - if (i < 0 || i >= _length) - return default; - - return (T)Marshal.PtrToStructure(Pointer + i * sizeofT, typeof(T)); - } - set - { - if (i < 0 || i >= _length) - return; - - Marshal.StructureToPtr(value, Pointer + i * sizeofT, false); - } - } - - public FixedArray(int length) - { - Pointer = Marshal.AllocHGlobal(sizeofT * length); - _length = 0; - } - - ~FixedArray() - { - Marshal.FreeHGlobal(Pointer); - } - - public static implicit operator T*(FixedArray arr) => (T*)arr.Pointer; - - public static implicit operator T[](FixedArray arr) => arr.ToArray(); - - /// - public bool SequenceEqual(T[] arr) - { - if (arr.Length < _length) - return false; - - for (int i = 0; i < _length; i++) - { - if (!this[i].Equals(arr[i])) - return false; - } - - return true; - } - - /// - /// Convert the unmanaged data to an array - /// - /// Array created from the pointer data - public T[] ToArray() - { - T[] arr = new T[_length]; - for (int i = 0; i < _length; i++) - { - arr[i] = this[i]; - } - - return arr; - } - } -} \ No newline at end of file diff --git a/libmspack/HLP/Compressor.cs b/libmspack/HLP/Compressor.cs deleted file mode 100644 index 4b3ac8c..0000000 --- a/libmspack/HLP/Compressor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SabreTools.Compression.libmspack.HLP -{ - /// - /// TODO - /// - public class Compressor : BaseCompressor - { - /// - /// Creates a new HLP compressor - /// - public Compressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file diff --git a/libmspack/HLP/Decompressor.cs b/libmspack/HLP/Decompressor.cs deleted file mode 100644 index 91f2327..0000000 --- a/libmspack/HLP/Decompressor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SabreTools.Compression.libmspack.HLP -{ - /// - /// TODO - /// - public class Decompressor : BaseDecompressor - { - /// - /// Creates a new HLP decompressor - /// - public Decompressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file diff --git a/libmspack/KWAJ/Compressor.cs b/libmspack/KWAJ/Compressor.cs deleted file mode 100644 index 59183bd..0000000 --- a/libmspack/KWAJ/Compressor.cs +++ /dev/null @@ -1,111 +0,0 @@ -namespace SabreTools.Compression.libmspack.KWAJ -{ - /// - /// A compressor for the KWAJ file format. - /// - /// All fields are READ ONLY. - /// - public unsafe class Compressor : BaseCompressor - { - public int[] param { get; private set; } = new int[2]; - - /// - /// Creates a new KWAJ compressor - /// - public Compressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Reads an input file and creates a compressed output file in the - /// KWAJ compressed file format. The KWAJ compression format is quick - /// but gives poor compression. It is possible for the compressed output - /// file to be larger than the input file. - /// - /// - /// The name of the file to compressed. This is passed - /// passed directly to mspack_system::open() - /// - /// - /// The name of the file to write compressed data to. - /// This is passed directly to mspack_system::open(). - /// - /// - /// The length of the uncompressed file, or -1 to indicate - /// that this should be determined automatically by using - /// mspack_system::seek() on the input file. - /// - /// An error code, or MSPACK_ERR_OK if successful - /// - public MSPACK_ERR compress(in string input, in string output, long length) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Sets an KWAJ compression engine parameter. - /// - /// The following parameters are defined: - /// - /// - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must - /// be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD - /// or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH. - /// - /// - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed - /// output file should include the uncompressed length of the input - /// file in the header? This adds 4 bytes to the size of the output - /// file. A value of zero says "no", non-zero says "yes". The default - /// is "no". - /// - /// The parameter to set - /// The value to set the parameter to - /// - /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there - /// is a problem with either parameter or value. - /// - /// - public MSPACK_ERR set_param(MSKWAJC_PARAM param, int value) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Sets the original filename of the file before compression, - /// which will be stored in the header of the output file. - /// - /// The filename should be a null-terminated string, it must be an - /// MS-DOS "8.3" type filename (up to 8 bytes for the filename, then - /// optionally a "." and up to 3 bytes for a filename extension). - /// - /// If null is passed as the filename, no filename is included in the - /// header. This is the default. - /// - /// The original filename to use - /// - /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the - /// filename is too long - /// - public MSPACK_ERR set_filename(in string filename) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Sets arbitrary data that will be stored in the header of the - /// output file, uncompressed. It can be up to roughly 64 kilobytes, - /// as the overall size of the header must not exceed 65535 bytes. - /// The data can contain null bytes if desired. - /// - /// If null is passed as the data pointer, or zero is passed as the - /// length, no extra data is included in the header. This is the - /// default. - /// - /// A pointer to the data to be stored in the header - /// the length of the data in bytes - /// - /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data - /// is too long - /// - public MSPACK_ERR set_extra_data(void* data, int bytes) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Returns the error code set by the most recently called method. - /// - /// The most recent error code - /// - public MSPACK_ERR last_error() => MSPACK_ERR.MSPACK_ERR_OK; - } -} \ No newline at end of file diff --git a/libmspack/KWAJ/Constants.cs b/libmspack/KWAJ/Constants.cs deleted file mode 100644 index 0dd210e..0000000 --- a/libmspack/KWAJ/Constants.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace SabreTools.Compression.libmspack.KWAJ -{ - public static class Constants - { - public const byte kwajh_Signature1 = 0x00; - public const byte kwajh_Signature2 = 0x04; - public const byte kwajh_CompMethod = 0x08; - public const byte kwajh_DataOffset = 0x0a; - public const byte kwajh_Flags = 0x0c; - public const byte kwajh_SIZEOF = 0x0e; - - /// - /// Input buffer size during decompression - not worth parameterising IMHO - /// - public const int KWAJ_INPUT_SIZE = 2048; - - /// - /// Huffman codes that are 9 bits or less are decoded immediately - /// - public const int KWAJ_TABLEBITS = 9; - - // Number of codes in each huffman table - public const int KWAJ_MATCHLEN1_SYMS = 16; - public const int KWAJ_MATCHLEN2_SYMS = 16; - public const int KWAJ_LITLEN_SYMS = 32; - public const int KWAJ_OFFSET_SYMS = 64; - public const int KWAJ_LITERAL_SYMS = 256; - - // Define decoding table sizes - public const int KWAJ_TABLESIZE = 1 << KWAJ_TABLEBITS; - public const int KWAJ_MATCHLEN1_TBLSIZE = KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2); - public const int KWAJ_MATCHLEN2_TBLSIZE = KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2); - public const int KWAJ_LITLEN_TBLSIZE = KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2); - public const int KWAJ_OFFSET_TBLSIZE = KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2); - public const int KWAJ_LITERAL_TBLSIZE = KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2); - } -} \ No newline at end of file diff --git a/libmspack/KWAJ/Decompressor.cs b/libmspack/KWAJ/Decompressor.cs deleted file mode 100644 index 17972ca..0000000 --- a/libmspack/KWAJ/Decompressor.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using static SabreTools.Compression.libmspack.KWAJ.Constants; -using static SabreTools.Compression.libmspack.macros; - -namespace SabreTools.Compression.libmspack.KWAJ -{ - /// - /// A decompressor for KWAJ compressed files. - /// - /// All fields are READ ONLY. - /// - public unsafe class Decompressor : BaseDecompressor - { - /// - /// Creates a new KWAJ decompressor. - /// - public Decompressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Destroys an existing KWAJ decompressor - /// - ~Decompressor() - { - mspack_system sys = this.system; - //sys.free(this); - } - - /// - /// Opens a KWAJ file and reads the header. - /// - /// If the file opened is a valid KWAJ file, all headers will be read and - /// a mskwajd_header structure will be returned. - /// - /// In the case of an error occuring, null is returned and the error code - /// is available from last_error(). - /// - /// The filename pointer should be considered "in use" until close() is - /// called on the KWAJ file. - /// - /// - /// The filename of the KWAJ compressed file. This is - /// passed directly to mspack_system::open(). - /// - /// A pointer to a mskwajd_header structure, or null on failure - /// - public mskwajd_header Open(in string filename) - { - mspack_system sys = this.system; - - mspack_file fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - if (fh == null) - { - this.error = MSPACK_ERR.MSPACK_ERR_OPEN; - return null; - } - - mskwajd_header hdr = new mskwajd_header(); - hdr.fh = fh; - - MSPACK_ERR err; - if ((err = ReadHeaders(sys, fh, hdr)) != MSPACK_ERR.MSPACK_ERR_OK) - { - Close(hdr); - this.error = err; - return null; - } - - return hdr; - } - - /// - /// Closes a previously opened KWAJ file. - /// - /// This closes a KWAJ file and frees the mskwajd_header associated - /// with it. The KWAJ header pointer is now invalid and cannot be - /// used again. - /// - /// The KWAJ file to close - /// - public void Close(mskwajd_header kwaj) - { - if (this.system == null) - return; - - // Close the file handle associated - this.system.close(kwaj.fh); - - // Free the memory associated - //this.system.free(hdr.filename); - //this.system.free(hdr.extra); - //this.system.free(hdr); - - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Reads the headers of a KWAJ format file - /// - private MSPACK_ERR ReadHeaders(mspack_system sys, mspack_file fh, mskwajd_header hdr) - { - FixedArray buf = new FixedArray(16); - int i; - - // Read in the header - if (sys.read(fh, buf, kwajh_SIZEOF) != kwajh_SIZEOF) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // Check for "KWAJ" signature - if ((BitConverter.ToUInt32(buf, kwajh_Signature1) != 0x4A41574B) || - (BitConverter.ToUInt32(buf, kwajh_Signature2) != 0xD127F088)) - { - return MSPACK_ERR.MSPACK_ERR_SIGNATURE; - } - - // Basic header fields - hdr.comp_type = (MSKWAJ_COMP)BitConverter.ToUInt16(buf, kwajh_CompMethod); - hdr.data_offset = BitConverter.ToUInt16(buf, kwajh_DataOffset); - hdr.headers = (MSKWAJ_HDR)BitConverter.ToUInt16(buf, kwajh_Flags); - hdr.length = 0; - hdr.filename = null; - hdr.extra = null; - hdr.extra_length = 0; - - // Optional headers - - // 4 bytes: length of unpacked file - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASLENGTH)) - { - if (sys.read(fh, buf, 4) != 4) - return MSPACK_ERR.MSPACK_ERR_READ; - - hdr.length = BitConverter.ToUInt32(buf, 0); - } - - // 2 bytes: unknown purpose - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASUNKNOWN1)) - { - if (sys.read(fh, buf, 2) != 2) - return MSPACK_ERR.MSPACK_ERR_READ; - } - - // 2 bytes: length of section, then [length] bytes: unknown purpose - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASUNKNOWN2)) - { - if (sys.read(fh, buf, 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) != 0) - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Filename and extension - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR.MSKWAJ_HDR_HASFILEEXT)) - { - int len; - - // Allocate memory for maximum length filename - char* fn = (char*)sys.alloc(13); - if ((hdr.extra = fn) == null) - return MSPACK_ERR.MSPACK_ERR_NOMEMORY; - - // Copy filename if present - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASFILENAME)) - { - // Read and copy up to 9 bytes of a null terminated string - if ((len = sys.read(fh, buf, 9)) < 2) - return MSPACK_ERR.MSPACK_ERR_READ; - - for (i = 0; i < len; i++) - if ((*fn++ = (char)buf[i]) == '\0') - break; - - // If string was 9 bytes with no null terminator, reject it - if (i == 9 && buf[8] != '\0') - 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) != 0) - return MSPACK_ERR.MSPACK_ERR_SEEK; - - fn--; // Remove the null terminator - } - - // Copy extension if present - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASFILEEXT)) - { - *fn++ = '.'; - - // Read and copy up to 4 bytes of a null terminated string - if ((len = sys.read(fh, buf, 4)) < 2) - return MSPACK_ERR.MSPACK_ERR_READ; - - for (i = 0; i < len; i++) - if ((*fn++ = (char)buf[i]) == '\0') - break; - - // If string was 4 bytes with no null terminator, reject it - if (i == 4 && buf[3] != '\0') - 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) != 0) - return MSPACK_ERR.MSPACK_ERR_SEEK; - - fn--; // Remove the null terminator - } - *fn = '\0'; - } - - // 2 bytes: extra text length then [length] bytes of extra text data - if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASEXTRATEXT)) - { - if (sys.read(fh, buf, 2) != 2) - return MSPACK_ERR.MSPACK_ERR_READ; - - i = EndGetI16(buf, 0); - 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 = (ushort)i; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Extracts the compressed data from a KWAJ file. - /// - /// This decompresses the compressed KWAJ data stream and writes it to - /// an output file. - /// - /// The KWAJ file to extract data from - /// - /// The filename to write the decompressed data to. This - /// 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) - { - 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. - /// - /// This opens an KWAJ file as input, reads the header, then decompresses - /// the compressed data immediately to an output file, finally closing - /// both the input and output file. It is more convenient to use than - /// open() then extract() then close(), if you do not need to know the - /// KWAJ output size or output filename. - /// - /// - /// The filename of the input KWAJ file. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename to write the decompressed data to. This - /// 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) - { - 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. - /// - /// This is useful for open() which does not return an - /// error code directly. - /// - /// The most recent error code - /// - /// - public MSPACK_ERR LastError() - { - return this.error; - } - } -} \ No newline at end of file diff --git a/libmspack/KWAJ/kwajd_stream.cs b/libmspack/KWAJ/kwajd_stream.cs deleted file mode 100644 index 8bf8599..0000000 --- a/libmspack/KWAJ/kwajd_stream.cs +++ /dev/null @@ -1,60 +0,0 @@ -using static SabreTools.Compression.libmspack.KWAJ.Constants; -using static SabreTools.Compression.libmspack.lzss; - -namespace SabreTools.Compression.libmspack -{ - public unsafe class kwajd_stream : readbits - { - #region Huffman code lengths - - public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS]; - - public byte[] MATCHLEN2_len { get; set; } = new byte[KWAJ_MATCHLEN2_SYMS]; - - public byte[] LITLEN_len { get; set; } = new byte[KWAJ_LITLEN_SYMS]; - - public byte[] OFFSET_len { get; set; } = new byte[KWAJ_OFFSET_SYMS]; - - public byte[] LITERAL_len { get; set; } = new byte[KWAJ_LITERAL_SYMS]; - - #endregion - - #region Huffman decoding tables - - public ushort[] MATCHLEN1_table { get; set; } = new ushort[KWAJ_MATCHLEN1_TBLSIZE]; - - public ushort[] MATCHLEN2_table { get; set; } = new ushort[KWAJ_MATCHLEN2_TBLSIZE]; - - public ushort[] LITLEN_table { get; set; } = new ushort[KWAJ_LITLEN_TBLSIZE]; - - public ushort[] OFFSET_table { get; set; } = new ushort[KWAJ_OFFSET_TBLSIZE]; - - public ushort[] LITERAL_table { get; set; } = new ushort[KWAJ_LITERAL_TBLSIZE]; - - #endregion - - #region Input buffer - - public new byte[] inbuf { get; set; } = new byte[KWAJ_INPUT_SIZE]; - - #endregion - - #region History window - - public byte[] window { get; set; } = new byte[LZSS_WINDOW_SIZE]; - - #endregion - - public override void READ_BYTES() - { - if (i_ptr >= i_end) - { - if ((err = lzh_read_input(lzh))) - return err; - i_ptr = lzh.i_ptr; - i_end = lzh.i_end; - } - INJECT_BITS_MSB(*i_ptr++, 8); - } - } -} \ No newline at end of file diff --git a/libmspack/KWAJ/mskwajd_header.cs b/libmspack/KWAJ/mskwajd_header.cs deleted file mode 100644 index f87ffdf..0000000 --- a/libmspack/KWAJ/mskwajd_header.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents an KWAJ compressed file. - /// - /// All fields are READ ONLY. - /// - public unsafe class mskwajd_header - { - /// - /// The compression type - /// - public MSKWAJ_COMP comp_type { get; set; } - - /// - /// The offset in the file where the compressed data stream begins - /// - public long data_offset { get; set; } - - /// - /// Flags indicating which optional headers were included. - /// - public MSKWAJ_HDR headers { get; set; } - - /// - /// The amount of uncompressed data in the file, or 0 if not present. - /// - public long length { get; set; } - - /// - /// Output filename, or null if not present - /// - 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 char* extra { get; set; } - - /// - /// Length of extra uncompressed data in the header - /// - public ushort extra_length { get; set; } - - public mspack_file fh { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/LIT/Compressor.cs b/libmspack/LIT/Compressor.cs deleted file mode 100644 index abe76ce..0000000 --- a/libmspack/LIT/Compressor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SabreTools.Compression.libmspack.LIT -{ - /// - /// TODO - /// - public class Compressor : BaseCompressor - { - /// - /// Creates a new LIT compressor - /// - public Compressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file diff --git a/libmspack/LIT/Decompressor.cs b/libmspack/LIT/Decompressor.cs deleted file mode 100644 index e2a7b3b..0000000 --- a/libmspack/LIT/Decompressor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SabreTools.Compression.libmspack.LIT -{ - /// - /// TODO - /// - public class Decompressor : BaseDecompressor - { - /// - /// Creates a new LIT decompressor - /// - public Decompressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file diff --git a/libmspack/LZSS/lzss.cs b/libmspack/LZSS/lzss.cs deleted file mode 100644 index f6eac7c..0000000 --- a/libmspack/LZSS/lzss.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class lzss - { - public const int LZSS_WINDOW_SIZE = 4096; - public const byte LZSS_WINDOW_FILL = 0x20; - - /// - /// Decompresses an LZSS stream. - /// - /// Input bytes will be read in as necessary using the system->read() - /// function with the input file handle given. This will continue until - /// system->read() returns 0 bytes, or an error. Errors will be passed - /// out of the function as MSPACK_ERR_READ errors. Input streams should - /// convey an "end of input stream" by refusing to supply all the bytes - /// that LZSS asks for when they reach the end of the stream, rather - /// than return an error code. - /// - /// Output bytes will be passed to the system->write() function, using - /// the output file handle given. More than one call may be made to - /// system->write(). - /// - /// As EXPAND.EXE (SZDD/KWAJ), Microsoft Help and QBasic have slightly - /// different encodings for the control byte and matches, a "mode" - /// parameter is allowed, to choose the encoding. - /// - /// - /// An mspack_system structure used to read from - /// the input stream and write to the output - /// stream, also to allocate and free memory. - /// - /// An input stream with the LZSS data. - /// An output stream to write the decoded data to. - /// The number of bytes to use as an input bitstream buffer. - /// One of values - /// An error code, or MSPACK_ERR_OK if successful - public static MSPACK_ERR lzss_decompress(mspack_system system, mspack_file input, mspack_file output, int input_buffer_size, LZSS_MODE mode) => MSPACK_ERR.MSPACK_ERR_OK; - } -} \ No newline at end of file diff --git a/libmspack/LZX/lzx.cs b/libmspack/LZX/lzx.cs deleted file mode 100644 index ad2b907..0000000 --- a/libmspack/LZX/lzx.cs +++ /dev/null @@ -1,147 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class lzx - { - // Some constants defined by the LZX specification - public const int LZX_MIN_MATCH = 2; - public const int LZX_MAX_MATCH = 257; - public const int LZX_NUM_CHARS = 256; - public const int LZX_BLOCKTYPE_INVALID = 0; /* also blocktypes 4-7 invalid */ - public const int LZX_BLOCKTYPE_VERBATIM = 1; - public const int LZX_BLOCKTYPE_ALIGNED = 2; - public const int LZX_BLOCKTYPE_UNCOMPRESSED = 3; - public const int LZX_PRETREE_NUM_ELEMENTS = 20; - public const int LZX_ALIGNED_NUM_ELEMENTS = 8; /* aligned offset tree #elements */ - public const int LZX_NUM_PRIMARY_LENGTHS = 7; /* this one missing from spec! */ - public const int LZX_NUM_SECONDARY_LENGTHS = 249; /* length tree #elements */ - - // LZX huffman defines: tweak tablebits as desired - public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS; - public const int LZX_PRETREE_TABLEBITS = 6; - public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8; - public const int LZX_MAINTREE_TABLEBITS = 12; - public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1; - public const int LZX_LENGTH_TABLEBITS = 12; - public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS; - public const int LZX_ALIGNED_TABLEBITS = 7; - public const int LZX_LENTABLE_SAFETY = 64; /* table decoding overruns are allowed */ - - public const int LZX_FRAME_SIZE = 32768; /* the size of a frame in LZX */ - - /// - /// Allocates and initialises LZX decompression state for decoding an LZX - /// stream. - /// - /// This routine uses system->alloc() to allocate memory. If memory - /// allocation fails, or the parameters to this function are invalid, - /// null is returned. - /// - /// - /// An mspack_system structure used to read from - /// the input stream and write to the output - /// stream, also to allocate and free memory. - /// - /// An input stream with the LZX data. - /// An output stream to write the decoded data to. - /// - /// The size of the decoding window, which must be - /// between 15 and 21 inclusive for regular LZX - /// data, or between 17 and 25 inclusive for - /// LZX DELTA data. - /// - /// - /// The interval at which the LZX bitstream is - /// reset, in multiples of LZX frames (32678 - /// bytes), e.g. a value of 2 indicates the input - /// stream resets after every 65536 output bytes. - /// A value of 0 indicates that the bitstream never - /// resets, such as in CAB LZX streams. - /// - /// The number of bytes to use as an input bitstream buffer. - /// - /// The length in bytes of the entirely - /// decompressed output stream, if known in - /// advance. It is used to correctly perform the - /// Intel E8 transformation, which must stop 6 - /// bytes before the very end of the - /// decompressed stream. It is not otherwise used - /// or adhered to. If the full decompressed - /// length is known in advance, set it here. - /// If it is NOT known, use the value 0, and call - /// lzxd_set_output_length() once it is - /// known. If never set, 4 of the final 6 bytes - /// of the output stream may be incorrect. - /// - /// - /// Should be zero for all regular LZX data, - /// non-zero for LZX DELTA encoded data. - /// - /// - /// A pointer to an initialised lzxd_stream structure, or null if - /// there was not enough memory or parameters to the function were wrong. - /// - public static lzxd_stream lzxd_init(mspack_system system, mspack_file input, mspack_file output, int window_bits, int reset_interval, int input_buffer_size, long output_length, char is_delta) => null; - - /// - /// See description of output_length in lzxd_init() - /// - public static void lzxd_set_output_length(lzxd_stream lzx, long output_length) { } - - /// - /// Reads LZX DELTA reference data into the window and allows - /// lzxd_decompress() to reference it. - /// - /// Call this before the first call to lzxd_decompress(). - /// - /// The LZX stream to apply this reference data to - /// - /// An mspack_system implementation to use with the - /// input param. Only read() will be called. - /// - /// - /// An input file handle to read reference data using - /// system->read(). - /// - /// - /// The length of the reference data. Cannot be longer - /// than the LZX window size. - /// - /// An error code, or MSPACK_ERR_OK if successful - public static MSPACK_ERR lzxd_set_reference_data(lzxd_stream lzx, mspack_system system, mspack_file input, uint length) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Decompresses entire or partial LZX streams. - /// - /// The number of bytes of data that should be decompressed is given as the - /// out_bytes parameter. If more bytes are decoded than are needed, they - /// will be kept over for a later invocation. - /// - /// The output bytes will be passed to the system->write() function given in - /// lzxd_init(), using the output file handle given in lzxd_init(). More than - /// one call may be made to system->write(). - /// - /// Input bytes will be read in as necessary using the system->read() - /// function given in lzxd_init(), using the input file handle given in - /// lzxd_init(). This will continue until system->read() returns 0 bytes, - /// or an error. Errors will be passed out of the function as - /// MSPACK_ERR_READ errors. Input streams should convey an "end of input - /// stream" by refusing to supply all the bytes that LZX asks for when they - /// reach the end of the stream, rather than return an error code. - /// - /// If any error code other than MSPACK_ERR_OK is returned, the stream - /// should be considered unusable and lzxd_decompress() should not be - /// called again on this stream. - /// - /// LZX decompression state, as allocated by lzxd_init(). - /// The number of bytes of data to decompress. - /// An error code, or MSPACK_ERR_OK if successful - public static MSPACK_ERR lzxd_decompress(lzxd_stream lzx, long out_bytes) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Frees all state associated with an LZX data stream. This will call - /// system->free() using the system pointer given in lzxd_init(). - /// - /// LZX decompression state to free. - public static void lzxd_free(lzxd_stream lzx) { } - } -} \ No newline at end of file diff --git a/libmspack/LZX/lzxd_stream.cs b/libmspack/LZX/lzxd_stream.cs deleted file mode 100644 index 1b1e972..0000000 --- a/libmspack/LZX/lzxd_stream.cs +++ /dev/null @@ -1,148 +0,0 @@ -using static SabreTools.Compression.libmspack.lzx; - -namespace SabreTools.Compression.libmspack -{ - public unsafe class lzxd_stream : readbits - { - /// - /// Number of bytes actually output - /// - public long offset { get; set; } - - /// - /// Overall decompressed length of stream - /// - public long length { get; set; } - - /// - /// Decoding window - /// - public byte* window { get; set; } - - /// - /// Window size - /// - public uint window_size { get; set; } - - /// - /// LZX DELTA reference data size - /// - public uint ref_data_size { get; set; } - - /// - /// Number of match_offset entries in table - /// - public uint num_offsets { get; set; } - - /// - /// Current frame offset within in window - /// - public uint frame_posn { get; set; } - - /// - /// The number of 32kb frames processed - /// - public uint frame { get; set; } - - /// - /// Which frame do we reset the compressor? - /// - public uint reset_interval { get; set; } - - /// - /// For the LRU offset system - /// - public uint R0 { get; set; } - - /// - /// For the LRU offset system - /// - public uint R1 { get; set; } - - /// - /// For the LRU offset system - /// - public uint R2 { get; set; } - - /// - /// Uncompressed length of this LZX block - /// - public uint block_length { get; set; } - - /// - /// Uncompressed bytes still left to decode - /// - public uint block_remaining { get; set; } - - /// - /// Magic header value used for transform - /// - public int intel_filesize { get; set; } - - /// - /// Has intel E8 decoding started? - /// - public byte intel_started { get; set; } - - /// - /// Type of the current block - /// - public byte block_type { get; set; } - - /// - /// Have we started decoding at all yet? - /// - public byte header_read { get; set; } - - /// - /// Have we reached the end of input? - /// - public new byte input_end { get; set; } - - /// - /// Does stream follow LZX DELTA spec? - /// - public byte is_delta { get; set; } - - #region Huffman code lengths - - public byte[] PRETREE_len { get; set; } = new byte[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - public byte[] MAINTREE_len { get; set; } = new byte[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - public byte[] LENGTH_len { get; set; } = new byte[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - public byte[] ALIGNED_len { get; set; } = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - #endregion - - #region Huffman decoding tables - - public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS * 2)]; - - public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS * 2)]; - - public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS * 2)]; - - public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS * 2)]; - - public byte LENGTH_empty { get; set; } - - #endregion - - /// - /// This is used purely for doing the intel E8 transform - /// - public byte[] e8_buf { get; set; } = new byte[LZX_FRAME_SIZE]; - - public override void READ_BYTES() - { - byte b0, b1; - READ_IF_NEEDED(ref i_ptr, ref i_end); - b0 = *i_ptr++; - READ_IF_NEEDED(ref i_ptr, ref i_end); - b1 = *i_ptr++; - INJECT_BITS_MSB((b1 << 8) | b0, 16); - } - } -} \ No newline at end of file diff --git a/libmspack/MSZIP/mszip.cs b/libmspack/MSZIP/mszip.cs deleted file mode 100644 index 8182b59..0000000 --- a/libmspack/MSZIP/mszip.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class mszip - { - /// - /// Size of LZ history window - /// - public const int MSZIP_FRAME_SIZE = 32768; - - /// - /// Literal/length huffman tree - /// - public const int MSZIP_LITERAL_MAXSYMBOLS = 288; - - public const int MSZIP_LITERAL_TABLEBITS = 9; - - /// - /// Distance huffman tree - /// - public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; - - public const int MSZIP_DISTANCE_TABLEBITS = 6; - - // If there are less direct lookup entries than symbols, the longer - // code pointers will be <= maxsymbols. This must not happen, or we - // will decode entries badly - public const int MSZIP_LITERAL_TABLESIZE = MSZIP_LITERAL_MAXSYMBOLS * 4; - public const int MSZIP_DISTANCE_TABLESIZE = 1 << MSZIP_DISTANCE_TABLEBITS + (MSZIP_DISTANCE_MAXSYMBOLS * 2); - - /// - /// Allocates MS-ZIP decompression stream for decoding the given stream. - /// - /// - uses system->alloc() to allocate memory - /// - /// - returns null if not enough memory - /// - /// - input_buffer_size is how many bytes to use as an input bitstream buffer - /// - /// - if repair_mode is non-zero, errors in decompression will be skipped - /// and 'holes' left will be filled with zero bytes. This allows at least - /// a partial recovery of erroneous data. - /// - /// - /// - /// - /// - /// - /// - public static mszipd_stream mszipd_init(mspack_system system, mspack_file input, mspack_file output, int input_buffer_size, int repair_mode) => null; - - /// - /// Decompresses, or decompresses more of, an MS-ZIP stream. - /// - /// - out_bytes of data will be decompressed and the function will return - /// with an MSPACK_ERR_OK return code. - /// - /// - decompressing will stop as soon as out_bytes is reached. if the true - /// amount of bytes decoded spills over that amount, they will be kept for - /// a later invocation of mszipd_decompress(). - /// - /// - the output bytes will be passed to the system->write() function given in - /// mszipd_init(), using the output file handle given in mszipd_init(). More - /// than one call may be made to system->write() - /// - /// - MS-ZIP will read input bytes as necessary using the system->read() - /// function given in mszipd_init(), using the input file handle given in - /// mszipd_init(). This will continue until system->read() returns 0 bytes, - /// or an error. - /// - /// - /// - /// - public static MSPACK_ERR mszipd_decompress(mszipd_stream zip, long out_bytes) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much - /// like mszipd_decompress(), but doesn't take an out_bytes parameter - /// - /// - /// - public static MSPACK_ERR mszipd_decompress_kwaj(mszipd_stream zip) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Frees all stream associated with an MS-ZIP data stream - /// - /// - calls system->free() using the system pointer given in mszipd_init() - /// - /// - public static void mszipd_free(mszipd_stream zip) { } - } -} \ No newline at end of file diff --git a/libmspack/MSZIP/mszipd_stream.cs b/libmspack/MSZIP/mszipd_stream.cs deleted file mode 100644 index 1f01490..0000000 --- a/libmspack/MSZIP/mszipd_stream.cs +++ /dev/null @@ -1,46 +0,0 @@ -using static SabreTools.Compression.libmspack.lzx; -using static SabreTools.Compression.libmspack.mszip; - -namespace SabreTools.Compression.libmspack -{ - public unsafe class mszipd_stream : readbits - { - /// - /// inflate() will call this whenever the window should be emptied. - /// - /// - /// - public int flush_window(uint val) => 0; - - public int repair_mode { get; set; } - - public int bytes_output { get; set; } - - #region Huffman code lengths - - public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - public byte[] DISTANCE_len { get; set; } = new byte[MSZIP_DISTANCE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; - - #endregion - - #region Huffman decoding tables - - public ushort[] LITERAL_table { get; set; } = new ushort[(1 << MSZIP_LITERAL_TABLESIZE) + (LZX_PRETREE_MAXSYMBOLS * 2)]; - - public ushort[] DISTANCE_table { get; set; } = new ushort[(1 << MSZIP_DISTANCE_TABLESIZE) + (LZX_MAINTREE_MAXSYMBOLS * 2)]; - - #endregion - - /// - /// 32kb history window - /// - public byte[] window { get; set; } = new byte[MSZIP_FRAME_SIZE]; - - public override void READ_BYTES() - { - READ_IF_NEEDED; - INJECT_BITS_LSB(*i_ptr++, 8); - } - } -} \ No newline at end of file diff --git a/libmspack/None/DecompressState.cs b/libmspack/None/DecompressState.cs deleted file mode 100644 index fde6e1b..0000000 --- a/libmspack/None/DecompressState.cs +++ /dev/null @@ -1,47 +0,0 @@ -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, run) != run) return MSPACK_ERR.MSPACK_ERR_READ; - if (s.InternalSystem.write(s.Output, s.Buffer, 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 deleted file mode 100644 index 1b87b68..0000000 --- a/libmspack/None/State.cs +++ /dev/null @@ -1,34 +0,0 @@ -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 FixedArray 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 = new FixedArray(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/OAB/Compressor.cs b/libmspack/OAB/Compressor.cs deleted file mode 100644 index 64d3418..0000000 --- a/libmspack/OAB/Compressor.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; - -namespace SabreTools.Compression.libmspack.OAB -{ - /// - /// A compressor for the Offline Address Book (OAB) format. - /// - /// All fields are READ ONLY. - /// - public class Compressor : BaseCompressor - { - /// - /// Creates a new OAB compressor - /// - public Compressor() - { - throw new NotImplementedException(); - } - - /// - /// Destroys an existing OAB compressor - /// - ~Compressor() - { - throw new NotImplementedException(); - } - - /// - /// Compress a full OAB file. - /// - /// The input file will be read and the compressed contents written to the - /// output file. - /// - /// - /// The filename of the input file. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename of the output file. This is passed - /// directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR compress(in string input, in string output) => throw new NotImplementedException(); - - /// - /// Generate a compressed incremental OAB patch file. - /// - /// The two uncompressed files "input" and "base" will be read, and an - /// incremental patch to generate "input" from "base" will be written to - /// the output file. - /// - /// - /// The filename of the input file containing the new - /// version of its contents. This is passed directly - /// to mspack_system::open(). - /// - /// - /// The filename of the original base file containing - /// the old version of its contents, against which the - /// incremental patch shall generated. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename of the output file. This is passed - /// directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR compress_incremental(in string input, in string @base, in string output) => throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/libmspack/OAB/Decompressor.cs b/libmspack/OAB/Decompressor.cs deleted file mode 100644 index 5f0e879..0000000 --- a/libmspack/OAB/Decompressor.cs +++ /dev/null @@ -1,400 +0,0 @@ -using static SabreTools.Compression.libmspack.macros; -using static SabreTools.Compression.libmspack.oab; - -namespace SabreTools.Compression.libmspack.OAB -{ - /// - /// A decompressor for .LZX (Offline Address Book) files - /// - /// All fields are READ ONLY. - /// - /// - /// - public unsafe class Decompressor : BaseDecompressor - { - public int buf_size { get; private set; } - - /// - /// Creates a new OAB decompressor - /// - public Decompressor() - { - this.system = new OABSystem(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - this.buf_size = 4096; - } - - /// - /// Decompresses a full Offline Address Book file. - /// - /// If the input file is a valid compressed Offline Address Book file, - /// it will be read and the decompressed contents will be written to - /// the output file. - /// - /// - /// The filename of the input file. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename of the output file. This is passed - /// directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful - public MSPACK_ERR decompress(in string input, in string output) - { - mspack_system sys; - mspack_file infh = null; - mspack_file outfh = null; - FixedArray buf = null; - FixedArray hdrbuf = new FixedArray(oabhead_SIZEOF); - uint block_max, target_size; - lzxd_stream lzx = null; - OABSystem oabd_sys; - uint window_bits; - MSPACK_ERR ret = MSPACK_ERR.MSPACK_ERR_OK; - - sys = this.system; - - infh = sys.open(input, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - if (infh == null) - { - ret = MSPACK_ERR.MSPACK_ERR_OPEN; - goto outlbl; - } - - if (sys.read(infh, hdrbuf, oabhead_SIZEOF) != oabhead_SIZEOF) - { - ret = MSPACK_ERR.MSPACK_ERR_READ; - goto outlbl; - } - - if (EndGetI32(hdrbuf, oabhead_VersionHi) != 3 || - EndGetI32(hdrbuf, oabhead_VersionLo) != 1) - { - ret = MSPACK_ERR.MSPACK_ERR_SIGNATURE; - goto outlbl; - } - - block_max = EndGetI32(hdrbuf, oabhead_BlockMax); - target_size = EndGetI32(hdrbuf, oabhead_TargetSize); - - outfh = sys.open(output, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE); - if (outfh == null) - { - ret = MSPACK_ERR.MSPACK_ERR_OPEN; - goto outlbl; - } - - buf = new FixedArray(this.buf_size); - if (buf == null) - { - ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - goto outlbl; - } - - oabd_sys = sys as OABSystem; - - oabd_file in_ofh = new oabd_file(); - in_ofh.orig_sys = sys; - in_ofh.orig_file = infh; - - oabd_file out_ofh = new oabd_file(); - out_ofh.orig_sys = sys; - out_ofh.orig_file = outfh; - - while (target_size > 0) - { - uint blk_csize, blk_dsize, blk_crc, blk_flags; - - if (sys.read(infh, buf, oabblk_SIZEOF) != oabblk_SIZEOF) - { - ret = MSPACK_ERR.MSPACK_ERR_READ; - goto outlbl; - } - blk_flags = EndGetI32(buf, oabblk_Flags); - blk_csize = EndGetI32(buf, oabblk_CompSize); - blk_dsize = EndGetI32(buf, oabblk_UncompSize); - blk_crc = EndGetI32(buf, oabblk_CRC); - - if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1) - { - ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - goto outlbl; - } - - if (blk_flags == 0) - { - // Uncompressed block - if (blk_dsize != blk_csize) - { - ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - goto outlbl; - } - ret = copy_fh(sys, infh, outfh, (int)blk_dsize, buf, this.buf_size); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) goto outlbl; - } - else - { - // LZX compressed block - window_bits = 17; - - while (window_bits < 25 && (1 << (int)window_bits) < blk_dsize) - window_bits++; - - in_ofh.available = (int)blk_csize; - out_ofh.crc = 0xffffffff; - - lzx = lzxd_init(&oabd_sys, (void*)&in_ofh, (void*)&out_ofh, window_bits, 0, this.buf_size, blk_dsize, 1); - if (lzx == null) - { - ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - goto outlbl; - } - - ret = lzxd_decompress(lzx, blk_dsize); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) - goto outlbl; - - lzxd_free(lzx); - lzx = null; - - // Consume any trailing padding bytes before the next block - ret = copy_fh(sys, infh, null, in_ofh.available, buf, this.buf_size); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) goto outlbl; - - if (out_ofh.crc != blk_crc) - { - ret = MSPACK_ERR.MSPACK_ERR_CHECKSUM; - goto outlbl; - } - } - target_size -= blk_dsize; - } - - outlbl: - if (lzx != null) lzxd_free(lzx); - if (outfh != null) sys.close(outfh); - if (infh != null) sys.close(infh); - sys.free(buf); - - return ret; - } - - /// - /// Decompresses an Offline Address Book with an incremental patch file. - /// - /// This requires both a full UNCOMPRESSED Offline Address Book file to - /// act as the "base", and a compressed incremental patch file as input. - /// If the input file is valid, it will be decompressed with reference to - /// the base file, and the decompressed contents will be written to the - /// output file. - /// - /// There is no way to tell what the right base file is for the given - /// incremental patch, but if you get it wrong, this will usually result - /// in incorrect data being decompressed, which will then fail a checksum - /// test. - /// - /// - /// The filename of the input file. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename of the base file to which the - /// incremental patch shall be applied. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename of the output file. This is passed - /// directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful - public MSPACK_ERR decompress_incremental(in string input, in string @base, in string output) - { - mspack_file infh = null, basefh = null, outfh = null; - lzxd_stream lzx = null; - FixedArray buf = null; - uint window_bits, window_size; - MSPACK_ERR ret = MSPACK_ERR.MSPACK_ERR_OK; - - mspack_system sys = this.system; - - infh = sys.open(input, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - if (infh == null) - { - ret = MSPACK_ERR.MSPACK_ERR_OPEN; - goto outlbl; - } - - FixedArray hdrbuf = new FixedArray(patchhead_SIZEOF); - if (sys.read(infh, hdrbuf, patchhead_SIZEOF) != patchhead_SIZEOF) - { - ret = MSPACK_ERR.MSPACK_ERR_READ; - goto outlbl; - } - - if (EndGetI32(hdrbuf, patchhead_VersionHi) != 3 || - EndGetI32(hdrbuf, patchhead_VersionLo) != 2) - { - ret = MSPACK_ERR.MSPACK_ERR_SIGNATURE; - goto outlbl; - } - - uint block_max = EndGetI32(hdrbuf, patchhead_BlockMax); - uint target_size = EndGetI32(hdrbuf, patchhead_TargetSize); - - // We use it for reading block headers too - if (block_max < patchblk_SIZEOF) - block_max = patchblk_SIZEOF; - - basefh = sys.open(@base, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - if (basefh == null) - { - ret = MSPACK_ERR.MSPACK_ERR_OPEN; - goto outlbl; - } - - outfh = sys.open(output, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE); - if (outfh == null) - { - ret = MSPACK_ERR.MSPACK_ERR_OPEN; - goto outlbl; - } - - buf = new FixedArray(this.buf_size); - if (buf == null) - { - ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - goto outlbl; - } - - OABSystem oabd_sys = sys as OABSystem; - - oabd_file in_ofh = new oabd_file(); - in_ofh.orig_sys = sys; - in_ofh.orig_file = infh; - - oabd_file out_ofh = new oabd_file(); - out_ofh.orig_sys = sys; - out_ofh.orig_file = outfh; - - while (target_size > 0) - { - if (sys.read(infh, buf, patchblk_SIZEOF) != patchblk_SIZEOF) - { - ret = MSPACK_ERR.MSPACK_ERR_READ; - goto outlbl; - } - - uint blk_csize = EndGetI32(buf, patchblk_PatchSize); - uint blk_dsize = EndGetI32(buf, patchblk_TargetSize); - uint blk_ssize = EndGetI32(buf, patchblk_SourceSize); - uint blk_crc = EndGetI32(buf, patchblk_CRC); - - if (blk_dsize > block_max || blk_dsize > target_size || - blk_ssize > block_max) - { - ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - goto outlbl; - } - - - window_size = (uint)((blk_ssize + 32767) & ~32767); - window_size += blk_dsize; - window_bits = 17; - - while (window_bits < 25 && (1 << (int)window_bits) < window_size) - window_bits++; - - in_ofh.available = (int)blk_csize; - out_ofh.crc = 0xffffffff; - - lzx = lzxd_init(&oabd_sys, (void*)&in_ofh, (void*)&out_ofh, window_bits, 0, 4096, blk_dsize, 1); - if (lzx == null) - { - ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY; - goto outlbl; - } - ret = lzxd_set_reference_data(lzx, sys, basefh, blk_ssize); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) - goto outlbl; - - ret = lzxd_decompress(lzx, blk_dsize); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) - goto outlbl; - - lzxd_free(lzx); - lzx = null; - - // Consume any trailing padding bytes before the next block - ret = copy_fh(sys, infh, null, in_ofh.available, buf, this.buf_size); - if (ret != MSPACK_ERR.MSPACK_ERR_OK) goto outlbl; - - if (out_ofh.crc != blk_crc) - { - ret = MSPACK_ERR.MSPACK_ERR_CHECKSUM; - goto outlbl; - } - - target_size -= blk_dsize; - } - - outlbl: - if (lzx != null) lzxd_free(lzx); - if (outfh != null) sys.close(outfh); - if (basefh != null) sys.close(basefh); - if (infh != null) sys.close(infh); - //if (buf != null) sys.free(buf); - - return ret; - } - - private static MSPACK_ERR copy_fh(mspack_system sys, mspack_file infh, mspack_file outfh, int bytes_to_copy, byte* buf, int buf_size) - { - while (bytes_to_copy > 0) - { - int run = buf_size; - if (run > bytes_to_copy) - { - run = bytes_to_copy; - } - if (sys.read(infh, buf, run) != run) - { - return MSPACK_ERR.MSPACK_ERR_READ; - } - if (outfh != null && sys.write(outfh, buf, run) != run) - { - return MSPACK_ERR.MSPACK_ERR_WRITE; - } - bytes_to_copy -= run; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Sets an OAB decompression engine parameter. Available only in OAB - /// decompressor version 2 and above. - /// - /// - #MSOABD_PARAM_DECOMPBUF: How many bytes should be used as an input - /// buffer by decompressors? The minimum value is 16. The default value - /// is 4096. - /// - /// The parameter to set - /// The value to set the parameter to - /// - /// MSPACK_ERR.MSPACK_ERR_OK if all is OK, or MSPACK_ERR.MSPACK_ERR_ARGS if there - /// is a problem with either parameter or value. - /// - public MSPACK_ERR set_param(MSOABD_PARAM param, int value) - { - if (param == MSOABD_PARAM.MSOABD_PARAM_DECOMPBUF && value >= 16) - { - // Must be at least 16 bytes (patchblk_SIZEOF, oabblk_SIZEOF) - this.buf_size = value; - return MSPACK_ERR.MSPACK_ERR_OK; - } - - return MSPACK_ERR.MSPACK_ERR_ARGS; - } - } -} \ No newline at end of file diff --git a/libmspack/OAB/OABSystem.cs b/libmspack/OAB/OABSystem.cs deleted file mode 100644 index 8fe0678..0000000 --- a/libmspack/OAB/OABSystem.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace SabreTools.Compression.libmspack.OAB -{ - public unsafe class OABSystem : mspack_default_system - { - /// - public override unsafe int read(mspack_file base_file, void* buf, int size) - { - oabd_file file = (oabd_file)base_file; - int bytes_read; - - if (size > file.available) - size = file.available; - - bytes_read = file.orig_sys.read(file.orig_file, buf, size); - if (bytes_read < 0) - return bytes_read; - - file.available -= bytes_read; - return bytes_read; - } - - /// - public override unsafe int write(mspack_file base_file, void* buf, int size) - { - oabd_file file = (oabd_file)base_file; - int bytes_written = file.orig_sys.write(file.orig_file, buf, size); - - if (bytes_written > 0) - file.crc = mspack.crc32(file.crc, buf, bytes_written); - - return bytes_written; - } - } -} \ No newline at end of file diff --git a/libmspack/OAB/oab.cs b/libmspack/OAB/oab.cs deleted file mode 100644 index f868a43..0000000 --- a/libmspack/OAB/oab.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class oab - { - public const int oabhead_VersionHi = 0x0000; - public const int oabhead_VersionLo = 0x0004; - public const int oabhead_BlockMax = 0x0008; - public const int oabhead_TargetSize = 0x000c; - public const int oabhead_SIZEOF = 0x0010; - - public const int oabblk_Flags = 0x0000; - public const int oabblk_CompSize = 0x0004; - public const int oabblk_UncompSize = 0x0008; - public const int oabblk_CRC = 0x000c; - public const int oabblk_SIZEOF = 0x0010; - - public const int patchhead_VersionHi = 0x0000; - public const int patchhead_VersionLo = 0x0004; - public const int patchhead_BlockMax = 0x0008; - public const int patchhead_SourceSize = 0x000c; - public const int patchhead_TargetSize = 0x0010; - public const int patchhead_SourceCRC = 0x0014; - public const int patchhead_TargetCRC = 0x0018; - public const int patchhead_SIZEOF = 0x001c; - - public const int patchblk_PatchSize = 0x0000; - public const int patchblk_TargetSize = 0x0004; - public const int patchblk_SourceSize = 0x0008; - public const int patchblk_CRC = 0x000c; - public const int patchblk_SIZEOF = 0x0010; - } -} \ No newline at end of file diff --git a/libmspack/OAB/oabd_file.cs b/libmspack/OAB/oabd_file.cs deleted file mode 100644 index 4a838b9..0000000 --- a/libmspack/OAB/oabd_file.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public class oabd_file : mspack_file - { - public mspack_system orig_sys { get; set; } - - public mspack_file orig_file { get; set; } - - public uint crc { get; set; } - - public int available { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/Quantum/qtm.cs b/libmspack/Quantum/qtm.cs deleted file mode 100644 index 4bf9c7e..0000000 --- a/libmspack/Quantum/qtm.cs +++ /dev/null @@ -1,241 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public unsafe static class qtm - { - public const int QTM_FRAME_SIZE = 32768; - - #region Quantum static data tables - - /* - * Quantum uses 'position slots' to represent match offsets. For every - * match, a small 'position slot' number and a small offset from that slot - * are encoded instead of one large offset. - * - * position_base[] is an index to the position slot bases - * - * extra_bits[] states how many bits of offset-from-base data is needed. - * - * length_base[] and length_extra[] are equivalent in function, but are - * used for encoding selector 6 (variable length match) match lengths, - * instead of match offsets. - * - * They are generated with the following code: - * unsigned int i, offset; - * for (i = 0, offset = 0; i < 42; i++) { - * position_base[i] = offset; - * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; - * offset += 1 << extra_bits[i]; - * } - * for (i = 0, offset = 0; i < 26; i++) { - * length_base[i] = offset; - * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; - * offset += 1 << length_extra[i]; - * } - * length_base[26] = 254; length_extra[26] = 0; - */ - - private static readonly uint[] position_base = new uint[42] - { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, - 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, - 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864 - }; - private static readonly byte[] extra_bits = new byte[42] - { - 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 - }; - - private static readonly byte[] length_base = new byte[27] - { - 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, - 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254 - }; - - private static readonly byte[] length_extra = new byte[27] - { - 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 - }; - - #endregion - - private static void qtmd_update_model(qtmd_model model) - { - qtmd_modelsym tmp; - int i, j; - - if (--model.shiftsleft > 0) - { - for (i = model.entries - 1; i >= 0; i--) - { - /* -1, not -2; the 0 entry saves this */ - model.syms[i].cumfreq >>= 1; - if (model.syms[i].cumfreq <= model.syms[i + 1].cumfreq) - { - model.syms[i].cumfreq = (ushort)(model.syms[i + 1].cumfreq + 1); - } - } - } - else - { - model.shiftsleft = 50; - for (i = 0; i < model.entries; i++) - { - /* no -1, want to include the 0 entry */ - /* this converts cumfreqs into frequencies, then shifts right */ - model.syms[i].cumfreq -= model.syms[i + 1].cumfreq; - model.syms[i].cumfreq++; /* avoid losing things entirely */ - model.syms[i].cumfreq >>= 1; - } - - /* now sort by frequencies, decreasing order -- this must be an - * inplace selection sort, or a sort with the same (in)stability - * characteristics */ - for (i = 0; i < model.entries - 1; i++) - { - for (j = i + 1; j < model.entries; j++) - { - if (model.syms[i].cumfreq < model.syms[j].cumfreq) - { - tmp = model.syms[i]; - model.syms[i] = model.syms[j]; - model.syms[j] = tmp; - } - } - } - - /* then convert frequencies back to cumfreq */ - for (i = model.entries - 1; i >= 0; i--) - { - model.syms[i].cumfreq += model.syms[i + 1].cumfreq; - } - } - } - - /// - /// Allocates Quantum decompression state for decoding the given stream. - /// - /// - returns null if window_bits is outwith the range 10 to 21 (inclusive). - /// - /// - uses system.alloc() to allocate memory - /// - /// - returns null if not enough memory - /// - /// - window_bits is the size of the Quantum window, from 1Kb (10) to 2Mb (21). - /// - /// - input_buffer_size is the number of bytes to use to store bitstream data. - /// - /// - /// - /// - /// - /// - /// - public static qtmd_stream qtmd_init(mspack_system system, mspack_file input, mspack_file output, int window_bits, int input_buffer_size) - { - uint window_size = (uint)(1 << window_bits); - int i; - - if (system == null) return null; - - // Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) - if (window_bits < 10 || window_bits > 21) return null; - - // Round up input buffer size to multiple of two - input_buffer_size = (input_buffer_size + 1) & -2; - if (input_buffer_size < 2) return null; - - // Allocate decompression state - qtmd_stream qtm = new qtmd_stream(); - - // Allocate decompression window and input buffer - qtm.window = (byte*)system.alloc((int)window_size); - qtm.inbuf = (byte*)system.alloc((int)input_buffer_size); - if (qtm.window == null || qtm.inbuf == null) - { - system.free(qtm.window); - system.free(qtm.inbuf); - //system.free(qtm); - return null; - } - - // Initialise decompression state - qtm.sys = system; - qtm.input = input; - qtm.output = output; - qtm.inbuf_size = (uint)input_buffer_size; - qtm.window_size = window_size; - qtm.window_posn = 0; - qtm.frame_todo = QTM_FRAME_SIZE; - qtm.header_read = 0; - qtm.error = MSPACK_ERR.MSPACK_ERR_OK; - - qtm.i_ptr = qtm.i_end = &qtm.inbuf[0]; - qtm.o_ptr = qtm.o_end = &qtm.window[0]; - qtm.input_end = 0; - qtm.bits_left = 0; - qtm.bit_buffer = 0; - - // Initialise arithmetic coding models - // - model 4 depends on window size, ranges from 20 to 24 - // - model 5 depends on window size, ranges from 20 to 36 - // - model 6pos depends on window size, ranges from 20 to 42 - i = window_bits * 2; - qtm.model0 = new qtmd_model(qtm.m0sym, 0, 64); - qtm.model1 = new qtmd_model(qtm.m1sym, 64, 64); - qtm.model2 = new qtmd_model(qtm.m2sym, 128, 64); - qtm.model3 = new qtmd_model(qtm.m3sym, 192, 64); - qtm.model4 = new qtmd_model(qtm.m4sym, 0, (i > 24) ? 24 : i); - qtm.model5 = new qtmd_model(qtm.m5sym, 0, (i > 36) ? 36 : i); - qtm.model6 = new qtmd_model(qtm.m6sym, 0, i); - qtm.model6len = new qtmd_model(qtm.m6lsym, 0, 27); - qtm.model7 = new qtmd_model(qtm.m7sym, 0, 7); - - // All ok - return qtm; - } - - /// - /// Decompresses, or decompresses more of, a Quantum stream. - /// - /// - out_bytes of data will be decompressed and the function will return - /// with an MSPACK_ERR_OK return code. - /// - /// - decompressing will stop as soon as out_bytes is reached. if the true - /// amount of bytes decoded spills over that amount, they will be kept for - /// a later invocation of qtmd_decompress(). - /// - /// - the output bytes will be passed to the system.write() function given in - /// qtmd_init(), using the output file handle given in qtmd_init(). More - /// than one call may be made to system.write() - /// - /// - Quantum will read input bytes as necessary using the system.read() - /// function given in qtmd_init(), using the input file handle given in - /// qtmd_init(). This will continue until system.read() returns 0 bytes, - /// or an error. - /// - /// - /// - /// - public static MSPACK_ERR qtmd_decompress(qtmd_stream qtm, long out_bytes) => MSPACK_ERR.MSPACK_ERR_OK; - - /// - /// Frees all state associated with a Quantum data stream - /// - /// - calls system.free() using the system pointer given in qtmd_init() - /// - /// - public static void qtmd_free(qtmd_stream qtm) - { - mspack_system sys; - if (qtm != null) - { - sys = qtm.sys; - //sys.free(qtm.window); - //sys.free(qtm.inbuf); - //sys.free(qtm); - } - } - } -} \ No newline at end of file diff --git a/libmspack/Quantum/qtmd_model.cs b/libmspack/Quantum/qtmd_model.cs deleted file mode 100644 index b08fc7d..0000000 --- a/libmspack/Quantum/qtmd_model.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public unsafe class qtmd_model - { - public int shiftsleft { get; set; } - - public int entries { get; set; } - - public qtmd_modelsym[] syms { get; set; } - - /// - /// Initialises a model to decode symbols from [start] to [start]+[len]-1 - /// - public qtmd_model(qtmd_modelsym[] syms, int start, int len) - { - this.shiftsleft = 4; - this.entries = len; - this.syms = syms; - - for (int i = 0; i <= len; i++) - { - syms[i].sym = (ushort)(start + i); // Actual symbol - syms[i].cumfreq = (ushort)(len - i); // Current frequency of that symbol - } - } - } -} \ No newline at end of file diff --git a/libmspack/Quantum/qtmd_modelsym.cs b/libmspack/Quantum/qtmd_modelsym.cs deleted file mode 100644 index 149c610..0000000 --- a/libmspack/Quantum/qtmd_modelsym.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public class qtmd_modelsym - { - public ushort sym { get; set; } - - public ushort cumfreq { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/Quantum/qtmd_stream.cs b/libmspack/Quantum/qtmd_stream.cs deleted file mode 100644 index 2424b2a..0000000 --- a/libmspack/Quantum/qtmd_stream.cs +++ /dev/null @@ -1,126 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public unsafe class qtmd_stream : readbits - { - /// - /// Decoding window - /// - public byte* window { get; set; } - - /// - /// Window size - /// - public uint window_size { get; set; } - - /// - /// Bytes remaining for current frame - /// - public uint frame_todo { get; set; } - - /// - /// High arith coding state - /// - public ushort H { get; set; } - - /// - /// Low arith coding state - /// - public ushort L { get; set; } - - /// - /// Current arith coding state - /// - public ushort C { get; set; } - - /// - /// Have we started decoding a new frame? - /// - public byte header_read { get; set; } - - #region Models - - #region Four literal models, each representing 64 symbols - - /// - /// model0 for literals from 0 to 63 (selector = 0) - /// - public qtmd_model model0 { get; set; } - - /// - /// model1 for literals from 64 to 127 (selector = 1) - /// - public qtmd_model model1 { get; set; } - - /// - /// model2 for literals from 128 to 191 (selector = 2) - /// - public qtmd_model model2 { get; set; } - - /// - /// model3 for literals from 129 to 255 (selector = 3) - /// - public qtmd_model model3 { get; set; } - - #endregion - - #region Three match models - - /// - /// model4 for match with fixed length of 3 bytes - /// - public qtmd_model model4 { get; set; } - - /// - /// model5 for match with fixed length of 4 bytes - /// - public qtmd_model model5 { get; set; } - - /// - /// model6 for variable length match, encoded with model6len model - /// - public qtmd_model model6 { get; set; } - - public qtmd_model model6len { get; set; } - - #endregion - - /// - /// selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6) - /// - public qtmd_model model7 { get; set; } - - #endregion - - #region Symbol arrays for all models - - public qtmd_modelsym[] m0sym { get; set; } = new qtmd_modelsym[64 + 1]; - - public qtmd_modelsym[] m1sym { get; set; } = new qtmd_modelsym[64 + 1]; - - public qtmd_modelsym[] m2sym { get; set; } = new qtmd_modelsym[64 + 1]; - - public qtmd_modelsym[] m3sym { get; set; } = new qtmd_modelsym[64 + 1]; - - public qtmd_modelsym[] m4sym { get; set; } = new qtmd_modelsym[24 + 1]; - - public qtmd_modelsym[] m5sym { get; set; } = new qtmd_modelsym[36 + 1]; - - public qtmd_modelsym[] m6sym { get; set; } = new qtmd_modelsym[42 + 1]; - - public qtmd_modelsym[] m6lsym { get; set; } = new qtmd_modelsym[27 + 1]; - - public qtmd_modelsym[] m7sym { get; set; } = new qtmd_modelsym[7 + 1]; - - #endregion - - public override void READ_BYTES() - { - byte b0, b1; - READ_IF_NEEDED; - b0 = *i_ptr++; - READ_IF_NEEDED; - b1 = *i_ptr++; - INJECT_BITS_MSB((b0 << 8) | b1, 16); - } - } -} \ No newline at end of file diff --git a/libmspack/SZDD/Compressor.cs b/libmspack/SZDD/Compressor.cs deleted file mode 100644 index d8c1cd3..0000000 --- a/libmspack/SZDD/Compressor.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace SabreTools.Compression.libmspack.SZDD -{ - /// - /// A compressor for the SZDD file format. - /// - /// All fields are READ ONLY. - /// - public abstract class Compressor : BaseCompressor - { - /// - /// Creates a new SZDD compressor - /// - public Compressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Reads an input file and creates a compressed output file in the - /// SZDD compressed file format. The SZDD compression format is quick - /// but gives poor compression. It is possible for the compressed output - /// file to be larger than the input file. - /// - /// Conventionally, SZDD compressed files have the final character in - /// their filename replaced with an underscore, to show they are - /// compressed. The missing character is stored in the compressed file - /// itself. This is due to the restricted filename conventions of MS-DOS, - /// most operating systems, such as UNIX, simply append another file - /// extension to the existing filename. As mspack does not deal with - /// filenames, this is left up to you. If you wish to set the missing - /// character stored in the file header, use set_param() with the - /// #MSSZDDC_PARAM_MISSINGCHAR parameter. - /// - /// "Stream" compression (where the length of the input data is not - /// known) is not possible. The length of the input data is stored in the - /// header of the SZDD file and must therefore be known before any data - /// is compressed. Due to technical limitations of the file format, the - /// maximum size of uncompressed file that will be accepted is 2147483647 - /// bytes. - /// - /// - /// The name of the file to compressed. This is passed - /// passed directly to mspack_system::open() - /// - /// - /// The name of the file to write compressed data to. - /// This is passed directly to mspack_system::open(). - /// - /// - /// The length of the uncompressed file, or -1 to indicate - /// that this should be determined automatically by using - /// mspack_system::seek() on the input file. - /// - /// An error code, or MSPACK_ERR_OK if successful - /// - public abstract MSPACK_ERR compress(in string input, in string output, long length); - - /// - /// Sets an SZDD compression engine parameter. - /// - /// The following parameters are defined: - /// - /// - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character - /// in the uncompressed file's filename, which is traditionally replaced - /// with an underscore to show the file is compressed. Traditionally, - /// this can only be a character that is a valid part of an MS-DOS, - /// filename, but libmspack permits any character between 0x00 and 0xFF - /// to be stored. 0x00 is the default, and it represents "no character - /// stored". - /// - /// The parameter to set - /// The value to set the parameter to - /// - /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there - /// is a problem with either parameter or value. - /// - /// - public abstract MSPACK_ERR set_param(MSSZDDC_PARAM param, int value); - - /// - /// Returns the error code set by the most recently called method. - /// - /// The most recent error code - /// - public abstract MSPACK_ERR last_error(); - } -} \ No newline at end of file diff --git a/libmspack/SZDD/Constants.cs b/libmspack/SZDD/Constants.cs deleted file mode 100644 index 5679b8c..0000000 --- a/libmspack/SZDD/Constants.cs +++ /dev/null @@ -1,19 +0,0 @@ -/* This file is part of libmspack. - * (C) 2003-2004 Stuart Caie. - * - * libmspack is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - * - * For further details, see the file COPYING.LIB distributed with libmspack - */ - -namespace SabreTools.Compression.libmspack.SZDD -{ - public static class Constants - { - /// - /// Input buffer size during decompression - not worth parameterising IMHO - /// - public const int SZDD_INPUT_SIZE = 2048; - } -} \ No newline at end of file diff --git a/libmspack/SZDD/Decompressor.cs b/libmspack/SZDD/Decompressor.cs deleted file mode 100644 index a1bc5ed..0000000 --- a/libmspack/SZDD/Decompressor.cs +++ /dev/null @@ -1,231 +0,0 @@ -using static SabreTools.Compression.libmspack.macros; -using static SabreTools.Compression.libmspack.SZDD.Constants; - -namespace SabreTools.Compression.libmspack.SZDD -{ - /// - /// A decompressor for SZDD compressed files. - /// - /// All fields are READ ONLY. - /// - /// - /// - public unsafe class Decompressor : BaseDecompressor - { - /// - /// Creates a new SZDD decompressor - /// - public Decompressor() - { - this.system = new mspack_default_system(); - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Destroys an existing SZDD decompressor - /// - ~Decompressor() - { - mspack_system sys = this.system; - //sys.free(self); - } - - /// - /// Opens a SZDD file and reads the header. - /// - /// If the file opened is a valid SZDD file, all headers will be read and - /// a msszddd_header structure will be returned. - /// - /// In the case of an error occuring, null is returned and the error code - /// is available from last_error(). - /// - /// The filename pointer should be considered "in use" until close() is - /// called on the SZDD file. - /// - /// - /// The filename of the SZDD compressed file. This is - /// passed directly to mspack_system::open(). - /// - /// A pointer to a msszddd_header structure, or null on failure - /// - public msszddd_header open(in string filename) - { - mspack_system sys = this.system; - - mspack_file fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); - msszddd_header hdr = new msszddd_header(); - hdr.fh = fh; - this.error = ReadHeaders(sys, fh, hdr); - - if (this.error != MSPACK_ERR.MSPACK_ERR_OK) - { - if (fh != null) sys.close(fh); - //sys.free(hdr); - hdr = null; - } - - return hdr; - } - - /// - /// Closes a previously opened SZDD file. - /// - /// This closes a SZDD file and frees the msszddd_header associated with - /// it. - /// - /// The SZDD header pointer is now invalid and cannot be used again. - /// - /// The SZDD file to close - public void close(msszddd_header szdd) - { - if (this.system == null) return; - - // Close the file handle associated - this.system.close(szdd.fh); - - // Free the memory associated - //this.system.free(hdr); - - this.error = MSPACK_ERR.MSPACK_ERR_OK; - } - - private static readonly byte[] szdd_signature_expand = new byte[8] - { - 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33 - }; - - private static readonly byte[] szdd_signature_qbasic = new byte[8] - { - 0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1 - }; - - /// - /// Reads the headers of an SZDD format file - /// - /// - /// - /// - /// - private static MSPACK_ERR ReadHeaders(mspack_system sys, mspack_file fh, msszddd_header hdr) - { - FixedArray buf = new FixedArray(8); - - // Read and check signature - if (sys.read(fh, buf, 8) != 8) return MSPACK_ERR.MSPACK_ERR_READ; - - if (buf.SequenceEqual(szdd_signature_expand)) - { - // Common SZDD - hdr.format = MSSZDD_FMT.MSSZDD_FMT_NORMAL; - - // Read the rest of the header - if (sys.read(fh, buf, 6) != 6) return MSPACK_ERR.MSPACK_ERR_READ; - if (buf[0] != 0x41) return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; - hdr.missing_char = (char)buf[1]; - hdr.length = EndGetI32(buf, 2); - } - else if (buf.SequenceEqual(szdd_signature_qbasic)) - { - // Special QBasic SZDD - hdr.format = MSSZDD_FMT.MSSZDD_FMT_QBASIC; - if (sys.read(fh, buf, 4) != 4) return MSPACK_ERR.MSPACK_ERR_READ; - hdr.missing_char = '\0'; - hdr.length = EndGetI32(buf, 0); - } - else - { - return MSPACK_ERR.MSPACK_ERR_SIGNATURE; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - /// - /// Extracts the compressed data from a SZDD file. - /// - /// This decompresses the compressed SZDD data stream and writes it to - /// an output file. - /// - /// The SZDD file to extract data from - /// - /// The filename to write the decompressed data to. This - /// is passed directly to mspack_system::open(). - /// - /// An error code, or MSPACK_ERR_OK if successful - public MSPACK_ERR extract(msszddd_header szdd, in string filename) - { - if (szdd == null) return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; - mspack_system sys = this.system; - - mspack_file fh = szdd.fh; - - // Seek to the compressed data - long data_offset = (szdd.format == MSSZDD_FMT.MSSZDD_FMT_NORMAL) ? 14 : 12; - if (sys.seek(fh, 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; - } - - // Decompress the data - this.error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE, - szdd.format == MSSZDD_FMT.MSSZDD_FMT_NORMAL - ? LZSS_MODE.LZSS_MODE_EXPAND - : LZSS_MODE.LZSS_MODE_QBASIC); - - // Close output file - sys.close(outfh); - - return this.error; - } - - /// - /// Decompresses an SZDD file to an output file in one step. - /// - /// This opens an SZDD file as input, reads the header, then decompresses - /// the compressed data immediately to an output file, finally closing - /// both the input and output file. It is more convenient to use than - /// open() then extract() then close(), if you do not need to know the - /// SZDD output size or missing character. - /// - /// - /// The filename of the input SZDD file. This is passed - /// directly to mspack_system::open(). - /// - /// - /// The filename to write the decompressed data to. This - /// 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) - { - msszddd_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. - /// - /// This is useful for open() which does not return an - /// error code directly. - /// - /// The most recent error code - /// - /// - /// - public MSPACK_ERR last_error() - { - return this.error; - } - } -} \ No newline at end of file diff --git a/libmspack/SZDD/msszddd_header.cs b/libmspack/SZDD/msszddd_header.cs deleted file mode 100644 index 74cdb1f..0000000 --- a/libmspack/SZDD/msszddd_header.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents an SZDD compressed file. - /// - /// All fields are READ ONLY. - /// - public class msszddd_header - { - public mspack_file fh { get; set; } - - /// - /// The file format - /// - public MSSZDD_FMT format { get; set; } - - /// - /// The amount of data in the SZDD file once uncompressed. - /// - public long length { get; set; } - - /// - /// The last character in the filename, traditionally replaced with an - /// underscore to show the file is compressed. The null character is used - /// to show that this character has not been stored (e.g. because the - /// filename is not known). Generally, only characters that may appear in - /// an MS-DOS filename (except ".") are valid. - /// - public char missing_char { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/crc32.cs b/libmspack/crc32.cs deleted file mode 100644 index 7887f7d..0000000 --- a/libmspack/crc32.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or - * code or tables extracted from it, as desired without restriction. - * - * First, the polynomial itself and its table of feedback terms. The - * polynomial is - * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 - * - * Note that we take it "backwards" and put the highest-order term in - * the lowest-order bit. The X^32 term is "implied"; the LSB is the - * X^31 term, etc. The X^0 term (usually shown as "+1") results in - * the MSB being 1 - * - * Note that the usual hardware shift register implementation, which - * is what we're using (we're merely optimizing it by doing eight-bit - * chunks at a time) shifts bits into the lowest-order term. In our - * implementation, that means shifting towards the right. Why do we - * do it this way? Because the calculated CRC must be transmitted in - * order from highest-order term to lowest-order term. UARTs transmit - * characters in order from LSB to MSB. By storing the CRC this way - * we hand it to the UART in the order low-byte to high-byte; the UART - * sends each low-bit to hight-bit; and the result is transmission bit - * by bit from highest- to lowest-order term without requiring any bit - * shuffling on our part. Reception works similarly - * - * The feedback terms table consists of 256, 32-bit entries. Notes - * - * The table can be generated at runtime if desired; code to do so - * is shown later. It might not be obvious, but the feedback - * terms simply represent the results of eight shift/xor opera - * tions for all combinations of data and CRC register values - * - * The values must be right-shifted by eight bits by the "updcrc - * logic; the shift must be unsigned (bring in zeroes). On some - * hardware you could probably optimize the shift in assembler by - * using byte-swap instructions - * polynomial $edb88320 - */ - -namespace SabreTools.Compression.libmspack -{ - public unsafe static partial class mspack - { - private static readonly uint[] crc32_table = new uint[256] - { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, - 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, - 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, - 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, - 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, - 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, - 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, - 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, - 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, - 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, - 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, - 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, - 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, - 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, - 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, - 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, - 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, - 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, - 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, - 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, - 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, - 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, - 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, - 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, - 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, - 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, - 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, - 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, - 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, - 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, - 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, - 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, - 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, - 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, - 0x2d02ef8d - }; - - /// - /// Return a 32-bit CRC of the contents of the buffer. - /// - /// - /// - /// - /// - public static uint crc32(uint val, void* ss, int len) - { - byte* s = (byte*)ss; - while (--len >= 0) - { - val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); - } - - return val; - } - } -} \ No newline at end of file diff --git a/libmspack/macros.cs b/libmspack/macros.cs deleted file mode 100644 index 311a76a..0000000 --- a/libmspack/macros.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public static class macros - { - private static uint __egi32(FixedArray a, int n) - { - return (uint)((a[n + 3] << 24) | (a[n + 2] << 16) | (a[n + 1] << 8) | (a[n + 0])); - } - - public static ulong EndGetI64(FixedArray a, int n) - { - return (__egi32(a, n + 4) << 32) | __egi32(a, n + 0); - } - - public static uint EndGetI32(FixedArray a, int n) - { - return __egi32(a, n + 0); - } - - public static ushort EndGetI16(FixedArray a, int n) - { - return (ushort)((a[n + 1] << 8) | a[n + 0]); - } - - public static uint EndGetM32(FixedArray a, int n) - { - return (uint)((a[n + 0] << 24) | (a[n + 1] << 16) | (a[n + 2] << 8) | (a[n + 3])); - } - - public static ushort EndGetM16(FixedArray a, int n) - { - return (ushort)((a[n + 0] << 8) | a[n + 1]); - } - } -} \ No newline at end of file diff --git a/libmspack/mspack_default_system.cs b/libmspack/mspack_default_system.cs deleted file mode 100644 index 8d25cb2..0000000 --- a/libmspack/mspack_default_system.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace SabreTools.Compression.libmspack -{ - public class mspack_default_system : mspack_system - { - /// - public override mspack_file open(in string filename, MSPACK_SYS_OPEN mode) - { - FileMode fmode; - FileAccess faccess; - - switch (mode) - { - case MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ: fmode = FileMode.Open; faccess = FileAccess.Read; break; - case MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE: fmode = FileMode.OpenOrCreate; faccess = FileAccess.Write; break; - case MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_UPDATE: fmode = FileMode.Open; faccess = FileAccess.ReadWrite; break; - case MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_APPEND: fmode = FileMode.Append; faccess = FileAccess.ReadWrite; break; - default: return null; - } - - try - { - var fh = new mspack_file_p - { - name = filename, - fh = File.Open(filename, fmode, faccess), - }; - return fh; - } - catch - { - return null; - } - } - - /// - public override void close(mspack_file file) - { - if (file is mspack_file_p self) - self.fh?.Dispose(); - } - - /// - public override unsafe int read(mspack_file file, void* buffer, int bytes) - { - try - { - if (file is mspack_file_p self && buffer != null && bytes >= 0) - { - var ums = new UnmanagedMemoryStream((byte*)buffer, bytes); - self.fh.CopyTo(ums, bytes); - return bytes; - } - } - catch { } - - return -1; - } - - /// - public override unsafe int write(mspack_file file, void* buffer, int bytes) - { - try - { - if (file is mspack_file_p self && buffer != null && bytes >= 0) - { - var ums = new UnmanagedMemoryStream((byte*)buffer, bytes); - ums.CopyTo(self.fh, bytes); - return bytes; - } - } - catch { } - - return -1; - } - - /// - public override int seek(mspack_file file, long offset, MSPACK_SYS_SEEK mode) - { - try - { - if (file is mspack_file_p self) - { - SeekOrigin origin; - switch (mode) - { - case MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START: origin = SeekOrigin.Begin; break; - case MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR: origin = SeekOrigin.Current; break; - case MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_END: origin = SeekOrigin.End; break; - default: return -1; - } - - self.fh.Seek(offset, origin); - return 0; - } - } - catch { } - - return -1; - } - - /// - public override long tell(mspack_file file) - { - var self = file as mspack_file_p; - return self != null ? self.fh.Position : 0; - } - - /// - public override void message(mspack_file file, in string format, params string[] args) - { - if (file != null) Console.Write((file as mspack_file_p)?.name); - Console.Write(format, args); - Console.Write("\n"); - } - - /// - public override unsafe void* alloc(int bytes) - { - return (void*)new FixedArray(bytes).Pointer; - } - - /// - public override unsafe void free(void* ptr) - { - Marshal.FreeCoTaskMem((IntPtr)ptr); - } - - /// - public override unsafe void copy(void* src, void* dest, int bytes) - { - byte[] temp = new byte[bytes]; - Marshal.Copy((IntPtr)src, temp, 0, bytes); - Marshal.Copy(temp, 0, (IntPtr)dest, bytes); - } - } -} \ No newline at end of file diff --git a/libmspack/mspack_file.cs b/libmspack/mspack_file.cs deleted file mode 100644 index 10e5d43..0000000 --- a/libmspack/mspack_file.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which represents an open file handle. The contents of this - /// structure are determined by the implementation of the - /// mspack_system::open() method. - /// - public unsafe abstract class mspack_file { } -} \ No newline at end of file diff --git a/libmspack/mspack_file_p.cs b/libmspack/mspack_file_p.cs deleted file mode 100644 index 344d5a0..0000000 --- a/libmspack/mspack_file_p.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.IO; - -namespace SabreTools.Compression.libmspack -{ - public class mspack_file_p : mspack_file - { - public Stream fh { get; set; } - - public string name { get; set; } - } -} \ No newline at end of file diff --git a/libmspack/mspack_system.cs b/libmspack/mspack_system.cs deleted file mode 100644 index b8a2ed3..0000000 --- a/libmspack/mspack_system.cs +++ /dev/null @@ -1,170 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - /// - /// A structure which abstracts file I/O and memory management. - /// - /// The library always uses the mspack_system structure for interaction - /// with the file system and to allocate, free and copy all memory. It also - /// uses it to send literal messages to the library user. - /// - /// When the library is compiled normally, passing null to a compressor or - /// decompressor constructor will result in a default mspack_system being - /// used, where all methods are implemented with the standard C library. - /// - /// However, all constructors support being given a custom created - /// mspack_system structure, with the library user's own methods. This - /// allows for more abstract interaction, such as reading and writing files - /// directly to memory, or from a network socket or pipe. - /// - /// Implementors of an mspack_system structure should read all - /// documentation entries for every structure member, and write methods - /// which conform to those standards. - /// - public unsafe abstract class mspack_system - { - /// - /// Opens a file for reading, writing, appending or updating - /// - /// - /// The file to be opened. It is passed directly from the - /// library caller without being modified, so it is up to - /// the caller what this parameter actually represents. - /// - /// One of values - /// - /// A pointer to a mspack_file structure. This structure officially - /// contains no members, its true contents are up to the - /// mspack_system implementor. It should contain whatever is needed - /// for other mspack_system methods to operate. Returning the null - /// pointer indicates an error condition. - /// - /// - /// - /// - /// - /// - /// - public abstract mspack_file open(in string filename, MSPACK_SYS_OPEN mode); - - /// - /// Closes a previously opened file. If any memory was allocated for this - /// particular file handle, it should be freed at this time. - /// - /// The file to close - /// - public abstract void close(mspack_file file); - - /// - /// Reads a given number of bytes from an open file. - /// - /// The file to read from - /// The location where the read bytes should be stored - /// The number of bytes to read from the file - /// - /// The number of bytes successfully read (this can be less than - /// the number requested), zero to mark the end of file, or less - /// than zero to indicate an error. The library does not "retry" - /// reads and assumes short reads are due to EOF, so you should - /// avoid returning short reads because of transient errors. - /// - /// - /// - public abstract int read(mspack_file file, void* buffer, int bytes); - - /// - /// Writes a given number of bytes to an open file. - /// - /// The file to write to - /// The location where the written bytes should be read from - /// The number of bytes to write to the file - /// - /// The number of bytes successfully written, this can be less - /// than the number requested. Zero or less can indicate an error - /// where no bytes at all could be written. All cases where less - /// bytes were written than requested are considered by the library - /// to be an error. - /// - /// - /// - public abstract int write(mspack_file file, void* buffer, int bytes); - - /// - /// Seeks to a specific file offset within an open file. - /// - /// Sometimes the library needs to know the length of a file. It does - /// this by seeking to the end of the file with seek(file, 0, - /// MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want - /// to make a special case for this. - /// - /// Due to the potentially varying 32/64 bit datatype off_t on some - /// architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before - /// using the library. If not, the error caused by the library passing an - /// inappropriate stackframe to seek() is subtle and hard to trace. - /// - /// The file to be seeked - /// An offset to seek, measured in bytes - /// One of values - /// Zero for success, non-zero for an error - /// - /// - public abstract int seek(mspack_file file, long offset, MSPACK_SYS_SEEK mode); - - /// - /// Returns the current file position (in bytes) of the given file. - /// - /// The file whose file position is wanted - /// The current file position of the file - /// - /// - public abstract long tell(mspack_file file); - - /// - /// Used to send messages from the library to the user. - /// - /// Occasionally, the library generates warnings or other messages in - /// plain english to inform the human user. These are informational only - /// and can be ignored if not wanted. - /// - /// - /// May be a file handle returned from open() if this message - /// pertains to a specific open file, or null if not related to - /// a specific file. - /// - /// - /// a printf() style format string. It does NOT include a - /// trailing newline. - /// - public abstract void message(mspack_file file, in string format, params string[] args); - - /// - /// Allocates memory - /// - /// The number of bytes to allocate - /// - /// A pointer to the requested number of bytes, or null if - /// not enough memory is available - /// - /// - public abstract void* alloc(int bytes); - - /// - /// Frees memory - /// - /// The memory to be freed. null is accepted and ignored. - /// - public abstract void free(void* ptr); - - /// - /// Copies from one region of memory to another. - /// - /// The regions of memory are guaranteed not to overlap, are usually less - /// than 256 bytes, and may not be aligned. Please note that the source - /// parameter comes before the destination parameter, unlike the standard - /// C function memcpy(). - /// - /// The region of memory to copy from - /// The region of memory to copy to - /// The size of the memory region, in bytes - public abstract void copy(void* src, void* dest, int bytes); - } -} \ No newline at end of file diff --git a/libmspack/readbits.cs b/libmspack/readbits.cs deleted file mode 100644 index ce14eba..0000000 --- a/libmspack/readbits.cs +++ /dev/null @@ -1,245 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - public unsafe abstract class readbits - { - /// - /// I/O routines - /// - public mspack_system sys { get; set; } - - /// - /// Input file handle - /// - public mspack_file input { get; set; } - - /// - /// Output file handle - /// - public mspack_file output { get; set; } - - /// - /// Decompression offset within window - /// - public uint window_posn { get; set; } - - #region I/O buffering - - public byte* inbuf { get; set; } - - public byte* i_ptr { get; set; } - - public byte* i_end { get; set; } - - public byte* o_ptr { get; set; } - - public byte* o_end { get; set; } - - public int input_end { get; set; } - - public uint bit_buffer { get; set; } - - public uint bits_left { get; set; } - - public uint inbuf_size { get; set; } - - #endregion - - public MSPACK_ERR error { get; set; } - - /// - #region readbits.h - - private const int BITBUF_WIDTH = 64; - - private static readonly ushort[] lsb_bit_mask = new ushort[17] - { - 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, - 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff - }; - - public void INIT_BITS() - { - this.i_ptr = inbuf; - this.i_end = inbuf; - this.bit_buffer = 0; - this.bits_left = 0; - this.input_end = 0; - } - - public void STORE_BITS(byte* i_ptr, byte* i_end, uint bit_buffer, uint bits_left) - { - this.i_ptr = i_ptr; - this.i_end = i_end; - this.bit_buffer = bit_buffer; - this.bits_left = bits_left; - } - - public void RESTORE_BITS(out byte* i_ptr, out byte* i_end, out uint bit_buffer, out uint bits_left) - { - i_ptr = this.i_ptr; - i_end = this.i_end; - bit_buffer = this.bit_buffer; - bits_left = this.bits_left; - } - - public void ENSURE_BITS(byte nbits, ref uint bits_left) - { - while (bits_left < nbits) - { - this.READ_BYTES(); - } - } - - #region MSB - - public void READ_BITS_MSB(out int val, byte nbits, ref uint bit_buffer, ref uint bits_left) - { - this.ENSURE_BITS(nbits, ref bits_left); - val = PEEK_BITS_MSB(nbits, bit_buffer); - REMOVE_BITS_MSB(nbits, ref bit_buffer, ref bits_left); - } - - public void READ_MANY_BITS_MSB(out int val, byte bits, ref uint bit_buffer, ref uint bits_left) - { - byte needed = bits; - byte bitrun; - val = 0; - while (needed > 0) - { - if (bits_left < (int)(BITBUF_WIDTH - 16)) - this.READ_BYTES(); - - bitrun = (bits_left < needed) ? (byte)bits_left : needed; - val = (val << bitrun) | PEEK_BITS_MSB(bitrun, bit_buffer); - REMOVE_BITS_MSB(bitrun, ref bit_buffer, ref bits_left); - needed -= bitrun; - } - } - - public int PEEK_BITS_MSB(byte nbits, uint bit_buffer) - { - return (int)(bit_buffer >> (BITBUF_WIDTH - nbits)); - } - - public void REMOVE_BITS_MSB(byte nbits, ref uint bit_buffer, ref uint bits_left) - { - bit_buffer <<= nbits; - bits_left -= nbits; - } - - public void INJECT_BITS_MSB(uint bitdata, byte nbits, ref uint bit_buffer, ref uint bits_left) - { - bit_buffer |= (uint)(int)(bitdata << (int)(BITBUF_WIDTH - nbits - bits_left)); - bits_left += nbits; - } - - #endregion - - #region LSB - - public void READ_BITS_LSB(out int val, byte nbits, ref uint bit_buffer, ref uint bits_left) - { - this.ENSURE_BITS(nbits, ref bits_left); - val = PEEK_BITS_LSB(nbits, bit_buffer); - REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left); - } - - public void READ_MANY_BITS_LSB(out int val, byte bits, ref uint bit_buffer, ref uint bits_left) - { - byte needed = bits; - byte bitrun; - val = 0; - while (needed > 0) - { - if (bits_left < (int)(BITBUF_WIDTH - 16)) - this.READ_BYTES(); - - bitrun = (bits_left < needed) ? (byte)bits_left : needed; - val = (val << bitrun) | PEEK_BITS_LSB(bitrun, bit_buffer); - REMOVE_BITS_LSB(bitrun, ref bit_buffer, ref bits_left); - needed -= bitrun; - } - } - - public int PEEK_BITS_LSB(byte nbits, uint bit_buffer) - { - return (int)(bit_buffer & ((uint)(1 << nbits) - 1)); - } - - public void REMOVE_BITS_LSB(byte nbits, ref uint bit_buffer, ref uint bits_left) - { - bit_buffer >>= nbits; - bits_left -= nbits; - } - - public void INJECT_BITS_LSB(uint bitdata, byte nbits, ref uint bit_buffer, ref uint bits_left) - { - bit_buffer |= bitdata << (int)bits_left; - bits_left += nbits; - } - - #endregion - - #region LSB_T - - public int PEEK_BITS_LSB_T(byte nbits, uint bit_buffer) - { - return (int)(bit_buffer & lsb_bit_mask[nbits]); - } - - public void READ_BITS_LSB_T(out int val, byte nbits, ref uint bit_buffer, ref uint bits_left) - { - this.ENSURE_BITS(nbits, ref bits_left); - val = PEEK_BITS_LSB_T(nbits, bit_buffer); - REMOVE_BITS_LSB(nbits, ref bit_buffer, ref bits_left); - } - - #endregion - - public abstract void READ_BYTES(); - - public MSPACK_ERR READ_IF_NEEDED(ref byte* i_ptr, ref byte* i_end) - { - if (i_ptr >= i_end) - { - if (ReadInput() != MSPACK_ERR.MSPACK_ERR_OK) - return this.error; - - i_ptr = this.i_ptr; - i_end = this.i_end; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - - private MSPACK_ERR ReadInput() - { - int read = this.sys.read(this.input, this.inbuf, (int)this.inbuf_size); - if (read < 0) return this.error = MSPACK_ERR.MSPACK_ERR_READ; - - /* we might overrun the input stream by asking for bits we don't use, - * so fake 2 more bytes at the end of input */ - if (read == 0) - { - if (this.input_end != 0) - { - System.Console.Error.WriteLine("Out of input bytes"); - return this.error = MSPACK_ERR.MSPACK_ERR_READ; - } - else - { - read = 2; - this.inbuf[0] = this.inbuf[1] = 0; - this.input_end = 1; - } - } - - // Update i_ptr and i_end - this.i_ptr = this.inbuf; - this.i_end = this.inbuf + read; - return MSPACK_ERR.MSPACK_ERR_OK; - } - - #endregion - } -} \ No newline at end of file diff --git a/libmspack/readhuff.cs b/libmspack/readhuff.cs deleted file mode 100644 index fb2d027..0000000 --- a/libmspack/readhuff.cs +++ /dev/null @@ -1,206 +0,0 @@ -namespace SabreTools.Compression.libmspack -{ - 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 diff --git a/libmspack/system.cs b/libmspack/system.cs deleted file mode 100644 index e272f61..0000000 --- a/libmspack/system.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* This file is part of libmspack. - * (C) 2003-2018 Stuart Caie. - * - * libmspack is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - * - * For further details, see the file COPYING.LIB distributed with libmspack - */ - -namespace SabreTools.Compression.libmspack -{ - public unsafe static class system - { - /// - /// Returns the length of a file opened for reading - /// - public static MSPACK_ERR mspack_sys_filelen(mspack_system system, mspack_file file, long* length) - { - if (system == null || file == null || length == null) return MSPACK_ERR.MSPACK_ERR_OPEN; - - // Get current offset - long current = system.tell(file); - - // Seek to end of file - if (system.seek(file, 0, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_END) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - // Get offset of end of file - *length = system.tell(file); - - // Seek back to original offset - if (system.seek(file, current, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) - { - return MSPACK_ERR.MSPACK_ERR_SEEK; - } - - return MSPACK_ERR.MSPACK_ERR_OK; - } - } -} \ No newline at end of file