Checkpoint (nw)

This commit is contained in:
Matt Nadareski
2023-09-19 17:12:11 -04:00
parent f23078d792
commit a7cfb47dbe
37 changed files with 1446 additions and 335 deletions

10
libmspack/Compressor.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace SabreTools.Compression.libmspack
{
/// <summary>
/// Base class for all compressor implementations
/// </summary>
public abstract class Compressor
{
public mspack_system system { get; set; }
}
}

10
libmspack/Decompressor.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace SabreTools.Compression.libmspack
{
/// <summary>
/// Base class for all decompressor implementations
/// </summary>
public abstract class Decompressor
{
public mspack_system system { get; set; }
}
}

View File

@@ -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
/// <summary>
/// cabd_free_decomp frees decompression state, according to which method
/// was used.
/// </summary>
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;
}
/// <summary>
/// cabd_init_decomp initialises decompression state, according to which
/// decompression method was used. relies on self.d.folder being the same
/// as when initialised.
/// </summary>
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<byte>(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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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.
/// </summary>
/// <param name="system">
/// An mspack_system structure used to read from
@@ -77,7 +77,7 @@ namespace SabreTools.Compression.libmspack
/// non-zero for LZX DELTA encoded data.
/// </param>
/// <returns>
/// 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.
/// </returns>
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;

View File

@@ -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
{
/// <summary>
/// I/O routines
/// </summary>
public mspack_system sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public mspack_file input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public mspack_file output { get; set; }
/// <summary>
/// Number of bytes actually output
/// </summary>
@@ -49,11 +34,6 @@ namespace SabreTools.Compression.libmspack
/// </summary>
public uint num_offsets { get; set; }
/// <summary>
/// Decompression offset within window
/// </summary>
public uint window_posn { get; set; }
/// <summary>
/// Current frame offset within in window
/// </summary>
@@ -117,35 +97,13 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Have we reached the end of input?
/// </summary>
public byte input_end { get; set; }
public new byte input_end { get; set; }
/// <summary>
/// Does stream follow LZX DELTA spec?
/// </summary>
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
/// </summary>
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);
}
}
}

View File

@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// TODO
/// </summary>
public class mscab_compressor
public class mscab_compressor : Compressor
{
public int dummy { get; set; }
public mspack_system system { get; set; }
}
}

View File

@@ -1,3 +1,5 @@
using static SabreTools.Compression.libmspack.cab;
namespace SabreTools.Compression.libmspack
{
/// <summary>
@@ -7,12 +9,10 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_cab_decompressor()"/>
/// <see cref="mspack_destroy_cab_decompressor()"/>
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().
/// </param>
/// <returns>A pointer to a mscabd_cabinet structure, or NULL on failure</returns>
/// <returns>A pointer to a mscabd_cabinet structure, or null on failure</returns>
/// <see cref="close(mscabd_cabinet)"/>
/// <see cref="search(in string)"/>
/// <see cref="last_error()"/>
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;
}
/// <summary>
/// Closes a previously opened cabinet or cabinet set.
@@ -77,7 +103,374 @@ namespace SabreTools.Compression.libmspack
/// <see cref="search(in string)"/>
/// <see cref="append(mscabd_cabinet, mscabd_cabinet)"/>
/// <see cref="prepend(mscabd_cabinet, mscabd_cabinet)"/>
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;
}
}
/// <summary>
/// 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
/// </summary>
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;
}
/// <summary>
/// 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().
/// </param>
/// <returns>A pointer to a mscabd_cabinet structure, or NULL</returns>
/// <returns>A pointer to a mscabd_cabinet structure, or null</returns>
/// <see cref="close(mscabd_cabinet)"/>
/// <see cref="open(in string)"/>
/// <see cref="last_error()"/>
@@ -121,7 +514,7 @@ namespace SabreTools.Compression.libmspack
/// set.
///
/// This will attempt to append one cabinet to another such that
/// <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and
/// <tt>(cab.nextcab == nextcab) && (nextcab.prevcab == cab)</tt> 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
/// </summary>
/// <param name="cab">The cabinet which will be appended to, predecessor of nextcab</param>
/// <param name="nextcab">The cabinet which will be appended, successor of cab</param>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
/// <returns>An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful</returns>
/// <see cref="prepend(mscabd_cabinet, mscabd_cabinet)"/>
/// <see cref="open(in string)"/>
/// <see cref="close(mscabd_cabinet)"/>
@@ -160,13 +553,13 @@ namespace SabreTools.Compression.libmspack
/// cabinet set.
///
/// This will attempt to prepend one cabinet to another, such that
/// <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In
/// <tt>(cab.prevcab == prevcab) && (prevcab.nextcab == cab)</tt>. In
/// all other respects, it is identical to append(). See append() for the
/// full documentation.
/// </summary>
/// <param name="cab">The cabinet which will be prepended to, successor of prevcab</param>
/// <param name="prevcab">The cabinet which will be prepended, predecessor of cab</param>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
/// <returns>An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful</returns>
/// <see cref="append(mscabd_cabinet, mscabd_cabinet)"/>
/// <see cref="open(in string)"/>
/// <see cref="close(mscabd_cabinet)"/>
@@ -190,7 +583,7 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <param name="file">The file to be decompressed</param>
/// <param name="filename">The filename of the file being written to</param>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
/// <returns>An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful</returns>
public abstract MSPACK_ERR extract(mscabd_file file, in string filename);
/// <summary>
@@ -210,12 +603,35 @@ namespace SabreTools.Compression.libmspack
/// <param name="param">The parameter to set</param>
/// <param name="value">The value to set the parameter to</param>
/// <returns>
/// 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.
/// </returns>
/// <see cref="search(in string)"/>
/// <see cref="extract(mscabd_file, in string)"/>
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;
}
/// <summary>
/// Returns the error code set by the most recently called method.
@@ -226,6 +642,9 @@ namespace SabreTools.Compression.libmspack
/// <returns>The most recent error code</returns>
/// <see cref="open(in string)"/>
/// <see cref="search(in string)"/>
public abstract MSPACK_ERR last_error();
public MSPACK_ERR last_error()
{
return this.error;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace SabreTools.Compression.libmspack
{
/// <summary>
/// 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.
/// </summary>
public mscabd_cabinet next { get; set; }
@@ -38,34 +38,34 @@ namespace SabreTools.Compression.libmspack
public uint length { get; set; }
/// <summary>
/// The previous cabinet in a cabinet set, or NULL.
/// The previous cabinet in a cabinet set, or null.
/// </summary>
public mscabd_cabinet prevcab { get; set; }
/// <summary>
/// The next cabinet in a cabinet set, or NULL.
/// The next cabinet in a cabinet set, or null.
/// </summary>
public mscabd_cabinet nextcab { get; set; }
/// <summary>
/// The filename of the previous cabinet in a cabinet set, or NULL.
/// The filename of the previous cabinet in a cabinet set, or null.
/// </summary>
public string prevname { get; set; }
/// <summary>
/// The filename of the next cabinet in a cabinet set, or NULL.
/// The filename of the next cabinet in a cabinet set, or null.
/// </summary>
public string nextname { get; set; }
/// <summary>
/// The name of the disk containing the previous cabinet in a cabinet
/// set, or NULL.
/// set, or null.
/// </summary>
public string previnfo { get; set; }
/// <summary>
/// The name of the disk containing the next cabinet in a cabinet set,
/// or NULL.
/// or null.
/// </summary>
public string nextinfo { get; set; }

View File

@@ -42,15 +42,12 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Decompressor code
/// </summary>
/// <param name="data"></param>
/// <param name="offset"></param>
/// <returns></returns>
public abstract int decompress(void* data, long offset);
public abstract MSPACK_ERR decompress(object data, long offset);
/// <summary>
/// Decompressor state
/// </summary>
public void* state { get; set; }
public object state { get; set; }
/// <summary>
/// Cabinet where input data comes from
@@ -82,4 +79,25 @@ namespace SabreTools.Compression.libmspack
/// </summary>
public byte[] input { get; set; } = new byte[CAB_INPUTBUF];
}
public unsafe class mscabd_noned_decompress_state : mscabd_decompress_state
{
/// <inheritdoc/>
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;
}
}
}

View File

@@ -8,7 +8,7 @@ namespace SabreTools.Compression.libmspack
public class mscabd_file
{
/// <summary>
/// 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.
/// </summary>
public mscabd_file next { get; set; }

View File

@@ -12,7 +12,7 @@ namespace SabreTools.Compression.libmspack
public class mscabd_folder
{
/// <summary>
/// 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.
/// </summary>
public mscabd_folder next { get; set; }

View File

@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_chm_compressor()"/>
/// <see cref="mspack_destroy_chm_compressor()"/>
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; }

View File

@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_chm_decompressor()"/>
/// <see cref="mspack_destroy_chm_decompressor()"/>
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().
/// </param>
/// <returns>A pointer to a mschmd_header structure, or NULL on failure</returns>
/// <returns>A pointer to a mschmd_header structure, or null on failure</returns>
/// <see cref="close(mschmd_header)"/>
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().
/// </param>
/// <returns>A pointer to a mschmd_header structure, or NULL on failure</returns>
/// <returns>A pointer to a mschmd_header structure, or null on failure</returns>
/// <see cref="open(in string)"/>
/// <see cref="close(mschmd_header)"/>
/// <see cref="fast_find(mschmd_header, in string, mschmd_file, int)"/>
@@ -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

View File

@@ -8,7 +8,7 @@ namespace SabreTools.Compression.libmspack
public class mschmd_file
{
/// <summary>
/// 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.
/// </summary>
public mschmd_file next { get; set; }

View File

@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// TODO
/// </summary>
public class mshlp_compressor
public class mshlp_compressor : Compressor
{
public int dummy { get; set; }
public mspack_system system { get; set; }
}
}

View File

@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// TODO
/// </summary>
public class mshlp_decompressor
public class mshlp_decompressor : Decompressor
{
public int dummy { get; set; }
public mspack_system system { get; set; }
}
}

View File

@@ -7,7 +7,7 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_kwaj_compressor()"/>
/// <see cref="mspack_destroy_kwaj_compressor()"/>
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.
/// </summary>
/// <param name="filename">The original filename to use</param>
@@ -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.
/// </summary>

View File

@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_kwaj_decompressor()"/>
/// <see cref="mspack_destroy_kwaj_decompressor()"/>
public abstract class mskwaj_decompressor
public abstract class mskwaj_decompressor : Decompressor
{
public mspack_system system { get; set; }
public MSPACK_ERR error { get; set; }
/// <summary>
@@ -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().
/// </param>
/// <returns>A pointer to a mskwajd_header structure, or NULL on failure</returns>
/// <returns>A pointer to a mskwajd_header structure, or null on failure</returns>
/// <see cref="close(mskwajd_header)"/>
public abstract mskwajd_header open(in string filename);

View File

@@ -28,7 +28,7 @@ namespace SabreTools.Compression.libmspack
public long length { get; set; }
/// <summary>
/// Output filename, or NULL if not present
/// Output filename, or null if not present
/// </summary>
public string filename { get; set; }

View File

@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// TODO
/// </summary>
public class mslit_compressor
public class mslit_compressor : Compressor
{
public int dummy { get; set; }
public mspack_system system { get; set; }
}
}

View File

@@ -3,10 +3,8 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// TODO
/// </summary>
public class mslit_decompressor
public class mslit_decompressor : Decompressor
{
public int dummy { get; set; }
public mspack_system system { get; set; }
}
}

View File

@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_oab_compressor()"/>
/// <see cref="mspack_destroy_oab_compressor()"/>
public abstract class msoab_compressor
public abstract class msoab_compressor : Compressor
{
public mspack_system system { get; set; }
/// <summary>
/// Compress a full OAB file.
///

View File

@@ -9,10 +9,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_oab_decompressor()"/>
/// <see cref="mspack_destroy_oab_decompressor()"/>
public unsafe class msoab_decompressor
public unsafe class msoab_decompressor : Decompressor
{
public mspack_system system { get; set; }
public int buf_size { get; set; }
/// <summary>
@@ -33,56 +31,54 @@ namespace SabreTools.Compression.libmspack
/// <returns>An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful</returns>
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;
}

View File

@@ -8,19 +8,18 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new CAB compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mscab_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mscab_compressor"/> or null</returns>
public static mscab_compressor mspack_create_cab_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new CAB decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mscab_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mscab_decompressor"/> or null</returns>
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
/// <summary>
/// Creates a new CHM compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mschm_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mschm_compressor"/> or null</returns>
public static mschm_compressor mspack_create_chm_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new CHM decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mschm_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mschm_decompressor"/> or null</returns>
public static mschm_decompressor mspack_create_chm_decompressor(mspack_system sys) => throw new NotImplementedException();
/// <summary>
@@ -92,15 +91,15 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new LIT compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mslit_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mslit_compressor"/> or null</returns>
public static mslit_compressor mspack_create_lit_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new LIT decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mslit_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mslit_decompressor"/> or null</returns>
public static mslit_decompressor mspack_create_lit_decompressor(mspack_system sys) => null;
/// <summary>
@@ -118,15 +117,15 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new HLP compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mshlp_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mshlp_compressor"/> or null</returns>
public static mshlp_compressor mspack_create_hlp_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new HLP decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mshlp_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mshlp_decompressor"/> or null</returns>
public static mshlp_decompressor mspack_create_hlp_decompressor(mspack_system sys) => null;
/// <summary>
@@ -144,15 +143,15 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new SZDD compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="msszdd_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="msszdd_compressor"/> or null</returns>
public static msszdd_compressor mspack_create_szdd_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new SZDD decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="msszdd_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="msszdd_decompressor"/> or null</returns>
public static msszdd_decompressor mspack_create_szdd_decompressor(mspack_system sys)
{
msszdd_decompressor self = null;
@@ -188,15 +187,15 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new KWAJ compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mskwaj_compressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mskwaj_compressor"/> or null</returns>
public static mskwaj_compressor mspack_create_kwaj_compressor(mspack_system sys) => null;
/// <summary>
/// Creates a new KWAJ decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="mskwaj_decompressor"/> or NULL</returns>
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="mskwaj_decompressor"/> or null</returns>
public static mskwaj_decompressor mspack_create_kwaj_decompressor(mspack_system sys) => throw new NotImplementedException();
/// <summary>
@@ -214,22 +213,37 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Creates a new OAB compressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="msoab_compressor"/> or NULL</returns>
public static msoab_compressor mspack_create_oab_compressor(mspack_system sys) => null;
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="msoab_compressor"/> or null</returns>
public static msoab_compressor mspack_create_oab_compressor(mspack_system sys) => throw new NotImplementedException();
/// <summary>
/// Creates a new OAB decompressor.
/// </summary>
/// <param name="sys">A custom mspack_system structure, or NULL to use the default</param>
/// <returns>A <see cref="msoab_decompressor"/> or NULL</returns>
public static msoab_decompressor mspack_create_oab_decompressor(mspack_system sys) => throw new NotImplementedException();
/// <param name="sys">A custom mspack_system structure, or null to use the default</param>
/// <returns>A <see cref="msoab_decompressor"/> or null</returns>
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;
}
/// <summary>
/// Destroys an existing OAB compressor.
/// </summary>
/// <param name="self">The <see cref="msoab_compressor"/> to destroy</param>
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);
}
}
/// <summary>
/// Destroys an existing OAB decompressor.

View File

@@ -0,0 +1,230 @@
using static SabreTools.Compression.libmspack.cab;
namespace SabreTools.Compression.libmspack
{
public unsafe class mspack_mscab_system : mspack_default_system
{
/// <summary>
/// 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
/// </summary>
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;
}
/// <summary>
/// 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
/// </summary>
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;
}
/// <summary>
/// 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
/// </summary>
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;
}
}
}

View File

@@ -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.
/// </returns>
/// <see cref="close(mspack_file)"/>
@@ -127,7 +127,7 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <param name="file">
/// 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.
/// </param>
/// <param name="format">
@@ -141,7 +141,7 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <param name="bytes">The number of bytes to allocate</param>
/// <returns>
/// 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
/// </returns>
/// <see cref="free(void*)"/>
@@ -150,7 +150,7 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// Frees memory
/// </summary>
/// <param name="ptr">The memory to be freed. NULL is accepted and ignored.</param>
/// <param name="ptr">The memory to be freed. null is accepted and ignored.</param>
/// <see cref="alloc(int)"/>
public abstract void free(void* ptr);

View File

@@ -7,10 +7,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_szdd_compressor()"/>
/// <see cref="mspack_destroy_szdd_compressor()"/>
public abstract class msszdd_compressor
public abstract class msszdd_compressor : Compressor
{
public mspack_system system { get; set; }
public MSPACK_ERR error { get; set; }
/// <summary>

View File

@@ -10,10 +10,8 @@ namespace SabreTools.Compression.libmspack
/// </summary>
/// <see cref="mspack_create_szdd_decompressor()"/>
/// <see cref="mspack_destroy_szdd_decompressor()"/>
public unsafe class msszdd_decompressor
public unsafe class msszdd_decompressor : Decompressor
{
public mspack_system system { get; set; }
public MSPACK_ERR error { get; set; }
/// <summary>

View File

@@ -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
///

View File

@@ -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
{
/// <summary>
/// I/O routines
/// </summary>
public mspack_system sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public mspack_file input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public mspack_file output { get; set; }
/// <summary>
/// Offset within window
/// </summary>
public uint window_posn { get; set; }
/// <summary>
/// inflate() will call this whenever the window should be emptied.
/// </summary>
@@ -32,34 +12,10 @@ namespace SabreTools.Compression.libmspack
/// <returns></returns>
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
/// </summary>
public byte[] window { get; set; } = new byte[MSZIP_FRAME_SIZE];
public override void READ_BYTES()
{
READ_IF_NEEDED;
INJECT_BITS_LSB(*i_ptr++, 8);
}
}
}

18
libmspack/noned_state.cs Normal file
View File

@@ -0,0 +1,18 @@
namespace SabreTools.Compression.libmspack
{
/// <summary>
/// The "not compressed" method decompressor
/// </summary>
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; }
}
}

View File

@@ -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;
}
}
}
/// <summary>
/// Initialises a model to decode symbols from [start] to [start]+[len]-1
/// </summary>
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
}
}
/// <summary>
/// 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
/// <param name="window_bits"></param>
/// <param name="input_buffer_size"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="qtm"></param>
@@ -52,9 +239,19 @@ namespace SabreTools.Compression.libmspack
/// <summary>
/// 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()
/// </summary>
/// <param name="qtm"></param>
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);
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -1,22 +1,7 @@
namespace SabreTools.Compression.libmspack
{
public unsafe class qtmd_stream
public unsafe class qtmd_stream : readbits
{
/// <summary>
/// I/O routines
/// </summary>
public mspack_system sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public mspack_file input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public mspack_file output { get; set; }
/// <summary>
/// Decoding window
/// </summary>
@@ -27,11 +12,6 @@ namespace SabreTools.Compression.libmspack
/// </summary>
public uint window_size { get; set; }
/// <summary>
/// Decompression offset within window
/// </summary>
public uint window_posn { get; set; }
/// <summary>
/// Bytes remaining for current frame
/// </summary>
@@ -57,30 +37,6 @@ namespace SabreTools.Compression.libmspack
/// </summary>
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);
}
}
}

245
libmspack/readbits.cs Normal file
View File

@@ -0,0 +1,245 @@
namespace SabreTools.Compression.libmspack
{
public unsafe abstract class readbits
{
/// <summary>
/// I/O routines
/// </summary>
public mspack_system sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public mspack_file input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public mspack_file output { get; set; }
/// <summary>
/// Decompression offset within window
/// </summary>
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; }
/// <see href="https://github.com/kyz/libmspack/blob/master/libmspack/mspack/readbits.h"/>
#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
}
}

7
libmspack/readhuff.cs Normal file
View File

@@ -0,0 +1,7 @@
namespace SabreTools.Compression.libmspack
{
public unsafe abstract class readhuff
{
public const int HUFF_MAXBITS = 16;
}
}