diff --git a/libmspack/CAB/CABSystem.cs b/libmspack/CAB/CABSystem.cs
index 81d9b30..9442bb2 100644
--- a/libmspack/CAB/CABSystem.cs
+++ b/libmspack/CAB/CABSystem.cs
@@ -109,12 +109,12 @@ namespace SabreTools.Compression.libmspack.CAB
int len, full_len;
// Reset the input block pointer and end of block pointer
- d.i_ptr = d.i_end = (byte*)d.input.Pointer;
+ d.i_ptr = d.i_end = d.input;
do
{
// Read the block header
- if (sys.read(d.infh, (byte*)hdr.Pointer, cfdata_SIZEOF) != cfdata_SIZEOF)
+ if (sys.read(d.infh, hdr, cfdata_SIZEOF) != cfdata_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
diff --git a/libmspack/CAB/Decompressor.cs b/libmspack/CAB/Decompressor.cs
index f96c1f9..6688a7e 100644
--- a/libmspack/CAB/Decompressor.cs
+++ b/libmspack/CAB/Decompressor.cs
@@ -21,7 +21,7 @@ namespace SabreTools.Compression.libmspack.CAB
public int salvage { get; private set; }
- public MSPACK_ERR read_error { get; private set; }
+ public MSPACK_ERR read_error { get; set; }
///
/// Creates a new CAB decompressor
@@ -236,7 +236,7 @@ namespace SabreTools.Compression.libmspack.CAB
}
// Read in the CFHEADER
- if (sys.read(fh, (byte*)buf.Pointer, cfhead_SIZEOF) != cfhead_SIZEOF)
+ if (sys.read(fh, buf, cfhead_SIZEOF) != cfhead_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
@@ -279,7 +279,7 @@ namespace SabreTools.Compression.libmspack.CAB
if (cab.flags.HasFlag(MSCAB_HDR.MSCAB_HDR_RESV))
{
- if (sys.read(fh, (byte*)buf.Pointer, cfheadext_SIZEOF) != cfheadext_SIZEOF)
+ if (sys.read(fh, buf, cfheadext_SIZEOF) != cfheadext_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
@@ -329,7 +329,7 @@ namespace SabreTools.Compression.libmspack.CAB
// Read folders
for (i = 0; i < num_folders; i++)
{
- if (sys.read(fh, (byte*)buf.Pointer, cffold_SIZEOF) != cffold_SIZEOF)
+ if (sys.read(fh, buf, cffold_SIZEOF) != cffold_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
@@ -361,7 +361,7 @@ namespace SabreTools.Compression.libmspack.CAB
// Read files
for (i = 0; i < num_files; i++)
{
- if (sys.read(fh, (byte*)buf.Pointer, cffile_SIZEOF) != cffile_SIZEOF)
+ if (sys.read(fh, buf, cffile_SIZEOF) != cffile_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
@@ -461,12 +461,12 @@ namespace SabreTools.Compression.libmspack.CAB
private static string ReadString(mspack_system sys, mspack_file fh, int permit_empty, out MSPACK_ERR error)
{
long @base = sys.tell(fh);
- byte[] buf = new byte[256];
+ FixedArray buf = new FixedArray(256);
string str;
int len, i, ok;
- // Read up to 256 bytes */
- if ((len = sys.read(fh, (byte*)buf.Pointer, 256)) <= 0)
+ // Read up to 256 bytes
+ if ((len = sys.read(fh, buf, 256)) <= 0)
{
error = MSPACK_ERR.MSPACK_ERR_READ;
return null;
@@ -492,9 +492,9 @@ namespace SabreTools.Compression.libmspack.CAB
return null;
}
- char[] strchr = new char[len];
- sys.copy((byte*)buf.Pointer, &strchr[0], len);
- str = new string(strchr);
+ FixedArray strchr = new FixedArray(len);
+ sys.copy(buf, (void*)strchr, len);
+ str = new string(strchr.ToArray());
error = MSPACK_ERR.MSPACK_ERR_OK;
return str;
}
@@ -538,14 +538,14 @@ namespace SabreTools.Compression.libmspack.CAB
{
mscabd_cabinet cab = null;
mspack_system sys;
- byte* search_buf;
+ FixedArray search_buf;
mspack_file fh;
long filelen, firstlen = 0;
sys = this.system;
// Allocate a search buffer
- search_buf = (byte*)sys.alloc(this.searchbuf_size);
+ search_buf = new FixedArray(this.searchbuf_size);
if (search_buf == null)
{
this.error = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
@@ -590,7 +590,7 @@ namespace SabreTools.Compression.libmspack.CAB
/// Find is the inner loop of , to make it easier to
/// break out of the loop and be sure that all resources are freed
///
- private MSPACK_ERR Find(byte* buf, mspack_file fh, in string filename, long flen, ref long firstlen, mscabd_cabinet firstcab)
+ private MSPACK_ERR Find(FixedArray buf, mspack_file fh, in string filename, long flen, ref long firstlen, mscabd_cabinet firstcab)
{
mscabd_cabinet cab, link = null;
long caboff, offset, length;
@@ -624,7 +624,7 @@ namespace SabreTools.Compression.libmspack.CAB
}
// Read through the entire buffer.
- for (p = (byte*)buf.Pointer, pend = &buf[length]; p < pend;)
+ for (p = buf, pend = (byte*)buf.Pointer + length; p < pend;)
{
switch (state)
{
@@ -660,7 +660,7 @@ namespace SabreTools.Compression.libmspack.CAB
foffset_u32 |= (uint)(*p++ << 24);
// Now we have recieved 20 bytes of potential cab header. work out
// the offset in the file of this potential cabinet */
- caboff = offset + (p - (byte*)buf.Pointer) - 20;
+ caboff = offset + (p - buf) - 20;
// Should reading cabinet fail, restart search just after 'MSCF'
offset = caboff + 4;
@@ -1132,7 +1132,7 @@ namespace SabreTools.Compression.libmspack.CAB
this.d.offset = 0;
this.d.block = 0;
this.d.outlen = 0;
- this.d.i_ptr = this.d.i_end = &d.input[0];
+ this.d.i_ptr = this.d.i_end = d.input;
// Read_error lasts for the lifetime of a decompressor
this.read_error = MSPACK_ERR.MSPACK_ERR_OK;
diff --git a/libmspack/CHM/Decompressor.cs b/libmspack/CHM/Decompressor.cs
index 078796f..da62113 100644
--- a/libmspack/CHM/Decompressor.cs
+++ b/libmspack/CHM/Decompressor.cs
@@ -1,4 +1,9 @@
using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using static SabreTools.Compression.libmspack.CHM.Constants;
+using static SabreTools.Compression.libmspack.macros;
+using static SabreTools.Compression.libmspack.system;
namespace SabreTools.Compression.libmspack.CHM
{
@@ -8,10 +13,30 @@ namespace SabreTools.Compression.libmspack.CHM
/// All fields are READ ONLY.
///
///
- public class Decompressor : BaseDecompressor
+ public unsafe class Decompressor : BaseDecompressor
{
public mschmd_decompress_state d { get; private set; }
+ // Filenames of the system files used for decompression.
+ // Content and ControlData are essential.
+ // ResetTable is preferred, but SpanInfo can be used if not available
+ private const string content_name = "::DataSpace/Storage/MSCompressed/Content";
+ private const string control_name = "::DataSpace/Storage/MSCompressed/ControlData";
+ private const string spaninfo_name = "::DataSpace/Storage/MSCompressed/SpanInfo";
+ private const string rtable_name = "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
+
+ // The GUIDs found in CHM header
+ private static readonly byte[] guids = new byte[32]
+ {
+ // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC}
+ 0x10, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
+ 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC,
+
+ // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC}
+ 0x11, 0xFD, 0x01, 0x7C, 0xAA, 0x7B, 0xD0, 0x11,
+ 0x9E, 0x0C, 0x00, 0xA0, 0xC9, 0x22, 0xE6, 0xEC
+ };
+
///
/// Creates a new CHM decompressor
///
@@ -56,7 +81,10 @@ namespace SabreTools.Compression.libmspack.CHM
///
/// A pointer to a mschmd_header structure, or null on failure
///
- public mschmd_header open(in string filename) => throw new NotImplementedException();
+ public mschmd_header open(in string filename)
+ {
+ return chmd_real_open(filename, 1);
+ }
///
/// Closes a previously opened CHM helpfile.
@@ -73,7 +101,364 @@ namespace SabreTools.Compression.libmspack.CHM
/// The CHM helpfile to close
///
///
- public void close(mschmd_header chm) => throw new NotImplementedException();
+ public void close(mschmd_header chm)
+ {
+ mschmd_file fi, nfi;
+ mspack_system sys;
+ uint i;
+
+ sys = this.system;
+
+ this.error = MSPACK_ERR.MSPACK_ERR_OK;
+
+ // Free files
+ for (fi = chm.files; fi != null; fi = nfi)
+ {
+ nfi = fi.next;
+ //sys.free(fi);
+ }
+ for (fi = chm.sysfiles; fi != null; fi = nfi)
+ {
+ nfi = fi.next;
+ //sys.free(fi);
+ }
+
+ // If this CHM was being decompressed, free decompression state
+ if (this.d != null && (this.d.chm == chm))
+ {
+ if (this.d.infh != null) sys.close(this.d.infh);
+ if (this.d.state != null) lzxd_free(this.d.state);
+ //sys.free(this.d);
+ this.d = null;
+ }
+
+ // If this CHM had a chunk cache, free it and contents
+ if (chm.chunk_cache != null)
+ {
+ for (i = 0; i < chm.num_chunks; i++) sys.free(chm.chunk_cache[i]);
+ sys.free(chm.chunk_cache);
+ }
+
+ //sys.free(chm);
+ }
+
+ ///
+ /// Reads the basic CHM file headers. If the "entire" parameter is
+ /// non-zero, all file entries will also be read. fills out a pre-existing
+ /// mschmd_header structure, allocates memory for files as necessary
+ ///
+ private MSPACK_ERR chmd_read_headers(mspack_system sys, mspack_file fh, mschmd_header chm, int entire)
+ {
+ uint errors, num_chunks;
+ FixedArray buf = new FixedArray(0x54);
+ FixedArray chunk = null;
+ byte* name, p, end;
+ mschmd_file fi, link = null;
+ long offset_hs0, filelen;
+ int num_entries;
+ MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK;
+
+ // Initialise pointers
+ chm.files = null;
+ chm.sysfiles = null;
+ chm.chunk_cache = null;
+ chm.sec0.chm = chm;
+ chm.sec0.id = 0;
+ chm.sec1.chm = chm;
+ chm.sec1.id = 1;
+ chm.sec1.content = null;
+ chm.sec1.control = null;
+ chm.sec1.spaninfo = null;
+ chm.sec1.rtable = null;
+
+ // Read the first header
+ if (sys.read(fh, buf, chmhead_SIZEOF) != chmhead_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ // Check ITSF signature
+ if (EndGetI32(buf, chmhead_Signature) != 0x46535449)
+ {
+ return MSPACK_ERR.MSPACK_ERR_SIGNATURE;
+ }
+
+ // Check both header GUIDs
+ if (!buf.ToArray().Skip(chmhead_GUID1).Take(guids.Length).SequenceEqual(guids))
+ {
+ Console.Error.WriteLine("Incorrect GUIDs");
+ return MSPACK_ERR.MSPACK_ERR_SIGNATURE;
+ }
+
+ chm.version = EndGetI32(buf, chmhead_Version);
+ chm.timestamp = EndGetM32(buf, chmhead_Timestamp);
+ chm.language = EndGetI32(buf, chmhead_LanguageID);
+ if (chm.version > 3)
+ {
+ sys.message(fh, "WARNING; CHM version > 3");
+ }
+
+ // Read the header section table
+ if (sys.read(fh, buf, chmhst3_SIZEOF) != chmhst3_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ // chmhst3_OffsetCS0 does not exist in version 1 or 2 CHM files.
+ // The offset will be corrected later, once HS1 is read.
+ if (read_off64(&offset_hs0, &buf[chmhst_OffsetHS0], sys, fh) ||
+ read_off64(&chm.dir_offset, &buf[chmhst_OffsetHS1], sys, fh) ||
+ read_off64(&chm.sec0.offset, &buf[chmhst3_OffsetCS0], sys, fh))
+ {
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // Seek to header section 0
+ if (sys.seek(fh, offset_hs0, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0)
+ {
+ return MSPACK_ERR.MSPACK_ERR_SEEK;
+ }
+
+ // Read header section 0
+ if (sys.read(fh, buf, chmhs0_SIZEOF) != chmhs0_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ if (read_off64(&chm.length, &buf[chmhs0_FileLen], sys, fh))
+ {
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // Compare declared CHM file size against actual size
+ if (mspack_sys_filelen(sys, fh, &filelen) == 0)
+ {
+ if (chm.length > filelen)
+ {
+ sys.message(fh, $"WARNING; file possibly truncated by {chm.length - filelen} bytes");
+ }
+ else if (chm.length < filelen)
+ {
+ sys.message(fh, $"WARNING; possible {filelen - chm.length} extra bytes at end of file");
+ }
+ }
+
+ // Seek to header section 1
+ if (sys.seek(fh, chm.dir_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0)
+ {
+ return MSPACK_ERR.MSPACK_ERR_SEEK;
+ }
+
+ // Read header section 1
+ if (sys.read(fh, buf, chmhs1_SIZEOF) != chmhs1_SIZEOF)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ chm.dir_offset = sys.tell(fh);
+ chm.chunk_size = EndGetI32(buf, chmhs1_ChunkSize);
+ chm.density = EndGetI32(buf, chmhs1_Density);
+ chm.depth = EndGetI32(buf, chmhs1_Depth);
+ chm.index_root = EndGetI32(buf, chmhs1_IndexRoot);
+ chm.num_chunks = EndGetI32(buf, chmhs1_NumChunks);
+ chm.first_pmgl = EndGetI32(buf, chmhs1_FirstPMGL);
+ chm.last_pmgl = EndGetI32(buf, chmhs1_LastPMGL);
+
+ if (chm.version < 3)
+ {
+ // Versions before 3 don't have chmhst3_OffsetCS0
+ chm.sec0.offset = chm.dir_offset + (chm.chunk_size * chm.num_chunks);
+ }
+
+ // Check if content offset or file size is wrong
+ if (chm.sec0.offset > chm.length)
+ {
+ Console.Error.WriteLine("content section begins after file has ended");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // Ensure there are chunks and that chunk size is
+ // large enough for signature and num_entries
+ if (chm.chunk_size < (pmgl_Entries + 2))
+ {
+ Console.Error.WriteLine("chunk size not large enough");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+ if (chm.num_chunks == 0)
+ {
+ Console.Error.WriteLine("no chunks");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // The chunk_cache data structure is not great; large values for num_chunks
+ // or num_chunks*chunk_size can exhaust all memory. Until a better chunk
+ // cache is implemented, put arbitrary limits on num_chunks and chunk size.
+ if (chm.num_chunks > 100000)
+ {
+ Console.Error.WriteLine("more than 100,000 chunks");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+ if (chm.chunk_size > 8192)
+ {
+ Console.Error.WriteLine("chunk size over 8192 (get in touch if this is valid)");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+ if ((long)chm.chunk_size * (long)chm.num_chunks > chm.length)
+ {
+ Console.Error.WriteLine("chunks larger than entire file");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // Common sense checks on header section 1 fields
+ if (chm.chunk_size != 4096)
+ {
+ sys.message(fh, "WARNING; chunk size is not 4096");
+ }
+ if (chm.first_pmgl != 0)
+ {
+ sys.message(fh, "WARNING; first PMGL chunk is not zero");
+ }
+ if (chm.first_pmgl > chm.last_pmgl)
+ {
+ Console.Error.WriteLine("first pmgl chunk is after last pmgl chunk");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+ if (chm.index_root != 0xFFFFFFFF && chm.index_root >= chm.num_chunks)
+ {
+ Console.Error.WriteLine("index_root outside valid range");
+ return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ // If we are doing a quick read, stop here!
+ if (entire == 0)
+ {
+ return MSPACK_ERR.MSPACK_ERR_OK;
+ }
+
+ // Seek to the first PMGL chunk, and reduce the number of chunks to read
+ if (chm.first_pmgl != 0)
+ {
+ long pmgl_offset = (long)chm.first_pmgl * (long)chm.chunk_size;
+ if (sys.seek(fh, pmgl_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0)
+ {
+ return MSPACK_ERR.MSPACK_ERR_SEEK;
+ }
+ }
+ num_chunks = chm.last_pmgl - chm.first_pmgl + 1;
+
+ chunk = new FixedArray((int)chm.chunk_size);
+
+ // Read and process all chunks from FirstPMGL to LastPMGL
+ errors = 0;
+ while (num_chunks-- > 0)
+ {
+ // Read next chunk
+ if (sys.read(fh, chunk, (int)chm.chunk_size) != (int)chm.chunk_size)
+ {
+ sys.free(chunk);
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+
+ // Process only directory (PMGL) chunks
+ if (EndGetI32(chunk, pmgl_Signature) != 0x4C474D50) continue;
+
+ if (EndGetI32(chunk, pmgl_QuickRefSize) < 2)
+ {
+ sys.message(fh, "WARNING; PMGL quickref area is too small");
+ }
+ if (EndGetI32(chunk, pmgl_QuickRefSize) >
+ (chm.chunk_size - pmgl_Entries))
+ {
+ sys.message(fh, "WARNING; PMGL quickref area is too large");
+ }
+
+ p = (byte*)chunk.Pointer + pmgl_Entries;
+ end = (byte*)chunk.Pointer + chm.chunk_size - 2;
+ num_entries = EndGetI16(chunk, (int)(chm.chunk_size - 2));
+
+ while (num_entries-- > 0)
+ {
+ uint name_len, section;
+ long offset, length;
+ name_len = read_encint(&p, end, &err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK || (name_len > (uint)(end - p))) goto encint_err;
+ name = p; p += name_len;
+ section = read_encint(&p, end, &err);
+ offset = read_encint(&p, end, &err);
+ length = read_encint(&p, end, &err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err;
+
+ // Ignore blank or one-char (e.g. "/") filenames we'd return as blank */
+ if (name_len < 2 || name[0] == 0x00 || name[1] == 0x00) continue;
+
+ // Empty files and directory names are stored as a file entry at
+ // offset 0 with length 0. We want to keep empty files, but not
+ // directory names, which end with a "/"
+ if ((offset == 0) && (length == 0))
+ {
+ if ((name_len > 0) && (name[name_len - 1] == '/')) continue;
+ }
+
+ if (section > 1)
+ {
+ sys.message(fh, $"Invalid section number '{section}'.");
+ continue;
+ }
+
+ fi = new mschmd_file();
+ fi.next = null;
+ fi.section = (section == 0 ? (mschmd_section)chm.sec0 : (mschmd_section)chm.sec1);
+ fi.offset = offset;
+ fi.length = length;
+
+ char[] filenameArr = new char[name_len];
+ Marshal.Copy((IntPtr)name, filenameArr, 0, (int)name_len);
+ filenameArr[(int)name_len] = '\0';
+ fi.filename = new string(filenameArr);
+
+ if (name[0] == ':' && name[1] == ':')
+ {
+ // System file
+ if (name_len == 40 && fi.filename.StartsWith(content_name))
+ {
+ chm.sec1.content = fi;
+ }
+ else if (name_len == 44 && fi.filename.StartsWith(control_name))
+ {
+ chm.sec1.control = fi;
+ }
+ else if (name_len == 41 && fi.filename.StartsWith(spaninfo_name))
+ {
+ chm.sec1.spaninfo = fi;
+ }
+ else if (name_len == 105 && fi.filename.StartsWith(rtable_name))
+ {
+ chm.sec1.rtable = fi;
+ }
+ fi.next = chm.sysfiles;
+ chm.sysfiles = fi;
+ }
+ else
+ {
+ // Normal file
+ if (link != null) link.next = fi; else chm.files = fi;
+ link = fi;
+ }
+ }
+
+ // This is reached either when num_entries runs out, or if
+ // an ENCINT is badly encoded
+ encint_err:
+ if (num_entries >= 0)
+ {
+ Console.Error.WriteLine("bad encint before all entries could be read");
+ errors++;
+ }
+ }
+
+ sys.free(chunk);
+ return (errors > 0) ? MSPACK_ERR.MSPACK_ERR_DATAFORMAT : MSPACK_ERR.MSPACK_ERR_OK;
+ }
///
/// Extracts a file from a CHM helpfile.
@@ -125,9 +510,56 @@ namespace SabreTools.Compression.libmspack.CHM
/// A pointer to a mschmd_header structure, or null on failure
///
///
- ///
+ ///
///
- public mschmd_header fast_open(in string filename) => throw new NotImplementedException();
+ public mschmd_header fast_open(in string filename)
+ {
+ return chmd_real_open(filename, 0);
+ }
+
+ ///
+ /// The real implementation of chmd_open() and chmd_fast_open(). It simply
+ /// passes the "entire" parameter to chmd_read_headers(), which will then
+ /// either read all headers, or a bare mininum.
+ ///
+ private mschmd_header chmd_real_open(in string filename, int entire)
+ {
+ mschmd_header chm = null;
+ MSPACK_ERR error;
+
+ mspack_system sys = this.system;
+
+ mspack_file fh;
+ if ((fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) != null)
+ {
+ chm = new mschmd_header();
+ chm.filename = filename;
+ error = chmd_read_headers(sys, fh, chm, entire);
+ if (error != MSPACK_ERR.MSPACK_ERR_OK)
+ {
+ // If the error is DATAFORMAT, and there are some results, return
+ // partial results with a warning, rather than nothing
+ if (error == MSPACK_ERR.MSPACK_ERR_DATAFORMAT && (chm.files != null || chm.sysfiles != null))
+ {
+ sys.message(fh, "WARNING; contents are corrupt");
+ error = MSPACK_ERR.MSPACK_ERR_OK;
+ }
+ else
+ {
+ close(chm);
+ chm = null;
+ }
+ }
+
+ this.error = error;
+ sys.close(fh);
+ }
+ else
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_OPEN;
+ }
+ return chm;
+ }
///
/// Finds file details quickly.
@@ -161,12 +593,330 @@ namespace SabreTools.Compression.libmspack.CHM
/// The CHM helpfile to search for the file
/// The filename of the file to search for
/// A pointer to a caller-provded mschmd_file structure
- /// sizeof(struct mschmd_file)
+ /// sizeof(mschmd_file)
/// An error code, or MSPACK_ERR_OK if successful
///
///
- ///
+ ///
///
- public MSPACK_ERR fast_find(mschmd_header chm, in string filename, mschmd_file f_ptr, int f_size) => throw new NotImplementedException();
+ public MSPACK_ERR fast_find(mschmd_header chm, in string filename, ref mschmd_file f_ptr, int f_size)
+ {
+ mspack_system sys;
+ mspack_file fh;
+
+ // p and end are initialised to prevent MSVC warning about "potentially"
+ // uninitialised usage. This is provably untrue, but MS won't fix:
+ // https://developercommunity.visualstudio.com/content/problem/363489/c4701-false-positive-warning.html
+ FixedArray chunk;
+ byte* p = null, end = null;
+ MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK;
+ int result = -1;
+ uint n, sec;
+
+ if (chm == null || f_ptr == null)
+ {
+ return MSPACK_ERR.MSPACK_ERR_ARGS;
+ }
+
+ sys = this.system;
+
+ // Clear the results structure
+ f_ptr = new mschmd_file();
+
+ if ((fh = sys.open(chm.filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ)) == null)
+ {
+ return MSPACK_ERR.MSPACK_ERR_OPEN;
+ }
+
+ // Go through PMGI chunk hierarchy to reach PMGL chunk
+ if (chm.index_root < chm.num_chunks)
+ {
+ n = chm.index_root;
+ for (; ; )
+ {
+ if ((chunk = read_chunk(chm, fh, n)) == null)
+ {
+ sys.close(fh);
+ return this.error;
+ }
+
+ // Search PMGI/PMGL chunk. exit early if no entry found
+ if ((result = search_chunk(chm, chunk, filename, &p, &end)) <= 0)
+ {
+ break;
+ }
+
+ // Found result. loop around for next chunk if this is PMGI
+ if (chunk[3] == 0x4C) break;
+
+ n = read_encint(&p, end, &err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err;
+ }
+ }
+ else
+ {
+ // PMGL chunks only, search from first_pmgl to last_pmgl
+ for (n = chm.first_pmgl; n <= chm.last_pmgl; n = EndGetI32(chunk, pmgl_NextChunk))
+ {
+ if ((chunk = read_chunk(chm, fh, n)) == null)
+ {
+ err = this.error;
+ break;
+ }
+
+ // Search PMGL chunk. exit if file found
+ if ((result = search_chunk(chm, chunk, filename, &p, &end)) > 0)
+ {
+ break;
+ }
+
+ // Stop simple infinite loops: can't visit the same chunk twice
+ if (n == EndGetI32(chunk, pmgl_NextChunk))
+ {
+ break;
+ }
+ }
+ }
+
+ // If we found a file, read it
+ if (result > 0)
+ {
+ sec = read_encint(&p, end, &err);
+ f_ptr.section = (sec == 0) ? (mschmd_section)chm.sec0 : (mschmd_section)chm.sec1;
+ f_ptr.offset = read_encint(&p, end, &err);
+ f_ptr.length = read_encint(&p, end, &err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK) goto encint_err;
+ }
+ else if (result < 0)
+ {
+ err = MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ sys.close(fh);
+ return this.error = err;
+
+ encint_err:
+ Console.Error.WriteLine("Bad encint in PGMI/PGML chunk");
+ sys.close(fh);
+ return this.error = MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ }
+
+ ///
+ /// Reads the given chunk into memory, storing it in a chunk cache
+ /// so it doesn't need to be read from disk more than once
+ ///
+ ///
+ private FixedArray read_chunk(mschmd_header chm, mspack_file fh, uint chunk_num)
+ {
+ mspack_system sys = this.system;
+ FixedArray buf;
+
+ // Check arguments - most are already checked by chmd_fast_find
+ if (chunk_num >= chm.num_chunks) return null;
+
+ // Ensure chunk cache is available
+ if (chm.chunk_cache == null)
+ {
+ chm.chunk_cache = new FixedArray[chm.num_chunks];
+ if (chm.chunk_cache == null)
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
+ return null;
+ }
+ }
+
+ // Try to answer out of chunk cache
+ if (chm.chunk_cache[chunk_num] != null) return chm.chunk_cache[chunk_num];
+
+ // Need to read chunk - allocate memory for it
+ buf = new FixedArray((int)chm.chunk_size);
+
+ // Seek to block and read it
+ if (sys.seek(fh, chm.dir_offset + (chunk_num * chm.chunk_size), MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0)
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_SEEK;
+ sys.free(buf);
+ return null;
+ }
+ if (sys.read(fh, buf, (int)chm.chunk_size) != (int)chm.chunk_size)
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_READ;
+ sys.free(buf);
+ return null;
+ }
+
+ // Check the signature. Is is PMGL or PMGI?
+ if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && ((buf[3] == 0x4C) || (buf[3] == 0x49))))
+ {
+ this.error = MSPACK_ERR.MSPACK_ERR_SEEK;
+ sys.free(buf);
+ return null;
+ }
+
+ // All OK. Store chunk in cache and return it
+ return chm.chunk_cache[chunk_num] = buf;
+ }
+
+ ///
+ /// Searches a PMGI/PMGL chunk for a given filename entry. Returns -1 on
+ /// data format error, 0 if entry definitely not found, 1 if entry
+ /// found. In the latter case, *result and *result_end are set pointing
+ /// to that entry's data (either the "next chunk" ENCINT for a PMGI or
+ /// the section, offset and length ENCINTs for a PMGL).
+ ///
+ /// In the case of PMGL chunks, the entry has definitely been
+ /// found. In the case of PMGI chunks, the entry which points to the
+ /// chunk that may eventually contain that entry has been found.
+ ///
+ ///
+ private int search_chunk(mschmd_header chm, in FixedArray chunk, in string filename, byte** result, byte** result_end)
+ {
+ byte* p;
+ uint qr_size, num_entries, qr_entries, qr_density, name_len;
+ uint L, R, M, entries_off, is_pmgl;
+ int cmp;
+ MSPACK_ERR err = MSPACK_ERR.MSPACK_ERR_OK;
+
+ uint fname_len = (uint)filename.Length;
+
+ // PMGL chunk or PMGI chunk? (note: read_chunk() has already
+ // checked the rest of the characters in the chunk signature)
+ if (chunk[3] == 0x4C)
+ {
+ is_pmgl = 1;
+ entries_off = pmgl_Entries;
+ }
+ else
+ {
+ is_pmgl = 0;
+ entries_off = pmgi_Entries;
+ }
+
+ // Step 1: binary search first filename of each QR entry
+ // - target filename == entry
+ // found file
+ // - target filename < all entries
+ // file not found
+ // - target filename > all entries
+ // proceed to step 2 using final entry
+ // - target filename between two searched entries
+ // proceed to step 2
+ qr_size = EndGetI32(chunk, pmgl_QuickRefSize);
+ int start = (int)(chm.chunk_size - 2);
+ int end = (int)(chm.chunk_size - qr_size);
+ num_entries = EndGetI16(chunk, (int)(chm.chunk_size - 2));
+ qr_density = (uint)(1 + (1 << (int)chm.density));
+ qr_entries = (num_entries + qr_density - 1) / qr_density;
+
+ if (num_entries == 0)
+ {
+ Console.Error.WriteLine("Chunk has no entries");
+ return -1;
+ }
+
+ if (qr_size > chm.chunk_size)
+ {
+ Console.Error.WriteLine("Quickref size > chunk size");
+ return -1;
+ }
+
+ *result_end = &chunk[end];
+
+ if (((int)qr_entries * 2) > (start - end))
+ {
+ Console.Error.WriteLine("WARNING; more quickrefs than quickref space");
+ qr_entries = 0; // But we can live with it
+ }
+
+ if (qr_entries > 0)
+ {
+ L = 0;
+ R = qr_entries - 1;
+ do
+ {
+ // Pick new midpoint
+ M = (L + R) >> 1;
+
+ // Compare filename with entry QR points to
+ p = &chunk[entries_off + (M != 0 ? EndGetI16(chunk, start - (int)(M << 1)) : 0)];
+ name_len = read_encint(&p, end, &err);
+ if (err != MSPACK_ERR.MSPACK_ERR_OK || (name_len > (uint)(end - p))) goto encint_err;
+ cmp = compare(filename, (char*)p, fname_len, name_len);
+
+ if (cmp == 0) break;
+ else if (cmp < 0) { if (M) R = M - 1; else return 0; }
+ else if (cmp > 0) L = M + 1;
+ } while (L <= R);
+ M = (L + R) >> 1;
+
+ if (cmp == 0)
+ {
+ /* exact match! */
+ p += name_len;
+ *result = p;
+ return 1;
+ }
+
+ /* otherwise, read the group of entries for QR entry M */
+ p = &chunk[entries_off + (M ? EndGetI16(chunk, start - (M << 1)) : 0)];
+ num_entries -= (M * qr_density);
+ if (num_entries > qr_density) num_entries = qr_density;
+ }
+ else
+ {
+ p = &chunk[entries_off];
+ }
+
+ /* Step 2: linear search through the set of entries reached in step 1.
+ * - filename == any entry
+ * found entry
+ * - filename < all entries (PMGI) or any entry (PMGL)
+ * entry not found, stop now
+ * - filename > all entries
+ * entry not found (PMGL) / maybe found (PMGI)
+ * -
+ */
+ *result = null;
+ while (num_entries-- > 0)
+ {
+ name_len = read_encint(&p, end, &err);
+ if (err || (name_len > (uint)(end - p))) goto encint_err;
+ cmp = compare(filename, (char*)p, fname_len, name_len);
+ p += name_len;
+
+ if (cmp == 0)
+ {
+ /* entry found */
+ *result = p;
+ return 1;
+ }
+
+ if (cmp < 0)
+ {
+ /* entry not found (PMGL) / maybe found (PMGI) */
+ break;
+ }
+
+ /* read and ignore the rest of this entry */
+ if (is_pmgl)
+ {
+ while (p < end && (*p++ & 0x80)) ; /* skip section ENCINT */
+ while (p < end && (*p++ & 0x80)) ; /* skip offset ENCINT */
+ while (p < end && (*p++ & 0x80)) ; /* skip length ENCINT */
+ }
+ else
+ {
+ *result = p; /* store potential final result */
+ while (p < end && (*p++ & 0x80)) ; /* skip chunk number ENCINT */
+ }
+ }
+
+ /* PMGL? not found. PMGI? maybe found */
+ return (is_pmgl) ? 0 : (*result ? 1 : 0);
+
+ encint_err:
+ Console.Error.WriteLine("bad encint while searching");
+ return -1;
+ }
}
}
\ No newline at end of file
diff --git a/libmspack/CHM/mschmd_header.cs b/libmspack/CHM/mschmd_header.cs
index 9b78b4a..844e5bc 100644
--- a/libmspack/CHM/mschmd_header.cs
+++ b/libmspack/CHM/mschmd_header.cs
@@ -115,6 +115,6 @@ namespace SabreTools.Compression.libmspack
/// A cache of loaded chunks, filled in by mschm_decoder::fast_find().
/// Available only in CHM decoder version 2 and above.
///
- public byte** chunk_cache { get; set; }
+ public FixedArray[] chunk_cache { get; set; }
}
}
\ No newline at end of file
diff --git a/libmspack/FixedArray.cs b/libmspack/FixedArray.cs
index 052d622..9493f11 100644
--- a/libmspack/FixedArray.cs
+++ b/libmspack/FixedArray.cs
@@ -5,18 +5,35 @@ namespace SabreTools.Compression.libmspack
{
public unsafe class FixedArray where T : struct
{
+ ///
+ /// Direct access to the internal pointer
+ ///
public IntPtr Pointer { get; private set; }
- int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }
+ ///
+ /// Size of the T object
+ ///
+ private int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }
+
+ ///
+ /// Length of the fixed array
+ ///
+ private int _length;
public T this[int i]
{
get
{
+ if (i < 0 || i >= _length)
+ return default;
+
return (T)Marshal.PtrToStructure(Pointer + i * sizeofT, typeof(T));
}
set
{
+ if (i < 0 || i >= _length)
+ return;
+
Marshal.StructureToPtr(value, Pointer + i * sizeofT, false);
}
}
@@ -24,6 +41,7 @@ namespace SabreTools.Compression.libmspack
public FixedArray(int length)
{
Pointer = Marshal.AllocHGlobal(sizeofT * length);
+ _length = 0;
}
~FixedArray()
@@ -31,10 +49,17 @@ namespace SabreTools.Compression.libmspack
Marshal.FreeHGlobal(Pointer);
}
+ public static implicit operator T*(FixedArray arr) => (T*)arr.Pointer;
+
+ public static implicit operator T[](FixedArray arr) => arr.ToArray();
+
///
public bool SequenceEqual(T[] arr)
{
- for (int i = 0; i < arr.Length; i++)
+ if (arr.Length < _length)
+ return false;
+
+ for (int i = 0; i < _length; i++)
{
if (!this[i].Equals(arr[i]))
return false;
@@ -42,5 +67,20 @@ namespace SabreTools.Compression.libmspack
return true;
}
+
+ ///
+ /// Convert the unmanaged data to an array
+ ///
+ /// Array created from the pointer data
+ public T[] ToArray()
+ {
+ T[] arr = new T[_length];
+ for (int i = 0; i < _length; i++)
+ {
+ arr[i] = this[i];
+ }
+
+ return arr;
+ }
}
}
\ No newline at end of file
diff --git a/libmspack/KWAJ/Decompressor.cs b/libmspack/KWAJ/Decompressor.cs
index 8cf6c4b..17972ca 100644
--- a/libmspack/KWAJ/Decompressor.cs
+++ b/libmspack/KWAJ/Decompressor.cs
@@ -1,5 +1,6 @@
using System;
using static SabreTools.Compression.libmspack.KWAJ.Constants;
+using static SabreTools.Compression.libmspack.macros;
namespace SabreTools.Compression.libmspack.KWAJ
{
@@ -101,11 +102,11 @@ namespace SabreTools.Compression.libmspack.KWAJ
///
private MSPACK_ERR ReadHeaders(mspack_system sys, mspack_file fh, mskwajd_header hdr)
{
- byte[] buf = new byte[16];
+ FixedArray buf = new FixedArray(16);
int i;
// Read in the header
- if (sys.read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF)
+ if (sys.read(fh, buf, kwajh_SIZEOF) != kwajh_SIZEOF)
{
return MSPACK_ERR.MSPACK_ERR_READ;
}
@@ -131,7 +132,7 @@ namespace SabreTools.Compression.libmspack.KWAJ
// 4 bytes: length of unpacked file
if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASLENGTH))
{
- if (sys.read(fh, &buf[0], 4) != 4)
+ if (sys.read(fh, buf, 4) != 4)
return MSPACK_ERR.MSPACK_ERR_READ;
hdr.length = BitConverter.ToUInt32(buf, 0);
@@ -140,14 +141,14 @@ namespace SabreTools.Compression.libmspack.KWAJ
// 2 bytes: unknown purpose
if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASUNKNOWN1))
{
- if (sys.read(fh, &buf[0], 2) != 2)
+ if (sys.read(fh, buf, 2) != 2)
return MSPACK_ERR.MSPACK_ERR_READ;
}
// 2 bytes: length of section, then [length] bytes: unknown purpose
if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASUNKNOWN2))
{
- if (sys.read(fh, &buf[0], 2) != 2)
+ if (sys.read(fh, buf, 2) != 2)
return MSPACK_ERR.MSPACK_ERR_READ;
i = BitConverter.ToUInt16(buf, 0);
if (sys.seek(fh, i, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_CUR) != 0)
@@ -168,7 +169,7 @@ namespace SabreTools.Compression.libmspack.KWAJ
if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASFILENAME))
{
// Read and copy up to 9 bytes of a null terminated string
- if ((len = sys.read(fh, &buf[0], 9)) < 2)
+ if ((len = sys.read(fh, buf, 9)) < 2)
return MSPACK_ERR.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
@@ -192,7 +193,7 @@ namespace SabreTools.Compression.libmspack.KWAJ
*fn++ = '.';
// Read and copy up to 4 bytes of a null terminated string
- if ((len = sys.read(fh, &buf[0], 4)) < 2)
+ if ((len = sys.read(fh, buf, 4)) < 2)
return MSPACK_ERR.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
@@ -215,10 +216,10 @@ namespace SabreTools.Compression.libmspack.KWAJ
// 2 bytes: extra text length then [length] bytes of extra text data
if (hdr.headers.HasFlag(MSKWAJ_HDR.MSKWAJ_HDR_HASEXTRATEXT))
{
- if (sys.read(fh, &buf[0], 2) != 2)
+ if (sys.read(fh, buf, 2) != 2)
return MSPACK_ERR.MSPACK_ERR_READ;
- i = BitConverter.ToUInt16(&buf[0]);
+ i = EndGetI16(buf, 0);
hdr.extra = (char*)sys.alloc(i + 1);
if (hdr.extra == null)
return MSPACK_ERR.MSPACK_ERR_NOMEMORY;
diff --git a/libmspack/None/DecompressState.cs b/libmspack/None/DecompressState.cs
index c429a99..fde6e1b 100644
--- a/libmspack/None/DecompressState.cs
+++ b/libmspack/None/DecompressState.cs
@@ -36,8 +36,8 @@ namespace SabreTools.Compression.libmspack.None
while (bytes > 0)
{
int run = (bytes > s.BufferSize) ? s.BufferSize : (int)bytes;
- if (s.InternalSystem.read(s.Input, &s.Buffer[0], run) != run) return MSPACK_ERR.MSPACK_ERR_READ;
- if (s.InternalSystem.write(s.Output, &s.Buffer[0], run) != run) return MSPACK_ERR.MSPACK_ERR_WRITE;
+ if (s.InternalSystem.read(s.Input, s.Buffer, run) != run) return MSPACK_ERR.MSPACK_ERR_READ;
+ if (s.InternalSystem.write(s.Output, s.Buffer, run) != run) return MSPACK_ERR.MSPACK_ERR_WRITE;
bytes -= run;
}
diff --git a/libmspack/None/State.cs b/libmspack/None/State.cs
index 3d7f401..1b87b68 100644
--- a/libmspack/None/State.cs
+++ b/libmspack/None/State.cs
@@ -11,7 +11,7 @@ namespace SabreTools.Compression.libmspack.None
public mspack_file Output { get; private set; }
- public byte* Buffer { get; private set; }
+ public FixedArray Buffer { get; private set; }
public int BufferSize { get; private set; }
@@ -20,7 +20,7 @@ namespace SabreTools.Compression.libmspack.None
this.InternalSystem = sys;
this.Input = infh;
this.Output = outfh;
- this.Buffer = (byte*)new FixedArray(bufsize).Pointer;
+ this.Buffer = new FixedArray(bufsize);
this.BufferSize = bufsize;
}
diff --git a/libmspack/OAB/Decompressor.cs b/libmspack/OAB/Decompressor.cs
index bff36d9..5f0e879 100644
--- a/libmspack/OAB/Decompressor.cs
+++ b/libmspack/OAB/Decompressor.cs
@@ -45,8 +45,8 @@ namespace SabreTools.Compression.libmspack.OAB
mspack_system sys;
mspack_file infh = null;
mspack_file outfh = null;
- byte* buf = null;
- byte[] hdrbuf = new byte[oabhead_SIZEOF];
+ FixedArray buf = null;
+ FixedArray hdrbuf = new FixedArray(oabhead_SIZEOF);
uint block_max, target_size;
lzxd_stream lzx = null;
OABSystem oabd_sys;
@@ -62,7 +62,7 @@ namespace SabreTools.Compression.libmspack.OAB
goto outlbl;
}
- if (sys.read(infh, &hdrbuf[0], oabhead_SIZEOF) != oabhead_SIZEOF)
+ if (sys.read(infh, hdrbuf, oabhead_SIZEOF) != oabhead_SIZEOF)
{
ret = MSPACK_ERR.MSPACK_ERR_READ;
goto outlbl;
@@ -85,7 +85,7 @@ namespace SabreTools.Compression.libmspack.OAB
goto outlbl;
}
- buf = (byte*)sys.alloc(this.buf_size);
+ buf = new FixedArray(this.buf_size);
if (buf == null)
{
ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
@@ -212,7 +212,7 @@ namespace SabreTools.Compression.libmspack.OAB
{
mspack_file infh = null, basefh = null, outfh = null;
lzxd_stream lzx = null;
- byte* buf = null;
+ FixedArray buf = null;
uint window_bits, window_size;
MSPACK_ERR ret = MSPACK_ERR.MSPACK_ERR_OK;
@@ -226,7 +226,7 @@ namespace SabreTools.Compression.libmspack.OAB
}
FixedArray hdrbuf = new FixedArray(patchhead_SIZEOF);
- if (sys.read(infh, (byte*)hdrbuf.Pointer, patchhead_SIZEOF) != patchhead_SIZEOF)
+ if (sys.read(infh, hdrbuf, patchhead_SIZEOF) != patchhead_SIZEOF)
{
ret = MSPACK_ERR.MSPACK_ERR_READ;
goto outlbl;
@@ -260,7 +260,7 @@ namespace SabreTools.Compression.libmspack.OAB
goto outlbl;
}
- buf = (byte*)sys.alloc(this.buf_size);
+ buf = new FixedArray(this.buf_size);
if (buf == null)
{
ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
@@ -343,7 +343,7 @@ namespace SabreTools.Compression.libmspack.OAB
if (outfh != null) sys.close(outfh);
if (basefh != null) sys.close(basefh);
if (infh != null) sys.close(infh);
- if (buf != null) sys.free(buf);
+ //if (buf != null) sys.free(buf);
return ret;
}
diff --git a/libmspack/Quantum/qtm.cs b/libmspack/Quantum/qtm.cs
index 024f04e..4bf9c7e 100644
--- a/libmspack/Quantum/qtm.cs
+++ b/libmspack/Quantum/qtm.cs
@@ -113,22 +113,6 @@ namespace SabreTools.Compression.libmspack
}
}
- ///
- /// 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.
///
@@ -198,15 +182,15 @@ namespace SabreTools.Compression.libmspack
// - 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);
+ qtm.model0 = new qtmd_model(qtm.m0sym, 0, 64);
+ qtm.model1 = new qtmd_model(qtm.m1sym, 64, 64);
+ qtm.model2 = new qtmd_model(qtm.m2sym, 128, 64);
+ qtm.model3 = new qtmd_model(qtm.m3sym, 192, 64);
+ qtm.model4 = new qtmd_model(qtm.m4sym, 0, (i > 24) ? 24 : i);
+ qtm.model5 = new qtmd_model(qtm.m5sym, 0, (i > 36) ? 36 : i);
+ qtm.model6 = new qtmd_model(qtm.m6sym, 0, i);
+ qtm.model6len = new qtmd_model(qtm.m6lsym, 0, 27);
+ qtm.model7 = new qtmd_model(qtm.m7sym, 0, 7);
// All ok
return qtm;
diff --git a/libmspack/Quantum/qtmd_model.cs b/libmspack/Quantum/qtmd_model.cs
index 1068f90..b08fc7d 100644
--- a/libmspack/Quantum/qtmd_model.cs
+++ b/libmspack/Quantum/qtmd_model.cs
@@ -6,6 +6,22 @@ namespace SabreTools.Compression.libmspack
public int entries { get; set; }
- public qtmd_modelsym* syms { get; set; }
+ public qtmd_modelsym[] syms { get; set; }
+
+ ///
+ /// Initialises a model to decode symbols from [start] to [start]+[len]-1
+ ///
+ public qtmd_model(qtmd_modelsym[] syms, int start, int len)
+ {
+ this.shiftsleft = 4;
+ this.entries = len;
+ this.syms = syms;
+
+ for (int i = 0; i <= len; i++)
+ {
+ syms[i].sym = (ushort)(start + i); // Actual symbol
+ syms[i].cumfreq = (ushort)(len - i); // Current frequency of that symbol
+ }
+ }
}
}
\ No newline at end of file
diff --git a/libmspack/SZDD/Decompressor.cs b/libmspack/SZDD/Decompressor.cs
index 6ff5f09..a1bc5ed 100644
--- a/libmspack/SZDD/Decompressor.cs
+++ b/libmspack/SZDD/Decompressor.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using static SabreTools.Compression.libmspack.macros;
using static SabreTools.Compression.libmspack.SZDD.Constants;
@@ -112,7 +111,7 @@ namespace SabreTools.Compression.libmspack.SZDD
FixedArray buf = new FixedArray(8);
// Read and check signature
- if (sys.read(fh, (byte*)buf.Pointer, 8) != 8) return MSPACK_ERR.MSPACK_ERR_READ;
+ if (sys.read(fh, buf, 8) != 8) return MSPACK_ERR.MSPACK_ERR_READ;
if (buf.SequenceEqual(szdd_signature_expand))
{
@@ -120,7 +119,7 @@ namespace SabreTools.Compression.libmspack.SZDD
hdr.format = MSSZDD_FMT.MSSZDD_FMT_NORMAL;
// Read the rest of the header
- if (sys.read(fh, (byte*)buf.Pointer, 6) != 6) return MSPACK_ERR.MSPACK_ERR_READ;
+ if (sys.read(fh, buf, 6) != 6) return MSPACK_ERR.MSPACK_ERR_READ;
if (buf[0] != 0x41) return MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
hdr.missing_char = (char)buf[1];
hdr.length = EndGetI32(buf, 2);
@@ -129,7 +128,7 @@ namespace SabreTools.Compression.libmspack.SZDD
{
// Special QBasic SZDD
hdr.format = MSSZDD_FMT.MSSZDD_FMT_QBASIC;
- if (sys.read(fh, (byte*)buf.Pointer, 4) != 4) return MSPACK_ERR.MSPACK_ERR_READ;
+ if (sys.read(fh, buf, 4) != 4) return MSPACK_ERR.MSPACK_ERR_READ;
hdr.missing_char = '\0';
hdr.length = EndGetI32(buf, 0);
}