Add initial port of libmspack (nw)

This commit is contained in:
Matt Nadareski
2022-05-19 11:20:44 -07:00
parent 901804e9e4
commit 38e1154bad
79 changed files with 13047 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
/* 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
{
/// <summary>
/// A structure which represents a single cabinet file.
///
/// All fields are READ ONLY.
///
/// If this cabinet is part of a merged cabinet set, the #files and #folders
/// fields are common to all cabinets in the set, and will be identical.
/// </summary>
/// <see cref="Decompressor.Open"/>
/// <see cref="Decompressor.Close"/>
/// <see cref="Decompressor.Search"/>
public class Cabinet
{
/// <summary>
/// The next cabinet in a chained list, if this cabinet was opened with
/// mscab_decompressor::search(). May be NULL to mark the end of the
/// list.
/// </summary>
public Cabinet Next { get; set; }
/// <summary>
/// The filename of the cabinet. More correctly, the filename of the
/// physical file that the cabinet resides in. This is given by the
/// library user and may be in any format.
/// </summary>
public string Filename { get; set; }
/// <summary>
/// The file offset of cabinet within the physical file it resides in.
/// </summary>
public long BaseOffset { get; set; }
/// <summary>
/// The length of the cabinet file in bytes.
/// </summary>
public uint Length { get; set; }
/// <summary>
/// The previous cabinet in a cabinet set, or NULL.
/// </summary>
public Cabinet PreviousCabinet { get; set; }
/// <summary>
/// The next cabinet in a cabinet set, or NULL.
/// </summary>
public Cabinet NextCabinet { get; set; }
/// <summary>
/// The filename of the previous cabinet in a cabinet set, or NULL.
/// </summary>
public string PreviousName { get; set; }
/// <summary>
/// The filename of the next cabinet in a cabinet set, or NULL.
/// </summary>
public string NextName { get; set; }
/// <summary>
/// The name of the disk containing the previous cabinet in a cabinet, or NULL.
/// </summary>
public string PreviousInfo { get; set; }
/// <summary>
/// The name of the disk containing the next cabinet in a cabinet set, or NULL.
/// </summary>
public string NextInfo { get; set; }
/// <summary>
/// A list of all files in the cabinet or cabinet set.
/// </summary>
public InternalFile Files { get; set; }
/// <summary>
/// A list of all folders in the cabinet or cabinet set.
/// </summary>
public Folder Folders { get; set; }
/// <summary>
/// The set ID of the cabinet. All cabinets in the same set should have
/// the same set ID.
/// </summary>
public ushort SetID { get; set; }
/// <summary>
/// The index number of the cabinet within the set. Numbering should
/// start from 0 for the first cabinet in the set, and increment by 1 for
/// each following cabinet.
/// </summary>
public ushort SetIndex { get; set; }
/// <summary>
/// The number of bytes reserved in the header area of the cabinet.
///
/// If this is non-zero and flags has MSCAB_HDR_RESV set, this data can
/// be read by the calling application. It is of the given length,
/// located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the
/// cabinet file.
/// </summary>
/// <see cref="Flags"/>
public ushort HeaderResv { get; set; }
/// <summary>
/// Header flags.
/// </summary>
/// <see cref="PreviousName"/>
/// <see cref="PreviousInfo"/>
/// <see cref="NextName"/>
/// <see cref="NextInfo"/>
/// <see cref="HeaderResv"/>
public HeaderFlags Flags { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
/* 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
*/
namespace LibMSPackSharp.CAB
{
public class CabinetImpl : Cabinet
{
public long BlocksOffset { get; set; }
public int BlockResverved { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// TODO
/// </summary>
public class Compressor
{
public int Dummy { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
/* 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
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// TODO
/// </summary>
public class CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
}
}

View File

@@ -0,0 +1,58 @@
/* 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
*/
using System;
namespace LibMSPackSharp.CAB
{
public class DataBlockHeader
{
#region Fields
/// <summary>
/// CRC32 checksum of the data
/// </summary>
public uint Checksum { get; private set; }
/// <summary>
/// Compressed size of the data
/// </summary>
public ushort CompressedSize { get; private set; }
/// <summary>
/// Uncompressed size of the data
/// </summary>
public ushort UncompressedSize { get; private set; }
#endregion
/// <summary>
/// Private constructor
/// </summary>
private DataBlockHeader() { }
/// <summary>
/// Constructor
/// </summary>
public static Error Create(byte[] data, out DataBlockHeader header)
{
header = null;
if (data == null || data.Length < 0x08)
return Error.MSPACK_ERR_READ;
header = new DataBlockHeader();
header.Checksum = BitConverter.ToUInt32(data, 0x00);
header.CompressedSize = BitConverter.ToUInt16(data, 0x04);
header.UncompressedSize = BitConverter.ToUInt16(data, 0x06);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -0,0 +1,91 @@
/* 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
*/
using System;
namespace LibMSPackSharp.CAB
{
public class DecompressState
{
/// <summary>
/// Current folder we're extracting from
/// </summary>
public Folder Folder { get; set; }
/// <summary>
/// Current folder split we're in
/// </summary>
public FolderData Data { get; set; }
/// <summary>
/// Uncompressed offset within folder
/// </summary>
public uint Offset { get; set; }
/// <summary>
/// Which block are we decompressing?
/// </summary>
public uint Block { get; set; }
/// <summary>
/// Cumulative sum of block output sizes
/// </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>
public CompressionType CompressionType { get; set; }
/// <summary>
/// Decompressor code
/// </summary>
public Func<object, long, Error> Decompress { get; set; }
/// <summary>
/// Decompressor state
/// </summary>
public object DecompressorState { get; set; }
/// <summary>
/// Cabinet where input data comes from
/// </summary>
public Cabinet InputCabinet { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public object InputFileHandle { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public object OutputFileHandle { get; set; }
/// <summary>
/// Input data consumed
/// </summary>
public int IPtr { get; set; }
/// <summary>
/// Input data end
/// </summary>
public int IEnd { get; set; }
/// <summary>
/// One input block of data
/// </summary>
public byte[] Input { get; set; } = new byte[Implementation.CAB_INPUTBUF];
}
}

View File

@@ -0,0 +1,268 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.CAB
{
/// <summary>
/// A decompressor for .CAB (Microsoft Cabinet) files
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="mspack_create_cab_decompressor()"/>
/// <see cref="mspack_destroy_cab_decompressor()"/>
public class Decompressor
{
/// <summary>
/// Opens a cabinet file and reads its contents.
///
/// If the file opened is a valid cabinet file, all headers will be read
/// and a Cabinet structure will be returned, with a full list of
/// folders and files.
///
/// In the case of an error occuring, NULL is returned and the error code
/// is available from last_error().
///
/// The filename pointer should be considered "in use" until close() is
/// called on the cabinet.
/// </summary>
/// <param name="decompressor">A self-referential pointer to the Decompressor instance being called</param>
/// <param name="filename">The filename of the cabinet file. This is passed directly to SystemImpl::open().</param>
/// <returns>A pointer to a Cabinet structure, or NULL on failure</returns>
/// <see cref="Close"/>
/// <see cref="Search"/>
/// <see cref="LastError"/>
public Func<Decompressor, string, CabinetImpl> Open;
/// <summary>
/// Closes a previously opened cabinet or cabinet set.
///
/// This closes a cabinet, all cabinets associated with it via the
/// Cabinet::next, Cabinet::prevcab and
/// Cabinet::nextcab pointers, and all folders and files. All
/// memory used by these entities is freed.
///
/// The cabinet pointer is now invalid and cannot be used again. All
/// Folder and InternalFile pointers from that cabinet or cabinet
/// set are also now invalid, and cannot be used again.
///
/// If the cabinet pointer given was created using search(), it MUST be
/// the cabinet pointer returned by search() and not one of the later
/// cabinet pointers further along the Cabinet::next chain.
///
/// If extra cabinets have been added using append() or prepend(), these
/// will all be freed, even if the cabinet pointer given is not the first
/// cabinet in the set. Do NOT close() more than one cabinet in the set.
///
/// The Cabinet::filename is not freed by the library, as it is
/// not allocated by the library. The caller should free this itself if
/// necessary, before it is lost forever.
/// </summary>
/// <param name="decompressor">A self-referential pointer to the Decompressor instance being called</param>
/// <param name="cab">The cabinet to close</param>
/// <see cref="Open"/>
/// <see cref="Search"/>
/// <see cref="Append"/>
/// <see cref="Prepend"/>
public Action<Decompressor, Cabinet> Close;
/// <summary>
/// Searches a regular file for embedded cabinets.
///
/// This opens a normal file with the given filename and will search the
/// entire file for embedded cabinet files
///
/// If any cabinets are found, the equivalent of open() is called on each
/// potential cabinet file at the offset it was found. All successfully
/// open()ed cabinets are kept in a list.
///
/// The first cabinet found will be returned directly as the result of
/// this method. Any further cabinets found will be chained in a list
/// using the Cabinet::next field.
///
/// In the case of an error occuring anywhere other than the simulated
/// open(), NULL is returned and the error code is available from
/// last_error().
///
/// If no error occurs, but no cabinets can be found in the file, NULL is
/// returned and last_error() returns MSPACK_ERR_OK.
///
/// The filename pointer should be considered in use until close() is
/// called on the cabinet.
///
/// close() should only be called on the result of search(), not on any
/// subsequent cabinets in the Cabinet::next chain.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </param>
/// <param name="filename">
/// the filename of the file to search for cabinets. This
/// is passed directly to SystemImpl::open().
/// </param>
/// <returns>a pointer to a Cabinet structure, or NULL</returns>
/// <see cref="Close"/>
/// <see cref="Open"/>
/// <see cref="LastError"/>
public Func<Decompressor, string, Cabinet> Search;
/// <summary>
/// Appends one Cabinet to another, forming or extending a cabinet
/// set.
///
/// This will attempt to append one cabinet to another such that
/// <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and
/// any folders split between the two cabinets are merged.
///
/// The cabinets MUST be part of a cabinet set -- a cabinet set is a
/// cabinet that spans more than one physical cabinet file on disk -- and
/// must be appropriately matched.
///
/// It can be determined if a cabinet has further parts to load by
/// examining the Cabinet::flags field:
///
/// - if <tt>(flags & MSCAB_HDR_PREVCAB)</tt> is non-zero, there is a
/// predecessor cabinet to open() and prepend(). Its MS-DOS
/// case-insensitive filename is Cabinet::prevname
/// - if <tt>(flags & MSCAB_HDR_NEXTCAB)</tt> is non-zero, there is a
/// successor cabinet to open() and append(). Its MS-DOS case-insensitive
/// filename is Cabinet::nextname
///
/// If the cabinets do not match, an error code will be returned. Neither
/// cabinet has been altered, and both should be closed seperately.
///
/// Files and folders in a cabinet set are a single entity. All cabinets
/// in a set use the same file list, which is updated as cabinets in the
/// set are added. All pointers to Folder and InternalFile
/// structures in either cabinet must be discarded and re-obtained after
/// merging.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </param>
/// <param name="cab">
/// the cabinet which will be appended to,
/// predecessor of nextcab
/// </param>
/// <param name="nextcab">
/// the cabinet which will be appended,
/// successor of cab
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="Prepend"/>
/// <see cref="Open"/>
/// <see cref="Close"/>
public Func<Decompressor, Cabinet, Cabinet, Error> Append;
/// <summary>
/// Prepends one Cabinet to another, forming or extending a
/// cabinet set.
///
/// This will attempt to prepend one cabinet to another, such that
/// <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In
/// all other respects, it is identical to append(). See append() for the
/// full documentation.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </param>
/// <param name="cab">
/// the cabinet which will be prepended to,
/// successor of nextcab
/// </param>
/// <param name="nextcab">
/// the cabinet which will be prepended,
/// predecessor of cab
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="Append"/>
/// <see cref="Open"/>
/// <see cref="Close"/>
public Func<Decompressor, Cabinet, Cabinet, Error> Prepend;
/// <summary>
/// Extracts a file from a cabinet or cabinet set.
///
/// This extracts a compressed file in a cabinet and writes it to the given
/// filename.
///
/// The MS-DOS filename of the file, InternalFile::filename, is NOT USED
/// by extract(). The caller must examine this MS-DOS filename, copy and
/// change it as necessary, create directories as necessary, and provide
/// the correct filename as a parameter, which will be passed unchanged
/// to the decompressor's SystemImpl::open()
///
/// If the file belongs to a split folder in a multi-part cabinet set,
/// and not enough parts of the cabinet set have been loaded and appended
/// or prepended, an error will be returned immediately.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </param>
/// <param name="file">the file to be decompressed</param>
/// <param name="filename">the filename of the file being written to</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, InternalFile, string, Error> Extract;
/// <summary>
/// Sets a CAB decompression engine parameter.
///
/// The following parameters are defined:
/// - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a
/// buffer when using search()? The minimum value is 4. The default
/// value is 32768.
/// - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad
/// checksums and recover from decompression errors in MS-ZIP
/// compressed folders. The default value is 0 (don't recover).
/// - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input
/// bit buffer by decompressors? The minimum value is 4. The default
/// value is 4096.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </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>
/// <see cref="Search"/>
/// <see cref="Extract"/>
public Func<Decompressor, Parameters, int, Error> SetParam;
/// <summary>
/// Returns the error code set by the most recently called method.
///
/// This is useful for open() and search(), which do not return an error
/// code directly.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Decompressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Open"/>
/// <see cref="Search"/>
public Func<Decompressor, Error> LastError;
}
}

View File

@@ -0,0 +1,30 @@
/* 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
*/
namespace LibMSPackSharp.CAB
{
public class DecompressorImpl : Decompressor
{
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; }
}
}

View File

@@ -0,0 +1,113 @@
/* 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
*/
using System;
namespace LibMSPackSharp.CAB
{
[Flags]
public enum CompressionType : ushort
{
COMPTYPE_MASK = 0x000f,
COMPTYPE_NONE = 0x0000,
COMPTYPE_MSZIP = 0x0001,
COMPTYPE_QUANTUM = 0x0002,
COMPTYPE_LZX = 0x0003,
}
[Flags]
public enum FileAttributes : byte
{
/// <summary>
/// Indicates the file is write protected.
/// </summary>
MSCAB_ATTRIB_RDONLY = 0x01,
/// <summary>
/// Indicates the file is hidden.
/// </summary>
MSCAB_ATTRIB_HIDDEN = 0x02,
/// <summary>
/// Indicates the file is a operating system file.
/// </summary>
MSCAB_ATTRIB_SYSTEM = 0x04,
/// <summary>
/// Indicates the file is "archived".
/// </summary>
MSCAB_ATTRIB_ARCH = 0x20,
/// <summary>
/// Indicates the file is an executable program.
/// </summary>
MSCAB_ATTRIB_EXEC = 0x40,
/// <summary>
/// Indicates the filename is in UTF8 format rather than ISO-8859-1.
/// </summary>
MSCAB_ATTRIB_UTF_NAME = 0x80,
}
[Flags]
public enum FileFlags : ushort
{
CONTINUED_FROM_PREV = 0xFFFD,
CONTINUED_TO_NEXT = 0xFFFE,
CONTINUED_PREV_AND_NEXT = 0xFFFF,
}
[Flags]
public enum HeaderFlags : ushort
{
/// <summary>
/// Indicates the cabinet is part of a cabinet set, and has a predecessor cabinet.
/// </summary>
MSCAB_HDR_PREVCAB = 0x0001,
/// <summary>
/// Indicates the cabinet is part of a cabinet set, and has a successor cabinet.
/// </summary>
MSCAB_HDR_NEXTCAB = 0x0002,
/// <summary>
/// Indicates the cabinet has reserved header space.
/// </summary>
MSCAB_HDR_RESV = 0x0004,
}
public enum Parameters
{
/// <summary>
/// Search buffer size.
/// </summary>
MSCABD_PARAM_SEARCHBUF = 0,
/// <summary>
/// Repair MS-ZIP streams?
/// </summary>
MSCABD_PARAM_FIXMSZIP = 1,
/// <summary>
/// Size of decompression buffer
/// </summary>
MSCABD_PARAM_DECOMPBUF = 2,
/// <summary>
/// salvage data from bad cabinets?
/// If enabled, open() will skip file with bad folder indices or filenames
/// rather than reject the whole cabinet, and extract() will limit rather than
/// reject files with invalid offsets and lengths, and bad data block checksums
/// will be ignored. Available only in CAB decoder version 2 and above.
/// </summary>
MSCABD_PARAM_SALVAGE = 3,
}
}

View File

@@ -0,0 +1,61 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// A structure which represents a single folder in a cabinet or cabinet set.
///
/// All fields are READ ONLY.
///
/// A folder is a single compressed stream of data. When uncompressed, it
/// holds the data of one or more files. A folder may be split across more
/// than one cabinet.
/// </summary>
public class Folder
{
/// <summary>
/// A pointer to the next folder in this cabinet or cabinet set, or NULL
/// if this is the final folder.
/// </summary>
public Folder Next { get; set; }
/// <summary>
/// The compression format used by this folder.
///
/// The macro MSCABD_COMP_METHOD() should be used on this field to get
/// the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get
/// the "compression level".
/// </summary>
/// <see cref="MSCABD_COMP_METHOD()"/>
/// <see cref="MSCABD_COMP_LEVEL()"/>
public CompressionType CompressionType { get; set; }
/// <summary>
/// The total number of data blocks used by this folder. This includes
/// data blocks present in other files, if this folder spans more than
/// one cabinet.
/// </summary>
public ushort NumBlocks { get; set; }
/// <summary>
/// Returns the compression method used by a folder.
/// </summary>
/// <param name="compType">a <see cref="CompressionType"/> value</param>
/// <returns>a <see cref="CompressionType"/> value</returns>
public CompressionType MSCABD_COMP_LEVEL(CompressionType compType) => (CompressionType)((((ushort)compType) >> 8) & 0x1F);
}
}

View File

@@ -0,0 +1,29 @@
/* 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
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// There is one of these for every cabinet a folder spans
/// </summary>
public class FolderData
{
public FolderData Next { get; set; }
/// <summary>
/// Cabinet file of this folder span
/// </summary>
public Cabinet Cab { get; set; }
/// <summary>
/// Cabinet offset of first datablock
/// </summary>
public long Offset { get; set; }
};
}

View File

@@ -0,0 +1,36 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CAB
{
public class FolderImpl : Folder
{
/// <summary>
/// Where are the data blocks?
/// </summary>
public FolderData Data { get; set; }
/// <summary>
/// First file needing backwards merge
/// </summary>
public InternalFile MergePrev { get; set; }
/// <summary>
/// First file needing forwards merge
/// </summary>
public InternalFile MergeNext { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// A structure which represents a single file in a cabinet or cabinet set.
///
/// All fields are READ ONLY.
/// </summary>
public class InternalFile
{
/// <summary>
/// The next file in the cabinet or cabinet set, or NULL if this is the final file.
/// </summary>
public InternalFile Next { get; set; }
/// <summary>
/// The filename of the file.
///
/// A null terminated string of up to 255 bytes in length, it may be in
/// either ISO-8859-1 or UTF8 format, depending on the file attributes.
/// </summary>
/// <see cref="Attributes"/>
public string Filename { get; set; }
/// <summary>
/// The uncompressed length of the file, in bytes.
/// </summary>
public uint Length { get; set; }
/// <summary>
/// File attributes.
/// </summary>
public FileAttributes Attributes { get; set; }
/// <summary>
/// File's last modified time, hour field.
/// </summary>
public byte LastModifiedTimeHour { get; set; }
/// <summary>
/// File's last modified time, minute field.
/// </summary>
public byte LastModifiedTimeMinute { get; set; }
/// <summary>
/// File's last modified time, second field.
/// </summary>
public byte LastModifiedTimeSecond { get; set; }
/// <summary>
/// File's last modified date, day field.
/// </summary>
public byte LastModifiedDateDay { get; set; }
/// <summary>
/// File's last modified date, month field.
/// </summary>
public byte LastModifiedDateMonth { get; set; }
/// <summary>
/// File's last modified date, year field.
/// </summary>
public int LastModifiedDateYear { get; set; }
/// <summary>
/// A pointer to the folder that contains this file.
/// </summary>
public Folder Folder { get; set; }
/// <summary>
/// The uncompressed offset of this file in its folder.
/// </summary>
public uint Offset { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
/* 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
*/
namespace LibMSPackSharp.CAB
{
/// <summary>
/// The "not compressed" method decompressor
/// </summary>
public class NoneState
{
public SystemImpl Sys { get; set; }
public object Input { get; set; }
public object Output { get; set; }
public byte[] Buffer { get; set; }
public int BufferSize { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents a file to be placed in a CHM helpfile.
///
/// A contiguous array of these structures should be passed to
/// Compressor.Generate(). The array list is terminated with an
/// entry whose InternalFile.Section field is set to #MSCHMC_ENDLIST, the
/// other fields in this entry are ignored.
/// </summary>
public class CompressFile
{
/// <summary>
/// One of #MSCHMC_ENDLIST, #MSCHMC_UNCOMP or #MSCHMC_MSCOMP.
/// </summary>
public SectionType Section { get; set; }
/// <summary>
/// The filename of the source file that will be added to the CHM. This
/// is passed directly to mspack_system::open()
/// </summary>
public string Filename { get; set; }
/// <summary>
/// The full path and filename of the file within the CHM helpfile, a
/// UTF-1 encoded null-terminated string.
/// </summary>
public string CHMFilename { get; set; }
/// <summary>
/// The length of the file, in bytes. This will be adhered to strictly
/// and a read error will be issued if this many bytes cannot be read
/// from the real file at CHM generation time.
/// </summary>
public long Length { get; set; }
}
}

View File

@@ -0,0 +1,157 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A compressor for .CHM (Microsoft HTMLHelp) files.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateCHMCompressor(SystemImpl)"/>
/// <see cref="Library.DestroyCHMCompressor(Compressor)"/>
public class Compressor
{
/// <summary>
/// Generates a CHM help file.
///
/// The help file will contain up to two sections, an Uncompressed
/// section and potentially an MSCompressed (LZX compressed)
/// section.
///
/// While the contents listing of a CHM file is always in lexical order,
/// the file list passed in will be taken as the correct order for files
/// within the sections. It is in your interest to place similar files
/// together for better compression.
///
/// There are two modes of generation, to use a temporary file or not to
/// use one. See use_temporary_file() for the behaviour of generate() in
/// these two different modes.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_compressor
/// instance being called
/// </param>
/// <param name="fileList">
/// an array of mschmc_file structures, terminated
/// with an entry whose mschmc_file::section field is
/// #MSCHMC_ENDLIST. The order of the list is
/// preserved within each section. The length of any
/// mschmc_file::chm_filename string cannot exceed
/// roughly 4096 bytes. Each source file must be able
/// to supply as many bytes as given in the
/// mschmc_file::length field.
/// </param>
/// <param name="outputFile">
/// the file to write the generated CHM helpfile to.
/// This is passed directly to mspack_system::open()
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="UseTemporaryFile"/>
/// <see cref="SetParam"/>
public Func<Compressor, CompressFile[], string, Error> Generate;
/// <summary>
/// Specifies whether a temporary file is used during CHM generation.
///
/// The CHM file format includes data about the compressed section (such
/// as its overall size) that is stored in the output CHM file prior to
/// the compressed section itself. This unavoidably requires that the
/// compressed section has to be generated, before these details can be
/// set. There are several ways this can be handled. Firstly, the
/// compressed section could be generated entirely in memory before
/// writing any of the output CHM file.This approach is not used in
/// libmspack, as the compressed section can exceed the addressable
/// memory space on most architectures.
///
/// libmspack has two options, either to write these unknowable sections
/// with blank data, generate the compressed section, then re-open the
/// output file for update once the compressed section has been
/// completed, or to write the compressed section to a temporary file,
/// then write the entire output file at once, performing a simple
/// file-to-file copy for the compressed section.
///
/// The simple solution of buffering the entire compressed section in
/// memory can still be used, if desired.As the temporary file's
/// filename is passed directly to mspack_system::open(), it is possible
/// for a custom mspack_system implementation to hold this file in memory,
/// without writing to a disk.
///
/// If a temporary file is set, generate() performs the following
/// sequence of events: the temporary file is opened for writing, the
/// compression algorithm writes to the temporary file, the temporary
/// file is closed.Then the output file is opened for writing and the
/// temporary file is re-opened for reading.The output file is written
/// and the temporary file is read from. Both files are then closed.The
/// temporary file itself is not deleted. If that is desired, the
/// temporary file should be deleted after the completion of generate(),
/// if it exists.
///
/// If a temporary file is set not to be used, generate() performs the
/// following sequence of events: the output file is opened for writing,
/// then it is written and closed.The output file is then re-opened for
/// update, the appropriate sections are seek() ed to and re-written, then
/// the output file is closed.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_compressor
/// instance being called
/// </param>
/// <param name="useTempFile">
/// non-zero if the temporary file should be used,
/// zero if the temporary file should not be used.
/// </param>
/// <param name="tempFile">
/// a file to temporarily write compressed data to,
/// before opening it for reading and copying the
/// contents to the output file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="Generate"/>
public Func<Compressor, bool, string, Error> UseTemporaryFile;
/// <summary>
/// Sets a CHM compression engine parameter.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_compressor
/// instance being called
/// </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>
/// <see cref="Generate"/>
public Func<Compressor, Parameters, int, Error> SetParam;
/// <summary>
/// Returns the error code set by the most recently called method.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_compressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="SetParam"/>
/// <see cref="Generate"/>
public Func<Compressor, Error> LastError;
}
}

View File

@@ -0,0 +1,22 @@
/* 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 CompressorImpl : Compressor
{
public SystemImpl System { get; set; }
public string TempFile { get; set; }
public bool UseTempFile { get; set; }
public Error Error { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents a file stored in a CHM helpfile.
///
/// All fields are READ ONLY.
/// </summary>
public class DecompressFile
{
/// <summary>
/// A pointer to the next file in the list, or NULL if this is the final file.
/// </summary>
public DecompressFile Next { get; set; }
/// <summary>
/// A pointer to the section that this file is located in. Indirectly,
/// it also points to the CHM helpfile the file is located in.
/// </summary>
public Section Section { get; set; }
/// <summary>
/// The offset within the section data that this file is located at.
/// </summary>
public long Offset { get; set; }
/// <summary>
/// The length of this file, in bytes
/// </summary>
public long Length { get; set; }
/// <summary>
/// The filename of this file -- a null terminated string in UTF-8.
/// </summary>
public string Filename { get; set; }
}
}

View File

@@ -0,0 +1,51 @@
/* 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 LibMSPackSharp.Compression;
namespace LibMSPackSharp.CHM
{
public class DecompressState
{
/// <summary>
/// CHM file being decompressed
/// </summary>
public Header Header { get; set; }
/// <summary>
/// Uncompressed offset within folder
/// </summary>
public long Offset { get; set; }
/// <summary>
/// Offset in input file
/// </summary>
public long InOffset { get; set; }
/// <summary>
/// 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 object InputFileHandle { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public object OutputFileHandle { get; set; }
}
}

View File

@@ -0,0 +1,185 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A decompressor for .CHM (Microsoft HTMLHelp) files
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateCHMDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyCHMDecompressor(Decompressor)"/>
public class Decompressor
{
/// <summary>
/// Opens a CHM helpfile and reads its contents.
///
/// If the file opened is a valid CHM helpfile, all headers will be read
/// and a mschmd_header structure will be returned, with a full list of
/// files.
///
/// In the case of an error occuring, NULL is returned and the error code
/// is available from last_error().
///
/// The filename pointer should be considered "in use" until close() is
/// called on the CHM helpfile.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <param name="filename">
/// the filename of the CHM helpfile. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>a pointer to a mschmd_header structure, or NULL on failure</returns>
/// <see cref="Close"/>
public Func<Decompressor, string, Header> Open;
/// <summary>
/// Closes a previously opened CHM helpfile.
///
/// This closes a CHM helpfile, frees the mschmd_header and all
/// mschmd_file structures associated with it (if any). This works on
/// both helpfiles opened with open() and helpfiles opened with
/// fast_open().
///
/// The CHM header pointer is now invalid and cannot be used again. All
/// mschmd_file pointers referencing that CHM are also now invalid, and
/// cannot be used again.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <param name="chm">the CHM helpfile to close</param>
/// <see cref="Open"/>
/// <see cref="FastOpen"/>
public Action<Decompressor, Header> Close;
/// <summary>
/// Extracts a file from a CHM helpfile.
///
/// This extracts a file from a CHM helpfile and writes it to the given
/// filename.The filename of the file, mscabd_file::filename, is not
/// used by extract(), but can be used by the caller as a guide for
/// constructing an appropriate filename.
///
/// This method works both with files found in the mschmd_header::files
/// and mschmd_header::sysfiles list and mschmd_file structures generated
/// on the fly by fast_find().
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <param name="file">the file to be decompressed</param>
/// <param name="filename">the filename of the file being written to</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Func<Decompressor, DecompressFile, string, Error> Extract;
/// <summary>
/// Returns the error code set by the most recently called method.
///
/// This is useful for open() and fast_open(), which do not return an
/// error code directly.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Open"/>
/// <see cref="Extract"/>
public Func<Decompressor, Error> LastError;
/// <summary>
/// Opens a CHM helpfile quickly.
///
/// If the file opened is a valid CHM helpfile, only essential headers
/// will be read.A mschmd_header structure will be still be returned, as
/// with open(), but the mschmd_header::files field will be NULL.No
/// files details will be automatically read.The fast_find() method
/// must be used to obtain file details.
///
/// In the case of an error occuring, NULL is returned and the error code
/// is available from last_error().
///
/// The filename pointer should be considered "in use" until close() is
/// called on the CHM helpfile.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <param name="filename">
/// the filename of the CHM helpfile. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>a pointer to a mschmd_header structure, or NULL on failure</returns>
/// <see cref="Open"/>
/// <see cref="Close"/>
/// <see cref="FastFind"/>
/// <see cref="Extract"/>
public Func<Decompressor, string, Header> FastOpen;
/// <summary>
/// Finds file details quickly.
///
/// Instead of reading all CHM helpfile headers and building a list of
/// files, fast_open() and fast_find() are intended for finding file
/// details only when they are needed.The CHM file format includes an
/// on-disk file index to allow this.
///
/// Given a case-sensitive filename, fast_find() will search the on-disk
/// index for that file.
///
/// If the file was found, the caller-provided mschmd_file structure will
/// be filled out like so:
/// - section: the correct value for the found file
/// - offset: the correct value for the found file
/// - length: the correct value for the found file
/// - all other structure elements: NULL or 0
///
/// If the file was not found, MSPACK_ERR_OK will still be returned as the
/// result, but the caller-provided structure will be filled out like so:
/// - section: NULL
/// - offset: 0
/// - length: 0
/// - all other structure elements: NULL or 0
///
/// This method is intended to be used in conjunction with CHM helpfiles
/// opened with fast_open(), but it also works with helpfiles opened
/// using the regular open().
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mschm_decompressor
/// instance being called
/// </param>
/// <param name="chm">the CHM helpfile to search for the file</param>
/// <param name="filename">the filename of the file to search for</param>
/// <param name="f_ptr">a pointer to a caller-provded mschmd_file structure</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="Open"/>
/// <see cref="Close"/>
/// <see cref="FastFind"/>
/// <see cref="Extract"/>
public Func<Decompressor, Header, string, DecompressFile, Error> FastFind;
}
}

View File

@@ -0,0 +1,20 @@
/* 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 DecompressorImpl : Decompressor
{
public SystemImpl System { get; set; }
public DecompressState State { get; set; }
public Error Error { get; set; }
}
}

View File

@@ -0,0 +1,83 @@
/* 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
*/
namespace LibMSPackSharp.CHM
{
public enum Parameters
{
/// <summary>
/// Sets the "timestamp" of the CHM file
/// generated. This is not a timestamp, see mschmd_header::timestamp
/// for a description. If this timestamp is 0, generate() will use its
/// own algorithm for making a unique ID, based on the lengths and
/// names of files in the CHM itself. Defaults to 0, any value between
/// 0 and (2^32)-1 is valid.
/// </summary>
MSCHMC_PARAM_TIMESTAMP = 0,
/// <summary>
/// Sets the "language" of the CHM file
/// generated. This is not the language used in the CHM file, but the
/// language setting of the user who ran the HTMLHelp compiler. It
/// defaults to 0x0409. The valid range is between 0x0000 and 0x7F7F.
/// </summary>
MSCHMC_PARAM_LANGUAGE = 1,
/// <summary>
/// Sets the size of the LZX history window,
/// which is also the interval at which the compressed data stream can be
/// randomly accessed. The value is not a size in bytes, but a power of
/// two. The default value is 16 (which makes the window 2^16 bytes, or
/// 64 kilobytes), the valid range is from 15 (32 kilobytes) to 21 (2
/// megabytes).
/// </summary>
MSCHMC_PARAM_LZXWINDOW = 2,
/// <summary>
/// Sets the "density" of quick reference
/// entries stored at the end of directory listing chunk. Each chunk is
/// 4096 bytes in size, and contains as many file entries as there is
/// room for. At the other end of the chunk, a list of "quick reference"
/// pointers is included. The offset of every 'N'th file entry is given a
/// quick reference, where N = (2 ^ density) + 1.The default density is
/// 2. The smallest density is 0 (N = 2), the maximum is 10 (N = 1025). As
/// each file entry requires at least 5 bytes, the maximum number of
/// entries in a single chunk is roughly 800, so the maximum value 10
/// can be used to indicate there are no quickrefs at all.
/// </summary>
MSCHMC_PARAM_DENSITY = 3,
/// <summary>
/// Sets whether or not to include quick lookup
/// index chunk(s), in addition to normal directory listing chunks. A
/// value of zero means no index chunks will be created, a non-zero value
/// means index chunks will be created. The default is zero, "don't
/// create an index".
/// </summary>
MSCHMC_PARAM_INDEX = 4,
}
public enum SectionType
{
/// <summary>
/// end of CHM file list
/// </summary>
MSCHMC_ENDLIST = 0,
/// <summary>
/// this file is in the Uncompressed section
/// </summary>
MSCHMC_UNCOMP = 1,
/// <summary>
/// this file is in the MSCompressed section
/// </summary>
MSCHMC_MSCOMP = 2,
}
}

View File

@@ -0,0 +1,135 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents a CHM helpfile.
///
/// All fields are READ ONLY.
/// </summary>
public class Header
{
/// <summary>
/// The version of the CHM file format used in this file.
/// </summary>
public uint Version { get; set; }
/// <summary>
/// The "timestamp" of the CHM helpfile.
///
/// It is the lower 32 bits of a 64-bit value representing the number of
/// centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful
/// as a timestamp, but it is useful as a semi-unique ID.
/// </summary>
public uint Timestamp { get; set; }
/// <summary>
/// The default Language and Country ID (LCID) of the user who ran the
/// HTMLHelp Compiler. This is not the language of the CHM file itself.
/// </summary>
public uint Language { get; set; }
/// <summary>
/// The filename of the CHM helpfile. This is given by the library user
/// and may be in any format.
/// </summary>
public string Filename { get; set; }
/// <summary>
/// The length of the CHM helpfile, in bytes.
/// </summary>
public long Length { get; set; }
/// <summary>
/// A list of all non-system files in the CHM helpfile.
/// </summary>
public DecompressFile Files { get; set; }
/// <summary>
/// A list of all system files in the CHM helpfile.
///
/// System files are files which begin with "::". They are meta-files
/// generated by the CHM creation process.
/// </summary>
public DecompressFile SysFiles { get; set; }
/// <summary>
/// The section 0 (uncompressed) data in this CHM helpfile.
/// </summary>
public UncompressedSection Sec0 { get; set; }
/// <summary>
/// The section 1 (MSCompressed) data in this CHM helpfile.
/// </summary>
public MSCompressedSection Sec1 { get; set; }
/// <summary>
/// The file offset of the first PMGL/PMGI directory chunk.
/// </summary>
public long DirOffset { get; set; }
/// <summary>
/// The number of PMGL/PMGI directory chunks in this CHM helpfile.
/// </summary>
public uint NumChunks { get; set; }
/// <summary>
/// The size of each PMGL/PMGI chunk, in bytes.
/// </summary>
public uint ChunkSize { get; set; }
/// <summary>
/// The "density" of the quick-reference section in PMGL/PMGI chunks.
/// </summary>
public uint Density { get; set; }
/// <summary>
/// The depth of the index tree.
///
/// - if 1, there are no PMGI chunks, only PMGL chunks.
/// - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks.
/// - if 3, the root PMGI chunk points to secondary PMGI chunks, which in turn point to PMGL chunks.
/// - and so on...
/// </summary>
public uint Depth { get; set; }
/// <summary>
/// The number of the root PMGI chunk.
///
/// If there is no index in the CHM helpfile, this will be 0xFFFFFFFF.
/// </summary>
public uint IndexRoot { get; set; }
/// <summary>
/// The number of the first PMGL chunk. Usually zero.
/// Available only in CHM decoder version 2 and above.
/// </summary>
public uint FirstPMGL { get; set; }
/// <summary>
/// The number of the last PMGL chunk. Usually num_chunks-1.
/// Available only in CHM decoder version 2 and above.
/// </summary>
public uint LastPMGL { get; set; }
/// <summary>
/// A cache of loaded chunks, filled in by mschm_decoder::fast_find().
/// Available only in CHM decoder version 2 and above.
/// </summary>
public byte[][] ChunkCache { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents the LZX compressed section of a CHM helpfile.
///
/// All fields are READ ONLY.
/// </summary>
public class MSCompressedSection : Section
{
/// <summary>
/// A pointer to the meta-file which represents all LZX compressed data.
/// </summary>
public DecompressFile Content { get; set; }
/// <summary>
/// A pointer to the file which contains the LZX control data.
/// </summary>
public DecompressFile Control { get; set; }
/// <summary>
/// A pointer to the file which contains the LZX reset table.
/// </summary>
public DecompressFile ResetTable { get; set; }
/// <summary>
/// A pointer to the file which contains the LZX span information.
/// Available only in CHM decoder version 2 and above.
/// </summary>
public DecompressFile SpanInfo { get; set; }
}
}

View File

@@ -0,0 +1,41 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents a section of a CHM helpfile.
///
/// All fields are READ ONLY.
///
/// Not used directly, but used as a generic base type for
/// mschmd_sec_uncompressed and mschmd_sec_mscompressed.
/// </summary>
public class Section
{
/// <summary>
/// A pointer to the CHM helpfile that contains this section.
/// </summary>
public Header Header { get; set; }
/// <summary>
/// The section ID. Either 0 for the uncompressed section
/// mschmd_sec_uncompressed, or 1 for the LZX compressed section
/// mschmd_sec_mscompressed. No other section IDs are known.
/// </summary>
public uint ID { get; set; }
}
}

View File

@@ -0,0 +1,31 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.CHM
{
/// <summary>
/// A structure which represents the uncompressed section of a CHM helpfile.
///
/// All fields are READ ONLY.
/// </summary>
public class UncompressedSection : Section
{
/// <summary>
/// The file offset of where this section begins in the CHM helpfile.
/// </summary>
public long Offset { get; set; }
}
}

View File

@@ -0,0 +1,111 @@
/*
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
* code or tables extracted from it, as desired without restriction.
*
* First, the polynomial itself and its table of feedback terms. The
* polynomial is
* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
*
* Note that we take it "backwards" and put the highest-order term in
* the lowest-order bit. The X^32 term is "implied"; the LSB is the
* X^31 term, etc. The X^0 term (usually shown as "+1") results in
* the MSB being 1
*
* Note that the usual hardware shift register implementation, which
* is what we're using (we're merely optimizing it by doing eight-bit
* chunks at a time) shifts bits into the lowest-order term. In our
* implementation, that means shifting towards the right. Why do we
* do it this way? Because the calculated CRC must be transmitted in
* order from highest-order term to lowest-order term. UARTs transmit
* characters in order from LSB to MSB. By storing the CRC this way
* we hand it to the UART in the order low-byte to high-byte; the UART
* sends each low-bit to hight-bit; and the result is transmission bit
* by bit from highest- to lowest-order term without requiring any bit
* shuffling on our part. Reception works similarly
*
* The feedback terms table consists of 256, 32-bit entries. Notes
*
* The table can be generated at runtime if desired; code to do so
* is shown later. It might not be obvious, but the feedback
* terms simply represent the results of eight shift/xor opera
* tions for all combinations of data and CRC register values
*
* The values must be right-shifted by eight bits by the "updcrc
* logic; the shift must be unsigned (bring in zeroes). On some
* hardware you could probably optimize the shift in assembler by
* using byte-swap instructions
* polynomial $edb88320
*/
namespace LibMSPackSharp
{
public class Checksum
{
public static readonly uint[] CRC32Table = new uint[256]
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
0x2d02ef8d
};
/// <summary>
/// Return a 32-bit CRC of the contents of the buffer.
/// </summary>
public static uint CRC32(byte[] ss, int pointer, int len, uint val)
{
while (--len >= 0)
val = CRC32Table[(val ^ ss[pointer++]) & 0xff] ^ (val >> 8);
return val;
}
}
}

View File

@@ -0,0 +1,460 @@
/* This file is part of libmspack.
* (C) 2003-2013 Stuart Caie.
*
* The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation.
*
* libmspack is free software { get; set; } 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;
namespace LibMSPackSharp.Compression
{
public abstract class CompressionStream
{
private const int CHAR_BIT = 8;
private const int BITBUF_WIDTH = 4 * CHAR_BIT;
/// <summary>
/// I/O routines
/// </summary>
public SystemImpl Sys { get; set; }
/// <summary>
/// Input file handle
/// </summary>
public object Input { get; set; }
/// <summary>
/// Output file handle
/// </summary>
public object Output { get; set; }
public Error Error { get; set; }
#region I/O buffering
public byte[] InputBuffer { get; set; }
public uint InputBufferSize { get; set; }
public int InputPointer { get; set; }
public int InputLength { get; set; }
public int OutputPointer { get; set; }
public int OutputLength { get; set; }
public uint BitBuffer { get; set; }
public uint BitsLeft { get; set; }
/// <summary>
/// Have we reached the end of input?
/// </summary>
public int InputEnd { get; set; }
#endregion
#region ReadBits Methods
/* This header defines macros that read data streams by
* the individual bits
*
* INIT_BITS initialises bitstream state in state structure
* STORE_BITS stores bitstream state in state structure
* RESTORE_BITS restores bitstream state from state structure
* ENSURE_BITS(n) ensure there are at least N bits in the bit buffer
* READ_BITS(var,n) takes N bits from the buffer and puts them in var
* PEEK_BITS(n) extracts without removing N bits from the bit buffer
* REMOVE_BITS(n) removes N bits from the bit buffer
*
* READ_BITS simply calls ENSURE_BITS, PEEK_BITS and REMOVE_BITS,
* which means it's limited to reading the number of bits you can
* ensure at any one time. It also fails if asked to read zero bits.
* If you need to read zero bits, or more bits than can be ensured in
* one go, use READ_MANY_BITS instead.
*
* These macros have variable names baked into them, so to use them
* you have to define some macros:
* - BITS_TYPE: the type name of your state structure
* - BITS_VAR: the variable that points to your state structure
* - define BITS_ORDER_MSB if bits are read from the MSB, or
* define BITS_ORDER_LSB if bits are read from the LSB
* - READ_BYTES: some code that reads more data into the bit buffer,
* it should use READ_IF_NEEDED (calls read_input if the byte buffer
* is empty), then INJECT_BITS(data,n) to put data from the byte
* buffer into the bit buffer.
*
* You also need to define some variables and structure members:
* - byte[] i_ptr; // current position in the byte buffer
* - byte[] i_end; // end of the byte buffer
* - uint bit_buffer; // the bit buffer itself
* - uint bits_left; // number of bits remaining
*
* If you use read_input() and READ_IF_NEEDED, they also expect these
* structure members:
* - struct mspack_system *sys; // to access sys->read()
* - uint error; // to record/return read errors
* - byte input_end; // to mark reaching the EOF
* - byte[] inbuf; // the input byte buffer
* - uint inbuf_size; // the size of the input byte buffer
*
* Your READ_BYTES implementation should read data from *i_ptr and
* put them in the bit buffer. READ_IF_NEEDED will call read_input()
* if i_ptr reaches i_end, and will fill up inbuf and set i_ptr to
* the start of inbuf and i_end to the end of inbuf.
*
* If you're reading in MSB order, the routines work by using the area
* beyond the MSB and the LSB of the bit buffer as a free source of
* zeroes when shifting. This avoids having to mask any bits. So we
* have to know the bit width of the bit buffer variable. We use
* <limits.h> and CHAR_BIT to find the size of the bit buffer in bits.
*
* If you are reading in LSB order, bits need to be masked. Normally
* this is done by computing the mask: N bits are masked by the value
* (1<<N)-1). However, you can define BITS_LSB_TABLE to use a lookup
* table instead of computing this. This adds two new macros,
* PEEK_BITS_T and READ_BITS_T which work the same way as PEEK_BITS
* and READ_BITS, except they use this lookup table. This is useful if
* you need to look up a number of bits that are only known at
* runtime, so the bit mask can't be turned into a constant by the
* compiler.
* The bit buffer datatype should be at least 32 bits wide: it must be
* possible to ENSURE_BITS(17), so it must be possible to add 16 new bits
* to the bit buffer when the bit buffer already has 1 to 15 bits left.
*/
public void INIT_BITS()
{
InputPointer = 0;
InputLength = 0;
BitBuffer = 0;
BitsLeft = 0;
InputEnd = 0;
}
public void STORE_BITS(int inputPointer, int inputLength, uint bitBuffer, uint bitsLeft)
{
InputPointer = inputPointer;
InputLength = inputLength;
BitBuffer = bitBuffer;
BitsLeft = bitsLeft;
}
public void RESTORE_BITS(ref int inputPointer, ref int inputLength, ref uint bitBuffer, ref uint bitsLeft)
{
inputPointer = InputPointer;
inputLength = InputLength;
bitBuffer = BitBuffer;
bitsLeft = BitsLeft;
}
public void ENSURE_BITS(int nbits, ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
while (bitsLeft < nbits)
{
READ_BYTES(ref i_ptr, ref i_end, ref bitsLeft, ref bitBuffer);
}
}
public void READ_BITS(ref int val, int nbits, ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bitsLeft, ref bitBuffer);
val = PEEK_BITS(nbits, bitBuffer);
REMOVE_BITS(nbits, ref bitsLeft, ref bitBuffer);
}
public Error READ_MANY_BITS(ref uint val, byte bits, ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
byte needed = bits, bitrun;
val = 0;
while (needed > 0)
{
if (bitsLeft <= (BITBUF_WIDTH - 16))
{
Error error = READ_BYTES(ref i_ptr, ref i_end, ref bitsLeft, ref bitBuffer);
if (error != Error.MSPACK_ERR_OK)
return error;
}
bitrun = (byte)((bitsLeft < needed) ? bitsLeft : needed);
val = (uint)((val << bitrun) | PEEK_BITS(bitrun, bitBuffer));
REMOVE_BITS(bitrun, ref bitsLeft, ref bitBuffer);
needed -= bitrun;
}
return Error.MSPACK_ERR_OK;
}
public int PEEK_BITS(int nbits, uint bitBuffer)
{
return (int)(bitBuffer & ((1 << (nbits)) - 1));
}
public void REMOVE_BITS(int nbits, ref uint bitsLeft, ref uint bitBuffer)
{
bitBuffer >>= nbits;
bitsLeft -= (uint)nbits;
}
public void INJECT_BITS(uint bitdata, int nbits, ref uint bitsLeft, ref uint bitBuffer)
{
bitBuffer |= bitdata << (int)bitsLeft;
bitsLeft += (uint)nbits;
}
public abstract Error READ_BYTES(ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer);
// lsb_bit_mask[n] = (1 << n) - 1 */
private static readonly ushort[] lsb_bit_mask = new ushort[17]
{
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
public int PEEK_BITS_T(int nbits)
{
return (int)(BitBuffer & lsb_bit_mask[nbits]);
}
public void READ_BITS_T(ref int val, int nbits, ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
ENSURE_BITS(nbits, ref i_ptr, ref i_end, ref bitsLeft, ref bitBuffer);
val = PEEK_BITS_T(nbits);
REMOVE_BITS(nbits, ref bitsLeft, ref bitBuffer);
}
public Error READ_IF_NEEDED(ref int iPtr, ref int iEnd)
{
if (iPtr >= iEnd)
{
if (ReadInput(this) != Error.MSPACK_ERR_OK)
return Error;
iPtr = InputPointer;
iEnd = InputLength;
}
return Error.MSPACK_ERR_OK;
}
private static Error ReadInput(CompressionStream p)
{
int read = p.Sys.Read(p.Input, p.InputBuffer, 0, (int)p.InputBufferSize);
if (read < 0)
return p.Error = Error.MSPACK_ERR_READ;
// We might overrun the input stream by asking for bits we don't use,
// so fake 2 more bytes at the end of input
if (read == 0)
{
if (p.InputEnd != 0)
{
Console.WriteLine("out of input bytes");
return p.Error = Error.MSPACK_ERR_READ;
}
else
{
read = 2;
p.InputBuffer[0] = p.InputBuffer[1] = 0;
p.InputEnd = 1;
}
}
// Update i_ptr and i_end
p.InputPointer = 0;
p.InputLength = read;
return Error.MSPACK_ERR_OK;
}
#endregion
#region ReadHuff Methods
private const int HUFF_MAXBITS = 16;
/// <summary>
/// Decodes the next huffman symbol from the input bitstream into var.
/// Do not use this macro on a table unless build_decode_table() succeeded.
/// </summary>
public int READ_HUFFSYM(ushort[] decodingTable, ref uint var, int tablebits, byte[] lengthTable, int maxsymbols, ref int i, ref ushort sym, ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
ENSURE_BITS(HUFF_MAXBITS, ref i_ptr, ref i_end, ref bitsLeft, ref bitBuffer);
sym = decodingTable[PEEK_BITS(tablebits, bitBuffer)];
if (sym >= maxsymbols)
{
int ret = HUFF_TRAVERSE(decodingTable, tablebits, maxsymbols, ref i, ref sym, bitBuffer);
if (ret != 0)
return ret;
}
var = sym;
i = lengthTable[sym];
REMOVE_BITS(i, ref bitsLeft, ref bitBuffer);
return (int)Error.MSPACK_ERR_OK;
}
public int HUFF_TRAVERSE(ushort[] decodingTable, int tablebits, int maxsymbols, ref int i, ref ushort sym, uint bitBuffer)
{
i = tablebits - 1;
do
{
if (i++ > HUFF_MAXBITS)
return HUFF_ERROR();
sym = decodingTable[(sym << 1) | ((bitBuffer >> i) & 1)];
} while (sym >= maxsymbols);
return (int)Error.MSPACK_ERR_OK;
}
public abstract int HUFF_ERROR();
/// <summary>
/// This function was originally coded by David Tritscher.
///
/// It builds a fast huffman decoding table from
/// a canonical huffman code lengths table.
/// </summary>
/// <param name="nsyms">total number of symbols in this huffman tree.</param>
/// <param name="nbits">any symbols with a code length of nbits or less can be decoded in one lookup of the table.</param>
/// <param name="length">A table to get code lengths from [0 to nsyms-1]</param>
/// <param name="table">
/// The table to fill up with decoded symbols and pointers.
/// Should be ((1<<nbits) + (nsyms*2)) in length.
/// </param>
/// <returns>0 for OK or 1 for error</returns>
public static int MakeDecodeTable(uint nsyms, uint nbits, byte[] length, ushort[] table)
{
ushort sym, next_symbol;
uint leaf, fill;
uint reverse;
byte bit_num;
uint pos = 0; // The current position in the decode table
uint table_mask = (uint)(1 << (int)nbits);
uint bit_mask = table_mask >> 1; // Don't do 0 length codes
// Fill entries for codes short enough for a direct mapping
for (bit_num = 1; bit_num <= nbits; bit_num++)
{
for (sym = 0; sym < nsyms; sym++)
{
if (length[sym] != bit_num)
continue;
// Reverse the significant bits
fill = length[sym];
reverse = pos >> (int)(nbits - fill);
leaf = 0;
do
{
leaf <<= 1;
leaf |= reverse & 1;
reverse >>= 1;
} while (--fill != 0);
if ((pos += bit_mask) > table_mask)
return 1; // Table overrun
// Fill all possible lookups of this symbol with the symbol itself
fill = bit_mask;
next_symbol = (ushort)(1 << bit_num);
do
{
table[leaf] = sym;
leaf += next_symbol;
} while (--fill != 0);
}
bit_mask >>= 1;
}
// Exit with success if table is now complete
if (pos == table_mask)
return 0;
// Mark all remaining table entries as unused
for (sym = (ushort)pos; sym < table_mask; sym++)
{
reverse = sym;
leaf = 0;
fill = nbits;
do
{
leaf <<= 1;
leaf |= reverse & 1;
reverse >>= 1;
} while (--fill != 0);
table[leaf] = 0xFFFF;
}
// next_symbol = base of allocation for long codes
next_symbol = (ushort)(((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1));
// Give ourselves room for codes to grow by up to 16 more bits.
// codes now start at bit nbits+16 and end at (nbits+16-codelength)
pos <<= 16;
table_mask <<= 16;
bit_mask = 1 << 15;
for (bit_num = (byte)(nbits + 1); bit_num <= HUFF_MAXBITS; bit_num++)
{
for (sym = 0; sym < nsyms; sym++)
{
if (length[sym] != bit_num)
continue;
if (pos >= table_mask)
return 1; // Table overflow
// Leaf = the first nbits of the code, reversed
reverse = pos >> 16;
leaf = 0;
fill = nbits;
do
{
leaf <<= 1;
leaf |= reverse & 1;
reverse >>= 1;
} while (--fill != 0);
for (fill = 0; fill < (bit_num - nbits); fill++)
{
// Ff this path hasn't been taken yet, 'allocate' two entries
if (table[leaf] == 0xFFFF)
{
table[(next_symbol << 1)] = 0xFFFF;
table[(next_symbol << 1) + 1] = 0xFFFF;
table[leaf] = next_symbol++;
}
// Follow the path and select either left or right for next bit
leaf = (uint)(table[leaf] << 1);
if (((pos >> (int)(15 - fill)) & 1) != 0)
leaf++;
}
table[leaf] = sym;
pos += bit_mask;
}
bit_mask >>= 1;
}
// Full table?
return (pos == table_mask) ? 0 : 1;
}
#endregion
}
}

View File

@@ -0,0 +1,15 @@
/* 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.Compression
{
public class des
{
}
}

View File

@@ -0,0 +1,114 @@
/* 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.Compression
{
public enum InflateErrorCode
{
INF_ERR_OK = 0,
/// <summary>
/// Unknown block type
/// </summary>
INF_ERR_BLOCKTYPE = -1,
/// <summary>
/// Block size complement mismatch
/// </summary>
INF_ERR_COMPLEMENT = -2,
/// <summary>
/// Error from flush_window callback
/// </summary>
INF_ERR_FLUSH = -3,
/// <summary>
/// Too many bits in bit buffer
/// </summary>
INF_ERR_BITBUF = -4,
/// <summary>
/// Too many symbols in blocktype 2 header
/// </summary>
INF_ERR_SYMLENS = -5,
/// <summary>
/// Failed to build bitlens huffman table
/// </summary>
INF_ERR_BITLENTBL = -6,
/// <summary>
/// Failed to build literals huffman table
/// </summary>
INF_ERR_LITERALTBL = -7,
/// <summary>
/// Failed to build distance huffman table
/// </summary>
INF_ERR_DISTANCETBL = -8,
/// <summary>
/// Bitlen RLE code goes over table size
/// </summary>
INF_ERR_BITOVERRUN = -9,
/// <summary>
/// Invalid bit-length code
/// </summary>
INF_ERR_BADBITLEN = -10,
/// <summary>
/// Out-of-range literal code
/// </summary>
INF_ERR_LITCODE = -11,
/// <summary>
/// Out-of-range distance code
/// </summary>
INF_ERR_DISTCODE = -12,
/// <summary>
/// Somehow, distance is beyond 32k
/// </summary>
INF_ERR_DISTANCE = -13,
/// <summary>
/// Out of bits decoding huffman symbol
/// </summary>
INF_ERR_HUFFSYM = -14,
}
public enum LZSSMode
{
LZSS_MODE_EXPAND = 0,
LZSS_MODE_MSHELP = 1,
LZSS_MODE_QBASIC = 2,
}
public enum LZXBlockType : byte
{
LZX_BLOCKTYPE_INVALID0 = 0,
LZX_BLOCKTYPE_VERBATIM = 1,
LZX_BLOCKTYPE_ALIGNED = 2,
LZX_BLOCKTYPE_UNCOMPRESSED = 3,
LZX_BLOCKTYPE_INVALID4 = 4,
LZX_BLOCKTYPE_INVALID5 = 5,
LZX_BLOCKTYPE_INVALID6 = 6,
LZX_BLOCKTYPE_INVALID7 = 7,
}
}

View File

@@ -0,0 +1,195 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* LZSS is a derivative of LZ77 and was created by James Storer and
* Thomas Szymanski in 1982. Haruhiko Okumura wrote a very popular C
* implementation.
*
* 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.Compression
{
public class LZSS
{
#region LZSS compression / decompression definitions
public const int LZSS_WINDOW_SIZE = 4096;
public const byte LZSS_WINDOW_FILL = 0x20;
#endregion
/// <summary>
/// Decompresses an LZSS stream.
///
/// Input bytes will be read in as necessary using the system.read()
/// function with the input file handle given.This will continue until
/// system.read() returns 0 bytes, or an error.Errors will be passed
/// out of the function as MSPACK_ERR_READ errors. Input streams should
/// convey an "end of input stream" by refusing to supply all the bytes
/// that LZSS asks for when they reach the end of the stream, rather
/// than return an error code.
///
/// Output bytes will be passed to the system.write() function, using
/// the output file handle given.More than one call may be made to
/// system.write().
///
/// As EXPAND.EXE (SZDD/KWAJ), Microsoft Help and QBasic have slightly
/// different encodings for the control byte and matches, a "mode"
/// parameter is allowed, to choose the encoding.
/// </summary>
/// <param name="system">
/// an mspack_system structure used to read from
/// the input stream and write to the output
/// stream, also to allocate and free memory.
/// </param>
/// <param name="input">an input stream with the LZSS data.</param>
/// <param name="output">an output stream to write the decoded data to.</param>
/// <param name="inputBufferSize">
/// the number of bytes to use as an input
/// bitstream buffer.
/// </param>
/// <param name="mode">one of LZSSMode values</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public static Error Decompress(SystemImpl system, object input, object output, int inputBufferSize, LZSSMode mode)
{
uint i, c, mpos, len;
int read;
// Check parameters
if (system == null
|| inputBufferSize < 1
|| (mode != LZSSMode.LZSS_MODE_EXPAND && mode != LZSSMode.LZSS_MODE_MSHELP && mode != LZSSMode.LZSS_MODE_QBASIC))
{
return Error.MSPACK_ERR_ARGS;
}
// Allocate memory
byte[] window = new byte[LZSS_WINDOW_SIZE + inputBufferSize];
// Initialise decompression
int inbuf = LZSS_WINDOW_SIZE;
for (i = 0; i < LZSS_WINDOW_SIZE; i++)
{
window[i] = LZSS_WINDOW_FILL;
}
uint pos = LZSS_WINDOW_SIZE - (uint)((mode == LZSSMode.LZSS_MODE_QBASIC) ? 18 : 16);
uint invert = (uint)((mode == LZSSMode.LZSS_MODE_MSHELP) ? ~0 : 0);
int iPtr = 0, iEnd = 0;
// Loop forever; exit condition is in ENSURE_BYTES macro
for (; ; )
{
//ENSURE_BYTES
if (iPtr >= iEnd)
{
read = system.Read(input, window, inbuf, inputBufferSize);
if (read <= 0)
{
system.Free(window);
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
}
iPtr = 0;
iEnd = read;
}
c = window[iPtr++] ^ invert;
for (i = 0x01; (i & 0xFF) != 0; i <<= 1)
{
if (c != 0 & i != 0)
{
// Literal
//ENSURE_BYTES
if (iPtr >= iEnd)
{
read = system.Read(input, window, inbuf, inputBufferSize);
if (read <= 0)
{
system.Free(window);
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
}
iPtr = 0;
iEnd = read;
}
window[pos] = window[iPtr++];
//ENSURE_BYTES
if (iPtr >= iEnd)
{
read = system.Read(input, window, inbuf, inputBufferSize);
if (read <= 0)
{
system.Free(window);
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
}
iPtr = 0;
iEnd = read;
}
pos++; pos &= LZSS_WINDOW_SIZE - 1;
}
else
{
// Match
//ENSURE_BYTES
if (iPtr >= iEnd)
{
read = system.Read(input, window, inbuf, inputBufferSize);
if (read <= 0)
{
system.Free(window);
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
}
iPtr = 0;
iEnd = read;
}
mpos = window[iPtr++];
//ENSURE_BYTES
if (iPtr >= iEnd)
{
read = system.Read(input, window, inbuf, inputBufferSize);
if (read <= 0)
{
system.Free(window);
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
}
iPtr = 0;
iEnd = read;
}
mpos |= (uint)(window[iPtr] & 0xF0) << 4;
len = (uint)(window[iPtr++] & 0x0F) + 3;
while (len-- != 0)
{
window[pos] = window[mpos];
//WRITE_BYTE;
if (system.Write(output, window, (int)pos, 1) != 1)
{
system.Free(window);
return Error.MSPACK_ERR_WRITE;
}
pos++;
pos &= LZSS_WINDOW_SIZE - 1;
mpos++;
mpos &= LZSS_WINDOW_SIZE - 1;
}
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
/* This file is part of libmspack.
* (C) 2003-2013 Stuart Caie.
*
* The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
* by Microsoft Corporation.
*
* libmspack is free software { get; set; } 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.Compression
{
public class LZXDStream : CompressionStream
{
/// <summary>
/// Number of bytes actually output
/// </summary>
public long Offset { get; set; }
/// <summary>
/// Overall decompressed length of stream
/// </summary>
public long Length { get; set; }
/// <summary>
/// Decoding window
/// </summary>
public byte[] Window { get; set; }
/// <summary>
/// Window size
/// </summary>
public uint WindowSize { get; set; }
/// <summary>
/// LZX DELTA reference data size
/// </summary>
public uint ReferenceDataSize { get; set; }
/// <summary>
/// Number of match_offset entries in table
/// </summary>
public uint NumOffsets { get; set; }
/// <summary>
/// Decompression offset within window
/// </summary>
public uint WindowPosition { get; set; }
/// <summary>
/// Current frame offset within in window
/// </summary>
public uint FramePosition { get; set; }
/// <summary>
/// The number of 32kb frames processed
/// </summary>
public uint Frame { get; set; }
/// <summary>
/// Which frame do we reset the compressor?
/// </summary>
public uint ResetInterval { get; set; }
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R0 { get; set; }
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R1 { get; set; }
/// <summary>
/// For the LRU offset system
/// </summary>
public uint R2 { get; set; }
/// <summary>
/// Uncompressed length of this LZX block
/// </summary>
public uint BlockLength { get; set; }
/// <summary>
/// Uncompressed bytes still left to decode
/// </summary>
public uint BlockRemaining { get; set; }
/// <summary>
/// Magic header value used for transform
/// </summary>
public int IntelFileSize { get; set; }
/// <summary>
/// Has intel E8 decoding started?
/// </summary>
public bool IntelStarted { get; set; }
/// <summary>
/// Type of the current block
/// </summary>
public LZXBlockType BlockType { get; set; }
/// <summary>
/// Have we started decoding at all yet?
/// </summary>
public byte HeaderRead { get; set; }
/// <summary>
/// Does stream follow LZX DELTA spec?
/// </summary>
public bool IsDelta { get; set; }
#region Huffman code lengths
public byte[] PRETREE_len { get; set; } = new byte[LZX.LZX_PRETREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] MAINTREE_len { get; set; } = new byte[LZX.LZX_MAINTREE_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] LENGTH_len { get; set; } = new byte[LZX.LZX_LENGTH_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
public byte[] ALIGNED_len { get; set; } = new byte[LZX.LZX_ALIGNED_MAXSYMBOLS + LZX.LZX_LENTABLE_SAFETY];
#endregion
#region Huffman decoding tables
public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX.LZX_PRETREE_TABLEBITS) + (LZX.LZX_PRETREE_MAXSYMBOLS * 2)];
public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX.LZX_MAINTREE_TABLEBITS) + (LZX.LZX_MAINTREE_MAXSYMBOLS * 2)];
public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX.LZX_LENGTH_TABLEBITS) + (LZX.LZX_LENGTH_MAXSYMBOLS * 2)];
public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX.LZX_ALIGNED_TABLEBITS) + (LZX.LZX_ALIGNED_MAXSYMBOLS * 2)];
#endregion
public byte LENGTH_empty { get; set; }
// This is used purely for doing the intel E8 transform
public byte[] e8_buf { get; set; } = new byte[LZX.LZX_FRAME_SIZE];
public override Error READ_BYTES(ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
Error error = READ_IF_NEEDED(ref i_ptr, ref i_end);
if (error != Error.MSPACK_ERR_OK)
return error;
byte b0 = InputBuffer[i_ptr++];
error = READ_IF_NEEDED(ref i_ptr, ref i_end);
if (error != Error.MSPACK_ERR_OK)
return error;
byte b1 = InputBuffer[i_ptr++];
INJECT_BITS((uint)((b1 << 8) | b0), 16, ref bitsLeft, ref bitBuffer);
return Error.MSPACK_ERR_OK;
}
public override int HUFF_ERROR() => (int)(Error = Error.MSPACK_ERR_DECRUNCH);
}
}

View File

@@ -0,0 +1,727 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The deflate method was created by Phil Katz. MSZIP is equivalent to the
* deflate method.
*
* 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;
namespace LibMSPackSharp.Compression
{
public class MSZIP
{
#region MSZIP (deflate) compression / (inflate) decompression definitions
public const int MSZIP_FRAME_SIZE = 32768; // Size of LZ history window
public const int MSZIP_LITERAL_MAXSYMBOLS = 288; // literal/length huffman tree
public const int MSZIP_LITERAL_TABLEBITS = 9;
public const int MSZIP_DISTANCE_MAXSYMBOLS = 32; // Distance huffman tree
public const int MSZIP_DISTANCE_TABLEBITS = 6;
// If there are less direct lookup entries than symbols, the longer
// code pointers will be <= maxsymbols. This must not happen, or we
// will decode entries badly
public static int MSZIP_LITERAL_TABLESIZE
{
get
{
if ((1 << MSZIP_LITERAL_TABLEBITS) < (MSZIP_LITERAL_MAXSYMBOLS * 2))
return (MSZIP_LITERAL_MAXSYMBOLS * 4);
else
return ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2));
}
}
public static int MSZIP_DISTANCE_TABLESIZE
{
get
{
if ((1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2))
return (MSZIP_DISTANCE_MAXSYMBOLS * 4);
else
return ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2));
}
}
/// <summary>
/// Match lengths for literal codes 257.. 285
/// </summary>
private static readonly ushort[] lit_lengths = new ushort[29]
{
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
};
/// <summary>
/// Match offsets for distance codes 0 .. 29
/// </summary>
private static readonly ushort[] dist_offsets = new ushort[30]
{
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385,
513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
/// <summary>
/// Extra bits required for literal codes 257.. 285
/// </summary>
private static readonly byte[] lit_extrabits = new byte[29]
{
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
/// <summary>
/// Extra bits required for distance codes 0 .. 29
/// </summary>
private static readonly byte[] dist_extrabits = new byte[30]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13
};
/// <summary>
/// The order of the bit length Huffman code lengths
/// </summary>
private static readonly byte[] bitlen_order = new byte[19]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
#endregion
/// <summary>
/// Allocates MS-ZIP decompression stream for decoding the given stream.
///
/// - uses system.alloc() to allocate memory
///
/// - returns null if not enough memory
///
/// - input_buffer_size is how many bytes to use as an input bitstream buffer
///
/// - if RepairMode is non-zero, errors in decompression will be skipped
/// and 'holes' left will be filled with zero bytes. This allows at least
/// a partial recovery of erroneous data.
/// </summary>
public static MSZIPDStream Init(SystemImpl system, object input, object output, int input_buffer_size, bool repair_mode)
{
if (system == null)
return null;
// Round up input buffer size to multiple of two
input_buffer_size = (input_buffer_size + 1) & -2;
if (input_buffer_size < 2)
return null;
// Allocate decompression state
MSZIPDStream zip = new MSZIPDStream();
// Allocate input buffer
zip.InputBuffer = new byte[input_buffer_size];
// Initialise decompression state
zip.Sys = system;
zip.Input = input;
zip.Output = output;
zip.InputBufferSize = (uint)input_buffer_size;
zip.InputEnd = 0;
zip.Error = Error.MSPACK_ERR_OK;
zip.RepairMode = repair_mode;
zip.FlushWindow = FlushWindow;
zip.InputPointer = zip.InputLength = 0;
zip.OutputPointer = zip.OutputLength = 0;
zip.BitBuffer = 0; zip.BitsLeft = 0;
return zip;
}
/// <summary>
/// Decompresses, or decompresses more of, an MS-ZIP stream.
///
/// - out_bytes of data will be decompressed and the function will return
/// with an MSPACK_ERR_OK return code.
///
/// - decompressing will stop as soon as out_bytes is reached. if the true
/// amount of bytes decoded spills over that amount, they will be kept for
/// a later invocation of mszipd_decompress().
///
/// - the output bytes will be passed to the system.write() function given in
/// mszipd_init(), using the output file handle given in mszipd_init(). More
/// than one call may be made to system.write()
///
/// - MS-ZIP will read input bytes as necessary using the system.read()
/// function given in mszipd_init(), using the input file handle given in
/// mszipd_init(). This will continue until system.read() returns 0 bytes,
/// or an error.
/// </summary>
public static Error Decompress(object o, long out_bytes)
{
MSZIPDStream zip = (MSZIPDStream)o;
if (zip == null)
return Error.MSPACK_ERR_ARGS;
// For the bit buffer
uint bit_buffer = 0, bits_left = 0;
int i_ptr = 0, i_end = 0;
int i, state, error;
// Easy answers
if (zip == null || (out_bytes < 0))
return Error.MSPACK_ERR_ARGS;
if (zip.Error != Error.MSPACK_ERR_OK)
return zip.Error;
// Flush out any stored-up bytes before we begin
i = zip.OutputLength - zip.OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i != 0)
{
if (zip.Sys.Write(zip.Output, zip.Window, zip.OutputPointer, i) != i)
return zip.Error = Error.MSPACK_ERR_WRITE;
zip.OutputPointer += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
while (out_bytes > 0)
{
// Unpack another block
zip.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Skip to next read 'CK' header
i = (int)(bits_left & 7);
zip.REMOVE_BITS(i, ref bits_left, ref bit_buffer); // Align to bytestream
state = 0;
do
{
zip.READ_BITS(ref i, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (i == 'C')
state = 1;
else if ((state == 1) && (i == 'K'))
state = 2;
else
state = 0;
} while (state != 2);
// Inflate a block, repair and realign if necessary
zip.WindowPosition = 0;
zip.BytesOutput = 0;
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
if ((error = (int)Inflate(zip)) != 0)
{
Console.WriteLine($"inflate error {error}");
if (zip.RepairMode)
{
// Recover partially-inflated buffers
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.");
for (i = zip.BytesOutput; i < MSZIP_FRAME_SIZE; i++)
{
zip.Window[i] = 0x00;
}
zip.BytesOutput = MSZIP_FRAME_SIZE;
}
else
{
return zip.Error = (error > 0) ? (Error)error : Error.MSPACK_ERR_DECRUNCH;
}
}
zip.OutputPointer = 0;
zip.OutputLength = zip.BytesOutput;
// 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)
return zip.Error = Error.MSPACK_ERR_WRITE;
// mspack errors (i.e. read errors) are fatal and can't be recovered
if (error > 0 && zip.RepairMode)
return (Error)error;
zip.OutputPointer += i;
out_bytes -= i;
}
if (out_bytes != 0)
{
Console.WriteLine("bytes left to output");
return zip.Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Decompresses an entire MS-ZIP stream in a KWAJ file. Acts very much
/// like mszipd_decompress(), but doesn't take an out_bytes parameter
/// </summary>
public static Error DecompressKWAJ(MSZIPDStream zip)
{
// For the bit buffer
uint bit_buffer = 0, bits_left = 0;
int i_ptr = 0, i_end = 0;
int i = 0, error, block_len = 0;
// Unpack blocks until block_len == 0
for (; ; )
{
zip.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Align to bytestream, read block_len
i = (int)(bits_left & 7);
zip.REMOVE_BITS(i, ref bits_left, ref bit_buffer);
zip.READ_BITS(ref block_len, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
zip.READ_BITS(ref i, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
block_len |= i << 8;
if (block_len == 0)
break;
// Read "CK" header
zip.READ_BITS(ref i, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (i != 'C')
return Error.MSPACK_ERR_DATAFORMAT;
zip.READ_BITS(ref i, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (i != 'K')
return Error.MSPACK_ERR_DATAFORMAT;
// Inflate block
zip.WindowPosition = 0;
zip.BytesOutput = 0;
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
if ((error = (int)Inflate(zip)) != 0)
{
Console.WriteLine($"inflate error {error}");
return zip.Error = (error > 0) ? (Error)error : Error.MSPACK_ERR_DECRUNCH;
}
// Write inflated block
if (zip.Sys.Write(zip.Output, zip.Window, 0, zip.BytesOutput) != zip.BytesOutput)
return zip.Error = Error.MSPACK_ERR_WRITE;
}
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Frees all stream associated with an MS-ZIP data stream
///
/// - calls system.free() using the system pointer given in mszipd_init()
/// </summary>
public static void Free(MSZIPDStream zip)
{
if (zip != null)
{
SystemImpl sys = zip.Sys;
sys.Free(zip.InputBuffer);
sys.Free(zip);
}
}
private static InflateErrorCode ZipReadLens(MSZIPDStream zip)
{
// For the bit buffer and huffman decoding
uint bit_buffer = 0, bits_left = 0;
int i_ptr = 0, i_end = 0;
// Bitlen Huffman codes -- immediate lookup, 7 bit max code length
ushort[] bl_table = new ushort[1 << 7];
byte[] bl_len = new byte[19];
byte[] lens = new byte[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS];
int lit_codes = 0, dist_codes = 0, code, last_code = 0, bitlen_codes = 0, i, run = 0;
zip.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Read the number of codes
zip.READ_BITS(ref lit_codes, 5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
lit_codes += 257;
zip.READ_BITS(ref dist_codes, 5, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
dist_codes += 1;
zip.READ_BITS(ref bitlen_codes, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
bitlen_codes += 4;
if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS)
return InflateErrorCode.INF_ERR_SYMLENS;
if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS)
return InflateErrorCode.INF_ERR_SYMLENS;
// Read in the bit lengths in their unusual order
for (i = 0; i < bitlen_codes; i++)
{
int blLenTemp = bl_len[bitlen_order[i]];
zip.READ_BITS(ref blLenTemp, 3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
bl_len[bitlen_order[i]] = (byte)blLenTemp;
}
while (i < 19)
{
bl_len[bitlen_order[i++]] = 0;
}
// Create decoding table with an immediate lookup */
if (MSZIPDStream.MakeDecodeTable(19, 7, bl_len, bl_table) != 0)
return InflateErrorCode.INF_ERR_BITLENTBL;
// Read literal / distance code lengths
for (i = 0; i < (lit_codes + dist_codes); i++)
{
// Single-level huffman lookup
zip.ENSURE_BITS(7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
code = bl_table[zip.PEEK_BITS(7, bit_buffer)];
zip.REMOVE_BITS(bl_len[code], ref bits_left, ref bit_buffer);
if (code < 16)
{
lens[i] = (byte)(last_code = code);
}
else
{
switch (code)
{
case 16:
zip.READ_BITS(ref run, 2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run += 3;
code = last_code;
break;
case 17:
zip.READ_BITS(ref run, 3, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run += 3;
code = 0;
break;
case 18:
zip.READ_BITS(ref run, 7, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
run += 11;
code = 0;
break;
default:
Console.WriteLine($"bad code!: {code}");
return InflateErrorCode.INF_ERR_BADBITLEN;
}
if ((i + run) > (lit_codes + dist_codes))
return InflateErrorCode.INF_ERR_BITOVERRUN;
while (run-- != 0)
{
lens[i++] = (byte)code;
}
i--;
}
}
// Copy LITERAL code lengths and clear any remaining
i = lit_codes;
zip.Sys.Copy(lens, 0, zip.LITERAL_len, 0, i);
while (i < MSZIP_LITERAL_MAXSYMBOLS)
{
zip.LITERAL_len[i++] = 0;
}
i = dist_codes;
zip.Sys.Copy(lens, lit_codes, zip.DISTANCE_len, 0, i);
while (i < MSZIP_DISTANCE_MAXSYMBOLS)
{
zip.DISTANCE_len[i++] = 0;
}
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
return 0;
}
/// <summary>
/// A clean implementation of RFC 1951 / inflate
/// </summary>
private static InflateErrorCode Inflate(MSZIPDStream zip)
{
int last_block = 0, block_type = 0, distance = 0, length = 0, this_run, i;
// For the bit buffer and huffman decoding
uint bit_buffer = 0, bits_left = 0;
ushort sym = 0;
int i_ptr = 0, i_end = 0;
zip.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
do
{
// Read in last block bit
zip.READ_BITS(ref last_block, 1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
// Read in block type
zip.READ_BITS(ref block_type, 2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (block_type == 0)
{
// Uncompressed block
byte[] lens_buf = new byte[4];
// Go to byte boundary
i = (int)(bits_left & 7);
zip.REMOVE_BITS(i, ref bits_left, ref bit_buffer);
// Read 4 bytes of data, emptying the bit-buffer if necessary
for (i = 0; (bits_left >= 8); i++)
{
if (i == 4)
return InflateErrorCode.INF_ERR_BITBUF;
lens_buf[i] = (byte)zip.PEEK_BITS(8, bit_buffer);
zip.REMOVE_BITS(8, ref bits_left, ref bit_buffer);
}
if (bits_left != 0) return InflateErrorCode.INF_ERR_BITBUF;
while (i < 4)
{
zip.READ_IF_NEEDED(ref i_ptr, ref i_end);
lens_buf[i++] = zip.InputBuffer[i_ptr++];
}
// Get the length and its complement
length = lens_buf[0] | (lens_buf[1] << 8);
i = lens_buf[2] | (lens_buf[3] << 8);
if (length != (~i & 0xFFFF))
return InflateErrorCode.INF_ERR_COMPLEMENT;
// Read and copy the uncompressed data into the window
while (length > 0)
{
zip.READ_IF_NEEDED(ref i_ptr, ref i_end);
this_run = length;
if (this_run > (uint)(i_end - i_ptr)) this_run = i_end - i_ptr;
if (this_run > (MSZIP_FRAME_SIZE - zip.WindowPosition))
this_run = (int)(MSZIP_FRAME_SIZE - zip.WindowPosition);
zip.Sys.Copy(zip.InputBuffer, i_ptr, zip.Window, (int)zip.WindowPosition, this_run);
zip.WindowPosition += (uint)this_run;
i_ptr += this_run;
length -= this_run;
// FLUSH_IF_NEEDED
if (zip.WindowPosition == MSZIP_FRAME_SIZE)
{
if (zip.FlushWindow(zip, MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
return InflateErrorCode.INF_ERR_FLUSH;
zip.WindowPosition = 0;
}
}
}
else if ((block_type == 1) || (block_type == 2))
{
// Huffman-compressed LZ77 block
uint match_posn, code = 0;
if (block_type == 1)
{
// Block with fixed Huffman codes
i = 0;
while (i < 144)
{
zip.LITERAL_len[i++] = 8;
}
while (i < 256)
{
zip.LITERAL_len[i++] = 9;
}
while (i < 280)
{
zip.LITERAL_len[i++] = 7;
}
while (i < 288)
{
zip.LITERAL_len[i++] = 8;
}
for (i = 0; i < 32; i++)
{
zip.DISTANCE_len[i] = 5;
}
}
else
{
// Block with dynamic Huffman codes
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
if ((i = (int)ZipReadLens(zip)) != 0)
return (InflateErrorCode)i;
zip.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
}
// Now huffman lengths are read for either kind of block,
// create huffman decoding tables
if (MSZIPDStream.MakeDecodeTable(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, zip.LITERAL_len, zip.LITERAL_table) != 0)
return InflateErrorCode.INF_ERR_LITERALTBL;
if (MSZIPDStream.MakeDecodeTable(MSZIP_DISTANCE_MAXSYMBOLS, MSZIP_DISTANCE_TABLEBITS, zip.DISTANCE_len, zip.DISTANCE_table) != 0)
return InflateErrorCode.INF_ERR_DISTANCETBL;
// Decode forever until end of block code
for (; ; )
{
zip.READ_HUFFSYM(zip.LITERAL_table, ref code, MSZIP_LITERAL_TABLEBITS, zip.LITERAL_len, MSZIP_LITERAL_MAXSYMBOLS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (code < 256)
{
zip.Window[zip.WindowPosition++] = (byte)code;
// FLUSH_IF_NEEDED
if (zip.WindowPosition == MSZIP_FRAME_SIZE)
{
if (zip.FlushWindow(zip, MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
return InflateErrorCode.INF_ERR_FLUSH;
zip.WindowPosition = 0;
}
}
else if (code == 256)
{
// END OF BLOCK CODE: loop break point
break;
}
else
{
code -= 257; // Codes 257-285 are matches
if (code >= 29)
return InflateErrorCode.INF_ERR_LITCODE; // Codes 286-287 are illegal
zip.READ_BITS_T(ref length, lit_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
length += lit_lengths[code];
zip.READ_HUFFSYM(zip.DISTANCE_table, ref code, MSZIP_DISTANCE_TABLEBITS, zip.DISTANCE_len, MSZIP_DISTANCE_MAXSYMBOLS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (code >= 30)
return InflateErrorCode.INF_ERR_DISTCODE;
zip.READ_BITS_T(ref distance, dist_extrabits[code], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
distance += dist_offsets[code];
// Match position is window position minus distance. If distance
// is more than window position numerically, it must 'wrap
// around' the frame size.
match_posn = (uint)(((distance > zip.WindowPosition) ? MSZIP_FRAME_SIZE : 0) + zip.WindowPosition - distance);
// Copy match
if (length < 12)
{
// Short match, use slower loop but no loop setup code
while (length-- != 0)
{
zip.Window[zip.WindowPosition++] = zip.Window[match_posn++];
match_posn &= MSZIP_FRAME_SIZE - 1;
// FLUSH_IF_NEEDED
if (zip.WindowPosition == MSZIP_FRAME_SIZE)
{
if (zip.FlushWindow(zip, MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
return InflateErrorCode.INF_ERR_FLUSH;
zip.WindowPosition = 0;
}
}
}
else
{
// Longer match, use faster loop but with setup expense
int runsrc, rundest;
do
{
this_run = length;
if ((match_posn + this_run) > MSZIP_FRAME_SIZE)
this_run = MSZIP_FRAME_SIZE - (int)match_posn;
if ((zip.WindowPosition + this_run) > MSZIP_FRAME_SIZE)
this_run = MSZIP_FRAME_SIZE - (int)zip.WindowPosition;
rundest = (int)zip.WindowPosition;
zip.WindowPosition += (uint)this_run;
runsrc = (int)match_posn;
match_posn += (uint)this_run;
length -= this_run;
while (this_run-- != 0)
{
zip.Window[rundest++] = zip.Window[runsrc++];
}
if (match_posn == MSZIP_FRAME_SIZE)
match_posn = 0;
// FLUSH_IF_NEEDED
if (zip.WindowPosition == MSZIP_FRAME_SIZE)
{
if (zip.FlushWindow(zip, MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
return InflateErrorCode.INF_ERR_FLUSH;
zip.WindowPosition = 0;
}
} while (length > 0);
}
}
}
}
else
{
// block_type == 3 -- bad block type
return InflateErrorCode.INF_ERR_BLOCKTYPE;
}
} while (last_block == 0);
// Flush the remaining data
if (zip.WindowPosition != 0)
{
if (zip.FlushWindow(zip, zip.WindowPosition) != Error.MSPACK_ERR_OK)
return InflateErrorCode.INF_ERR_FLUSH;
}
zip.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
// Return success
return InflateErrorCode.INF_ERR_OK;
}
/// <summary>
/// inflate() calls this whenever the window should be flushed. As
/// MSZIP only expands to the size of the window, the implementation used
/// simply keeps track of the amount of data flushed, and if more than 32k
/// is flushed, an error is raised.
/// </summary>
private static Error FlushWindow(MSZIPDStream zip, uint data_flushed)
{
zip.BytesOutput += (int)data_flushed;
if (zip.BytesOutput > MSZIP_FRAME_SIZE)
{
Console.WriteLine($"overflow: {data_flushed} bytes flushed, total is now {zip.BytesOutput}");
return Error.MSPACK_ERR_ARGS;
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -0,0 +1,64 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The deflate method was created by Phil Katz. MSZIP is equivalent to the
* deflate method.
*
* 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;
namespace LibMSPackSharp.Compression
{
public class MSZIPDStream : CompressionStream
{
/// <summary>
/// 32kb history window
/// </summary>
public byte[] Window { get; set; } = new byte[MSZIP.MSZIP_FRAME_SIZE];
/// <summary>
/// Offset within window
/// </summary>
public uint WindowPosition { get; set; }
/// <summary>
/// inflate() will call this whenever the window should be emptied.
/// </summary>
public Func<MSZIPDStream, uint, Error> FlushWindow;
public bool RepairMode { get; set; }
public int BytesOutput { get; set; }
#region Huffman code lengths
public byte[] LITERAL_len { get; set; } = new byte[MSZIP.MSZIP_LITERAL_MAXSYMBOLS];
public byte[] DISTANCE_len { get; set; } = new byte[MSZIP.MSZIP_DISTANCE_MAXSYMBOLS];
#endregion
#region Huffman decoding tables
public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP.MSZIP_LITERAL_TABLESIZE];
public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP.MSZIP_DISTANCE_TABLESIZE];
#endregion
public override Error READ_BYTES(ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
Error error = READ_IF_NEEDED(ref i_ptr, ref i_end);
if (error != Error.MSPACK_ERR_OK)
return error;
INJECT_BITS(InputBuffer[i_ptr++], 8, ref bitsLeft, ref bitBuffer);
return Error.MSPACK_ERR_OK;
}
public override int HUFF_ERROR() => (int)InflateErrorCode.INF_ERR_HUFFSYM;
}
}

View File

@@ -0,0 +1,611 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The Quantum method was created by David Stafford, adapted by Microsoft
* Corporation.
*
* This decompressor is based on an implementation by Matthew Russotto, used
* with permission.
*
* 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
*/
/* Quantum decompression implementation */
/* This decompressor was researched and implemented by Matthew Russotto. It
* has since been tidied up by Stuart Caie. More information can be found at
* http://www.speakeasy.org/~russotto/quantumcomp.html
*/
using System;
namespace LibMSPackSharp.Compression
{
public class QTM
{
public const int QTM_FRAME_SIZE = 32768;
/* Quantum static data tables:
*
* Quantum uses 'position slots' to represent match offsets. For every
* match, a small 'position slot' number and a small offset from that slot
* are encoded instead of one large offset.
*
* position_base[] is an index to the position slot bases
*
* extra_bits[] states how many bits of offset-from-base data is needed.
*
* length_base[] and length_extra[] are equivalent in function, but are
* used for encoding selector 6 (variable length match) match lengths,
* instead of match offsets.
*
* They are generated with the following code:
* uint i, offset;
* for (i = 0, offset = 0; i < 42; i++) {
* position_base[i] = offset;
* extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1;
* offset += 1 << extra_bits[i];
* }
* for (i = 0, offset = 0; i < 26; i++) {
* length_base[i] = offset;
* length_extra[i] = (i < 2 ? 0 : i - 2) >> 2;
* offset += 1 << length_extra[i];
* }
* length_base[26] = 254; length_extra[26] = 0;
*/
private static readonly uint[] position_base = new uint[42]
{
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152,
65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864
};
private static readonly byte[] extra_bits = new byte[42]
{
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19
};
private static readonly byte[] length_base = new byte[27]
{
0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26,
30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254
};
private static readonly byte[] length_extra = new byte[27]
{
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
};
/// <summary>
/// allocates Quantum decompression state for decoding the given stream.
///
/// - returns null if window_bits is outwith the range 10 to 21 (inclusive).
/// - uses system.alloc() to allocate memory
/// - returns null if not enough memory
/// - window_bits is the size of the Quantum window, from 1Kb(10) to 2Mb(21).
/// - input_buffer_size is the number of bytes to use to store bitstream data.
/// </summary>
public static QTMDStream Init(SystemImpl system, object input, object output, int window_bits, int input_buffer_size)
{
uint window_size = (uint)(1 << window_bits);
if (system == null)
return null;
// Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb)
if (window_bits < 10 || window_bits > 21)
return null;
// Round up input buffer size to multiple of two
input_buffer_size = (input_buffer_size + 1) & -2;
if (input_buffer_size < 2) return null;
// Allocate decompression state
QTMDStream qtm = new QTMDStream();
// Allocate decompression window and input buffer
qtm.Window = new byte[window_size];
qtm.InputBuffer = new byte[input_buffer_size];
// Initialise decompression state
qtm.Sys = system;
qtm.Input = input;
qtm.Output = output;
qtm.InputBufferSize = (uint)input_buffer_size;
qtm.WindowSize = window_size;
qtm.WindowPosition = 0;
qtm.FrameTODO = QTM_FRAME_SIZE;
qtm.HeaderRead = 0;
qtm.Error = Error.MSPACK_ERR_OK;
qtm.InputPointer = qtm.InputLength = 0;
qtm.OutputPointer = qtm.OutputLength = 0;
qtm.InputEnd = 0;
qtm.BitsLeft = 0;
qtm.BitBuffer = 0;
// Initialise arithmetic coding models
// - model 4 depends on window size, ranges from 20 to 24
// - model 5 depends on window size, ranges from 20 to 36
// - model 6pos depends on window size, ranges from 20 to 42
int i = window_bits * 2;
InitModel(qtm.Model0, qtm.Model0Symbols, 0, 64);
InitModel(qtm.Model1, qtm.Model1Symbols, 64, 64);
InitModel(qtm.Model2, qtm.Model2Symbols, 128, 64);
InitModel(qtm.Model3, qtm.Model3Symbols, 192, 64);
InitModel(qtm.Model4, qtm.Model4Symbols, 0, (i > 24) ? 24 : i);
InitModel(qtm.Model5, qtm.Model5Symbols, 0, (i > 36) ? 36 : i);
InitModel(qtm.Model6, qtm.Model6Symbols, 0, i);
InitModel(qtm.Model6Len, qtm.Model6LenSymbols, 0, 27);
InitModel(qtm.Model7, qtm.Model7Symbols, 0, 7);
// All ok
return qtm;
}
/// <summary>
/// Decompresses, or decompresses more of, a Quantum stream.
///
/// - out_bytes of data will be decompressed and the function will return
/// with an MSPACK_ERR_OK return code.
///
/// - decompressing will stop as soon as out_bytes is reached. if the true
/// amount of bytes decoded spills over that amount, they will be kept for
/// a later invocation of qtmd_decompress().
///
/// - the output bytes will be passed to the system.write() function given in
/// qtmd_init(), using the output file handle given in qtmd_init(). More
/// than one call may be made to system.write()
///
/// - Quantum will read input bytes as necessary using the system.read()
/// function given in qtmd_init(), using the input file handle given in
/// qtmd_init(). This will continue until system.read() returns 0 bytes,
/// or an error.
/// </summary>
public static Error Decompress(object o, long out_bytes)
{
QTMDStream qtm = (QTMDStream)o;
if (qtm == null)
return Error.MSPACK_ERR_ARGS;
uint frame_end, match_offset, range = 0, extra = 0;
int i_ptr = 0, i_end = 0, runsrc, rundest;
int i, j, selector = 0, sym = 0, match_length;
ushort symf = 0;
uint bit_buffer = 0, bits_left = 0;
// Easy answers
if (qtm == null || (out_bytes < 0))
return Error.MSPACK_ERR_ARGS;
if (qtm.Error != Error.MSPACK_ERR_OK)
return qtm.Error;
// Flush out any stored-up bytes before we begin
i = qtm.OutputLength - qtm.OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i != 0)
{
if (qtm.Sys.Write(qtm.Output, qtm.Window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
qtm.OutputPointer += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
// Restore local state
qtm.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
byte[] window = qtm.Window;
uint window_posn = qtm.WindowPosition;
uint frame_todo = qtm.FrameTODO;
ushort high = qtm.High;
ushort low = qtm.Low;
ushort current = qtm.Current;
// While we do not have enough decoded bytes in reserve:
while ((qtm.OutputLength - qtm.OutputPointer) < out_bytes)
{
// Read header if necessary. Initialises H, L and C
if (qtm.HeaderRead == 0)
{
high = 0xFFFF;
low = 0;
int tempCurrent = current;
qtm.READ_BITS(ref tempCurrent, 16, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
current = (ushort)tempCurrent;
qtm.HeaderRead = 1;
}
// Decode more, up to the number of bytes needed, the frame boundary,
// or the window boundary, whichever comes first
frame_end = (uint)(window_posn + (out_bytes - (qtm.OutputLength - qtm.OutputPointer)));
if ((window_posn + frame_todo) < frame_end)
frame_end = window_posn + frame_todo;
if (frame_end > qtm.WindowSize)
frame_end = qtm.WindowSize;
while (window_posn < frame_end)
{
GET_SYMBOL(qtm, qtm.Model7, ref selector, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (selector < 4)
{
// Literal byte
QTMDModel mdl;
switch (selector)
{
case 0: mdl = qtm.Model0; break;
case 1: mdl = qtm.Model1; break;
case 2: mdl = qtm.Model2; break;
default: mdl = qtm.Model3; break;
}
GET_SYMBOL(qtm, mdl, ref sym, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
window[window_posn++] = (byte)sym;
frame_todo--;
}
else
{
// Match repeated string
switch (selector)
{
// Selector 4 = fixed length match (3 bytes)
case 4:
GET_SYMBOL(qtm, qtm.Model4, ref sym, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
qtm.READ_MANY_BITS(ref extra, extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = position_base[sym] + extra + 1;
match_length = 3;
break;
// Selector 5 = fixed length match (4 bytes)
case 5:
GET_SYMBOL(qtm, qtm.Model5, ref sym, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
qtm.READ_MANY_BITS(ref extra, extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = position_base[sym] + extra + 1;
match_length = 4;
break;
// Selector 6 = variable length match
case 6:
GET_SYMBOL(qtm, qtm.Model6Len, ref sym, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
qtm.READ_MANY_BITS(ref extra, length_extra[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_length = (int)(length_base[sym] + extra + 5);
GET_SYMBOL(qtm, qtm.Model6, ref sym, ref range, ref symf, ref high, ref low, ref current, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
qtm.READ_MANY_BITS(ref extra, extra_bits[sym], ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
match_offset = position_base[sym] + extra + 1;
break;
default:
// Should be impossible, model7 can only return 0-6
Console.WriteLine("got %d from selector", selector);
return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
}
rundest = (int)window_posn;
frame_todo -= (uint)match_length;
// Does match destination wrap the window? This situation is possible
// where the window size is less than the 32k frame size, but matches
// must not go beyond a frame boundary */
if ((window_posn + match_length) > qtm.WindowSize)
{
/* copy first part of match, before window end */
i = (int)(qtm.WindowSize - window_posn);
j = (int)(window_posn - match_offset);
while (i-- != 0)
{
window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
}
// Flush currently stored data
i = (int)(qtm.WindowSize - qtm.OutputPointer);
// This should not happen, but if it does then this code
// can't handle the situation (can't flush up to the end of
// the window, but can't break out either because we haven't
// finished writing the match). bail out in this case */
if (i > out_bytes)
{
Console.WriteLine($"during window-wrap match; {i} bytes to flush but only need {out_bytes}");
return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
}
if (qtm.Sys.Write(qtm.Output, qtm.Window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
qtm.OutputPointer = 0;
qtm.OutputLength = 0;
// Copy second part of match, after window wrap
rundest = 0;
i = (int)(match_length - (qtm.WindowSize - window_posn));
while (i-- != 0)
{
window[rundest++] = window[j++ & (qtm.WindowSize - 1)];
}
window_posn = (uint)(window_posn + match_length - qtm.WindowSize);
break; // Because "window_posn < frame_end" has now failed
}
else
{
// Normal match - output won't wrap window or frame end
i = match_length;
// Does match _offset_ wrap the window?
if (match_offset > window_posn)
{
// j = length from match offset to end of window
j = (int)(match_offset - window_posn);
if (j > (int)qtm.WindowSize)
{
Console.WriteLine("match offset beyond window boundaries");
return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
}
runsrc = (int)(qtm.WindowSize - j);
if (j < i)
{
// If match goes over the window edge, do two copy runs
i -= j;
while (j-- > 0)
{
window[rundest++] = window[runsrc++];
}
runsrc = 0;
}
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
else
{
runsrc = (int)(rundest - match_offset);
while (i-- > 0)
{
window[rundest++] = window[runsrc++];
}
}
window_posn += (uint)match_length;
}
}
}
qtm.OutputLength = (int)window_posn;
// If we subtracted too much from frame_todo, it will
// wrap around past zero and go above its max value */
if (frame_todo > QTM_FRAME_SIZE)
{
Console.WriteLine("overshot frame alignment");
return qtm.Error = Error.MSPACK_ERR_DECRUNCH;
}
// Another frame completed?
if (frame_todo == 0)
{
// Re-align input
if ((bits_left & 7) != 0)
qtm.REMOVE_BITS((int)bits_left & 7, ref bits_left, ref bit_buffer);
// Special Quantum hack -- cabd.c injects a trailer byte to allow the
// decompressor to realign itself. CAB Quantum blocks, unlike LZX
// blocks, can have anything from 0 to 4 trailing null bytes. */
do
{
qtm.READ_BITS(ref i, 8, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
} while (i != 0xFF);
qtm.HeaderRead = 0;
frame_todo = QTM_FRAME_SIZE;
}
// Window wrap?
if (window_posn == qtm.WindowSize)
{
// Flush all currently stored data
i = (qtm.OutputLength - qtm.OutputPointer);
// Break out if we have more than enough to finish this request
if (i >= out_bytes)
break;
if (qtm.Sys.Write(qtm.Output, qtm.Window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
qtm.OutputPointer = 0;
qtm.OutputLength = 0;
window_posn = 0;
}
}
if (out_bytes != 0)
{
i = (int)out_bytes;
if (qtm.Sys.Write(qtm.Output, qtm.Window, qtm.OutputPointer, i) != i)
return qtm.Error = Error.MSPACK_ERR_WRITE;
qtm.OutputPointer += i;
}
// Store local state
qtm.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
qtm.WindowPosition = window_posn;
qtm.FrameTODO = frame_todo;
qtm.High = high;
qtm.Low = low;
qtm.Current = (ushort)current;
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Frees all state associated with a Quantum data stream
/// - calls system.free() using the system pointer given in qtmd_init()
/// </summary>
public static void Free(QTMDStream qtm)
{
if (qtm != null)
{
SystemImpl sys = qtm.Sys;
sys.Free(qtm.Window);
sys.Free(qtm.InputBuffer);
sys.Free(qtm);
}
}
/// <summary>
/// Arithmetic decoder:
///
/// GET_SYMBOL(model, var) fetches the next symbol from the stated model
/// and puts it in var.
///
/// If necessary, qtmd_update_model() is called.
/// </summary>
private static void GET_SYMBOL(QTMDStream qtm, QTMDModel model, ref int var, ref uint range, ref ushort symf, ref ushort high, ref ushort low, ref ushort current,
ref int i_ptr, ref int i_end, ref uint bit_buffer, ref uint bits_left)
{
range = (uint)(((high - low) & 0xFFFF) + 1);
symf = (ushort)(((((current - low + 1) * model.Syms[0].CumFreq) - 1) / range) & 0xFFFF);
int i;
for (i = 1; i < model.Entries; i++)
{
if (model.Syms[i].CumFreq <= symf)
break;
}
var = model.Syms[i - 1].Sym;
range = (ushort)((high - low) + 1);
symf = model.Syms[0].CumFreq;
high = (ushort)(low + ((model.Syms[i - 1].CumFreq * range) / symf) - 1);
low = (ushort)(low + ((model.Syms[i].CumFreq * range) / symf));
do
{
model.Syms[--i].CumFreq += 8;
} while (i > 0);
if (model.Syms[0].CumFreq > 3800)
UpdateModel(model);
while (true)
{
if ((low & 0x8000) != (high & 0x8000))
{
// Underflow case
if ((low & 0x4000) != 0 && (high & 0x4000) == 0)
{
current ^= 0x4000;
low &= 0x3FFF;
high |= 0x4000;
}
else
{
break;
}
}
low <<= 1;
high = (ushort)((high << 1) | 1);
qtm.ENSURE_BITS(1, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
current = (ushort)((current << 1) | qtm.PEEK_BITS(1, bit_buffer));
qtm.REMOVE_BITS(1, ref bits_left, ref bit_buffer);
}
}
private static void UpdateModel(QTMDModel model)
{
QTMDModelSym tmp;
int i, j;
if (--model.ShiftsLeft != 0)
{
for (i = model.Entries - 1; i >= 0; i--)
{
/* -1, not -2; the 0 entry saves this */
model.Syms[i].CumFreq >>= 1;
if (model.Syms[i].CumFreq <= model.Syms[i + 1].CumFreq)
{
model.Syms[i].CumFreq = (ushort)(model.Syms[i + 1].CumFreq + 1);
}
}
}
else
{
model.ShiftsLeft = 50;
for (i = 0; i < model.Entries; i++)
{
// No -1, want to include the 0 entry
// This converts CumFreqs into frequencies, then shifts right
model.Syms[i].CumFreq -= model.Syms[i + 1].CumFreq;
model.Syms[i].CumFreq++; // Avoid losing things entirely
model.Syms[i].CumFreq >>= 1;
}
// Now sort by frequencies, decreasing order -- this must be an
// inplace selection sort, or a sort with the same (in)stability
// characteristics
for (i = 0; i < model.Entries - 1; i++)
{
for (j = i + 1; j < model.Entries; j++)
{
if (model.Syms[i].CumFreq < model.Syms[j].CumFreq)
{
tmp = model.Syms[i];
model.Syms[i] = model.Syms[j];
model.Syms[j] = tmp;
}
}
}
// Then convert frequencies back to CumFreq
for (i = model.Entries - 1; i >= 0; i--)
{
model.Syms[i].CumFreq += model.Syms[i + 1].CumFreq;
}
}
}
private static void InitModel(QTMDModel model, QTMDModelSym[] syms, int start, int len)
{
model.ShiftsLeft = 4;
model.Entries = len;
model.Syms = syms;
for (int i = 0; i <= len; i++)
{
// Actual symbol
syms[i].Sym = (ushort)(start + i);
// Current frequency of that symbol
syms[i].CumFreq = (ushort)(len - i);
}
}
}
}

View File

@@ -0,0 +1,23 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The Quantum method was created by David Stafford, adapted by Microsoft
* 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.Compression
{
public class QTMDModel
{
public int ShiftsLeft { get; set; }
public int Entries { get; set; }
public QTMDModelSym[] Syms { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The Quantum method was created by David Stafford, adapted by Microsoft
* 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.Compression
{
public class QTMDModelSym
{
public ushort Sym { get; set; }
public ushort CumFreq { get; set; }
}
}

View File

@@ -0,0 +1,142 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* The Quantum method was created by David Stafford, adapted by Microsoft
* 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.Compression
{
public class QTMDStream : CompressionStream
{
/// <summary>
/// Decoding window
/// </summary>
public byte[] Window { get; set; }
/// <summary>
/// Window size
/// </summary>
public uint WindowSize { get; set; }
/// <summary>
/// Decompression offset within window
/// </summary>
public uint WindowPosition { get; set; }
/// <summary>
/// Bytes remaining for current frame
/// </summary>
public uint FrameTODO { get; set; }
/// <summary>
/// High: arith coding state
/// </summary>
public ushort High { get; set; }
/// <summary>
/// Low: arith coding state
/// </summary>
public ushort Low { get; set; }
/// <summary>
/// Current: arith coding state
/// </summary>
public ushort Current { get; set; }
/// <summary>
/// Have we started decoding a new frame?
/// </summary>
public byte HeaderRead { get; set; }
// Four literal models, each representing 64 symbols
/// <summary>
/// For literals from 0 to 63 (selector = 0)
/// </summary>
public QTMDModel Model0 { get; set; }
/// <summary>
/// For literals from 64 to 127 (selector = 1)
/// </summary>
public QTMDModel Model1 { get; set; }
/// <summary>
/// For literals from 128 to 191 (selector = 2)
/// </summary>
public QTMDModel Model2 { get; set; }
/// <summary>
/// For literals from 129 to 255 (selector = 3)
/// </summary>
public QTMDModel Model3 { get; set; }
// Three match models.
/// <summary>
/// For match with fixed length of 3 bytes
/// </summary>
public QTMDModel Model4 { get; set; }
/// <summary>
/// For match with fixed length of 4 bytes
/// </summary>
public QTMDModel Model5 { get; set; }
/// <summary>
/// For variable length match, encoded with model6len model
/// </summary>
public QTMDModel Model6 { get; set; }
public QTMDModel Model6Len { get; set; }
/// <summary>
/// Selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6)
/// </summary>
public QTMDModel Model7 { get; set; }
// Symbol arrays for all models
public QTMDModelSym[] Model0Symbols { get; set; } = new QTMDModelSym[64 + 1];
public QTMDModelSym[] Model1Symbols { get; set; } = new QTMDModelSym[64 + 1];
public QTMDModelSym[] Model2Symbols { get; set; } = new QTMDModelSym[64 + 1];
public QTMDModelSym[] Model3Symbols { get; set; } = new QTMDModelSym[64 + 1];
public QTMDModelSym[] Model4Symbols { get; set; } = new QTMDModelSym[24 + 1];
public QTMDModelSym[] Model5Symbols { get; set; } = new QTMDModelSym[36 + 1];
public QTMDModelSym[] Model6Symbols { get; set; } = new QTMDModelSym[42 + 1];
public QTMDModelSym[] Model6LenSymbols { get; set; } = new QTMDModelSym[27 + 1];
public QTMDModelSym[] Model7Symbols { get; set; } = new QTMDModelSym[7 + 1];
public override Error READ_BYTES(ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
Error error = READ_IF_NEEDED(ref i_ptr, ref i_end);
if (error != Error.MSPACK_ERR_OK)
return error;
byte b0 = InputBuffer[i_ptr++];
error = READ_IF_NEEDED(ref i_ptr, ref i_end);
if (error != Error.MSPACK_ERR_OK)
return error;
byte b1 = InputBuffer[i_ptr++];
INJECT_BITS((uint)((b0 << 8) | b1), 16, ref bitsLeft, ref bitBuffer);
return Error.MSPACK_ERR_OK;
}
public override int HUFF_ERROR() => (int)Error.MSPACK_ERR_OK;
}
}

View File

@@ -0,0 +1,20 @@
/* 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 class DefaultFileImpl
{
public Stream FileHandle { get; set; }
public string Name { get; set; }
};
}

215
BurnOutSharp/External/libmspack/Enums.cs vendored Normal file
View File

@@ -0,0 +1,215 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp
{
/// <summary>
/// All compressors and decompressors use the same set of error codes. Most
/// methods return an error code directly.For methods which do not
/// return error codes directly, the error code can be obtained with the
/// last_error() method.
/// </summary>
public enum Error
{
/// <summary>
/// Used to indicate success.
/// This error code is defined as zero, all other code are non-zero.
/// </summary>
MSPACK_ERR_OK = 0,
/// <summary>
/// A method was called with inappropriate arguments.
/// </summary>
MSPACK_ERR_ARGS = 1,
/// <summary>
/// Error opening file
/// </summary>
MSPACK_ERR_OPEN = 2,
/// <summary>
/// Error reading file
/// </summary>
MSPACK_ERR_READ = 3,
/// <summary>
/// Error writing file
/// </summary>
MSPACK_ERR_WRITE = 4,
/// <summary>
/// Seek error
/// </summary>
MSPACK_ERR_SEEK = 5,
/// <summary>
/// Out of memory
/// </summary>
MSPACK_ERR_NOMEMORY = 6,
/// <summary>
/// Bad "magic id" in file
/// </summary>
MSPACK_ERR_SIGNATURE = 7,
/// <summary>
/// Bad or corrupt file format
/// </summary>
MSPACK_ERR_DATAFORMAT = 8,
/// <summary>
/// Bad checksum or CRC
/// </summary>
MSPACK_ERR_CHECKSUM = 9,
/// <summary>
/// Error during compression
/// </summary>
MSPACK_ERR_CRUNCH = 10,
/// <summary>
/// Error during decompression
/// </summary>
MSPACK_ERR_DECRUNCH = 11,
}
/// <summary>
/// The interface to request current version of
/// </summary>
public enum Interfaces
{
/// <summary>
/// Pass to mspack_version() to get the overall library version
/// </summary>
MSPACK_VER_LIBRARY = 0,
/// <summary>
/// Pass to mspack_version() to get the mspack_system version
/// </summary>
MSPACK_VER_SYSTEM = 1,
/// <summary>
/// Pass to mspack_version() to get the mscab_decompressor version
/// </summary>
MSPACK_VER_MSCABD = 2,
/// <summary>
/// Pass to mspack_version() to get the mscab_compressor version
/// </summary>
MSPACK_VER_MSCABC = 3,
/// <summary>
/// Pass to mspack_version() to get the mschm_decompressor version
/// </summary>
MSPACK_VER_MSCHMD = 4,
/// <summary>
/// Pass to mspack_version() to get the mschm_compressor version
/// </summary>
MSPACK_VER_MSCHMC = 5,
/// <summary>
/// Pass to mspack_version() to get the mslit_decompressor version
/// </summary>
MSPACK_VER_MSLITD = 6,
/// <summary>
/// Pass to mspack_version() to get the mslit_compressor version
/// </summary>
MSPACK_VER_MSLITC = 7,
/// <summary>
/// Pass to mspack_version() to get the mshlp_decompressor version
/// </summary>
MSPACK_VER_MSHLPD = 8,
/// <summary>
/// Pass to mspack_version() to get the mshlp_compressor version
/// </summary>
MSPACK_VER_MSHLPC = 9,
/// <summary>
/// Pass to mspack_version() to get the msszdd_decompressor version
/// </summary>
MSPACK_VER_MSSZDDD = 10,
/// <summary>
/// Pass to mspack_version() to get the msszdd_compressor version
/// </summary>
MSPACK_VER_MSSZDDC = 11,
/// <summary>
/// Pass to mspack_version() to get the mskwaj_decompressor version
/// </summary>
MSPACK_VER_MSKWAJD = 12,
/// <summary>
/// Pass to mspack_version() to get the mskwaj_compressor version
/// </summary>
MSPACK_VER_MSKWAJC = 13,
/// <summary>
/// Pass to mspack_version() to get the msoab_decompressor version
/// </summary>
MSPACK_VER_MSOABD = 14,
/// <summary>
/// Pass to mspack_version() to get the msoab_compressor version
/// </summary>
MSPACK_VER_MSOABC = 15,
}
public enum OpenMode
{
/// <summary>
/// mspack_system::open() mode: open existing file for reading.
/// </summary>
MSPACK_SYS_OPEN_READ = 0,
/// <summary>
/// mspack_system::open() mode: open new file for writing
/// </summary>
MSPACK_SYS_OPEN_WRITE = 1,
/// <summary>
/// mspack_system::open() mode: open existing file for writing
/// </summary>
MSPACK_SYS_OPEN_UPDATE = 2,
/// <summary>
/// mspack_system::open() mode: open existing file for writing
/// </summary>
MSPACK_SYS_OPEN_APPEND = 3,
}
public enum SeekMode
{
/// <summary>
/// mspack_system::seek() mode: seek relative to start of file
/// </summary>
MSPACK_SYS_SEEK_START = 0,
/// <summary>
/// mspack_system::seek() mode: seek relative to current offset
/// </summary>
MSPACK_SYS_SEEK_CUR = 1,
/// <summary>
/// mspack_system::seek() mode: seek relative to end of file
/// </summary>
MSPACK_SYS_SEEK_END = 2,
}
}

View File

@@ -0,0 +1,23 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.HLP
{
public class Compressor
{
public int Dummy { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,23 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.HLP
{
public class Decompressor
{
public int Dummy { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,15 @@
/* 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 Implementation
{
}
}

View File

@@ -0,0 +1,139 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.KWAJ
{
/// <summary>
/// A compressor for the KWAJ file format.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateKWAJCompressor(SystemImpl)"/>
/// <see cref="Library.DestroyKWAJCompressor(Compressor)"/>
public class Compressor
{
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// KWAJ compressed file format.The KWAJ compression format is quick
/// but gives poor compression.It is possible for the compressed output
/// file to be larger than the input file.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Compressor
/// instance being called
/// </param>
/// <param name="input">
/// the name of the file to compressed. This is passed
/// passed directly to mspack_system::open()
/// </param>
/// <param name="output">
/// the name of the file to write compressed data to.
/// This is passed directly to mspack_system::open().
/// </param>
/// <param name="length">
/// the length of the uncompressed file, or -1 to indicate
/// that this should be determined automatically by using
/// mspack_system::seek() on the input file.
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="SetParam"/>
public Func<Compressor, string, string, long, Error> Compress;
/// <summary>
/// Sets an KWAJ compression engine parameter.
///
/// The following parameters are defined:
///
/// - #MSKWAJC_PARAM_COMP_TYPE: the compression method to use. Must
/// be one of #MSKWAJC_COMP_NONE, #MSKWAJC_COMP_XOR, #MSKWAJ_COMP_SZDD
/// or #MSKWAJ_COMP_LZH. The default is #MSKWAJ_COMP_LZH.
///
/// - #MSKWAJC_PARAM_INCLUDE_LENGTH: a boolean; should the compressed
/// output file should include the uncompressed length of the input
/// file in the header? This adds 4 bytes to the size of the output
/// file. A value of zero says "no", non-zero says "yes". The default
/// is "no".
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Compressor
/// instance being called
/// </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>
/// <see cref="Generate"/>
public Func<Compressor, Parameters, int, Error> SetParam;
/// <summary>
/// Sets the original filename of the file before compression,
/// which will be stored in the header of the output file.
///
/// The filename should be a null-terminated string, it must be an
/// MS-DOS "8.3" type filename (up to 8 bytes for the filename, then
/// optionally a "." and up to 3 bytes for a filename extension).
///
/// If NULL is passed as the filename, no filename is included in the
/// header. This is the default.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Compressor
/// instance being called
/// </param>
/// <param name="filename">the original filename to use</param>
/// <returns>
/// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if the
/// filename is too long
/// </returns>
public Func<Compressor, string, Error> SetFilename;
/// <summary>
/// Sets arbitrary data that will be stored in the header of the
/// output file, uncompressed. It can be up to roughly 64 kilobytes,
/// as the overall size of the header must not exceed 65535 bytes.
/// The data can contain null bytes if desired.
///
/// If NULL is passed as the data pointer, or zero is passed as the
/// length, no extra data is included in the header. This is the
/// default.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Compressor
/// instance being called
/// </param>
/// <param name="data">a pointer to the data to be stored in the header</param>
/// <param name="bytes">the length of the data in bytes</param>
/// <returns>
/// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS extra data
/// is too long
/// </returns>
public Func<Compressor, byte[], int, int, Error> SetExtraData;
/// <summary>
/// Returns the error code set by the most recently called method.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the Compressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Compress"/>
public Func<Compressor, Error> LastError;
}
}

View File

@@ -0,0 +1,25 @@
/* 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

@@ -0,0 +1,126 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.KWAJ
{
/// <summary>
/// A decompressor for KWAJ compressed files.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateKWAJDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyKWAJDecompressor(Decompressor)"/>
public class Decompressor
{
/// <summary>
/// Opens a KWAJ file and reads the header.
///
/// If the file opened is a valid KWAJ file, all headers will be read and
/// a mskwajd_header structure will be returned.
///
/// In the case of an error occuring, NULL is returned and the error code
/// is available from last_error().
///
/// The filename pointer should be considered "in use" until close() is
/// 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;
/// <summary>
/// Closes a previously opened KWAJ file.
///
/// This closes a KWAJ file and frees the mskwajd_header associated
/// 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;
/// <summary>
/// Extracts the compressed data from a KWAJ file.
///
/// 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="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;
/// <summary>
/// Decompresses an KWAJ file to an output file in one step.
///
/// This opens an KWAJ file as input, reads the header, then decompresses
/// the compressed data immediately to an output file, finally closing
/// both the input and output file. It is more convenient to use than
/// open() then extract() then close(), if you do not need to know the
/// KWAJ output size or output filename.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the mskwaj_decompressor
/// instance being called
/// </param>
/// <param name="input">
/// the filename of the input KWAJ file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// 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;
/// <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.
/// </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;
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,88 @@
/* 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
*/
using System;
namespace LibMSPackSharp.KWAJ
{
public enum CompressionType
{
/// <summary>
/// no compression.
/// </summary>
MSKWAJ_COMP_NONE = 0,
/// <summary>
/// no compression, 0xFF XOR "encryption".
/// </summary>
MSKWAJ_COMP_XOR = 1,
/// <summary>
/// LZSS (same method as SZDD)
/// </summary>
MSKWAJ_COMP_SZDD = 2,
/// <summary>
/// LZ+Huffman compression
/// </summary>
MSKWAJ_COMP_LZH = 3,
/// <summary>
/// MSZIP
/// </summary>
MSKWAJ_COMP_MSZIP = 4,
}
[Flags]
public enum OptionalHeaderFlag : ushort
{
/// <summary>
/// decompressed file length is included
/// </summary>
MSKWAJ_HDR_HASLENGTH = 0x01,
/// <summary>
/// unknown 2-byte structure is included
/// </summary>
MSKWAJ_HDR_HASUNKNOWN1 = 0x02,
/// <summary>
/// unknown multi-sized structure is included
/// </summary>
MSKWAJ_HDR_HASUNKNOWN2 = 0x04,
/// <summary>
/// file name (no extension) is included
/// </summary>
MSKWAJ_HDR_HASFILENAME = 0x08,
/// <summary>
/// file extension is included
/// </summary>
MSKWAJ_HDR_HASFILEEXT = 0x10,
/// <summary>
/// extra text is included
/// </summary>
MSKWAJ_HDR_HASEXTRATEXT = 0x20,
}
public enum Parameters
{
/// <summary>
/// Compression type
/// </summary>
MSKWAJC_PARAM_COMP_TYPE = 0,
/// <summary>
/// Include the length of the uncompressed file in the header?
/// </summary>
MSKWAJC_PARAM_INCLUDE_LENGTH = 1,
}
}

View File

@@ -0,0 +1,62 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.KWAJ
{
/// <summary>
/// A structure which represents an KWAJ compressed file.
///
/// All fields are READ ONLY.
/// </summary>
public class Header
{
/// <summary>
/// The compression type
/// </summary>
public CompressionType CompressionType { get; set; }
/// <summary>
/// The offset in the file where the compressed data stream begins
/// </summary>
public long DataOffset { get; set; }
/// <summary>
/// Flags indicating which optional headers were included.
/// </summary>
public OptionalHeaderFlag Headers { get; set; }
/// <summary>
/// The amount of uncompressed data in the file, or 0 if not present.
/// </summary>
public long Length { get; set; }
/// <summary>
/// Output filename, or NULL if not present
/// </summary>
public string Filename { get; set; }
/// <summary>
/// Extra uncompressed data (usually text) in the header.
/// This data can contain nulls so use extra_length to get the size.
/// </summary>
public string Extra { get; set; }
/// <summary>
/// Length of extra uncompressed data in the header
/// </summary>
public ushort ExtraLength { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
/* 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 HeaderImpl : Header
{
public object FileHandle { get; set; }
}
}

View File

@@ -0,0 +1,772 @@
/* 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;
using System.Text;
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.KWAJ
{
public class Implementation
{
#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 static int KWAJ_MATCHLEN1_TBLSIZE
{
get
{
if (KWAJ_TABLESIZE < (KWAJ_MATCHLEN1_SYMS * 2))
return (KWAJ_MATCHLEN1_SYMS * 4);
else
return (KWAJ_TABLESIZE + (KWAJ_MATCHLEN1_SYMS * 2));
}
}
public static int KWAJ_MATCHLEN2_TBLSIZE
{
get
{
if (KWAJ_TABLESIZE < (KWAJ_MATCHLEN2_SYMS * 2))
return (KWAJ_MATCHLEN2_SYMS * 4);
else
return (KWAJ_TABLESIZE + (KWAJ_MATCHLEN2_SYMS * 2));
}
}
public static int KWAJ_LITLEN_TBLSIZE
{
get
{
if (KWAJ_TABLESIZE < (KWAJ_LITLEN_SYMS * 2))
return (KWAJ_LITLEN_SYMS * 4);
else
return (KWAJ_TABLESIZE + (KWAJ_LITLEN_SYMS * 2));
}
}
public static int KWAJ_OFFSET_TBLSIZE
{
get
{
if (KWAJ_TABLESIZE < (KWAJ_OFFSET_SYMS * 2))
return (KWAJ_OFFSET_SYMS * 4);
else
return (KWAJ_TABLESIZE + (KWAJ_OFFSET_SYMS * 2));
}
}
public static int KWAJ_LITERAL_TBLSIZE
{
get
{
if (KWAJ_TABLESIZE < (KWAJ_LITERAL_SYMS * 2))
return (KWAJ_LITERAL_SYMS * 4);
else
return (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 = (DecompressorImpl)d;
if (self == null)
return null;
SystemImpl sys = self.System;
object fh = sys.Open(sys, 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);
sys.Free(hdr);
hdr = null;
}
return hdr;
}
#endregion
#region KWAJD_CLOSE
/// <summary>
/// Closes a KWAJ file
/// </summary>
public static void Close(Decompressor d, Header hdr)
{
DecompressorImpl self = (DecompressorImpl)d;
HeaderImpl hdr_p = (HeaderImpl)hdr;
if (self == null || self.System == null)
return;
// Close the file handle associated
self.System.Close(hdr_p.FileHandle);
// Free the memory associated
self.System.Free(hdr);
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, object 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 = (DecompressorImpl)d;
if (self == null)
return Error.MSPACK_ERR_ARGS;
if (hdr == null)
return self.Error = Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
object fh = ((HeaderImpl)hdr).FileHandle;
// 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
object outfh;
if ((outfh = sys.Open(sys, 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;
sys.Free(buf);
}
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)
{
InternalStream lzh = LZHInit(sys, fh, outfh);
self.Error = (lzh != null) ? LZHDecompress(lzh) : Error.MSPACK_ERR_NOMEMORY;
LZHFree(lzh);
}
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;
MSZIP.Free(zip);
}
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 = (DecompressorImpl)d;
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 = (DecompressorImpl)d;
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 InternalStream LZHInit(SystemImpl sys, object input, object output)
{
if (sys == null || input == null || output == null)
return null;
return new InternalStream()
{
Sys = sys,
Input = input,
Output = output,
};
}
private static Error LZHDecompress(InternalStream lzh)
{
uint bit_buffer = 0, bits_left = 0, len = 0, j = 0;
int i;
ushort sym = 0;
int i_ptr = 0, i_end = 0;
bool lit_run = false;
int pos = 0, offset;
int[] types = new int[6];
// Reset global state
lzh.INIT_BITS();
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
for (i = 0; i < LZSS.LZSS_WINDOW_SIZE; i++)
{
lzh.Window[i] = LZSS.LZSS_WINDOW_FILL;
}
// Read 6 encoding types (for byte alignment) but only 5 are needed
for (i = 0; i < 6; i++)
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref types[i], 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
}
// Read huffman table symbol lengths and build huffman trees
//BUILD_TREE(tbl, type)
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
Error err = LZHReadLens(lzh, (uint)types[0], KWAJ_MATCHLEN1_SYMS, lzh.MATCHLEN1_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (InternalStream.MakeDecodeTable(KWAJ_MATCHLEN1_SYMS, (uint)KWAJ_MATCHLEN1_TBLSIZE, lzh.MATCHLEN1_len, lzh.MATCHLEN1_table) != 0)
return Error.MSPACK_ERR_DATAFORMAT;
//BUILD_TREE(tbl, type)
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
err = LZHReadLens(lzh, (uint)types[1], KWAJ_MATCHLEN2_SYMS, lzh.MATCHLEN2_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (InternalStream.MakeDecodeTable(KWAJ_MATCHLEN2_SYMS, (uint)KWAJ_MATCHLEN2_TBLSIZE, lzh.MATCHLEN2_len, lzh.MATCHLEN2_table) != 0)
return Error.MSPACK_ERR_DATAFORMAT;
//BUILD_TREE(tbl, type)
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
err = LZHReadLens(lzh, (uint)types[2], KWAJ_LITLEN_SYMS, lzh.LITLEN_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (InternalStream.MakeDecodeTable(KWAJ_LITLEN_SYMS, (uint)KWAJ_LITLEN_TBLSIZE, lzh.LITLEN_len, lzh.LITLEN_table) != 0)
return Error.MSPACK_ERR_DATAFORMAT;
//BUILD_TREE(tbl, type)
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
err = LZHReadLens(lzh, (uint)types[3], KWAJ_OFFSET_SYMS, lzh.OFFSET_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (InternalStream.MakeDecodeTable(KWAJ_OFFSET_SYMS, (uint)KWAJ_OFFSET_TBLSIZE, lzh.OFFSET_len, lzh.OFFSET_table) != 0)
return Error.MSPACK_ERR_DATAFORMAT;
//BUILD_TREE(tbl, type)
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
err = LZHReadLens(lzh, (uint)types[4], KWAJ_LITERAL_SYMS, lzh.LITERAL_len);
if (err != Error.MSPACK_ERR_OK)
return err;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (InternalStream.MakeDecodeTable(KWAJ_LITERAL_SYMS, (uint)KWAJ_LITERAL_TBLSIZE, lzh.LITERAL_len, lzh.LITERAL_table) != 0)
return Error.MSPACK_ERR_DATAFORMAT;
while (lzh.InputEnd == 0)
{
if (lit_run)
{
//READ_HUFFSYM_SAFE(tbl, val)
lzh.READ_HUFFSYM(lzh.MATCHLEN2_table, ref len, KWAJ_MATCHLEN2_TBLSIZE, lzh.MATCHLEN2_len, KWAJ_MATCHLEN2_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
}
else
{
//READ_HUFFSYM_SAFE(tbl, val)
lzh.READ_HUFFSYM(lzh.MATCHLEN1_table, ref len, KWAJ_MATCHLEN1_TBLSIZE, lzh.MATCHLEN1_len, KWAJ_MATCHLEN1_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
}
if (len > 0)
{
len += 2;
lit_run = false; // Not the end of a literal run
//READ_HUFFSYM_SAFE(tbl, val)
lzh.READ_HUFFSYM(lzh.OFFSET_table, ref j, KWAJ_OFFSET_TBLSIZE, lzh.OFFSET_len, KWAJ_OFFSET_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
offset = (int)(j << 6);
//READ_BITS_SAFE(val, n)
int tempj = (int)j;
lzh.READ_BITS(ref tempj, 6, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
j = (uint)tempj;
offset |= tempj;
// Copy match as output and into the ring buffer
while (len-- > 0)
{
lzh.Window[pos] = lzh.Window[(pos + 4096 - offset) & 4095];
if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1)
return Error.MSPACK_ERR_WRITE;
pos++; pos &= 4095;
}
}
else
{
//READ_HUFFSYM_SAFE(tbl, val)
lzh.READ_HUFFSYM(lzh.LITLEN_table, ref len, KWAJ_LITLEN_TBLSIZE, lzh.LITLEN_len, KWAJ_LITLEN_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
len++;
lit_run = (len == 32) ? false : true; // End of a literal run?
while (len-- > 0)
{
//READ_HUFFSYM_SAFE(tbl, val)
lzh.READ_HUFFSYM(lzh.LITERAL_table, ref j, KWAJ_LITERAL_TBLSIZE, lzh.LITERAL_len, KWAJ_LITERAL_SYMS, ref i, ref sym, ref i_ptr, ref i_end, ref bits_left, ref bit_buffer);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
// Copy as output and into the ring buffer
lzh.Window[pos] = (byte)j;
if (lzh.Sys.Write(lzh.Output, lzh.Window, pos, 1) != 1)
return Error.MSPACK_ERR_WRITE;
pos++;
pos &= 4095;
}
}
}
return Error.MSPACK_ERR_OK;
}
private static void LZHFree(InternalStream lzh)
{
if (lzh == null || lzh.Sys == null)
return;
SystemImpl sys = lzh.Sys;
sys.Free(lzh);
}
public static Error LZHReadLens(InternalStream lzh, uint type, uint numsyms, byte[] lens)
{
uint bit_buffer = 0, bits_left = 0;
int i_ptr = 0, i_end = 0;
uint i;
int c = 0, sel = 0;
lzh.RESTORE_BITS(ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
switch (type)
{
case 0:
i = numsyms;
c = (i == 16) ? 4 : (i == 32) ? 5 : (i == 64) ? 6 : (i == 256) ? 8 : 0;
for (i = 0; i < numsyms; i++)
{
lens[i] = (byte)c;
}
break;
case 1:
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref sel, 1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
if (sel == 0)
{
lens[i] = (byte)c;
}
else
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref sel, 1, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
if (sel == 0)
{
lens[i] = (byte)++c;
}
else
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
lens[i] = (byte)c;
}
}
}
break;
case 2:
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref sel, 2, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
if (sel == 3)
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
}
else
{
c += (char)sel - 1;
}
lens[i] = (byte)c;
}
break;
case 3:
for (i = 0; i < numsyms; i++)
{
//READ_BITS_SAFE(val, n)
lzh.READ_BITS(ref c, 4, ref i_ptr, ref i_end, ref bit_buffer, ref bits_left);
if (lzh.InputEnd != 0 && bits_left < lzh.InputEnd)
return Error.MSPACK_ERR_OK;
lens[i] = (byte)c;
}
break;
}
lzh.STORE_BITS(i_ptr, i_end, bit_buffer, bits_left);
return Error.MSPACK_ERR_OK;
}
public static Error LZHReadInput(InternalStream lzh)
{
int read;
if (lzh.InputEnd != 0)
{
lzh.InputEnd += 8;
lzh.InputBuffer[0] = 0;
read = 1;
}
else
{
read = lzh.Sys.Read(lzh.Input, lzh.InputBuffer, 0, KWAJ_INPUT_SIZE);
if (read < 0)
return Error.MSPACK_ERR_READ;
if (read == 0)
{
lzh.InputLength = 8;
lzh.InputBuffer[0] = 0;
read = 1;
}
}
// Update InputPointer and InputLength
lzh.InputPointer = 0;
lzh.InputLength = read;
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -0,0 +1,54 @@
/* 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 LibMSPackSharp.Compression;
namespace LibMSPackSharp.KWAJ
{
public class InternalStream : 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];
// 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];
// History window
public byte[] Window { get; set; } = new byte[LZSS.LZSS_WINDOW_SIZE];
public override Error READ_BYTES(ref int i_ptr, ref int i_end, ref uint bitsLeft, ref uint bitBuffer)
{
Error error = Error.MSPACK_ERR_OK;
if (i_ptr >= i_end)
{
if ((error = Implementation.LZHReadInput(this)) != Error.MSPACK_ERR_OK)
return error;
i_ptr = InputPointer;
i_end = InputLength;
}
INJECT_BITS(InputBuffer[i_ptr++], 8, ref bitsLeft, ref bitBuffer);
return error;
}
public override int HUFF_ERROR() => (int)Error.MSPACK_ERR_DATAFORMAT;
}
}

View File

@@ -0,0 +1,24 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.LIT
{
// TODO
public class Compressor
{
public int Dummy { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,24 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.LIT
{
// TODO
public class Decompressor
{
public int Dummy { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,15 @@
/* 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

@@ -0,0 +1,564 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** \mainpage
*
* \section intro Introduction
*
* libmspack is a library which provides compressors and decompressors,
* archivers and dearchivers for Microsoft compression formats.
*
* \section formats Formats supported
*
* The following file formats are supported:
* - SZDD files, which use LZSS compression
* - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression
* - .HLP (MS Help) files, which use LZSS compression
* - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression
* - .CHM (HTML Help) files, which use LZX compression
* - .LIT (MS EBook) files, which use LZX compression and DES encryption
* - .LZX (Exchange Offline Addressbook) files, which use LZX compression
*
* To determine the capabilities of the library, and the binary
* compatibility version of any particular compressor or decompressor, use
* the mspack_version() function. The UNIX library interface version is
* defined as the highest-versioned library component.
*
* \section starting Getting started
*
* The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can
* be used. In particular, it checks if the caller is using 32-bit file I/O
* when the library is compiled for 64-bit file I/O and vice versa.
*
* If compiled normally, the library includes basic file I/O and memory
* management functionality using the standard C library. This can be
* customised and replaced entirely by creating a SystemImpl structure.
*
* A compressor or decompressor for the required format must be
* instantiated before it can be used. Each construction function takes
* one parameter, which is either a pointer to a custom SystemImpl
* structure, or null to use the default. The instantiation returned, if
* not null, contains function pointers (methods) to work with the given
* file format.
*
* For compression:
* - CreateCABCompressor() creates a mscab_compressor
* - CreateCHMCompressor() creates a mschm_compressor
* - CreateLITCompressor() creates a mslit_compressor
* - CreateHLPCompressor() creates a mshlp_compressor
* - CreateSZDDCompressor() creates a msszdd_compressor
* - CreateKWAJCompressor() creates a mskwaj_compressor
* - CreateOABCompressor() creates a msoab_compressor
*
* For decompression:
* - mspack_create_cab_decompressor() creates a mscab_decompressor
* - mspack_create_chm_decompressor() creates a mschm_decompressor
* - mspack_create_lit_decompressor() creates a mslit_decompressor
* - mspack_create_hlp_decompressor() creates a mshlp_decompressor
* - mspack_create_szdd_decompressor() creates a msszdd_decompressor
* - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor
* - mspack_create_oab_decompressor() creates a msoab_decompressor
*
* Once finished working with a format, each kind of
* compressor/decompressor has its own specific destructor:
* - mspack_destroy_cab_compressor()
* - mspack_destroy_cab_decompressor()
* - mspack_destroy_chm_compressor()
* - mspack_destroy_chm_decompressor()
* - mspack_destroy_lit_compressor()
* - mspack_destroy_lit_decompressor()
* - mspack_destroy_hlp_compressor()
* - mspack_destroy_hlp_decompressor()
* - mspack_destroy_szdd_compressor()
* - mspack_destroy_szdd_decompressor()
* - mspack_destroy_kwaj_compressor()
* - mspack_destroy_kwaj_decompressor()
* - mspack_destroy_oab_compressor()
* - mspack_destroy_oab_decompressor()
*
* Destroying a compressor or decompressor does not destroy any objects,
* structures or handles that have been created using that compressor or
* decompressor. Ensure that everything created or opened is destroyed or
* closed before compressor/decompressor is itself destroyed.
*
* \section threading Multi-threading
*
* libmspack methods are reentrant and multithreading-safe when each
* thread has its own compressor or decompressor.
* You should not call multiple methods simultaneously on a single
* compressor or decompressor instance.
*
* If this may happen, you can either use one compressor or
* decompressor per thread, or you can use your preferred lock,
* semaphore or mutex library to ensure no more than one method on a
* compressor/decompressor is called simultaneously. libmspack will
* not do this locking for you.
*
* Example of incorrect behaviour:
* - thread 1 calls mspack_create_cab_decompressor()
* - thread 1 calls open()
* - thread 1 calls extract() for one file
* - thread 2 simultaneously calls extract() for another file
*
* Correct behaviour:
* - thread 1 calls mspack_create_cab_decompressor()
* - thread 2 calls mspack_create_cab_decompressor()
* - thread 1 calls its own open() / extract()
* - thread 2 simultaneously calls its own open() / extract()
*
* Also correct behaviour:
* - thread 1 calls mspack_create_cab_decompressor()
* - thread 1 locks a mutex for with the decompressor before
* calling any methods on it, and unlocks the mutex after each
* method returns.
* - thread 1 can share the results of open() with thread 2, and both
* can call extract(), provided they both guard against simultaneous
* use of extract(), and any other methods, with the mutex
*/
using LibMSPackSharp.Compression;
namespace LibMSPackSharp
{
public partial class Library
{
#region CAB
/// <summary>
/// Creates a new CAB compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="CAB.Compressor"/> or null</returns>
public CAB.Compressor CreateCABCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new CAB decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="CAB.Decompressor"/> or null</returns>
public CAB.Decompressor CreateCABDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new CAB.DecompressorImpl()
{
Open = CAB.Implementation.Open,
Close = CAB.Implementation.Close,
Search = CAB.Implementation.Search,
Extract = CAB.Implementation.Extract,
Prepend = CAB.Implementation.Prepend,
Append = CAB.Implementation.Append,
SetParam = CAB.Implementation.Param,
LastError = CAB.Implementation.LastError,
System = sys,
State = null,
Error = Error.MSPACK_ERR_OK,
SearchBufferSize = 32768,
FixMSZip = false,
BufferSize = 4096,
Salvage = false,
};
}
/// <summary>
/// Destroys an existing CAB compressor.
/// </summary>
/// <param name="c">the <see cref="CAB.Compressor"/> to destroy</param>
public void DestroyCABCompressor(CAB.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing CAB decompressor.
/// </summary>
/// <param name="d">the <see cref="CAB.Decompressor"/> to destroy</param>
public void DestroyCABDecompressor(CAB.Decompressor d)
{
CAB.DecompressorImpl self = (CAB.DecompressorImpl)d;
if (self != null)
{
SystemImpl sys = self.System;
if (self.State != null)
{
if (self.State.InputFileHandle != null)
sys.Close(self.State.InputFileHandle);
CAB.Implementation.FreeDecompressionState(self);
sys.Free(self.State);
}
sys.Free(self);
}
}
#endregion
#region CHM
/// <summary>
/// Creates a new CHM compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="CHM.Compressor"/> or null</returns>
public CHM.Compressor CreateCHMCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new CHM decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="CHM.Decompressor"/> or null</returns>
public CHM.Decompressor CreateCHMDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new CHM.DecompressorImpl()
{
Open = CHM.Implementation.Open,
Close = CHM.Implementation.Close,
Extract = CHM.Implementation.Extract,
LastError = CHM.Implementation.LastError,
FastOpen = CHM.Implementation.FastOpen,
FastFind = CHM.Implementation.FastFind,
System = sys,
Error = Error.MSPACK_ERR_OK,
State = null,
};
}
/// <summary>
/// Destroys an existing CHM compressor.
/// </summary>
/// <param name="c">the <see cref="CHM.Compressor"/> to destroy</param>
public void DestroyCHMCompressor(CHM.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing CHM decompressor.
/// </summary>
/// <param name="d">the <see cref="CHM.Decompressor"/> to destroy</param>
public void DestroyCHMDecompressor(CHM.Decompressor d)
{
CHM.DecompressorImpl self = (CHM.DecompressorImpl)d;
if (self != null)
{
SystemImpl sys = self.System;
if (self.State != null)
{
if (self.State.InputFileHandle != null)
sys.Close(self.State.InputFileHandle);
if (self.State.State != null)
LZX.Free(self.State.State);
sys.Free(self.State);
}
sys.Free(self);
}
}
#endregion
#region LIT
/// <summary>
/// Creates a new LIT compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="LIT.Compressor"/> or null</returns>
public LIT.Compressor CreateLITCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new LIT decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="LIT.Decompressor"/> or null</returns>
public LIT.Decompressor CreateLITDecompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Destroys an existing LIT compressor.
/// </summary>
/// <param name="c">the <see cref="LIT.Compressor"/> to destroy</param>
public void DestroyLITCompressor(LIT.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing LIT decompressor.
/// </summary>
/// <param name="d">the <see cref="LIT.Decompressor"/> to destroy</param>
public void DestroyLITDecompressor(LIT.Decompressor d)
{
// TODO
}
#endregion
#region HLP
/// <summary>
/// Creates a new HLP compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="HLP.Compressor"/> or null</returns>
public HLP.Compressor CreateHLPCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new HLP decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="HLP.Decompressor"/> or null</returns>
public HLP.Decompressor CreateHLPDecompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Destroys an existing HLP compressor.
/// </summary>
/// <param name="c">the <see cref="HLP.Compressor"/> to destroy</param>
public void DestroyHLPCompressor(HLP.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing HLP decompressor.
/// </summary>
/// <param name="d">the <see cref="HLP.Decompressor"/> to destroy</param>
public void DestroyHLPDecompressor(HLP.Decompressor d)
{
// TODO
}
#endregion
#region SZDD
/// <summary>
/// Creates a new SZDD compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="SZDD.Compressor"/> or null</returns>
public SZDD.Compressor CreateSZDDCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new SZDD decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="SZDD.Decompressor"/> or null</returns>
public SZDD.Decompressor CreateSZDDDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new SZDD.DecompressorImpl()
{
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,
};
}
/// <summary>
/// Destroys an existing SZDD compressor.
/// </summary>
/// <param name="c">the <see cref="SZDD.Compressor"/> to destroy</param>
public void DestroySZDDCompressor(SZDD.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing SZDD decompressor.
/// </summary>
/// <param name="d">the <see cref="SZDD.Decompressor"/> to destroy</param>
public void DestroySZDDDecompressor(SZDD.Decompressor d)
{
SZDD.DecompressorImpl self = (SZDD.DecompressorImpl)d;
if (self != null)
{
SystemImpl sys = self.System;
sys.Free(self);
}
}
#endregion
#region KWAJ
/// <summary>
/// Creates a new KWAJ compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="KWAJ.Compressor"/> or null</returns>
public KWAJ.Compressor CreateKWAJCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new KWAJ decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="KWAJ.Decompressor"/> or null</returns>
public KWAJ.Decompressor CreateKWAJDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new KWAJ.DecompressorImpl()
{
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,
};
}
/// <summary>
/// Destroys an existing KWAJ compressor.
/// </summary>
/// <param name="c">the <see cref="KWAJ.Compressor"/> to destroy</param>
public void DestroyKWAJCompressor(KWAJ.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing KWAJ decompressor.
/// </summary>
/// <param name="d">the <see cref="KWAJ.Decompressor"/> to destroy</param>
public void DestroyKWAJDecompressor(KWAJ.Decompressor d)
{
KWAJ.DecompressorImpl self = (KWAJ.DecompressorImpl)d;
if (self != null)
{
SystemImpl sys = self.System;
sys.Free(self);
}
}
#endregion
#region OAB
/// <summary>
/// Creates a new OAB compressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="OAB.Compressor"/> or null</returns>
public OAB.Compressor CreateOABCompressor(SystemImpl sys)
{
// TODO
return null;
}
/// <summary>
/// Creates a new OAB decompressor.
/// </summary>
/// <param name="sys">a custom SystemImpl structure, or null to use the default</param>
/// <returns>a <see cref="OAB.Decompressor"/> or null</returns>
public OAB.Decompressor CreateOABDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new OAB.DecompressorImpl()
{
Decompress = OAB.Implementation.Decompress,
DecompressIncremental = OAB.Implementation.DecompressIncremental,
SetParam = OAB.Implementation.Param,
System = sys,
BufferSize = 4096,
};
}
/// <summary>
/// Destroys an existing OAB compressor.
/// </summary>
/// <param name="c">the <see cref="OAB.Compressor"/> to destroy</param>
public void DestroyOABCompressor(OAB.Compressor c)
{
// TODO
}
/// <summary>
/// Destroys an existing OAB decompressor.
/// </summary>
/// <param name="d">the <see cref="OAB.Decompressor"/> to destroy</param>
public void DestroyOABDecompressor(OAB.Decompressor d)
{
OAB.DecompressorImpl self = (OAB.DecompressorImpl)d;
if (self != null)
{
SystemImpl sys = self.System;
sys.Free(self);
}
}
#endregion
}
}

View File

@@ -0,0 +1,80 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.OAB
{
/// <summary>
/// A compressor for the Offline Address Book (OAB) format.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateOABCompressor(SystemImpl)"/>
/// <see cref="Library.DestroyOABCompressor(Compressor)"/>
public class Compressor
{
/// <summary>
/// Compress a full OAB file.
///
/// The input file will be read and the compressed contents 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
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// 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<Compressor, string, string, Error> Compress;
/// <summary>
/// Generate a compressed incremental OAB patch file.
///
/// The two uncompressed files "input" and "base" will be read, and an
/// incremental patch to generate "input" from "base" 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 containing the new
/// version of its contents. This is passed directly
/// to mspack_system::open().
/// </param>
/// <param name="base">
/// the filename of the original base file containing
/// the old version of its contents, against which the
/// incremental patch shall generated. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// 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<Compressor, string, string, string, Error> CompressIncremental;
}
}

View File

@@ -0,0 +1,16 @@
/* 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

@@ -0,0 +1,106 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.OAB
{
/// <summary>
/// A decompressor for .LZX (Offline Address Book) files
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateOABDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroyOABDecompressor(Decompressor)"/>
public class Decompressor
{
/// <summary>
/// Decompresses a full Offline Address Book file.
///
/// If the input file is a valid compressed Offline Address Book file,
/// 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
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// 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;
/// <summary>
/// Decompresses an Offline Address Book with an incremental patch file.
///
/// This requires both a full UNCOMPRESSED Offline Address Book file to
/// act as the "base", and a compressed incremental patch file as input.
/// If the input file is valid, it will be decompressed with reference to
/// the base file, and the decompressed contents will be written to the
/// output file.
///
/// There is no way to tell what the right base file is for the given
/// incremental patch, but if you get it wrong, this will usually result
/// 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
/// directly to mspack_system::open().
/// </param>
/// <param name="base">
/// 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
/// 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;
/// <summary>
/// Sets an OAB decompression engine parameter. Available only in OAB
/// decompressor version 2 and above.
///
/// - #MSOABD_PARAM_DECOMPBUF: How many bytes should be used as an input
/// 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>
/// <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;
}
}

View File

@@ -0,0 +1,20 @@
/* 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

@@ -0,0 +1,19 @@
/* 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
*/
namespace LibMSPackSharp.OAB
{
public enum Parameters
{
/// <summary>
/// Size of decompression buffer
/// </summary>
MSOABD_PARAM_DECOMPBUF = 0,
}
}

View File

@@ -0,0 +1,620 @@
/* 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 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 = (InternalFile)baseFile;
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)
{
InternalFile file = (InternalFile)baseFile;
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;
}
#endregion
#region OABD_DECOMPRESS
public static Error Decompress(Decompressor d, string input, string output)
{
DecompressorImpl self = (DecompressorImpl)d;
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;
object infh = sys.Open(sys, input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
sys.Close(infh);
return ret;
}
if (sys.Read(infh, hdrbuf, 0, oabhead_SIZEOF) != oabhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
sys.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, oabhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, oabhead_VersionLo) != 1)
{
ret = Error.MSPACK_ERR_SIGNATURE;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
sys.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, oabhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, oabhead_TargetSize);
object outfh = sys.Open(sys, output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
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;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
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;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
if (blk_flags == 0)
{
// Uncompressed block
if (blk_dsize != blk_csize)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
ret = CopyFileHandle(sys, infh, outfh, (int)blk_dsize, buf, self.BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
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, out_ofh, window_bits, 0, self.BufferSize, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
LZX.Free(lzx);
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)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
}
target_size -= blk_dsize;
}
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
#endregion
#region OABD_DECOMPRESS_INCREMENTAL
public static Error DecompressIncremental(Decompressor d, string input, string basePath, string output)
{
DecompressorImpl self = (DecompressorImpl)d;
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;
object infh = sys.Open(sys, input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
sys.Close(infh);
return ret;
}
if (sys.Read(infh, hdrbuf, 0, patchhead_SIZEOF) != patchhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
sys.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, patchhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, patchhead_VersionLo) != 2)
{
ret = Error.MSPACK_ERR_SIGNATURE;
if (lzx != null)
LZX.Free(lzx);
if (infh != null)
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;
object basefh = sys.Open(sys, basePath, OpenMode.MSPACK_SYS_OPEN_READ);
if (basefh == null)
{
ret = Error.MSPACK_ERR_OPEN;
if (lzx != null)
LZX.Free(lzx);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
return ret;
}
object outfh = sys.Open(sys, output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
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;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
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;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
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, out_ofh, window_bits, 0, 4096, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
ret = LZX.SetReferenceData(lzx, sys, basefh, blk_ssize);
if (ret != Error.MSPACK_ERR_OK)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
ret = LZX.Decompress(lzx, blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
LZX.Free(lzx);
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)
{
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
target_size -= blk_dsize;
}
if (lzx != null)
LZX.Free(lzx);
if (outfh != null)
sys.Close(outfh);
if (basefh != null)
sys.Close(basefh);
if (infh != null)
sys.Close(infh);
sys.Free(buf);
return ret;
}
private static Error CopyFileHandle(SystemImpl sys, object infh, object 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 = (DecompressorImpl)d;
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

@@ -0,0 +1,29 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.OAB
{
public class InternalFile
{
public SystemImpl OrigSys { get; set; }
public object OrigFile { get; set; }
public uint CRC { get; set; }
public int Available { get; set; }
}
}

View File

@@ -0,0 +1,110 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.SZDD
{
/// <summary>
/// A compressor for the SZDD file format.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateSZDDCompressor(SystemImpl)"/>
/// <see cref="Library.DestroySZDDCompressor(Compressor)"/>
public class Compressor
{
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// SZDD compressed file format. The SZDD compression format is quick
/// but gives poor compression. It is possible for the compressed output
/// file to be larger than the input file.
///
/// Conventionally, SZDD compressed files have the final character in
/// their filename replaced with an underscore, to show they are
/// compressed. The missing character is stored in the compressed file
/// itself. This is due to the restricted filename conventions of MS-DOS,
/// most operating systems, such as UNIX, simply append another file
/// extension to the existing filename. As mspack does not deal with
/// filenames, this is left up to you. If you wish to set the missing
/// character stored in the file header, use set_param() with the
/// #MSSZDDC_PARAM_MISSINGCHAR parameter.
///
/// "Stream" compression (where the length of the input data is not
/// known) is not possible. The length of the input data is stored in the
/// header of the SZDD file and must therefore be known before any data
/// is compressed. Due to technical limitations of the file format, the
/// maximum size of uncompressed file that will be accepted is 2147483647
/// bytes.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_compressor
/// instance being called
/// </param>
/// <param name="input">
/// the name of the file to compressed. This is passed
/// passed directly to mspack_system::open()
/// </param>
/// <param name="output">
/// the name of the file to write compressed data to.
/// This is passed directly to mspack_system::open().
/// </param>
/// <param name="length">
/// the length of the uncompressed file, or -1 to indicate
/// that this should be determined automatically by using
/// mspack_system::seek() on the input file.
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
/// <see cref="SetParam"/>
public Func<Compressor, string, string, long, Error> Compress;
/// <summary>
/// Sets an SZDD compression engine parameter.
///
/// The following parameters are defined:
/// - #MSSZDDC_PARAM_CHARACTER: the "missing character", the last character
/// in the uncompressed file's filename, which is traditionally replaced
/// with an underscore to show the file is compressed. Traditionally,
/// this can only be a character that is a valid part of an MS-DOS,
/// filename, but libmspack permits any character between 0x00 and 0xFF
/// to be stored. 0x00 is the default, and it represents "no character
/// stored".
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_compressor
/// instance being called
/// </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>
/// <see cref="Compress"/>
public Func<Compressor, Parameters, int, Error> SetParam;
/// <summary>
/// Returns the error code set by the most recently called method.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the msszdd_compressor
/// instance being called
/// </param>
/// <returns>the most recent error code</returns>
/// <see cref="Compress"/>
public Func<Compressor, Error> LastError;
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,128 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
namespace LibMSPackSharp.SZDD
{
/// <summary>
/// A decompressor for SZDD compressed files.
///
/// All fields are READ ONLY.
/// </summary>
/// <see cref="Library.CreateSZDDDecompressor(SystemImpl)"/>
/// <see cref="Library.DestroySZDDDecompressor(Decompressor)"/>
public class Decompressor
{
/// <summary>
/// Opens a SZDD file and reads the header.
///
/// If the file opened is a valid SZDD file, all headers will be read and
/// a msszddd_header structure will be returned.
///
/// In the case of an error occuring, NULL is returned and the error code
/// is available from last_error().
///
/// 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
/// 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;
/// <summary>
/// Closes a previously opened SZDD file.
///
/// This closes a SZDD file and frees the msszddd_header associated with
/// it.
///
/// 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;
/// <summary>
/// Extracts the compressed data from a SZDD file.
///
/// 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="filename">
/// 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;
/// <summary>
/// Decompresses an SZDD file to an output file in one step.
///
/// This opens an SZDD file as input, reads the header, then decompresses
/// the compressed data immediately to an output file, finally closing
/// both the input and output file. It is more convenient to use than
/// 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
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// 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;
/// <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.
/// </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;
}
}

View File

@@ -0,0 +1,18 @@
/* 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

@@ -0,0 +1,32 @@
/* 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
*/
namespace LibMSPackSharp.SZDD
{
public enum Format
{
/// <summary>
/// a regular SZDD file
/// </summary>
MSSZDD_FMT_NORMAL = 0,
/// <summary>
/// a special QBasic SZDD file
/// </summary>
MSSZDD_FMT_QBASIC = 1,
}
public enum Parameters
{
/// <summary>
/// The missing character
/// </summary>
MSSZDDC_PARAM_MISSINGCHAR = 0,
}
}

View File

@@ -0,0 +1,45 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
namespace LibMSPackSharp.SZDD
{
/// <summary>
/// A structure which represents an SZDD compressed file.
///
/// All fields are READ ONLY.
/// </summary>
public class Header
{
/// <summary>
/// The file format
/// </summary>
public Format Format { get; set; }
/// <summary>
/// The amount of data in the SZDD file once uncompressed.
/// </summary>
public long Length { get; set; }
/// <summary>
/// The last character in the filename, traditionally replaced with an
/// underscore to show the file is compressed. The null character is used
/// to show that this character has not been stored (e.g. because the
/// filename is not known). Generally, only characters that may appear in
/// an MS-DOS filename (except ".") are valid.
/// </summary>
public char MissingChar { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
/* 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 HeaderImpl : Header
{
public object FileHandle { get; set; }
}
}

View File

@@ -0,0 +1,226 @@
/* 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.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 = (DecompressorImpl)d;
if (self == null)
return null;
SystemImpl sys = self.System;
object fh = sys.Open(sys, 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);
sys.Free(hdr);
hdr = null;
}
return hdr;
}
#endregion
#region SZDDD_CLOSE
/// <summary>
/// Closes an SZDD file
/// </summary>
public static void Close(Decompressor d, Header hdr)
{
DecompressorImpl self = (DecompressorImpl)d;
HeaderImpl hdr_p = (HeaderImpl)hdr;
if (self == null || self.System == null)
return;
// Close the file handle associated
self.System.Close(hdr_p.FileHandle);
// Free the memory associated
self.System.Free(hdr);
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, object 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 = (DecompressorImpl)d;
if (self == null)
return Error.MSPACK_ERR_ARGS;
if (hdr == null)
return self.Error = Error.MSPACK_ERR_ARGS;
SystemImpl sys = self.System;
object fh = ((HeaderImpl)hdr).FileHandle;
// 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
object outfh;
if ((outfh = sys.Open(sys, 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 = (DecompressorImpl)d;
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 = (DecompressorImpl)d;
return (self != null) ? self.Error : Error.MSPACK_ERR_ARGS;
}
#endregion
}
}

View File

@@ -0,0 +1,375 @@
/* libmspack -- a library for working with Microsoft compression formats.
* (C) 2003-2019 Stuart Caie <kyzer@cabextract.org.uk>
*
* 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
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
using System.IO;
namespace LibMSPackSharp
{
/// <summary>
/// A structure which abstracts file I/O and memory management.
///
/// The library always uses the SystemImpl structure for interaction
/// with the file system and to allocate, free and copy all memory.It also
/// uses it to send literal messages to the library user.
///
/// When the library is compiled normally, passing null to a compressor or
/// decompressor constructor will result in a default SystemImpl being
/// used, where all methods are implemented with the standard C library.
/// However, all constructors support being given a custom created
/// SystemImpl structure, with the library user's own methods. This
/// allows for more abstract interaction, such as reading and writing files
/// directly to memory, or from a network socket or pipe.
///
/// Implementors of an SystemImpl structure should read all
/// documentation entries for every structure member, and write methods
/// which conform to those standards.
/// </summary>
public class SystemImpl
{
/// <summary>
/// Opens a file for reading, writing, appending or updating.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the SystemImpl
/// structure whose Open() method is being called. If
/// this pointer is required by Close(), Read(), Write(),
/// Seek() or Tell(), it should be stored in the result
/// structure at this time.
/// </param>
/// <param name="filename">
/// the file to be opened. It is passed directly from the
/// library caller without being modified, so it is up to
/// the caller what this parameter actually represents.
/// </param>
/// <param name="mode">One of the <see cref="OpenMode"/> values</param>
/// <returns>
/// a pointer to a mspack_file structure. This structure officially
/// contains no members, its true contents are up to the
/// SystemImpl implementor. It should contain whatever is needed
/// for other SystemImpl methods to operate. Returning the null
/// pointer indicates an error condition.
/// </returns>
public Func<SystemImpl, string, OpenMode, object> Open;
/// <summary>
/// Closes a previously opened file. If any memory was allocated for this
/// particular file handle, it should be freed at this time.
/// </summary>
/// <param name="file">the file to close</param>
/// <see cref="Open"/>
public Action<object> Close;
/// <summary>
/// Reads a given number of bytes from an open file.
/// </summary>
/// <param name="file">the file to read from</param>
/// <param name="buffer">the location where the read bytes should be stored</param>
/// <param name="bytes">the number of bytes to read from the file.</param>
/// <returns>
/// the number of bytes successfully read (this can be less than
/// the number requested), zero to mark the end of file, or less
/// than zero to indicate an error. The library does not "retry"
/// reads and assumes short reads are due to EOF, so you should
/// avoid returning short reads because of transient errors.
/// </returns>
/// <see cref="Open"/>
/// <see cref="Write"/>
public Func<object, byte[], int, int, int> Read;
/// <summary>
/// Writes a given number of bytes to an open file.
/// </summary>
/// <param name="file">the file to write to</param>
/// <param name="buffer">the location where the written bytes should be read from</param>
/// <param name="bytes">the number of bytes to write to the file.</param>
/// <returns>
/// the number of bytes successfully written, this can be less
/// than the number requested. Zero or less can indicate an error
/// where no bytes at all could be written. All cases where less
/// bytes were written than requested are considered by the library
/// to be an error.
/// </returns>
/// <see cref="Open"/>
/// <see cref="Read"/>
public Func<object, byte[], int, int, int> Write;
/// <summary>
/// Seeks to a specific file offset within an open file.
///
/// Sometimes the library needs to know the length of a file. It does
/// this by seeking to the end of the file with seek(file, 0,
/// MSPACK_SYS_SEEK_END), then calling Tell(). Implementations may want
/// to make a special case for this.
///
/// Due to the potentially varying 32/64 bit datatype int on some
/// architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before
/// using the library. If not, the error caused by the library passing an
/// inappropriate stackframe to Seek() is subtle and hard to trace.
/// </summary>
/// <param name="file">the file to be seeked</param>
/// <param name="offset">an offset to seek, measured in bytes</param>
/// <param name="mode">One of the <see cref="SeekMode"/> values</param>
/// <returns>zero for success, non-zero for an error</returns>
/// <see cref="Open"/>
/// <see cref="Tell"/>
public Func<object, long, SeekMode, bool> Seek;
/// <summary>
/// Returns the current file position (in bytes) of the given file.
/// </summary>
/// <param name="file">the file whose file position is wanted</param>
/// <returns>the current file position of the file</returns>
/// <see cref="Open"/>
/// <see cref="Seek"/>
public Func<object, long> Tell;
/// <summary>
/// Used to send messages from the library to the user.
///
/// Occasionally, the library generates warnings or other messages in
/// plain english to inform the human user. These are informational only
/// and can be ignored if not wanted.
/// </summary>
/// <param name="file">
/// may be a file handle returned from Open() if this message
/// pertains to a specific open file, or null if not related to
/// a specific file.
/// </param>
/// <param name="format">a printf() style format string. It does NOT include a trailing newline.</param>
/// <see cref="Open"/>
public Action<object, string> Message;
/// <summary>
/// Allocates memory.
/// </summary>
/// <param name="self">
/// a self-referential pointer to the SystemImpl
/// structure whose Alloc() method is being called.
/// </param>
/// <param name="bytes">the number of bytes to allocate</param>
/// <returns>
/// a pointer to the requested number of bytes, or null if
/// not enough memory is available
/// </returns>
/// <see cref="Free"/>
public Func<SystemImpl, int, byte[]> Alloc;
/// <summary>
/// Frees memory.
/// </summary>
/// <param name="ptr">the memory to be freed. null is accepted and ignored.</param>
/// <see cref="Alloc"/>
public Action<object> Free;
/// <summary>
/// Copies from one region of memory to another.
///
/// The regions of memory are guaranteed not to overlap, are usually less
/// than 256 bytes, and may not be aligned. Please note that the source
/// parameter comes before the destination parameter, unlike the standard
/// C function memcpy().
/// </summary>
/// <param name="src">the region of memory to copy from</param>
/// <param name="dest">the region of memory to copy to</param>
/// <param name="bytes">the size of the memory region, in bytes</param>
public Action<byte[], int, byte[], int, int> Copy;
/// <summary>
/// A null pointer to mark the end of SystemImpl. It must equal null.
///
/// Should the SystemImpl structure extend in the future, this null
/// will be seen, rather than have an invalid method pointer called.
/// </summary>
public readonly object NullPtr = null;
#region Helpers
/// <summary>
/// Returns the length of a file opened for reading
/// </summary>
public static Error GetFileLength(SystemImpl system, object file, out long length)
{
length = 0;
long current;
if (system == null || file == null)
return Error.MSPACK_ERR_OPEN;
// Get current offset
current = system.Tell(file);
// Seek to end of file
if (!system.Seek(file, 0, SeekMode.MSPACK_SYS_SEEK_END))
return Error.MSPACK_ERR_SEEK;
// Get offset of end of file
length = system.Tell(file);
// Seek back to original offset
if (!system.Seek(file, current, SeekMode.MSPACK_SYS_SEEK_START))
return Error.MSPACK_ERR_SEEK;
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Validates a system structure
/// </summary>
public static bool ValidSystem(SystemImpl sys)
{
return (sys != null) && (sys.Open != null) && (sys.Close != null) &&
(sys.Read != null) && (sys.Write != null) && (sys.Seek != null) &&
(sys.Tell != null) && (sys.Message != null) && (sys.Alloc != null) &&
(sys.Free != null) && (sys.Copy != null) && (sys.NullPtr == null);
}
#endregion
#region Default Implementation
public static SystemImpl DefaultSystem => new SystemImpl()
{
Open = DefaultOpen,
Close = DefaultClose,
Read = DefaultRead,
Write = DefaultWrite,
Seek = DefaultSeek,
Tell = DefaultTell,
Message = DefaultMessage,
Alloc = DefaultAlloc,
Free = DefaultFree,
Copy = DefaultCopy,
};
private static object DefaultOpen(SystemImpl self, string filename, OpenMode mode)
{
DefaultFileImpl fileHandle = new DefaultFileImpl();
switch (mode)
{
case OpenMode.MSPACK_SYS_OPEN_READ:
fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.Read);
break;
case OpenMode.MSPACK_SYS_OPEN_WRITE:
fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.Write);
break;
case OpenMode.MSPACK_SYS_OPEN_UPDATE:
fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.ReadWrite);
break;
case OpenMode.MSPACK_SYS_OPEN_APPEND:
fileHandle.FileHandle = File.Open(filename, FileMode.Append);
break;
default:
return null;
}
return fileHandle;
}
private static void DefaultClose(object file)
{
DefaultFileImpl self = (DefaultFileImpl)file;
if (self != null)
self.FileHandle.Close();
}
private static int DefaultRead(object file, byte[] buffer, int pointer, int bytes)
{
DefaultFileImpl self = (DefaultFileImpl)file;
if (self != null && buffer != null && bytes >= 0)
{
try { return self.FileHandle.Read(buffer, pointer, bytes); }
catch { }
}
return -1;
}
private static int DefaultWrite(object file, byte[] buffer, int pointer, int bytes)
{
DefaultFileImpl self = (DefaultFileImpl)file;
if (self != null && buffer != null && bytes >= 0)
{
try { self.FileHandle.Write(buffer, pointer, bytes); }
catch { return -1; }
return bytes;
}
return -1;
}
private static bool DefaultSeek(object file, long offset, SeekMode mode)
{
DefaultFileImpl self = (DefaultFileImpl)file;
if (self != null)
{
switch (mode)
{
case SeekMode.MSPACK_SYS_SEEK_START:
try { self.FileHandle.Seek(offset, SeekOrigin.Begin); return true; }
catch { return false; }
case SeekMode.MSPACK_SYS_SEEK_CUR:
try { self.FileHandle.Seek(offset, SeekOrigin.Current); return true; }
catch { return false; }
case SeekMode.MSPACK_SYS_SEEK_END:
try { self.FileHandle.Seek(offset, SeekOrigin.End); return true; }
catch { return false; }
default:
return false;
}
}
return false;
}
private static long DefaultTell(object file)
{
DefaultFileImpl self = (DefaultFileImpl)file;
return (self != null ? (int)self.FileHandle.Position : 0);
}
private static void DefaultMessage(object file, string format)
{
if (file != null)
Console.Error.Write($"{((DefaultFileImpl)file).Name}: ");
Console.Error.Write($"{format}\n");
}
private static byte[] DefaultAlloc(SystemImpl self, int bytes)
{
return new byte[bytes];
}
private static void DefaultFree(object buffer)
{
buffer = null;
}
private static void DefaultCopy(byte[] src, int srcPtr, byte[] dest, int destPtr, int bytes)
{
Array.Copy(src, srcPtr, dest, destPtr, bytes);
}
#endregion
}
}