diff --git a/libmspack/mspack.cs b/libmspack/mspack.cs index 318a26f..d45839a 100644 --- a/libmspack/mspack.cs +++ b/libmspack/mspack.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace SabreTools.Compression.libmspack { @@ -16,7 +17,25 @@ namespace SabreTools.Compression.libmspack /// /// A custom mspack_system structure, or NULL to use the default /// A or NULL - public static mscab_decompressor mspack_create_cab_decompressor(mspack_system sys) => throw new NotImplementedException(); + public static mscab_decompressor mspack_create_cab_decompressor(mspack_system sys) + { + if (sys == null) + return null; + + var self = new mscab_decompressor + { + system = sys, + d = null, + error = MSPACK_ERR.MSPACK_ERR_OK, + + searchbuf_size = 32768, + fix_mszip = 0, + buf_size = 4096, + salvage = 0, + }; + + return self; + } /// /// Destroys an existing CAB compressor. @@ -28,7 +47,21 @@ namespace SabreTools.Compression.libmspack /// Destroys an existing CAB decompressor. /// /// The to destroy - public static void mspack_destroy_cab_decompressor(mscab_decompressor self) => throw new NotImplementedException(); + public static void mspack_destroy_cab_decompressor(mscab_decompressor self) + { + if (self != null) + { + mspack_system sys = self.system; + if (self.d != null) + { + if (self.d.infh != null) sys.close(self.d.infh); + cabd_free_decomp(self); + //sys.free(self.d); + } + + //sys.free(self); + } + } /// /// Creates a new CHM compressor. @@ -120,7 +153,18 @@ namespace SabreTools.Compression.libmspack /// /// A custom mspack_system structure, or NULL to use the default /// A or NULL - public static msszdd_decompressor mspack_create_szdd_decompressor(mspack_system sys) => throw new NotImplementedException(); + public static msszdd_decompressor mspack_create_szdd_decompressor(mspack_system sys) + { + msszdd_decompressor self = null; + + if (sys == null) sys = new mspack_default_system(); + + self = new msszdd_decompressor(); + self.system = sys; + self.error = MSPACK_ERR.MSPACK_ERR_OK; + + return self; + } /// /// Destroys an existing SZDD compressor. @@ -132,7 +176,14 @@ namespace SabreTools.Compression.libmspack /// Destroys an existing SZDD decompressor. /// /// The to destroy - public static void mspack_destroy_szdd_decompressor(msszdd_decompressor self) => throw new NotImplementedException(); + public static void mspack_destroy_szdd_decompressor(msszdd_decompressor self) + { + if (self != null) + { + mspack_system sys = self.system; + //sys.free(self); + } + } /// /// Creates a new KWAJ compressor. diff --git a/libmspack/msszdd_decompressor.cs b/libmspack/msszdd_decompressor.cs index de22af0..10d377a 100644 --- a/libmspack/msszdd_decompressor.cs +++ b/libmspack/msszdd_decompressor.cs @@ -1,3 +1,6 @@ +using System.Linq; +using static SabreTools.Compression.libmspack.szdd; + namespace SabreTools.Compression.libmspack { /// @@ -7,7 +10,7 @@ namespace SabreTools.Compression.libmspack /// /// /// - public abstract class msszdd_decompressor + public unsafe class msszdd_decompressor { public mspack_system system { get; set; } @@ -19,7 +22,7 @@ namespace SabreTools.Compression.libmspack /// If the file opened is a valid SZDD file, all headers will be read and /// a msszddd_header structure will be returned. /// - /// In the case of an error occuring, NULL is returned and the error code + /// 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,9 +32,26 @@ namespace SabreTools.Compression.libmspack /// The filename of the SZDD compressed file. This is /// passed directly to mspack_system::open(). /// - /// A pointer to a msszddd_header structure, or NULL on failure + /// A pointer to a msszddd_header structure, or null on failure /// - public abstract msszddd_header open(in string filename); + public msszddd_header open(in string filename) + { + mspack_system sys = this.system; + + mspack_file fh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ); + msszddd_header hdr = new msszddd_header(); + hdr.fh = fh; + this.error = szddd_read_headers(sys, fh, hdr); + + if (this.error != MSPACK_ERR.MSPACK_ERR_OK) + { + if (fh != null) sys.close(fh); + //sys.free(hdr); + hdr = null; + } + + return hdr; + } /// /// Closes a previously opened SZDD file. @@ -42,7 +62,69 @@ namespace SabreTools.Compression.libmspack /// The SZDD header pointer is now invalid and cannot be used again. /// /// The SZDD file to close - public abstract void close(msszddd_header szdd); + public void close(msszddd_header szdd) + { + if (this.system == null) return; + + // Close the file handle associated + this.system.close(szdd.fh); + + // Free the memory associated + //this.system.free(hdr); + + this.error = MSPACK_ERR.MSPACK_ERR_OK; + } + + private static readonly byte[] szdd_signature_expand = new byte[8] + { + 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33 + }; + + private static readonly byte[] szdd_signature_qbasic = new byte[8] + { + 0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1 + }; + + /// + /// Reads the headers of an SZDD format file + /// + /// + /// + /// + /// + private static MSPACK_ERR szddd_read_headers(mspack_system sys, mspack_file fh, msszddd_header hdr) + { + byte[] buf = new byte[8]; + byte* bufPtr = libmspack.system.GetArrayPointer(buf); + + // Read and check signature + if (sys.read(fh, buffer: bufPtr, 8) != 8) return MSPACK_ERR.MSPACK_ERR_READ; + + if (buf.SequenceEqual(szdd_signature_expand)) + { + // Common SZDD + hdr.format = MSSZDD_FMT.MSSZDD_FMT_NORMAL; + + // Read the rest of the header + if (sys.read(fh, bufPtr, 6) != 6) return MSPACK_ERR.MSPACK_ERR_READ; + if (buf[0] != 0x41) return MSPACK_ERR.MSPACK_ERR_DATAFORMAT; + hdr.missing_char = (char)buf[1]; + hdr.length = EndGetI32(&buf[2]); + } + else if (buf.SequenceEqual(szdd_signature_qbasic)) + { + // Special QBasic SZDD + hdr.format = MSSZDD_FMT.MSSZDD_FMT_QBASIC; + if (sys.read(fh, bufPtr, 4) != 4) return MSPACK_ERR.MSPACK_ERR_READ; + hdr.missing_char = '\0'; + hdr.length = EndGetI32(buf); + } + else + { + return MSPACK_ERR.MSPACK_ERR_SIGNATURE; + } + return MSPACK_ERR.MSPACK_ERR_OK; + } /// /// Extracts the compressed data from a SZDD file. @@ -56,7 +138,38 @@ namespace SabreTools.Compression.libmspack /// is passed directly to mspack_system::open(). /// /// An error code, or MSPACK_ERR_OK if successful - public abstract MSPACK_ERR extract(msszddd_header szdd, in string filename); + public MSPACK_ERR extract(msszddd_header szdd, in string filename) + { + if (szdd == null) return this.error = MSPACK_ERR.MSPACK_ERR_ARGS; + mspack_system sys = this.system; + + mspack_file fh = szdd.fh; + + // Seek to the compressed data + long data_offset = (szdd.format == MSSZDD_FMT.MSSZDD_FMT_NORMAL) ? 14 : 12; + if (sys.seek(fh, data_offset, MSPACK_SYS_SEEK.MSPACK_SYS_SEEK_START) != 0) + { + return this.error = MSPACK_ERR.MSPACK_ERR_SEEK; + } + + // Open file for output + mspack_file outfh; + if ((outfh = sys.open(filename, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE)) == null) + { + return this.error = MSPACK_ERR.MSPACK_ERR_OPEN; + } + + // Decompress the data + this.error = lzss_decompress(sys, fh, outfh, SZDD_INPUT_SIZE, + szdd.format == MSSZDD_FMT.MSSZDD_FMT_NORMAL + ? LZSS_MODE.LZSS_MODE_EXPAND + : LZSS_MODE.LZSS_MODE_QBASIC); + + // Close output file + sys.close(outfh); + + return this.error; + } /// /// Decompresses an SZDD file to an output file in one step. @@ -76,7 +189,15 @@ namespace SabreTools.Compression.libmspack /// is passed directly to mspack_system::open(). /// /// An error code, or MSPACK_ERR_OK if successful - public abstract MSPACK_ERR decompress(in string input, in string output); + public MSPACK_ERR decompress(in string input, in string output) + { + msszddd_header hdr; + + if ((hdr = open(input)) == null) return this.error; + MSPACK_ERR error = extract(hdr, output); + close(hdr); + return this.error = error; + } /// /// Returns the error code set by the most recently called method. @@ -88,6 +209,9 @@ namespace SabreTools.Compression.libmspack /// /// /// - public abstract MSPACK_ERR last_error(); + public MSPACK_ERR last_error() + { + return this.error; + } } } \ No newline at end of file diff --git a/libmspack/system.cs b/libmspack/system.cs index a8bbffe..0cb029a 100644 --- a/libmspack/system.cs +++ b/libmspack/system.cs @@ -9,12 +9,12 @@ namespace SabreTools.Compression.libmspack { - public static class system + public unsafe static class system { /// /// Returns the length of a file opened for reading /// - public unsafe static MSPACK_ERR mspack_sys_filelen(mspack_system system, mspack_file file, long* length) + public static MSPACK_ERR mspack_sys_filelen(mspack_system system, mspack_file file, long* length) { if (system == null || file == null || length == null) return MSPACK_ERR.MSPACK_ERR_OPEN; @@ -38,5 +38,18 @@ namespace SabreTools.Compression.libmspack return MSPACK_ERR.MSPACK_ERR_OK; } + + public static T* CreateArray(int length) + { + T[] buf = new T[length]; + return GetArrayPointer(buf); + } + + public static T* GetArrayPointer(T[] arr) + { + var bufRef = __makeref(arr); + var bufPtr = **(System.IntPtr**)&bufRef; + return (T*)bufPtr.ToPointer(); + } } } \ No newline at end of file diff --git a/libmspack/szdd.cs b/libmspack/szdd.cs index 73f8861..d416f90 100644 --- a/libmspack/szdd.cs +++ b/libmspack/szdd.cs @@ -9,7 +9,7 @@ namespace SabreTools.Compression.libmspack { - public static class SZDD + public static class szdd { /// /// Input buffer size during decompression - not worth parameterising IMHO