34 Commits
1.5.6 ... 1.6.1

Author SHA1 Message Date
Matt Nadareski
fb39216c82 Bump verison 2025-07-24 09:11:34 -04:00
Matt Nadareski
1a2de39868 Fix Dir serialization issue 2025-07-24 09:11:10 -04:00
Matt Nadareski
f18b6c8850 Bump version 2025-07-23 09:48:18 -04:00
Matt Nadareski
8f1e49e464 Add .NET Standard 2.0 and 2.1 2025-07-23 09:43:12 -04:00
Matt Nadareski
6547242f93 Clean up more Matroskha comments 2025-07-21 11:27:20 -04:00
Matt Nadareski
edf00f3ab2 Add analysis block comment 2025-07-21 11:25:18 -04:00
Matt Nadareski
bce4736037 Clean up more Matroskha comments 2025-07-21 11:23:25 -04:00
Matt Nadareski
81f28974c0 More remark corrections based on more samples 2025-07-21 11:10:35 -04:00
Matt Nadareski
30ebe84af4 Remove disproven theory 2025-07-21 11:08:01 -04:00
Matt Nadareski
b07fbdedd6 Add secondary length note 2025-07-21 09:39:31 -04:00
Matt Nadareski
cd8fff4a86 Slight tweaks to Matroska entry notes 2025-07-21 09:21:56 -04:00
Matt Nadareski
3d3275e3cb Add SecuROM Matroshka models
All research thanks to HeroponRikiBestest
2025-07-21 09:11:37 -04:00
Matt Nadareski
4c76ce1230 Add SecuROM DFA models
All research thanks to HeroponRikiBestest
2025-07-21 08:10:25 -04:00
Matt Nadareski
0c4e3b4bf2 Dirs can be nested 2025-06-24 12:32:14 -04:00
Matt Nadareski
e8f4386199 Add mess alternative ListXML model 2025-05-28 09:53:05 -04:00
Matt Nadareski
ab66ccf3c5 Update copyright 2024-12-30 21:19:05 -05:00
Matt Nadareski
cc60d54a33 Remove unnecessary action step 2024-12-30 21:18:59 -05:00
Matt Nadareski
e805f4cb9a Ensure .NET versions are installed for testing 2024-12-19 10:50:01 -05:00
Matt Nadareski
328c893a38 Ensure .NET versions are installed for testing 2024-12-19 10:45:08 -05:00
Matt Nadareski
ab2a12c996 Bump version 2024-12-16 12:10:13 -05:00
Matt Nadareski
362b123661 Allow symbols to be packed 2024-12-16 11:37:18 -05:00
Matt Nadareski
4a8a4746a2 Add big-endian note for PS4 PKG 2024-12-16 11:31:38 -05:00
Deterous
3f368a3be8 Create model for PS4 app.pkg header (#7)
* Create model for PS4 app.pkg header

* Rename PS4 app.pkg class

* Fix typo

* Requested changes

* Add newline
2024-12-15 20:12:06 -05:00
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
43 changed files with 962 additions and 513 deletions

View File

@@ -16,22 +16,22 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: 'Nuget Package'
path: '*.nupkg'
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: '*.nupkg'
artifacts: "*.nupkg,*.snupkg"
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True

View File

@@ -11,7 +11,13 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Build
run: dotnet build
run: dotnet build
- name: Run tests
run: dotnet test

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

@@ -0,0 +1,16 @@
using System.Xml;
using System.Xml.Serialization;
namespace SabreTools.Models.Listxml
{
[XmlRoot("mess")]
public class Mess
{
[XmlAttribute("version")]
public string? Version { get; set; }
[XmlElement("machine", typeof(Machine))]
[XmlElement("game", typeof(Game))]
public GameBase[]? Game { get; set; }
}
}

View File

@@ -10,6 +10,9 @@ namespace SabreTools.Models.Logiqx
[XmlAttribute("name")]
public string? Name { get; set; }
[XmlElement("dir", typeof(Dir))]
public Dir[]? Subdir { get; set; }
[XmlElement("game", typeof(Game))]
[XmlElement("machine", typeof(Machine))]
public GameBase[]? Game { get; set; }

View File

@@ -100,7 +100,7 @@ namespace SabreTools.Models.Metadata
string[]? asArray = Read<string[]>(key);
if (asArray != null)
#if NETFRAMEWORK
#if NETFRAMEWORK || NETSTANDARD2_0
return string.Join(",", asArray);
#else
return string.Join(',', asArray);

View File

@@ -4,6 +4,16 @@ namespace SabreTools.Models.PlayStation3
/// <see href="https://psdevwiki.com/ps3/PS3_DISC.SFB"/>
public static class Constants
{
/// <summary>
/// Identifying bytes for SFO file
/// </summary>
public const uint SFOMagic = 0x00505346;
/// <summary>
/// Identifying bytes for SFB file
/// </summary>
public const uint SFBMagic = 0x2E534642;
#region Hybrid Flags
/// <summary>

View File

@@ -9,8 +9,7 @@ namespace SabreTools.Models.PlayStation3
/// <summary>
/// ".SFB"
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[]? Magic;
public uint Magic;
/// <summary>
/// File version(?)

View File

@@ -9,8 +9,7 @@ namespace SabreTools.Models.PlayStation3
/// <summary>
/// "\0PSF"
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[]? Magic;
public uint Magic;
/// <summary>
/// Version

View File

@@ -0,0 +1,237 @@
using System.Runtime.InteropServices;
namespace SabreTools.Models.PlayStation4
{
/// <see href="https://www.psdevwiki.com/ps4/PKG_files"/>
/// <remarks>All numeric values are big-endian</remarks>
[StructLayout(LayoutKind.Sequential)]
public class AppPkgHeader
{
/// <summary>
/// Identifying bytes for app.pkg file, "\7FCNT"
/// </summary>
public uint Magic;
/// <summary>
/// PKG Type
/// </summary>
public uint Type;
/// <summary>
/// PKG Unknown Field
/// </summary>
public uint PKGUnknown;
/// <summary>
/// PKG File count
/// </summary>
public uint FileCount;
/// <summary>
/// PKG Entry count
/// </summary>
public uint EntryCount;
/// <summary>
/// SC Entry count
/// </summary>
public ushort SCEntryCount;
/// <summary>
/// PKG Entry count (duplicated)
/// </summary>
public ushort EntryCount2;
/// <summary>
/// PKG File Table offset
/// </summary>
public uint TableOffset;
/// <summary>
/// PKG Entry data size
/// </summary>
public uint EntryDataSize;
/// <summary>
/// Offset of PKG Entries
/// </summary>
public ulong BodyOffset;
/// <summary>
/// Length of all PKG Entries
/// </summary>
public ulong BodySize;
/// <summary>
/// PKG Content offset
/// </summary>
public ulong ContentOffset;
/// <summary>
/// PKG Content size
/// </summary>
public ulong ContentSize;
/// <summary>
/// PKG Content ID
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x24)]
public string? ContentID;
/// <summary>
/// PKG Content Padding (Zeroes)
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC)]
public byte[]? ContentZeroes;
/// <summary>
/// PKG DRM Type
/// </summary>
public uint DRMType;
/// <summary>
/// PKG Content Type
/// </summary>
public uint ContentType;
/// <summary>
/// PKG Content Flags
/// </summary>
public uint ContentFlags;
/// <summary>
/// PKG Promote Size
/// </summary>
public uint PromoteSize;
/// <summary>
/// PKG Version Date
/// </summary>
public uint VersionDate;
/// <summary>
/// PKG Content Flags
/// </summary>
public uint VersionHash;
/// <summary>
/// PKG Padding Section 1
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x78)]
public byte[]? Zeroes1;
/// <summary>
/// PKG SHA256 for Main Entry 1
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? MainEntry1SHA256;
/// <summary>
/// PKG SHA256 for Main Entry 2
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? MainEntry2SHA256;
/// <summary>
/// PKG SHA256 for Digest Table
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? DigestTableSHA256;
/// <summary>
/// PKG SHA256 for Main Table
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? MainTableSHA256;
/// <summary>
/// PKG Padding Section 2
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x280)]
public byte[]? Zeroes2;
/// <summary>
/// PFS Unknown Field
/// </summary>
public uint PFSUnknown;
/// <summary>
/// PFS Image Count
/// </summary>
public uint PFSImageCount;
/// <summary>
/// PFS Image Flags
/// </summary>
public ulong PFSImageFlags;
/// <summary>
/// PFS Image Offset
/// </summary>
public ulong PFSImageOffset;
/// <summary>
/// PFS Image Size
/// </summary>
public ulong PFSImageSize;
/// <summary>
/// Mount Image Offset
/// </summary>
public ulong MountImageOffset;
/// <summary>
/// Mount Image Size
/// </summary>
public ulong MountImageSize;
/// <summary>
/// PKG Size
/// </summary>
public ulong PKGSize;
/// <summary>
/// PKG Signed Size
/// </summary>
public uint PKGSignedSize;
/// <summary>
/// PKG Signed Size
/// </summary>
public uint PKGCacheSize;
/// <summary>
/// SHA256 for PFS Image
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? PFSImageSHA256;
/// <summary>
/// SHA256 for PFS Signed
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? PFSSignedSHA256;
/// <summary>
/// PFS Split Size nth 0
/// </summary>
public ulong PFSSplitSize0;
/// <summary>
/// PFS Split Size nth 1
/// </summary>
public ulong PFSSplitSize1;
/// <summary>
/// PKG Padding Section 3
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB50)]
public byte[]? Zeroes3;
/// <summary>
/// SHA256 for PKG
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[]? PKGSHA256;
}
}

View File

@@ -0,0 +1,11 @@
namespace SabreTools.Models.PlayStation4
{
/// <see href="https://www.psdevwiki.com/ps4/PKG_files"/>
public class Constants
{
/// <summary>
/// Identifying bytes for app.pkg file, "\7FCNT"
/// </summary>
public const uint AppPkgMagic = 0x7F434E54;
}
}

View File

@@ -2,17 +2,19 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<NoWarn>CS0618</NoWarn>
<NoWarn>CS0618;NETSDK1215</NoWarn>
<Nullable>enable</Nullable>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.5.6</Version>
<Version>1.6.1</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Common models used by other SabreTools projects</Description>
<Copyright>Copyright (c) Matt Nadareski 2022-2024</Copyright>
<Copyright>Copyright (c) Matt Nadareski 2022-2025</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Models</RepositoryUrl>

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SabreTools.Models.SafeDisc
namespace SabreTools.Models.SafeDisc
{
public static class Constants
{

View File

@@ -0,0 +1,122 @@
namespace SabreTools.Models.SecuROM
{
public static class Constants
{
#region DFA
public static readonly string DFAMagicString = "SDFA" + (char)0x04 + (char)0x00 + (char)0x00 + (char)0x00;
public static readonly byte[] DFAMagicBytes = [0x53, 0x44, 0x46, 0x41, 0x04, 0x00, 0x00, 0x00];
#region Keys
/// <summary>
/// 128-bit value, possibly a GUID
/// </summary>
public const string COID = "COID";
/// <summary>
/// 128-bit value, possibly a GUID
/// </summary>
/// <remarks>Only a value of D0 A2 25 C7 16 20 B7 43 99 74 2A BB 39 6B C3 57 has been found</remarks>
public const string CUID = "CUID";
/// <summary>
/// Encrypted data section
/// </summary>
public const string DATA = "DATA";
/// <summary>
/// Header version (?)
/// </summary>
/// <remarks>Only a value of 0C 00 00 00 has been found</remarks>
public const string HVER = "HVER";
/// <summary>
/// Unknown value
/// </summary>
public const string INVE = "INVE";
/// <summary>
/// Unknown key value
/// </summary>
public const string KEYB = "KEYB";
/// <summary>
/// Unknown key value
/// </summary>
public const string KEYL = "KEYL";
/// <summary>
/// MAC address (?)
/// </summary>
public const string MAC1 = "MAC1";
/// <summary>
/// MAC address (?)
/// </summary>
public const string MAC2 = "MAC2";
/// <summary>
/// Padding section
/// </summary>
/// <remarks>Only a length of 832 has been found</remarks>
public const string PAD1 = "PAD1";
/// <summary>
/// Private key ID (?)
/// </summary>
public const string PKID = "PKID";
/// <summary>
/// Private key name (?)
/// </summary>
/// <remarks>Seemingly a UTF-16 string</remarks>
public const string PKNA = "PKNA";
/// <summary>
/// Size of the decrypted executable
/// </summary>
public const string RAWS = "RAWS";
/// <summary>
/// 128-bit value, possibly a GUID
/// </summary>
/// <remarks>Only a value of all zeroes has been found</remarks>
public const string SCID = "SCID";
/// <summary>
/// Time stored in NTFS filetime
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times"/>
public const string TIME = "TIME";
/// <summary>
/// First URL to connect to
/// </summary>
public const string UR01 = "UR01";
/// <summary>
/// Second URL to connect to
/// </summary>
public const string UR02 = "UR02";
/// <summary>
/// Unknown value
/// </summary>
public const string XSPF = "XSPF";
#endregion
#endregion
#region Matroshka
public const string MatroshkaMagicString = "MatR";
public static readonly byte[] MatroshkaMagicBytes = [0x4D, 0x61, 0x74, 0x52];
#endregion
}
}

View File

@@ -0,0 +1,24 @@
namespace SabreTools.Models.SecuROM
{
/// <summary>
/// Represents a single key-length-value tuple in a
/// SecuROM DFA file
/// </summary>
public class DFAEntry
{
/// <summary>
/// Entry name, always 4 ASCII characters
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Length of the value in bytes
/// </summary>
public uint Length { get; set; }
/// <summary>
/// Value of the entry whose length is given by <see cref="Length"/>
/// </summary>
public byte[]? Value { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
namespace SabreTools.Models.SecuROM
{
/// <remarks>
/// Most DFA-protected files seem to also have additional encryption,
/// possibly SecuROM DFE. Only early RC-encrypted executables can be
/// parsed beyond the initial header.
/// </remarks>
public class DFAFile
{
/// <summary>
/// "SDFA" 0x04 0x00 0x00 0x00
/// </summary>
/// <remarks>8 bytes</remarks>
public byte[]? Signature { get; set; }
/// <summary>
/// Unknown value, possibly a block or header size
/// </summary>
/// <remarks>Only a value of 0x400 has been found</remarks>
public uint BlockOrHeaderSize { get; set; }
/// <summary>
/// All entries in the file
/// </summary>
public DFAEntry[]? Entries { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
namespace SabreTools.Models.SecuROM
{
public enum MatroshkaEntryType : uint
{
/// <summary>
/// Helper or activation executable
/// </summary>
Helper = 0x01,
/// <summary>
/// Main executable, usually one of the following:
/// - RC-encrypted executable to be decrypted later
/// - Main game program executable
/// - Revoker executable
/// </summary>
/// <remarks>Usually the second entry</remarks>
Main = 0x02,
/// <summary>
/// Required libraries for the main executable
/// </summary>
/// <remarks>
/// Examples include:
/// - DFA.dll for RC-encrypted executables
/// - paul.dll for PA-protected games
/// - remover.exe for revocation
/// executables.
/// </remarks>
Dependency = 0x04,
/// <summary>
/// Similar use to <see cref="Dependency"/>
/// </summary>
Unknown0x08 = 0x08,
}
}

View File

@@ -0,0 +1,59 @@
namespace SabreTools.Models.SecuROM
{
public class MatroshkaEntry
{
/// <summary>
/// File entry path, either 256 or 512 bytes
/// </summary>
/// <remarks>
/// Versions without a key prefix are 256 bytes.
/// Versions with key values either are 256 or 512 bytes.
/// </remarks>
public byte[]? Path { get; set; }
/// <summary>
/// Type of the entry data
/// </summary>
public MatroshkaEntryType EntryType { get; set; }
/// <summary>
/// Data size
/// </summary>
public uint Size { get; set; }
/// <summary>
/// Data offset within the package
/// </summary>
public uint Offset { get; set; }
/// <summary>
/// Unknown value only seen in later versions
/// </summary>
/// <remarks>Possibly indicates that the offset is a 64-bit value</remarks>
public uint? Unknown { get; set; }
/// <summary>
/// File modification time, stored in NTFS filetime.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times"/>
public ulong ModifiedTime { get; set; }
/// <summary>
/// File creation time, stored in NTFS filetime.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times"/>
public ulong CreatedTime { get; set; }
/// <summary>
/// File access time, stored in NTFS filetime.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times"/>
public ulong AccessedTime { get; set; }
/// <summary>
/// MD5 hash of the data
/// </summary>
/// <remarks>16 bytes</remarks>
public byte[]? MD5 { get; set; }
}
}

View File

@@ -0,0 +1,79 @@
namespace SabreTools.Models.SecuROM
{
/// <summary>
/// Securom Matroschka Package PE section
/// </summary>
/// <remarks>
/// Offered by SecuROM, its main purpose seems to be managing some sort
/// of SecuROM-related operation involving multiple temporary files
/// contained within the package. Observed in Release Control executables,
/// Product Activation Revocation executables, and in some regular
/// Product-Activation-protected releases (such as the digital download
/// releases of Neverwinter Nights 2 and Test Drive Unlimited) where the
/// game executable, paul.dll and other PA-related files are stored in
/// the matroschka package.
/// </remarks>
public class MatroshkaPackage
{
/// <summary>
/// "MatR"
/// </summary>
/// <remarks>4 bytes</remarks>
public string? Signature { get; set; }
/// <summary>
/// Number of internal entries
/// </summary>
public uint EntryCount { get; set; }
#region Release Control only
// The combination of the 3 following values have only been seen in
// one of 3 distinct patterns. The meaning of these patterns is unknown.
// - 0 0 1
// - 0 1 1
// - 1 1 1
// These values do not seem to have a link to whether the paths included
// in entries are 256- or 512-byte. There also do not seem to be any links
// between these values and the hex string values.
/// <summary>
/// One of four unknown values only observed on RC matroschka sections
/// </summary>
/// <remarks>Only values of 0 or 1 have been found</remarks>
public uint? UnknownRCValue1 { get; set; }
/// <summary>
/// One of four unknown values only observed on RC matroschka sections
/// </summary>
/// <remarks>Only values of 0 or 1 have been found</remarks>
public uint? UnknownRCValue2 { get; set; }
/// <summary>
/// One of four unknown values only observed on RC matroschka sections
/// </summary>
/// <remarks>Only a value of 1 has been found</remarks>
public uint? UnknownRCValue3 { get; set; }
/// <summary>
/// 32-character hex string
/// </summary>
/// <remarks>
/// Due to encryption on later DFA-encrypted RC executables, this is the
/// most reliable way to identify which executables are using the same key.
/// </remarks>
public string? KeyHexString { get; set; }
/// <summary>
/// Padding for alignment, always 0x00000000
/// </summary>
public uint? Padding { get; set; }
#endregion
/// <summary>
/// Entries array whose length is given by <see cref="EntryCount"/>
/// </summary>
public MatroshkaEntry[]? Entries { get; set; }
}
}