mirror of
https://github.com/SabreTools/SabreTools.Compression.git
synced 2026-07-09 02:16:47 +00:00
Checkpoint (nw)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// 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<byte> buf = new FixedArray<byte>(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<char> strchr = new FixedArray<char>(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<byte> 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<byte>(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 <see cref="search(in string)">, to make it easier to
|
||||
/// break out of the loop and be sure that all resources are freed
|
||||
/// </summary>
|
||||
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<byte> 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;
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <see cref="mspack.DestroyCHMDecomperssor(Decompressor)"/>
|
||||
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
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new CHM decompressor
|
||||
/// </summary>
|
||||
@@ -56,7 +81,10 @@ namespace SabreTools.Compression.libmspack.CHM
|
||||
/// </param>
|
||||
/// <returns>A pointer to a mschmd_header structure, or null on failure</returns>
|
||||
/// <see cref="close(mschmd_header)"/>
|
||||
public mschmd_header open(in string filename) => throw new NotImplementedException();
|
||||
public mschmd_header open(in string filename)
|
||||
{
|
||||
return chmd_real_open(filename, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes a previously opened CHM helpfile.
|
||||
@@ -73,7 +101,364 @@ namespace SabreTools.Compression.libmspack.CHM
|
||||
/// <param name="chm">The CHM helpfile to close</param>
|
||||
/// <see cref="open(in string)"/>
|
||||
/// <see cref="fast_open(in string)"/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
private MSPACK_ERR chmd_read_headers(mspack_system sys, mspack_file fh, mschmd_header chm, int entire)
|
||||
{
|
||||
uint errors, num_chunks;
|
||||
FixedArray<byte> buf = new FixedArray<byte>(0x54);
|
||||
FixedArray<byte> 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<byte>((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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a file from a CHM helpfile.
|
||||
@@ -125,9 +510,56 @@ namespace SabreTools.Compression.libmspack.CHM
|
||||
/// <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)"/>
|
||||
/// <see cref="fast_find(mschmd_header, in string, ref mschmd_file, int)"/>
|
||||
/// <see cref="extract(mschmd_file, in string)"/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds file details quickly.
|
||||
@@ -161,12 +593,330 @@ namespace SabreTools.Compression.libmspack.CHM
|
||||
/// <param name="chm">The CHM helpfile to search for the file</param>
|
||||
/// <param name="filename">The filename of the file to search for</param>
|
||||
/// <param name="f_ptr">A pointer to a caller-provded mschmd_file structure</param>
|
||||
/// <param name="f_size"><tt>sizeof(struct mschmd_file)</tt></param>
|
||||
/// <param name="f_size"><tt>sizeof(mschmd_file)</tt></param>
|
||||
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
|
||||
/// <see cref="open(in string)"/>
|
||||
/// <see cref="close(mschmd_header)"/>
|
||||
/// <see cref="fast_find(mschmd_header, in string, mschmd_file, int)"/>
|
||||
/// <see cref="fast_find(mschmd_header, in string, ref mschmd_file, int)"/>
|
||||
/// <see cref="extract(mschmd_file, in string)"/>
|
||||
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<byte> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private FixedArray<byte> read_chunk(mschmd_header chm, mspack_file fh, uint chunk_num)
|
||||
{
|
||||
mspack_system sys = this.system;
|
||||
FixedArray<byte> 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<byte>[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<byte>((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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private int search_chunk(mschmd_header chm, in FixedArray<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public byte** chunk_cache { get; set; }
|
||||
public FixedArray<byte>[] chunk_cache { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,35 @@ namespace SabreTools.Compression.libmspack
|
||||
{
|
||||
public unsafe class FixedArray<T> where T : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// Direct access to the internal pointer
|
||||
/// </summary>
|
||||
public IntPtr Pointer { get; private set; }
|
||||
|
||||
int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }
|
||||
/// <summary>
|
||||
/// Size of the T object
|
||||
/// </summary>
|
||||
private int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the fixed array
|
||||
/// </summary>
|
||||
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<T> arr) => (T*)arr.Pointer;
|
||||
|
||||
public static implicit operator T[](FixedArray<T> arr) => arr.ToArray();
|
||||
|
||||
/// <inheritdoc cref="System.Linq.Enumerable.SequenceEqual{TSource}(System.Collections.Generic.IEnumerable{TSource}, System.Collections.Generic.IEnumerable{TSource})"/>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the unmanaged data to an array
|
||||
/// </summary>
|
||||
/// <returns>Array created from the pointer data</returns>
|
||||
public T[] ToArray()
|
||||
{
|
||||
T[] arr = new T[_length];
|
||||
for (int i = 0; i < _length; i++)
|
||||
{
|
||||
arr[i] = this[i];
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </summary>
|
||||
private MSPACK_ERR ReadHeaders(mspack_system sys, mspack_file fh, mskwajd_header hdr)
|
||||
{
|
||||
byte[] buf = new byte[16];
|
||||
FixedArray<byte> buf = new FixedArray<byte>(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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace SabreTools.Compression.libmspack.None
|
||||
|
||||
public mspack_file Output { get; private set; }
|
||||
|
||||
public byte* Buffer { get; private set; }
|
||||
public FixedArray<byte> 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<byte>(bufsize).Pointer;
|
||||
this.Buffer = new FixedArray<byte>(bufsize);
|
||||
this.BufferSize = bufsize;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<byte> buf = null;
|
||||
FixedArray<byte> hdrbuf = new FixedArray<byte>(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<byte>(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<byte> 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<byte> hdrbuf = new FixedArray<byte>(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<byte>(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;
|
||||
}
|
||||
|
||||
@@ -113,22 +113,6 @@ namespace SabreTools.Compression.libmspack
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.
|
||||
///
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a model to decode symbols from [start] to [start]+[len]-1
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<byte> buf = new FixedArray<byte>(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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user