libmspack cleanup

This commit is contained in:
Matt Nadareski
2022-05-23 13:36:19 -07:00
parent b29198b3d4
commit 394b4e70fb
48 changed files with 1434 additions and 1979 deletions

View File

@@ -0,0 +1,36 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
namespace LibMSPackSharp
{
public abstract class BaseDecompressState
{
/// <summary>
/// System wrapper for I/O operations
/// </summary>
public SystemImpl System { get; set; }
/// <summary>
/// Persistent error state of the state
/// </summary>
public Error Error { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public FileStream InputFileHandle { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public FileStream OutputFileHandle { get; set; }
}
}

View File

@@ -0,0 +1,32 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp
{
/// <summary>
/// Set of common fields shared by all decompressors
/// </summary>
public abstract class BaseDecompressor
{
/// <summary>
/// System wrapper for I/O operations
/// </summary>
public SystemImpl System { get; set; }
/// <summary>
/// Persistent error state of the decompressor
/// </summary>
public Error Error { get; set; }
/// <summary>
/// Size of the internal buffer
/// </summary>
public int BufferSize { get; set; }
}
}

View File

@@ -7,9 +7,10 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.HLP
namespace LibMSPackSharp
{
public class Implementation
{
}
/// <summary>
/// Base class for decompressor-used objects
/// </summary>
public abstract class BaseHeader { }
}

View File

@@ -72,7 +72,7 @@ namespace LibMSPackSharp.CAB
/// <see cref="Decompressor.Open(string)"/>
/// <see cref="Decompressor.Close(Cabinet)"/>
/// <see cref="Decompressor.Search(string)"/>
public class Cabinet
public class Cabinet : BaseHeader
{
#region Internal

View File

@@ -1,91 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains
* "folders", which are compressed spans of data. Each cabinet has
* "files", whose metadata is in the cabinet header, but whose actual data
* is stored compressed in one of the "folders". Cabinets can span more
* than one physical file on disk, in which case they are a "cabinet set",
* and usually the last folder of each cabinet extends into the next
* cabinet.
*
* For a complete description of the format, see the MSDN site:
* http://msdn.microsoft.com/en-us/library/bb267310.aspx
*/
/* Notes on compliance with cabinet specification:
*
* One of the main changes between cabextract 0.6 and libmspack's cab
* decompressor is the move from block-oriented decompression to
* stream-oriented decompression.
*
* cabextract would read one data block from disk, decompress it with the
* appropriate method, then write the decompressed data. The CAB
* specification is specifically designed to work like this, as it ensures
* compression matches do not span the maximum decompressed block size
* limit of 32kb.
*
* However, the compression algorithms used are stream oriented, with
* specific hacks added to them to enforce the "individual 32kb blocks"
* rule in CABs. In other file formats, they do not have this limitation.
*
* In order to make more generalised decompressors, libmspack's CAB
* decompressor has moved from being block-oriented to more stream
* oriented. This also makes decompression slightly faster.
*
* However, this leads to incompliance with the CAB specification. The
* CAB controller can no longer ensure each block of input given to the
* decompressors is matched with their output. The "decompressed size" of
* each individual block is thrown away.
*
* Each CAB block is supposed to be seen as individually compressed. This
* means each consecutive data block can have completely different
* "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in
* reality, all data blocks in a folder decompress to exactly 32768 bytes,
* excepting the final block.
*
* Given this situation, the decompression algorithms are designed to
* realign their input bitstreams on 32768 output-byte boundaries, and
* various other special cases have been made. libmspack will not
* correctly decompress LZX or Quantum compressed folders where the blocks
* do not follow this "32768 bytes until last block" pattern. It could be
* implemented if needed, but hopefully this is not necessary -- it has
* not been seen in over 3Gb of CAB archives.
*/
namespace LibMSPackSharp.CAB
{
public static class Constants
{
// CAB data blocks are <= 32768 bytes in uncompressed form.Uncompressed
// blocks have zero growth. MSZIP guarantees that it won't grow above
// uncompressed size by more than 12 bytes.LZX guarantees it won't grow
// more than 6144 bytes.Quantum has no documentation, but the largest
// block seen in the wild is 337 bytes above uncompressed size.
public const int CAB_BLOCKMAX = 32768;
public const int CAB_INPUTMAX = CAB_BLOCKMAX + 6144;
// input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block
// plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment.
//
// When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be
// up to 65535 bytes, so max input buffer size needed is 65535 + 1
public const int CAB_INPUTMAX_SALVAGE = 65535;
public const int CAB_INPUTBUF = CAB_INPUTMAX_SALVAGE + 1;
// There are no more than 65535 data blocks per folder, so a folder cannot
// be more than 32768*65535 bytes in length.As files cannot span more than
// one folder, this is also their max offset, length and offset+length limit.
public const int CAB_FOLDERMAX = 65535;
public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX;
}
}

View File

@@ -8,11 +8,10 @@
*/
using System;
using System.IO;
namespace LibMSPackSharp.CAB
{
public class DecompressState
public class DecompressState : BaseDecompressState
{
/// <summary>
/// Current folder we're extracting from
@@ -39,11 +38,6 @@ namespace LibMSPackSharp.CAB
/// </summary>
public long Outlen { get; set; }
/// <summary>
/// Special I/O code for decompressor
/// </summary>
public SystemImpl Sys { get; set; }
/// <summary>
/// Type of compression used by folder
/// </summary>
@@ -57,23 +51,13 @@ namespace LibMSPackSharp.CAB
/// <summary>
/// Decompressor state
/// </summary>
public object DecompressorState { get; set; }
public BaseDecompressState DecompressorState { get; set; }
/// <summary>
/// Cabinet where input data comes from
/// </summary>
public Cabinet InputCabinet { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public FileStream InputFileHandle { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public FileStream OutputFileHandle { get; set; }
/// <summary>
/// Input data consumed
/// </summary>

View File

@@ -18,7 +18,7 @@ using System;
using System.IO;
using System.Text;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.CAB.Constants;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.CAB
{
@@ -29,24 +29,18 @@ namespace LibMSPackSharp.CAB
/// </summary>
/// <see cref="Library.CreateCABDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyCABDecompressor(Decompressor)"/>
public class Decompressor
public class Decompressor : BaseDecompressor
{
#region Fields
public DecompressState State { get; set; }
public SystemImpl System { get; set; }
public int BufferSize { get; set; }
public int SearchBufferSize { get; set; }
public bool FixMSZip { get; set; }
public bool Salvage { get; set; }
public Error Error { get; set; }
public Error ReadError { get; set; }
#endregion
@@ -294,6 +288,7 @@ namespace LibMSPackSharp.CAB
/// <see cref="Close(Cabinet)"/>
public Error Prepend(Cabinet cab, Cabinet prevcab) => Merge(prevcab, cab);
/// <summary>
/// <summary>
/// Extracts a file from a cabinet or cabinet set.
///
@@ -367,7 +362,7 @@ namespace LibMSPackSharp.CAB
Block = 0,
Outlen = 0,
Sys = System,
System = System,
InputCabinet = fol.Data.Cab,
InputFileHandle = System.Open(fol.Data.Cab.Filename, OpenMode.MSPACK_SYS_OPEN_READ),
@@ -376,8 +371,8 @@ namespace LibMSPackSharp.CAB
InputEnd = 0,
};
State.Sys.Read = SysRead;
State.Sys.Write = SysWrite;
State.System.Read = SysRead;
State.System.Write = SysWrite;
if (State.InputFileHandle == null)
return Error = Error.MSPACK_ERR_OPEN;
@@ -501,22 +496,22 @@ namespace LibMSPackSharp.CAB
{
case CompressionType.COMPTYPE_NONE:
State.Decompress = None.Decompress;
State.DecompressorState = None.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, BufferSize);
State.DecompressorState = None.Init(State.System, State.InputFileHandle, State.OutputFileHandle, BufferSize);
break;
case CompressionType.COMPTYPE_MSZIP:
State.Decompress = MSZIP.Decompress;
State.DecompressorState = MSZIP.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, BufferSize, FixMSZip);
State.DecompressorState = MSZIP.Init(State.System, State.InputFileHandle, State.OutputFileHandle, BufferSize, FixMSZip);
break;
case CompressionType.COMPTYPE_QUANTUM:
State.Decompress = QTM.Decompress;
State.DecompressorState = QTM.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, BufferSize);
State.DecompressorState = QTM.Init(State.System, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, BufferSize);
break;
case CompressionType.COMPTYPE_LZX:
State.Decompress = LZX.Decompress;
State.DecompressorState = LZX.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, 0, BufferSize, 0, false);
State.DecompressorState = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, 0, BufferSize, 0, false);
break;
default:
@@ -534,19 +529,10 @@ namespace LibMSPackSharp.CAB
switch (State.CompressionType & CompressionType.COMPTYPE_MASK)
{
case CompressionType.COMPTYPE_NONE:
(State.DecompressorState as NoneState).Output = State.OutputFileHandle;
break;
case CompressionType.COMPTYPE_MSZIP:
(State.DecompressorState as MSZIPDStream).Output = State.OutputFileHandle;
break;
case CompressionType.COMPTYPE_QUANTUM:
(State.DecompressorState as QTMDStream).Output = State.OutputFileHandle;
break;
case CompressionType.COMPTYPE_LZX:
(State.DecompressorState as LZXDStream).Output = State.OutputFileHandle;
State.DecompressorState.OutputFileHandle = State.OutputFileHandle;
break;
default:

View File

@@ -21,7 +21,7 @@ namespace LibMSPackSharp.CHM
///
/// All fields are READ ONLY.
/// </summary>
public class CHM
public class CHM : BaseHeader
{
#region Internal

View File

@@ -1,53 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.CHM
{
public class Constants
{
// _PMGHeader
internal const int pmgl_Signature = 0x0000;
internal const int pmgl_QuickRefSize = 0x0004;
internal const int pmgl_PMGIEntries = 0x0008; // Unknown1 in PMGL
internal const int pmgl_PrevChunk = 0x000C; // Not in PMGI
internal const int pmgl_NextChunk = 0x0010; // Not in PMGI
internal const int pmgl_PMGLEntries = 0x0014; // Not in PMGI
internal const int pmgl_headerSIZEOF = 0x0014;
internal const int pmgi_headerSIZEOF = 0x000C;
// _LZXControlData
internal const int lzxcd_Length = 0x0000;
internal const int lzxcd_Signature = 0x0004;
internal const int lzxcd_Version = 0x0008;
internal const int lzxcd_ResetInterval = 0x000C;
internal const int lzxcd_WindowSize = 0x0010;
internal const int lzxcd_CacheSize = 0x0014;
internal const int lzxcd_Unknown1 = 0x0018;
internal const int lzxcd_SIZEOF = 0x001C;
// _LZXResetTable
internal const int lzxrt_Unknown1 = 0x0000;
internal const int lzxrt_NumEntries = 0x0004;
internal const int lzxrt_EntrySize = 0x0008;
internal const int lzxrt_TableOffset = 0x000C;
internal const int lzxrt_UncompLen = 0x0010;
internal const int lzxrt_CompLen = 0x0018;
internal const int lzxrt_FrameLen = 0x0020;
internal const int lzxrt_Entries = 0x0028;
internal const int lzxrt_headerSIZEOF = 0x0028;
// Filenames of the system files used for decompression.
// - Content and ControlData are essential.
// - ResetTable is preferred, but SpanInfo can be used if not available
internal const string ContentName = "::DataSpace/Storage/MSCompressed/Content";
internal const string ControlName = "::DataSpace/Storage/MSCompressed/ControlData";
internal const string SpanInfoName = "::DataSpace/Storage/MSCompressed/SpanInfo";
internal const string ResetTableName = "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
}
}

View File

@@ -7,12 +7,11 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.CHM
{
public class DecompressState
public class DecompressState : BaseDecompressState
{
/// <summary>
/// CHM file being decompressed
@@ -33,20 +32,5 @@ namespace LibMSPackSharp.CHM
/// LZX decompressor state
/// </summary>
public LZXDStream State { get; set; }
/// <summary>
/// Special I/O code for decompressor
/// </summary>
public SystemImpl Sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public FileStream InputFileHandle { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public FileStream OutputFileHandle { get; set; }
}
}

View File

@@ -18,7 +18,7 @@ using System;
using System.IO;
using System.Text;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.CHM.Constants;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.CHM
{
@@ -29,16 +29,12 @@ namespace LibMSPackSharp.CHM
/// </summary>
/// <see cref="Library.CreateCHMDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyCHMDecompressor(Decompressor)"/>
public class Decompressor
public class Decompressor : BaseDecompressor
{
#region Fields
public SystemImpl System { get; set; }
public DecompressState State { get; set; }
public Error Error { get; set; }
#endregion
#region Public Functionality
@@ -62,10 +58,7 @@ namespace LibMSPackSharp.CHM
/// </param>
/// <returns>a pointer to a mschmd_header structure, or NULL on failure</returns>
/// <see cref="Close(CHM)"/>
public CHM Open(string filename)
{
return RealOpen(filename, true);
}
public CHM Open(string filename) => RealOpen(filename, true);
/// <summary>
/// Closes a previously opened CHM helpfile.
@@ -137,8 +130,8 @@ namespace LibMSPackSharp.CHM
State.Header = chm;
State.Offset = 0;
State.State = null;
State.Sys = System;
State.Sys.Write = SysWrite;
State.System = System;
State.System.Write = SysWrite;
State.InputFileHandle = null;
State.OutputFileHandle = null;
}
@@ -279,10 +272,7 @@ namespace LibMSPackSharp.CHM
/// <see cref="Close(CHM)"/>
/// <see cref="FastFind(CHM, string, DecompressFile)"/>
/// <see cref="Extract(DecompressFile, string)"/>
public CHM FastOpen(string filename)
{
return RealOpen(filename, false);
}
public CHM FastOpen(string filename) => RealOpen(filename, false);
/// <summary>
/// Finds file details quickly.
@@ -587,7 +577,7 @@ namespace LibMSPackSharp.CHM
length -= State.Offset;
// Initialise LZX stream
State.State = LZX.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, window_bits, reset_interval / LZX.LZX_FRAME_SIZE, 4096, length, false);
State.State = LZX.Init(State.System, State.InputFileHandle, State.OutputFileHandle, window_bits, reset_interval / LZX.LZX_FRAME_SIZE, 4096, length, false);
if (State.State == null)
Error = Error.MSPACK_ERR_NOMEMORY;

View File

@@ -11,33 +11,15 @@
*/
using System;
using System.IO;
namespace LibMSPackSharp.Compression
{
public abstract class CompressionStream
public abstract class CompressionStream : BaseDecompressState
{
private const int CHAR_BIT = 8;
public const int BITBUF_WIDTH = 4 * CHAR_BIT;
/// <summary>
/// I/O routines
/// </summary>
public SystemImpl Sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public FileStream Input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public FileStream Output { get; set; }
public Error Error { get; set; }
#region I/O buffering
public byte[] InputBuffer { get; set; }
@@ -135,7 +117,7 @@ namespace LibMSPackSharp.Compression
public Error ReadInput()
{
int read = Sys.Read(Input, InputBuffer, 0, (int)InputBufferSize);
int read = System.Read(InputFileHandle, InputBuffer, 0, (int)InputBufferSize);
if (read < 0)
return Error = Error.MSPACK_ERR_READ;

View File

@@ -7,415 +7,39 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
using System.IO;
using System.Text;
using LibMSPackSharp.Compression;
using LibMSPackSharp.KWAJ;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.KWAJ
namespace LibMSPackSharp.Compression
{
public class Implementation
/// <summary>
/// In the KWAJ LZH format, there is no special 'eof' marker, it just
/// ends. Depending on how many bits are left in the final byte when
/// the stream ends, that might be enough to start another literal or
/// match. The only easy way to detect that we've come to an end is to
/// guard all bit-reading. We allow fake bits to be read once we reach
/// the end of the stream, but we check if we then consumed any of
/// those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
/// isn't how the default ReadInput works (it simply lets
/// 2 fake bytes in then stops), so we implement our own.
/// </summary>
public class LZHKWAJ
{
#region Generic KWAJ Definitions
private const byte kwajh_Signature1 = 0x00;
private const byte kwajh_Signature2 = 0x04;
private const byte kwajh_CompMethod = 0x08;
private const byte kwajh_DataOffset = 0x0a;
private const byte kwajh_Flags = 0x0c;
private const byte kwajh_SIZEOF = 0x0e;
#endregion
#region KWAJ Decompression Definitions
// Input buffer size during decompression - not worth parameterising IMHO
private const int KWAJ_INPUT_SIZE = (2048);
// Huffman codes that are 9 bits or less are decoded immediately
public const int KWAJ_TABLEBITS = (9);
// Number of codes in each huffman table
public const int KWAJ_MATCHLEN1_SYMS = (16);
public const int KWAJ_MATCHLEN2_SYMS = (16);
public const int KWAJ_LITLEN_SYMS = (32);
public const int KWAJ_OFFSET_SYMS = (64);
public const int KWAJ_LITERAL_SYMS = (256);
// Define decoding table sizes
public const int KWAJ_TABLESIZE = (1 << KWAJ_TABLEBITS);
//public const int KWAJ_MATCHLEN1_TBLSIZE = (KWAJ_MATCHLEN1_SYMS * 4);
public const int KWAJ_MATCHLEN1_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2));
//public const int KWAJ_MATCHLEN2_TBLSIZE = (KWAJ_MATCHLEN2_SYMS * 4);
public const int KWAJ_MATCHLEN2_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2));
//public const int KWAJ_LITLEN_TBLSIZE = (KWAJ_LITLEN_SYMS * 4);
public const int KWAJ_LITLEN_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2));
//public const int KWAJ_OFFSET_TBLSIZE = (KWAJ_OFFSET_SYMS * 4);
public const int KWAJ_OFFSET_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2));
//public const int KWAJ_LITERAL_TBLSIZE = (KWAJ_LITERAL_SYMS * 4);
public const int KWAJ_LITERAL_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2));
#endregion
#region KWAJD_OPEN
/// <summary>
/// Opens a KWAJ file without decompressing, reads header
/// </summary>
public static Header Open(Decompressor d, string filename)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return null;
SystemImpl sys = self.System;
FileStream fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
HeaderImpl hdr = new HeaderImpl();
if (fh != null && hdr != null)
{
hdr.FileHandle = fh;
self.Error = ReadHeaders(sys, fh, hdr);
}
else
{
if (fh == null)
self.Error = Error.MSPACK_ERR_OPEN;
if (hdr == null)
self.Error = Error.MSPACK_ERR_NOMEMORY;
}
if (self.Error != Error.MSPACK_ERR_OK)
{
if (fh != null)
sys.Close(fh);
hdr = null;
}
return hdr;
}
#endregion
#region KWAJD_CLOSE
/// <summary>
/// Closes a KWAJ file
/// </summary>
public static void Close(Decompressor d, Header hdr)
{
DecompressorImpl self = d as DecompressorImpl;
HeaderImpl hdr_p = hdr as HeaderImpl;
if (self?.System == null || hdr_p == null)
return;
// Close the file handle associated
self.System.Close(hdr_p.FileHandle);
self.Error = Error.MSPACK_ERR_OK;
}
#endregion
#region KWAJD_READ_HEADERS
/// <summary>
/// Reads the headers of a KWAJ format file
/// </summary>
public static Error ReadHeaders(SystemImpl sys, FileStream fh, Header hdr)
{
int i;
// Read in the header
byte[] buf = new byte[16];
if (sys.Read(fh, buf, 0, kwajh_SIZEOF) != kwajh_SIZEOF)
{
return Error.MSPACK_ERR_READ;
}
// Check for "KWAJ" signature
if ((BitConverter.ToUInt32(buf, kwajh_Signature1) != 0x4A41574B) ||
(BitConverter.ToUInt32(buf, kwajh_Signature2) != 0xD127F088))
{
return Error.MSPACK_ERR_SIGNATURE;
}
// Basic header fields
hdr.CompressionType = (CompressionType)BitConverter.ToUInt16(buf, kwajh_CompMethod);
hdr.DataOffset = BitConverter.ToUInt16(buf, kwajh_DataOffset);
hdr.Headers = (OptionalHeaderFlag)BitConverter.ToUInt16(buf, kwajh_Flags);
hdr.Length = 0;
hdr.Filename = null;
hdr.Extra = null;
hdr.ExtraLength = 0;
// Optional headers
// 4 bytes: length of unpacked file
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASLENGTH))
{
if (sys.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
// 2 bytes: unknown purpose
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN1))
{
if (sys.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
}
// 2 bytes: length of section, then [length] bytes: unknown purpose
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN2))
{
if (sys.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
i = BitConverter.ToUInt16(buf, 0);
if (sys.Seek(fh, i, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
}
// Filename and extension
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME) || hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT))
{
int len;
// Allocate memory for maximum length filename
char[] fn = new char[13];
int fnPtr = 0;
// Copy filename if present
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME))
{
// Read and copy up to 9 bytes of a null terminated string
if ((len = sys.Read(fh, buf, 0, 9)) < 2)
return Error.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 9 bytes with no null terminator, reject it
if (i == 9 && buf[8] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (sys.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
// Copy extension if present
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT))
{
fn[fnPtr++] = '.';
// Read and copy up to 4 bytes of a null terminated string
if ((len = sys.Read(fh, buf, 0, 4)) < 2)
return Error.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 4 bytes with no null terminator, reject it
if (i == 4 && buf[3] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (sys.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
fn[fnPtr] = '\0';
}
// 2 bytes: extra text length then [length] bytes of extra text data
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASEXTRATEXT))
{
if (sys.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
i = BitConverter.ToUInt16(buf, 0);
byte[] extra = new byte[i + 1];
if (sys.Read(fh, extra, 0, i) != i)
return Error.MSPACK_ERR_READ;
extra[i] = 0x00;
hdr.Extra = Encoding.ASCII.GetString(extra, 0, extra.Length);
hdr.ExtraLength = (ushort)i;
}
return Error.MSPACK_ERR_OK;
}
#endregion
#region KWAJD_EXTRACT
/// <summary>
/// Decompresses a KWAJ file
/// </summary>
public static Error Extract(Decompressor d, Header hdr, string filename)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return Error.MSPACK_ERR_ARGS;
if (hdr == null)
return self.Error = Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
FileStream fh = (hdr as HeaderImpl)?.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
if (sys.Seek(fh, hdr.DataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return self.Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh;
if ((outfh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null)
return self.Error = Error.MSPACK_ERR_OPEN;
self.Error = Error.MSPACK_ERR_OK;
// Decompress based on format
if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_NONE ||
hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
// NONE is a straight copy. XOR is a copy xored with 0xFF
byte[] buf = new byte[KWAJ_INPUT_SIZE];
int read, i;
while ((read = sys.Read(fh, buf, 0, KWAJ_INPUT_SIZE)) > 0)
{
if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
for (i = 0; i < read; i++)
{
buf[i] ^= 0xFF;
}
}
if (sys.Write(outfh, buf, 0, read) != read)
{
self.Error = Error.MSPACK_ERR_WRITE;
break;
}
}
if (read < 0)
self.Error = Error.MSPACK_ERR_READ;
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_SZDD)
{
self.Error = LZSS.Decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, LZSSMode.LZSS_MODE_EXPAND);
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_LZH)
{
LZHKWAJStream lzh = LZHInit(sys, fh, outfh);
self.Error = (lzh != null) ? LZHDecompress(lzh) : Error.MSPACK_ERR_NOMEMORY;
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP)
{
MSZIPDStream zip = MSZIP.Init(sys, fh, outfh, KWAJ_INPUT_SIZE, false);
self.Error = (zip != null) ? MSZIP.DecompressKWAJ(zip) : Error.MSPACK_ERR_NOMEMORY;
}
else
{
self.Error = Error.MSPACK_ERR_DATAFORMAT;
}
// Close output file
sys.Close(outfh);
return self.Error;
}
#endregion
#region KWAJD_DECOMPRESS
/// <summary>
/// Unpacks directly from input to output
/// </summary>
public static Error Decompress(Decompressor d, string input, string output)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return Error.MSPACK_ERR_ARGS;
Header hdr;
if ((hdr = Open(d, input)) == null)
return self.Error;
Error error = Extract(d, hdr, output);
Close(d, hdr);
return self.Error = error;
}
#endregion
#region KWAJD_ERROR
/// <summary>
/// Returns the last error that occurred
/// </summary>
public static Error LastError(Decompressor d)
{
DecompressorImpl self = d as DecompressorImpl;
return (self != null) ? self.Error : Error.MSPACK_ERR_ARGS;
}
#endregion
#region LZH_INIT, LZH_DECOMPRESS, LZH_FREE
/* In the KWAJ LZH format, there is no special 'eof' marker, it just
* ends. Depending on how many bits are left in the final byte when
* the stream ends, that might be enough to start another literal or
* match. The only easy way to detect that we've come to an end is to
* guard all bit-reading. We allow fake bits to be read once we reach
* the end of the stream, but we check if we then consumed any of
* those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
* isn't how the default readbits.h read_input() works (it simply lets
* 2 fake bytes in then stops), so we implement our own.
*/
private static LZHKWAJStream LZHInit(SystemImpl sys, FileStream input, FileStream output)
public static LZHKWAJStream Init(SystemImpl sys, FileStream input, FileStream output)
{
if (sys == null || input == null || output == null)
return null;
return new LZHKWAJStream()
{
Sys = sys,
Input = input,
Output = output,
System = sys,
InputFileHandle = input,
OutputFileHandle = output,
};
}
private static Error LZHDecompress(LZHKWAJStream lzh)
public static Error Decompress(LZHKWAJStream lzh)
{
uint bit_buffer;
int bits_left, i;
@@ -464,7 +88,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -506,7 +130,7 @@ namespace LibMSPackSharp.KWAJ
lzh.BitsLeft = bits_left;
}
err = LZHReadLens(lzh, types[0], KWAJ_MATCHLEN1_SYMS, lzh.MATCHLEN1_len);
err = ReadLens(lzh, types[0], KWAJ_MATCHLEN1_SYMS, lzh.MATCHLEN1_len);
if (err != Error.MSPACK_ERR_OK)
return err;
@@ -532,7 +156,7 @@ namespace LibMSPackSharp.KWAJ
lzh.BitsLeft = bits_left;
}
err = LZHReadLens(lzh, types[1], KWAJ_MATCHLEN2_SYMS, lzh.MATCHLEN2_len);
err = ReadLens(lzh, types[1], KWAJ_MATCHLEN2_SYMS, lzh.MATCHLEN2_len);
if (err != Error.MSPACK_ERR_OK)
return err;
@@ -558,7 +182,7 @@ namespace LibMSPackSharp.KWAJ
lzh.BitsLeft = bits_left;
}
err = LZHReadLens(lzh, types[2], KWAJ_LITLEN_SYMS, lzh.LITLEN_len);
err = ReadLens(lzh, types[2], KWAJ_LITLEN_SYMS, lzh.LITLEN_len);
if (err != Error.MSPACK_ERR_OK)
return err;
@@ -584,7 +208,7 @@ namespace LibMSPackSharp.KWAJ
lzh.BitsLeft = bits_left;
}
err = LZHReadLens(lzh, types[3], KWAJ_OFFSET_SYMS, lzh.OFFSET_len);
err = ReadLens(lzh, types[3], KWAJ_OFFSET_SYMS, lzh.OFFSET_len);
if (err != Error.MSPACK_ERR_OK)
return err;
@@ -610,7 +234,7 @@ namespace LibMSPackSharp.KWAJ
lzh.BitsLeft = bits_left;
}
err = LZHReadLens(lzh, types[4], KWAJ_LITERAL_SYMS, lzh.LITERAL_len);
err = ReadLens(lzh, types[4], KWAJ_LITERAL_SYMS, lzh.LITERAL_len);
if (err != Error.MSPACK_ERR_OK)
return err;
@@ -642,7 +266,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -702,7 +326,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -766,7 +390,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -826,7 +450,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -864,7 +488,7 @@ namespace LibMSPackSharp.KWAJ
//WRITE_BYTE
{
if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1)
if (lzh.System.Write(lzh.OutputFileHandle, lzh.Window, pos, 1) != 1)
return Error.MSPACK_ERR_WRITE;
}
@@ -886,7 +510,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -948,7 +572,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -999,7 +623,7 @@ namespace LibMSPackSharp.KWAJ
//WRITE_BYTE
{
if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1)
if (lzh.System.Write(lzh.OutputFileHandle, lzh.Window, pos, 1) != 1)
return Error.MSPACK_ERR_WRITE;
}
@@ -1011,7 +635,7 @@ namespace LibMSPackSharp.KWAJ
return Error.MSPACK_ERR_OK;
}
public static Error LZHReadLens(LZHKWAJStream lzh, uint type, uint numsyms, byte[] lens)
private static Error ReadLens(LZHKWAJStream lzh, uint type, uint numsyms, byte[] lens)
{
uint bit_buffer;
int bits_left;
@@ -1052,7 +676,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1096,7 +720,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1143,7 +767,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1190,7 +814,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1239,7 +863,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1283,7 +907,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1326,7 +950,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1380,7 +1004,7 @@ namespace LibMSPackSharp.KWAJ
{
if (i_ptr >= i_end)
{
if ((err = LZHReadInput(lzh)) != Error.MSPACK_ERR_OK)
if ((err = ReadInput(lzh)) != Error.MSPACK_ERR_OK)
return err;
i_ptr = lzh.InputPointer;
@@ -1426,7 +1050,7 @@ namespace LibMSPackSharp.KWAJ
return Error.MSPACK_ERR_OK;
}
public static Error LZHReadInput(LZHKWAJStream lzh)
private static Error ReadInput(LZHKWAJStream lzh)
{
int read;
if (lzh.InputEnd != 0)
@@ -1437,7 +1061,7 @@ namespace LibMSPackSharp.KWAJ
}
else
{
read = lzh.Sys.Read(lzh.Input, lzh.InputBuffer, 0, KWAJ_INPUT_SIZE);
read = lzh.System.Read(lzh.InputFileHandle, lzh.InputBuffer, 0, KWAJ_INPUT_SIZE);
if (read < 0)
return Error.MSPACK_ERR_READ;
@@ -1454,7 +1078,5 @@ namespace LibMSPackSharp.KWAJ
lzh.InputLength = read;
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -7,27 +7,27 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.KWAJ
namespace LibMSPackSharp.Compression
{
public class LZHKWAJStream : CompressionStream
{
// Huffman code lengths
public byte[] MATCHLEN1_len { get; set; } = new byte[Implementation.KWAJ_MATCHLEN1_SYMS];
public byte[] MATCHLEN2_len { get; set; } = new byte[Implementation.KWAJ_MATCHLEN2_SYMS];
public byte[] LITLEN_len { get; set; } = new byte[Implementation.KWAJ_LITLEN_SYMS];
public byte[] OFFSET_len { get; set; } = new byte[Implementation.KWAJ_OFFSET_SYMS];
public byte[] LITERAL_len { get; set; } = new byte[Implementation.KWAJ_LITERAL_SYMS];
public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS];
public byte[] MATCHLEN2_len { get; set; } = new byte[KWAJ_MATCHLEN2_SYMS];
public byte[] LITLEN_len { get; set; } = new byte[KWAJ_LITLEN_SYMS];
public byte[] OFFSET_len { get; set; } = new byte[KWAJ_OFFSET_SYMS];
public byte[] LITERAL_len { get; set; } = new byte[KWAJ_LITERAL_SYMS];
// Huffman decoding tables
public ushort[] MATCHLEN1_table { get; set; } = new ushort[Implementation.KWAJ_MATCHLEN1_TBLSIZE];
public ushort[] MATCHLEN2_table { get; set; } = new ushort[Implementation.KWAJ_MATCHLEN2_TBLSIZE];
public ushort[] LITLEN_table { get; set; } = new ushort[Implementation.KWAJ_LITLEN_TBLSIZE];
public ushort[] OFFSET_table { get; set; } = new ushort[Implementation.KWAJ_OFFSET_TBLSIZE];
public ushort[] LITERAL_table { get; set; } = new ushort[Implementation.KWAJ_LITERAL_TBLSIZE];
public ushort[] MATCHLEN1_table { get; set; } = new ushort[KWAJ_MATCHLEN1_TBLSIZE];
public ushort[] MATCHLEN2_table { get; set; } = new ushort[KWAJ_MATCHLEN2_TBLSIZE];
public ushort[] LITLEN_table { get; set; } = new ushort[KWAJ_LITLEN_TBLSIZE];
public ushort[] OFFSET_table { get; set; } = new ushort[KWAJ_OFFSET_TBLSIZE];
public ushort[] LITERAL_table { get; set; } = new ushort[KWAJ_LITERAL_TBLSIZE];
// History window

View File

@@ -191,8 +191,6 @@ namespace LibMSPackSharp.Compression
private static void ResetState(LZXDStream lzx)
{
int i;
lzx.R0 = 1;
lzx.R1 = 1;
lzx.R2 = 1;
@@ -201,12 +199,12 @@ namespace LibMSPackSharp.Compression
lzx.BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0;
// Initialise tables to 0 (because deltas will be applied to them)
for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++)
for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++)
{
lzx.MAINTREE_len[i] = 0;
}
for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
lzx.LENGTH_len[i] = 0;
}
@@ -307,9 +305,9 @@ namespace LibMSPackSharp.Compression
Window = new byte[window_size],
InputBuffer = new byte[input_buffer_size],
Sys = system,
Input = input,
Output = output,
System = system,
InputFileHandle = input,
OutputFileHandle = output,
Offset = 0,
Length = output_length,
@@ -472,7 +470,7 @@ namespace LibMSPackSharp.Compression
if (i != 0)
{
try { lzx.Sys.Write(lzx.Output, lzx.e8_buf, lzx.OutputPointer, i); }
try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
lzx.OutputPointer += i;
@@ -512,7 +510,7 @@ namespace LibMSPackSharp.Compression
Console.WriteLine($"{lzx.BlockRemaining} bytes remaining at reset interval");
if (warned == 0)
{
lzx.Sys.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
lzx.System.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
warned++;
}
}
@@ -577,182 +575,6 @@ namespace LibMSPackSharp.Compression
}
}
// Read header if necessary
if (lzx.HeaderRead == 0)
{
// Read 1 bit. if bit=0, intel_filesize = 0.
// if bit=1, read intel filesize (32 bits)
j = 0;
//READ_BITS(i, 1)
{
//ENSURE_BITS(1)
{
while (bits_left < (1))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(i) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (1))); //PEEK_BITS(1)
//REMOVE_BITS(1)
{
bit_buffer <<= (1);
bits_left -= (1);
}
}
if (i != 0)
{
//READ_BITS(i, 16)
{
//ENSURE_BITS(16)
{
while (bits_left < (16))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(i) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (16))); //PEEK_BITS(16)
//REMOVE_BITS(16)
{
bit_buffer <<= (16);
bits_left -= (16);
}
}
//READ_BITS(j, 16)
{
//ENSURE_BITS(16)
{
while (bits_left < (16))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(j) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (16))); //PEEK_BITS(16)
//REMOVE_BITS(16)
{
bit_buffer <<= (16);
bits_left -= (16);
}
}
}
lzx.IntelFileSize = (i << 16) | j;
lzx.HeaderRead = 1;
}
// Calculate size of frame: all frames are 32k except the final frame
// which is 32kb or less. this can only be calculated when lzx.Length
// has been filled in.
@@ -789,7 +611,7 @@ namespace LibMSPackSharp.Compression
//READ_BITS(lzx.BlockType, 3)
{
//ENSURE_BITS(3)
//ENSURE_BITS(3) // THIS IS NOT 3 BECAUSE OF OTHER CODE I FOUND
{
while (bits_left < (3))
{
@@ -841,6 +663,182 @@ namespace LibMSPackSharp.Compression
}
}
// Read header if necessary
if (lzx.HeaderRead == 0)
{
// Read 1 bit. if bit=0, intel_filesize = 0.
// if bit=1, read intel filesize (32 bits)
j = 0;
//READ_BITS(i, 1)
{
//ENSURE_BITS(1)
{
while (bits_left < (1))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(i) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (1))); //PEEK_BITS(1)
//REMOVE_BITS(1)
{
bit_buffer <<= (1);
bits_left -= (1);
}
}
if (i != 0)
{
//READ_BITS(i, 16)
{
//ENSURE_BITS(16)
{
while (bits_left < (16))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(i) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (16))); //PEEK_BITS(16)
//REMOVE_BITS(16)
{
bit_buffer <<= (16);
bits_left -= (16);
}
}
//READ_BITS(j, 16)
{
//ENSURE_BITS(16)
{
while (bits_left < (16))
{
//READ_BYTES
{
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b0 = lzx.InputBuffer[i_ptr++];
//READ_IF_NEEDED
{
if (i_ptr >= i_end)
{
if (lzx.ReadInput() != Error.MSPACK_ERR_OK)
return lzx.Error;
i_ptr = lzx.InputPointer;
i_end = lzx.InputLength;
}
}
byte b1 = lzx.InputBuffer[i_ptr++];
//INJECT_BITS((b1 << 8) | b0, 16)
{
bit_buffer |= (uint)((b1 << 8) | b0) << (CompressionStream.BITBUF_WIDTH - (16) - bits_left);
bits_left += (16);
}
}
}
}
(j) = (int)(bit_buffer >> (CompressionStream.BITBUF_WIDTH - (16))); //PEEK_BITS(16)
//REMOVE_BITS(16)
{
bit_buffer <<= (16);
bits_left -= (16);
}
}
}
lzx.IntelFileSize = (i << 16) | j;
lzx.HeaderRead = 1;
}
//READ_BITS(i, 16)
{
//ENSURE_BITS(16)
@@ -2415,7 +2413,7 @@ namespace LibMSPackSharp.Compression
// Write a frame
i = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { lzx.Output.Write(lzx.e8_buf, lzx.OutputPointer, i); }
try { lzx.System.Write(lzx.OutputFileHandle, lzx.e8_buf, lzx.OutputPointer, i); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
}
else
@@ -2425,7 +2423,7 @@ namespace LibMSPackSharp.Compression
// Write a frame
i = (int)((out_bytes < frame_size) ? out_bytes : frame_size);
try { lzx.Output.Write(lzx.Window, lzx.OutputPointer, i); }
try { lzx.System.Write(lzx.OutputFileHandle, lzx.Window, lzx.OutputPointer, i); }
catch { return lzx.Error = Error.MSPACK_ERR_WRITE; }
}

View File

@@ -112,9 +112,9 @@ namespace LibMSPackSharp.Compression
InputBuffer = new byte[input_buffer_size],
// Initialise decompression state
Sys = system,
Input = input,
Output = output,
System = system,
InputFileHandle = input,
OutputFileHandle = output,
InputBufferSize = (uint)input_buffer_size,
InputEnd = 0,
Error = Error.MSPACK_ERR_OK,
@@ -176,7 +176,7 @@ namespace LibMSPackSharp.Compression
if (i != 0)
{
if (zip.Sys.Write(zip.Output, zip.Window, zip.OutputPointer, i) != i)
if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
return zip.Error = Error.MSPACK_ERR_WRITE;
zip.OutputPointer += i;
@@ -279,7 +279,7 @@ namespace LibMSPackSharp.Compression
if (zip.BytesOutput == 0 && zip.WindowPosition > 0)
zip.FlushWindow(zip, zip.WindowPosition);
zip.Sys.Message(null, $"MSZIP error, {MSZIP_FRAME_SIZE - zip.BytesOutput} bytes of data lost.");
zip.System.Message(null, $"MSZIP error, {MSZIP_FRAME_SIZE - zip.BytesOutput} bytes of data lost.");
for (i = zip.BytesOutput; i < MSZIP_FRAME_SIZE; i++)
{
zip.Window[i] = 0x00;
@@ -298,7 +298,7 @@ namespace LibMSPackSharp.Compression
// Write a frame
i = (out_bytes < zip.BytesOutput) ? (int)out_bytes : zip.BytesOutput;
if (zip.Sys.Write(zip.Output, zip.Window, zip.OutputPointer, i) != i)
if (zip.System.Write(zip.OutputFileHandle, zip.Window, zip.OutputPointer, i) != i)
return zip.Error = Error.MSPACK_ERR_WRITE;
// mspack errors (i.e. read errors) are fatal and can't be recovered
@@ -536,7 +536,7 @@ namespace LibMSPackSharp.Compression
}
// Write inflated block
try { zip.Sys.Write(zip.Output, zip.Window, 0, zip.BytesOutput); }
try { zip.System.Write(zip.OutputFileHandle, zip.Window, 0, zip.BytesOutput); }
catch { return zip.Error = Error.MSPACK_ERR_WRITE; }
}

View File

@@ -17,9 +17,9 @@ namespace LibMSPackSharp.Compression
{
return new NoneState()
{
Sys = sys,
Input = input,
Output = output,
System = sys,
InputFileHandle = input,
OutputFileHandle = output,
Buffer = new byte[bufsize],
BufferSize = bufsize,
};
@@ -36,10 +36,10 @@ namespace LibMSPackSharp.Compression
{
run = (bytes > state.BufferSize) ? state.BufferSize : (int)bytes;
if (state.Sys.Read(state.Input, state.Buffer, 0, run) != run)
if (state.System.Read(state.InputFileHandle, state.Buffer, 0, run) != run)
return Error.MSPACK_ERR_READ;
if (state.Sys.Write(state.Output, state.Buffer, 0, run) != run)
if (state.System.Write(state.OutputFileHandle, state.Buffer, 0, run) != run)
return Error.MSPACK_ERR_WRITE;
bytes -= run;

View File

@@ -7,21 +7,13 @@
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
namespace LibMSPackSharp.Compression
{
/// <summary>
/// The "not compressed" method decompressor
/// </summary>
public class NoneState
public class NoneState : BaseDecompressState
{
public SystemImpl Sys { get; set; }
public FileStream Input { get; set; }
public FileStream Output { get; set; }
public byte[] Buffer { get; set; }
public int BufferSize { get; set; }

View File

@@ -116,9 +116,9 @@ namespace LibMSPackSharp.Compression
InputBuffer = new byte[input_buffer_size],
// Initialise decompression state
Sys = system,
Input = input,
Output = output,
System = system,
InputFileHandle = input,
OutputFileHandle = output,
InputBufferSize = (uint)input_buffer_size,
WindowSize = window_size,
WindowPosition = 0,
@@ -203,7 +203,7 @@ namespace LibMSPackSharp.Compression
if (i != 0)
{
if (qtm.Sys.Write(qtm.Output, qtm.Window, qtm.OutputPointer, i) != i)
if (qtm.System.Write(qtm.OutputFileHandle, qtm.Window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
qtm.OutputPointer += i;
@@ -1223,7 +1223,7 @@ namespace LibMSPackSharp.Compression
return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
}
if (qtm.Sys.Write(qtm.Output, window, qtm.OutputPointer, i) != i)
if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
@@ -1388,7 +1388,7 @@ namespace LibMSPackSharp.Compression
if (i >= out_bytes)
break;
if (qtm.Sys.Write(qtm.Output, window, qtm.OutputPointer, i) != i)
if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
@@ -1403,7 +1403,7 @@ namespace LibMSPackSharp.Compression
{
i = (int)out_bytes;
if (qtm.Sys.Write(qtm.Output, window, qtm.OutputPointer, i) != i)
if (qtm.System.Write(qtm.OutputFileHandle, window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
qtm.OutputPointer += i;

View File

@@ -0,0 +1,196 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp
{
internal class Constants
{
#region CAB
// CAB data blocks are <= 32768 bytes in uncompressed form.Uncompressed
// blocks have zero growth. MSZIP guarantees that it won't grow above
// uncompressed size by more than 12 bytes.LZX guarantees it won't grow
// more than 6144 bytes.Quantum has no documentation, but the largest
// block seen in the wild is 337 bytes above uncompressed size.
public const int CAB_BLOCKMAX = 32768;
public const int CAB_INPUTMAX = CAB_BLOCKMAX + 6144;
// input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block
// plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment.
//
// When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be
// up to 65535 bytes, so max input buffer size needed is 65535 + 1
public const int CAB_INPUTMAX_SALVAGE = 65535;
public const int CAB_INPUTBUF = CAB_INPUTMAX_SALVAGE + 1;
// There are no more than 65535 data blocks per folder, so a folder cannot
// be more than 32768*65535 bytes in length.As files cannot span more than
// one folder, this is also their max offset, length and offset+length limit.
public const int CAB_FOLDERMAX = 65535;
public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX;
#endregion
#region CHM
// _PMGHeader
public const int pmgl_Signature = 0x0000;
public const int pmgl_QuickRefSize = 0x0004;
public const int pmgl_PMGIEntries = 0x0008; // Unknown1 in PMGL
public const int pmgl_PrevChunk = 0x000C; // Not in PMGI
public const int pmgl_NextChunk = 0x0010; // Not in PMGI
public const int pmgl_PMGLEntries = 0x0014; // Not in PMGI
public const int pmgl_headerSIZEOF = 0x0014;
public const int pmgi_headerSIZEOF = 0x000C;
// _LZXControlData
public const int lzxcd_Length = 0x0000;
public const int lzxcd_Signature = 0x0004;
public const int lzxcd_Version = 0x0008;
public const int lzxcd_ResetInterval = 0x000C;
public const int lzxcd_WindowSize = 0x0010;
public const int lzxcd_CacheSize = 0x0014;
public const int lzxcd_Unknown1 = 0x0018;
public const int lzxcd_SIZEOF = 0x001C;
// _LZXResetTable
public const int lzxrt_Unknown1 = 0x0000;
public const int lzxrt_NumEntries = 0x0004;
public const int lzxrt_EntrySize = 0x0008;
public const int lzxrt_TableOffset = 0x000C;
public const int lzxrt_UncompLen = 0x0010;
public const int lzxrt_CompLen = 0x0018;
public const int lzxrt_FrameLen = 0x0020;
public const int lzxrt_Entries = 0x0028;
public const int lzxrt_headerSIZEOF = 0x0028;
// Filenames of the system files used for decompression.
// - Content and ControlData are essential.
// - ResetTable is preferred, but SpanInfo can be used if not available
public const string ContentName = "::DataSpace/Storage/MSCompressed/Content";
public const string ControlName = "::DataSpace/Storage/MSCompressed/ControlData";
public const string SpanInfoName = "::DataSpace/Storage/MSCompressed/SpanInfo";
public const string ResetTableName = "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable";
#endregion
#region HLP
// None currently
#endregion
#region KWAJ
public const byte kwajh_Signature1 = 0x00;
public const byte kwajh_Signature2 = 0x04;
public const byte kwajh_CompMethod = 0x08;
public const byte kwajh_DataOffset = 0x0a;
public const byte kwajh_Flags = 0x0c;
public const byte kwajh_SIZEOF = 0x0e;
// Input buffer size during decompression - not worth parameterising IMHO
public const int KWAJ_INPUT_SIZE = (2048);
// Huffman codes that are 9 bits or less are decoded immediately
public const int KWAJ_TABLEBITS = (9);
// Number of codes in each huffman table
public const int KWAJ_MATCHLEN1_SYMS = (16);
public const int KWAJ_MATCHLEN2_SYMS = (16);
public const int KWAJ_LITLEN_SYMS = (32);
public const int KWAJ_OFFSET_SYMS = (64);
public const int KWAJ_LITERAL_SYMS = (256);
// Define decoding table sizes
public const int KWAJ_TABLESIZE = (1 << KWAJ_TABLEBITS);
//public const int KWAJ_MATCHLEN1_TBLSIZE = (KWAJ_MATCHLEN1_SYMS * 4);
public const int KWAJ_MATCHLEN1_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2));
//public const int KWAJ_MATCHLEN2_TBLSIZE = (KWAJ_MATCHLEN2_SYMS * 4);
public const int KWAJ_MATCHLEN2_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2));
//public const int KWAJ_LITLEN_TBLSIZE = (KWAJ_LITLEN_SYMS * 4);
public const int KWAJ_LITLEN_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2));
//public const int KWAJ_OFFSET_TBLSIZE = (KWAJ_OFFSET_SYMS * 4);
public const int KWAJ_OFFSET_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2));
//public const int KWAJ_LITERAL_TBLSIZE = (KWAJ_LITERAL_SYMS * 4);
public const int KWAJ_LITERAL_TBLSIZE = (KWAJ_TABLESIZE + (KWAJ_LITERAL_SYMS * 2));
#endregion
#region LIT
// None currently
#endregion
#region OAB
// _Header
public const int oabhead_VersionHi = 0x0000;
public const int oabhead_VersionLo = 0x0004;
public const int oabhead_BlockMax = 0x0008;
public const int oabhead_TargetSize = 0x000c;
public const int oabhead_SIZEOF = 0x0010;
// _Block
public const int oabblk_Flags = 0x0000;
public const int oabblk_CompSize = 0x0004;
public const int oabblk_UncompSize = 0x0008;
public const int oabblk_CRC = 0x000c;
public const int oabblk_SIZEOF = 0x0010;
// _PatchHeader
public const int patchhead_VersionHi = 0x0000;
public const int patchhead_VersionLo = 0x0004;
public const int patchhead_BlockMax = 0x0008;
public const int patchhead_SourceSize = 0x000c;
public const int patchhead_TargetSize = 0x0010;
public const int patchhead_SourceCRC = 0x0014;
public const int patchhead_TargetCRC = 0x0018;
public const int patchhead_SIZEOF = 0x001c;
// _PatchBlock
public const int patchblk_PatchSize = 0x0000;
public const int patchblk_TargetSize = 0x0004;
public const int patchblk_SourceSize = 0x0008;
public const int patchblk_CRC = 0x000c;
public const int patchblk_SIZEOF = 0x0010;
#endregion
#region SZDD
/// <summary>
/// Input buffer size during decompression - not worth parameterising IMHO
/// </summary>
public const int SZDD_INPUT_SIZE = 2048;
public static readonly byte[] expandSignature = new byte[8]
{
0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33
};
public static readonly byte[] qbasicSignature = new byte[8]
{
0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1
};
#endregion
}
}

View File

@@ -16,8 +16,11 @@
namespace LibMSPackSharp.HLP
{
// TODO
public class Compressor
{
public SystemImpl System { get; set; }
public int Dummy { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.HLP
{
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
// TODO
}
}

View File

@@ -16,8 +16,8 @@
namespace LibMSPackSharp.HLP
{
public class Decompressor
public class Decompressor : BaseDecompressor
{
public int Dummy { get; set; }
// TODO
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.HLP
{
public class DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
// TODO
}
}

View File

@@ -27,6 +27,19 @@ namespace LibMSPackSharp.KWAJ
/// <see cref="Library.DestroyKWAJCompressor(Compressor)"/>
public class Compressor
{
#region Fields
public SystemImpl System { get; set; }
/// <remarks>
/// !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!!
/// </remarks>
public int[] Param { get; set; } = new int[2];
public Error Error { get; set; }
#endregion
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// KWAJ compressed file format.The KWAJ compression format is quick

View File

@@ -1,25 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.KWAJ
{
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
// TODO
/// <remarks>
/// !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!!
/// </remarks>
public int[] Param { get; set; } = new int[2];
public Error Error { get; set; }
}
}

View File

@@ -15,6 +15,10 @@
*/
using System;
using System.IO;
using System.Text;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.KWAJ
{
@@ -25,8 +29,10 @@ namespace LibMSPackSharp.KWAJ
/// </summary>
/// <see cref="Library.CreateKWAJDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyKWAJDecompressor(Decompressor)"/>
public class Decompressor
public class Decompressor : BaseDecompressor
{
#region Public Functionality
/// <summary>
/// Opens a KWAJ file and reads the header.
///
@@ -39,17 +45,37 @@ namespace LibMSPackSharp.KWAJ
/// The filename pointer should be considered "in use" until close() is
/// called on the KWAJ file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mskwaj_decompressor
/// instance being called
/// </param>
/// <param name="filename">
/// the filename of the KWAJ compressed file. This is
/// passed directly to mspack_system::open().
/// </param>
/// <returns>a pointer to a mskwajd_header structure, or NULL on failure</returns>
/// <see cref="Close"/>
public Func<Decompressor, string, Header> Open;
/// <returns>A pointer to a mskwajd_header structure, or NULL on failure</returns>
/// <see cref="Close(Header)"/>
public Header Open(string filename)
{
FileStream fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
Header hdr = new Header();
if (fh != null && hdr != null)
{
hdr.FileHandle = fh;
Error = ReadHeaders(fh, hdr);
}
else
{
if (fh == null)
Error = Error.MSPACK_ERR_OPEN;
if (hdr == null)
Error = Error.MSPACK_ERR_NOMEMORY;
}
if (Error != Error.MSPACK_ERR_OK)
{
System.Close(fh);
hdr = null;
}
return hdr;
}
/// <summary>
/// Closes a previously opened KWAJ file.
@@ -58,13 +84,18 @@ namespace LibMSPackSharp.KWAJ
/// with it. The KWAJ header pointer is now invalid and cannot be
/// used again.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mskwaj_decompressor
/// instance being called
/// </param>
/// <param name="kwaj">the KWAJ file to close</param>
/// <see cref="Open"/>
public Action<Decompressor, Header> Close;
/// <param name="hdr">The KWAJ file to close</param>
/// <see cref="Open(string)"/>
public void Close(Header hdr)
{
if (System == null || hdr == null)
return;
// Close the file handle associated
System.Close(hdr.FileHandle);
Error = Error.MSPACK_ERR_OK;
}
/// <summary>
/// Extracts the compressed data from a KWAJ file.
@@ -72,17 +103,81 @@ namespace LibMSPackSharp.KWAJ
/// This decompresses the compressed KWAJ data stream and writes it to
/// an output file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mskwaj_decompressor
/// instance being called
/// </param>
/// <param name="kwaj">the KWAJ file to extract data from</param>
/// <param name="hdr">The KWAJ file to extract data from</param>
/// <param name="filename">
/// the filename to write the decompressed data to. This
/// is passed directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, Header, string, Error> Extract;
public Error Extract(Header hdr, string filename)
{
FileStream fh = hdr?.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
if (System.Seek(fh, hdr.DataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh;
if ((outfh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null)
return Error = Error.MSPACK_ERR_OPEN;
Error = Error.MSPACK_ERR_OK;
// Decompress based on format
if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_NONE ||
hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
// NONE is a straight copy. XOR is a copy xored with 0xFF
byte[] buf = new byte[KWAJ_INPUT_SIZE];
int read, i;
while ((read = System.Read(fh, buf, 0, KWAJ_INPUT_SIZE)) > 0)
{
if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
for (i = 0; i < read; i++)
{
buf[i] ^= 0xFF;
}
}
if (System.Write(outfh, buf, 0, read) != read)
{
Error = Error.MSPACK_ERR_WRITE;
break;
}
}
if (read < 0)
Error = Error.MSPACK_ERR_READ;
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_SZDD)
{
Error = LZSS.Decompress(System, fh, outfh, KWAJ_INPUT_SIZE, LZSSMode.LZSS_MODE_EXPAND);
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_LZH)
{
LZHKWAJStream lzh = LZHKWAJ.Init(System, fh, outfh);
Error = (lzh != null) ? LZHKWAJ.Decompress(lzh) : Error.MSPACK_ERR_NOMEMORY;
}
else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP)
{
MSZIPDStream zip = MSZIP.Init(System, fh, outfh, KWAJ_INPUT_SIZE, false);
Error = (zip != null) ? MSZIP.DecompressKWAJ(zip) : Error.MSPACK_ERR_NOMEMORY;
}
else
{
Error = Error.MSPACK_ERR_DATAFORMAT;
}
// Close output file
System.Close(outfh);
return Error;
}
/// <summary>
/// Decompresses an KWAJ file to an output file in one step.
@@ -106,21 +201,159 @@ namespace LibMSPackSharp.KWAJ
/// is passed directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, string, string, Error> Decompress;
public Error Decompress(string input, string output)
{
Header hdr = Open(input) as Header;
if (hdr == null)
return Error;
Error error = Extract(hdr, output);
Close(hdr);
return Error = error;
}
#endregion
#region Helpers
/// <summary>
/// Returns the error code set by the most recently called method.
///
/// This is useful for open() which does not return an
/// error code directly.
/// Reads the headers of a KWAJ format file
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mskwaj_decompressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Open"/>
/// <see cref="Search"/>
public Func<Decompressor, Error> LastError;
private Error ReadHeaders(FileStream fh, Header hdr)
{
int i;
// Read in the header
byte[] buf = new byte[16];
if (System.Read(fh, buf, 0, kwajh_SIZEOF) != kwajh_SIZEOF)
return Error.MSPACK_ERR_READ;
// Check for "KWAJ" signature
if ((BitConverter.ToUInt32(buf, kwajh_Signature1) != 0x4A41574B) ||
(BitConverter.ToUInt32(buf, kwajh_Signature2) != 0xD127F088))
{
return Error.MSPACK_ERR_SIGNATURE;
}
// Basic header fields
hdr.CompressionType = (CompressionType)BitConverter.ToUInt16(buf, kwajh_CompMethod);
hdr.DataOffset = BitConverter.ToUInt16(buf, kwajh_DataOffset);
hdr.Headers = (OptionalHeaderFlag)BitConverter.ToUInt16(buf, kwajh_Flags);
hdr.Length = 0;
hdr.Filename = null;
hdr.Extra = null;
hdr.ExtraLength = 0;
// Optional headers
// 4 bytes: length of unpacked file
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASLENGTH))
{
if (System.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
// 2 bytes: unknown purpose
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN1))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
}
// 2 bytes: length of section, then [length] bytes: unknown purpose
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN2))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
i = BitConverter.ToUInt16(buf, 0);
if (System.Seek(fh, i, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
}
// Filename and extension
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME) || hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT))
{
int len;
// Allocate memory for maximum length filename
char[] fn = new char[13];
int fnPtr = 0;
// Copy filename if present
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME))
{
// Read and copy up to 9 bytes of a null terminated string
if ((len = System.Read(fh, buf, 0, 9)) < 2)
return Error.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 9 bytes with no null terminator, reject it
if (i == 9 && buf[8] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (System.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
// Copy extension if present
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT))
{
fn[fnPtr++] = '.';
// Read and copy up to 4 bytes of a null terminated string
if ((len = System.Read(fh, buf, 0, 4)) < 2)
return Error.MSPACK_ERR_READ;
for (i = 0; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 4 bytes with no null terminator, reject it
if (i == 4 && buf[3] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (System.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
fn[fnPtr] = '\0';
}
// 2 bytes: extra text length then [length] bytes of extra text data
if (hdr.Headers.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASEXTRATEXT))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
i = BitConverter.ToUInt16(buf, 0);
byte[] extra = new byte[i + 1];
if (System.Read(fh, extra, 0, i) != i)
return Error.MSPACK_ERR_READ;
extra[i] = 0x00;
hdr.Extra = Encoding.ASCII.GetString(extra, 0, extra.Length);
hdr.ExtraLength = (ushort)i;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.KWAJ
{
public class DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
public Error Error { get; set; }
}
}

View File

@@ -14,6 +14,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System.IO;
namespace LibMSPackSharp.KWAJ
{
/// <summary>
@@ -21,7 +23,7 @@ namespace LibMSPackSharp.KWAJ
///
/// All fields are READ ONLY.
/// </summary>
public class Header
public class Header : BaseHeader
{
/// <summary>
/// The compression type
@@ -58,5 +60,7 @@ namespace LibMSPackSharp.KWAJ
/// Length of extra uncompressed data in the header
/// </summary>
public ushort ExtraLength { get; set; }
public FileStream FileHandle { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
namespace LibMSPackSharp.KWAJ
{
public class HeaderImpl : Header
{
public FileStream FileHandle { get; set; }
}
}

View File

@@ -19,6 +19,6 @@ namespace LibMSPackSharp.LIT
// TODO
public class Compressor
{
public int Dummy { get; set; }
public SystemImpl System { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.LIT
{
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
// TODO
}
}

View File

@@ -16,9 +16,8 @@
namespace LibMSPackSharp.LIT
{
// TODO
public class Decompressor
public class Decompressor : BaseDecompressor
{
public int Dummy { get; set; }
// TODO
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.LIT
{
public class DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
// TODO
}
}

View File

@@ -1,15 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.LIT
{
public class Implementation
{
}
}

View File

@@ -374,13 +374,8 @@ namespace LibMSPackSharp
if (!SystemImpl.ValidSystem(sys))
return null;
return new SZDD.DecompressorImpl()
return new SZDD.Decompressor()
{
Open = SZDD.Implementation.Open,
Close = SZDD.Implementation.Close,
Extract = SZDD.Implementation.Extract,
Decompress = SZDD.Implementation.Decompress,
LastError = SZDD.Implementation.LastError,
System = sys,
Error = Error.MSPACK_ERR_OK,
};
@@ -428,13 +423,8 @@ namespace LibMSPackSharp
if (!SystemImpl.ValidSystem(sys))
return null;
return new KWAJ.DecompressorImpl()
return new KWAJ.Decompressor()
{
Open = KWAJ.Implementation.Open,
Close = KWAJ.Implementation.Close,
Extract = KWAJ.Implementation.Extract,
Decompress = KWAJ.Implementation.Decompress,
LastError = KWAJ.Implementation.LastError,
System = sys,
Error = Error.MSPACK_ERR_OK,
};
@@ -482,11 +472,8 @@ namespace LibMSPackSharp
if (!SystemImpl.ValidSystem(sys))
return null;
return new OAB.DecompressorImpl()
return new OAB.Decompressor()
{
Decompress = OAB.Implementation.Decompress,
DecompressIncremental = OAB.Implementation.DecompressIncremental,
SetParam = OAB.Implementation.Param,
System = sys,
BufferSize = 4096,
};

View File

@@ -27,6 +27,12 @@ namespace LibMSPackSharp.OAB
/// <see cref="Library.DestroyOABCompressor(Compressor)"/>
public class Compressor
{
#region Fields
public SystemImpl System { get; set; }
#endregion
/// <summary>
/// Compress a full OAB file.
///

View File

@@ -1,16 +0,0 @@
/* This file is part of libmspack.
* © 2013 Intel Corporation
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.OAB
{
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
}
}

View File

@@ -15,6 +15,9 @@
*/
using System;
using System.IO;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.OAB
{
@@ -25,8 +28,10 @@ namespace LibMSPackSharp.OAB
/// </summary>
/// <see cref="Library.CreateOABDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyOABDecompressor(Decompressor)"/>
public class Decompressor
public class Decompressor : BaseDecompressor
{
#region Public Functionality
/// <summary>
/// Decompresses a full Offline Address Book file.
///
@@ -34,20 +39,170 @@ namespace LibMSPackSharp.OAB
/// it will be read and the decompressed contents will be written to
/// the output file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msoab_decompressor
/// instance being called
/// </param>
/// <param name="input">
/// the filename of the input file. This is passed
/// The filename of the input file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// the filename of the output file. This is passed
/// The filename of the output file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, string, string, Error> Decompress;
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error Decompress(string input, string output)
{
byte[] hdrbuf = new byte[oabhead_SIZEOF];
LZXDStream lzx = null;
Error ret = Error.MSPACK_ERR_OK;
FileStream infh = System.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(infh);
return ret;
}
if (System.Read(infh, hdrbuf, 0, oabhead_SIZEOF) != oabhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, oabhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, oabhead_VersionLo) != 1)
{
ret = Error.MSPACK_ERR_SIGNATURE;
System.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, oabhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, oabhead_TargetSize);
FileStream outfh = System.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(outfh);
System.Close(infh);
return ret;
}
byte[] buf = new byte[BufferSize];
SystemImpl oabd_sys = System;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = System;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = System;
out_ofh.OrigFile = outfh;
while (target_size != 0)
{
if (System.Read(infh, buf, 0, oabblk_SIZEOF) != oabblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(outfh);
System.Close(infh);
return ret;
}
uint blk_flags = BitConverter.ToUInt32(buf, oabblk_Flags);
uint blk_csize = BitConverter.ToUInt32(buf, oabblk_CompSize);
uint blk_dsize = BitConverter.ToUInt32(buf, oabblk_UncompSize);
uint blk_crc = BitConverter.ToUInt32(buf, oabblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(infh);
return ret;
}
if (blk_flags == 0)
{
// Uncompressed block
if (blk_dsize != blk_csize)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(infh);
return ret;
}
ret = CopyFileHandle(infh, outfh, (int)blk_dsize, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
}
else
{
// LZX compressed block
int window_bits = 17;
while (window_bits < 25 && (1U << window_bits) < blk_dsize)
{
window_bits++;
}
in_ofh.Available = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, BufferSize, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
System.Close(outfh);
System.Close(infh);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(infh, null, in_ofh.Available, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
System.Close(outfh);
System.Close(infh);
return ret;
}
}
target_size -= blk_dsize;
}
System.Close(outfh);
System.Close(infh);
return ret;
}
/// <summary>
/// Decompresses an Offline Address Book with an incremental patch file.
@@ -63,25 +218,184 @@ namespace LibMSPackSharp.OAB
/// in incorrect data being decompressed, which will then fail a checksum
/// test.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msoab_decompressor
/// instance being called
/// </param>
/// <param name="input">
/// the filename of the input file. This is passed
/// The filename of the input file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="base">
/// the filename of the base file to which the
/// <param name="basePath">
/// The filename of the base file to which the
/// incremental patch shall be applied. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// the filename of the output file. This is passed
/// The filename of the output file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, string, string, string, Error> DecompressIncremental;
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error DecompressIncremental(string input, string basePath, string output)
{
byte[] hdrbuf = new byte[patchhead_SIZEOF];
LZXDStream lzx = null;
int window_bits;
uint window_size;
Error ret = Error.MSPACK_ERR_OK;
FileStream infh = System.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(infh);
return ret;
}
if (System.Read(infh, hdrbuf, 0, patchhead_SIZEOF) != patchhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, patchhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, patchhead_VersionLo) != 2)
{
ret = Error.MSPACK_ERR_SIGNATURE;
System.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, patchhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, patchhead_TargetSize);
// We use it for reading block headers too
if (block_max < patchblk_SIZEOF)
block_max = patchblk_SIZEOF;
FileStream basefh = System.Open(basePath, OpenMode.MSPACK_SYS_OPEN_READ);
if (basefh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(basefh);
System.Close(infh);
return ret;
}
FileStream outfh = System.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
byte[] buf = new byte[BufferSize];
SystemImpl oabd_sys = System;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = System;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = System;
out_ofh.OrigFile = outfh;
while (target_size != 0)
{
if (System.Read(infh, buf, 0, patchblk_SIZEOF) != patchblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
uint blk_csize = BitConverter.ToUInt32(buf, patchblk_PatchSize);
uint blk_dsize = BitConverter.ToUInt32(buf, patchblk_TargetSize);
uint blk_ssize = BitConverter.ToUInt32(buf, patchblk_SourceSize);
uint blk_crc = BitConverter.ToUInt32(buf, patchblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_ssize > block_max)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
window_size = (uint)((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 = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, 4096, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
ret = LZX.SetReferenceData(lzx, System, basefh, blk_ssize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(infh, null, in_ofh.Available, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
target_size -= blk_dsize;
}
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
/// <summary>
/// Sets an OAB decompression engine parameter. Available only in OAB
@@ -91,16 +405,95 @@ namespace LibMSPackSharp.OAB
/// buffer by decompressors? The minimum value is 16. The default value
/// is 4096.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msoab_decompressor
/// instance being called
/// </param>
/// <param name="param">the parameter to set</param>
/// <param name="value">the value to set the parameter to</param>
/// <param name="param">The parameter to set</param>
/// <param name="value">The value to set the parameter to</param>
/// <returns>
/// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there
/// is a problem with either parameter or value.
/// </returns>
public Func<Decompressor, Parameters, int, Error> SetParam;
public Error SetParam(Parameters param, int value)
{
if (param == Parameters.MSOABD_PARAM_DECOMPBUF && value >= 16)
{
// Must be at least 16 bytes (patchblk_SIZEOF, oabblk_SIZEOF)
BufferSize = value;
return Error.MSPACK_ERR_OK;
}
return Error.MSPACK_ERR_ARGS;
}
#endregion
#region I/O Methods
private static int SysRead(object baseFile, byte[] buf, int pointer, int size)
{
InternalFile file = baseFile as InternalFile;
if (file == null)
return 0;
int bytes_read;
if (size > file.Available)
size = file.Available;
bytes_read = file.OrigSys.Read(file.OrigFile, buf, pointer, size);
if (bytes_read < 0)
return bytes_read;
file.Available -= bytes_read;
return bytes_read;
}
private static int SysWrite(object baseFile, byte[] buf, int pointer, int size)
{
// Null output file means skip those bytes
if (baseFile == null)
{
return size;
}
else if (baseFile is InternalFile file)
{
int bytes_written = file.OrigSys.Write(file.OrigFile, buf, pointer, size);
if (bytes_written > 0)
file.CRC = Checksum.CRC32(buf, 0, bytes_written, file.CRC);
return bytes_written;
}
else if (baseFile is FileStream impl)
{
return SystemImpl.DefaultSystem.Write(impl, buf, pointer, size);
}
// Unknown file to write to
return -1;
}
#endregion
#region Helpers
private Error CopyFileHandle(FileStream infh, FileStream 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 (System.Read(infh, buf, 0, run) != run)
return Error.MSPACK_ERR_READ;
if (outfh != null && System.Write(outfh, buf, 0, run) != run)
return Error.MSPACK_ERR_WRITE;
bytes_to_copy -= run;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,20 +0,0 @@
/* This file is part of libmspack.
* © 2013 Intel Corporation
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.OAB
{
public class DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
public int BufferSize { get; set; }
// TODO
}
}

View File

@@ -1,491 +0,0 @@
/* This file is part of libmspack.
* © 2013 Intel Corporation
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
/* The Exchange Online Addressbook (OAB or sometimes OAL) is distributed
* as a .LZX file in one of two forms. Either a "full download" containing
* the entire address list, or an incremental binary patch which should be
* applied to a previous version of the full decompressed data.
*
* The contents and format of the decompressed OAB are not handled here.
*
* For a complete description of the format, see the MSDN site:
*
* http://msdn.microsoft.com/en-us/library/cc463914 - [MS-OXOAB].pdf
* http://msdn.microsoft.com/en-us/library/cc483133 - [MS-PATCH].pdf
*/
using System;
using System.IO;
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.OAB
{
public class Implementation
{
#region OAB decompression definitions
private const int oabhead_VersionHi = 0x0000;
private const int oabhead_VersionLo = 0x0004;
private const int oabhead_BlockMax = 0x0008;
private const int oabhead_TargetSize = 0x000c;
private const int oabhead_SIZEOF = 0x0010;
private const int oabblk_Flags = 0x0000;
private const int oabblk_CompSize = 0x0004;
private const int oabblk_UncompSize = 0x0008;
private const int oabblk_CRC = 0x000c;
private const int oabblk_SIZEOF = 0x0010;
private const int patchhead_VersionHi = 0x0000;
private const int patchhead_VersionLo = 0x0004;
private const int patchhead_BlockMax = 0x0008;
private const int patchhead_SourceSize = 0x000c;
private const int patchhead_TargetSize = 0x0010;
private const int patchhead_SourceCRC = 0x0014;
private const int patchhead_TargetCRC = 0x0018;
private const int patchhead_SIZEOF = 0x001c;
private const int patchblk_PatchSize = 0x0000;
private const int patchblk_TargetSize = 0x0004;
private const int patchblk_SourceSize = 0x0008;
private const int patchblk_CRC = 0x000c;
private const int patchblk_SIZEOF = 0x0010;
#endregion
#region OABD_SYS_READ
private static int SysRead(object baseFile, byte[] buf, int pointer, int size)
{
InternalFile file = baseFile as InternalFile;
if (file == null)
return 0;
int bytes_read;
if (size > file.Available)
size = file.Available;
bytes_read = file.OrigSys.Read(file.OrigFile, buf, pointer, size);
if (bytes_read < 0)
return bytes_read;
file.Available -= bytes_read;
return bytes_read;
}
#endregion
#region OABD_SYS_WRITE
private static int SysWrite(object baseFile, byte[] buf, int pointer, int size)
{
// Null output file means skip those bytes
if (baseFile == null)
{
return size;
}
else if (baseFile is InternalFile file)
{
int bytes_written = file.OrigSys.Write(file.OrigFile, buf, pointer, size);
if (bytes_written > 0)
file.CRC = Checksum.CRC32(buf, 0, bytes_written, file.CRC);
return bytes_written;
}
else if (baseFile is FileStream impl)
{
return SystemImpl.DefaultSystem.Write(impl, buf, pointer, size);
}
// Unknown file to write to
return -1;
}
#endregion
#region OABD_DECOMPRESS
public static Error Decompress(Decompressor d, string input, string output)
{
DecompressorImpl self = d as DecompressorImpl;
byte[] hdrbuf = new byte[oabhead_SIZEOF];
LZXDStream lzx = null;
Error ret = Error.MSPACK_ERR_OK;
if (self == null)
return Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
FileStream infh = sys.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
sys.Close(infh);
return ret;
}
if (sys.Read(infh, hdrbuf, 0, oabhead_SIZEOF) != oabhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
sys.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, oabhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, oabhead_VersionLo) != 1)
{
ret = Error.MSPACK_ERR_SIGNATURE;
sys.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, oabhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, oabhead_TargetSize);
FileStream outfh = sys.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
byte[] buf = new byte[self.BufferSize];
SystemImpl oabd_sys = sys;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = sys;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = sys;
out_ofh.OrigFile = outfh;
while (target_size != 0)
{
if (sys.Read(infh, buf, 0, oabblk_SIZEOF) != oabblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
uint blk_flags = BitConverter.ToUInt32(buf, oabblk_Flags);
uint blk_csize = BitConverter.ToUInt32(buf, oabblk_CompSize);
uint blk_dsize = BitConverter.ToUInt32(buf, oabblk_UncompSize);
uint blk_crc = BitConverter.ToUInt32(buf, oabblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
if (blk_flags == 0)
{
// Uncompressed block
if (blk_dsize != blk_csize)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
ret = CopyFileHandle(sys, infh, outfh, (int)blk_dsize, buf, self.BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(infh);
return ret;
}
}
else
{
// LZX compressed block
int window_bits = 17;
while (window_bits < 25 && (1U << window_bits) < blk_dsize)
{
window_bits++;
}
in_ofh.Available = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, self.BufferSize, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(sys, infh, null, in_ofh.Available, buf, self.BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
sys.Close(outfh);
sys.Close(infh);
return ret;
}
}
target_size -= blk_dsize;
}
sys.Close(outfh);
sys.Close(infh);
return ret;
}
#endregion
#region OABD_DECOMPRESS_INCREMENTAL
public static Error DecompressIncremental(Decompressor d, string input, string basePath, string output)
{
DecompressorImpl self = d as DecompressorImpl;
byte[] hdrbuf = new byte[patchhead_SIZEOF];
LZXDStream lzx = null;
int window_bits;
uint window_size;
Error ret = Error.MSPACK_ERR_OK;
if (self == null)
return Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
FileStream infh = sys.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
sys.Close(infh);
return ret;
}
if (sys.Read(infh, hdrbuf, 0, patchhead_SIZEOF) != patchhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
sys.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, patchhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, patchhead_VersionLo) != 2)
{
ret = Error.MSPACK_ERR_SIGNATURE;
sys.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, patchhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, patchhead_TargetSize);
// We use it for reading block headers too
if (block_max < patchblk_SIZEOF)
block_max = patchblk_SIZEOF;
FileStream basefh = sys.Open(basePath, OpenMode.MSPACK_SYS_OPEN_READ);
if (basefh == null)
{
ret = Error.MSPACK_ERR_OPEN;
sys.Close(basefh);
sys.Close(infh);
return ret;
}
FileStream outfh = sys.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
byte[] buf = new byte[self.BufferSize];
SystemImpl oabd_sys = sys;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = sys;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = sys;
out_ofh.OrigFile = outfh;
while (target_size != 0)
{
if (sys.Read(infh, buf, 0, patchblk_SIZEOF) != patchblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
uint blk_csize = BitConverter.ToUInt32(buf, patchblk_PatchSize);
uint blk_dsize = BitConverter.ToUInt32(buf, patchblk_TargetSize);
uint blk_ssize = BitConverter.ToUInt32(buf, patchblk_SourceSize);
uint blk_crc = BitConverter.ToUInt32(buf, patchblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_ssize > block_max)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
window_size = (uint)((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 = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, 4096, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
ret = LZX.SetReferenceData(lzx, sys, basefh, blk_ssize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(sys, infh, null, in_ofh.Available, buf, self.BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
target_size -= blk_dsize;
}
sys.Close(outfh);
sys.Close(basefh);
sys.Close(infh);
return ret;
}
private static Error CopyFileHandle(SystemImpl sys, FileStream infh, FileStream 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, 0, run) != run)
return Error.MSPACK_ERR_READ;
if (outfh != null && sys.Write(outfh, buf, 0, run) != run)
return Error.MSPACK_ERR_WRITE;
bytes_to_copy -= run;
}
return Error.MSPACK_ERR_OK;
}
#endregion
#region OABD_PARAM
public static Error Param(Decompressor d, Parameters param, int value)
{
DecompressorImpl self = d as DecompressorImpl;
if (self != null && param == Parameters.MSOABD_PARAM_DECOMPBUF && value >= 16)
{
// must be at least 16 bytes (patchblk_SIZEOF, oabblk_SIZEOF)
self.BufferSize = value;
return Error.MSPACK_ERR_OK;
}
return Error.MSPACK_ERR_ARGS;
}
#endregion
}
}

View File

@@ -27,6 +27,14 @@ namespace LibMSPackSharp.SZDD
/// <see cref="Library.DestroySZDDCompressor(Compressor)"/>
public class Compressor
{
#region Fields
public SystemImpl System { get; set; }
public Error Error { get; set; }
#endregion
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// SZDD compressed file format. The SZDD compression format is quick

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.SZDD
{
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
public Error Error { get; set; }
}
}

View File

@@ -15,6 +15,10 @@
*/
using System;
using System.IO;
using System.Linq;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.SZDD
{
@@ -25,8 +29,10 @@ namespace LibMSPackSharp.SZDD
/// </summary>
/// <see cref="Library.CreateSZDDDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroySZDDDecompressor(Decompressor)"/>
public class Decompressor
public class Decompressor : BaseDecompressor
{
#region Public Functionality
/// <summary>
/// Opens a SZDD file and reads the header.
///
@@ -39,17 +45,37 @@ namespace LibMSPackSharp.SZDD
/// The filename pointer should be considered "in use" until close() is
/// called on the SZDD file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_decompressor
/// instance being called
/// </param>
/// <param name="filename">
/// the filename of the SZDD compressed file. This is
/// The filename of the SZDD compressed file. This is
/// passed directly to mspack_system::open().
/// </param>
/// <returns>a pointer to a msszddd_header structure, or NULL on failure</returns>
/// <see cref="Close"/>
public Func<Decompressor, string, Header> Open;
/// <returns>A pointer to a msszddd_header structure, or NULL on failure</returns>
/// <see cref="Close(Header)"/>
public Header Open(string filename)
{
FileStream fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
Header hdr = new Header();
if (fh != null && hdr != null)
{
hdr.FileHandle = fh;
Error = ReadHeaders(fh, hdr);
}
else
{
if (fh == null)
Error = Error.MSPACK_ERR_OPEN;
if (hdr == null)
Error = Error.MSPACK_ERR_NOMEMORY;
}
if (Error != Error.MSPACK_ERR_OK)
{
System.Close(fh);
hdr = null;
}
return hdr;
}
/// <summary>
/// Closes a previously opened SZDD file.
@@ -59,13 +85,18 @@ namespace LibMSPackSharp.SZDD
///
/// The SZDD header pointer is now invalid and cannot be used again.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_decompressor
/// instance being called
/// </param>
/// <param name="szdd">the SZDD file to close</param>
/// <see cref="Open"/>
public Action<Decompressor, Header> Close;
/// <param name="szdd">The SZDD file to close</param>
/// <see cref="Open(string)"/>
public void Close(Header hdr)
{
if (System == null || hdr == null)
return;
// Close the file handle associated
System.Close(hdr.FileHandle);
Error = Error.MSPACK_ERR_OK;
}
/// <summary>
/// Extracts the compressed data from a SZDD file.
@@ -73,17 +104,46 @@ namespace LibMSPackSharp.SZDD
/// This decompresses the compressed SZDD data stream and writes it to
/// an output file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_decompressor
/// instance being called
/// </param>
/// <param name="szdd">the SZDD file to extract data from</param>
/// <param name="hdr">The SZDD file to extract data from</param>
/// <param name="filename">
/// filename the filename to write the decompressed data to. This
/// The filename to write the decompressed data to. This
/// is passed directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, Header, string, Error> Extract;
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error Extract(Header hdr, string filename)
{
if (hdr == null)
return Error = Error.MSPACK_ERR_ARGS;
FileStream fh = hdr.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
long dataOffset = (hdr.Format == Format.MSSZDD_FMT_NORMAL) ? 14 : 12;
if (System.Seek(fh, dataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
return Error = Error.MSPACK_ERR_OPEN;
// Decompress the data
Error = LZSS.Decompress(
System,
fh,
outfh,
SZDD_INPUT_SIZE,
hdr.Format == Format.MSSZDD_FMT_NORMAL
? LZSSMode.LZSS_MODE_EXPAND
: LZSSMode.LZSS_MODE_QBASIC);
// Close output file
System.Close(outfh);
return Error;
}
/// <summary>
/// Decompresses an SZDD file to an output file in one step.
@@ -94,35 +154,73 @@ namespace LibMSPackSharp.SZDD
/// open() then extract() then close(), if you do not need to know the
/// SZDD output size or missing character.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_decompressor
/// instance being called
/// </param>
/// <param name="input">
/// the filename of the input SZDD file. This is passed
/// The filename of the input SZDD file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// the filename to write the decompressed data to. This
/// The filename to write the decompressed data to. This
/// is passed directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, string, string, Error> Decompress;
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error Decompress(string input, string output)
{
Header hdr = Open(input) as Header;
if (hdr == null)
return Error;
Error error = Extract(hdr, output);
Close(hdr);
return Error = error;
}
#endregion
#region Helpers
/// <summary>
/// Returns the error code set by the most recently called method.
///
/// This is useful for open() which does not return an
/// error code directly.
/// Reads the headers of an SZDD format file
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_decompressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Open"/>
/// <see cref="Extract"/>
/// <see cref="Decompress"/>
public Func<Decompressor, Error> LastError;
private Error ReadHeaders(FileStream fh, Header hdr)
{
// Read and check signature
byte[] buf = new byte[8];
if (System.Read(fh, buf, 0, 8) != 8)
return Error.MSPACK_ERR_READ;
if (buf.SequenceEqual(expandSignature))
{
// Common SZDD
hdr.Format = Format.MSSZDD_FMT_NORMAL;
// Read the rest of the header
if (System.Read(fh, buf, 0, 6) != 6)
return Error.MSPACK_ERR_READ;
if (buf[0] != 0x41)
return Error.MSPACK_ERR_DATAFORMAT;
hdr.MissingChar = (char)buf[1];
hdr.Length = BitConverter.ToUInt32(buf, 2);
}
if (buf.SequenceEqual(qbasicSignature))
{
// Special QBasic SZDD
hdr.Format = Format.MSSZDD_FMT_QBASIC;
if (System.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.MissingChar = '\0';
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
else
{
return Error.MSPACK_ERR_SIGNATURE;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.SZDD
{
public class DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
public Error Error { get; set; }
}
}

View File

@@ -14,6 +14,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System.IO;
namespace LibMSPackSharp.SZDD
{
/// <summary>
@@ -21,7 +23,7 @@ namespace LibMSPackSharp.SZDD
///
/// All fields are READ ONLY.
/// </summary>
public class Header
public class Header : BaseHeader
{
/// <summary>
/// The file format
@@ -41,5 +43,7 @@ namespace LibMSPackSharp.SZDD
/// an MS-DOS filename (except ".") are valid.
/// </summary>
public char MissingChar { get; set; }
public FileStream FileHandle { get; set; }
}
}

View File

@@ -1,18 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
namespace LibMSPackSharp.SZDD
{
public class HeaderImpl : Header
{
public FileStream FileHandle { get; set; }
}
}

View File

@@ -1,223 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
using System.IO;
using System.Linq;
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.SZDD
{
public class Implementation
{
/// <summary>
/// Input buffer size during decompression - not worth parameterising IMHO
/// </summary>
private const int SZDD_INPUT_SIZE = 2048;
#region SZDDD_OPEN
/// <summary>
/// Opens an SZDD file without decompressing, reads header
/// </summary>
public static Header Open(Decompressor d, string filename)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return null;
SystemImpl sys = self.System;
FileStream fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
HeaderImpl hdr = new HeaderImpl();
if (fh != null && hdr != null)
{
hdr.FileHandle = fh;
self.Error = ReadHeaders(sys, fh, hdr);
}
else
{
if (fh == null)
self.Error = Error.MSPACK_ERR_OPEN;
if (hdr == null)
self.Error = Error.MSPACK_ERR_NOMEMORY;
}
if (self.Error != Error.MSPACK_ERR_OK)
{
sys.Close(fh);
hdr = null;
}
return hdr;
}
#endregion
#region SZDDD_CLOSE
/// <summary>
/// Closes an SZDD file
/// </summary>
public static void Close(Decompressor d, Header hdr)
{
DecompressorImpl self = d as DecompressorImpl;
HeaderImpl hdr_p = hdr as HeaderImpl;
if (self?.System == null || hdr == null)
return;
// Close the file handle associated
self.System.Close(hdr_p.FileHandle);
self.Error = Error.MSPACK_ERR_OK;
}
#endregion
#region SZDDD_READ_HEADERS
private static byte[] expandSignature = new byte[8]
{
0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33
};
private static byte[] qbasicSignature = new byte[8]
{
0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1
};
/// <summary>
/// Reads the headers of an SZDD format file
/// </summary>
public static Error ReadHeaders(SystemImpl sys, FileStream fh, Header hdr)
{
// Read and check signature
byte[] buf = new byte[8];
if (sys.Read(fh, buf, 0, 8) != 8)
return Error.MSPACK_ERR_READ;
if (buf.SequenceEqual(expandSignature))
{
// Common SZDD
hdr.Format = Format.MSSZDD_FMT_NORMAL;
// Read the rest of the header
if (sys.Read(fh, buf, 0, 6) != 6)
return Error.MSPACK_ERR_READ;
if (buf[0] != 0x41)
return Error.MSPACK_ERR_DATAFORMAT;
hdr.MissingChar = (char)buf[1];
hdr.Length = BitConverter.ToUInt32(buf, 2);
}
if (buf.SequenceEqual(qbasicSignature))
{
// Special QBasic SZDD
hdr.Format = Format.MSSZDD_FMT_QBASIC;
if (sys.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.MissingChar = '\0';
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
else
{
return Error.MSPACK_ERR_SIGNATURE;
}
return Error.MSPACK_ERR_OK;
}
#endregion
#region SZDDD_EXTRACT
/// <summary>
/// Decompresses an SZDD file
/// </summary>
public static Error Extract(Decompressor d, Header hdr, string filename)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return Error.MSPACK_ERR_ARGS;
if (hdr == null)
return self.Error = Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
FileStream fh = (hdr as HeaderImpl)?.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
long dataOffset = (hdr.Format == Format.MSSZDD_FMT_NORMAL) ? 14 : 12;
if (sys.Seek(fh, dataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return self.Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh;
if ((outfh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null)
return self.Error = Error.MSPACK_ERR_OPEN;
// Decompress the data
self.Error = LZSS.Decompress(
sys,
fh,
outfh,
SZDD_INPUT_SIZE,
hdr.Format == Format.MSSZDD_FMT_NORMAL
? LZSSMode.LZSS_MODE_EXPAND
: LZSSMode.LZSS_MODE_QBASIC);
// Close output file
sys.Close(outfh);
return self.Error;
}
#endregion
#region SZDDD_DECOMPRESS
/// <summary>
/// Unpacks directly from input to output
/// </summary>
public static Error Decompress(Decompressor d, string input, string output)
{
DecompressorImpl self = d as DecompressorImpl;
if (self == null)
return Error.MSPACK_ERR_ARGS;
Header hdr;
if ((hdr = Open(d, input)) == null)
return self.Error;
Error error = Extract(d, hdr, output);
Close(d, hdr);
return self.Error = error;
}
#endregion
#region SZDDD_ERROR
/// <summary>
/// Returns the last error that occurred
/// </summary>
public static Error LastError(Decompressor d)
{
DecompressorImpl self = d as DecompressorImpl;
return (self != null) ? self.Error : Error.MSPACK_ERR_ARGS;
}
#endregion
}
}