11 Commits
1.5.6 ... 1.5.7

Author SHA1 Message Date
Matt Nadareski
2749c2f5bd Bump version 2024-12-11 11:25:41 -05:00
Matt Nadareski
41ce962700 Reorder LZ constants 2024-12-11 11:10:01 -05:00
Matt Nadareski
85b7103bd3 Re-rename Expand to SZDD for consistency 2024-12-11 11:09:28 -05:00
Matt Nadareski
4c61a191e8 Remove MS-ZIP Block type 2024-12-11 11:00:44 -05:00
Matt Nadareski
0f70598969 Add LZ prefixes for easier checking 2024-12-11 10:58:47 -05:00
Matt Nadareski
d6b057d808 Move LZ models out of Compression namespace 2024-12-11 10:54:10 -05:00
Matt Nadareski
3d8036e7b5 Remove things that Compression doesn't need anymore 2024-12-11 10:46:28 -05:00
Matt Nadareski
bb35946866 Ensure KWAJ header flags serialized 2024-12-11 04:19:02 -05:00
Matt Nadareski
78f9f1b36f Fix KWAJ serialization 2024-12-11 04:16:23 -05:00
Matt Nadareski
9d1b1ca36d Further simplify LZ models 2024-12-11 03:48:10 -05:00
Matt Nadareski
6e1f8bf55e Fix issues with MS-LZ models 2024-12-11 01:50:02 -05:00
26 changed files with 313 additions and 490 deletions

View File

@@ -1,18 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class BlockHeader
{
/// <summary>
/// Set if and only if this is the last block of the data set.
/// </summary>
/// <remarks>Bit 0</remarks>
public bool BFINAL { get; set; }
/// <summary>
/// Specifies how the data are compressed
/// </summary>
/// <remarks>Bits 1-2</remarks>
public CompressionType BTYPE { get; set; }
}
}

View File

@@ -1,20 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <summary>
/// Compression with Huffman codes (BTYPE=01 or BTYPE=02)
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public abstract class CompressedDataHeader : DataHeader
{
/// <summary>
/// Huffman code lengths for the literal / length alphabet
/// </summary>
public virtual uint[]? LiteralLengths { get; set; }
/// <summary>
/// Huffman distance codes for the literal / length alphabet
/// </summary>
public virtual uint[]? DistanceCodes { get; set; }
}
}

View File

@@ -1,136 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public static class Constants
{
/// <summary>
/// Bits in base literal/length lookup table
/// </summary>
public const int ZIPLBITS = 9;
/// <summary>
/// Bits in base distance lookup table
/// </summary>
public const int ZIPDBITS = 6;
/// <summary>
/// Maximum bit length of any code
/// </summary>
public const int ZIPBMAX = 16;
/// <summary>
/// Maximum number of codes in any set
/// </summary>
public const int ZIPN_MAX = 288;
#region Fixed Huffman Codes
/// <summary>
/// Fixed Huffman code lengths for the literal / length alphabet
/// </summary>
public static readonly uint[] FixedLiteralLengths =
[
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8,
];
/// <summary>
/// Fixed Huffman distance codes for the literal / length alphabet
/// </summary>
public static readonly uint[] FixedDistanceCodes =
[
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5,
];
#endregion
#region Literal and Length Alphabets
/// <summary>
/// Extra bits for distance codes
/// </summary>
public static readonly ushort[] DistanceExtraBits =
[
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>
/// Copy offsets for distance codes 0..29
/// </summary>
public static readonly ushort[] DistanceOffsets =
[
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 for literal codes 257..285
/// </summary>
public static readonly ushort[] LiteralExtraBits =
[
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>
/// Copy lengths for literal codes 257..285
/// </summary>
public static readonly ushort[] LiteralLengths =
[
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>
/// Order of the bit length code lengths
/// </summary>
public static readonly byte[] BitLengthOrder =
[
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
];
#endregion
}
}

View File

@@ -1,11 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <summary>
/// Base class for all data headers (BTYPE=00, BTYPE=01, or BTYPE=02)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public abstract class DataHeader
{
// No common fields between all data headers
}
}

View File

@@ -1,11 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <summary>
/// Compression with dynamic Huffman codes (BTYPE=10)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class DynamicCompressedDataHeader : CompressedDataHeader
{
// Codes are provided externally
}
}

View File

@@ -1,26 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public enum CompressionType : byte
{
/// <summary>
/// no compression
/// </summary>
NoCompression = 0b00,
/// <summary>
/// Compressed with fixed Huffman codes
/// </summary>
FixedHuffman = 0b01,
/// <summary>
/// Compressed with dynamic Huffman codes
/// </summary>
DynamicHuffman = 0b10,
/// <summary>
/// Reserved (error)
/// </summary>
Reserved = 0b11,
}
}

View File

@@ -1,24 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <summary>
/// Compression with fixed Huffman codes (BTYPE=01)
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class FixedCompressedDataHeader : CompressedDataHeader
{
#region Properties
/// <summary>
/// Huffman code lengths for the literal / length alphabet
/// </summary>
public override uint[]? LiteralLengths => Constants.FixedLiteralLengths;
/// <summary>
/// Huffman distance codes for the literal / length alphabet
/// </summary>
public override uint[]? DistanceCodes => Constants.FixedDistanceCodes;
#endregion
}
}

View File

@@ -1,21 +0,0 @@
namespace SabreTools.Models.Compression.Deflate
{
/// <summary>
/// Non-compressed blocks (BTYPE=00)
/// </summary>
/// <see href="https://www.rfc-editor.org/rfc/rfc1951"/>
public class NonCompressedBlockHeader : DataHeader
{
/// <summary>
/// The number of data bytes in the block
/// </summary>
/// <remarks>Bytes 0-1</remarks>
public ushort LEN { get; set; }
/// <summary>
/// The one's complement of LEN
/// </summary>
/// <remarks>Bytes 2-3</remarks>
public ushort NLEN { get; set; }
}
}

View File

@@ -1,23 +0,0 @@
namespace SabreTools.Models.Compression.LZ
{
public static class Constants
{
public const int GETLEN = 2048;
public const int LZ_MAGIC_LEN = 8;
public const int LZ_HEADER_LEN = 14;
public static readonly byte[] MagicBytes = [0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33];
public static readonly string MagicString = System.Text.Encoding.ASCII.GetString(MagicBytes);
public const ulong MagicUInt64 = 0x3327f08844445a53;
public const int LZ_TABLE_SIZE = 0x1000;
public const int MAX_LZSTATES = 16;
public const int LZ_MIN_HANDLE = 0x400;
}
}

View File

@@ -1,17 +0,0 @@
namespace SabreTools.Models.Compression.LZ
{
/// <see href="https://github.com/wine-mirror/wine/blob/master/include/lzexpand.h"/>
public enum LZERROR
{
LZERROR_OK = 1,
LZERROR_NOT_LZ = 0,
LZERROR_BADINHANDLE = -1,
LZERROR_BADOUTHANDLE = -2,
LZERROR_READ = -3,
LZERROR_WRITE = -4,
LZERROR_GLOBALLOC = -5,
LZERROR_GLOBLOCK = -6,
LZERROR_BADVALUE = -7,
LZERROR_UNKNOWNALG = -8,
}
}

View File

@@ -1,22 +0,0 @@
using System.Runtime.InteropServices;
namespace SabreTools.Models.Compression.LZ
{
/// <summary>
/// Format of first 14 byte of LZ compressed file
/// </summary>
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/lzexpand.c"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class FileHeaader
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string? Magic;
public byte CompressionType;
[MarshalAs(UnmanagedType.U1)]
public char LastChar;
public uint RealLength;
}
}

View File

@@ -1,72 +0,0 @@
using System.IO;
namespace SabreTools.Models.Compression.LZ
{
public sealed class State
{
/// <summary>
/// Internal backing stream
/// </summary>
public Stream? Source { get; set; }
/// <summary>
/// The last char of the filename for replacement
/// </summary>
public char LastChar { get; set; }
/// <summary>
/// Decompressed length of the file
/// </summary>
public uint RealLength { get; set; }
/// <summary>
/// Position the decompressor currently is
/// </summary>
public uint RealCurrent { get; set; }
/// <summary>
/// Position the user wants to read from
/// </summary>
public uint RealWanted { get; set; }
/// <summary>
/// The rotating LZ table
/// </summary>
public byte[]? Table { get; set; }
/// <summary>
/// CURrent TABle ENTry
/// </summary>
public uint CurrentTableEntry { get; set; }
/// <summary>
/// Length and position of current string
/// </summary>
public byte StringLength { get; set; }
/// <summary>
/// From stringtable
/// </summary>
public uint StringPosition { get; set; }
/// <summary>
/// Bitmask within blocks
/// </summary>
public ushort ByteType { get; set; }
/// <summary>
/// GETLEN bytes
/// </summary>
public byte[]? Window { get; set; }
/// <summary>
/// Current read
/// </summary>
public uint WindowCurrent { get; set; }
/// <summary>
/// Length last got
/// </summary>
public uint WindowLength { get; set; }
}
}

View File

@@ -1,33 +0,0 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Each MSZIP block MUST consist of a 2-byte MSZIP signature and one or more RFC 1951 blocks. The
/// 2-byte MSZIP signature MUST consist of the bytes 0x43 and 0x4B. The MSZIP signature MUST be
/// the first 2 bytes in the MSZIP block. The MSZIP signature is shown in the following packet diagram.
///
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
///
/// The maximum compressed size of each MSZIP block is 32 KB + 12 bytes. This enables the MSZIP
/// block to contain 32 KB of data split between two noncompressed RFC 1951 blocks, each of which
/// has a value of BTYPE = 00.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class Block
{
/// <summary>
/// Block header
/// </summary>
public BlockHeader? BlockHeader { get; set; }
/// <summary>
/// Compressed blocks
/// </summary>
public DeflateBlock[]? CompressedBlocks { get; set; }
}
}

View File

@@ -4,6 +4,18 @@ namespace SabreTools.Models.Compression.MSZIP
/// Each MSZIP block MUST consist of a 2-byte MSZIP signature and one or more RFC 1951 blocks. The
/// 2-byte MSZIP signature MUST consist of the bytes 0x43 and 0x4B. The MSZIP signature MUST be
/// the first 2 bytes in the MSZIP block. The MSZIP signature is shown in the following packet diagram.
///
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
///
/// The maximum compressed size of each MSZIP block is 32 KB + 12 bytes. This enables the MSZIP
/// block to contain 32 KB of data split between two noncompressed RFC 1951 blocks, each of which
/// has a value of BTYPE = 00.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class BlockHeader

View File

@@ -1,20 +0,0 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
public static class Constants
{
/// <summary>
/// Window size
/// </summary>
public const ushort ZIPWSIZE = 0x8000;
/// <summary>
/// And'ing with Zipmask[n] masks the lower n bits
/// </summary>
public static readonly ushort[] BitMasks =
[
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
];
}
}

View File

@@ -1,35 +0,0 @@
namespace SabreTools.Models.Compression.MSZIP
{
/// <summary>
/// Each MSZIP block is the result of a single deflate compression operation, as defined in [RFC1951].
/// The compressor that performs the compression operation MUST generate one or more RFC 1951
/// blocks, as defined in [RFC1951]. The number, deflation mode, and type of RFC 1951 blocks in each
/// MSZIP block is determined by the compressor, as defined in [RFC1951]. The last RFC 1951 block in
/// each MSZIP block MUST be marked as the "end" of the stream(1), as defined by [RFC1951]
/// section 3.2.3. Decoding trees MUST be discarded after each RFC 1951 block, but the history buffer
/// MUST be maintained.Each MSZIP block MUST represent no more than 32 KB of uncompressed data.
/// </summary>
/// <see href="https://interoperability.blob.core.windows.net/files/MS-MCI/%5bMS-MCI%5d.pdf"/>
public class DeflateBlock
{
/// <summary>
/// Deflate block (RFC-1951) header
/// </summary>
public Deflate.BlockHeader? Header { get; set; }
/// <summary>
/// Compression-specific data header
/// </summary>
public Deflate.DataHeader? DataHeader { get; set; }
/// <summary>
/// MSZIP data
/// </summary>
/// <remarks>
/// Depending on the implementation of these models, this property could either be
/// compressed or uncompressed data. Keep this in mind when using the built
/// versions of this model.
/// </remarks>
public byte[]? Data { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
namespace SabreTools.Models.LZ
{
public static class Constants
{
public const string KWAJPrefix = "KWAJ";
public static readonly byte[] KWAJSignatureBytes = [0x4B, 0x57, 0x41, 0x4A, 0x88, 0xF0, 0x27, 0xD1];
public const string QBasicPrefix = "SZ ";
public static readonly byte[] QBasicSignatureBytes = [0x53, 0x5A, 0x20, 0x88, 0xF0, 0x27, 0x33, 0xD1];
public const string SZDDPrefix = "SZDD";
public static readonly byte[] SZDDSignatureBytes = [0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33];
}
}

View File

@@ -0,0 +1,82 @@
using System;
namespace SabreTools.Models.LZ
{
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public enum KWAJCompressionType : ushort
{
/// <summary>
/// No compression
/// </summary>
NoCompression = 0,
/// <summary>
/// No compression, data is XORed with byte 0xFF
/// </summary>
NoCompressionXor = 1,
/// <summary>
/// The same compression method as the QBasic variant of SZDD
/// </summary>
QBasic = 2,
/// <summary>
/// LZ + Huffman "Jeff Johnson" compression
/// </summary>
LZH = 3,
/// <summary>
/// MS-ZIP
/// </summary>
MSZIP = 4,
}
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
[Flags]
public enum KWAJHeaderFlags : ushort
{
/// <summary>
/// Header extensions contains 4-byte decompressed length
/// </summary>
HasDecompressedLength = 0x0001,
/// <summary>
/// Header extensions contains 2-byte unknown value
/// </summary>
HasUnknownFlag = 0x0002,
/// <summary>
/// Header extensions contains 2-byte prefix followed by
/// that many bytes of (unknown purpose) data
/// </summary>
HasPrefixedData = 0x0004,
/// <summary>
/// Header extensions contains null-terminated string of
/// max length 8 representing the file name
/// </summary>
HasFileName = 0x0008,
/// <summary>
/// Header extensions contains null-terminated string of
/// max length 3 representing the file name
/// </summary>
HasFileExtension = 0x0010,
/// <summary>
/// Header extensions contains 2-byte prefix followed by
/// that many bytes of (arbitrary text) data
/// </summary>
HasAdditionalText = 0x0020,
}
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/lzexpand.c"/>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public enum ExpandCompressionType : byte
{
/// <summary>
/// Only valid compression type: 'A'
/// </summary>
A = 0x41,
}
}

View File

@@ -0,0 +1,21 @@
namespace SabreTools.Models.LZ
{
/// <summary>
/// LZ variant with variable compression
/// </summary>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public sealed class KWAJFile
{
/// <summary>
/// Header
/// </summary>
public KWAJHeader? Header { get; set; }
/// <summary>
/// Optional extensions defined by <see cref="KWAJHeader.HeaderFlags"/>
/// </summary>
public KWAJHeaderExtensions? HeaderExtensions { get; set; }
// Followed immediately by compressed data
}
}

View File

@@ -0,0 +1,35 @@
using System.Runtime.InteropServices;
namespace SabreTools.Models.LZ
{
/// <summary>
/// LZ variant with variable compression
/// </summary>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class KWAJHeader
{
/// <summary>
/// "KWAJ" signature
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public byte[]? Magic;
/// <summary>
/// Compression method
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public KWAJCompressionType CompressionType;
/// <summary>
/// File offset of compressed data
/// </summary>
public ushort DataOffset;
/// <summary>
/// Header flags to mark header extensions
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public KWAJHeaderFlags HeaderFlags;
}
}

View File

@@ -0,0 +1,51 @@
namespace SabreTools.Models.LZ
{
/// <summary>
/// Additional information stored after the KWAJ header
/// </summary>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public sealed class KWAJHeaderExtensions
{
/// <summary>
/// Decompressed length of file
/// </summary>
public uint? DecompressedLength { get; set; }
/// <summary>
/// Unknown purpose
/// </summary>
public ushort? UnknownPurpose { get; set; }
/// <summary>
/// Length of <see cref="UnknownData"/>
/// </summary>
public ushort? UnknownDataLength { get; set; }
/// <summary>
/// Unknown purpose data whose length is defined
/// by <see cref="UnknownDataLength"/>
/// </summary>
public byte[]? UnknownData { get; set; }
/// <summary>
/// Null-terminated string with max length 8: file name
/// </summary>
public string? FileName { get; set; }
/// <summary>
/// Null-terminated string with max length 3: file extension
/// </summary>
public string? FileExtension { get; set; }
/// <summary>
/// Length of <see cref="ArbitraryText"/>
/// </summary>
public ushort? ArbitraryTextLength { get; set; }
/// <summary>
/// Arbitrary text data whose length is defined
/// by <see cref="ArbitraryTextLength"/>
/// </summary>
public byte[]? ArbitraryText { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
namespace SabreTools.Models.LZ
{
/// <summary>
/// LZ variant used in QBasic 4.5 installer
/// </summary>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public sealed class QBasicFile
{
/// <summary>
/// Header
/// </summary>
public QBasicHeader? Header { get; set; }
// Followed immediately by compressed data
}
}

View File

@@ -0,0 +1,23 @@
using System.Runtime.InteropServices;
namespace SabreTools.Models.LZ
{
/// <summary>
/// LZ variant used in QBasic 4.5 installer
/// </summary>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class QBasicHeader
{
/// <summary>
/// "SZ" signature
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public byte[]? Magic;
/// <summary>
/// The integer length of the file when unpacked
/// </summary>
public uint RealLength;
}
}

View File

@@ -0,0 +1,17 @@
namespace SabreTools.Models.LZ
{
/// <summary>
/// Standard LZ variant
/// </summary>
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/lzexpand.c"/>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
public sealed class SZDDFile
{
/// <summary>
/// Header
/// </summary>
public SZDDHeader? Header { get; set; }
// Followed immediately by compressed data
}
}

View File

@@ -0,0 +1,38 @@
using System.Runtime.InteropServices;
namespace SabreTools.Models.LZ
{
/// <summary>
/// Standard LZ variant
/// </summary>
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/lzexpand.c"/>
/// <see href="https://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class SZDDHeader
{
/// <summary>
/// "SZDD" signature
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public byte[]? Magic;
/// <summary>
/// Compression mode
/// </summary>
/// <remarks>Only <see cref="ExpandCompressionType.A"/> is supported</remarks>
[MarshalAs(UnmanagedType.U1)]
public ExpandCompressionType CompressionType;
/// <summary>
/// The character missing from the end of the filename
/// </summary>
/// <remarks>0 means unknown</remarks>
[MarshalAs(UnmanagedType.U1)]
public char LastChar;
/// <summary>
/// The integer length of the file when unpacked
/// </summary>
public uint RealLength;
}
}

View File

@@ -7,7 +7,7 @@
<NoWarn>CS0618</NoWarn>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.5.6</Version>
<Version>1.5.7</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>