diff --git a/libmspack/msoab_decompressor.cs b/libmspack/msoab_decompressor.cs
index a9f2466..933a114 100644
--- a/libmspack/msoab_decompressor.cs
+++ b/libmspack/msoab_decompressor.cs
@@ -1,3 +1,5 @@
+using static SabreTools.Compression.libmspack.oab;
+
namespace SabreTools.Compression.libmspack
{
///
@@ -7,7 +9,7 @@ namespace SabreTools.Compression.libmspack
///
///
///
- public abstract class msoab_decompressor
+ public unsafe class msoab_decompressor
{
public mspack_system system { get; set; }
@@ -28,8 +30,150 @@ namespace SabreTools.Compression.libmspack
/// The filename of the output file. This 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);
+ /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
+ public MSPACK_ERR decompress(in string input, in string output)
+ {
+ msoab_decompressor_p* self = (msoab_decompressor_p*)_self;
+ mspack_system* sys;
+ mspack_file* infh = NULL;
+ mspack_file* outfh = NULL;
+ byte* buf = NULL;
+ byte[] hdrbuf = new byte[oabhead_SIZEOF];
+ uint block_max, target_size;
+ lzxd_stream* lzx = NULL;
+ mspack_system oabd_sys;
+ oabd_file in_ofh, out_ofh;
+ uint window_bits;
+ int ret = MSPACK_ERR_OK;
+
+ if (!self) return MSPACK_ERR_ARGS;
+ sys = self->system;
+
+ infh = sys->open(sys, input, MSPACK_SYS_OPEN_READ);
+ if (!infh)
+ {
+ ret = MSPACK_ERR_OPEN;
+ goto outlbl;
+ }
+
+ if (sys->read(infh, hdrbuf, oabhead_SIZEOF) != oabhead_SIZEOF)
+ {
+ ret = MSPACK_ERR_READ;
+ goto outlbl;
+ }
+
+ if (EndGetI32(&hdrbuf[oabhead_VersionHi]) != 3 ||
+ EndGetI32(&hdrbuf[oabhead_VersionLo]) != 1)
+ {
+ ret = MSPACK_ERR_SIGNATURE;
+ goto outlbl;
+ }
+
+ block_max = EndGetI32(&hdrbuf[oabhead_BlockMax]);
+ target_size = EndGetI32(&hdrbuf[oabhead_TargetSize]);
+
+ outfh = sys->open(sys, output, MSPACK_SYS_OPEN_WRITE);
+ if (!outfh)
+ {
+ ret = MSPACK_ERR_OPEN;
+ goto outlbl;
+ }
+
+ buf = sys->alloc(sys, self->buf_size);
+ if (!buf)
+ {
+ ret = MSPACK_ERR_NOMEMORY;
+ goto outlbl;
+ }
+
+ oabd_sys = *sys;
+ oabd_sys.read = oabd_sys_read;
+ oabd_sys.write = oabd_sys_write;
+
+ in_ofh.orig_sys = sys;
+ in_ofh.orig_file = infh;
+
+ out_ofh.orig_sys = sys;
+ out_ofh.orig_file = outfh;
+
+ while (target_size)
+ {
+ uint blk_csize, blk_dsize, blk_crc, blk_flags;
+
+ if (sys->read(infh, buf, oabblk_SIZEOF) != oabblk_SIZEOF)
+ {
+ ret = MSPACK_ERR_READ;
+ goto outlbl;
+ }
+ blk_flags = EndGetI32(&buf[oabblk_Flags]);
+ blk_csize = EndGetI32(&buf[oabblk_CompSize]);
+ blk_dsize = EndGetI32(&buf[oabblk_UncompSize]);
+ blk_crc = EndGetI32(&buf[oabblk_CRC]);
+
+ if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1)
+ {
+ ret = MSPACK_ERR_DATAFORMAT;
+ goto outlbl;
+ }
+
+ if (!blk_flags)
+ {
+ /* Uncompressed block */
+ if (blk_dsize != blk_csize)
+ {
+ ret = MSPACK_ERR_DATAFORMAT;
+ goto outlbl;
+ }
+ ret = copy_fh(sys, infh, outfh, blk_dsize, buf, self->buf_size);
+ if (ret) goto outlbl;
+ }
+ else
+ {
+ /* LZX compressed block */
+ window_bits = 17;
+
+ while (window_bits < 25 && (1U << window_bits) < blk_dsize)
+ window_bits++;
+
+ in_ofh.available = blk_csize;
+ out_ofh.crc = 0xffffffff;
+
+ lzx = lzxd_init(&oabd_sys, (void*)&in_ofh, (void*)&out_ofh, window_bits,
+ 0, self->buf_size, blk_dsize, 1);
+ if (!lzx)
+ {
+ ret = MSPACK_ERR_NOMEMORY;
+ goto outlbl;
+ }
+
+ ret = lzxd_decompress(lzx, blk_dsize);
+ if (ret != MSPACK_ERR_OK)
+ goto outlbl;
+
+ lzxd_free(lzx);
+ lzx = NULL;
+
+ /* Consume any trailing padding bytes before the next block */
+ ret = copy_fh(sys, infh, NULL, in_ofh.available, buf, self->buf_size);
+ if (ret) goto outlbl;
+
+ if (out_ofh.crc != blk_crc)
+ {
+ ret = MSPACK_ERR_CHECKSUM;
+ goto outlbl;
+ }
+ }
+ target_size -= blk_dsize;
+ }
+
+ outlbl:
+ if (lzx) lzxd_free(lzx);
+ if (outfh) sys->close(outfh);
+ if (infh) sys->close(infh);
+ sys->free(buf);
+
+ return ret;
+ }
///
/// Decompresses an Offline Address Book with an incremental patch file.
@@ -58,8 +202,169 @@ namespace SabreTools.Compression.libmspack
/// The filename of the output file. This is passed
/// directly to mspack_system::open().
///
- /// An error code, or MSPACK_ERR_OK if successful
- public abstract MSPACK_ERR decompress_incremental(in string input, in string @base, in string output);
+ /// An error code, or MSPACK_ERR.MSPACK_ERR_OK if successful
+ public MSPACK_ERR decompress_incremental(in string input, in string @base, in string output)
+ {
+ lzxd_stream lzx = null;
+ uint window_bits, window_size;
+ MSPACK_ERR ret = MSPACK_ERR.MSPACK_ERR_OK;
+
+ mspack_system sys = this.system;
+
+ mspack_file infh = sys.open(input, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ);
+ if (infh == null)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_OPEN;
+ goto outlbl;
+ }
+
+ byte[] hdrbuf = new byte[patchhead_SIZEOF];
+ byte* hdrbufPtr = libmspack.system.GetArrayPointer(hdrbuf);
+ if (sys.read(infh, hdrbufPtr, patchhead_SIZEOF) != patchhead_SIZEOF)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_READ;
+ goto outlbl;
+ }
+
+ if (EndGetI32(&hdrbuf[patchhead_VersionHi]) != 3 ||
+ EndGetI32(&hdrbuf[patchhead_VersionLo]) != 2)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_SIGNATURE;
+ goto outlbl;
+ }
+
+ uint block_max = EndGetI32(&hdrbuf[patchhead_BlockMax]);
+ uint target_size = EndGetI32(&hdrbuf[patchhead_TargetSize]);
+
+ // We use it for reading block headers too
+ if (block_max < patchblk_SIZEOF)
+ block_max = patchblk_SIZEOF;
+
+ mspack_file basefh = sys.open(@base, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_READ);
+ if (basefh == null)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_OPEN;
+ goto outlbl;
+ }
+
+ mspack_file outfh = sys.open(output, MSPACK_SYS_OPEN.MSPACK_SYS_OPEN_WRITE);
+ if (outfh == null)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_OPEN;
+ goto outlbl;
+ }
+
+ byte* buf = (byte*)sys.alloc(this.buf_size);
+ if (buf == null)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
+ goto outlbl;
+ }
+
+ mspack_oab_system oabd_sys = sys as mspack_oab_system;
+
+ oabd_file in_ofh = new oabd_file();
+ in_ofh.orig_sys = sys;
+ in_ofh.orig_file = infh;
+
+ oabd_file out_ofh = new oabd_file();
+ out_ofh.orig_sys = sys;
+ out_ofh.orig_file = outfh;
+
+ while (target_size > 0)
+ {
+ if (sys.read(infh, buf, patchblk_SIZEOF) != patchblk_SIZEOF)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_READ;
+ goto outlbl;
+ }
+
+ uint blk_csize = EndGetI32(&buf[patchblk_PatchSize]);
+ uint blk_dsize = EndGetI32(&buf[patchblk_TargetSize]);
+ uint blk_ssize = EndGetI32(&buf[patchblk_SourceSize]);
+ uint blk_crc = EndGetI32(&buf[patchblk_CRC]);
+
+ if (blk_dsize > block_max || blk_dsize > target_size ||
+ blk_ssize > block_max)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_DATAFORMAT;
+ goto outlbl;
+ }
+
+
+ window_size = (blk_ssize + 32767) & ~32767;
+ window_size += blk_dsize;
+ window_bits = 17;
+
+ while (window_bits < 25 && (1U << window_bits) < window_size)
+ window_bits++;
+
+ in_ofh.available = blk_csize;
+ out_ofh.crc = 0xffffffff;
+
+ lzx = lzxd_init(&oabd_sys, (void*)&in_ofh, (void*)&out_ofh, window_bits,
+ 0, 4096, blk_dsize, 1);
+ if (!lzx)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_NOMEMORY;
+ goto outlbl;
+ }
+ ret = lzxd_set_reference_data(lzx, sys, basefh, blk_ssize);
+ if (ret != MSPACK_ERR.MSPACK_ERR_OK)
+ goto outlbl;
+
+ ret = lzxd_decompress(lzx, blk_dsize);
+ if (ret != MSPACK_ERR.MSPACK_ERR_OK)
+ goto outlbl;
+
+ lzxd_free(lzx);
+ lzx = null;
+
+ /* Consume any trailing padding bytes before the next block */
+ ret = copy_fh(sys, infh, null, in_ofh.available, buf, this.buf_size);
+ if (ret) goto outlbl;
+
+ if (out_ofh.crc != blk_crc)
+ {
+ ret = MSPACK_ERR.MSPACK_ERR_CHECKSUM;
+ goto outlbl;
+ }
+
+ target_size -= blk_dsize;
+ }
+
+ outlbl:
+ if (lzx) lzxd_free(lzx);
+ if (outfh) sys.close(outfh);
+ if (basefh) sys.close(basefh);
+ if (infh) sys.close(infh);
+ sys.free(buf);
+
+ return ret;
+ }
+
+ private static MSPACK_ERR copy_fh(mspack_system sys, mspack_file infh, mspack_file outfh, int bytes_to_copy, byte* buf, int buf_size)
+ {
+ while (bytes_to_copy > 0)
+ {
+ int run = buf_size;
+ if (run > bytes_to_copy)
+ {
+ run = bytes_to_copy;
+ }
+ if (sys.read(infh, buf, run) != run)
+ {
+ return MSPACK_ERR.MSPACK_ERR_READ;
+ }
+ if (outfh != null && sys.write(outfh, buf, run) != run)
+ {
+ return MSPACK_ERR.MSPACK_ERR_WRITE;
+ }
+ bytes_to_copy -= run;
+ }
+
+ return MSPACK_ERR.MSPACK_ERR_OK;
+ }
///
/// Sets an OAB decompression engine parameter. Available only in OAB
@@ -72,9 +377,19 @@ namespace SabreTools.Compression.libmspack
/// The parameter to set
/// The value to set the parameter to
///
- /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
+ /// MSPACK_ERR.MSPACK_ERR_OK if all is OK, or MSPACK_ERR.MSPACK_ERR_ARGS if there
/// is a problem with either parameter or value.
///
- public abstract MSPACK_ERR set_param(MSOABD_PARAM param, int value);
+ public MSPACK_ERR set_param(MSOABD_PARAM param, int value)
+ {
+ if (param == MSOABD_PARAM.MSOABD_PARAM_DECOMPBUF && value >= 16)
+ {
+ // Must be at least 16 bytes (patchblk_SIZEOF, oabblk_SIZEOF)
+ this.buf_size = value;
+ return MSPACK_ERR.MSPACK_ERR_OK;
+ }
+
+ return MSPACK_ERR.MSPACK_ERR_ARGS;
+ }
}
}
\ No newline at end of file
diff --git a/libmspack/mspack_default_system.cs b/libmspack/mspack_default_system.cs
index 0b70577..d33d37a 100644
--- a/libmspack/mspack_default_system.cs
+++ b/libmspack/mspack_default_system.cs
@@ -120,8 +120,7 @@ namespace SabreTools.Compression.libmspack
///
public override unsafe void* alloc(int bytes)
{
- var arr = new byte[bytes];
- return (byte*)arr[0];
+ return system.CreateArray(bytes);
}
///
diff --git a/libmspack/mspack_oab_system.cs b/libmspack/mspack_oab_system.cs
new file mode 100644
index 0000000..fd0c187
--- /dev/null
+++ b/libmspack/mspack_oab_system.cs
@@ -0,0 +1,34 @@
+namespace SabreTools.Compression.libmspack
+{
+ public unsafe class mspack_oab_system : mspack_default_system
+ {
+ ///
+ public override unsafe int read(mspack_file base_file, void* buf, int size)
+ {
+ oabd_file file = (oabd_file)base_file;
+ int bytes_read;
+
+ if (size > file.available)
+ size = file.available;
+
+ bytes_read = file.orig_sys.read(file.orig_file, buf, size);
+ if (bytes_read < 0)
+ return bytes_read;
+
+ file.available -= bytes_read;
+ return bytes_read;
+ }
+
+ ///
+ public override unsafe int write(mspack_file base_file, void* buf, int size)
+ {
+ oabd_file file = (oabd_file)base_file;
+ int bytes_written = file.orig_sys.write(file.orig_file, buf, size);
+
+ if (bytes_written > 0)
+ file.crc = mspack.crc32(file.crc, buf, bytes_written);
+
+ return bytes_written;
+ }
+ }
+}
\ No newline at end of file
diff --git a/libmspack/oabd_file.cs b/libmspack/oabd_file.cs
new file mode 100644
index 0000000..4a838b9
--- /dev/null
+++ b/libmspack/oabd_file.cs
@@ -0,0 +1,13 @@
+namespace SabreTools.Compression.libmspack
+{
+ public class oabd_file : mspack_file
+ {
+ public mspack_system orig_sys { get; set; }
+
+ public mspack_file orig_file { get; set; }
+
+ public uint crc { get; set; }
+
+ public int available { get; set; }
+ }
+}
\ No newline at end of file