Migrate to external libmspack

This commit is contained in:
Matt Nadareski
2022-05-31 16:34:40 -07:00
parent 66c7afe20b
commit 439f71ef5c
85 changed files with 11 additions and 12977 deletions

3
.gitmodules vendored
View File

@@ -7,3 +7,6 @@
[submodule "HLLibSharp"]
path = HLLibSharp
url = https://github.com/mnadareski/HLLibSharp
[submodule "LibMSPackSharp"]
path = LibMSPackSharp
url = https://github.com/mnadareski/LibMSPackSharp.git

View File

@@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Dtf.Compression.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HLLibSharp", "HLLibSharp\HLLibSharp\HLLibSharp.csproj", "{14E9764A-A8BF-44C0-A1A8-2C95EA307040}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibMSPackSharp", "LibMSPackSharp\LibMSPackSharp\LibMSPackSharp.csproj", "{BD8144B8-4857-47E6-9717-E9F2DB374A89}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -52,6 +54,10 @@ Global
{14E9764A-A8BF-44C0-A1A8-2C95EA307040}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14E9764A-A8BF-44C0-A1A8-2C95EA307040}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14E9764A-A8BF-44C0-A1A8-2C95EA307040}.Release|Any CPU.Build.0 = Release|Any CPU
{BD8144B8-4857-47E6-9717-E9F2DB374A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD8144B8-4857-47E6-9717-E9F2DB374A89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD8144B8-4857-47E6-9717-E9F2DB374A89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD8144B8-4857-47E6-9717-E9F2DB374A89}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -37,9 +37,6 @@
<DefaultItemExcludes>
$(DefaultItemExcludes);
**\AssemblyInfo.cs;
External\hllib\HLExtract\**\*;
External\hllib\HLExtract.Net\Program.cs;
External\hllib\HLLib\**\*;
External\stormlibsharp\lib\**;
External\stormlibsharp\TestConsole\**
</DefaultItemExcludes>
@@ -59,15 +56,10 @@
<ItemGroup>
<ProjectReference Include="..\HLLibSharp\HLLibSharp\HLLibSharp.csproj" />
<ProjectReference Include="..\WixToolset\src\WixToolset.Dtf.Compression.Cab\WixToolset.Dtf.Compression.Cab.csproj" />
<ProjectReference Include="..\WixToolset\src\WixToolset.Dtf.Compression\WixToolset.Dtf.Compression.csproj" />
<ProjectReference Include="..\LibMSPackSharp\LibMSPackSharp\LibMSPackSharp.csproj" />
<ProjectReference Include="..\WixToolset\src\WixToolset.Dtf.WindowsInstaller\WixToolset.Dtf.WindowsInstaller.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="External\cabextract\" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

@@ -1,145 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains
* "folders", which are compressed spans of data. Each cabinet has
* "files", whose metadata is in the cabinet header, but whose actual data
* is stored compressed in one of the "folders". Cabinets can span more
* than one physical file on disk, in which case they are a "cabinet set",
* and usually the last folder of each cabinet extends into the next
* cabinet.
*
* For a complete description of the format, see the MSDN site:
* http://msdn.microsoft.com/en-us/library/bb267310.aspx
*/
/* Notes on compliance with cabinet specification:
*
* One of the main changes between cabextract 0.6 and libmspack's cab
* decompressor is the move from block-oriented decompression to
* stream-oriented decompression.
*
* cabextract would read one data block from disk, decompress it with the
* appropriate method, then write the decompressed data. The CAB
* specification is specifically designed to work like this, as it ensures
* compression matches do not span the maximum decompressed block size
* limit of 32kb.
*
* However, the compression algorithms used are stream oriented, with
* specific hacks added to them to enforce the "individual 32kb blocks"
* rule in CABs. In other file formats, they do not have this limitation.
*
* In order to make more generalised decompressors, libmspack's CAB
* decompressor has moved from being block-oriented to more stream
* oriented. This also makes decompression slightly faster.
*
* However, this leads to incompliance with the CAB specification. The
* CAB controller can no longer ensure each block of input given to the
* decompressors is matched with their output. The "decompressed size" of
* each individual block is thrown away.
*
* Each CAB block is supposed to be seen as individually compressed. This
* means each consecutive data block can have completely different
* "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in
* reality, all data blocks in a folder decompress to exactly 32768 bytes,
* excepting the final block.
*
* Given this situation, the decompression algorithms are designed to
* realign their input bitstreams on 32768 output-byte boundaries, and
* various other special cases have been made. libmspack will not
* correctly decompress LZX or Quantum compressed folders where the blocks
* do not follow this "32768 bytes until last block" pattern. It could be
* implemented if needed, but hopefully this is not necessary -- it has
* not been seen in over 3Gb of CAB archives.
*/
namespace LibMSPackSharp.CAB
{
/// <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(string)"/>
/// <see cref="Decompressor.Close(Cabinet)"/>
/// <see cref="Decompressor.Search(string)"/>
public class Cabinet : BaseHeader
{
#region Internal
/// <summary>
/// Cabinet header information
/// </summary>
internal _CabinetHeader Header { get; set; }
#endregion
/// <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 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; }
}
}

View File

@@ -1,24 +0,0 @@
/* 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
{
// TODO
public class Compressor
{
public SystemImpl System { get; set; }
}
}

View File

@@ -1,95 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.CAB
{
public class DecompressState : BaseDecompressState
{
/// <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>
/// Type of compression used by folder
/// </summary>
public CompressionType CompressionType { get; set; }
/// <summary>
/// Decompressor state
/// </summary>
public BaseDecompressState DecompressorState { get; set; }
/// <summary>
/// Cabinet where input data comes from
/// </summary>
public Cabinet InputCabinet { get; set; }
/// <summary>
/// Input data consumed
/// </summary>
public int InputPointer { get; set; }
/// <summary>
/// Input data end
/// </summary>
public int InputEnd { get; set; }
/// <summary>
/// One input block of data
/// </summary>
public byte[] Input { get; set; } = new byte[Constants.CAB_INPUTBUF];
/// <summary>
/// Decompressor code
/// </summary>
public Error Decompress(object o, long bytes)
{
switch (CompressionType & CompressionType.COMPTYPE_MASK)
{
case CompressionType.COMPTYPE_NONE:
return (o as None)?.Decompress(bytes) ?? Error.MSPACK_ERR_ARGS;
case CompressionType.COMPTYPE_MSZIP:
return (o as MSZIP)?.Decompress(bytes) ?? Error.MSPACK_ERR_ARGS;
case CompressionType.COMPTYPE_QUANTUM:
return (o as QTM)?.Decompress(bytes) ?? Error.MSPACK_ERR_ARGS;
case CompressionType.COMPTYPE_LZX:
return (o as LZX)?.Decompress(bytes) ?? Error.MSPACK_ERR_ARGS;
default:
return Error = Error.MSPACK_ERR_DATAFORMAT;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
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

@@ -1,60 +0,0 @@
/* 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
{
#region Internal
/// <summary>
/// Folder header information
/// </summary>
internal _FolderHeader Header { get; set; }
#endregion
/// <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>
/// 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; }
}
}

View File

@@ -1,29 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
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

@@ -1,54 +0,0 @@
/* 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
{
#region Internal
/// <summary>
/// File header information
/// </summary>
internal _FileHeader Header { get; set; }
#endregion
/// <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="_FileHeader.Attributes"/>
public string Filename { get; set; }
/// <summary>
/// A pointer to the folder that contains this file.
/// </summary>
public Folder Folder { get; set; }
}
}

View File

@@ -1,204 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CAB
{
internal class _CabinetHeader
{
#region Normal Header
/// <summary>
/// "MSCF"
/// </summary>
/// <remarks>0x00</remarks>
public uint Signature { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x04</remarks>
public uint Reserved0 { get; private set; }
/// <summary>
/// The length of the cabinet file in bytes.
/// </summary>
/// <remarks>0x08</remarks>
public uint CabinetSize { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0C</remarks>
public uint Reserved1 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x10</remarks>
public uint FileOffset { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x14</remarks>
public uint Reserved2 { get; private set; }
/// <summary>
/// Minor cabinet version
/// </summary>
/// <remarks>0x18</remarks>
public byte MinorVersion { get; private set; }
/// <summary>
/// Major cabinet version
/// </summary>
/// <remarks>0x19</remarks>
public byte MajorVersion { get; private set; }
/// <summary>
/// Number of internal folders
/// </summary>
/// <remarks>0x1A</remarks>
public ushort NumFolders { get; private set; }
/// <summary>
/// Number of internal files
/// </summary>
/// <remarks>0x1C</remarks>
public ushort NumFiles { get; private set; }
/// <summary>
/// Header flags.
/// </summary>
/// <remarks>0x1E</remarks>
/// <see cref="Cabinet.PreviousName"/>
/// <see cref="Cabinet.PreviousInfo"/>
/// <see cref="Cabinet.NextName"/>
/// <see cref="Cabinet.NextInfo"/>
/// <see cref="Cabinet.HeaderResv"/>
public HeaderFlags Flags { get; private set; }
/// <summary>
/// The set ID of the cabinet. All cabinets in the same set should have
/// the same set ID.
/// </summary>
/// <remarks>0x20</remarks>
public ushort SetID { get; private 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>
/// <remarks>0x22</remarks>
public ushort CabinetIndex { get; private set; }
/// <summary>
/// Total size of the normal Cabinet header in bytes
/// </summary>
public const int Size = 0x24;
#endregion
#region Extended Header
/// <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>
/// <remarks>0x24</remarks>
/// <see cref="Flags"/>
public ushort HeaderReserved { get; private set; }
/// <summary>
/// The number of bytes reserved in the folder area of the cabinet
/// </summary>
/// <remarks>0x26</remarks>
public byte FolderReserved { get; private set; }
/// <summary>
/// The number of bytes reserved in the data area of the cabinet
/// </summary>
/// <remarks>0x27</remarks>
public byte DataReserved { get; private set; }
/// <summary>
/// Total size of the extended Cabinet header in bytes
/// </summary>
public const int ExtendedSize = 0x04;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _CabinetHeader() { }
/// <summary>
/// Create a _CabinetHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _CabinetHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _CabinetHeader();
header.Signature = BitConverter.ToUInt32(buffer, 0x00);
if (header.Signature != 0x4643534D)
return Error.MSPACK_ERR_SIGNATURE;
header.Reserved0 = BitConverter.ToUInt32(buffer, 0x04);
header.CabinetSize = BitConverter.ToUInt32(buffer, 0x08);
header.Reserved1 = BitConverter.ToUInt32(buffer, 0x0C);
header.FileOffset = BitConverter.ToUInt32(buffer, 0x10);
header.Reserved2 = BitConverter.ToUInt32(buffer, 0x14);
// Expect version 1.3, but don't validate
header.MinorVersion = buffer[0x18];
header.MajorVersion = buffer[0x19];
header.NumFolders = BitConverter.ToUInt16(buffer, 0x1A);
if (header.NumFolders == 0)
return Error.MSPACK_ERR_DATAFORMAT;
header.NumFiles = BitConverter.ToUInt16(buffer, 0x1C);
if (header.NumFiles == 0)
return Error.MSPACK_ERR_DATAFORMAT;
header.Flags = (HeaderFlags)BitConverter.ToUInt16(buffer, 0x1E);
header.SetID = BitConverter.ToUInt16(buffer, 0x20);
header.CabinetIndex = BitConverter.ToUInt16(buffer, 0x20);
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Populate the extended header fields from buffer
/// </summary>
/// <param name="buffer"></param>
public void PopulateExtendedHeader(byte[] buffer)
{
if (buffer.Length < ExtendedSize)
return;
// Expect less than 60,000, but don't validate
HeaderReserved = BitConverter.ToUInt16(buffer, 0x00);
FolderReserved = buffer[0x02];
DataReserved = buffer[0x03];
}
}
}

View File

@@ -1,66 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CAB
{
internal class _DataBlockHeader
{
#region Fields
/// <summary>
/// Block CRC32 checksum
/// </summary>
/// <remarks>0x00</remarks>
public uint CheckSum { get; private set; }
/// <summary>
/// Compressed size of the data block
/// </summary>
/// <remarks>0x04</remarks>
public ushort CompressedSize { get; private set; }
/// <summary>
/// Uncompressed size of the data block
/// </summary>
/// <remarks>0x06</remarks>
public ushort UncompressedSize { get; private set; }
/// <summary>
/// Size of the Data Block header in bytes
/// </summary>
public const int Size = 0x08;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _DataBlockHeader() { }
/// <summary>
/// Create a _DataBlockHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _DataBlockHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _DataBlockHeader();
header.CheckSum = BitConverter.ToUInt32(buffer, 0x00);
header.CompressedSize = BitConverter.ToUInt16(buffer, 0x04);
header.UncompressedSize = BitConverter.ToUInt16(buffer, 0x06);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,122 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CAB
{
internal class _FileHeader
{
#region Fields
/// <summary>
/// The uncompressed length of the file, in bytes.
/// </summary>
/// <remarks>0x00</remarks>
public uint UncompressedSize { get; private set; }
/// <summary>
/// The uncompressed offset of this file in its folder.
/// </summary>
/// <remarks>0x04</remarks>
public uint FolderOffset { get; private set; }
/// <summary>
/// Internal index of the folder
/// </summary>
/// <remarks>0x08</remarks>
public FileFlags FolderIndex { get; internal set; }
/// <summary>
/// File's last modified date, day field.
/// </summary>
/// <remarks>0x0A</remarks>
public byte LastModifiedDateDay { get; set; }
/// <summary>
/// File's last modified date, month field.
/// </summary>
/// <remarks>0x0A</remarks>
public byte LastModifiedDateMonth { get; set; }
/// <summary>
/// File's last modified date, year field.
/// </summary>
/// <remarks>0x0A</remarks>
public int LastModifiedDateYear { get; set; }
/// <summary>
/// File's last modified time, hour field.
/// </summary>
/// <remarks>0x0C</remarks>
public byte LastModifiedTimeHour { get; set; }
/// <summary>
/// File's last modified time, minute field.
/// </summary>
/// <remarks>0x0C</remarks>
public byte LastModifiedTimeMinute { get; set; }
/// <summary>
/// File's last modified time, second field.
/// </summary>
/// <remarks>0x0C</remarks>
public byte LastModifiedTimeSecond { get; set; }
/// <summary>
/// File attributes.
/// </summary>
/// <remarks>0x0E</remarks>
public FileAttributes Attributes { get; set; }
/// <summary>
/// Size of the File header in bytes
/// </summary>
public const int Size = 0x10;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _FileHeader() { }
/// <summary>
/// Create a _FileHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _FileHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _FileHeader();
header.UncompressedSize = BitConverter.ToUInt32(buffer, 0x00);
header.FolderOffset = BitConverter.ToUInt32(buffer, 0x04);
header.FolderIndex = (FileFlags)BitConverter.ToUInt16(buffer, 0x08);
// Get date
ushort x = BitConverter.ToUInt16(buffer, 0x0A);
header.LastModifiedDateDay = (byte)(x & 0x1F);
header.LastModifiedDateMonth = (byte)((x >> 5) & 0xF);
header.LastModifiedDateYear = (x >> 9) + 1980;
// Get time
x = BitConverter.ToUInt16(buffer, 0x0C);
header.LastModifiedTimeHour = (byte)(x >> 11);
header.LastModifiedTimeMinute = (byte)((x >> 5) & 0x3F);
header.LastModifiedTimeSecond = (byte)((x << 1) & 0x3E);
header.Attributes = (FileAttributes)BitConverter.ToUInt16(buffer, 0x0E);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,72 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CAB
{
internal class _FolderHeader
{
#region Fields
/// <summary>
/// Cabinet offset of first datablock
/// </summary>
/// <remarks>0x00</remarks>
public uint DataOffset { get; internal 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>
/// <remarks>0x04</remarks>
public ushort NumBlocks { get; internal 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>
/// <remarks>0x06</remarks>
public CompressionType CompType { get; private set; }
/// <summary>
/// Size of the Folder header in bytes
/// </summary>
public const int Size = 0x08;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _FolderHeader() { }
/// <summary>
/// Create a _FolderHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _FolderHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _FolderHeader();
header.DataOffset = BitConverter.ToUInt32(buffer, 0x00);
header.NumBlocks = BitConverter.ToUInt16(buffer, 0x04);
header.CompType = (CompressionType)BitConverter.ToUInt16(buffer, 0x06);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,85 +0,0 @@
/* 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 CHM : BaseHeader
{
#region Internal
/// <summary>
/// CHM header information
/// </summary>
internal _CHMHeader Header { get; set; }
/// <summary>
/// Header section table information
/// </summary>
internal _HeaderSectionTable HeaderSectionTable { get; set; }
/// <summary>
/// Header section 0 information
/// </summary>
internal _HeaderSection0 HeaderSection0 { get; set; }
/// <summary>
/// Header section 1 information
/// </summary>
internal _HeaderSection1 HeaderSection1 { get; set; }
#endregion
/// <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>
/// 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>
/// 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; }
}
}

View File

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

@@ -1,149 +0,0 @@
/* 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
{
#region Fields
public SystemImpl System { get; set; }
public string TempFile { get; set; }
public bool UseTempFile { get; set; }
public Error Error { get; set; }
#endregion
#region Public Functionality
/// <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="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 Error Generate(CompressFile[] fileList, string outputFile) => throw new NotImplementedException();
/// <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="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 Error UseTemporaryFile(bool useTempFile, string tempFile) => throw new NotImplementedException();
/// <summary>
/// Sets a CHM compression engine parameter.
/// </summary>
/// <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 Error SetParam(Parameters param, int value) => throw new NotImplementedException();
#endregion
}
}

View File

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

@@ -1,36 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using LibMSPackSharp.Compression;
namespace LibMSPackSharp.CHM
{
public class DecompressState : BaseDecompressState
{
/// <summary>
/// CHM file being decompressed
/// </summary>
public CHM 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 LZX State { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,83 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
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

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

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

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

@@ -1,118 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _CHMHeader
{
#region Fields
/// <summary>
/// "ITSF"
/// </summary>
/// <remarks>0x0000</remarks>
public uint Signature { get; private set; }
/// <summary>
/// The version of the CHM file format used in this file.
/// </summary>
/// <remarks>0x0004</remarks>
public uint Version { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0008</remarks>
public uint HeaderLen { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x000C</remarks>
public uint Unknown1 { get; private set; }
/// <summary>
/// The "timestamp" of the CHM helpfile.
/// </summary>
/// <remarks>
/// 0x0010
///
/// 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.
/// </remarks>
public uint Timestamp { get; private 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>
/// <remarks>0x0014</remarks>
public uint LanguageID { get; private set; }
/// <summary>
/// Header GUID 1
/// </summary>
/// <remarks>0x0018</remarks>
public Guid GUID1 { get; private set; }
/// <summary>
/// Header GUID 2
/// </summary>
/// <remarks>0x0028</remarks>
public Guid GUID2 { get; private set; }
/// <summary>
/// Total size of the CHM header in bytes
/// </summary>
public const int Size = 0x0038;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _CHMHeader() { }
/// <summary>
/// Create a _CHMHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _CHMHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _CHMHeader();
header.Signature = BitConverter.ToUInt32(buffer, 0x0000);
if (header.Signature != 0x46535449)
return Error.MSPACK_ERR_SIGNATURE;
// Expect version less than or equal to 3, but don't validate
header.Version = BitConverter.ToUInt32(buffer, 0x0004);
header.HeaderLen = BitConverter.ToUInt32(buffer, 0x0008);
header.Unknown1 = BitConverter.ToUInt32(buffer, 0x000C);
header.Timestamp = BitConverter.ToUInt32(buffer, 0x0010);
header.LanguageID = BitConverter.ToUInt32(buffer, 0x0014);
header.GUID1 = new Guid(new ReadOnlySpan<byte>(buffer, 0x0018, 0x10).ToArray());
if (header.GUID1 != Guid.Parse("7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC"))
return Error.MSPACK_ERR_SIGNATURE;
header.GUID2 = new Guid(new ReadOnlySpan<byte>(buffer, 0x0028, 0x10).ToArray());
if (header.GUID2 != Guid.Parse("7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC"))
return Error.MSPACK_ERR_SIGNATURE;
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,85 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _HeaderSection0
{
#region Fields
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0000</remarks>
public uint Unknown1 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0004</remarks>
public uint Unknown2 { get; private set; }
/// <summary>
/// The length of the CHM helpfile, in bytes.
/// </summary>
/// <remarks>0x0008</remarks>
public long FileLength { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0010</remarks>
public uint Unknown3 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0014</remarks>
public uint Unknown4 { get; private set; }
/// <summary>
/// Total size of the header section 0 in bytes
/// </summary>
public const int Size = 0x0018;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _HeaderSection0() { }
/// <summary>
/// Create a _HeaderSection0 from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _HeaderSection0 headerSection)
{
headerSection = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
headerSection = new _HeaderSection0();
headerSection.Unknown1 = BitConverter.ToUInt32(buffer, 0x0000);
headerSection.Unknown2 = BitConverter.ToUInt32(buffer, 0x0004);
headerSection.FileLength = BitConverter.ToInt64(buffer, 0x0008);
// TODO: Is this supposed to be <= 0?
if (headerSection.FileLength != 0)
return Error.MSPACK_ERR_DATAFORMAT;
headerSection.Unknown3 = BitConverter.ToUInt32(buffer, 0x0010);
headerSection.Unknown4 = BitConverter.ToUInt32(buffer, 0x0014);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,184 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _HeaderSection1
{
#region Fields
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0000</remarks>
public uint Signature { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0004</remarks>
public uint Version { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0008</remarks>
public uint HeaderLen { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x000C</remarks>
public uint Unknown1 { get; private set; }
/// <summary>
/// The size of each PMGL/PMGI chunk, in bytes.
/// </summary>
/// <remarks>0x0010</remarks>
public uint ChunkSize { get; private set; }
/// <summary>
/// The "density" of the quick-reference section in PMGL/PMGI chunks.
/// </summary>
/// <remarks>0x0014</remarks>
public uint Density { get; private set; }
/// <summary>
/// The depth of the index tree.
/// </summary>
/// <remarks>
/// 0x0018
///
/// - 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...
/// </remarks>
public uint Depth { get; private set; }
/// <summary>
/// The number of the root PMGI chunk.
/// </summary>
/// <remarks>
/// 0x001C
///
/// If there is no index in the CHM helpfile, this will be 0xFFFFFFFF.
/// </remarks>
public uint IndexRoot { get; private set; }
/// <summary>
/// The number of the first PMGL chunk. Usually zero.
/// Available only in CHM decoder version 2 and above.
/// </summary>
/// <remarks>0x0020</remarks>
public uint FirstPMGL { get; private set; }
/// <summary>
/// The number of the last PMGL chunk. Usually num_chunks-1.
/// Available only in CHM decoder version 2 and above.
/// </summary>
/// <remarks>0x0024</remarks>
public uint LastPMGL { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0028</remarks>
public uint Unknown2 { get; private set; }
/// <summary>
/// The number of PMGL/PMGI directory chunks in this CHM helpfile.
/// </summary>
/// <remarks>0x002C</remarks>
public uint NumChunks { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0030</remarks>
public uint LanguageID { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0034</remarks>
public Guid GUID { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0044</remarks>
public uint Unknown3 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0048</remarks>
public uint Unknown4 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x004C</remarks>
public uint Unknown5 { get; private set; }
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0050</remarks>
public uint Unknown6 { get; private set; }
/// <summary>
/// Total size of the header section 1 in bytes
/// </summary>
public const int Size = 0x0054;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _HeaderSection1() { }
/// <summary>
/// Create a _HeaderSection1 from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _HeaderSection1 headerSection)
{
headerSection = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
headerSection = new _HeaderSection1();
headerSection.Signature = BitConverter.ToUInt32(buffer, 0x0000);
headerSection.Version = BitConverter.ToUInt32(buffer, 0x0004);
headerSection.HeaderLen = BitConverter.ToUInt32(buffer, 0x0008);
headerSection.Unknown1 = BitConverter.ToUInt32(buffer, 0x000C);
headerSection.ChunkSize = BitConverter.ToUInt32(buffer, 0x0010);
headerSection.Density = BitConverter.ToUInt32(buffer, 0x0014);
headerSection.Depth = BitConverter.ToUInt32(buffer, 0x0018);
headerSection.IndexRoot = BitConverter.ToUInt32(buffer, 0x001C);
headerSection.FirstPMGL = BitConverter.ToUInt32(buffer, 0x0020);
headerSection.LastPMGL = BitConverter.ToUInt32(buffer, 0x0024);
headerSection.Unknown2 = BitConverter.ToUInt32(buffer, 0x0028);
headerSection.NumChunks = BitConverter.ToUInt32(buffer, 0x002C);
headerSection.LanguageID = BitConverter.ToUInt32(buffer, 0x0030);
headerSection.GUID = new Guid(new ReadOnlySpan<byte>(buffer, 0x0034, 0x10).ToArray());
headerSection.Unknown3 = BitConverter.ToUInt32(buffer, 0x0044);
headerSection.Unknown4 = BitConverter.ToUInt32(buffer, 0x0048);
headerSection.Unknown5 = BitConverter.ToUInt32(buffer, 0x004C);
headerSection.Unknown6 = BitConverter.ToUInt32(buffer, 0x0050);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,104 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _HeaderSectionTable
{
#region Regular Table
/// <summary>
/// Header section 0 offset
/// </summary>
/// <remarks>0x0000</remarks>
public long OffsetHS0 { get; private set; }
/// <summary>
/// Header section 0 length
/// </summary>
/// <remarks>0x0008</remarks>
public long LengthHS0 { get; private set; }
/// <summary>
/// Header section 1 offset
/// The file offset of the first PMGL/PMGI directory chunk.
/// </summary>
/// <remarks>
/// 0x0010
///
/// This is internally settable because it has to be corrected in some cases
/// </remarks>
public long OffsetHS1 { get; internal set; }
/// <summary>
/// Header section 1 length
/// </summary>
/// <remarks>0x0018</remarks>
public long LengthHS1 { get; private set; }
/// <summary>
/// Total size of the version 1 and 2 header section table in bytes
/// </summary>
public const int Size = 0x0020;
#endregion
#region Version 3 Table
/// <summary>
/// Header section 0 offset correct
/// </summary>
/// <remarks>0x0020</remarks>
public long OffsetCS0 { get; private set; }
/// <summary>
/// Total size of the version 3 header section table in bytes
/// </summary>
public const int V3Size = 0x0028;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _HeaderSectionTable() { }
/// <summary>
/// Create a _HeaderSectionTable from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _HeaderSectionTable headerSectionTable)
{
headerSectionTable = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
headerSectionTable = new _HeaderSectionTable();
headerSectionTable.OffsetHS0 = BitConverter.ToInt64(buffer, 0x0000);
headerSectionTable.LengthHS0 = BitConverter.ToInt64(buffer, 0x0008);
headerSectionTable.OffsetHS1 = BitConverter.ToInt64(buffer, 0x0010);
headerSectionTable.LengthHS1 = BitConverter.ToInt64(buffer, 0x0018);
if (buffer.Length >= V3Size)
{
headerSectionTable.OffsetCS0 = BitConverter.ToInt64(buffer, 0x0020);
}
// OffsetCS0 does not exist in version 1 or 2 CHM files.
// The offset will be corrected later, once HS1 is read.
// TODO: Are these supposed to be == 0?
if (headerSectionTable.OffsetHS0 != 0 || headerSectionTable.OffsetHS1 != 0 || headerSectionTable.OffsetCS0 != 0)
return Error.MSPACK_ERR_DATAFORMAT;
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,155 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.CHM
{
internal class _LZXControlData
{
#region Fields
/// <summary>
/// Length of the control data
/// </summary>
/// <remarks>0x0000</remarks>
public uint Length { get; private set; }
/// <summary>
/// "LZXC"
/// </summary>
/// <remarks>0x0004</remarks>
public uint Signature { get; private set; }
/// <summary>
/// Control data version
/// </summary>
/// <remarks>0x0008</remarks>
public uint Version { get; private set; }
/// <summary>
/// Reset interval
/// </summary>
/// <remarks>0x000C</remarks>
public uint ResetInterval { get; private set; }
/// <summary>
/// Window size
/// </summary>
/// <remarks>0x0010</remarks>
public uint WindowSize { get; private set; }
/// <summary>
/// Cache size
/// </summary>
/// <remarks>0x0014</remarks>
public uint CacheSize { get; private set; }
/// <summary>
/// Cache size
/// </summary>
/// <remarks>0x0018</remarks>
public uint Unknown1 { get; private set; }
/// <summary>
/// Total size of the LZX control data in bytes
/// </summary>
public const int Size = 0x001C;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _LZXControlData() { }
/// <summary>
/// Create a _LZXControlData from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _LZXControlData controlData)
{
controlData = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
controlData = new _LZXControlData();
controlData.Length = BitConverter.ToUInt32(buffer, 0x0000);
controlData.Signature = BitConverter.ToUInt32(buffer, 0x0004);
if (controlData.Signature != 0x43585A4C)
return Error.MSPACK_ERR_SIGNATURE;
controlData.Version = BitConverter.ToUInt32(buffer, 0x0008);
switch (controlData.Version)
{
case 1:
controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C);
controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010);
break;
case 2:
controlData.ResetInterval = BitConverter.ToUInt32(buffer, 0x000C) * LZX_FRAME_SIZE;
controlData.WindowSize = BitConverter.ToUInt32(buffer, 0x0010) * LZX_FRAME_SIZE;
break;
default:
return Error.MSPACK_ERR_DATAFORMAT;
}
controlData.CacheSize = BitConverter.ToUInt32(buffer, 0x0014);
controlData.Unknown1 = BitConverter.ToUInt32(buffer, 0x0018);
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Get the number of bits in the window based on the window size
/// </summary>
/// <param name="windowBits">Window </param>
/// <returns>An error code or MSPACK_ERR_OK if all is good</returns>
public Error GetWindowBits(out int windowBits)
{
switch (WindowSize)
{
case 0x008000:
windowBits = 15;
break;
case 0x010000:
windowBits = 16;
break;
case 0x020000:
windowBits = 17;
break;
case 0x040000:
windowBits = 18;
break;
case 0x080000:
windowBits = 19;
break;
case 0x100000:
windowBits = 20;
break;
case 0x200000:
windowBits = 21;
break;
default:
windowBits = -1;
return Error.MSPACK_ERR_DATAFORMAT;
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,94 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _LZXResetTable
{
#region Fields
/// <summary>
/// UNKNOWN
/// </summary>
/// <remarks>0x0000</remarks>
public uint Unknown1 { get; private set; }
/// <summary>
/// Number of entries in the table
/// </summary>
/// <remarks>0x0004</remarks>
public uint NumEntries { get; private set; }
/// <summary>
/// Size of each entry
/// </summary>
/// <remarks>0x0008</remarks>
public uint EntrySize { get; private set; }
/// <summary>
/// Table offset
/// </summary>
/// <remarks>0x000C</remarks>
public uint TableOffset { get; private set; }
/// <summary>
/// Uncompressed length
/// </summary>
/// <remarks>0x0010</remarks>
public long UncompressedLength { get; private set; }
/// <summary>
/// Compressed length
/// </summary>
/// <remarks>0x0018</remarks>
public long CompressedLength { get; private set; }
/// <summary>
/// Frame length
/// </summary>
/// <remarks>0x0020</remarks>
public long FrameLength { get; private set; }
/// <summary>
/// Total size of the LZX reset table in bytes
/// </summary>
public const int Size = 0x0028;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _LZXResetTable() { }
/// <summary>
/// Create a _LZXControlData from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _LZXResetTable resetTable)
{
resetTable = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
resetTable = new _LZXResetTable();
resetTable.Unknown1 = BitConverter.ToUInt32(buffer, 0x0000);
resetTable.NumEntries = BitConverter.ToUInt32(buffer, 0x0004);
resetTable.EntrySize = BitConverter.ToUInt32(buffer, 0x0008);
resetTable.TableOffset = BitConverter.ToUInt32(buffer, 0x000C);
resetTable.UncompressedLength = BitConverter.ToInt64(buffer, 0x0010);
resetTable.CompressedLength = BitConverter.ToInt64(buffer, 0x0018);
resetTable.FrameLength = BitConverter.ToInt64(buffer, 0x0020);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,118 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.CHM
{
internal class _PMGHeader
{
#region Fields
/// <summary>
/// "PMGL" or "PMGI"
/// </summary>
/// <remarks>0x0000</remarks>
public uint Signature { get; private set; }
/// <summary>
/// Quick reference size
/// </summary>
/// <remarks>0x0004</remarks>
public uint QuickRefSize { get; private set; }
/// <summary>
/// Number of entries in a PMGI chunk
/// </summary>
/// <remarks>
/// 0x0008
///
/// Unused in PMGL
/// </remarks>
public uint PMGIEntries { get; private set; }
/// <summary>
/// Previous chunk ID
/// </summary>
/// <remarks>
/// 0x000C
///
/// Does not exist in PMGI
/// </remarks>
public uint PrevChunk { get; private set; }
/// <summary>
/// Next chunk ID
/// </summary>
/// <remarks>
/// 0x0010
///
/// Does not exist in PMGI
/// </remarks>
public uint NextChunk { get; private set; }
/// <summary>
/// Number of entries in a PMGL chunk
/// </summary>
/// <remarks>
/// 0x0014
///
/// Does not exist in PMGI
/// </remarks>
public uint PMGLEntries { get; private set; }
/// <summary>
/// Total size of the PMGI header in bytes
/// </summary>
public const int PMGISize = 0x000C;
/// <summary>
/// Total size of the PMGL header in bytes
/// </summary>
public const int PMGLSize = 0x0014;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _PMGHeader() { }
/// <summary>
/// Create a _PMGHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _PMGHeader header)
{
header = null;
if (buffer == null || buffer.Length < PMGISize)
return Error.MSPACK_ERR_READ;
header = new _PMGHeader();
header.Signature = BitConverter.ToUInt32(buffer, 0x0000);
header.QuickRefSize = BitConverter.ToUInt32(buffer, 0x0004);
header.PMGIEntries = BitConverter.ToUInt32(buffer, 0x0008);
if (buffer.Length >= PMGLSize)
{
header.PrevChunk = BitConverter.ToUInt32(buffer, 0x000C);
header.NextChunk = BitConverter.ToUInt32(buffer, 0x0010);
header.PMGLEntries = BitConverter.ToUInt32(buffer, 0x0014);
}
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Determines if a PMG chunk is PMGL or PMGI
/// </summary>
/// <returns>True for PMGL and false of PMGI</returns>
public bool IsPMGL() => Signature == 0x4C474D50;
}
}

View File

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

@@ -1,294 +0,0 @@
/* 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;
using System.Linq;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
/* 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 abstract partial class CompressionStream : BaseDecompressState
{
#region Common
/// <summary>
/// Initialises bitstream state in state structure
/// </summary>
public void INIT_BITS()
{
InputPointer = 0;
InputEnd = 0;
BitBuffer = 0;
BitsLeft = 0;
EndOfInput = 0;
}
/// <summary>
/// Ensure there are at least N bits in the bit buffer
/// </summary>
public void ENSURE_BITS(int nbits)
{
while (BitsLeft < nbits)
{
READ_BYTES();
if (Error != Error.MSPACK_ERR_OK)
return;
}
}
/// <summary>
/// Read from the input if the buffer is empty
/// </summary>
public void READ_IF_NEEDED()
{
if (InputPointer >= InputEnd)
ReadInput();
}
/// <summary>
/// Read bytes from the input into the bit buffer
/// </summary>
public abstract void READ_BYTES();
/// <summary>
/// Read an input stream and fill the buffer
/// </summary>
protected virtual void ReadInput()
{
int read = System.Read(InputFileHandle, InputBuffer, 0, (int)InputBufferSize);
if (read < 0)
{
Error = Error.MSPACK_ERR_READ;
return;
}
// 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 (EndOfInput > 0)
{
Console.WriteLine("Out of input bytes");
Error = Error.MSPACK_ERR_READ;
return;
}
else
{
read = 2;
InputBuffer[0] = InputBuffer[1] = 0;
EndOfInput = 1;
}
}
// Update i_ptr and i_end
InputPointer = 0;
InputEnd = read;
}
#endregion
#region MSB
/// <summary>
/// Inject data into the bit buffer
/// </summary>
public void INJECT_BITS_MSB(int bitdata, int nbits)
{
BitBuffer |= ((uint)bitdata << (BITBUF_WIDTH - nbits - BitsLeft));
BitsLeft += nbits;
}
/// <summary>
/// Extracts without removing N bits from the bit buffer
/// </summary>
public long PEEK_BITS_MSB(int nbits) => (BitBuffer >> (BITBUF_WIDTH - (nbits)));
/// <summary>
/// Takes N bits from the buffer and puts them in var
/// </summary>
public long READ_BITS_MSB(int nbits)
{
ENSURE_BITS(nbits);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_MSB(nbits);
REMOVE_BITS_MSB(nbits);
return temp;
}
/// <summary>
/// Read multiple bits and put them in var
/// </summary>
public long READ_MANY_BITS_MSB(int nbits)
{
byte needed = (byte)(nbits), bitrun;
long temp = 0;
while (needed > 0)
{
if (BitsLeft <= (BITBUF_WIDTH - 16))
{
READ_BYTES();
if (Error != Error.MSPACK_ERR_OK)
return -1;
}
bitrun = (byte)((BitsLeft < needed) ? BitsLeft : needed);
temp = (temp << bitrun) | PEEK_BITS_MSB(bitrun);
REMOVE_BITS_MSB(bitrun);
needed -= bitrun;
}
return temp;
}
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_MSB(int nbits)
{
BitBuffer <<= nbits;
BitsLeft -= nbits;
}
#endregion
#region LSB
/// <summary>
/// Inject data into the bit buffer
/// </summary>
public void INJECT_BITS_LSB(int bitdata, int nbits)
{
BitBuffer |= (uint)(bitdata << BitsLeft);
BitsLeft += nbits;
}
/// <summary>
/// Extracts without removing N bits from the bit buffer
/// </summary>
public long PEEK_BITS_LSB(int nbits) => (BitBuffer & ((1 << (nbits)) - 1));
/// <summary>
/// Extracts without removing N bits from the bit buffer using a bit mask
/// </summary>
public long PEEK_BITS_T_LSB(int nbits) => BitBuffer & LSBBitMask[(nbits)];
/// <summary>
/// Takes N bits from the buffer and puts them in var
/// </summary>
public long READ_BITS_LSB(int nbits)
{
ENSURE_BITS(nbits);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_LSB(nbits);
REMOVE_BITS_LSB(nbits);
return temp;
}
/// <summary>
/// Takes N bits from the buffer and puts them in var using a bit mask
/// </summary>
public long READ_BITS_T_LSB(int nbits)
{
ENSURE_BITS(nbits);
if (Error != Error.MSPACK_ERR_OK)
return -1;
long temp = PEEK_BITS_T_LSB(nbits);
REMOVE_BITS_LSB(nbits);
return temp;
}
/// <summary>
/// Removes N bits from the bit buffer
/// </summary>
public void REMOVE_BITS_LSB(int nbits)
{
BitBuffer >>= nbits;
BitsLeft -= nbits;
}
#endregion
}
}

View File

@@ -1,342 +0,0 @@
/* 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 static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public abstract partial class CompressionStream : BaseDecompressState
{
#region Common
/// <summary>
/// Per compression error code for decoding failure
/// </summary>
public abstract Error HUFF_ERROR();
#endregion
#region MSB
/// <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 long READ_HUFFSYM_MSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
ENSURE_BITS(HUFF_MAXBITS);
ushort sym = table[PEEK_BITS_MSB(tablebits)];
if (sym >= maxsymbols)
HUFF_TRAVERSE_MSB(ref sym, table, tablebits, maxsymbols);
REMOVE_BITS_MSB(lengths[sym]);
return sym;
}
/// <summary>
/// Traverse for a single symbol
/// </summary>
private void HUFF_TRAVERSE_MSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols)
{
int i = 1 << (BITBUF_WIDTH - tablebits);
do
{
if ((i >>= 1) == 0)
{
Error = HUFF_ERROR();
return;
}
sym = table[(sym << 1) | ((BitBuffer & i) != 0 ? 1 : 0)];
} while (sym >= maxsymbols);
}
/// <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>True for OK or false for error</returns>
public static bool MakeDecodeTableMSB(int nsyms, int nbits, byte[] length, ushort[] table)
{
ushort sym, next_symbol;
long leaf, fill;
byte bit_num;
long pos = 0; // The current position in the decode table
long table_mask = 1 << nbits;
long 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;
leaf = pos;
if ((pos += bit_mask) > table_mask)
return false; // Table overrun
// Fill all possible lookups of this symbol with the symbol itself
for (fill = bit_mask; fill-- > 0;)
{
table[leaf++] = sym;
}
}
bit_mask >>= 1;
}
// Exit with success if table is now complete
if (pos == table_mask)
return true;
// Mark all remaining table entries as unused
for (sym = (ushort)pos; sym < table_mask; sym++)
{
table[sym] = 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 false; // Table overflow
leaf = pos >> 16;
for (fill = 0; fill < (bit_num - nbits); fill++)
{
// If 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 >> (15 - (int)fill)) & 1) != 0)
leaf++;
}
table[leaf] = sym;
pos += bit_mask;
}
bit_mask >>= 1;
}
// Full table?
return pos == table_mask;
}
#endregion
#region LSB
/// <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 long READ_HUFFSYM_LSB(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
ENSURE_BITS(HUFF_MAXBITS);
ushort sym = table[PEEK_BITS_LSB(tablebits)];
if (sym >= maxsymbols)
HUFF_TRAVERSE_LSB(ref sym, table, tablebits, maxsymbols);
REMOVE_BITS_LSB(lengths[sym]);
return sym;
}
/// <summary>
/// Traverse for a single symbol
/// </summary>
private void HUFF_TRAVERSE_LSB(ref ushort sym, ushort[] table, int tablebits, int maxsymbols)
{
int i = tablebits - 1;
do
{
if (i++ > HUFF_MAXBITS)
{
Error = HUFF_ERROR();
return;
}
sym = table[(sym << 1) | ((BitBuffer >> i) & 1)];
} while (sym >= maxsymbols);
}
/// <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>True for OK or false for error</returns>
public static bool MakeDecodeTableLSB(int nsyms, int 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 << 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 false; // 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 true;
// Mark all remaining table entries as unused
for (sym = (ushort)pos; sym < table_mask; sym++)
{
reverse = sym;
leaf = 0;
fill = (uint)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 = ((table_mask >> 1) < nsyms) ? (ushort)nsyms : (ushort)(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 false; // Table overflow
// leaf = the first nbits of the code, reversed
reverse = pos >> 16;
leaf = 0;
fill = (uint)nbits;
do
{
leaf <<= 1;
leaf |= reverse & 1;
reverse >>= 1;
} while (--fill > 0);
for (fill = 0; fill < (bit_num - nbits); fill++)
{
// If 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] = (ushort)next_symbol++;
}
// Follow the path and select either left or right for next bit
leaf = (uint)(table[leaf] << 1);
if (((pos >> (15 - (int)fill)) & 1) != 0)
leaf++;
}
table[leaf] = sym;
pos += bit_mask;
}
bit_mask >>= 1;
}
// Full table?
return pos == table_mask;
}
#endregion
}
}

View File

@@ -1,56 +0,0 @@
/* 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 abstract partial class CompressionStream : BaseDecompressState
{
public byte[] InputBuffer { get; set; }
public uint InputBufferSize { get; set; }
/// <summary>
/// i_ptr
/// </summary>
public int InputPointer { get; set; }
/// <summary>
/// i_end
/// </summary>
public int InputEnd { get; set; }
/// <summary>
/// o_ptr
/// </summary>
public int OutputPointer { get; set; }
/// <summary>
/// o_end
/// </summary>
public int OutputEnd { get; set; }
/// <summary>
/// bit_buffer
/// </summary>
public uint BitBuffer { get; set; }
/// <summary>
/// bits_left
/// </summary>
public int BitsLeft { get; set; }
/// <summary>
/// Have we reached the end of input?
/// </summary>
public int EndOfInput { get; set; }
}
}

View File

@@ -1,281 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.Compression
{
internal static class Constants
{
#region readbits.h
/// <summary>
/// Bit width of a UInt32 bit buffer
/// </summary>
public const int BITBUF_WIDTH = 4 * CHAR_BIT;
/// <summary>
/// Number of bits in a character
/// </summary>
internal const int CHAR_BIT = 8;
// lsb_bit_mask[n] = (1 << n) - 1
internal static readonly ushort[] LSBBitMask = new ushort[17]
{
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#endregion
#region readhuff.h
/// <summary>
/// Maximum bits in a Huffman code
/// </summary>
public const int HUFF_MAXBITS = 16;
#endregion
#region LZSS
/// <summary>
/// Size of an LZSS window
/// </summary>
public const int LZSS_WINDOW_SIZE = 4096;
/// <summary>
/// LZSS window fill byte
/// </summary>
public const byte LZSS_WINDOW_FILL = 0x20;
#endregion
#region LZX
// Some constants defined by the LZX specification
public const int LZX_MIN_MATCH = 2;
public const int LZX_MAX_MATCH = 257;
public const int LZX_NUM_CHARS = 256;
public const int LZX_PRETREE_NUM_ELEMENTS = 20;
public const int LZX_ALIGNED_NUM_ELEMENTS = 8; // Aligned offset tree #elements
public const int LZX_NUM_PRIMARY_LENGTHS = 7; // This one missing from spec!
public const int LZX_NUM_SECONDARY_LENGTHS = 249; // Length tree #elements
// LZX huffman defines: tweak tablebits as desired
public const int LZX_PRETREE_MAXSYMBOLS = LZX_PRETREE_NUM_ELEMENTS;
public const byte LZX_PRETREE_TABLEBITS = 6;
public const int LZX_MAINTREE_MAXSYMBOLS = LZX_NUM_CHARS + 290 * 8;
public const byte LZX_MAINTREE_TABLEBITS = 12;
public const int LZX_LENGTH_MAXSYMBOLS = LZX_NUM_SECONDARY_LENGTHS + 1;
public const byte LZX_LENGTH_TABLEBITS = 12;
public const int LZX_ALIGNED_MAXSYMBOLS = LZX_ALIGNED_NUM_ELEMENTS;
public const byte LZX_ALIGNED_TABLEBITS = 7;
public const int LZX_LENTABLE_SAFETY = 64; // Table decoding overruns are allowed
public const int LZX_FRAME_SIZE = 32768; // The size of a frame in LZX
#region LZX static data tables
/// <summary>
/// LZX 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.
///
/// The number of slots is decided by how many are needed to encode the
/// largest offset for a given window size. This is easy when the gap between
/// slots is less than 128Kb, it's a linear relationship. But when extra_bits
/// reaches its limit of 17 (because LZX can only ensure reading 17 bits of
/// data at a time), we can only jump 128Kb at a time and have to start
/// using more and more position slots as each window size doubles.
/// </summary>
public static readonly uint[] LZXPositionSlots = new uint[11]
{
30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
};
/// <summary>
/// An index to the position slot bases
/// </summary>
/// <remarks>
/// Calculated as follows:
/// LZXPositionBase[0] = 0
/// LZXPositionBase[i] = LZXPositionBase[i - 1] + (1 << ExtraBits(i - 1))
/// </remarks>
public static readonly uint[] LZXPositionBase = new uint[290]
{
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, 655360,
786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
1835008, 1966080, 2097152, 2228224, 2359296, 2490368, 2621440, 2752512,
2883584, 3014656, 3145728, 3276800, 3407872, 3538944, 3670016, 3801088,
3932160, 4063232, 4194304, 4325376, 4456448, 4587520, 4718592, 4849664,
4980736, 5111808, 5242880, 5373952, 5505024, 5636096, 5767168, 5898240,
6029312, 6160384, 6291456, 6422528, 6553600, 6684672, 6815744, 6946816,
7077888, 7208960, 7340032, 7471104, 7602176, 7733248, 7864320, 7995392,
8126464, 8257536, 8388608, 8519680, 8650752, 8781824, 8912896, 9043968,
9175040, 9306112, 9437184, 9568256, 9699328, 9830400, 9961472, 10092544,
10223616, 10354688, 10485760, 10616832, 10747904, 10878976, 11010048,
11141120, 11272192, 11403264, 11534336, 11665408, 11796480, 11927552,
12058624, 12189696, 12320768, 12451840, 12582912, 12713984, 12845056,
12976128, 13107200, 13238272, 13369344, 13500416, 13631488, 13762560,
13893632, 14024704, 14155776, 14286848, 14417920, 14548992, 14680064,
14811136, 14942208, 15073280, 15204352, 15335424, 15466496, 15597568,
15728640, 15859712, 15990784, 16121856, 16252928, 16384000, 16515072,
16646144, 16777216, 16908288, 17039360, 17170432, 17301504, 17432576,
17563648, 17694720, 17825792, 17956864, 18087936, 18219008, 18350080,
18481152, 18612224, 18743296, 18874368, 19005440, 19136512, 19267584,
19398656, 19529728, 19660800, 19791872, 19922944, 20054016, 20185088,
20316160, 20447232, 20578304, 20709376, 20840448, 20971520, 21102592,
21233664, 21364736, 21495808, 21626880, 21757952, 21889024, 22020096,
22151168, 22282240, 22413312, 22544384, 22675456, 22806528, 22937600,
23068672, 23199744, 23330816, 23461888, 23592960, 23724032, 23855104,
23986176, 24117248, 24248320, 24379392, 24510464, 24641536, 24772608,
24903680, 25034752, 25165824, 25296896, 25427968, 25559040, 25690112,
25821184, 25952256, 26083328, 26214400, 26345472, 26476544, 26607616,
26738688, 26869760, 27000832, 27131904, 27262976, 27394048, 27525120,
27656192, 27787264, 27918336, 28049408, 28180480, 28311552, 28442624,
28573696, 28704768, 28835840, 28966912, 29097984, 29229056, 29360128,
29491200, 29622272, 29753344, 29884416, 30015488, 30146560, 30277632,
30408704, 30539776, 30670848, 30801920, 30932992, 31064064, 31195136,
31326208, 31457280, 31588352, 31719424, 31850496, 31981568, 32112640,
32243712, 32374784, 32505856, 32636928, 32768000, 32899072, 33030144,
33161216, 33292288, 33423360
};
#endregion
#endregion
#region MSZIP
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 const int MSZIP_LITERAL_TABLESIZE = (MSZIP_LITERAL_MAXSYMBOLS * 4);
public const int MSZIP_LITERAL_TABLESIZE = ((1 << MSZIP_LITERAL_TABLEBITS) + (MSZIP_LITERAL_MAXSYMBOLS * 2));
//public const int MSZIP_DISTANCE_TABLESIZE = (MSZIP_DISTANCE_MAXSYMBOLS * 4);
public const int MSZIP_DISTANCE_TABLESIZE = ((1 << MSZIP_DISTANCE_TABLEBITS) + (MSZIP_DISTANCE_MAXSYMBOLS * 2));
/// <summary>
/// Match lengths for literal codes 257.. 285
/// </summary>
public static readonly ushort[] LiteralLengths = 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>
public static readonly ushort[] DistanceOffsets = 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>
public static readonly byte[] LiteralExtraBits = 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>
public static readonly byte[] DistanceExtraBits = 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>
public static readonly byte[] BitLengthOrder = new byte[19]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
#endregion
#region 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;
*/
public static readonly uint[] QTMPositionBase = 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
};
public static readonly byte[] QTMExtraBits = 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
};
public static readonly byte[] QTMLengthBase = 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
};
public static readonly byte[] QTMLengthExtra = 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
};
#endregion
}
}

View File

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

View File

@@ -1,39 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2004 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.Compression
{
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

@@ -1,293 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
/// <summary>
/// In the KWAJ LZH format, there is no special 'eof' marker, it just
/// ends. Depending on how many bits are left in the final byte when
/// the stream ends, that might be enough to start another literal or
/// match. The only easy way to detect that we've come to an end is to
/// guard all bit-reading. We allow fake bits to be read once we reach
/// the end of the stream, but we check if we then consumed any of
/// those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
/// isn't how the default ReadInput works (it simply lets
/// 2 fake bytes in then stops), so we implement our own.
/// </summary>
public partial class LZHKWAJ
{
public static LZHKWAJ Init(SystemImpl sys, FileStream input, FileStream output)
{
if (sys == null || input == null || output == null)
return null;
return new LZHKWAJ()
{
System = sys,
InputFileHandle = input,
OutputFileHandle = output,
};
}
public Error Decompress()
{
int i;
int lit_run = 0;
int j, pos = 0, len, offset;
uint[] types = new uint[6];
// Reset global state
INIT_BITS();
for (i = 0; i < LZSS_WINDOW_SIZE; i++)
{
Window[i] = LZSS_WINDOW_FILL;
}
// Read 6 encoding types (for byte alignment) but only 5 are needed
for (i = 0; i < 6; i++)
{
types[i] = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
// Read huffman table symbol lengths and build huffman trees
BUILD_TREE(types[0], MATCHLEN1_table, MATCHLEN1_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN1_SYMS);
BUILD_TREE(types[1], MATCHLEN2_table, MATCHLEN2_len, KWAJ_TABLEBITS, KWAJ_MATCHLEN2_SYMS);
BUILD_TREE(types[2], LITLEN_table, LITLEN_len, KWAJ_TABLEBITS, KWAJ_LITLEN_SYMS);
BUILD_TREE(types[3], OFFSET_table, OFFSET_len, KWAJ_TABLEBITS, KWAJ_OFFSET_SYMS);
BUILD_TREE(types[4], LITERAL_table, LITERAL_len, KWAJ_TABLEBITS, KWAJ_LITERAL_SYMS);
while (EndOfInput == 0)
{
if (lit_run > 0)
{
len = (int)READ_HUFFSYM_SAFE(MATCHLEN2_table, MATCHLEN2_len, KWAJ_MATCHLEN2_TBLSIZE, KWAJ_MATCHLEN2_SYMS);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
else
{
len = (int)READ_HUFFSYM_SAFE(MATCHLEN1_table, MATCHLEN1_len, KWAJ_MATCHLEN1_TBLSIZE, KWAJ_MATCHLEN1_SYMS);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
if (len > 0)
{
len += 2;
lit_run = 0; // Not the end of a literal run
j = (int)READ_HUFFSYM_SAFE(OFFSET_table, OFFSET_len, KWAJ_OFFSET_TBLSIZE, KWAJ_OFFSET_SYMS);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
offset = j << 6;
j = (int)READ_BITS_SAFE(6);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
offset |= j;
// Copy match as output and into the ring buffer
while (len-- > 0)
{
Window[pos] = Window[(pos + 4096 - offset) & 4095];
WRITE_BYTE(pos);
if (Error != Error.MSPACK_ERR_OK)
return Error;
pos++;
pos &= 4095;
}
}
else
{
len = (int)READ_HUFFSYM_SAFE(LITLEN_table, LITLEN_len, KWAJ_LITLEN_TBLSIZE, KWAJ_LITLEN_SYMS);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
len++;
lit_run = (len == 32) ? 0 : 1; // End of a literal run?
while (len-- > 0)
{
j = (int)READ_HUFFSYM_SAFE(LITERAL_table, LITERAL_len, KWAJ_LITERAL_TBLSIZE, KWAJ_LITERAL_SYMS);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
// Copy as output and into the ring buffer
Window[pos] = (byte)j;
WRITE_BYTE(pos);
if (Error != Error.MSPACK_ERR_OK)
return Error;
pos++; pos &= 4095;
}
}
}
return Error.MSPACK_ERR_OK;
}
private Error BUILD_TREE(uint type, ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
Error err = ReadLens(type, (uint)maxsymbols, MATCHLEN1_len);
if (err != Error.MSPACK_ERR_OK)
return err;
if (!CompressionStream.MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
return Error.MSPACK_ERR_DATAFORMAT;
return Error.MSPACK_ERR_OK;
}
/// <summary>
/// Safely read bits from the buffer
/// </summary>
private long READ_BITS_SAFE(int nbits)
{
long val = READ_BITS_MSB(nbits);
if (EndOfInput != 0 && BitsLeft < EndOfInput)
Error = Error.MSPACK_ERR_NOMEMORY;
else
Error = Error.MSPACK_ERR_OK;
return val;
}
/// <summary>
/// Safely read a symbol from a Huffman tree
/// </summary>
private long READ_HUFFSYM_SAFE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
long val = READ_HUFFSYM_MSB(table, lengths, tablebits, maxsymbols);
if (EndOfInput != 0 && BitsLeft < EndOfInput)
Error = Error.MSPACK_ERR_NOMEMORY;
else
Error = Error.MSPACK_ERR_OK;
return val;
}
/// <summary>
/// Write a single byte to the output stream
/// </summary>
private void WRITE_BYTE(int pos)
{
if (System.Write(OutputFileHandle, Window, pos, 1) != 1)
Error = Error.MSPACK_ERR_WRITE;
}
private Error ReadLens(uint type, uint numsyms, byte[] lens)
{
uint i, c, sel;
switch (type)
{
case 0:
i = numsyms;
c = (uint)((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:
c = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
sel = (uint)READ_BITS_SAFE(1);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
if (sel == 0)
{
lens[i] = (byte)c;
}
else
{
sel = (uint)READ_BITS_SAFE(1);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
if (sel == 0)
{
lens[i] = (byte)++c;
}
else
{
c = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[i] = (byte)c;
}
}
}
break;
case 2:
c = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[0] = (byte)c;
for (i = 1; i < numsyms; i++)
{
sel = (uint)READ_BITS_SAFE(2);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
if (sel == 3)
{
c = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
}
else
{
c += (uint)((byte)sel - 1);
}
lens[i] = (byte)c;
}
break;
case 3:
for (i = 0; i < numsyms; i++)
{
c = (uint)READ_BITS_SAFE(4);
if (Error == Error.MSPACK_ERR_NOMEMORY)
return Error.MSPACK_ERR_OK;
lens[i] = (byte)c;
}
break;
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,64 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using static LibMSPackSharp.Constants;
namespace LibMSPackSharp.Compression
{
public partial class LZHKWAJ : CompressionStream
{
/// <inheritdoc/>
public override void READ_BYTES()
{
if (InputPointer >= InputEnd)
{
ReadInput();
if (Error != Error.MSPACK_ERR_OK)
return;
InputPointer = InputPointer;
InputEnd = InputEnd;
}
INJECT_BITS_MSB(InputBuffer[InputPointer++], 8);
}
/// <inheritdoc/>
protected override void ReadInput()
{
int read;
if (EndOfInput != 0)
{
EndOfInput += 8;
InputBuffer[0] = 0;
read = 1;
}
else
{
read = System.Read(InputFileHandle, InputBuffer, 0, KWAJ_INPUT_SIZE);
if (read < 0)
{
Error = Error.MSPACK_ERR_READ;
return;
}
if (read == 0)
{
InputEnd = 8;
InputBuffer[0] = 0;
read = 1;
}
}
// Update InputPointer and InputLength
InputPointer = 0;
InputEnd = read;
}
}
}

View File

@@ -1,17 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.Compression
{
public partial class LZHKWAJ : CompressionStream
{
/// <inheritdoc/>
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DATAFORMAT;
}
}

View File

@@ -1,37 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2010 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using static LibMSPackSharp.Constants;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class LZHKWAJ : CompressionStream
{
// Huffman code lengths
public byte[] MATCHLEN1_len { get; set; } = new byte[KWAJ_MATCHLEN1_SYMS];
public byte[] MATCHLEN2_len { get; set; } = new byte[KWAJ_MATCHLEN2_SYMS];
public byte[] LITLEN_len { get; set; } = new byte[KWAJ_LITLEN_SYMS];
public byte[] OFFSET_len { get; set; } = new byte[KWAJ_OFFSET_SYMS];
public byte[] LITERAL_len { get; set; } = new byte[KWAJ_LITERAL_SYMS];
// Huffman decoding tables
public ushort[] MATCHLEN1_table { get; set; } = new ushort[KWAJ_MATCHLEN1_TBLSIZE];
public ushort[] MATCHLEN2_table { get; set; } = new ushort[KWAJ_MATCHLEN2_TBLSIZE];
public ushort[] LITLEN_table { get; set; } = new ushort[KWAJ_LITLEN_TBLSIZE];
public ushort[] OFFSET_table { get; set; } = new ushort[KWAJ_OFFSET_TBLSIZE];
public ushort[] LITERAL_table { get; set; } = new ushort[KWAJ_LITERAL_TBLSIZE];
// History window
public byte[] Window { get; set; } = new byte[LZSS_WINDOW_SIZE];
}
}

View File

@@ -1,177 +0,0 @@
/* 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
*/
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public class LZSS
{
/// <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, FileStream input, FileStream output, int input_buffer_size, LZSSMode mode)
{
// Check parameters
if (system == null || input_buffer_size < 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 + input_buffer_size];
// Initialise decompression
int inbuf = LZSS_WINDOW_SIZE;
for (int j = inbuf; j < window.Length; j++)
{
window[j] = 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 i_ptr = 0, i_end = 0, read;
// Loop forever; exit condition is in ENSURE_BYTES macro
for (; ; )
{
//ENSURE_BYTES
{
if (i_ptr >= i_end)
{
read = input.Read(window, inbuf, input_buffer_size);
if (read <= 0)
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
i_ptr = inbuf;
i_end = read;
}
}
uint c = window[i_ptr++] ^ invert;
for (uint i = 0x01; (i & 0xFF) != 0; i <<= 1)
{
// Literal
if (c != 0 & i != 0)
{
//ENSURE_BYTES
{
if (i_ptr >= i_end)
{
read = input.Read(window, inbuf, input_buffer_size);
if (read <= 0)
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
i_ptr = inbuf;
i_end = read;
}
}
window[pos] = window[i_ptr++];
//WRITE_BYTE
{
try { output.Write(window, (int)pos, 1); }
catch { return Error.MSPACK_ERR_WRITE; }
}
pos++;
pos &= LZSS_WINDOW_SIZE - 1;
}
// Match
else
{
//ENSURE_BYTES
{
if (i_ptr >= i_end)
{
read = input.Read(window, inbuf, input_buffer_size);
if (read <= 0)
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
i_ptr = inbuf;
i_end = read;
}
}
uint mpos = window[i_ptr++];
//ENSURE_BYTES
{
if (i_ptr >= i_end)
{
read = input.Read(window, inbuf, input_buffer_size);
if (read <= 0)
return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK;
i_ptr = inbuf;
i_end = read;
}
}
mpos |= (uint)(window[i_ptr] & 0xF0) << 4;
uint len = (uint)(window[i_ptr++] & 0x0F) + 3;
while (len-- != 0)
{
window[pos] = window[mpos];
//WRITE_BYTE
{
try { output.Write(window, (int)pos, 1); }
catch { return Error.MSPACK_ERR_WRITE; }
}
pos++;
pos &= LZSS_WINDOW_SIZE - 1;
mpos++;
mpos &= LZSS_WINDOW_SIZE - 1;
}
}
}
}
/* not reached */
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,847 +0,0 @@
/* 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; 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
*/
/* Microsoft's LZX document (in cab-sdk.exe) and their implementation
* of the com.ms.util.cab Java package do not concur.
*
* In the LZX document, there is a table showing the correlation between
* window size and the number of position slots. It states that the 1MB
* window = 40 slots and the 2MB window = 42 slots. In the implementation,
* 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
* first slot whose position base is equal to or more than the required
* window size'. This would explain why other tables in the document refer
* to 50 slots rather than 42.
*
* The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
* is not defined in the specification.
*
* The LZX document does not state the uncompressed block has an
* uncompressed length field. Where does this length field come from, so
* we can know how large the block is? The implementation has it as the 24
* bits following after the 3 blocktype bits, before the alignment
* padding.
*
* The LZX document states that aligned offset blocks have their aligned
* offset huffman tree AFTER the main and length trees. The implementation
* suggests that the aligned offset tree is BEFORE the main and length
* trees.
*
* The LZX document decoding algorithm states that, in an aligned offset
* block, if an extra_bits value is 1, 2 or 3, then that number of bits
* should be read and the result added to the match offset. This is
* correct for 1 and 2, but not 3, where just a huffman symbol (using the
* aligned tree) should be read.
*
* Regarding the E8 preprocessing, the LZX document states 'No translation
* may be performed on the last 6 bytes of the input block'. This is
* correct. However, the pseudocode provided checks for the *E8 leader*
* up to the last 6 bytes. If the leader appears between -10 and -7 bytes
* from the end, this would cause the next four bytes to be modified, at
* least one of which would be in the last 6 bytes, which is not allowed
* according to the spec.
*
* The specification states that the huffman trees must always contain at
* least one element. However, many CAB files contain blocks where the
* length tree is completely empty (because there are no matches), and
* this is expected to succeed.
*
* The errors in LZX documentation appear have been corrected in the
* new documentation for the LZX DELTA format.
*
* http://msdn.microsoft.com/en-us/library/cc483133.aspx
*
* However, this is a different format, an extension of regular LZX.
* I have noticed the following differences, there may be more:
*
* The maximum window size has increased from 2MB to 32MB. This also
* increases the maximum number of position slots, etc.
*
* If the match length is 257 (the maximum possible), this signals
* a further length decoding step, that allows for matches up to
* 33024 bytes long.
*
* The format now allows for "reference data", supplied by the caller.
* If match offsets go further back than the number of bytes
* decompressed so far, that is them accessing the reference data.
*/
using System;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class LZX
{
#region Public Functionality
/// <summary>
/// Allocates and initialises LZX decompression state for decoding an LZX
/// stream.
///
/// This routine uses system.alloc() to allocate memory. If memory
/// allocation fails, or the parameters to this function are invalid,
/// null is returned.
/// </summary>
/// <param name="system">A SystemImpl 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 LZX data.</param>
/// <param name="output">An output stream to write the decoded data to.</param>
/// <param name="window_bits">The size of the decoding window, which must be between 15 and 21 inclusive for regular LZX data, or between 17 and 25 inclusive for LZX DELTA data.</param>
/// <param name="reset_interval">
/// The interval at which the LZX bitstream is reset, in multiples of LZX frames (32678 bytes),
/// e.g. a value of 2 indicates the input stream resets after every 65536 output bytes.
/// A value of 0 indicates that the bitstream never resets, such as in CAB LZX streams.
/// </param>
/// <param name="input_buffer_size">The number of bytes to use as an input bitstream buffer.</param>
/// <param name="output_length">
/// The length in bytes of the entirely decompressed output stream, if known in advance.
/// It is used to correctly perform the Intel E8 transformation, which must stop 6 bytes before the very end of the decompressed stream.
/// It is not otherwise used or adhered to. If the full decompressed length is known in advance, set it here.
/// If it is NOT known, use the value 0, and call lzxd_set_outputLength() once it is known.
/// If never set, 4 of the final 6 bytes of the output stream may be incorrect
/// </param>
/// <param name="is_delta">Should be zero for all regular LZX data, non-zero for LZX DELTA encoded data.</param>
/// <returns>A pointer to an initialised LZX structure, or null if there was not enough memory or parameters to the function were wrong.</returns>
public static LZX Init(SystemImpl system, FileStream input, FileStream output, int window_bits, int reset_interval, int input_buffer_size, long output_length, bool is_delta)
{
if (system == null)
return null;
// LZX DELTA window sizes are between 2^17 (128KiB) and 2^25 (32MiB),
// regular LZX windows are between 2^15 (32KiB) and 2^21 (2MiB)
if (is_delta)
{
if (window_bits < 17 || window_bits > 25)
return null;
}
else
{
if (window_bits < 15 || window_bits > 21)
return null;
}
if (reset_interval < 0 || output_length < 0)
{
Console.WriteLine("Reset interval or output length < 0");
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
LZX lzx = new LZX()
{
// Allocate decompression window and input buffer
Window = new byte[1 << window_bits],
InputBuffer = new byte[input_buffer_size],
System = system,
InputFileHandle = input,
OutputFileHandle = output,
Offset = 0,
Length = output_length,
InputBufferSize = (uint)input_buffer_size,
WindowSize = (uint)(1 << window_bits),
ReferenceDataSize = 0,
WindowPosition = 0,
FramePosition = 0,
Frame = 0,
ResetInterval = (uint)reset_interval,
IntelFileSize = 0,
IntelStarted = false,
Error = Error.MSPACK_ERR_OK,
NumOffsets = LZXPositionSlots[window_bits - 15] << 3,
IsDelta = is_delta,
OutputPointer = 0,
OutputEnd = 0,
WriteFromE8 = false,
};
lzx.ResetState();
lzx.INIT_BITS();
return lzx;
}
/// <summary>
/// Reads LZX DELTA reference data into the window and allows
/// lzxd_decompress() to reference it.
///
/// Call this before the first call to lzxd_decompress().
/// </summary>
/// <param name="system">A SystemImpl structure to use with the input param. Only read() will be called.</param>
/// <param name="input">An input file handle to read reference data using system.read().</param>
/// <param name="length">The length of the reference data. Cannot be longer than the LZX window size.</param>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error SetReferenceData(SystemImpl system, FileStream input, uint length)
{
if (!IsDelta)
{
Console.WriteLine("Only LZX DELTA streams support reference data");
return Error.MSPACK_ERR_ARGS;
}
if (Offset > 0)
{
Console.WriteLine("Too late to set reference data after decoding starts");
return Error.MSPACK_ERR_ARGS;
}
if (length > WindowSize)
{
Console.WriteLine($"Reference length ({length}) is longer than the window");
return Error.MSPACK_ERR_ARGS;
}
if (length > 0 && (system == null || input == null))
{
Console.WriteLine("Length > 0 but no system or input");
return Error.MSPACK_ERR_ARGS;
}
ReferenceDataSize = length;
if (length > 0)
{
// Copy reference data
int pos = (int)(WindowSize - length);
int bytes = system.Read(input, Window, pos, (int)length);
// Length can't be more than 2^25, so no signedness problem
if (bytes < (int)length)
return Error.MSPACK_ERR_READ;
}
ReferenceDataSize = length;
return Error.MSPACK_ERR_OK;
}
// See description of outputLength in Init()
public void SetOutputLength(long outputLength)
{
if (outputLength > 0)
Length = outputLength;
}
/// <summary>
/// Decompresses entire or partial LZX streams.
///
/// The number of bytes of data that should be decompressed is given as the
/// out_bytes parameter. If more bytes are decoded than are needed, they
/// will be kept over for a later invocation.
///
/// The output bytes will be passed to the system.write() function given in
/// lzxd_init(), using the output file handle given in lzxd_init(). More than
/// one call may be made to system.write().
/// Input bytes will be read in as necessary using the system.read()
/// function given in lzxd_init(), using the input file handle given in
/// lzxd_init(). 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 LZX asks for when they
/// reach the end of the stream, rather than return an error code.
///
/// If any error code other than MSPACK_ERR_OK is returned, the stream
/// should be considered unusable and lzxd_decompress() should not be
/// called again on this stream.
/// </summary>
/// <param name="out_bytes">The number of bytes of data to decompress.</param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Error Decompress(long out_bytes)
{
int match_length, length_footer, extra, verbatim_bits, bytes_todo;
int this_run, main_element, aligned_bits, j, warned = 0;
byte[] buf = new byte[12];
int runsrc, rundest;
uint frame_size = 0, end_frame, match_offset;
// Easy answers
if (out_bytes < 0)
return Error.MSPACK_ERR_ARGS;
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Flush out any stored-up bytes before we begin
int i = OutputEnd - OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i > 0)
{
if (System.Write(OutputFileHandle, WriteFromE8 ? E8Buffer : Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
Offset += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
end_frame = (uint)((Offset + out_bytes) / LZX_FRAME_SIZE) + 1;
while (Frame < end_frame)
{
// Have we reached the reset interval? (if there is one?)
if (ResetInterval != 0 && ((Frame % ResetInterval) == 0))
{
if (BlockRemaining > 0)
{
// This is a file format error, we can make a best effort to extract what we can
Console.WriteLine($"{BlockRemaining} bytes remaining at reset interval");
if (warned == 0)
{
System.Message(null, "WARNING; invalid reset interval detected during LZX decompression");
warned++;
}
}
// Re-read the intel header and reset the huffman lengths
ResetState();
}
// LZX DELTA format has chunk_size, not present in LZX format
if (IsDelta)
{
ENSURE_BITS(16);
REMOVE_BITS_MSB(16);
}
// Read header if necessary
if (HeaderRead == 0)
{
// Read 1 bit. If bit=0, intel filesize = 0.
// If bit=1, read intel filesize (32 bits)
j = 0;
i = (int)READ_BITS_MSB(1);
if (i != 0)
{
i = (int)READ_BITS_MSB(16);
j = (int)READ_BITS_MSB(16);
}
IntelFileSize = (i << 16) | j;
HeaderRead = 1;
}
// Calculate size of frame: all frames are 32k except the final frame
// which is 32kb or less. this can only be calculated when lzx->length
// has been filled in.
frame_size = LZX_FRAME_SIZE;
if (Length != 0 && (Length - Offset) < frame_size)
frame_size = (uint)(Length - Offset);
// Decode until one more frame is available
bytes_todo = (int)(FramePosition + frame_size - WindowPosition);
while (bytes_todo > 0)
{
// Initialise new block, if one is needed
if (BlockRemaining == 0)
{
// Realign if previous block was an odd-sized UNCOMPRESSED block
if (BlockType == LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED && (BlockLength & 1) != 0)
{
READ_IF_NEEDED();
InputPointer++;
}
// Read block type (3 bits) and block length (24 bits)
BlockType = (LZXBlockType)READ_BITS_MSB(3);
i = (int)READ_BITS_MSB(16);
j = (int)READ_BITS_MSB(8);
BlockRemaining = BlockLength = (i << 8) | j;
Console.WriteLine($"New block - type: {BlockType}, length: {BlockLength}");
// Read individual block headers
switch (BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
// Read lengths of and build aligned huffman decoding tree
for (i = 0; i < 8; i++)
{
j = (int)READ_BITS_MSB(3);
ALIGNED_len[i] = (byte)j;
}
BUILD_TABLE(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
// Rest of aligned header is same as verbatim
// Read lengths of and build main huffman decoding tree
ReadLengths(MAINTREE_len, 0, 256);
ReadLengths(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
// If the literal 0xE8 is anywhere in the block...
if (MAINTREE_len[0xE8] != 0)
IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
ReadLengths(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE_MAYBE_EMPTY();
break;
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
// Read lengths of and build main huffman decoding tree
ReadLengths(MAINTREE_len, 0, 256);
ReadLengths(MAINTREE_len, 256, LZX_NUM_CHARS + NumOffsets);
BUILD_TABLE(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
// If the literal 0xE8 is anywhere in the block...
if (MAINTREE_len[0xE8] != 0)
IntelStarted = true;
// Read lengths of and build lengths huffman decoding tree
ReadLengths(LENGTH_len, 0, LZX_NUM_SECONDARY_LENGTHS);
BUILD_TABLE_MAYBE_EMPTY();
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// Because we can't assume otherwise
IntelStarted = true;
// Read 1-16 (not 0-15) bits to align to bytes
if (BitsLeft == 0)
ENSURE_BITS(16);
BitsLeft = 0;
BitBuffer = 0;
// Read 12 bytes of stored R0 / R1 / R2 values
for (rundest = 0, i = 0; i < 12; i++)
{
READ_IF_NEEDED();
buf[rundest++] = InputBuffer[InputPointer++];
}
R[0] = (uint)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24));
R[1] = (uint)(buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24));
R[2] = (uint)(buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24));
break;
default:
Console.WriteLine($"Bad block type {BlockType}");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
}
// Decode more of the block:
// run = min(what's available, what's needed)
this_run = BlockRemaining;
if (this_run > bytes_todo)
this_run = bytes_todo;
// Assume we decode exactly this_run bytes, for now
bytes_todo -= this_run;
BlockRemaining -= this_run;
// Decode at least this_run bytes
switch (BlockType)
{
case LZXBlockType.LZX_BLOCKTYPE_ALIGNED:
case LZXBlockType.LZX_BLOCKTYPE_VERBATIM:
while (this_run > 0)
{
main_element = (int)READ_HUFFSYM_MSB(MAINTREE_table, MAINTREE_len, LZX_MAINTREE_TABLEBITS, LZX_MAINTREE_MAXSYMBOLS);
if (main_element < LZX_NUM_CHARS)
{
// Literal: 0 to LZX_NUM_CHARS-1
Window[WindowPosition++] = (byte)main_element;
this_run--;
}
else
{
// Match: LZX_NUM_CHARS ((slot<<3) | length_header (3 bits))
main_element -= LZX_NUM_CHARS;
// Get match length
match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
if (match_length == LZX_NUM_PRIMARY_LENGTHS)
{
if (LENGTH_empty)
{
Console.WriteLine("LENGTH symbol needed but tree is empty");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
length_footer = (int)READ_HUFFSYM_MSB(LENGTH_table, LENGTH_len, LZX_LENGTH_TABLEBITS, LZX_LENGTH_MAXSYMBOLS);
match_length += length_footer;
}
match_length += LZX_MIN_MATCH;
// Get match offset
switch ((match_offset = (uint)(main_element >> 3)))
{
case 0:
match_offset = R[0];
break;
case 1:
match_offset = R[1];
R[1] = R[0];
R[0] = match_offset;
break;
case 2:
match_offset = R[2];
R[2] = R[0];
R[0] = match_offset;
break;
default:
if (BlockType == LZXBlockType.LZX_BLOCKTYPE_VERBATIM)
{
if (match_offset == 3)
{
match_offset = 1;
}
else
{
extra = ExtraBits(match_offset);
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset = (uint)(PositionBase(match_offset) - 2 + verbatim_bits);
}
}
else // LZX_BLOCKTYPE_ALIGNED
{
extra = ExtraBits(match_offset);
match_offset = (uint)(PositionBase(match_offset) - 2);
if (extra > 3) // >3: verbatim and aligned bits
{
extra -= 3;
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset += (uint)(verbatim_bits << 3);
aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
match_offset += (uint)aligned_bits;
}
else if (extra == 3) // 3: aligned bits only
{
aligned_bits = (int)READ_HUFFSYM_MSB(ALIGNED_table, ALIGNED_len, LZX_ALIGNED_TABLEBITS, LZX_ALIGNED_MAXSYMBOLS);
match_offset += (uint)aligned_bits;
}
else if (extra > 0) // 1-2: verbatim bits only
{
verbatim_bits = (int)READ_BITS_MSB(extra);
match_offset += (uint)verbatim_bits;
}
else // 0: not defined in LZX specification!
{
match_offset = 1;
}
}
// Update repeated offset LRU queue
R[2] = R[1];
R[1] = R[0];
R[0] = match_offset;
break;
}
// LZX DELTA uses max match length to signal even longer match
if (match_length == LZX_MAX_MATCH && IsDelta)
{
int extra_len = 0;
ENSURE_BITS(3); // 4 entry huffman tree
if (PEEK_BITS_MSB(1) == 0)
{
REMOVE_BITS_MSB(1); // '0' -> 8 extra length bits
extra_len = (int)READ_BITS_MSB(8);
}
else if (PEEK_BITS_MSB(2) == 2)
{
REMOVE_BITS_MSB(2); // '10' -> 10 extra length bits + 0x100
extra_len = (int)READ_BITS_MSB(10);
extra_len += 0x100;
}
else if (PEEK_BITS_MSB(3) == 6)
{
REMOVE_BITS_MSB(3); // '110' -> 12 extra length bits + 0x500
extra_len = (int)READ_BITS_MSB(12);
extra_len += 0x500;
}
else
{
REMOVE_BITS_MSB(3); // '111' -> 15 extra length bits
extra_len = (int)READ_BITS_MSB(15);
}
match_length += extra_len;
}
if ((WindowPosition + match_length) > WindowSize)
{
Console.WriteLine("Match ran over window wrap");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// TODO: This falls out of sync after (91,3)
// - Official program goes to (94, 3)
// - Ours goes to (95, 3)
// Copy match
rundest = WindowPosition;
i = match_length;
// Does match offset wrap the window?
if (match_offset > WindowPosition)
{
if (match_offset > Offset && (match_offset - WindowPosition) > ReferenceDataSize)
{
Console.WriteLine("Match offset beyond LZX stream");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// j = length from match offset to end of window
j = (int)match_offset - WindowPosition;
if (j > (int)WindowSize)
{
Console.WriteLine("Match offset beyond window boundaries");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
runsrc = (int)(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 = rundest - (int)match_offset;
while (i-- > 0)
{
Window[rundest++] = Window[runsrc++];
}
}
this_run -= match_length;
WindowPosition += match_length;
}
}
break;
case LZXBlockType.LZX_BLOCKTYPE_UNCOMPRESSED:
// As this_run is limited not to wrap a frame, this also means it
// won't wrap the window (as the window is a multiple of 32k)
rundest = WindowPosition;
WindowPosition += this_run;
while (this_run > 0)
{
if ((i = InputEnd - InputPointer) == 0)
{
READ_IF_NEEDED();
}
else
{
if (i > this_run)
i = this_run;
Array.Copy(InputBuffer, InputPointer, Window, rundest, i);
rundest += 1;
InputPointer += i;
this_run -= i;
}
}
break;
default:
return Error = Error.MSPACK_ERR_DECRUNCH; // Might as well
}
// Did the final match overrun our desired this_run length?
if (this_run < 0)
{
if ((uint)(-this_run) > BlockRemaining)
{
Console.Write($"Overrun went past end of block by {-this_run} ({BlockRemaining} remaining)");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
BlockRemaining -= -this_run;
}
}
// Streams don't extend over frame boundaries
if ((WindowPosition - FramePosition) != frame_size)
{
Console.WriteLine($"Decode beyond output frame limits {WindowPosition - FramePosition} != {frame_size}");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Re-align input bitstream
if (BitsLeft > 0)
ENSURE_BITS(16);
if ((BitsLeft & 15) != 0)
REMOVE_BITS_MSB(BitsLeft & 15);
// Check that we've used all of the previous frame first
if (OutputPointer != OutputEnd)
{
Console.Write($"{OutputEnd - OutputPointer} avail bytes, new {frame_size} frame");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
// Does this intel block _really_ need decoding?
if (IntelStarted && IntelFileSize != 0 && Frame < 32768 && frame_size > 10)
{
int data = 0;
int dataend = (int)frame_size - 10;
int curpos = (int)Offset;
int filesize = IntelFileSize;
int abs_off, rel_off;
// Copy E8 block to the e8 buffer and tweak if needed
WriteFromE8 = true;
OutputPointer = data;
Array.Copy(Window, FramePosition, E8Buffer, data, frame_size);
while (data < dataend)
{
if (E8Buffer[data++] != 0xE8)
{
curpos++;
continue;
}
abs_off = E8Buffer[data + 0] | (E8Buffer[data + 1] << 8) | (E8Buffer[data + 2] << 16) | (E8Buffer[data + 3] << 24);
if ((abs_off >= -curpos) && (abs_off < filesize))
{
rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
E8Buffer[data + 0] = (byte)rel_off;
E8Buffer[data + 1] = (byte)(rel_off >> 8);
E8Buffer[data + 2] = (byte)(rel_off >> 16);
E8Buffer[data + 3] = (byte)(rel_off >> 24);
}
data += 4;
curpos += 5;
}
}
else
{
WriteFromE8 = false;
OutputPointer = (int)FramePosition;
}
OutputEnd = (int)frame_size;
// Write a frame
i = (int)(out_bytes < frame_size ? out_bytes : frame_size);
if (System.Write(OutputFileHandle, WriteFromE8 ? E8Buffer : Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
Offset += i;
out_bytes -= i;
// Advance frame start position
FramePosition += (uint)frame_size;
Frame++;
// Wrap window / frame position pointers
if (WindowPosition == WindowSize)
WindowPosition = 0;
if (FramePosition == WindowSize)
FramePosition = 0;
}
if (out_bytes > 0)
{
Console.WriteLine($"{out_bytes} bytes left to output");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error.MSPACK_ERR_OK;
}
#endregion
#region Helpers
/// <summary>
/// States how many bits of offset-from-base data is needed.
/// </summary>
private int ExtraBits(uint offset)
{
if (offset < 4)
return 0;
else if (offset >= 4 && offset < 36)
return (int)Math.Floor((double)(offset / 2)) - 1;
else // offset >= 36
return 17;
}
/// <summary>
/// An index to the position slot bases
/// </summary>
private long PositionBase(uint offset)
{
if (offset < 0 || offset >= 290)
return 0;
return LZXPositionBase[offset];
// TODO: Replace naieve recursive implementation
if (offset == 0)
return 0;
else
return PositionBase(offset - 1) + (1 << ExtraBits(offset - 1));
}
/// <summary>
/// Reset the internal state
/// </summary>
private void ResetState()
{
R[0] = 1;
R[1] = 1;
R[2] = 1;
HeaderRead = 0;
BlockRemaining = 0;
BlockType = LZXBlockType.LZX_BLOCKTYPE_INVALID0;
WriteFromE8 = false;
// Initialise tables to 0 (because deltas will be applied to them)
for (int i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++)
{
MAINTREE_len[i] = 0;
}
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
LENGTH_len[i] = 0;
}
}
#endregion
}
}

View File

@@ -1,34 +0,0 @@
/* 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 partial class LZX : CompressionStream
{
/// <inheritdoc/>
public override void READ_BYTES()
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return;
byte b0 = InputBuffer[InputPointer++];
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return;
byte b1 = InputBuffer[InputPointer++];
INJECT_BITS_MSB((b1 << 8) | b0, 16);
}
}
}

View File

@@ -1,132 +0,0 @@
/* 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;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class LZX : CompressionStream
{
/// <inheritdoc/>
public override Error HUFF_ERROR() => Error.MSPACK_ERR_DECRUNCH;
/// <summary>
/// BUILD_TABLE(tbl) builds a huffman lookup table from code lengths
/// </summary>
private Error BUILD_TABLE(ushort[] table, byte[] lengths, int tablebits, int maxsymbols)
{
if (!MakeDecodeTableMSB(maxsymbols, tablebits, lengths, table))
{
Console.WriteLine("Failed to build table");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
return Error = Error.MSPACK_ERR_OK;
}
private Error BUILD_TABLE_MAYBE_EMPTY()
{
LENGTH_empty = false;
if (!MakeDecodeTableMSB(LZX_LENGTH_MAXSYMBOLS, LZX_LENGTH_TABLEBITS, LENGTH_len, LENGTH_table))
{
for (int i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)
{
if (LENGTH_len[i] > 0)
{
Console.WriteLine("Failed to build LENGTH table");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
}
// Empty tree - allow it, but don't decode symbols with it
LENGTH_empty = true;
}
return Error = Error.MSPACK_ERR_OK;
}
/// <summary>
/// READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
/// first to last in the given table. The code lengths are stored in their
/// own special LZX way.
/// </summary>
private Error ReadLengths(byte[] lens, uint first, uint last)
{
uint x, y;
int z;
// Read lengths for pretree (20 symbols, lengths stored in fixed 4 bits)
for (x = 0; x < LZX_PRETREE_MAXSYMBOLS; x++)
{
y = (uint)READ_BITS_MSB(4);
PRETREE_len[x] = (byte)y;
}
BUILD_TABLE(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
if (Error != Error.MSPACK_ERR_OK)
return Error;
for (x = first; x < last;)
{
z = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
if (z == 17)
{
// Code = 17, run of ([read 4 bits]+4) zeros
y = (uint)READ_BITS_MSB(4);
y += 4;
while (y-- > 0)
{
lens[x++] = 0;
}
}
else if (z == 18)
{
// Code = 18, run of ([read 5 bits]+20) zeros
y = (uint)READ_BITS_MSB(5);
y += 20;
while (y-- > 0)
{
lens[x++] = 0;
}
}
else if (z == 19)
{
// Code = 19, run of ([read 1 bit]+4) [read huffman symbol]
y = (uint)READ_BITS_MSB(1);
y += 4;
z = (int)READ_HUFFSYM_MSB(PRETREE_table, PRETREE_len, LZX_PRETREE_TABLEBITS, LZX_PRETREE_MAXSYMBOLS);
z = lens[x] - z;
if (z < 0)
z += 17;
while (y-- > 0)
{
lens[x++] = (byte)z;
}
}
else
{
// Code = 0 to 16, delta current length entry
z = lens[x] - z;
if (z < 0)
z += 17;
lens[x++] = (byte)z;
}
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,133 +0,0 @@
/* 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 static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class LZX : 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 int 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[] R { get; set; } = new uint[3];
/// <summary>
/// Uncompressed length of this LZX block
/// </summary>
public int BlockLength { get; set; }
/// <summary>
/// Uncompressed bytes still left to decode
/// </summary>
public int 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_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] MAINTREE_len { get; set; } = new byte[LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] LENGTH_len { get; set; } = new byte[LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
public byte[] ALIGNED_len { get; set; } = new byte[LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY];
#endregion
#region Huffman decoding tables
public ushort[] PRETREE_table { get; set; } = new ushort[(1 << LZX_PRETREE_TABLEBITS) + (LZX_PRETREE_MAXSYMBOLS * 2)];
public ushort[] MAINTREE_table { get; set; } = new ushort[(1 << LZX_MAINTREE_TABLEBITS) + (LZX_MAINTREE_MAXSYMBOLS * 2)];
public ushort[] LENGTH_table { get; set; } = new ushort[(1 << LZX_LENGTH_TABLEBITS) + (LZX_LENGTH_MAXSYMBOLS * 2)];
public ushort[] ALIGNED_table { get; set; } = new ushort[(1 << LZX_ALIGNED_TABLEBITS) + (LZX_ALIGNED_MAXSYMBOLS * 2)];
#endregion
public bool LENGTH_empty { get; set; }
public byte[] E8Buffer { get; set; } = new byte[LZX_FRAME_SIZE];
public bool WriteFromE8 { get; set; }
}
}

View File

@@ -1,613 +0,0 @@
/* 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;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class MSZIP
{
/// <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 MSZIP Init(SystemImpl system, FileStream input, FileStream 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
return new MSZIP
{
// Allocate input buffer
InputBuffer = new byte[input_buffer_size],
// Initialise decompression state
System = system,
InputFileHandle = input,
OutputFileHandle = output,
InputBufferSize = (uint)input_buffer_size,
EndOfInput = 0,
Error = Error.MSPACK_ERR_OK,
RepairMode = repair_mode,
InputPointer = 0,
InputEnd = 0,
OutputPointer = 0,
OutputEnd = 0,
BitBuffer = 0,
BitsLeft = 0,
};
}
/// <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 Error Decompress(long out_bytes)
{
int i, readState;
Error error;
// Easy answers
if (out_bytes < 0)
return Error.MSPACK_ERR_ARGS;
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Flush out any stored-up bytes before we begin
i = OutputEnd - OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i > 0)
{
if (System.Write(OutputFileHandle, Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
while (out_bytes > 0)
{
// Skip to next read 'CK' header
i = BitsLeft & 7;
// Align to bytestream
REMOVE_BITS_LSB(i);
readState = 0;
do
{
i = (int)READ_BITS_LSB(8);
if (i == 'C')
readState = 1;
else if ((readState == 1) && (i == 'K'))
readState = 2;
else
readState = 0;
} while (readState != 2);
// Inflate a block, repair and realign if necessary
WindowPosition = 0;
BytesOutput = 0;
if ((error = Inflate()) != Error.MSPACK_ERR_OK)
{
Console.WriteLine($"Inflate error {error}");
if (RepairMode)
{
// Recover partially-inflated buffers
if (BytesOutput == 0 && WindowPosition > 0)
FlushWindow(WindowPosition);
System.Message(null, $"MSZIP error, {MSZIP_FRAME_SIZE - BytesOutput} bytes of data lost.");
for (i = BytesOutput; i < MSZIP_FRAME_SIZE; i++)
{
Window[i] = 0x00;
}
BytesOutput = MSZIP_FRAME_SIZE;
}
else
{
return Error = error;
}
}
OutputPointer = 0;
OutputEnd = BytesOutput;
// Write a frame
i = (out_bytes < BytesOutput) ? (int)out_bytes : BytesOutput;
if (System.Write(OutputFileHandle, Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
// mspack errors (i.e. read errors) are fatal and can't be recovered
if ((error > 0) && RepairMode)
return error;
OutputPointer += i;
out_bytes -= i;
}
if (out_bytes > 0)
{
Console.WriteLine($"Bytes left to output: {out_bytes}");
return 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 Error DecompressKWAJ()
{
int i, block_len;
Error error;
// Unpack blocks until block_len == 0
for (; ; )
{
// Align to bytestream, read block_len
i = BitsLeft & 7;
REMOVE_BITS_LSB(i);
block_len = (int)READ_BITS_LSB(8);
i = (int)READ_BITS_LSB(8);
block_len |= i << 8;
if (block_len == 0)
break;
// Read "CK" header
i = (int)READ_BITS_LSB(8);
if (i != 'C')
return Error.MSPACK_ERR_DATAFORMAT;
i = (int)READ_BITS_LSB(8);
if (i != 'K')
return Error.MSPACK_ERR_DATAFORMAT;
// Inflate block
WindowPosition = 0;
BytesOutput = 0;
if ((error = Inflate()) != Error.MSPACK_ERR_OK)
{
Console.WriteLine($"Inflate error {error}");
return Error = (error > 0) ? error : Error.MSPACK_ERR_DECRUNCH;
}
// Write inflated block
try { System.Write(OutputFileHandle, Window, 0, BytesOutput); }
catch { return Error = Error.MSPACK_ERR_WRITE; }
}
return Error.MSPACK_ERR_OK;
}
private Error ReadLens()
{
// 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];
uint lit_codes, dist_codes, code, last_code = 0, bitlen_codes, i, run;
// Read the number of codes
lit_codes = (uint)READ_BITS_LSB(5);
lit_codes += 257;
dist_codes = (uint)READ_BITS_LSB(5);
dist_codes += 1;
bitlen_codes = (uint)READ_BITS_LSB(5);
bitlen_codes += 4;
if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS)
return Error.INF_ERR_SYMLENS;
if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS)
return Error.INF_ERR_SYMLENS;
// Read in the bit lengths in their unusual order
for (i = 0; i < bitlen_codes; i++)
{
bl_len[BitLengthOrder[i]] = (byte)READ_BITS_LSB(3);
}
while (i < 19)
{
bl_len[BitLengthOrder[i++]] = 0;
}
// Create decoding table with an immediate lookup
if (!CompressionStream.MakeDecodeTableLSB(19, 7, bl_len, bl_table))
return Error.INF_ERR_BITLENTBL;
// Read literal / distance code lengths
for (i = 0; i < (lit_codes + dist_codes); i++)
{
// Single-level huffman lookup
ENSURE_BITS(7);
code = bl_table[PEEK_BITS_LSB(7)];
REMOVE_BITS_LSB(bl_len[code]);
if (code < 16)
{
lens[i] = (byte)(last_code = code);
}
else
{
switch (code)
{
case 16:
run = (uint)READ_BITS_LSB(2);
run += 3;
code = last_code;
break;
case 17:
run = (uint)READ_BITS_LSB(3);
run += 3;
code = 0;
break;
case 18:
run = (uint)READ_BITS_LSB(7);
run += 11;
code = 0;
break;
default:
Console.WriteLine($"Bad code!: {code}");
return Error.INF_ERR_BADBITLEN;
}
if ((i + run) > (lit_codes + dist_codes))
return Error.INF_ERR_BITOVERRUN;
while (run-- > 0)
{
lens[i++] = (byte)code;
}
i--;
}
}
// Copy LITERAL code lengths and clear any remaining
i = lit_codes;
Array.Copy(lens, 0, LITERAL_len, 0, i);
while (i < MSZIP_LITERAL_MAXSYMBOLS)
{
LITERAL_len[i++] = 0;
}
i = dist_codes;
Array.Copy(lens, lit_codes, DISTANCE_len, 0, i);
while (i < MSZIP_DISTANCE_MAXSYMBOLS)
{
DISTANCE_len[i++] = 0;
}
return 0;
}
/// <summary>
/// A clean implementation of RFC 1951 / inflate
/// </summary>
private Error Inflate()
{
uint last_block, block_type, distance, length, this_run, i;
Error err;
ushort sym;
do
{
// Read in last block bit
last_block = (uint)READ_BITS_LSB(1);
// Read in block type
block_type = (uint)READ_BITS_LSB(2);
if (block_type == 0)
{
// Uncompressed block
byte[] lens_buf = new byte[4];
// Go to byte boundary
i = (uint)(BitsLeft & 7);
REMOVE_BITS_LSB((int)i);
// Read 4 bytes of data, emptying the bit-buffer if necessary
for (i = 0; (BitsLeft >= 8); i++)
{
if (i == 4)
return Error.INF_ERR_BITBUF;
lens_buf[i] = (byte)PEEK_BITS_LSB(8);
REMOVE_BITS_LSB(8);
}
if (BitsLeft != 0)
return Error.INF_ERR_BITBUF;
while (i < 4)
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return Error;
lens_buf[i++] = InputBuffer[InputPointer++];
}
// Get the length and its complement
length = (ushort)(lens_buf[0] | (lens_buf[1] << 8));
i = (ushort)(lens_buf[2] | (lens_buf[3] << 8));
ushort compl = (ushort)(~i & 0xFFFF);
if (length != compl)
return Error.INF_ERR_COMPLEMENT;
// Read and copy the uncompressed data into the window
while (length > 0)
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return Error;
this_run = length;
if (this_run > (uint)(InputEnd - InputPointer))
this_run = (uint)(InputEnd - InputPointer);
if (this_run > (MSZIP_FRAME_SIZE - WindowPosition))
this_run = MSZIP_FRAME_SIZE - WindowPosition;
Array.Copy(InputBuffer, InputPointer, Window, WindowPosition, this_run);
WindowPosition += this_run;
InputPointer += (int)this_run;
length -= this_run;
err = FLUSH_IF_NEEDED();
if (err != Error.MSPACK_ERR_OK)
return err;
}
}
else if ((block_type == 1) || (block_type == 2))
{
// Huffman-compressed LZ77 block
uint match_posn, code;
if (block_type == 1)
{
// Block with fixed Huffman codes
i = 0;
while (i < 144)
{
LITERAL_len[i++] = 8;
}
while (i < 256)
{
LITERAL_len[i++] = 9;
}
while (i < 280)
{
LITERAL_len[i++] = 7;
}
while (i < 288)
{
LITERAL_len[i++] = 8;
}
for (i = 0; i < 32; i++)
{
DISTANCE_len[i] = 5;
}
}
else
{
// Block with dynamic Huffman codes
if ((err = ReadLens()) != Error.MSPACK_ERR_OK)
return err;
}
// Now huffman lengths are read for either kind of block,
// create huffman decoding tables
if (!CompressionStream.MakeDecodeTableLSB(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, LITERAL_len, LITERAL_table))
return Error.INF_ERR_LITERALTBL;
if (!CompressionStream.MakeDecodeTableLSB(MSZIP_DISTANCE_MAXSYMBOLS, MSZIP_DISTANCE_TABLEBITS, DISTANCE_len, DISTANCE_table))
return Error.INF_ERR_DISTANCETBL;
// Decode forever until end of block code
for (; ; )
{
code = (uint)READ_HUFFSYM_LSB(LITERAL_table, LITERAL_len, MSZIP_LITERAL_TABLEBITS, MSZIP_LITERAL_MAXSYMBOLS);
if (code < 256)
{
Window[WindowPosition++] = (byte)code;
err = FLUSH_IF_NEEDED();
if (err != Error.MSPACK_ERR_OK)
return err;
}
else if (code == 256)
{
// END OF BLOCK CODE: loop break point
break;
}
else
{
code -= 257; // Codes 257-285 are matches
if (code >= 29)
return Error.INF_ERR_LITCODE; // Codes 286-287 are illegal
length = (uint)READ_BITS_T_LSB(LiteralExtraBits[code]);
length += LiteralLengths[code];
code = (uint)READ_HUFFSYM_LSB(DISTANCE_table, DISTANCE_len, MSZIP_DISTANCE_TABLEBITS, MSZIP_DISTANCE_MAXSYMBOLS);
if (code >= 30)
return Error.INF_ERR_DISTCODE;
distance = (uint)READ_BITS_T_LSB(DistanceExtraBits[code]);
distance += DistanceOffsets[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 > WindowPosition) ? MSZIP_FRAME_SIZE : 0) + WindowPosition - distance;
// Copy match
if (length < 12)
{
// Short match, use slower loop but no loop setup code
while (length-- > 0)
{
Window[WindowPosition++] = Window[match_posn++];
match_posn &= MSZIP_FRAME_SIZE - 1;
err = FLUSH_IF_NEEDED();
if (err != Error.MSPACK_ERR_OK)
return err;
}
}
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 - match_posn;
if ((WindowPosition + this_run) > MSZIP_FRAME_SIZE)
this_run = MSZIP_FRAME_SIZE - WindowPosition;
rundest = (int)WindowPosition;
WindowPosition += this_run;
runsrc = (int)match_posn;
match_posn += this_run;
length -= this_run;
while (this_run-- > 0)
{
Window[rundest++] = Window[runsrc++];
}
if (match_posn == MSZIP_FRAME_SIZE)
match_posn = 0;
err = FLUSH_IF_NEEDED();
if (err != Error.MSPACK_ERR_OK)
return err;
} while (length > 0);
}
}
}
}
else
{
// block_type == 3 -- bad block type
return Error.INF_ERR_BLOCKTYPE;
}
} while (last_block == 0);
// Flush the remaining data
if (WindowPosition > 0)
{
if (FlushWindow(WindowPosition) != Error.MSPACK_ERR_OK)
return Error.INF_ERR_FLUSH;
}
// Return success
return Error.MSPACK_ERR_OK;
}
private Error FLUSH_IF_NEEDED()
{
if (WindowPosition == MSZIP_FRAME_SIZE)
{
if (FlushWindow(MSZIP_FRAME_SIZE) != Error.MSPACK_ERR_OK)
return Error.INF_ERR_FLUSH;
WindowPosition = 0;
}
return Error.MSPACK_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 Error FlushWindow(uint data_flushed)
{
BytesOutput += (int)data_flushed;
if (BytesOutput > MSZIP_FRAME_SIZE)
{
Console.WriteLine($"Overflow: {data_flushed} bytes flushed, total is now {BytesOutput}");
return Error.MSPACK_ERR_ARGS;
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,27 +0,0 @@
/* 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
*/
namespace LibMSPackSharp.Compression
{
public partial class MSZIP : CompressionStream
{
/// <inheritdoc/>
public override void READ_BYTES()
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return;
INJECT_BITS_LSB(InputBuffer[InputPointer++], 8);
}
}
}

View File

@@ -1,20 +0,0 @@
/* 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
*/
namespace LibMSPackSharp.Compression
{
public partial class MSZIP : CompressionStream
{
/// <inheritdoc/>
public override Error HUFF_ERROR() => Error.INF_ERR_HUFFSYM;
}
}

View File

@@ -1,47 +0,0 @@
/* 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 static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class MSZIP : CompressionStream
{
/// <summary>
/// 32kb history window
/// </summary>
public byte[] Window { get; set; } = new byte[MSZIP_FRAME_SIZE];
/// <summary>
/// Offset within window
/// </summary>
public uint WindowPosition { get; set; }
public bool RepairMode { get; set; }
public int BytesOutput { get; set; }
#region Huffman code lengths
public byte[] LITERAL_len { get; set; } = new byte[MSZIP_LITERAL_MAXSYMBOLS];
public byte[] DISTANCE_len { get; set; } = new byte[MSZIP_DISTANCE_MAXSYMBOLS];
#endregion
#region Huffman decoding tables
public ushort[] LITERAL_table { get; set; } = new ushort[MSZIP_LITERAL_TABLESIZE];
public ushort[] DISTANCE_table { get; set; } = new ushort[MSZIP_DISTANCE_TABLESIZE];
#endregion
}
}

View File

@@ -1,46 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System.IO;
namespace LibMSPackSharp.Compression
{
public partial class None
{
public static None Init(SystemImpl sys, FileStream input, FileStream output, int bufsize)
{
return new None()
{
System = sys,
InputFileHandle = input,
OutputFileHandle = output,
Buffer = new byte[bufsize],
BufferSize = bufsize,
};
}
public Error Decompress(long bytes)
{
int run;
while (bytes > 0)
{
run = (bytes > BufferSize) ? BufferSize : (int)bytes;
if (System.Read(InputFileHandle, Buffer, 0, run) != run)
return Error.MSPACK_ERR_READ;
if (System.Write(OutputFileHandle, Buffer, 0, run) != run)
return Error.MSPACK_ERR_WRITE;
bytes -= run;
}
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,21 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.Compression
{
/// <summary>
/// The "not compressed" method decompressor
/// </summary>
public partial class None : BaseDecompressState
{
public byte[] Buffer { get; set; }
public int BufferSize { get; set; }
}
}

View File

@@ -1,542 +0,0 @@
/* 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;
using System.IO;
using static LibMSPackSharp.Compression.Constants;
namespace LibMSPackSharp.Compression
{
public partial class QTM
{
/// <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 QTM Init(SystemImpl system, FileStream input, FileStream 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
QTM qtm = new QTM()
{
// Allocate decompression window and input buffer
Window = new byte[window_size],
InputBuffer = new byte[input_buffer_size],
// Initialise decompression state
System = system,
InputFileHandle = input,
OutputFileHandle = output,
InputBufferSize = (uint)input_buffer_size,
WindowSize = window_size,
WindowPosition = 0,
FrameTODO = QTM_FRAME_SIZE,
HeaderRead = 0,
Error = Error.MSPACK_ERR_OK,
InputPointer = 0,
InputEnd = 0,
OutputPointer = 0,
OutputEnd = 0,
BitBuffer = 0,
BitsLeft = 0,
EndOfInput = 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;
qtm.InitModel(qtm.Model0, qtm.Model0Symbols, 0, 64);
qtm.InitModel(qtm.Model1, qtm.Model1Symbols, 64, 64);
qtm.InitModel(qtm.Model2, qtm.Model2Symbols, 128, 64);
qtm.InitModel(qtm.Model3, qtm.Model3Symbols, 192, 64);
qtm.InitModel(qtm.Model4, qtm.Model4Symbols, 0, (i > 24) ? 24 : i);
qtm.InitModel(qtm.Model5, qtm.Model5Symbols, 0, (i > 36) ? 36 : i);
qtm.InitModel(qtm.Model6, qtm.Model6Symbols, 0, i);
qtm.InitModel(qtm.Model6Len, qtm.Model6LenSymbols, 0, 27);
qtm.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 Error Decompress(long out_bytes)
{
uint frame_todo, frame_end, window_posn, match_offset, range;
byte[] window;
int runsrc, rundest;
int i, j, selector, extra, sym, match_length;
ushort H, L, C, symf;
// Easy answers
if (out_bytes < 0)
return Error.MSPACK_ERR_ARGS;
if (Error != Error.MSPACK_ERR_OK)
return Error;
// Flush out any stored-up bytes before we begin
i = OutputEnd - OutputPointer;
if (i > out_bytes)
i = (int)out_bytes;
if (i > 0)
{
if (System.Write(OutputFileHandle, Window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
out_bytes -= i;
}
if (out_bytes == 0)
return Error.MSPACK_ERR_OK;
// Restore local state
window = Window;
window_posn = WindowPosition;
frame_todo = FrameTODO;
H = High;
L = Low;
C = Current;
// While we do not have enough decoded bytes in reserve
while ((OutputEnd - OutputPointer) < out_bytes)
{
// Read header if necessary. Initialises H, L and C
if (HeaderRead != 0)
{
H = 0xFFFF;
L = 0;
C = (ushort)READ_BITS_MSB(16);
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 - (OutputEnd - OutputPointer)));
if ((window_posn + frame_todo) < frame_end)
frame_end = window_posn + frame_todo;
if (frame_end > WindowSize)
frame_end = WindowSize;
while (window_posn < frame_end)
{
selector = GET_SYMBOL(Model7, ref H, ref L, ref C);
if (selector < 4)
{
// Literal byte
QTMDModel mdl;
switch (selector)
{
case 0:
mdl = Model0;
break;
case 1:
mdl = Model1;
break;
case 2:
mdl = Model2;
break;
case 3:
default:
mdl = Model3;
break;
}
sym = GET_SYMBOL(mdl, ref H, ref L, ref C);
window[window_posn++] = (byte)sym;
frame_todo--;
}
else
{
// Match repeated string
switch (selector)
{
// Selector 4 = fixed length match (3 bytes)
case 4:
sym = GET_SYMBOL(Model4, ref H, ref L, ref C);
extra = (int)READ_MANY_BITS_MSB(QTMExtraBits[sym]);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
match_length = 3;
break;
// Selector 5 = fixed length match (4 bytes)
case 5:
sym = GET_SYMBOL(Model5, ref H, ref L, ref C);
extra = (int)READ_MANY_BITS_MSB(QTMExtraBits[sym]);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
match_length = 4;
break;
// Selector 6 = variable length match
case 6:
sym = GET_SYMBOL(Model6Len, ref H, ref L, ref C);
extra = (int)READ_MANY_BITS_MSB(QTMLengthExtra[sym]);
match_length = QTMLengthBase[sym] + extra + 5;
sym = GET_SYMBOL(Model6, ref H, ref L, ref C);
extra = (int)READ_MANY_BITS_MSB(QTMExtraBits[sym]);
match_offset = (uint)(QTMPositionBase[sym] + extra + 1);
break;
default:
// Should be impossible, model7 can only return 0-6
Console.WriteLine($"Got {selector} from selector");
return 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) > WindowSize)
{
// Copy first part of match, before window end
i = (int)(WindowSize - window_posn);
j = (int)(window_posn - match_offset);
while (i-- > 0)
{
window[rundest++] = window[j++ & (WindowSize - 1)];
}
// Flush currently stored data
i = (int)(WindowSize - 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 Error = Error.MSPACK_ERR_DECRUNCH;
}
if (System.Write(OutputFileHandle, window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
OutputPointer = 0;
OutputEnd = 0;
// Copy second part of match, after window wrap
rundest = 0;
i = (int)(match_length - (WindowSize - window_posn));
while (i-- > 0)
{
window[rundest++] = window[j++ & (WindowSize - 1)];
}
window_posn = (uint)(window_posn + match_length - 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)WindowSize)
{
Console.WriteLine("Match offset beyond window boundaries");
return Error = Error.MSPACK_ERR_DECRUNCH;
}
runsrc = (int)(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;
}
}
}
OutputEnd = (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 Error = Error.MSPACK_ERR_DECRUNCH;
}
// Another frame completed?
if (frame_todo == 0)
{
// Re-align input
if ((BitsLeft & 7) != 0)
REMOVE_BITS_MSB(BitsLeft & 7);
// 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
{
i = (int)READ_BITS_MSB(8);
} while (i != 0xFF);
HeaderRead = 0;
frame_todo = QTM_FRAME_SIZE;
}
// Window wrap?
if (window_posn == WindowSize)
{
// Flush all currently stored data
i = (OutputEnd - OutputPointer);
// Break out if we have more than enough to finish this request
if (i >= out_bytes)
break;
if (System.Write(OutputFileHandle, window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
out_bytes -= i;
OutputPointer = 0;
OutputEnd = 0;
window_posn = 0;
}
}
if (out_bytes > 0)
{
i = (int)out_bytes;
if (System.Write(OutputFileHandle, window, OutputPointer, i) != i)
return Error = Error.MSPACK_ERR_WRITE;
OutputPointer += i;
}
// Store local state
WindowPosition = window_posn;
FrameTODO = frame_todo;
High = H;
Low = L;
Current = C;
return Error.MSPACK_ERR_OK;
}
private ushort GET_SYMBOL(QTMDModel model, ref ushort H, ref ushort L, ref ushort C)
{
uint range = (uint)((H - L) & 0xFFFF) + 1;
ushort symf = (ushort)(((((C - L + 1) * model.Syms[0].CumulativeFrequency) - 1) / range) & 0xFFFF);
int i = 1;
for (; i < model.Entries; i++)
{
if (model.Syms[i].CumulativeFrequency <= symf)
break;
}
ushort temp = model.Syms[i - 1].Sym;
range = (uint)(H - L) + 1;
symf = model.Syms[0].CumulativeFrequency;
H = (ushort)(L + ((model.Syms[i - 1].CumulativeFrequency * range) / symf) - 1);
L = (ushort)(L + ((model.Syms[i].CumulativeFrequency * range) / symf));
do
{
model.Syms[--i].CumulativeFrequency += 8;
} while (i > 0);
if (model.Syms[0].CumulativeFrequency > 3800)
UpdateModel(model);
while (true)
{
if ((L & 0x8000) != (H & 0x8000))
{
if ((L & 0x4000) != 0 && (H & 0x4000) == 0)
{
// Underflow case
C ^= 0x4000;
L &= 0x3FFF;
H |= 0x4000;
}
else
{
break;
}
}
L <<= 1;
H = (ushort)((H << 1) | 1);
ENSURE_BITS(1);
C = (ushort)((C << 1) | (PEEK_BITS_MSB(1)));
REMOVE_BITS_MSB(1);
}
return temp;
}
private 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].CumulativeFrequency >>= 1;
if (model.Syms[i].CumulativeFrequency <= model.Syms[i + 1].CumulativeFrequency)
model.Syms[i].CumulativeFrequency = (ushort)(model.Syms[i + 1].CumulativeFrequency + 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].CumulativeFrequency -= model.Syms[i + 1].CumulativeFrequency;
model.Syms[i].CumulativeFrequency++; // Avoid losing things entirely
model.Syms[i].CumulativeFrequency >>= 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].CumulativeFrequency < model.Syms[j].CumulativeFrequency)
{
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].CumulativeFrequency += model.Syms[i + 1].CumulativeFrequency;
}
}
}
private 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].CumulativeFrequency = (ushort)(len - i);
}
}
}
}

View File

@@ -1,34 +0,0 @@
/* 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 partial class QTM : CompressionStream
{
/// <inheritdoc/>
public override void READ_BYTES()
{
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return;
byte b0 = InputBuffer[InputPointer++];
READ_IF_NEEDED();
if (Error != Error.MSPACK_ERR_OK)
return;
byte b1 = InputBuffer[InputPointer++];
INJECT_BITS_MSB((b0 << 8) | b1, 16);
}
}
}

View File

@@ -1,22 +0,0 @@
/* 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
*/
using System;
namespace LibMSPackSharp.Compression
{
public partial class QTM : CompressionStream
{
/// <inheritdoc/>
public override Error HUFF_ERROR() => throw new NotImplementedException();
}
}

View File

@@ -1,123 +0,0 @@
/* 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 partial class QTM : 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];
}
}

View File

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

@@ -1,21 +0,0 @@
/* 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 CumulativeFrequency { get; set; }
}
}

View File

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

View File

@@ -1,293 +0,0 @@
/* 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
{
#region MSPACK Errors
/// <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,
#endregion
#region Inflate Errors
/// <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,
#endregion
}
/// <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

@@ -1,24 +0,0 @@
/* 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
{
// TODO
public class Compressor
{
public SystemImpl System { get; set; }
}
}

View File

@@ -1,23 +0,0 @@
/* 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 : BaseDecompressor
{
// TODO
}
}

View File

@@ -1,119 +0,0 @@
/* 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
{
#region Fields
public SystemImpl System { get; set; }
/// <remarks>
/// !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!!
/// </remarks>
public int[] Param { get; set; } = new int[2];
public Error Error { get; set; }
#endregion
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// KWAJ compressed file format.The KWAJ compression format is quick
/// but gives poor compression.It is possible for the compressed output
/// file to be larger than the input file.
/// </summary>
/// <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(Parameters, int)"/>
public Error Compress(string input, string output, long length) => throw new NotImplementedException();
/// <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="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 Error SetParam(Parameters param, int value) => throw new NotImplementedException();
/// <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="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 Error SetFilename(string filename) => throw new NotImplementedException();
/// <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="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 Error SetExtraData(byte[] data, int pointer, int bytes) => throw new NotImplementedException();
}
}

View File

@@ -1,352 +0,0 @@
/* 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;
using System.Text;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
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 : BaseDecompressor
{
#region Public Functionality
/// <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="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(Header)"/>
public Header Open(string filename)
{
FileStream fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
if (fh == null)
{
Error = Error.MSPACK_ERR_OPEN;
return null;
}
Header hdr = new Header() { FileHandle = fh };
Error = ReadHeaders(fh, hdr);
if (Error != Error.MSPACK_ERR_OK)
{
System.Close(fh);
hdr = null;
}
return hdr;
}
/// <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="hdr">The KWAJ file to close</param>
/// <see cref="Open(string)"/>
public void Close(Header hdr)
{
if (System == null || hdr == null)
return;
// Close the file handle associated
System.Close(hdr.FileHandle);
Error = Error.MSPACK_ERR_OK;
}
/// <summary>
/// Extracts the compressed data from a KWAJ file.
///
/// This decompresses the compressed KWAJ data stream and writes it to
/// an output file.
/// </summary>
/// <param name="hdr">The KWAJ file to extract data from</param>
/// <param name="filename">
/// the filename to write the decompressed data to. This
/// is passed directly to mspack_system::open().
/// </param>
/// <returns>an error code, or MSPACK_ERR_OK if successful</returns>
public Error Extract(Header hdr, string filename)
{
FileStream fh = hdr?.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
if (System.Seek(fh, hdr.KWAJHeader.DataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh;
if ((outfh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null)
return Error = Error.MSPACK_ERR_OPEN;
Error = Error.MSPACK_ERR_OK;
// Decompress based on format
if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_NONE ||
hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
// NONE is a straight copy. XOR is a copy xored with 0xFF
byte[] buf = new byte[KWAJ_INPUT_SIZE];
int read, i;
while ((read = System.Read(fh, buf, 0, KWAJ_INPUT_SIZE)) > 0)
{
if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_XOR)
{
for (i = 0; i < read; i++)
{
buf[i] ^= 0xFF;
}
}
if (System.Write(outfh, buf, 0, read) != read)
{
Error = Error.MSPACK_ERR_WRITE;
break;
}
}
if (read < 0)
Error = Error.MSPACK_ERR_READ;
}
else if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_SZDD)
{
Error = LZSS.Decompress(System, fh, outfh, KWAJ_INPUT_SIZE, LZSSMode.LZSS_MODE_EXPAND);
}
else if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_LZH)
{
LZHKWAJ lzh = LZHKWAJ.Init(System, fh, outfh);
Error = (lzh != null) ? lzh.Decompress() : Error.MSPACK_ERR_NOMEMORY;
}
else if (hdr.KWAJHeader.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP)
{
MSZIP zip = MSZIP.Init(System, fh, outfh, KWAJ_INPUT_SIZE, false);
Error = (zip != null) ? zip.DecompressKWAJ() : Error.MSPACK_ERR_NOMEMORY;
}
else
{
Error = Error.MSPACK_ERR_DATAFORMAT;
}
// Close output file
System.Close(outfh);
return Error;
}
/// <summary>
/// Decompresses an KWAJ file to an output file in one step.
///
/// 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 Error Decompress(string input, string output)
{
Header hdr = Open(input);
if (hdr == null)
return Error;
Error error = Extract(hdr, output);
Close(hdr);
return Error = error;
}
#endregion
#region Helpers
/// <summary>
/// Reads the headers of a KWAJ format file
/// </summary>
private Error ReadHeaders(FileStream fh, Header hdr)
{
// Read in the header
byte[] buf = new byte[16];
if (System.Read(fh, buf, 0, _KWAJHeader.Size) != _KWAJHeader.Size)
return Error.MSPACK_ERR_READ;
// Create a new header based on that
Error err = _KWAJHeader.Create(buf, out _KWAJHeader kwajHeader);
if (err != Error.MSPACK_ERR_OK)
return Error = err;
// Assign the header
hdr.KWAJHeader = kwajHeader;
// Basic header fields
hdr.Length = 0;
hdr.Filename = null;
hdr.Extra = null;
hdr.ExtraLength = 0;
// Optional headers
// 4 bytes: length of unpacked file
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASLENGTH))
{
if (System.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
// 2 bytes: unknown purpose
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN1))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
}
// 2 bytes: length of section, then [length] bytes: unknown purpose
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASUNKNOWN2))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
int i = BitConverter.ToUInt16(buf, 0);
if (System.Seek(fh, i, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
}
// Filename and extension
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME) || hdr.KWAJHeader.Flags.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.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILENAME))
{
// Read and copy up to 9 bytes of a null terminated string
if ((len = System.Read(fh, buf, 0, 9)) < 2)
return Error.MSPACK_ERR_READ;
int i = 0;
for (; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 9 bytes with no null terminator, reject it
if (i == 9 && buf[8] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (System.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
// Copy extension if present
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASFILEEXT))
{
fn[fnPtr++] = '.';
// Read and copy up to 4 bytes of a null terminated string
if ((len = System.Read(fh, buf, 0, 4)) < 2)
return Error.MSPACK_ERR_READ;
int i = 0;
for (; i < len; i++)
{
if ((fn[fnPtr++] = (char)buf[i]) == '\0')
break;
}
// If string was 4 bytes with no null terminator, reject it
if (i == 4 && buf[3] != '\0')
return Error.MSPACK_ERR_DATAFORMAT;
// Seek to byte after string ended in file
if (System.Seek(fh, i + 1 - len, SeekMode.MSPACK_SYS_SEEK_CUR))
return Error.MSPACK_ERR_SEEK;
fnPtr--; // Remove the null terminator
}
fn[fnPtr] = '\0';
}
// 2 bytes: extra text length then [length] bytes of extra text data
if (hdr.KWAJHeader.Flags.HasFlag(OptionalHeaderFlag.MSKWAJ_HDR_HASEXTRATEXT))
{
if (System.Read(fh, buf, 0, 2) != 2)
return Error.MSPACK_ERR_READ;
int i = BitConverter.ToUInt16(buf, 0);
byte[] extra = new byte[i + 1];
if (System.Read(fh, extra, 0, i) != i)
return Error.MSPACK_ERR_READ;
extra[i] = 0x00;
hdr.Extra = Encoding.ASCII.GetString(extra, 0, extra.Length);
hdr.ExtraLength = (ushort)i;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,88 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
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

@@ -1,68 +0,0 @@
/* 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.IO;
namespace LibMSPackSharp.KWAJ
{
/// <summary>
/// A structure which represents an KWAJ compressed file.
///
/// All fields are READ ONLY.
/// </summary>
public class Header : BaseHeader
{
#region Internal
/// <summary>
/// KWAJ header information
/// </summary>
internal _KWAJHeader KWAJHeader { get; set; }
#endregion
/// <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; }
/// <summary>
/// Internal file handle
/// </summary>
public FileStream FileHandle { get; set; }
}
}

View File

@@ -1,85 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
using System;
namespace LibMSPackSharp.KWAJ
{
internal class _KWAJHeader
{
#region Fields
/// <summary>
/// "KWAA"
/// </summary>
/// <remarks>0x00</remarks>
public uint Signature1 { get; private set; }
/// <summary>
/// Signature extension
/// </summary>
/// <remarks>0x04</remarks>
public uint Signature2 { get; private set; }
/// <summary>
/// The compression type
/// </summary>
/// <remarks>0x08</remarks>
public CompressionType CompressionType { get; private set; }
/// <summary>
/// The offset in the file where the compressed data stream begins
/// </summary>
/// <remarks>0x0a</remarks>
public ushort DataOffset { get; private set; }
/// <summary>
///
/// </summary>
public OptionalHeaderFlag Flags { get; private set; }
/// <summary>
/// Total size of the KWAJ header in bytes
/// </summary>
public const int Size = 0x0e;
#endregion
/// <summary>
/// Private constructor
/// </summary>
private _KWAJHeader() { }
/// <summary>
/// Create a _KWAJHeader from a byte array, if possible
/// </summary>
public static Error Create(byte[] buffer, out _KWAJHeader header)
{
header = null;
if (buffer == null || buffer.Length < Size)
return Error.MSPACK_ERR_READ;
header = new _KWAJHeader();
header.Signature1 = BitConverter.ToUInt32(buffer, 0x00);
if (header.Signature1 != 0x4A41574B)
return Error.MSPACK_ERR_SIGNATURE;
header.Signature2 = BitConverter.ToUInt32(buffer, 0x04);
if (header.Signature1 != 0xD127F088)
return Error.MSPACK_ERR_SIGNATURE;
header.CompressionType = (CompressionType)BitConverter.ToUInt16(buffer, 0x08);
header.DataOffset = BitConverter.ToUInt16(buffer, 0x0a);
header.Flags = (OptionalHeaderFlag)BitConverter.ToUInt16(buffer, 0x0c);
return Error.MSPACK_ERR_OK;
}
}
}

View File

@@ -1,24 +0,0 @@
/* 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 SystemImpl System { get; set; }
}
}

View File

@@ -1,23 +0,0 @@
/* 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
{
public class Decompressor : BaseDecompressor
{
// TODO
}
}

View File

@@ -1,499 +0,0 @@
/* 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
*/
namespace LibMSPackSharp
{
public static 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 static 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 static CAB.Decompressor CreateCABDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new CAB.Decompressor()
{
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="self">the <see cref="CAB.Compressor"/> to destroy</param>
public static void DestroyCABCompressor(CAB.Compressor self)
{
// TODO
}
/// <summary>
/// Destroys an existing CAB decompressor.
/// </summary>
/// <param name="self">the <see cref="CAB.Decompressor"/> to destroy</param>
public static void DestroyCABDecompressor(CAB.Decompressor self)
{
if (self != null)
{
SystemImpl sys = self.System;
if (self.State != null)
{
sys.Close(self.State.InputFileHandle);
sys.Close(self.State.OutputFileHandle);
self.FreeDecompressionState();
}
}
}
#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 static 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 static CHM.Decompressor CreateCHMDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new CHM.Decompressor()
{
System = sys,
Error = Error.MSPACK_ERR_OK,
State = null,
};
}
/// <summary>
/// Destroys an existing CHM compressor.
/// </summary>
/// <param name="self">the <see cref="CHM.Compressor"/> to destroy</param>
public static void DestroyCHMCompressor(CHM.Compressor self)
{
// TODO
}
/// <summary>
/// Destroys an existing CHM decompressor.
/// </summary>
/// <param name="self">the <see cref="CHM.Decompressor"/> to destroy</param>
public static void DestroyCHMDecompressor(CHM.Decompressor self)
{
if (self != null)
{
SystemImpl sys = self.System;
if (self.State != null)
{
sys.Close(self.State.InputFileHandle);
sys.Close(self.State.OutputFileHandle);
}
}
}
#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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static SZDD.Decompressor CreateSZDDDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new SZDD.Decompressor()
{
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 static 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 static void DestroySZDDDecompressor(SZDD.Decompressor d) { }
#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 static 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 static KWAJ.Decompressor CreateKWAJDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new KWAJ.Decompressor()
{
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 static 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 static void DestroyKWAJDecompressor(KWAJ.Decompressor d) { }
#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 static 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 static OAB.Decompressor CreateOABDecompressor(SystemImpl sys)
{
if (sys == null)
sys = SystemImpl.DefaultSystem;
if (!SystemImpl.ValidSystem(sys))
return null;
return new OAB.Decompressor()
{
System = sys,
BufferSize = 4096,
};
}
/// <summary>
/// Destroys an existing OAB compressor.
/// </summary>
/// <param name="c">the <see cref="OAB.Compressor"/> to destroy</param>
public static 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 static void DestroyOABDecompressor(OAB.Decompressor d) { }
#endregion
}
}

View File

@@ -1,78 +0,0 @@
/* 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
{
#region Fields
public SystemImpl System { get; set; }
#endregion
/// <summary>
/// Compress a full OAB file.
///
/// The input file will be read and the compressed contents written to the
/// output file.
/// </summary>
/// <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 Error Compress(string input, string output) => throw new NotImplementedException();
/// <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="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="baseFile">
/// 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 Error CompressIncremental(string input, string baseFile, string output) => throw new NotImplementedException();
}
}

View File

@@ -1,502 +0,0 @@
/* 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;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
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 : BaseDecompressor
{
#region Public Functionality
/// <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="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 Error Decompress(string input, string output)
{
byte[] hdrbuf = new byte[oabhead_SIZEOF];
LZX lzx = null;
Error ret = Error.MSPACK_ERR_OK;
FileStream infh = System.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(infh);
return ret;
}
if (System.Read(infh, hdrbuf, 0, oabhead_SIZEOF) != oabhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, oabhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, oabhead_VersionLo) != 1)
{
ret = Error.MSPACK_ERR_SIGNATURE;
System.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, oabhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, oabhead_TargetSize);
FileStream outfh = System.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(outfh);
System.Close(infh);
return ret;
}
byte[] buf = new byte[BufferSize];
SystemImpl oabd_sys = System;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = System;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = System;
out_ofh.OrigFile = outfh;
while (target_size > 0)
{
if (System.Read(infh, buf, 0, oabblk_SIZEOF) != oabblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(outfh);
System.Close(infh);
return ret;
}
uint blk_flags = BitConverter.ToUInt32(buf, oabblk_Flags);
uint blk_csize = BitConverter.ToUInt32(buf, oabblk_CompSize);
uint blk_dsize = BitConverter.ToUInt32(buf, oabblk_UncompSize);
uint blk_crc = BitConverter.ToUInt32(buf, oabblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(infh);
return ret;
}
if (blk_flags == 0)
{
// Uncompressed block
if (blk_dsize != blk_csize)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(infh);
return ret;
}
ret = CopyFileHandle(infh, outfh, (int)blk_dsize, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
}
else
{
// LZX compressed block
int window_bits = 17;
while (window_bits < 25 && (1U << window_bits) < blk_dsize)
{
window_bits++;
}
in_ofh.Available = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, BufferSize, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
System.Close(outfh);
System.Close(infh);
return ret;
}
ret = lzx.Decompress(blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(infh, null, in_ofh.Available, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
System.Close(outfh);
System.Close(infh);
return ret;
}
}
target_size -= blk_dsize;
}
System.Close(outfh);
System.Close(infh);
return ret;
}
/// <summary>
/// Decompresses an Offline Address Book with an incremental patch file.
///
/// 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="input">
/// The filename of the input file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="basePath">
/// The filename of the base file to which the
/// incremental patch shall be applied. This is passed
/// directly to mspack_system::open().
/// </param>
/// <param name="output">
/// The filename of the output file. This is passed
/// directly to mspack_system::open().
/// </param>
/// <returns>An error code, or MSPACK_ERR_OK if successful</returns>
public Error DecompressIncremental(string input, string basePath, string output)
{
byte[] hdrbuf = new byte[patchhead_SIZEOF];
LZX lzx = null;
int window_bits;
uint window_size;
Error ret = Error.MSPACK_ERR_OK;
FileStream infh = System.Open(input, OpenMode.MSPACK_SYS_OPEN_READ);
if (infh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(infh);
return ret;
}
if (System.Read(infh, hdrbuf, 0, patchhead_SIZEOF) != patchhead_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(infh);
return ret;
}
if (BitConverter.ToUInt32(hdrbuf, patchhead_VersionHi) != 3 ||
BitConverter.ToUInt32(hdrbuf, patchhead_VersionLo) != 2)
{
ret = Error.MSPACK_ERR_SIGNATURE;
System.Close(infh);
return ret;
}
uint block_max = BitConverter.ToUInt32(hdrbuf, patchhead_BlockMax);
uint target_size = BitConverter.ToUInt32(hdrbuf, patchhead_TargetSize);
// We use it for reading block headers too
if (block_max < patchblk_SIZEOF)
block_max = patchblk_SIZEOF;
FileStream basefh = System.Open(basePath, OpenMode.MSPACK_SYS_OPEN_READ);
if (basefh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(basefh);
System.Close(infh);
return ret;
}
FileStream outfh = System.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
{
ret = Error.MSPACK_ERR_OPEN;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
byte[] buf = new byte[BufferSize];
SystemImpl oabd_sys = System;
oabd_sys.Read = SysRead;
oabd_sys.Write = SysWrite;
InternalFile in_ofh = new InternalFile();
in_ofh.OrigSys = System;
in_ofh.OrigFile = infh;
InternalFile out_ofh = new InternalFile();
out_ofh.OrigSys = System;
out_ofh.OrigFile = outfh;
while (target_size > 0)
{
if (System.Read(infh, buf, 0, patchblk_SIZEOF) != patchblk_SIZEOF)
{
ret = Error.MSPACK_ERR_READ;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
uint blk_csize = BitConverter.ToUInt32(buf, patchblk_PatchSize);
uint blk_dsize = BitConverter.ToUInt32(buf, patchblk_TargetSize);
uint blk_ssize = BitConverter.ToUInt32(buf, patchblk_SourceSize);
uint blk_crc = BitConverter.ToUInt32(buf, patchblk_CRC);
if (blk_dsize > block_max || blk_dsize > target_size || blk_ssize > block_max)
{
ret = Error.MSPACK_ERR_DATAFORMAT;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
window_size = (uint)((blk_ssize + 32767) & ~32767);
window_size += blk_dsize;
window_bits = 17;
while (window_bits < 25 && (1U << window_bits) < window_size)
window_bits++;
in_ofh.Available = (int)blk_csize;
out_ofh.CRC = 0xffffffff;
lzx = LZX.Init(oabd_sys, in_ofh.OrigFile, out_ofh.OrigFile, window_bits, 0, 4096, blk_dsize, true);
if (lzx == null)
{
ret = Error.MSPACK_ERR_NOMEMORY;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
ret = lzx.SetReferenceData(System, basefh, blk_ssize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
ret = lzx.Decompress(blk_dsize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
lzx = null;
// Consume any trailing padding bytes before the next block
ret = CopyFileHandle(infh, null, in_ofh.Available, buf, BufferSize);
if (ret != Error.MSPACK_ERR_OK)
{
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
if (out_ofh.CRC != blk_crc)
{
ret = Error.MSPACK_ERR_CHECKSUM;
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
target_size -= blk_dsize;
}
System.Close(outfh);
System.Close(basefh);
System.Close(infh);
return ret;
}
/// <summary>
/// Sets an OAB decompression engine parameter. Available only in OAB
/// 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="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 Error SetParam(Parameters param, int value)
{
if (param == Parameters.MSOABD_PARAM_DECOMPBUF && value >= 16)
{
// Must be at least 16 bytes (patchblk_SIZEOF, oabblk_SIZEOF)
BufferSize = value;
return Error.MSPACK_ERR_OK;
}
return Error.MSPACK_ERR_ARGS;
}
#endregion
#region I/O Methods
private static int SysRead(object baseFile, byte[] buf, int pointer, int size)
{
InternalFile file = baseFile as InternalFile;
if (file == null)
return 0;
int bytes_read;
if (size > file.Available)
size = file.Available;
bytes_read = file.OrigSys.Read(file.OrigFile, buf, pointer, size);
if (bytes_read < 0)
return bytes_read;
file.Available -= bytes_read;
return bytes_read;
}
private static int SysWrite(object baseFile, byte[] buf, int pointer, int size)
{
// Null output file means skip those bytes
if (baseFile == null)
{
return size;
}
else if (baseFile is InternalFile file)
{
int bytes_written = file.OrigSys.Write(file.OrigFile, buf, pointer, size);
if (bytes_written > 0)
file.CRC = Checksum.CRC32(buf, 0, bytes_written, file.CRC);
return bytes_written;
}
else if (baseFile is FileStream impl)
{
return SystemImpl.DefaultSystem.Write(impl, buf, pointer, size);
}
// Unknown file to write to
return -1;
}
#endregion
#region Helpers
/// <summary>
/// Copy between the input and output, if possible
/// </summary>
private Error CopyFileHandle(FileStream input, FileStream output, int bytesToCopy, byte[] buf, int bufferSize)
{
while (bytesToCopy > 0)
{
int run = bufferSize;
if (run > bytesToCopy)
run = bytesToCopy;
if (System.Read(input, buf, 0, run) != run)
return Error.MSPACK_ERR_READ;
if (output != null && System.Write(output, buf, 0, run) != run)
return Error.MSPACK_ERR_WRITE;
bytesToCopy -= run;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,19 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
namespace LibMSPackSharp.OAB
{
public enum Parameters
{
/// <summary>
/// Size of decompression buffer
/// </summary>
MSOABD_PARAM_DECOMPBUF = 0,
}
}

View File

@@ -1,31 +0,0 @@
/* 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.IO;
namespace LibMSPackSharp.OAB
{
public class InternalFile
{
public SystemImpl OrigSys { get; set; }
public FileStream OrigFile { get; set; }
public uint CRC { get; set; }
public int Available { get; set; }
}
}

View File

@@ -1,118 +0,0 @@
/* 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
{
#region Fields
public SystemImpl System { get; set; }
public Error Error { get; set; }
#endregion
/// <summary>
/// Reads an input file and creates a compressed output file in the
/// SZDD compressed file format. The SZDD compression format is quick
/// 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

@@ -1,226 +0,0 @@
/* 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;
using System.Linq;
using LibMSPackSharp.Compression;
using static LibMSPackSharp.Constants;
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 : BaseDecompressor
{
#region Public Functionality
/// <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="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(Header)"/>
public Header Open(string filename)
{
FileStream fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ);
Header hdr = new Header();
if (fh != null && hdr != null)
{
hdr.FileHandle = fh;
Error = ReadHeaders(fh, hdr);
}
else
{
if (fh == null)
Error = Error.MSPACK_ERR_OPEN;
if (hdr == null)
Error = Error.MSPACK_ERR_NOMEMORY;
}
if (Error != Error.MSPACK_ERR_OK)
{
System.Close(fh);
hdr = null;
}
return hdr;
}
/// <summary>
/// Closes a previously opened SZDD file.
///
/// 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="szdd">The SZDD file to close</param>
/// <see cref="Open(string)"/>
public void Close(Header hdr)
{
if (System == null || hdr == null)
return;
// Close the file handle associated
System.Close(hdr.FileHandle);
Error = Error.MSPACK_ERR_OK;
}
/// <summary>
/// Extracts the compressed data from a SZDD file.
///
/// This decompresses the compressed SZDD data stream and writes it to
/// an output file.
/// </summary>
/// <param name="hdr">The SZDD 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 Error Extract(Header hdr, string filename)
{
if (hdr == null)
return Error = Error.MSPACK_ERR_ARGS;
FileStream fh = hdr.FileHandle;
if (fh == null)
return Error.MSPACK_ERR_ARGS;
// Seek to the compressed data
long dataOffset = (hdr.Format == Format.MSSZDD_FMT_NORMAL) ? 14 : 12;
if (System.Seek(fh, dataOffset, SeekMode.MSPACK_SYS_SEEK_START))
return Error = Error.MSPACK_ERR_SEEK;
// Open file for output
FileStream outfh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE);
if (outfh == null)
return Error = Error.MSPACK_ERR_OPEN;
// Decompress the data
Error = LZSS.Decompress(
System,
fh,
outfh,
SZDD_INPUT_SIZE,
hdr.Format == Format.MSSZDD_FMT_NORMAL
? LZSSMode.LZSS_MODE_EXPAND
: LZSSMode.LZSS_MODE_QBASIC);
// Close output file
System.Close(outfh);
return Error;
}
/// <summary>
/// Decompresses an SZDD file to an output file in one step.
///
/// 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="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 Error Decompress(string input, string output)
{
Header hdr = Open(input) as Header;
if (hdr == null)
return Error;
Error error = Extract(hdr, output);
Close(hdr);
return Error = error;
}
#endregion
#region Helpers
/// <summary>
/// Reads the headers of an SZDD format file
/// </summary>
private Error ReadHeaders(FileStream fh, Header hdr)
{
// Read and check signature
byte[] buf = new byte[8];
if (System.Read(fh, buf, 0, 8) != 8)
return Error.MSPACK_ERR_READ;
if (buf.SequenceEqual(expandSignature))
{
// Common SZDD
hdr.Format = Format.MSSZDD_FMT_NORMAL;
// Read the rest of the header
if (System.Read(fh, buf, 0, 6) != 6)
return Error.MSPACK_ERR_READ;
if (buf[0] != 0x41)
return Error.MSPACK_ERR_DATAFORMAT;
hdr.MissingChar = (char)buf[1];
hdr.Length = BitConverter.ToUInt32(buf, 2);
}
if (buf.SequenceEqual(qbasicSignature))
{
// Special QBasic SZDD
hdr.Format = Format.MSSZDD_FMT_QBASIC;
if (System.Read(fh, buf, 0, 4) != 4)
return Error.MSPACK_ERR_READ;
hdr.MissingChar = '\0';
hdr.Length = BitConverter.ToUInt32(buf, 0);
}
else
{
return Error.MSPACK_ERR_SIGNATURE;
}
return Error.MSPACK_ERR_OK;
}
#endregion
}
}

View File

@@ -1,32 +0,0 @@
/* This file is part of libmspack.
* (C) 2003-2018 Stuart Caie.
*
* libmspack is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
*
* For further details, see the file COPYING.LIB distributed with libmspack
*/
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

@@ -1,49 +0,0 @@
/* 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.IO;
namespace LibMSPackSharp.SZDD
{
/// <summary>
/// A structure which represents an SZDD compressed file.
///
/// All fields are READ ONLY.
/// </summary>
public class Header : BaseHeader
{
/// <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; }
public FileStream FileHandle { get; set; }
}
}

View File

@@ -1,274 +0,0 @@
/* 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="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 FileStream 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 FileStream Open(string filename, OpenMode mode)
{
try
{
switch (mode)
{
case OpenMode.MSPACK_SYS_OPEN_READ:
return File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
case OpenMode.MSPACK_SYS_OPEN_WRITE:
return File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
case OpenMode.MSPACK_SYS_OPEN_UPDATE:
return File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
case OpenMode.MSPACK_SYS_OPEN_APPEND:
return File.Open(filename, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite);
default:
return null;
}
}
catch (Exception ex)
{
Message(null, $"Could not open {filename}: {ex}");
return null;
}
}
/// <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(string, OpenMode)"/>
public void Close(FileStream file) => file?.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(string, OpenMode)"/>
/// <see cref="Write"/>
public Func<FileStream, 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(string, OpenMode)"/>
/// <see cref="Read"/>
public Func<FileStream, 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(string, OpenMode)"/>
/// <see cref="Tell(FileStream)"/>
public bool Seek(FileStream self, long offset, SeekMode mode)
{
if (self == null)
return false;
switch (mode)
{
case SeekMode.MSPACK_SYS_SEEK_START:
try { self.Seek(offset, SeekOrigin.Begin); return true; }
catch { return false; }
case SeekMode.MSPACK_SYS_SEEK_CUR:
try { self.Seek(offset, SeekOrigin.Current); return true; }
catch { return false; }
case SeekMode.MSPACK_SYS_SEEK_END:
try { self.Seek(offset, SeekOrigin.End); return true; }
catch { return false; }
default:
return false;
}
}
/// <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(string, OpenMode)"/>
/// <see cref="Seek(FileStream, long, SeekMode)"/>
public long Tell(FileStream self) => self?.Position ?? 0;
/// <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(string, OpenMode)"/>
public void Message(FileStream file, string format)
{
if (file != null)
Console.Error.Write($"{file.Name}: ");
Console.Error.Write($"{format}\n");
}
#region Helpers
/// <summary>
/// Returns the length of a file opened for reading
/// </summary>
public Error GetFileLength(FileStream file, out long length)
{
try
{
length = file?.Length ?? 0;
return Error.MSPACK_ERR_OK;
}
catch
{
length = 0;
return Error.MSPACK_ERR_SEEK;
}
}
/// <summary>
/// Validates a system structure
/// </summary>
public static bool ValidSystem(SystemImpl sys)
{
return (sys != null) && (sys.Read != null) && (sys.Write != null);
}
#endregion
#region Default Implementation
public static SystemImpl DefaultSystem => new SystemImpl()
{
Read = DefaultRead,
Write = DefaultWrite,
};
private static int DefaultRead(object file, byte[] buffer, int pointer, int bytes)
{
FileStream self = file as FileStream;
if (self != null && buffer != null && bytes >= 0)
{
try { return self.Read(buffer, pointer, bytes); }
catch { }
}
return -1;
}
private static int DefaultWrite(object file, byte[] buffer, int pointer, int bytes)
{
// Null output file means skip those bytes
if (file == null)
{
return bytes;
}
FileStream self = file as FileStream;
if (self != null && buffer != null && bytes >= 0)
{
try { self.Write(buffer, pointer, bytes); }
catch { return -1; }
return bytes;
}
return -1;
}
#endregion
}
}

1
LibMSPackSharp Submodule

Submodule LibMSPackSharp added at cc08388953