17 Commits
1.5.8 ... 1.6.0

Author SHA1 Message Date
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
13 changed files with 380 additions and 18 deletions

View File

@@ -16,7 +16,10 @@ 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
@@ -24,12 +27,6 @@ jobs:
- name: Run publish script
run: ./publish-nix.sh
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: 'Nuget Package'
path: "*.nupkg,*.snupkg"
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:

View File

@@ -11,7 +11,10 @@ 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

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(Game))]
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

@@ -2,19 +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.8</Version>
<Version>1.6.0</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; }
}
}