diff --git a/libmspack/Compressor.cs b/libmspack/Compressor.cs
new file mode 100644
index 0000000..6c58243
--- /dev/null
+++ b/libmspack/Compressor.cs
@@ -0,0 +1,10 @@
+namespace SabreTools.Compression.libmspack
+{
+ ///
+ /// Base class for all compressor implementations
+ ///
+ public abstract class Compressor
+ {
+ public mspack_system system { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/libmspack/Decompressor.cs b/libmspack/Decompressor.cs
new file mode 100644
index 0000000..1f8c949
--- /dev/null
+++ b/libmspack/Decompressor.cs
@@ -0,0 +1,10 @@
+namespace SabreTools.Compression.libmspack
+{
+ ///
+ /// Base class for all decompressor implementations
+ ///
+ public abstract class Decompressor
+ {
+ public mspack_system system { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/libmspack/cab.cs b/libmspack/cab.cs
index a71792f..e3d91fb 100644
--- a/libmspack/cab.cs
+++ b/libmspack/cab.cs
@@ -1,6 +1,8 @@
+using System;
+
namespace SabreTools.Compression.libmspack
{
- public static class cab
+ public unsafe static class cab
{
/* structure offsets */
public const byte cfhead_Signature = 0x00;
@@ -36,13 +38,6 @@ namespace SabreTools.Compression.libmspack
/* flags */
public const ushort cffoldCOMPTYPE_MASK = 0x000f;
- public const ushort cffoldCOMPTYPE_NONE = 0x0000;
- public const ushort cffoldCOMPTYPE_MSZIP = 0x0001;
- public const ushort cffoldCOMPTYPE_QUANTUM = 0x0002;
- public const ushort cffoldCOMPTYPE_LZX = 0x0003;
- public const ushort cfheadPREV_CABINET = 0x0001;
- public const ushort cfheadNEXT_CABINET = 0x0002;
- public const ushort cfheadRESERVE_PRESENT = 0x0004;
public const ushort cffileCONTINUED_FROM_PREV = 0xFFFD;
public const ushort cffileCONTINUED_TO_NEXT = 0xFFFE;
public const ushort cffileCONTINUED_PREV_AND_NEXT = 0xFFFF;
@@ -71,5 +66,91 @@ namespace SabreTools.Compression.libmspack
*/
public const int CAB_FOLDERMAX = 65535;
public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX;
+
+ #region decomp
+
+ ///
+ /// cabd_free_decomp frees decompression state, according to which method
+ /// was used.
+ ///
+ public static MSPACK_ERR cabd_init_decomp(mscab_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 mscabd_noned_decompress_state();
+ self.d.state = noned_init(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(mscab_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_NONE: noned_free((noned_state)self.d.state); break;
+ case MSCAB_COMP.MSCAB_COMP_MSZIP: mszipd_free((mszipd_stream)self.d.state); break;
+ case MSCAB_COMP.MSCAB_COMP_QUANTUM: qtmd_free((qtmd_stream)self.d.state); break;
+ case MSCAB_COMP.MSCAB_COMP_LZX: lzxd_free((lzxd_stream)self.d.state); break;
+ }
+
+ //self.d.decompress = null;
+ self.d.state = null;
+ }
+
+ #endregion
+
+ #region noned_state
+
+ public static noned_state noned_init(mspack_system sys, mspack_file @in, mspack_file @out, int bufsize)
+ {
+ noned_state state = new noned_state();
+
+ state.sys = sys;
+ state.i = @in;
+ state.o = @out;
+ state.buf = system.CreateArray(bufsize);
+ state.bufsize = bufsize;
+ return state;
+ }
+
+ public static void noned_free(noned_state state)
+ {
+ mspack_system sys;
+ if (state != null)
+ {
+ sys = state.sys;
+ sys.free(state.buf);
+ //sys.free(state);
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/libmspack/kwajd_stream.cs b/libmspack/kwajd_stream.cs
index 73eebb6..71def9f 100644
--- a/libmspack/kwajd_stream.cs
+++ b/libmspack/kwajd_stream.cs
@@ -3,28 +3,8 @@ using static SabreTools.Compression.libmspack.lzss;
namespace SabreTools.Compression.libmspack
{
- public unsafe class kwajd_stream
+ public unsafe class kwajd_stream : readbits
{
- #region I/O buffering
-
- public mspack_system sys { get; set; }
-
- public mspack_file input { get; set; }
-
- public mspack_file output { get; set; }
-
- public byte* i_ptr { get; set; }
-
- public byte* i_end { get; set; }
-
- public uint bit_buffer { get; set; }
-
- public uint bits_left { get; set; }
-
- public int input_end { get; set; }
-
- #endregion
-
#region Huffman code lengths
public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS];
@@ -55,7 +35,7 @@ namespace SabreTools.Compression.libmspack
#region Input buffer
- public byte[] inbuf { get; set; } = new byte[KWAJ_INPUT_SIZE];
+ public new byte[] inbuf { get; set; } = new byte[KWAJ_INPUT_SIZE];
#endregion
@@ -64,5 +44,17 @@ namespace SabreTools.Compression.libmspack
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/lzx.cs b/libmspack/lzx.cs
index 3b9e0fa..ad2b907 100644
--- a/libmspack/lzx.cs
+++ b/libmspack/lzx.cs
@@ -34,7 +34,7 @@ namespace SabreTools.Compression.libmspack
///
/// This routine uses system->alloc() to allocate memory. If memory
/// allocation fails, or the parameters to this function are invalid,
- /// NULL is returned.
+ /// null is returned.
///
///
/// An mspack_system structure used to read from
@@ -77,7 +77,7 @@ namespace SabreTools.Compression.libmspack
/// non-zero for LZX DELTA encoded data.
///
///
- /// A pointer to an initialised lzxd_stream structure, or NULL if
+ /// 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;
diff --git a/libmspack/lzxd_stream.cs b/libmspack/lzxd_stream.cs
index db81352..1b1e972 100644
--- a/libmspack/lzxd_stream.cs
+++ b/libmspack/lzxd_stream.cs
@@ -2,23 +2,8 @@ using static SabreTools.Compression.libmspack.lzx;
namespace SabreTools.Compression.libmspack
{
- public unsafe class lzxd_stream
+ public unsafe class lzxd_stream : 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; }
-
///
/// Number of bytes actually output
///
@@ -49,11 +34,6 @@ namespace SabreTools.Compression.libmspack
///
public uint num_offsets { get; set; }
- ///
- /// Decompression offset within window
- ///
- public uint window_posn { get; set; }
-
///
/// Current frame offset within in window
///
@@ -117,35 +97,13 @@ namespace SabreTools.Compression.libmspack
///
/// Have we reached the end of input?
///
- public byte input_end { get; set; }
+ public new byte input_end { get; set; }
///
/// Does stream follow LZX DELTA spec?
///
public byte is_delta { get; set; }
- public MSPACK_ERR error { 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 uint bit_buffer { get; set; }
-
- public uint bits_left { get; set; }
-
- public uint inbuf_size { get; set; }
-
- #endregion
-
#region Huffman code lengths
public byte[] PRETREE_len { get; set; } = new byte[LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
@@ -176,5 +134,15 @@ namespace SabreTools.Compression.libmspack
/// 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/mscab_compressor.cs b/libmspack/mscab_compressor.cs
index f6429a6..d20c6fa 100644
--- a/libmspack/mscab_compressor.cs
+++ b/libmspack/mscab_compressor.cs
@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
///
/// TODO
///
- public class mscab_compressor
+ public class mscab_compressor : Compressor
{
public int dummy { get; set; }
-
- public mspack_system system { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/mscab_decompressor.cs b/libmspack/mscab_decompressor.cs
index 00d380e..bfc95fb 100644
--- a/libmspack/mscab_decompressor.cs
+++ b/libmspack/mscab_decompressor.cs
@@ -1,3 +1,5 @@
+using static SabreTools.Compression.libmspack.cab;
+
namespace SabreTools.Compression.libmspack
{
///
@@ -7,12 +9,10 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class mscab_decompressor
+ public unsafe class mscab_decompressor : Decompressor
{
public mscabd_decompress_state d { get; set; }
- public mspack_system system { get; set; }
-
public int buf_size { get; set; }
public int searchbuf_size { get; set; }
@@ -32,7 +32,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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
@@ -42,11 +42,37 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// A pointer to a mscabd_cabinet structure, or null on failure
///
///
///
- public abstract mscabd_cabinet open(in string filename);
+ public mscabd_cabinet open(in string filename)
+ {
+ mscabd_cabinet cab = null;
+ mspack_file fh;
+ MSPACK_ERR error;
+
+ mspack_system sys = this.system;
+ if ((fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) != null)
+ {
+ cab = new mscabd_cabinet();
+ cab.filename = filename;
+ error = cabd_read_headers(sys, fh, cab, 0, this.salvage, 0);
+ if (error != MSPACK_ERR.MSPACK_ERR_OK)
+ {
+ close(cab);
+ cab = null;
+ }
+ this.error = error;
+ sys.close(fh);
+ }
+ else
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_OPEN;
+ }
+
+ return cab;
+ }
///
/// Closes a previously opened cabinet or cabinet set.
@@ -77,7 +103,374 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract void close(mscabd_cabinet cab);
+ 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();
+ //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 cabd_read_headers(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;
+ byte[] buf = new byte[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, libmspack.system.GetArrayPointer(buf), cfhead_SIZEOF) != cfhead_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ // Check for "MSCF" signature
+ if (EndGetI32((byte*)buf[cfhead_Signature]) != 0x4643534D)
+ {
+ return MSPACK_ERR.MSPACK_ERR_SIGNATURE;
+ }
+
+ // Some basic header fields
+ cab.length = EndGetI32((byte*)buf[cfhead_CabinetSize]);
+ cab.set_id = EndGetI16((byte*)buf[cfhead_SetID]);
+ cab.set_index = EndGetI16((byte*)buf[cfhead_CabinetIndex]);
+
+ // Get the number of folders
+ num_folders = EndGetI16((byte*)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((byte*)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 = EndGetI16((byte*)buf[cfhead_Flags]);
+
+ if (cab.flags.HasFlag(MSCAB_HDR.MSCAB_HDR_RESV))
+ {
+ if (sys.read(fh, libmspack.system.GetArrayPointer(buf), cfheadext_SIZEOF) != cfheadext_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+ cab.header_resv = EndGetI16((byte*)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 = cabd_read_string(sys, fh, 0, out err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK) return err;
+ cab.previnfo = cabd_read_string(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 = cabd_read_string(sys, fh, 0, out err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK) return err;
+ cab.nextinfo = cabd_read_string(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, libmspack.system.GetArrayPointer(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 = EndGetI16((byte*)buf[cffold_CompType]);
+ fol.num_blocks = EndGetI16((byte*)buf[cffold_NumBlocks]);
+ fol.data.next = null;
+ fol.data.cab = cab;
+ fol.data.offset = offset + (long)((uint)EndGetI32((byte*)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, libmspack.system.GetArrayPointer(buf), cffile_SIZEOF) != cffile_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ file = new mscabd_file();
+
+ file.next = null;
+ file.length = EndGetI32((byte*)buf[cffile_UncompressedSize]);
+ file.attribs = EndGetI16((byte*)buf[cffile_Attribs]);
+ file.offset = EndGetI32((byte*)buf[cffile_FolderOffset]);
+
+ // Set folder pointer
+ fidx = EndGetI16((byte*)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((byte*)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((byte*)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 = cabd_read_string(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 cabd_read_string(mspack_system sys, mspack_file fh, int permit_empty, out MSPACK_ERR error)
+ {
+ long @base = sys.tell(fh);
+ byte[] buf = new byte[256];
+ string str;
+ int len, i, ok;
+
+ // Read up to 256 bytes */
+ if ((len = sys.read(fh, libmspack.system.GetArrayPointer(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;
+ }
+
+ char[] strchr = new char[len];
+ sys.copy(libmspack.system.GetArrayPointer(buf), libmspack.system.GetArrayPointer(strchr), len);
+ str = new string(strchr);
+ error = MSPACK_ERR.MSPACK_ERR_OK;
+ return str;
+ }
///
/// Searches a regular file for embedded cabinets.
@@ -94,11 +487,11 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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_OK.
+ /// 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.
@@ -110,7 +503,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// A pointer to a mscabd_cabinet structure, or null
///
///
///
@@ -121,7 +514,7 @@ namespace SabreTools.Compression.libmspack
/// set.
///
/// This will attempt to append one cabinet to another such that
- /// (cab->nextcab == nextcab) && (nextcab->prevcab == cab) and
+ /// (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
@@ -149,7 +542,7 @@ namespace SabreTools.Compression.libmspack
///
/// 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_OK if successful
+ /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
///
///
///
@@ -160,13 +553,13 @@ namespace SabreTools.Compression.libmspack
/// cabinet set.
///
/// This will attempt to prepend one cabinet to another, such that
- /// (cab->prevcab == prevcab) && (prevcab->nextcab == cab). In
+ /// (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_OK if successful
+ /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
///
///
///
@@ -190,7 +583,7 @@ namespace SabreTools.Compression.libmspack
///
/// The file to be decompressed
/// The filename of the file being written to
- /// An error code, or MSPACK_ERR_OK if successful
+ /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
public abstract MSPACK_ERR extract(mscabd_file file, in string filename);
///
@@ -210,12 +603,35 @@ namespace SabreTools.Compression.libmspack
/// The parameter to set
/// The value to set the parameter to
///
- /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
+ /// 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 abstract MSPACK_ERR set_param(MSCABD_PARAM param, int 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.
@@ -226,6 +642,9 @@ namespace SabreTools.Compression.libmspack
/// The most recent error code
///
///
- public abstract MSPACK_ERR last_error();
+ public MSPACK_ERR last_error()
+ {
+ return this.error;
+ }
}
}
\ No newline at end of file
diff --git a/libmspack/mscabd_cabinet.cs b/libmspack/mscabd_cabinet.cs
index 5806e92..dfb5588 100644
--- a/libmspack/mscabd_cabinet.cs
+++ b/libmspack/mscabd_cabinet.cs
@@ -15,7 +15,7 @@ namespace SabreTools.Compression.libmspack
{
///
/// 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
+ /// mscab_decompressor::search(). May be null to mark the end of the
/// list.
///
public mscabd_cabinet next { get; set; }
@@ -38,34 +38,34 @@ namespace SabreTools.Compression.libmspack
public uint length { get; set; }
///
- /// The previous cabinet in a cabinet set, or NULL.
+ /// The previous cabinet in a cabinet set, or null.
///
public mscabd_cabinet prevcab { get; set; }
///
- /// The next cabinet in a cabinet set, or NULL.
+ /// 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.
+ /// 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.
+ /// 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.
+ /// set, or null.
///
public string previnfo { get; set; }
///
/// The name of the disk containing the next cabinet in a cabinet set,
- /// or NULL.
+ /// or null.
///
public string nextinfo { get; set; }
diff --git a/libmspack/mscabd_decompress_state.cs b/libmspack/mscabd_decompress_state.cs
index 39e27bf..c1ebcc0 100644
--- a/libmspack/mscabd_decompress_state.cs
+++ b/libmspack/mscabd_decompress_state.cs
@@ -42,15 +42,12 @@ namespace SabreTools.Compression.libmspack
///
/// Decompressor code
///
- ///
- ///
- ///
- public abstract int decompress(void* data, long offset);
+ public abstract MSPACK_ERR decompress(object data, long offset);
///
/// Decompressor state
///
- public void* state { get; set; }
+ public object state { get; set; }
///
/// Cabinet where input data comes from
@@ -82,4 +79,25 @@ namespace SabreTools.Compression.libmspack
///
public byte[] input { get; set; } = new byte[CAB_INPUTBUF];
}
+
+ public unsafe class mscabd_noned_decompress_state : mscabd_decompress_state
+ {
+ ///
+ public override unsafe MSPACK_ERR decompress(object data, long bytes)
+ {
+ noned_state s = data as noned_state;
+ while (bytes > 0)
+ {
+ int run = (bytes > s.bufsize) ? s.bufsize : (int)bytes;
+ {
+ if (s.sys.read(s.i, &s.buf[0], run) != run) return MSPACK_ERR.MSPACK_ERR_READ;
+ if (s.sys.write(s.o, &s.buf[0], run) != run) return MSPACK_ERR.MSPACK_ERR_WRITE;
+ bytes -= run;
+ }
+ return MSPACK_ERR.MSPACK_ERR_OK;
+ }
+
+ return MSPACK_ERR.MSPACK_ERR_DECRUNCH;
+ }
+ }
}
\ No newline at end of file
diff --git a/libmspack/mscabd_file.cs b/libmspack/mscabd_file.cs
index e16aa75..57682ed 100644
--- a/libmspack/mscabd_file.cs
+++ b/libmspack/mscabd_file.cs
@@ -8,7 +8,7 @@ namespace SabreTools.Compression.libmspack
public class mscabd_file
{
///
- /// The next file in the cabinet or cabinet set, or NULL if this is the
+ /// The next file in the cabinet or cabinet set, or null if this is the
/// final file.
///
public mscabd_file next { get; set; }
diff --git a/libmspack/mscabd_folder.cs b/libmspack/mscabd_folder.cs
index 33ccdaf..31d17ff 100644
--- a/libmspack/mscabd_folder.cs
+++ b/libmspack/mscabd_folder.cs
@@ -12,7 +12,7 @@ namespace SabreTools.Compression.libmspack
public class mscabd_folder
{
///
- /// A pointer to the next folder in this cabinet or cabinet set, or NULL
+ /// 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; }
diff --git a/libmspack/mschm_compressor.cs b/libmspack/mschm_compressor.cs
index 04f8529..19d8820 100644
--- a/libmspack/mschm_compressor.cs
+++ b/libmspack/mschm_compressor.cs
@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class mschm_compressor
+ public abstract class mschm_compressor : Compressor
{
- public mspack_system system { get; set; }
-
public string temp_file { get; set; }
public int use_temp_file { get; set; }
diff --git a/libmspack/mschm_decompressor.cs b/libmspack/mschm_decompressor.cs
index 315ff74..4015593 100644
--- a/libmspack/mschm_decompressor.cs
+++ b/libmspack/mschm_decompressor.cs
@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class mschm_decompressor
+ public abstract class mschm_decompressor : Decompressor
{
- public mspack_system system { get; set; }
-
public mschmd_decompress_state d { get; set; }
public MSPACK_ERR error { get; set; }
@@ -22,7 +20,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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
@@ -32,7 +30,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// A pointer to a mschmd_header structure, or null on failure
///
public abstract mschmd_header open(in string filename);
@@ -86,11 +84,11 @@ namespace SabreTools.Compression.libmspack
///
/// 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
+ /// 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
+ /// 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
@@ -100,7 +98,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// A pointer to a mschmd_header structure, or null on failure
///
///
///
@@ -123,14 +121,14 @@ namespace SabreTools.Compression.libmspack
/// - 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
+ /// - 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
+ /// - section: null
/// - offset: 0
/// - length: 0
- /// - all other structure elements: NULL or 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
diff --git a/libmspack/mschmd_file.cs b/libmspack/mschmd_file.cs
index e5a62fe..0b92633 100644
--- a/libmspack/mschmd_file.cs
+++ b/libmspack/mschmd_file.cs
@@ -8,7 +8,7 @@ namespace SabreTools.Compression.libmspack
public class mschmd_file
{
///
- /// A pointer to the next file in the list, or NULL if this is the final
+ /// A pointer to the next file in the list, or null if this is the final
/// file.
///
public mschmd_file next { get; set; }
diff --git a/libmspack/mshlp_compressor.cs b/libmspack/mshlp_compressor.cs
index 9d1bd1d..0c4b44f 100644
--- a/libmspack/mshlp_compressor.cs
+++ b/libmspack/mshlp_compressor.cs
@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
///
/// TODO
///
- public class mshlp_compressor
+ public class mshlp_compressor : Compressor
{
public int dummy { get; set; }
-
- public mspack_system system { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/mshlp_decompressor.cs b/libmspack/mshlp_decompressor.cs
index 18b2933..4b9947a 100644
--- a/libmspack/mshlp_decompressor.cs
+++ b/libmspack/mshlp_decompressor.cs
@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
///
/// TODO
///
- public class mshlp_decompressor
+ public class mshlp_decompressor : Decompressor
{
public int dummy { get; set; }
-
- public mspack_system system { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/mskwaj_compressor.cs b/libmspack/mskwaj_compressor.cs
index 57f0a49..e7de9d2 100644
--- a/libmspack/mskwaj_compressor.cs
+++ b/libmspack/mskwaj_compressor.cs
@@ -7,7 +7,7 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public unsafe abstract class mskwaj_compressor
+ public unsafe abstract class mskwaj_compressor : Compressor
{
public mspack_system system { get; set; }
@@ -70,7 +70,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// If null is passed as the filename, no filename is included in the
/// header. This is the default.
///
/// The original filename to use
@@ -86,7 +86,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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.
///
diff --git a/libmspack/mskwaj_decompressor.cs b/libmspack/mskwaj_decompressor.cs
index 1e2962a..45478f4 100644
--- a/libmspack/mskwaj_decompressor.cs
+++ b/libmspack/mskwaj_decompressor.cs
@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class mskwaj_decompressor
+ public abstract class mskwaj_decompressor : Decompressor
{
- public mspack_system system { get; set; }
-
public MSPACK_ERR error { get; set; }
///
@@ -19,7 +17,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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
@@ -29,7 +27,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// A pointer to a mskwajd_header structure, or null on failure
///
public abstract mskwajd_header open(in string filename);
diff --git a/libmspack/mskwajd_header.cs b/libmspack/mskwajd_header.cs
index fda8fff..0740d3b 100644
--- a/libmspack/mskwajd_header.cs
+++ b/libmspack/mskwajd_header.cs
@@ -28,7 +28,7 @@ namespace SabreTools.Compression.libmspack
public long length { get; set; }
///
- /// Output filename, or NULL if not present
+ /// Output filename, or null if not present
///
public string filename { get; set; }
diff --git a/libmspack/mslit_compressor.cs b/libmspack/mslit_compressor.cs
index 4c423f3..9d40235 100644
--- a/libmspack/mslit_compressor.cs
+++ b/libmspack/mslit_compressor.cs
@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
///
/// TODO
///
- public class mslit_compressor
+ public class mslit_compressor : Compressor
{
public int dummy { get; set; }
-
- public mspack_system system { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/mslit_decompressor.cs b/libmspack/mslit_decompressor.cs
index d9c5bce..5d8d609 100644
--- a/libmspack/mslit_decompressor.cs
+++ b/libmspack/mslit_decompressor.cs
@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
///
/// TODO
///
- public class mslit_decompressor
+ public class mslit_decompressor : Decompressor
{
public int dummy { get; set; }
-
- public mspack_system system { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/msoab_compressor.cs b/libmspack/msoab_compressor.cs
index bbe93ca..dba4489 100644
--- a/libmspack/msoab_compressor.cs
+++ b/libmspack/msoab_compressor.cs
@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class msoab_compressor
+ public abstract class msoab_compressor : Compressor
{
- public mspack_system system { get; set; }
-
///
/// Compress a full OAB file.
///
diff --git a/libmspack/msoab_decompressor.cs b/libmspack/msoab_decompressor.cs
index 933a114..b9bb32e 100644
--- a/libmspack/msoab_decompressor.cs
+++ b/libmspack/msoab_decompressor.cs
@@ -9,10 +9,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public unsafe class msoab_decompressor
+ public unsafe class msoab_decompressor : Decompressor
{
- public mspack_system system { get; set; }
-
public int buf_size { get; set; }
///
@@ -33,56 +31,54 @@ namespace SabreTools.Compression.libmspack
/// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
public MSPACK_ERR decompress(in string input, in string output)
{
- msoab_decompressor_p* self = (msoab_decompressor_p*)_self;
- mspack_system* sys;
- mspack_file* infh = NULL;
- mspack_file* outfh = NULL;
- byte* buf = NULL;
+ mspack_system sys;
+ mspack_file infh = null;
+ mspack_file outfh = null;
+ byte* buf = null;
byte[] hdrbuf = new byte[oabhead_SIZEOF];
uint block_max, target_size;
- lzxd_stream* lzx = NULL;
- mspack_system oabd_sys;
+ lzxd_stream lzx = null;
+ mspack_oab_system oabd_sys;
oabd_file in_ofh, out_ofh;
uint window_bits;
- int ret = MSPACK_ERR_OK;
+ MSPACK_ERR ret = MSPACK_ERR.MSPACK_ERR_OK;
- if (!self) return MSPACK_ERR_ARGS;
- sys = self->system;
+ sys = this.system;
- infh = sys->open(sys, input, MSPACK_SYS_OPEN_READ);
+ infh = sys.open(sys, input, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ);
if (!infh)
{
- ret = MSPACK_ERR_OPEN;
+ ret = MSPACK_ERR.MSPACK_ERR_OPEN;
goto outlbl;
}
- if (sys->read(infh, hdrbuf, oabhead_SIZEOF) != oabhead_SIZEOF)
+ if (sys.read(infh, hdrbuf, oabhead_SIZEOF) != oabhead_SIZEOF)
{
- ret = MSPACK_ERR_READ;
+ ret = MSPACK_ERR.MSPACK_ERR_READ;
goto outlbl;
}
if (EndGetI32(&hdrbuf[oabhead_VersionHi]) != 3 ||
EndGetI32(&hdrbuf[oabhead_VersionLo]) != 1)
{
- ret = MSPACK_ERR_SIGNATURE;
+ ret = MSPACK_ERR.MSPACK_ERR_SIGNATURE;
goto outlbl;
}
block_max = EndGetI32(&hdrbuf[oabhead_BlockMax]);
target_size = EndGetI32(&hdrbuf[oabhead_TargetSize]);
- outfh = sys->open(sys, output, MSPACK_SYS_OPEN_WRITE);
+ outfh = sys.open(sys, output, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE);
if (!outfh)
{
- ret = MSPACK_ERR_OPEN;
+ ret = MSPACK_ERR.MSPACK_ERR_OPEN;
goto outlbl;
}
- buf = sys->alloc(sys, self->buf_size);
+ buf = sys.alloc(sys, this.buf_size);
if (!buf)
{
- ret = MSPACK_ERR_NOMEMORY;
+ ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
goto outlbl;
}
@@ -100,9 +96,9 @@ namespace SabreTools.Compression.libmspack
{
uint blk_csize, blk_dsize, blk_crc, blk_flags;
- if (sys->read(infh, buf, oabblk_SIZEOF) != oabblk_SIZEOF)
+ if (sys.read(infh, buf, oabblk_SIZEOF) != oabblk_SIZEOF)
{
- ret = MSPACK_ERR_READ;
+ ret = MSPACK_ERR.MSPACK_ERR_READ;
goto outlbl;
}
blk_flags = EndGetI32(&buf[oabblk_Flags]);
@@ -112,7 +108,7 @@ namespace SabreTools.Compression.libmspack
if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1)
{
- ret = MSPACK_ERR_DATAFORMAT;
+ ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
goto outlbl;
}
@@ -121,10 +117,10 @@ namespace SabreTools.Compression.libmspack
/* Uncompressed block */
if (blk_dsize != blk_csize)
{
- ret = MSPACK_ERR_DATAFORMAT;
+ ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
goto outlbl;
}
- ret = copy_fh(sys, infh, outfh, blk_dsize, buf, self->buf_size);
+ ret = copy_fh(sys, infh, outfh, blk_dsize, buf, this.buf_size);
if (ret) goto outlbl;
}
else
@@ -139,27 +135,27 @@ namespace SabreTools.Compression.libmspack
out_ofh.crc = 0xffffffff;
lzx = lzxd_init(&oabd_sys, (void*)&in_ofh, (void*)&out_ofh, window_bits,
- 0, self->buf_size, blk_dsize, 1);
+ 0, this.buf_size, blk_dsize, 1);
if (!lzx)
{
- ret = MSPACK_ERR_NOMEMORY;
+ ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
goto outlbl;
}
ret = lzxd_decompress(lzx, blk_dsize);
- if (ret != MSPACK_ERR_OK)
+ if (ret != MSPACK_ERR.MSPACK_ERR_OK)
goto outlbl;
lzxd_free(lzx);
- lzx = NULL;
+ lzx = null;
/* Consume any trailing padding bytes before the next block */
- ret = copy_fh(sys, infh, NULL, in_ofh.available, buf, self->buf_size);
+ ret = copy_fh(sys, infh, null, in_ofh.available, buf, this.buf_size);
if (ret) goto outlbl;
if (out_ofh.crc != blk_crc)
{
- ret = MSPACK_ERR_CHECKSUM;
+ ret = MSPACK_ERR.MSPACK_ERR_CHECKSUM;
goto outlbl;
}
}
@@ -168,9 +164,9 @@ namespace SabreTools.Compression.libmspack
outlbl:
if (lzx) lzxd_free(lzx);
- if (outfh) sys->close(outfh);
- if (infh) sys->close(infh);
- sys->free(buf);
+ if (outfh) sys.close(outfh);
+ if (infh) sys.close(infh);
+ sys.free(buf);
return ret;
}
diff --git a/libmspack/mspack.cs b/libmspack/mspack.cs
index d45839a..05981d9 100644
--- a/libmspack/mspack.cs
+++ b/libmspack/mspack.cs
@@ -8,19 +8,18 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new CAB compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mscab_compressor mspack_create_cab_compressor(mspack_system sys) => null;
///
/// Creates a new CAB decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mscab_decompressor mspack_create_cab_decompressor(mspack_system sys)
{
- if (sys == null)
- return null;
+ if (sys == null) sys = new mspack_mscab_system();
var self = new mscab_decompressor
{
@@ -66,15 +65,15 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new CHM compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mschm_compressor mspack_create_chm_compressor(mspack_system sys) => null;
///
/// Creates a new CHM decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mschm_decompressor mspack_create_chm_decompressor(mspack_system sys) => throw new NotImplementedException();
///
@@ -92,15 +91,15 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new LIT compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mslit_compressor mspack_create_lit_compressor(mspack_system sys) => null;
///
/// Creates a new LIT decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mslit_decompressor mspack_create_lit_decompressor(mspack_system sys) => null;
///
@@ -118,15 +117,15 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new HLP compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mshlp_compressor mspack_create_hlp_compressor(mspack_system sys) => null;
///
/// Creates a new HLP decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mshlp_decompressor mspack_create_hlp_decompressor(mspack_system sys) => null;
///
@@ -144,15 +143,15 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new SZDD compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static msszdd_compressor mspack_create_szdd_compressor(mspack_system sys) => null;
///
/// Creates a new SZDD decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static msszdd_decompressor mspack_create_szdd_decompressor(mspack_system sys)
{
msszdd_decompressor self = null;
@@ -188,15 +187,15 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new KWAJ compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mskwaj_compressor mspack_create_kwaj_compressor(mspack_system sys) => null;
///
/// Creates a new KWAJ decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
public static mskwaj_decompressor mspack_create_kwaj_decompressor(mspack_system sys) => throw new NotImplementedException();
///
@@ -214,22 +213,37 @@ namespace SabreTools.Compression.libmspack
///
/// Creates a new OAB compressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
- public static msoab_compressor mspack_create_oab_compressor(mspack_system sys) => null;
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
+ public static msoab_compressor mspack_create_oab_compressor(mspack_system sys) => throw new NotImplementedException();
///
/// Creates a new OAB decompressor.
///
- /// A custom mspack_system structure, or NULL to use the default
- /// A or NULL
- public static msoab_decompressor mspack_create_oab_decompressor(mspack_system sys) => throw new NotImplementedException();
+ /// A custom mspack_system structure, or null to use the default
+ /// A or null
+ public static msoab_decompressor mspack_create_oab_decompressor(mspack_system sys)
+ {
+ if (sys == null) sys = new mspack_oab_system();
+
+ msoab_decompressor self = new msoab_decompressor();
+ self.system = sys;
+ self.buf_size = 4096;
+ return self;
+ }
///
/// Destroys an existing OAB compressor.
///
/// The to destroy
- public static void mspack_destroy_oab_compressor(msoab_compressor self) { }
+ public static void mspack_destroy_oab_compressor(msoab_compressor self)
+ {
+ if (self != null)
+ {
+ mspack_system sys = self.system;
+ //sys.free(self);
+ }
+ }
///
/// Destroys an existing OAB decompressor.
diff --git a/libmspack/mspack_mscab_system.cs b/libmspack/mspack_mscab_system.cs
new file mode 100644
index 0000000..1fc822b
--- /dev/null
+++ b/libmspack/mspack_mscab_system.cs
@@ -0,0 +1,230 @@
+using static SabreTools.Compression.libmspack.cab;
+
+namespace SabreTools.Compression.libmspack
+{
+ public unsafe class mspack_mscab_system : 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 unsafe int read(mspack_file file, void* buffer, int bytes)
+ {
+ mscab_decompressor self = (mscab_decompressor)file;
+ byte* buf = (byte*)buffer;
+ mspack_system sys = self.system;
+ int avail, todo, outlen, ignore_cksum, ignore_blocksize;
+
+ ignore_cksum = self.salvage != 0 || (self.fix_mszip != 0 && (((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_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 = cabd_sys_read_block(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 (((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_QUANTUM)
+ {
+ *self.d.i_end++ = 0xFF;
+ }
+
+ // Is this the last block?
+ if (self.d.block >= self.d.folder.num_blocks)
+ {
+ if (((int)self.d.comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_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 unsafe int write(mspack_file file, void* buffer, int bytes)
+ {
+ mscab_decompressor self = (mscab_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 cabd_sys_read_block(mspack_system sys, mscabd_decompress_state d, ref int @out, int ignore_cksum, int ignore_blocksize)
+ {
+ byte[] hdr = new byte[cfdata_SIZEOF];
+ uint cksum;
+ int len, full_len;
+
+ // Reset the input block pointer and end of block pointer
+ d.i_ptr = d.i_end = system.GetArrayPointer(d.input);
+
+ do
+ {
+ // Read the block header
+ if (sys.read(d.infh, system.GetArrayPointer(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])))
+ {
+ uint sum2 = cabd_checksum(d.i_end, (uint)len, 0);
+ if (cabd_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])))
+ {
+ 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 cabd_checksum(byte* data, uint bytes, uint cksum)
+ {
+ uint len, ul = 0;
+
+ for (len = bytes >> 2; len-- > 0; data += 4)
+ {
+ cksum ^= EndGetI32(data);
+ }
+
+ switch (bytes & 3)
+ {
+ case 3: ul |= (uint)(*data++ << 16); goto case 2;
+ case 2: ul |= (uint)(*data++ << 8); goto case 1;
+ case 1: ul |= *data; break;
+ }
+ cksum ^= ul;
+
+ return cksum;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libmspack/mspack_system.cs b/libmspack/mspack_system.cs
index 8578cd4..b8a2ed3 100644
--- a/libmspack/mspack_system.cs
+++ b/libmspack/mspack_system.cs
@@ -7,7 +7,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// 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.
///
@@ -35,7 +35,7 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// for other mspack_system methods to operate. Returning the null
/// pointer indicates an error condition.
///
///
@@ -127,7 +127,7 @@ namespace SabreTools.Compression.libmspack
///
///
/// May be a file handle returned from open() if this message
- /// pertains to a specific open file, or NULL if not related to
+ /// pertains to a specific open file, or null if not related to
/// a specific file.
///
///
@@ -141,7 +141,7 @@ namespace SabreTools.Compression.libmspack
///
/// The number of bytes to allocate
///
- /// A pointer to the requested number of bytes, or NULL if
+ /// A pointer to the requested number of bytes, or null if
/// not enough memory is available
///
///
@@ -150,7 +150,7 @@ namespace SabreTools.Compression.libmspack
///
/// Frees memory
///
- /// The memory to be freed. NULL is accepted and ignored.
+ /// The memory to be freed. null is accepted and ignored.
///
public abstract void free(void* ptr);
diff --git a/libmspack/msszdd_compressor.cs b/libmspack/msszdd_compressor.cs
index 6ddc6f2..4653317 100644
--- a/libmspack/msszdd_compressor.cs
+++ b/libmspack/msszdd_compressor.cs
@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class msszdd_compressor
+ public abstract class msszdd_compressor : Compressor
{
- public mspack_system system { get; set; }
-
public MSPACK_ERR error { get; set; }
///
diff --git a/libmspack/msszdd_decompressor.cs b/libmspack/msszdd_decompressor.cs
index 10d377a..e546f3b 100644
--- a/libmspack/msszdd_decompressor.cs
+++ b/libmspack/msszdd_decompressor.cs
@@ -10,10 +10,8 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public unsafe class msszdd_decompressor
+ public unsafe class msszdd_decompressor : Decompressor
{
- public mspack_system system { get; set; }
-
public MSPACK_ERR error { get; set; }
///
diff --git a/libmspack/mszip.cs b/libmspack/mszip.cs
index 2946c35..8182b59 100644
--- a/libmspack/mszip.cs
+++ b/libmspack/mszip.cs
@@ -32,7 +32,7 @@ namespace SabreTools.Compression.libmspack
///
/// - uses system->alloc() to allocate memory
///
- /// - returns NULL if not enough memory
+ /// - returns null if not enough memory
///
/// - input_buffer_size is how many bytes to use as an input bitstream buffer
///
diff --git a/libmspack/mszipd_stream.cs b/libmspack/mszipd_stream.cs
index 7056b52..1f01490 100644
--- a/libmspack/mszipd_stream.cs
+++ b/libmspack/mszipd_stream.cs
@@ -3,28 +3,8 @@ using static SabreTools.Compression.libmspack.mszip;
namespace SabreTools.Compression.libmspack
{
- public unsafe class mszipd_stream
+ public unsafe class mszipd_stream : 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; }
-
- ///
- /// Offset within window
- ///
- public uint window_posn { get; set; }
-
///
/// inflate() will call this whenever the window should be emptied.
///
@@ -32,34 +12,10 @@ namespace SabreTools.Compression.libmspack
///
public int flush_window(uint val) => 0;
- public MSPACK_ERR error { get; set; }
-
public int repair_mode { get; set; }
public int bytes_output { 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 byte input_end { get; set; }
-
- public uint bit_buffer { get; set; }
-
- public uint bits_left { get; set; }
-
- public uint inbuf_size { get; set; }
-
- #endregion
-
#region Huffman code lengths
public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
@@ -80,5 +36,11 @@ namespace SabreTools.Compression.libmspack
/// 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/noned_state.cs b/libmspack/noned_state.cs
new file mode 100644
index 0000000..d946d7c
--- /dev/null
+++ b/libmspack/noned_state.cs
@@ -0,0 +1,18 @@
+namespace SabreTools.Compression.libmspack
+{
+ ///
+ /// The "not compressed" method decompressor
+ ///
+ public unsafe class noned_state
+ {
+ public mspack_system sys { get; set; }
+
+ public mspack_file i { get; set; }
+
+ public mspack_file o { get; set; }
+
+ public byte* buf { get; set; }
+
+ public int bufsize { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/libmspack/qtm.cs b/libmspack/qtm.cs
index df1c3a2..024f04e 100644
--- a/libmspack/qtm.cs
+++ b/libmspack/qtm.cs
@@ -1,17 +1,142 @@
namespace SabreTools.Compression.libmspack
{
- public static class qtm
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// Initialises a model to decode symbols from [start] to [start]+[len]-1
+ ///
+ private static void qtmd_init_model(qtmd_model model, qtmd_modelsym* syms, int start, int len)
+ {
+ model.shiftsleft = 4;
+ model.entries = len;
+ model.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
+ }
+ }
+
///
/// Allocates Quantum decompression state for decoding the given stream.
///
- /// - returns NULL if window_bits is outwith the range 10 to 21 (inclusive).
+ /// - returns null if window_bits is outwith the range 10 to 21 (inclusive).
///
- /// - uses system->alloc() to allocate memory
+ /// - uses system.alloc() to allocate memory
///
- /// - returns NULL if not enough memory
+ /// - returns null if not enough memory
///
/// - window_bits is the size of the Quantum window, from 1Kb (10) to 2Mb (21).
///
@@ -23,8 +148,70 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public static qtmd_stream qtmd_init(mspack_system system, mspack_file input, mspack_file output, int window_bits, int input_buffer_size) => null;
-
+ 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;
+ qtmd_init_model(qtm.model0, qtm.m0sym, 0, 64);
+ qtmd_init_model(qtm.model1, qtm.m1sym, 64, 64);
+ qtmd_init_model(qtm.model2, qtm.m2sym, 128, 64);
+ qtmd_init_model(qtm.model3, qtm.m3sym, 192, 64);
+ qtmd_init_model(qtm.model4, qtm.m4sym, 0, (i > 24) ? 24 : i);
+ qtmd_init_model(qtm.model5, qtm.m5sym, 0, (i > 36) ? 36 : i);
+ qtmd_init_model(qtm.model6, qtm.m6sym, 0, i);
+ qtmd_init_model(qtm.model6len, qtm.m6lsym, 0, 27);
+ qtmd_init_model(qtm.model7, qtm.m7sym, 0, 7);
+
+ // All ok
+ return qtm;
+ }
+
///
/// Decompresses, or decompresses more of, a Quantum stream.
///
@@ -35,13 +222,13 @@ namespace SabreTools.Compression.libmspack
/// 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
+ /// - 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()
+ /// than one call may be made to system.write()
///
- /// - Quantum will read input bytes as necessary using the system->read()
+ /// - 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,
+ /// qtmd_init(). This will continue until system.read() returns 0 bytes,
/// or an error.
///
///
@@ -52,9 +239,19 @@ namespace SabreTools.Compression.libmspack
///
/// Frees all state associated with a Quantum data stream
///
- /// - calls system->free() using the system pointer given in qtmd_init()
+ /// - calls system.free() using the system pointer given in qtmd_init()
///
///
- public static void qtmd_free(qtmd_stream qtm) { }
+ 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/qtmd_model.cs b/libmspack/qtmd_model.cs
index e331261..1068f90 100644
--- a/libmspack/qtmd_model.cs
+++ b/libmspack/qtmd_model.cs
@@ -6,6 +6,6 @@ namespace SabreTools.Compression.libmspack
public int entries { get; set; }
- public qtmd_modelsym** syms { get; set; }
+ public qtmd_modelsym* syms { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/qtmd_stream.cs b/libmspack/qtmd_stream.cs
index 0c063f3..2424b2a 100644
--- a/libmspack/qtmd_stream.cs
+++ b/libmspack/qtmd_stream.cs
@@ -1,22 +1,7 @@
namespace SabreTools.Compression.libmspack
{
- public unsafe class qtmd_stream
+ public unsafe class qtmd_stream : 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; }
-
///
/// Decoding window
///
@@ -27,11 +12,6 @@ namespace SabreTools.Compression.libmspack
///
public uint window_size { get; set; }
- ///
- /// Decompression offset within window
- ///
- public uint window_posn { get; set; }
-
///
/// Bytes remaining for current frame
///
@@ -57,30 +37,6 @@ namespace SabreTools.Compression.libmspack
///
public byte header_read { get; set; }
- public MSPACK_ERR error { 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 uint bit_buffer { get; set; }
-
- public uint inbuf_size { get; set; }
-
- public byte bits_left { get; set; }
-
- public byte input_end { get; set; }
-
- #endregion
-
#region Models
#region Four literal models, each representing 64 symbols
@@ -156,5 +112,15 @@ namespace SabreTools.Compression.libmspack
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/readbits.cs b/libmspack/readbits.cs
new file mode 100644
index 0000000..a1b0f9e
--- /dev/null
+++ b/libmspack/readbits.cs
@@ -0,0 +1,245 @@
+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 (read_input() != 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 read_input()
+ {
+ 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
new file mode 100644
index 0000000..7d44321
--- /dev/null
+++ b/libmspack/readhuff.cs
@@ -0,0 +1,7 @@
+namespace SabreTools.Compression.libmspack
+{
+ public unsafe abstract class readhuff
+ {
+ public const int HUFF_MAXBITS = 16;
+ }
+}
\ No newline at end of file