Compare commits

...

41 Commits
1.9.2 ... 1.9.6

Author SHA1 Message Date
Matt Nadareski
9574232719 Bump version 2025-09-24 11:06:47 -04:00
Matt Nadareski
676bf584d6 Clean up some console writes 2025-09-24 10:55:39 -04:00
Matt Nadareski
0a4fd01ede Gate MS-CAB warning behind debug to avoid confusion 2025-09-24 10:51:27 -04:00
Matt Nadareski
6cb4023498 Update namespaces in README 2025-09-24 10:50:12 -04:00
Matt Nadareski
8f775dbb3b This should be ObjectIdentifier not strictly ASN.1 2025-09-24 10:47:00 -04:00
Matt Nadareski
91785eab1f Fix tests 2025-09-24 10:40:39 -04:00
Matt Nadareski
4f8751667a Namespace ASN models until Models is updated 2025-09-24 10:40:30 -04:00
Matt Nadareski
ad7508c464 Add some more OIDs 2025-09-24 10:11:21 -04:00
Matt Nadareski
97a9e141ae Add some more OIDs 2025-09-24 09:54:18 -04:00
Matt Nadareski
842a0c3daf Remove useless PathProcessor helper class 2025-09-24 09:43:27 -04:00
Matt Nadareski
b6acde9145 Make consistent with other deserializers 2025-09-24 09:27:24 -04:00
Matt Nadareski
b97dbc2ac5 Simplify extension code slightly 2025-09-24 09:22:23 -04:00
Matt Nadareski
e03852bd7e Simplify ASN.1 integration 2025-09-24 09:19:46 -04:00
Matt Nadareski
0db5de204e Rename TLV deserializer to be more consistent 2025-09-24 09:06:26 -04:00
Matt Nadareski
5fe3f14419 Make TypeLengthValue more model-like 2025-09-24 09:00:48 -04:00
Matt Nadareski
ffe1d9a82d Create and use TypeLengthValue deserializer 2025-09-24 08:54:25 -04:00
Matt Nadareski
11d6560290 Update IO to 1.7.5 2025-09-24 08:30:24 -04:00
Matt Nadareski
e29d8e6728 Integrate ASN.1 code from separate library 2025-09-24 08:25:11 -04:00
Matt Nadareski
918e81a4cb Move extensions to new namespace for cleanliness 2025-09-23 09:58:44 -04:00
Matt Nadareski
ad0ddede38 Fix reference issue 2025-09-22 21:21:43 -04:00
Matt Nadareski
e969b12884 Move wrapper factory up a level 2025-09-22 21:14:33 -04:00
Matt Nadareski
7a5475255f Fix the fake wrapper tests 2025-09-22 20:14:01 -04:00
Matt Nadareski
0ffd436de8 Update packages 2025-09-22 20:07:18 -04:00
Matt Nadareski
4f685187e9 COFF naming doesn't need to continue 2025-09-22 12:56:19 -04:00
Matt Nadareski
2b356b37b1 Sync NE overlay handling with PE 2025-09-22 10:08:46 -04:00
Matt Nadareski
7c56268eb1 Add PFF version 0 detection 2025-09-22 09:35:46 -04:00
Matt Nadareski
970a54e6e3 Add placeholder SFFS wrapper 2025-09-22 09:13:27 -04:00
Matt Nadareski
e35ddf0780 Always overwrite on output, ensure flushed streams 2025-09-21 21:10:40 -04:00
Matt Nadareski
bf35b7c10b Bump version 2025-09-21 12:35:37 -04:00
Matt Nadareski
4026b8ca09 Handle differently-encoded XMLs as textfiles 2025-09-21 12:10:58 -04:00
Matt Nadareski
6d2e2d8c3b Handle some invalid parsing cases that were missed previously 2025-09-21 11:44:58 -04:00
Matt Nadareski
a2b08157cc Fix issue with split resource tables 2025-09-21 11:23:53 -04:00
Matt Nadareski
0108ecf4c1 Bump version 2025-09-21 09:33:55 -04:00
Matt Nadareski
4921da0bb5 Fix issues from porting MPQ from BOS 2025-09-21 00:04:16 -04:00
Matt Nadareski
0c836bb3b1 Try one more thing? 2025-09-20 23:39:49 -04:00
Matt Nadareski
a8e41c1505 Try this 2025-09-20 23:24:12 -04:00
Matt Nadareski
f67e1c9d2b Bump version 2025-09-20 22:38:08 -04:00
Matt Nadareski
f0ce58a79e Move two things out of the lock 2025-09-20 22:32:54 -04:00
Matt Nadareski
b7f782c1b7 Update packages 2025-09-20 22:31:58 -04:00
Matt Nadareski
5e39e169b2 Clean up PE printing a bit 2025-09-20 20:17:38 -04:00
Matt Nadareski
104c5ccad4 XML resources and overlay 2025-09-20 19:48:45 -04:00
104 changed files with 21777 additions and 875 deletions

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.2</Version>
<Version>1.9.6</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -66,8 +66,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="SabreTools.IO" Version="1.7.5" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Serialization;
using SabreTools.Serialization.Wrappers;
namespace ExtractionTool

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.2</Version>
<Version>1.9.6</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -32,7 +32,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="SabreTools.IO" Version="1.7.5" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
</ItemGroup>

View File

@@ -110,6 +110,9 @@ Below is a table of all namespaces within the library and what they represent
| --- | --- |
| `SabreTools.Serialization.CrossModel` | Convert between models; mainly used for metadata files converting to and from a common, `Dictionary`-based model |
| `SabreTools.Serialization.Deserializers` | Convert from external sources to models |
| `SabreTools.Serialization.Extensions` | Extension methods for both models and wrappers |
| `SabreTools.Serialization.Interfaces` | Interfaces used commonly throughout the library |
| `SabreTools.Serialization.ObjectIdentifier` | Object Identifier (OID) parsing |
| `SabreTools.Serialization.Printers` | Export model information in a formatted manner |
| `SabreTools.Serialization.Serializers` | Convert from models to external sources |
| `SabreTools.Serialization.Wrappers` | Classes that wrap serialization and models to allow for including extension properties |

View File

@@ -97,7 +97,11 @@ namespace SabreTools.Serialization.Test.CrossModel
Name = "XXXXXX",
Size = "XXXXXX",
CRC = "XXXXXX",
MD2 = "XXXXXX",
MD4 = "XXXXXX",
MD5 = "XXXXXX",
RIPEMD128 = "XXXXXX",
RIPEMD160 = "XXXXXX",
SHA1 = "XXXXXX",
Merge = "XXXXXX",
Status = "XXXXXX",
@@ -346,7 +350,11 @@ namespace SabreTools.Serialization.Test.CrossModel
Assert.Equal("XXXXXX", rom.Name);
Assert.Equal("XXXXXX", rom.Size);
Assert.Equal("XXXXXX", rom.CRC);
Assert.Equal("XXXXXX", rom.MD2);
Assert.Equal("XXXXXX", rom.MD4);
Assert.Equal("XXXXXX", rom.MD5);
Assert.Equal("XXXXXX", rom.RIPEMD128);
Assert.Equal("XXXXXX", rom.RIPEMD160);
Assert.Equal("XXXXXX", rom.SHA1);
Assert.Equal("XXXXXX", rom.Merge);
Assert.Equal("XXXXXX", rom.Status);

View File

@@ -150,7 +150,11 @@ namespace SabreTools.Serialization.Test.CrossModel
Name = "XXXXXX",
Size = "XXXXXX",
CRC = "XXXXXX",
MD2 = "XXXXXX",
MD4 = "XXXXXX",
MD5 = "XXXXXX",
RIPEMD128 = "XXXXXX",
RIPEMD160 = "XXXXXX",
SHA1 = "XXXXXX",
SHA256 = "XXXXXX",
SHA384 = "XXXXXX",
@@ -460,7 +464,11 @@ namespace SabreTools.Serialization.Test.CrossModel
Assert.Equal("XXXXXX", rom.Name);
Assert.Equal("XXXXXX", rom.Size);
Assert.Equal("XXXXXX", rom.CRC);
Assert.Equal("XXXXXX", rom.MD2);
Assert.Equal("XXXXXX", rom.MD4);
Assert.Equal("XXXXXX", rom.MD5);
Assert.Equal("XXXXXX", rom.RIPEMD128);
Assert.Equal("XXXXXX", rom.RIPEMD160);
Assert.Equal("XXXXXX", rom.SHA1);
Assert.Equal("XXXXXX", rom.SHA256);
Assert.Equal("XXXXXX", rom.SHA384);

View File

@@ -0,0 +1,152 @@
using System.IO;
using System.Linq;
using SabreTools.Models.ASN1;
using SabreTools.Serialization.Deserializers;
using Xunit;
namespace SabreTools.Serialization.Test.Deserializers
{
public class AbstractSyntaxNotationOneTests
{
[Fact]
public void NullArray_Null()
{
byte[]? data = null;
int offset = 0;
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}
[Fact]
public void EmptyArray_Null()
{
byte[]? data = [];
int offset = 0;
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}
[Fact]
public void InvalidArray_Null()
{
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
int offset = 0;
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data, offset);
Assert.Null(actual);
}
[Fact]
public void NullStream_Null()
{
Stream? data = null;
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}
[Fact]
public void EmptyStream_Null()
{
Stream? data = new MemoryStream([]);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}
[Fact]
public void InvalidStream_Null()
{
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}
[Fact]
public void ValidMinimalStream_NotNull()
{
Stream data = new MemoryStream([0x00]);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.NotNull(actual);
var actualSingle = Assert.Single(actual);
Assert.Equal(ASN1Type.V_ASN1_EOC, actualSingle.Type);
Assert.Equal(default, actualSingle.Length);
Assert.Null(actualSingle.Value);
}
[Fact]
public void ValidBoolean_NotNull()
{
Stream data = new MemoryStream([0x01, 0x01, 0x01]);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.NotNull(actual);
var actualSingle = Assert.Single(actual);
Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, actualSingle.Type);
Assert.Equal(1UL, actualSingle.Length);
Assert.NotNull(actualSingle.Value);
byte[]? valueAsArray = actualSingle.Value as byte[];
Assert.NotNull(valueAsArray);
byte actualValue = Assert.Single(valueAsArray);
Assert.Equal(0x01, actualValue);
}
[Theory]
[InlineData(new byte[] { 0x26, 0x81, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x82, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x83, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x84, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x85, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
public void ComplexValue_NotNull(byte[] arr)
{
Stream data = new MemoryStream(arr);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.NotNull(actual);
var actualSingle = Assert.Single(actual);
Assert.Equal(ASN1Type.V_ASN1_CONSTRUCTED | ASN1Type.V_ASN1_OBJECT, actualSingle.Type);
Assert.Equal(3UL, actualSingle.Length);
Assert.NotNull(actualSingle.Value);
TypeLengthValue[]? valueAsArray = actualSingle.Value as TypeLengthValue[];
Assert.NotNull(valueAsArray);
TypeLengthValue actualSub = Assert.Single(valueAsArray);
Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, actualSub.Type);
Assert.Equal(1UL, actualSub.Length);
Assert.NotNull(actualSub.Value);
}
[Theory]
[InlineData(new byte[] { 0x26, 0x80 })]
[InlineData(new byte[] { 0x26, 0x89 })]
public void ComplexValueInvalidLength_Null(byte[] arr)
{
Stream data = new MemoryStream(arr);
var deserializer = new AbstractSyntaxNotationOne();
var actual = deserializer.Deserialize(data);
Assert.Null(actual);
}
}
}

View File

@@ -215,7 +215,11 @@ namespace SabreTools.Serialization.Test.Deserializers
Name = "XXXXXX",
Size = "XXXXXX",
CRC = "XXXXXX",
MD2 = "XXXXXX",
MD4 = "XXXXXX",
MD5 = "XXXXXX",
RIPEMD128 = "XXXXXX",
RIPEMD160 = "XXXXXX",
SHA1 = "XXXXXX",
Merge = "XXXXXX",
Status = "XXXXXX",
@@ -464,7 +468,11 @@ namespace SabreTools.Serialization.Test.Deserializers
Assert.Equal("XXXXXX", rom.Name);
Assert.Equal("XXXXXX", rom.Size);
Assert.Equal("XXXXXX", rom.CRC);
Assert.Equal("XXXXXX", rom.MD2);
Assert.Equal("XXXXXX", rom.MD4);
Assert.Equal("XXXXXX", rom.MD5);
Assert.Equal("XXXXXX", rom.RIPEMD128);
Assert.Equal("XXXXXX", rom.RIPEMD160);
Assert.Equal("XXXXXX", rom.SHA1);
Assert.Equal("XXXXXX", rom.Merge);
Assert.Equal("XXXXXX", rom.Status);

View File

@@ -217,7 +217,11 @@ namespace SabreTools.Serialization.Test.Deserializers
Name = "XXXXXX",
Size = "XXXXXX",
CRC = "XXXXXX",
MD2 = "XXXXXX",
MD4 = "XXXXXX",
MD5 = "XXXXXX",
RIPEMD128 = "XXXXXX",
RIPEMD160 = "XXXXXX",
SHA1 = "XXXXXX",
SHA256 = "XXXXXX",
SHA384 = "XXXXXX",
@@ -512,7 +516,11 @@ namespace SabreTools.Serialization.Test.Deserializers
Assert.Equal("XXXXXX", rom.Name);
Assert.Equal("XXXXXX", rom.Size);
Assert.Equal("XXXXXX", rom.CRC);
Assert.Equal("XXXXXX", rom.MD2);
Assert.Equal("XXXXXX", rom.MD4);
Assert.Equal("XXXXXX", rom.MD5);
Assert.Equal("XXXXXX", rom.RIPEMD128);
Assert.Equal("XXXXXX", rom.RIPEMD160);
Assert.Equal("XXXXXX", rom.SHA1);
Assert.Equal("XXXXXX", rom.SHA256);
Assert.Equal("XXXXXX", rom.SHA384);

View File

@@ -0,0 +1,213 @@
using System;
using SabreTools.Models.ASN1;
using SabreTools.Serialization.Extensions;
using Xunit;
namespace SabreTools.Serialization.Test.Extensions
{
public class TypeLengthValueTests
{
#region Formatting
[Fact]
public void Format_EOC()
{
string expected = "Type: V_ASN1_EOC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ZeroLength()
{
string expected = "Type: V_ASN1_NULL, Length: 0";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = (object?)false };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var boolTlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new Models.ASN1.TypeLengthValue[] { boolTlv } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidDataType()
{
string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = (object?)false };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidLength()
{
string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidBooleanLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidBooleanArrayLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBoolean()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidInteger()
{
string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBitString_NoBits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBitString_Bits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidOctetString()
{
string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidObject()
{
string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUTF8String()
{
string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidPrintableString()
{
string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidTeletexString()
{
string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidIA5String()
{
string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBmpString()
{
string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUnformatted()
{
string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01";
var tlv = new Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,111 @@
using SabreTools.Serialization.ObjectIdentifier;
using Xunit;
namespace SabreTools.Serialization.Test.ObjectIdentifier
{
// These tests are known to be incomplete due to the sheer number
// of possible OIDs that exist. The tests below are a minimal
// representation of functionality to guarantee proper behavior
// not necessarily absolute outputs
public class ParserTests
{
#region ASN.1
[Fact]
public void ASN1Notation_AlwaysNull()
{
ulong[]? values = null;
string? actual = Parser.ParseOIDToASN1Notation(values);
Assert.Null(actual);
}
#endregion
#region Dot Notation
[Fact]
public void DotNotation_NullValues_Null()
{
ulong[]? values = null;
string? actual = Parser.ParseOIDToDotNotation(values);
Assert.Null(actual);
}
[Fact]
public void DotNotation_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = Parser.ParseOIDToDotNotation(values);
Assert.Null(actual);
}
[Fact]
public void DotNotation_Values_Formatted()
{
string expected = "0.1.2.3";
ulong[]? values = [0, 1, 2, 3];
string? actual = Parser.ParseOIDToDotNotation(values);
Assert.Equal(expected, actual);
}
#endregion
#region Modified OID-IRI
[Fact]
public void ModifiedOIDIRI_NullValues_Null()
{
ulong[]? values = null;
string? actual = Parser.ParseOIDToModifiedOIDIRI(values);
Assert.Null(actual);
}
[Fact]
public void ModifiedOIDIRI_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = Parser.ParseOIDToModifiedOIDIRI(values);
Assert.Null(actual);
}
[Fact]
public void ModifiedOIDIRI_Values_Formatted()
{
string expected = "/ITU-T/[question]/2/3";
ulong[]? values = [0, 1, 2, 3];
string? actual = Parser.ParseOIDToModifiedOIDIRI(values);
Assert.Equal(expected, actual);
}
#endregion
#region OID-IRI
[Fact]
public void OIDIRI_NullValues_Null()
{
ulong[]? values = null;
string? actual = Parser.ParseOIDToOIDIRINotation(values);
Assert.Null(actual);
}
[Fact]
public void OIDIRI_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = Parser.ParseOIDToOIDIRINotation(values);
Assert.Null(actual);
}
[Fact]
public void OIDIRI_Values_Formatted()
{
string expected = "/ITU-T/1/2/3";
ulong[]? values = [0, 1, 2, 3];
string? actual = Parser.ParseOIDToOIDIRINotation(values);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -28,7 +28,7 @@
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.7.1" />
<PackageReference Include="SabreTools.Models" Version="1.7.2" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -0,0 +1,61 @@
using System.IO;
using System.Linq;
using SabreTools.Serialization.Wrappers;
using Xunit;
namespace SabreTools.Serialization.Test.Wrappers
{
public class LDSCRYPTTests
{
[Fact]
public void NullArray_Null()
{
byte[]? data = null;
int offset = 0;
var actual = LDSCRYPT.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void EmptyArray_Null()
{
byte[]? data = [];
int offset = 0;
var actual = LDSCRYPT.Create(data, offset);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidArray_Null()
{
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
int offset = 0;
var actual = LDSCRYPT.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void NullStream_Null()
{
Stream? data = null;
var actual = LDSCRYPT.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void EmptyStream_Null()
{
Stream? data = new MemoryStream([]);
var actual = LDSCRYPT.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidStream_Null()
{
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
var actual = LDSCRYPT.Create(data);
Assert.Null(actual);
}
}
}

View File

@@ -0,0 +1,61 @@
using System.IO;
using System.Linq;
using SabreTools.Serialization.Wrappers;
using Xunit;
namespace SabreTools.Serialization.Test.Wrappers
{
public class RealArcadeInstallerTests
{
[Fact]
public void NullArray_Null()
{
byte[]? data = null;
int offset = 0;
var actual = RealArcadeInstaller.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void EmptyArray_Null()
{
byte[]? data = [];
int offset = 0;
var actual = RealArcadeInstaller.Create(data, offset);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidArray_Null()
{
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
int offset = 0;
var actual = RealArcadeInstaller.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void NullStream_Null()
{
Stream? data = null;
var actual = RealArcadeInstaller.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void EmptyStream_Null()
{
Stream? data = new MemoryStream([]);
var actual = RealArcadeInstaller.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidStream_Null()
{
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
var actual = RealArcadeInstaller.Create(data);
Assert.Null(actual);
}
}
}

View File

@@ -0,0 +1,61 @@
using System.IO;
using System.Linq;
using SabreTools.Serialization.Wrappers;
using Xunit;
namespace SabreTools.Serialization.Test.Wrappers
{
public class RealArcadeMezzanineTests
{
[Fact]
public void NullArray_Null()
{
byte[]? data = null;
int offset = 0;
var actual = RealArcadeMezzanine.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void EmptyArray_Null()
{
byte[]? data = [];
int offset = 0;
var actual = RealArcadeMezzanine.Create(data, offset);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidArray_Null()
{
byte[]? data = [.. Enumerable.Repeat<byte>(0xFF, 1024)];
int offset = 0;
var actual = RealArcadeMezzanine.Create(data, offset);
Assert.Null(actual);
}
[Fact]
public void NullStream_Null()
{
Stream? data = null;
var actual = RealArcadeMezzanine.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void EmptyStream_Null()
{
Stream? data = new MemoryStream([]);
var actual = RealArcadeMezzanine.Create(data);
Assert.Null(actual);
}
[Fact(Skip = "This will never pass with the current code")]
public void InvalidStream_Null()
{
Stream? data = new MemoryStream([.. Enumerable.Repeat<byte>(0xFF, 1024)]);
var actual = RealArcadeMezzanine.Create(data);
Assert.Null(actual);
}
}
}

View File

@@ -275,7 +275,11 @@ namespace SabreTools.Serialization.CrossModel
Name = item.ReadString(Models.Metadata.Rom.NameKey),
Size = item.ReadString(Models.Metadata.Rom.SizeKey),
CRC = item.ReadString(Models.Metadata.Rom.CRCKey),
MD2 = item.ReadString(Models.Metadata.Rom.MD2Key),
MD4 = item.ReadString(Models.Metadata.Rom.MD4Key),
MD5 = item.ReadString(Models.Metadata.Rom.MD5Key),
RIPEMD128 = item.ReadString(Models.Metadata.Rom.RIPEMD128Key),
RIPEMD160 = item.ReadString(Models.Metadata.Rom.RIPEMD160Key),
SHA1 = item.ReadString(Models.Metadata.Rom.SHA1Key),
SHA256 = item.ReadString(Models.Metadata.Rom.SHA256Key),
SHA384 = item.ReadString(Models.Metadata.Rom.SHA384Key),

View File

@@ -291,7 +291,11 @@ namespace SabreTools.Serialization.CrossModel
[Models.Metadata.Rom.NameKey] = item.Name,
[Models.Metadata.Rom.SizeKey] = item.Size,
[Models.Metadata.Rom.CRCKey] = item.CRC,
[Models.Metadata.Rom.MD2Key] = item.MD2,
[Models.Metadata.Rom.MD4Key] = item.MD4,
[Models.Metadata.Rom.MD5Key] = item.MD5,
[Models.Metadata.Rom.RIPEMD128Key] = item.RIPEMD128,
[Models.Metadata.Rom.RIPEMD160Key] = item.RIPEMD160,
[Models.Metadata.Rom.SHA1Key] = item.SHA1,
[Models.Metadata.Rom.SHA256Key] = item.SHA256,
[Models.Metadata.Rom.SHA384Key] = item.SHA384,

View File

@@ -28,6 +28,8 @@ namespace SabreTools.Serialization.CrossModel
var md2s = new List<MD2>();
var md4s = new List<MD4>();
var md5s = new List<MD5>();
var ripemd128s = new List<RIPEMD128>();
var ripemd160s = new List<RIPEMD160>();
var sha1s = new List<SHA1>();
var sha256s = new List<SHA256>();
var sha384s = new List<SHA384>();
@@ -44,6 +46,10 @@ namespace SabreTools.Serialization.CrossModel
md4s.AddRange(hashfile.MD4);
if (hashfile.MD5 != null && hashfile.MD5.Length > 0)
md5s.AddRange(hashfile.MD5);
if (hashfile.RIPEMD128 != null && hashfile.RIPEMD128.Length > 0)
ripemd128s.AddRange(hashfile.RIPEMD128);
if (hashfile.RIPEMD160 != null && hashfile.RIPEMD160.Length > 0)
ripemd160s.AddRange(hashfile.RIPEMD160);
if (hashfile.SHA1 != null && hashfile.SHA1.Length > 0)
sha1s.AddRange(hashfile.SHA1);
if (hashfile.SHA256 != null && hashfile.SHA256.Length > 0)
@@ -66,6 +72,10 @@ namespace SabreTools.Serialization.CrossModel
hashfileItem.MD4 = [.. md4s];
if (md5s.Count > 0)
hashfileItem.MD5 = [.. md5s];
if (ripemd128s.Count > 0)
hashfileItem.RIPEMD128 = [.. ripemd128s];
if (ripemd160s.Count > 0)
hashfileItem.RIPEMD160 = [.. ripemd160s];
if (sha1s.Count > 0)
hashfileItem.SHA1 = [.. sha1s];
if (sha256s.Count > 0)
@@ -103,6 +113,12 @@ namespace SabreTools.Serialization.CrossModel
MD5 = hash == HashType.MD5
? Array.ConvertAll(roms, ConvertToMD5)
: null,
RIPEMD128 = hash == HashType.RIPEMD128
? Array.ConvertAll(roms, ConvertToRIPEMD128)
: null,
RIPEMD160 = hash == HashType.RIPEMD160
? Array.ConvertAll(roms, ConvertToRIPEMD160)
: null,
SHA1 = hash == HashType.SHA1
? Array.ConvertAll(roms, ConvertToSHA1)
: null,
@@ -160,6 +176,32 @@ namespace SabreTools.Serialization.CrossModel
return md5;
}
/// <summary>
/// Convert from <see cref="Models.Metadata.Rom"/> to <see cref="Models.Hashfile.RIPEMD128"/>
/// </summary>
private static RIPEMD128 ConvertToRIPEMD128(Models.Metadata.Rom item)
{
var ripemd128 = new RIPEMD128
{
Hash = item.ReadString(Models.Metadata.Rom.RIPEMD128Key),
File = item.ReadString(Models.Metadata.Rom.NameKey),
};
return ripemd128;
}
/// <summary>
/// Convert from <see cref="Models.Metadata.Rom"/> to <see cref="Models.Hashfile.RIPEMD160"/>
/// </summary>
private static RIPEMD160 ConvertToRIPEMD160(Models.Metadata.Rom item)
{
var ripemd160 = new RIPEMD160
{
Hash = item.ReadString(Models.Metadata.Rom.RIPEMD160Key),
File = item.ReadString(Models.Metadata.Rom.NameKey),
};
return ripemd160;
}
/// <summary>
/// Convert from <see cref="Models.Metadata.Rom"/> to <see cref="Models.Hashfile.SFV"/>
/// </summary>

View File

@@ -50,6 +50,10 @@ namespace SabreTools.Serialization.CrossModel
machine[Models.Metadata.Machine.RomKey] = Array.ConvertAll(item.MD4, ConvertToInternalModel);
else if (item.MD5 != null && item.MD5.Length > 0)
machine[Models.Metadata.Machine.RomKey] = Array.ConvertAll(item.MD5, ConvertToInternalModel);
else if (item.RIPEMD128 != null && item.RIPEMD128.Length > 0)
machine[Models.Metadata.Machine.RomKey] = Array.ConvertAll(item.RIPEMD128, ConvertToInternalModel);
else if (item.RIPEMD160 != null && item.RIPEMD160.Length > 0)
machine[Models.Metadata.Machine.RomKey] = Array.ConvertAll(item.RIPEMD160, ConvertToInternalModel);
else if (item.SHA1 != null && item.SHA1.Length > 0)
machine[Models.Metadata.Machine.RomKey] = Array.ConvertAll(item.SHA1, ConvertToInternalModel);
else if (item.SHA256 != null && item.SHA256.Length > 0)
@@ -103,6 +107,32 @@ namespace SabreTools.Serialization.CrossModel
return rom;
}
/// <summary>
/// Convert from <see cref="Models.Hashfile.RIPEMD128"/> to <see cref="Models.Metadata.Rom"/>
/// </summary>
private static Models.Metadata.Rom ConvertToInternalModel(RIPEMD128 item)
{
var rom = new Models.Metadata.Rom
{
[Models.Metadata.Rom.RIPEMD128Key] = item.Hash,
[Models.Metadata.Rom.NameKey] = item.File,
};
return rom;
}
/// <summary>
/// Convert from <see cref="Models.Hashfile.RIPEMD160"/> to <see cref="Models.Metadata.Rom"/>
/// </summary>
private static Models.Metadata.Rom ConvertToInternalModel(RIPEMD160 item)
{
var rom = new Models.Metadata.Rom
{
[Models.Metadata.Rom.RIPEMD160Key] = item.Hash,
[Models.Metadata.Rom.NameKey] = item.File,
};
return rom;
}
/// <summary>
/// Convert from <see cref="Models.Hashfile.SFV"/> to <see cref="Models.Metadata.Rom"/>
/// </summary>

View File

@@ -302,7 +302,11 @@ namespace SabreTools.Serialization.CrossModel
Name = item.ReadString(Models.Metadata.Rom.NameKey),
Size = item.ReadString(Models.Metadata.Rom.SizeKey),
CRC = item.ReadString(Models.Metadata.Rom.CRCKey),
MD2 = item.ReadString(Models.Metadata.Rom.MD2Key),
MD4 = item.ReadString(Models.Metadata.Rom.MD4Key),
MD5 = item.ReadString(Models.Metadata.Rom.MD5Key),
RIPEMD128 = item.ReadString(Models.Metadata.Rom.RIPEMD128Key),
RIPEMD160 = item.ReadString(Models.Metadata.Rom.RIPEMD160Key),
SHA1 = item.ReadString(Models.Metadata.Rom.SHA1Key),
SHA256 = item.ReadString(Models.Metadata.Rom.SHA256Key),
SHA384 = item.ReadString(Models.Metadata.Rom.SHA384Key),

View File

@@ -298,7 +298,11 @@ namespace SabreTools.Serialization.CrossModel
[Models.Metadata.Rom.NameKey] = item.Name,
[Models.Metadata.Rom.SizeKey] = item.Size,
[Models.Metadata.Rom.CRCKey] = item.CRC,
[Models.Metadata.Rom.MD2Key] = item.MD2,
[Models.Metadata.Rom.MD4Key] = item.MD4,
[Models.Metadata.Rom.MD5Key] = item.MD5,
[Models.Metadata.Rom.RIPEMD128Key] = item.RIPEMD128,
[Models.Metadata.Rom.RIPEMD160Key] = item.RIPEMD160,
[Models.Metadata.Rom.SHA1Key] = item.SHA1,
[Models.Metadata.Rom.SHA256Key] = item.SHA256,
[Models.Metadata.Rom.SHA384Key] = item.SHA384,

View File

@@ -70,25 +70,22 @@ namespace SabreTools.Serialization.Deserializers
data.Seek(-4, SeekOrigin.Current);
// Create a record based on the type
switch (type)
return type switch
{
// Known record types
case RecordType.EndOfMediaKeyBlock: return ParseEndOfMediaKeyBlockRecord(data);
case RecordType.ExplicitSubsetDifference: return ParseExplicitSubsetDifferenceRecord(data);
case RecordType.MediaKeyData: return ParseMediaKeyDataRecord(data);
case RecordType.SubsetDifferenceIndex: return ParseSubsetDifferenceIndexRecord(data);
case RecordType.TypeAndVersion: return ParseTypeAndVersionRecord(data);
case RecordType.DriveRevocationList: return ParseDriveRevocationListRecord(data);
case RecordType.HostRevocationList: return ParseHostRevocationListRecord(data);
case RecordType.VerifyMediaKey: return ParseVerifyMediaKeyRecord(data);
case RecordType.Copyright: return ParseCopyrightRecord(data);
RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data),
RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data),
RecordType.MediaKeyData => ParseMediaKeyDataRecord(data),
RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data),
RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data),
RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data),
RecordType.HostRevocationList => ParseHostRevocationListRecord(data),
RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data),
RecordType.Copyright => ParseCopyrightRecord(data),
// Unknown record type
default:
if (recordLength > 4)
_ = data.ReadBytes((int)recordLength - 4);
return null;
}
_ => ParseGenericRecord(data),
};
}
/// <summary>
@@ -235,6 +232,22 @@ namespace SabreTools.Serialization.Deserializers
return obj;
}
/// <summary>
/// Parse a Stream into a GenericRecord
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled GenericRecord on success, null on error</returns>
public static GenericRecord ParseGenericRecord(Stream data)
{
var obj = new GenericRecord();
obj.RecordType = (RecordType)data.ReadByteValue();
obj.RecordLength = data.ReadUInt24LittleEndian();
obj.Data = data.ReadBytes(0x10);
return obj;
}
/// <summary>
/// Parse a Stream into a HostRevocationListEntry
/// </summary>

View File

@@ -0,0 +1,158 @@
using System.Collections.Generic;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Models.ASN1;
namespace SabreTools.Serialization.Deserializers
{
public class AbstractSyntaxNotationOne : BaseBinaryDeserializer<TypeLengthValue[]>
{
/// <inheritdoc/>
public override TypeLengthValue[]? Deserialize(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
try
{
// Cache the current offset
long initialOffset = data.Position;
// Loop through the data and return all top-level values
var topLevelValues = new List<TypeLengthValue>();
while (data.Position < data.Length)
{
var topLevelValue = ParseTypeLengthValue(data);
if (topLevelValue == null)
break;
topLevelValues.Add(topLevelValue);
}
// Return null instead of empty
if (topLevelValues.Count == 0)
return null;
// Return the top-level values
return [.. topLevelValues];
}
catch
{
// Ignore the actual error
return null;
}
}
/// <summary>
/// Parse a Stream into a TypeLengthValue
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled TypeLengthValue on success, null on error</returns>
public TypeLengthValue? ParseTypeLengthValue(Stream data)
{
var obj = new TypeLengthValue();
// Get the type and modifiers
obj.Type = (ASN1Type)data.ReadByteValue();
// If we have an end indicator, we just return
if (obj.Type == ASN1Type.V_ASN1_EOC)
return obj;
// Get the length of the value
ulong? length = ReadLength(data);
if (length == null)
return null;
// Set the length
obj.Length = length.Value;
// Read the value
#if NET20 || NET35
if ((obj.Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
#else
if (obj.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
var valueList = new List<TypeLengthValue>();
long currentIndex = data.Position;
while (data.Position < currentIndex + (long)obj.Length)
{
var value = ParseTypeLengthValue(data);
valueList.Add(value);
}
obj.Value = valueList.ToArray();
}
else
{
// TODO: Get more granular based on type
obj.Value = data.ReadBytes((int)obj.Length);
}
return obj;
}
/// <summary>
/// Reads the length field for a type
/// </summary>
/// <param name="data">Stream representing data to read</param>
/// <returns>The length value read from the array</returns>
private static ulong? ReadLength(Stream data)
{
// Read the first byte, assuming it's the length
byte length = data.ReadByteValue();
// If the bit 7 is not set, then use the value as it is
if ((length & 0x80) == 0)
return length;
// Otherwise, use the value as the number of remaining bytes to read
int bytesToRead = length & ~0x80;
// Assemble the length based on byte count
ulong fullLength = 0;
switch (bytesToRead)
{
case 8:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 7;
case 7:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 6;
case 6:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 5;
case 5:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 4;
case 4:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 3;
case 3:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 2;
case 2:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 1;
case 1:
fullLength |= data.ReadByteValue();
break;
default:
return null;
}
return fullLength;
}
}
}

View File

@@ -21,7 +21,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return default;

View File

@@ -41,8 +41,21 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public virtual TModel? Deserialize(string? path)
{
using var stream = PathProcessor.OpenStream(path);
return Deserialize(stream);
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return default;
// Open the file for deserialization
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Deserialize(stream);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
#endregion

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.CFB;
using SabreTools.Serialization.Extensions;
using static SabreTools.Models.CFB.Constants;
namespace SabreTools.Serialization.Deserializers

View File

@@ -42,8 +42,21 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(string?)"/>
public MetadataFile? Deserialize(string? path, bool quotes)
{
using var stream = PathProcessor.OpenStream(path);
return Deserialize(stream, quotes);
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return default;
// Open the file for deserialization
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Deserialize(stream, quotes);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
#endregion
@@ -57,7 +70,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public MetadataFile? Deserialize(Stream? data, bool quotes)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -460,9 +473,21 @@ namespace SabreTools.Serialization.Deserializers
case "crc":
rom.CRC = kvp.Value;
break;
case "md2":
rom.MD2 = kvp.Value;
break;
case "md4":
rom.MD4 = kvp.Value;
break;
case "md5":
rom.MD5 = kvp.Value;
break;
case "ripemd128":
rom.RIPEMD128 = kvp.Value;
break;
case "ripemd160":
rom.RIPEMD160 = kvp.Value;
break;
case "sha1":
rom.SHA1 = kvp.Value;
break;

View File

@@ -11,7 +11,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;

View File

@@ -41,8 +41,21 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(string?)"/>
public Models.Hashfile.Hashfile? Deserialize(string? path, HashType hash)
{
using var stream = PathProcessor.OpenStream(path);
return Deserialize(stream, hash);
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return default;
// Open the file for deserialization
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Deserialize(stream, hash);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
#endregion
@@ -61,6 +74,8 @@ namespace SabreTools.Serialization.Deserializers
HashType.MD2 => DeserializeMD2(data),
HashType.MD4 => DeserializeMD4(data),
HashType.MD5 => DeserializeMD5(data),
HashType.RIPEMD128 => DeserializeRIPEMD128(data),
HashType.RIPEMD160 => DeserializeRIPEMD160(data),
HashType.SHA1 => DeserializeSHA1(data),
HashType.SHA256 => DeserializeSHA256(data),
HashType.SHA384 => DeserializeSHA384(data),
@@ -74,7 +89,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSFV(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -117,7 +132,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD2(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -161,7 +176,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD4(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -205,7 +220,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeMD5(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -238,10 +253,82 @@ namespace SabreTools.Serialization.Deserializers
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeRIPEMD128(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
// Setup the reader and output
var reader = new StreamReader(data);
var ripemd128List = new List<RIPEMD128>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var ripemd128 = new RIPEMD128
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
ripemd128List.Add(ripemd128);
}
// Assign the hashes to the hashfile and return
if (ripemd128List.Count > 0)
return new Models.Hashfile.Hashfile { RIPEMD128 = [.. ripemd128List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeRIPEMD160(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
// Setup the reader and output
var reader = new StreamReader(data);
var ripemd160List = new List<RIPEMD160>();
// Loop through the rows and parse out values
while (!reader.EndOfStream)
{
// Read and split the line
string? line = reader.ReadLine();
string[]? lineParts = line?.Split([' '], StringSplitOptions.RemoveEmptyEntries);
if (lineParts == null || lineParts.Length < 2)
continue;
// Parse the line into a hash
var ripemd160 = new RIPEMD160
{
Hash = lineParts[0],
File = string.Join(" ", lineParts, 1, lineParts.Length - 1),
};
ripemd160List.Add(ripemd160);
}
// Assign the hashes to the hashfile and return
if (ripemd160List.Count > 0)
return new Models.Hashfile.Hashfile { RIPEMD160 = [.. ripemd160List] };
return null;
}
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA1(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -285,7 +372,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA256(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -329,7 +416,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA384(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -373,7 +460,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSHA512(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;
@@ -417,7 +504,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public Models.Hashfile.Hashfile? DeserializeSpamSum(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return default;

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.InstallShieldCabinet;
using SabreTools.Serialization.Extensions;
using static SabreTools.Models.InstallShieldCabinet.Constants;
namespace SabreTools.Serialization.Deserializers

View File

@@ -56,8 +56,21 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled object on success, null on error</returns>
public T? Deserialize(string? path, Encoding encoding)
{
using var data = PathProcessor.OpenStream(path);
return Deserialize(data, encoding);
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return default;
// Open the file for deserialization
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Deserialize(stream, encoding);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
#endregion

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.NewExecutable;
using SabreTools.Serialization.Extensions;
using static SabreTools.Models.NewExecutable.Constants;
namespace SabreTools.Serialization.Deserializers
@@ -420,7 +421,14 @@ namespace SabreTools.Serialization.Deserializers
obj.RelocationRecords = new RelocationRecord[obj.RelocationRecordCount];
for (int i = 0; i < obj.RelocationRecords.Length; i++)
{
obj.RelocationRecords[i] = ParseRelocationRecord(data);
if (data.Position >= data.Length)
break;
var record = ParseRelocationRecord(data);
if (record == null)
break;
obj.RelocationRecords[i] = record;
}
return obj;
@@ -433,12 +441,20 @@ namespace SabreTools.Serialization.Deserializers
/// <returns>Filled RelocationRecord on success, null on error</returns>
public static RelocationRecord ParseRelocationRecord(Stream data)
{
// Handle partial relocation sections
if (data.Position > data.Length - 4)
return null;
var obj = new RelocationRecord();
obj.SourceType = (RelocationRecordSourceType)data.ReadByteValue();
obj.Flags = (RelocationRecordFlag)data.ReadByteValue();
obj.Offset = data.ReadUInt16LittleEndian();
// Handle incomplete entries
if (data.Position > data.Length - 4)
return obj;
switch (obj.Flags & RelocationRecordFlag.TARGET_MASK)
{
case RelocationRecordFlag.INTERNALREF:

View File

@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.PKZIP;
using static SabreTools.Models.PKZIP.Constants;

View File

@@ -1,30 +0,0 @@
using System.IO;
namespace SabreTools.Serialization.Deserializers
{
internal class PathProcessor
{
/// <summary>
/// Opens a path as a stream in a safe manner, decompressing if needed
/// </summary>
/// <param name="path">Path to open as a stream</param>
/// <returns>Stream representing the file, null on error</returns>
public static Stream? OpenStream(string? path)
{
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return null;
// Open the file for deserialization
return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch
{
// TODO: Handle logging the exception
return null;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;

View File

@@ -1,14 +1,14 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.PortableExecutable;
using SabreTools.Models.SecuROM;
namespace SabreTools.Serialization.Deserializers
{
public class SecuROMAddD : BaseBinaryDeserializer<Models.PortableExecutable.SecuROMAddD>
public class SecuROMAddD : BaseBinaryDeserializer<AddD>
{
/// <inheritdoc/>
public override Models.PortableExecutable.SecuROMAddD? Deserialize(Stream? data)
public override AddD? Deserialize(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
@@ -37,9 +37,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SecuROMAddD on success, null on error</returns>
private static Models.PortableExecutable.SecuROMAddD ParseSecuROMAddD(Stream data)
private static AddD ParseSecuROMAddD(Stream data)
{
var obj = new Models.PortableExecutable.SecuROMAddD();
var obj = new AddD();
obj.Signature = data.ReadUInt32LittleEndian();
obj.EntryCount = data.ReadUInt32LittleEndian();
@@ -49,7 +49,7 @@ namespace SabreTools.Serialization.Deserializers
obj.Build = buildStr.ToCharArray();
obj.Unknown14h = data.ReadBytes(1); // TODO: Figure out how to determine how many bytes are here consistently
obj.Entries = new SecuROMAddDEntry[obj.EntryCount];
obj.Entries = new AddDEntry[obj.EntryCount];
for (int i = 0; i < obj.Entries.Length; i++)
{
var entry = ParseSecuROMAddDEntry(data);
@@ -64,9 +64,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled SecuROMAddDEntry on success, null on error</returns>
private static SecuROMAddDEntry ParseSecuROMAddDEntry(Stream data)
private static AddDEntry ParseSecuROMAddDEntry(Stream data)
{
var obj = new SecuROMAddDEntry();
var obj = new AddDEntry();
obj.PhysicalOffset = data.ReadUInt32LittleEndian();
obj.Length = data.ReadUInt32LittleEndian();

View File

@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.SecuROM;
using static SabreTools.Models.SecuROM.Constants;

View File

@@ -1,12 +1,12 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.SecuROM;
using static SabreTools.Models.SecuROM.Constants;
namespace SabreTools.Serialization.Deserializers
{
// TODO: Cache data blocks during parse
public class SecuROMMatroschkaPackage : BaseBinaryDeserializer<MatroshkaPackage>
{
/// <inheritdoc/>
@@ -96,7 +96,7 @@ namespace SabreTools.Serialization.Deserializers
data.Seek(data.Position + 256, SeekOrigin.Begin);
var tempValue = data.ReadUInt32LittleEndian();
data.Seek(tempPosition, SeekOrigin.Begin);
int gapSize = tempValue == 0 ? 512 : 256;
int pathSize = tempValue == 0 ? 512 : 256;
// Set default value for unknown value checking
bool? hasUnknown = null;
@@ -106,7 +106,8 @@ namespace SabreTools.Serialization.Deserializers
{
var entry = new MatroshkaEntry();
entry.Path = data.ReadBytes(gapSize);
byte[] pathBytes = data.ReadBytes(pathSize);
entry.Path = Encoding.ASCII.GetString(pathBytes);
entry.EntryType = (MatroshkaEntryType)data.ReadUInt32LittleEndian();
entry.Size = data.ReadUInt32LittleEndian();
entry.Offset = data.ReadUInt32LittleEndian();

View File

@@ -49,8 +49,21 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(string?)"/>
public MetadataFile? Deserialize(string? path, char delim)
{
using var stream = PathProcessor.OpenStream(path);
return Deserialize(stream, delim);
try
{
// If we don't have a file
if (string.IsNullOrEmpty(path) || !File.Exists(path))
return default;
// Open the file for deserialization
using var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return Deserialize(stream, delim);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
#endregion
@@ -64,7 +77,7 @@ namespace SabreTools.Serialization.Deserializers
/// <inheritdoc cref="Deserialize(Stream)"/>
public MetadataFile? Deserialize(Stream? data, char delim)
{
// If tthe data is invalid
// If the data is invalid
if (data == null || !data.CanRead)
return null;

View File

@@ -2,7 +2,6 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.WiseInstaller;
using static SabreTools.Models.WiseInstaller.Constants;

View File

@@ -1,8 +1,8 @@
using System.Text;
namespace SabreTools.Serialization
namespace SabreTools.Serialization.Extensions
{
public static partial class Extensions
public static class CFB
{
/// <summary>
/// Decode a MIME-encoded stream name stored as a byte array

View File

@@ -1,8 +1,8 @@
using SabreTools.Models.InstallShieldCabinet;
namespace SabreTools.Serialization
namespace SabreTools.Serialization.Extensions
{
public static partial class Extensions
public static class InstallShieldCabinet
{
#region File Descriptors

View File

@@ -1,8 +1,8 @@
using SabreTools.Models.NewExecutable;
namespace SabreTools.Serialization
namespace SabreTools.Serialization.Extensions
{
public static partial class Extensions
public static class NewExecutable
{
/// <summary>
/// Determine if a resource type information entry is an integer or offset

View File

@@ -4,12 +4,14 @@ using System.IO;
using System.Text;
using System.Xml.Serialization;
using SabreTools.IO.Extensions;
using SabreTools.Models.COFF;
using SabreTools.Models.PortableExecutable;
using SabreTools.Models.PortableExecutable.ResourceEntries;
using SabreTools.Models.PortableExecutable.Resource.Entries;
using SabreTools.Models.SecuROM;
namespace SabreTools.Serialization
namespace SabreTools.Serialization.Extensions
{
public static partial class Extensions
public static class PortableExecutable
{
/// <summary>
/// Convert a relative virtual address to a physical one
@@ -108,9 +110,9 @@ namespace SabreTools.Serialization
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled NB10ProgramDatabase on success, null on error</returns>
public static NB10ProgramDatabase? ParseNB10ProgramDatabase(this byte[] data, ref int offset)
public static Models.PortableExecutable.DebugData.NB10ProgramDatabase? ParseNB10ProgramDatabase(this byte[] data, ref int offset)
{
var obj = new NB10ProgramDatabase();
var obj = new Models.PortableExecutable.DebugData.NB10ProgramDatabase();
obj.Signature = data.ReadUInt32LittleEndian(ref offset);
if (obj.Signature != 0x3031424E)
@@ -130,9 +132,9 @@ namespace SabreTools.Serialization
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled RSDSProgramDatabase on success, null on error</returns>
public static RSDSProgramDatabase? ParseRSDSProgramDatabase(this byte[] data, ref int offset)
public static Models.PortableExecutable.DebugData.RSDSProgramDatabase? ParseRSDSProgramDatabase(this byte[] data, ref int offset)
{
var obj = new RSDSProgramDatabase();
var obj = new Models.PortableExecutable.DebugData.RSDSProgramDatabase();
obj.Signature = data.ReadUInt32LittleEndian(ref offset);
if (obj.Signature != 0x53445352)
@@ -155,10 +157,10 @@ namespace SabreTools.Serialization
/// <param name="data">Data to parse into overlay data</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled SecuROMAddD on success, null on error</returns>
public static SecuROMAddD? ParseSecuROMAddD(this byte[] data, ref int offset)
public static AddD? ParseSecuROMAddD(this byte[] data, ref int offset)
{
// Read in the table
var obj = new SecuROMAddD();
var obj = new AddD();
obj.Signature = data.ReadUInt32LittleEndian(ref offset);
if (obj.Signature != 0x44646441)
@@ -186,7 +188,7 @@ namespace SabreTools.Serialization
obj.Unknown14h = data.ReadBytes(ref offset, bytesToRead);
obj.Entries = new SecuROMAddDEntry[obj.EntryCount];
obj.Entries = new AddDEntry[obj.EntryCount];
for (int i = 0; i < obj.EntryCount; i++)
{
obj.Entries[i] = ParseSecuROMAddDEntry(data, ref offset);
@@ -201,9 +203,9 @@ namespace SabreTools.Serialization
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled SecuROMAddDEntry on success, null on error</returns>
public static SecuROMAddDEntry ParseSecuROMAddDEntry(this byte[] data, ref int offset)
public static AddDEntry ParseSecuROMAddDEntry(this byte[] data, ref int offset)
{
var obj = new SecuROMAddDEntry();
var obj = new AddDEntry();
obj.PhysicalOffset = data.ReadUInt32LittleEndian(ref offset);
obj.Length = data.ReadUInt32LittleEndian(ref offset);
@@ -229,7 +231,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into an accelerator table resource</param>
/// <returns>A filled accelerator table resource on success, null on error</returns>
public static AcceleratorTableEntry[]? AsAcceleratorTableResource(this ResourceDataEntry? entry)
public static AcceleratorTableEntry[]? AsAcceleratorTableResource(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have data that's invalid for this resource type, we can't do anything
if (entry?.Data == null || entry.Data.Length % 8 != 0)
@@ -258,7 +260,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a side-by-side assembly manifest</param>
/// <returns>A filled side-by-side assembly manifest on success, null on error</returns>
public static AssemblyManifest? AsAssemblyManifest(this ResourceDataEntry? entry)
public static AssemblyManifest? AsAssemblyManifest(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -280,7 +282,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a dialog box</param>
/// <returns>A filled dialog box on success, null on error</returns>
public static DialogBoxResource? AsDialogBox(this ResourceDataEntry? entry)
public static DialogBoxResource? AsDialogBox(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -729,7 +731,7 @@ namespace SabreTools.Serialization
#region Creation data
dialogItemTemplate.CreationDataSize = entry.Data.ReadUInt16LittleEndian(ref offset);
if (dialogItemTemplate.CreationDataSize != 0)
if (dialogItemTemplate.CreationDataSize != 0 && dialogItemTemplate.CreationDataSize + offset < entry.Data.Length)
dialogItemTemplate.CreationData = entry.Data.ReadBytes(ref offset, dialogItemTemplate.CreationDataSize);
#endregion
@@ -757,7 +759,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a font group</param>
/// <returns>A filled font group on success, null on error</returns>
public static FontGroupHeader? AsFontGroup(this ResourceDataEntry? entry)
public static FontGroupHeader? AsFontGroup(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -826,7 +828,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a menu</param>
/// <returns>A filled menu on success, null on error</returns>
public static MenuResource? AsMenu(this ResourceDataEntry? entry)
public static MenuResource? AsMenu(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -922,7 +924,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a message table resource</param>
/// <returns>A filled message table resource on success, null on error</returns>
public static MessageResourceData? AsMessageResourceData(this ResourceDataEntry? entry)
public static MessageResourceData? AsMessageResourceData(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1010,10 +1012,10 @@ namespace SabreTools.Serialization
// Align to the DWORD boundary if we're not at the end
data.AlignToBoundary(ref offset, 4);
var stringFileInfoChildren = new List<StringTable>();
var stringFileInfoChildren = new List<Models.PortableExecutable.Resource.Entries.StringTable>();
while ((offset - currentOffset) < stringFileInfo.Length)
{
var stringTable = new StringTable();
var stringTable = new Models.PortableExecutable.Resource.Entries.StringTable();
stringTable.Length = data.ReadUInt16LittleEndian(ref offset);
stringTable.ValueLength = data.ReadUInt16LittleEndian(ref offset);
@@ -1067,7 +1069,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a string table resource</param>
/// <returns>A filled string table resource on success, null on error</returns>
public static Dictionary<int, string?>? AsStringTable(this ResourceDataEntry? entry)
public static Dictionary<int, string?>? AsStringTable(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1159,7 +1161,7 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="entry">Resource data entry to parse into a version info resource</param>
/// <returns>A filled version info resource on success, null on error</returns>
public static VersionInfo? AsVersionInfo(this ResourceDataEntry? entry)
public static VersionInfo? AsVersionInfo(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1356,10 +1358,10 @@ namespace SabreTools.Serialization
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled ResourceHeader on success, null on error</returns>
public static ResourceHeader ParseResourceHeader(this byte[] data, ref int offset)
public static Models.PortableExecutable.Resource.ResourceHeader ParseResourceHeader(this byte[] data, ref int offset)
{
// Read in the table
var obj = new ResourceHeader();
var obj = new Models.PortableExecutable.Resource.ResourceHeader();
obj.DataSize = data.ReadUInt32LittleEndian(ref offset);
obj.HeaderSize = data.ReadUInt32LittleEndian(ref offset);

View File

@@ -0,0 +1,160 @@
using System;
using System.Numerics;
using System.Text;
using SabreTools.Models.ASN1;
using SabreTools.Serialization.ObjectIdentifier;
namespace SabreTools.Serialization.Extensions
{
public static class TypeLengthValue
{
/// <summary>
/// Format a TypeLengthValue as a string
/// </summary>
/// <param name="paddingLevel">Padding level of the item when formatting</param>
/// <returns>String representing the TypeLengthValue, if possible</returns>
public static string Format(this Models.ASN1.TypeLengthValue tlv, int paddingLevel = 0)
{
// Create the left-padding string
string padding = new(' ', paddingLevel);
// Create the string builder
var formatBuilder = new StringBuilder();
// Append the type
formatBuilder.Append($"{padding}Type: {tlv.Type}");
if (tlv.Type == ASN1Type.V_ASN1_EOC)
return formatBuilder.ToString();
// Append the length
formatBuilder.Append($", Length: {tlv.Length}");
if (tlv.Length == 0)
return formatBuilder.ToString();
// If we have a constructed type
#if NET20 || NET35
if ((tlv.Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
#else
if (tlv.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
if (tlv.Value is not Models.ASN1.TypeLengthValue[] valueAsObjectArray)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
}
formatBuilder.Append(", Value:\n");
for (int i = 0; i < valueAsObjectArray.Length; i++)
{
var child = valueAsObjectArray[i];
string childString = child.Format(paddingLevel + 1);
formatBuilder.Append($"{childString}\n");
}
return formatBuilder.ToString().TrimEnd('\n');
}
// Get the value as a byte array
if (tlv.Value is not byte[] valueAsByteArray)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
}
else if (valueAsByteArray.Length == 0)
{
formatBuilder.Append(", Value: [NO DATA]");
return formatBuilder.ToString();
}
// If we have a primitive type
switch (tlv.Type)
{
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-boolean"/>
case ASN1Type.V_ASN1_BOOLEAN:
if (tlv.Length > 1)
formatBuilder.Append($" [Expected length of 1]");
else if (valueAsByteArray.Length > 1)
formatBuilder.Append($" [Expected value length of 1]");
bool booleanValue = valueAsByteArray[0] != 0x00;
formatBuilder.Append($", Value: {booleanValue}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-integer"/>
case ASN1Type.V_ASN1_INTEGER:
Array.Reverse(valueAsByteArray);
var integerValue = new BigInteger(valueAsByteArray);
formatBuilder.Append($", Value: {integerValue}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bit-string"/>
case ASN1Type.V_ASN1_BIT_STRING:
// TODO: Read into a BitArray and print that out instead?
int unusedBits = valueAsByteArray[0];
if (unusedBits == 0)
formatBuilder.Append($", Value with {unusedBits} unused bits");
else
formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray, 1).Replace('-', ' ')}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-octet-string"/>
case ASN1Type.V_ASN1_OCTET_STRING:
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier"/>
/// <see cref="http://snmpsharpnet.com/index.php/2009/03/02/ber-encoding-and-decoding-oid-values/"/>
case ASN1Type.V_ASN1_OBJECT:
// Derive array of values
ulong[] objectNodes = Parser.ParseDERIntoArray(valueAsByteArray, tlv.Length);
// Append the dot and modified OID-IRI notations
string? dotNotationString = Parser.ParseOIDToDotNotation(objectNodes);
string? oidIriString = Parser.ParseOIDToOIDIRINotation(objectNodes);
formatBuilder.Append($", Value: {dotNotationString} ({oidIriString})");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-utf8string"/>
case ASN1Type.V_ASN1_UTF8STRING:
formatBuilder.Append($", Value: {Encoding.UTF8.GetString(valueAsByteArray)}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-printablestring"/>
case ASN1Type.V_ASN1_PRINTABLESTRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
//case ASN1Type.V_ASN1_T61STRING:
case ASN1Type.V_ASN1_TELETEXSTRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-ia5string"/>
case ASN1Type.V_ASN1_IA5STRING:
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
break;
case ASN1Type.V_ASN1_UTCTIME:
string utctimeString = Encoding.ASCII.GetString(valueAsByteArray);
if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime))
formatBuilder.Append($", Value: {utctimeDateTime:yyyy-MM-dd HH:mm:ss}");
else
formatBuilder.Append($", Value: {utctimeString}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bmpstring"/>
case ASN1Type.V_ASN1_BMPSTRING:
formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}");
break;
default:
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
break;
}
// Return the formatted string
return formatBuilder.ToString();
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Text.RegularExpressions;
namespace SabreTools.Serialization
namespace SabreTools.Serialization.Extensions
{
public static partial class Extensions
public static class WiseScript
{
/// <summary>
/// Convert a Wise function ID to the formal action name

View File

@@ -5,39 +5,39 @@
namespace SabreTools.Serialization
{
public delegate TResult Func<out TResult>();
internal delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
internal delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
internal delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
internal delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
internal delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
}
#endif

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Serialization.ObjectIdentifier
{
/// <summary>
/// Methods related to Object Identifiers (OID) and ASN.1 notation
/// </summary>
public static partial class Parser
{
/// <summary>
/// Parse an OID in separated-value notation into ASN.1 notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <returns>ASN.1 formatted string, if possible</returns>
/// <remarks>
public static string? ParseOIDToASN1Notation(ulong[]? values)
{
// TODO: Once the modified OID-IRI formatting is done, make an ASN.1 notation version
return null;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,907 @@
using System;
using System.Text;
namespace SabreTools.Serialization.ObjectIdentifier
{
/// <summary>
/// Methods related to Object Identifiers (OID) and OID-IRI formatting
/// </summary>
public static partial class Parser
{
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="https://oid-base.com/"/>
public static string? ParseOIDToOIDIRINotation(ulong[]? values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// Set the initial index
int index = 0;
// Get a string builder for the path
var nameBuilder = new StringBuilder();
// Try to parse the standard value
string? standard = ParseOIDToOIDIRINotation(values, ref index);
if (standard == null)
return null;
// Add the standard value to the output
nameBuilder.Append(standard);
// If we have no more items
if (index == values.Length)
return nameBuilder.ToString();
// Add trailing items as just values
nameBuilder.Append("/");
// Get the remaining values in a new array
var remainingValues = new ulong[values.Length - index];
Array.Copy(values, index, remainingValues, 0, remainingValues.Length);
// Convert the values and append to the builder
var stringValues = Array.ConvertAll(remainingValues, v => v.ToString());
nameBuilder.Append(string.Join("/", stringValues));
// Create and return the string
return nameBuilder.ToString();
}
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="https://oid-base.com/"/>
private static string? ParseOIDToOIDIRINotation(ulong[]? values, ref int index)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// If we have an invalid index, we can't do anything
if (index < 0 || index >= values.Length)
return null;
#region Start
var oidPath = string.Empty;
switch (values[index++])
{
case 0: goto oid_0;
case 1: goto oid_1;
case 2: goto oid_2;
default: return oidPath;
}
#endregion
// itu-t, ccitt, itu-r
#region 0.*
oid_0:
oidPath += "/ITU-T";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_0_0;
case 2: return $"{oidPath}/Administration";
case 3: return $"{oidPath}/Network-Operator";
case 4: return $"{oidPath}/Identified-Organization";
case 5: return "/ITU-R/R-Recommendation";
case 9: return $"{oidPath}/Data";
default: return $"{oidPath}/{values[index - 1]}";
}
;
// recommendation
#region 0.0.*
oid_0_0:
oidPath += "/Recommendation";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/A",
2 => $"{oidPath}/B",
3 => $"{oidPath}/C",
4 => $"{oidPath}/D",
5 => $"{oidPath}/E",
6 => $"{oidPath}/F",
7 => $"{oidPath}/G",
8 => $"{oidPath}/H",
9 => $"{oidPath}/I",
10 => $"{oidPath}/J",
11 => $"{oidPath}/K",
12 => $"{oidPath}/L",
13 => $"{oidPath}/M",
14 => $"{oidPath}/N",
15 => $"{oidPath}/O",
16 => $"{oidPath}/P",
17 => $"{oidPath}/Q",
18 => $"{oidPath}/R",
19 => $"{oidPath}/S",
20 => $"{oidPath}/T",
21 => $"{oidPath}/U",
22 => $"{oidPath}/V",
24 => $"{oidPath}/X",
25 => $"{oidPath}/Y",
26 => $"{oidPath}/Z",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// iso
#region 1.*
oid_1:
oidPath += "/ISO";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: return $"{oidPath}/Standard";
case 1: return $"{oidPath}/Registration-Authority";
case 2: goto oid_1_2;
case 3: return $"{oidPath}/Identified-Organization";
default: return $"{oidPath}/{values[index - 1]}";
}
// member-body
#region 1.2.*
oid_1_2:
oidPath += "/Member-Body";
if (index == values.Length) return oidPath;
return values[index++] switch
{
36 => $"{oidPath}/AU",
40 => $"{oidPath}/AT",
56 => $"{oidPath}/BE",
124 => $"{oidPath}/CA",
156 => $"{oidPath}/CN",
203 => $"{oidPath}/CZ",
208 => $"{oidPath}/DK",
246 => $"{oidPath}/FI",
250 => $"{oidPath}/FR",
276 => $"{oidPath}/DE",
300 => $"{oidPath}/GR",
344 => $"{oidPath}/HK",
372 => $"{oidPath}/IE",
392 => $"{oidPath}/JP",
398 => $"{oidPath}/KZ",
410 => $"{oidPath}/KR",
498 => $"{oidPath}/MD",
528 => $"{oidPath}/NL",
566 => $"{oidPath}/NG",
578 => $"{oidPath}/NO",
616 => $"{oidPath}/PL",
643 => $"{oidPath}/RU",
702 => $"{oidPath}/SG",
752 => $"{oidPath}/SE",
804 => $"{oidPath}/UA",
826 => $"{oidPath}/GB",
840 => $"{oidPath}/US",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// joint-iso-itu-t, joint-iso-ccitt
#region 2.*
oid_2:
oidPath += "/Joint-ISO-ITU-T";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 1: return "/ASN.1";
case 16: oidPath = string.Empty; goto oid_2_16;
case 17: return $"{oidPath}/Registration-Procedures";
case 23: return $"{oidPath}/International-Organizations";
case 25: goto oid_2_25;
case 27: return "/Tag-Based";
case 28: return $"{oidPath}/ITS";
case 41: return "/BIP";
case 42: oidPath = string.Empty; goto oid_2_42;
case 48: oidPath = string.Empty; goto oid_2_48;
case 49: oidPath = string.Empty; goto oid_2_49;
case 50: return "/OIDResolutionSystem";
case 51: return "/GS1";
case 52: return $"{oidPath}/UAV";
case 999: return $"{oidPath}/Example";
default: return $"{oidPath}/{values[index - 1]}";
}
// country
#region 2.16.*
oid_2_16:
oidPath += "/Country";
if (index == values.Length) return oidPath;
return values[index++] switch
{
4 => $"{oidPath}AF",
8 => $"{oidPath}AL",
12 => $"{oidPath}DZ",
20 => $"{oidPath}AD",
24 => $"{oidPath}AO",
28 => $"{oidPath}AG",
31 => $"{oidPath}AZ",
32 => $"{oidPath}AR",
36 => $"{oidPath}AU",
40 => $"{oidPath}AT",
44 => $"{oidPath}BS",
48 => $"{oidPath}BH",
50 => $"{oidPath}BD",
51 => $"{oidPath}AM",
52 => $"{oidPath}BB",
56 => $"{oidPath}BE",
60 => $"{oidPath}BM",
64 => $"{oidPath}BT",
68 => $"{oidPath}BO",
70 => $"{oidPath}BA",
72 => $"{oidPath}BW",
76 => $"{oidPath}BR",
84 => $"{oidPath}BZ",
90 => $"{oidPath}SB",
96 => $"{oidPath}BN",
100 => $"{oidPath}BG",
104 => $"{oidPath}MM",
108 => $"{oidPath}BI",
112 => $"{oidPath}BY",
116 => $"{oidPath}KH",
120 => $"{oidPath}CM",
124 => $"{oidPath}CA",
132 => $"{oidPath}CV",
140 => $"{oidPath}CF",
144 => $"{oidPath}LK",
148 => $"{oidPath}TD",
152 => $"{oidPath}CL",
156 => $"{oidPath}CN",
158 => $"{oidPath}TW",
170 => $"{oidPath}CO",
174 => $"{oidPath}KM",
178 => $"{oidPath}CG",
180 => $"{oidPath}CD",
188 => $"{oidPath}CR",
191 => $"{oidPath}HR",
192 => $"{oidPath}CU",
196 => $"{oidPath}CY",
203 => $"{oidPath}CZ",
204 => $"{oidPath}BJ",
208 => $"{oidPath}DK",
212 => $"{oidPath}DM",
214 => $"{oidPath}DO",
218 => $"{oidPath}EC",
222 => $"{oidPath}SV",
226 => $"{oidPath}GQ",
231 => $"{oidPath}ET",
232 => $"{oidPath}ER",
233 => $"{oidPath}EE",
242 => $"{oidPath}FJ",
246 => $"{oidPath}FI",
250 => $"{oidPath}FR",
262 => $"{oidPath}DJ",
266 => $"{oidPath}GA",
268 => $"{oidPath}GE",
270 => $"{oidPath}GM",
275 => $"{oidPath}PS",
276 => $"{oidPath}DE",
288 => $"{oidPath}GH",
296 => $"{oidPath}KI",
300 => $"{oidPath}GR",
308 => $"{oidPath}GD",
320 => $"{oidPath}GT",
324 => $"{oidPath}GN",
328 => $"{oidPath}GY",
332 => $"{oidPath}HT",
336 => $"{oidPath}VA",
340 => $"{oidPath}HN",
344 => $"{oidPath}HK",
348 => $"{oidPath}HU",
352 => $"{oidPath}IS",
356 => $"{oidPath}IN",
360 => $"{oidPath}ID",
364 => $"{oidPath}IR",
368 => $"{oidPath}IQ",
372 => $"{oidPath}IE",
376 => $"{oidPath}IL",
380 => $"{oidPath}IT",
384 => $"{oidPath}CI",
388 => $"{oidPath}JM",
392 => $"{oidPath}JP",
398 => $"{oidPath}KZ",
400 => $"{oidPath}JO",
404 => $"{oidPath}KE",
408 => $"{oidPath}KP",
410 => $"{oidPath}KR",
414 => $"{oidPath}KW",
417 => $"{oidPath}KG",
418 => $"{oidPath}LA",
422 => $"{oidPath}LB",
426 => $"{oidPath}LS",
428 => $"{oidPath}LV",
430 => $"{oidPath}LR",
434 => $"{oidPath}LY",
438 => $"{oidPath}LI",
440 => $"{oidPath}LT",
442 => $"{oidPath}LU",
450 => $"{oidPath}MG",
454 => $"{oidPath}MW",
458 => $"{oidPath}MY",
462 => $"{oidPath}MV",
466 => $"{oidPath}ML",
470 => $"{oidPath}MT",
478 => $"{oidPath}MR",
480 => $"{oidPath}MU",
484 => $"{oidPath}MX",
492 => $"{oidPath}MC",
496 => $"{oidPath}MN",
498 => $"{oidPath}MD",
499 => $"{oidPath}ME",
504 => $"{oidPath}MA",
508 => $"{oidPath}MZ",
512 => $"{oidPath}OM",
516 => $"{oidPath}NA",
520 => $"{oidPath}NR",
524 => $"{oidPath}NP",
528 => $"{oidPath}NL",
530 => $"{oidPath}AN",
548 => $"{oidPath}VU",
554 => $"{oidPath}NZ",
558 => $"{oidPath}NI",
562 => $"{oidPath}NE",
566 => $"{oidPath}NG",
578 => $"{oidPath}NO",
583 => $"{oidPath}FM",
584 => $"{oidPath}MH",
585 => $"{oidPath}PW",
586 => $"{oidPath}PK",
591 => $"{oidPath}PA",
598 => $"{oidPath}PG",
600 => $"{oidPath}PY",
604 => $"{oidPath}PE",
608 => $"{oidPath}PH",
616 => $"{oidPath}PL",
620 => $"{oidPath}PT",
624 => $"{oidPath}GW",
626 => $"{oidPath}TL",
634 => $"{oidPath}QA",
642 => $"{oidPath}RO",
643 => $"{oidPath}RU",
646 => $"{oidPath}RW",
659 => $"{oidPath}KN",
662 => $"{oidPath}LC",
670 => $"{oidPath}VC",
674 => $"{oidPath}SM",
678 => $"{oidPath}ST",
682 => $"{oidPath}SA",
686 => $"{oidPath}SN",
688 => $"{oidPath}RS",
690 => $"{oidPath}SC",
694 => $"{oidPath}SL",
702 => $"{oidPath}SG",
703 => $"{oidPath}SK",
704 => $"{oidPath}VN",
705 => $"{oidPath}SI",
706 => $"{oidPath}SO",
710 => $"{oidPath}ZA",
716 => $"{oidPath}ZW",
724 => $"{oidPath}ES",
728 => $"{oidPath}SS",
729 => $"{oidPath}SD",
740 => $"{oidPath}SR",
748 => $"{oidPath}SZ",
752 => $"{oidPath}SE",
756 => $"{oidPath}CH",
760 => $"{oidPath}SY",
762 => $"{oidPath}TJ",
764 => $"{oidPath}TH",
768 => $"{oidPath}TG",
776 => $"{oidPath}TO",
780 => $"{oidPath}TT",
784 => $"{oidPath}AE",
788 => $"{oidPath}TN",
792 => $"{oidPath}TR",
795 => $"{oidPath}TM",
798 => $"{oidPath}TV",
800 => $"{oidPath}UG",
804 => $"{oidPath}UA",
807 => $"{oidPath}MK",
818 => $"{oidPath}EG",
826 => $"{oidPath}GB",
834 => $"{oidPath}TZ",
840 => $"{oidPath}US",
854 => $"{oidPath}BF",
858 => $"{oidPath}UY",
860 => $"{oidPath}UZ",
862 => $"{oidPath}VE",
882 => $"{oidPath}WS",
887 => $"{oidPath}YE",
894 => $"{oidPath}ZM",
_ => $"{oidPath}{values[index - 1]}",
};
#endregion
// uuid [TODO: Requires 128-bit values]
#region 2.25.*
oid_2_25:
oidPath += "/UUID";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/00000000-0000-0000-0000-000000000000",
//case 288786655511405443130567505384701230: return $"{oidPath}/00379e48-0a2b-1085-b288-0002a5d5fd2e";
//case 987895962269883002155146617097157934: return $"{oidPath}/00be4308-0c89-1085-8ea0-0002a5d5fd2e";
//case 1858228783942312576083372383319475483: return $"{oidPath}/0165e1c0-a655-11e0-95b8-0002a5d5c51b";
//case 2474299330026746002885628159579243803: return $"{oidPath}/01dc8860-25fb-11da-82b2-0002a5d5c51b";
//case 3263645701162998421821186056373271854: return $"{oidPath}/02748e28-08c4-1085-b21d-0002a5d5fd2e";
//case 3325839809379844461264382260940242222: return $"{oidPath}/02808890-0ad8-1085-9bdf-0002a5d5fd2e";
// TODO: Left off at http://www.oid-info.com/get/2.25.3664154270495270126161055518190585115
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// telebiometrics
#region 2.42.*
oid_2_42:
oidPath += "/Telebiometrics";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_0;
case 1: goto oid_2_42_1;
case 2: goto oid_2_42_2;
case 3: goto oid_2_42_3;
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.0.*
oid_2_42_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.0.0.*
oid_2_42_0_0:
oidPath += "/Main_Module";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// tmm
#region 2.42.1.*
oid_2_42_1:
oidPath += "/TMM";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_1_0;
case 1: goto oid_2_42_1_1;
case 2: goto oid_2_42_1_2;
case 3: goto oid_2_42_1_3;
case 4: return $"{oidPath}/Practitioners";
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.1.0.*
oid_2_42_1_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_1_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.1.0.0.*
oid_2_42_1_0_0:
oidPath += "/Main";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/First_Version",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// measures, metric
#region 2.42.1.1.*
oid_2_42_1_1:
oidPath += "/Measures";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 1: goto oid_2_42_1_1_1;
case 2: return $"{oidPath}/Units";
case 3: return $"{oidPath}";
case 4: return $"{oidPath}/Conditions";
case 5: goto oid_2_42_1_1_5;
default: return $"{oidPath}/{values[index - 1]}";
}
// quantities
#region 2.42.1.1.1.*
oid_2_42_1_1_1:
oidPath += "/Quantities";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// methods
#region 2.42.1.1.5.*
oid_2_42_1_1_5:
oidPath += "/Methods";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// fields-of-study, scientific
#region 2.42.1.2.*
oid_2_42_1_2:
oidPath += "/Fields_of_Study";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// modalities, sensory
#region 2.42.1.3.*
oid_2_42_1_3:
oidPath += "/Modalities";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Tango",
2 => $"{oidPath}/Video",
3 => $"{oidPath}/Audio",
4 => $"{oidPath}/Chemo",
5 => $"{oidPath}/Radio",
6 => $"{oidPath}/Calor",
7 => $"{oidPath}/Electro",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// human-physiology
#region 2.42.2.*
oid_2_42_2:
oidPath += "/Human_Physiology";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_2_0;
case 1: goto oid_2_42_2_1;
case 2: return $"{oidPath}/Symbol_Combinations";
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.2.0.*
oid_2_42_2_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_2_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.2.0.0.*
oid_2_42_2_0_0:
oidPath += "/Main_Module";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/First_Version",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// symbols
#region 2.42.2.1.*
oid_2_42_2_1:
oidPath += "/Symbols";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Tango_in",
2 => $"{oidPath}/Video_in",
3 => $"{oidPath}/Audio_in",
4 => $"{oidPath}/Chemo_in",
5 => $"{oidPath}/Radio_in",
6 => $"{oidPath}/Calor_in",
7 => $"{oidPath}/Tango_out",
8 => $"{oidPath}/Video_out",
9 => $"{oidPath}/Audio_out",
10 => $"{oidPath}/Chemo_out",
11 => $"{oidPath}/Radio_out",
12 => $"{oidPath}/Calor_out",
13 => $"{oidPath}/Safe",
14 => $"{oidPath}/Threshold",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.*
oid_2_42_3:
oidPath += "/E_Health_Protocol";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_3_0;
case 1: return $"{oidPath}/[Patient schemes]";
case 2: return $"{oidPath}/[Medical staff schemes]";
case 3: return $"{oidPath}/[Observer schemes]";
case 4: return $"{oidPath}/[Pharmaceutical schemes]";
case 5: return $"{oidPath}/[Laboratory schemes]";
case 6: return $"{oidPath}/[Drug manufacturer schemes]";
case 7: return $"{oidPath}/[Medical device schemes]";
case 8: return $"{oidPath}/[Medical software schemes]";
case 9: return $"{oidPath}/[Medical insurance schemes]";
case 10: return $"{oidPath}/[Medical record schemes]";
default: return $"{oidPath}/{values[index - 1]}";
}
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.0.*
oid_2_42_3_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_3_0_0;
case 1: goto oid_2_42_3_0_1;
case 2: goto oid_2_42_3_0_2;
case 3: goto oid_2_42_3_0_3;
case 4: goto oid_2_42_3_0_4;
case 5: goto oid_2_42_3_0_5;
default: return $"{oidPath}/{values[index - 1]}";
}
// identification
#region 2.42.3.0.0.*
oid_2_42_3_0_0:
oidPath += "/Identification";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// set-up
#region 2.42.3.0.1.*
oid_2_42_3_0_1:
oidPath += "/Setup";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// send-and-ack
#region 2.42.3.0.2.*
oid_2_42_3_0_2:
oidPath += "/Send-and-ack";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// command-response
#region 2.42.3.0.3.*
oid_2_42_3_0_3:
oidPath += "/Command-response";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// quantity-and-units
#region 2.42.3.0.4.*
oid_2_42_3_0_4:
oidPath += "/Quantities_And_Units";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// examples
#region 2.42.3.0.5.*
oid_2_42_3_0_5:
oidPath += "/Examples";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/Command_Response",
1 => $"{oidPath}/Data_Message",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
#endregion
#endregion
// cybersecurity
#region 2.48.*
oid_2_48:
oidPath += "/Cybersecurity";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Country",
2 => $"{oidPath}/International-Org",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// alerting
#region 2.49.*
oid_2_49:
oidPath += "/Alerting";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/WMO",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
namespace SabreTools.Serialization.ObjectIdentifier
{
/// <summary>
/// Methods related to Object Identifiers (OID)
/// </summary>
public static partial class Parser
{
// TODO: ulong[] isn't going to work. If we can use .NET 7, we can use UInt128
// We might want to look into storing all values as GUID? I don't remember if
// you can do value comparisions between an integral value and a GUID, though.
/// <summary>
/// Parse an OID in DER-encoded byte notation into a list of values
/// </summary>
/// <param name="data">Byte array representing the data to read</param>
/// <param name="length">Total length of the data according to the DER TLV</param>
/// <returns>Array of values representing the OID</returns>
public static ulong[] ParseDERIntoArray(byte[] data, ulong length)
{
// The first byte contains nodes 1 and 2
int firstNode = Math.DivRem(data[0], 40, out int secondNode);
// Create a list for all nodes
List<ulong> nodes = [(ulong)firstNode, (ulong)secondNode];
// All other nodes are encoded uniquely
int offset = 1;
while (offset < (long)length)
{
// If bit 7 is not set
if ((data[offset] & 0x80) == 0)
{
nodes.Add(data[offset]);
offset++;
continue;
}
// Otherwise, read the encoded value in a loop
ulong dotValue = 0;
bool doneProcessing = false;
do
{
// Shift the current encoded value
dotValue <<= 7;
// If we have a leading zero byte, we're at the end
if ((data[offset] & 0x80) == 0)
doneProcessing = true;
// Clear the top byte
unchecked { data[offset] &= (byte)~0x80; }
// Add the new value to the result
dotValue |= data[offset];
// Increment the offset
offset++;
} while (offset < data.Length && !doneProcessing);
// Add the parsed value to the output
nodes.Add(dotValue);
}
return [.. nodes];
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace SabreTools.Serialization.ObjectIdentifier
{
/// <summary>
/// Methods related to Object Identifiers (OID) and dot notation
/// </summary>
public static partial class Parser
{
/// <summary>
/// Parse an OID in separated-value notation into dot notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <returns>List of values representing the dot notation</returns>
public static string? ParseOIDToDotNotation(ulong[]? values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
var stringValues = Array.ConvertAll(values, v => v.ToString());
return string.Join(".", [.. stringValues]);
}
}
}

View File

@@ -1,6 +1,5 @@
using System.Text;
using SabreTools.Models.BSP;
using SabreTools.Models.TAR;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Text;
using SabreTools.Models.NewExecutable;
using SabreTools.Serialization.Extensions;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers

View File

@@ -2,12 +2,12 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using SabreTools.ASN1;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.COFF;
using SabreTools.Models.COFF.SymbolTableEntries;
using SabreTools.Models.PortableExecutable;
using SabreTools.Models.PortableExecutable.COFFSymbolTableEntries;
using SabreTools.Models.PortableExecutable.ResourceEntries;
using SabreTools.Models.PortableExecutable.Resource.Entries;
using SabreTools.Serialization.Extensions;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers
@@ -28,22 +28,36 @@ namespace SabreTools.Serialization.Printers
Print(builder, executable.Stub?.Header);
// Header
Print(builder, executable.Signature, executable.COFFFileHeader);
Print(builder, executable.Signature, executable.FileHeader);
Print(builder, executable.OptionalHeader, executable.SectionTable);
// Tables
// COFF Tables
Print(builder, executable.SectionTable);
Print(builder, executable.COFFSymbolTable);
Print(builder, executable.COFFStringTable);
Print(builder, executable.SymbolTable);
Print(builder, executable.StringTable);
// Export Table
Print(builder, executable.ExportDirectoryTable, executable.SectionTable);
Print(builder, executable.ExportAddressTable, executable.SectionTable);
Print(builder, executable.NamePointerTable);
Print(builder, executable.OrdinalTable);
Print(builder, executable.ExportNameTable);
// Import Table
Print(builder, executable.ImportDirectoryTable, executable.SectionTable);
Print(builder, executable.ImportLookupTables, executable.SectionTable);
Print(builder, executable.ImportAddressTables, executable.SectionTable);
Print(builder, executable.HintNameTable);
// Resource Table
Print(builder, executable.ResourceDirectoryTable, executable.SectionTable);
Print(builder, executable.AttributeCertificateTable);
Print(builder, executable.DelayLoadDirectoryTable);
Print(builder, executable.DelayLoadDirectoryTable, executable.SectionTable);
// Named Sections
Print(builder, executable.BaseRelocationTable, executable.SectionTable);
Print(builder, executable.DebugTable);
Print(builder, executable.ExportTable);
Print(builder, executable.ImportTable, executable.SectionTable);
Print(builder, executable.ResourceDirectoryTable);
}
private static void Print(StringBuilder builder, Models.MSDOS.ExecutableHeader? header)
@@ -83,13 +97,13 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
private static void Print(StringBuilder builder, string? signature, COFFFileHeader? header)
private static void Print(StringBuilder builder, string? signature, FileHeader? header)
{
builder.AppendLine(" COFF File Header Information:");
builder.AppendLine(" File Header Information:");
builder.AppendLine(" -------------------------");
if (header == null)
{
builder.AppendLine(" No COFF file header");
builder.AppendLine(" No file header");
builder.AppendLine();
return;
}
@@ -105,7 +119,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
private static void Print(StringBuilder builder, OptionalHeader? header, SectionHeader[]? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.OptionalHeader? header, SectionHeader[]? table)
{
builder.AppendLine(" Optional Header Information:");
builder.AppendLine(" -------------------------");
@@ -127,10 +141,7 @@ namespace SabreTools.Serialization.Printers
if (header.Magic == OptionalHeaderMagicNumber.PE32)
builder.AppendLine(header.BaseOfData, " Base of data");
if (header.Magic == OptionalHeaderMagicNumber.PE32)
builder.AppendLine(header.ImageBase_PE32, " Image base");
else
builder.AppendLine(header.ImageBase_PE32Plus, " Image base");
builder.AppendLine(header.ImageBase, " Image base");
builder.AppendLine(header.SectionAlignment, " Section alignment");
builder.AppendLine(header.FileAlignment, " File alignment");
builder.AppendLine(header.MajorOperatingSystemVersion, " Major operating system version");
@@ -145,20 +156,10 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(header.CheckSum, " Checksum");
builder.AppendLine($" Subsystem: {header.Subsystem} (0x{header.Subsystem:X})");
builder.AppendLine($" DLL characteristics: {header.DllCharacteristics} (0x{header.DllCharacteristics:X})");
if (header.Magic == OptionalHeaderMagicNumber.PE32)
{
builder.AppendLine(header.SizeOfStackReserve_PE32, " Size of stack reserve");
builder.AppendLine(header.SizeOfStackCommit_PE32, " Size of stack commit");
builder.AppendLine(header.SizeOfHeapReserve_PE32, " Size of heap reserve");
builder.AppendLine(header.SizeOfHeapCommit_PE32, " Size of heap commit");
}
else
{
builder.AppendLine(header.SizeOfStackReserve_PE32Plus, " Size of stack reserve");
builder.AppendLine(header.SizeOfStackCommit_PE32Plus, " Size of stack commit");
builder.AppendLine(header.SizeOfHeapReserve_PE32Plus, " Size of heap reserve");
builder.AppendLine(header.SizeOfHeapCommit_PE32Plus, " Size of heap commit");
}
builder.AppendLine(header.SizeOfStackReserve, " Size of stack reserve");
builder.AppendLine(header.SizeOfStackCommit, " Size of stack commit");
builder.AppendLine(header.SizeOfHeapReserve, " Size of heap reserve");
builder.AppendLine(header.SizeOfHeapCommit, " Size of heap commit");
builder.AppendLine(header.LoaderFlags, " Loader flags");
builder.AppendLine(header.NumberOfRvaAndSizes, " Number of data-directory entries");
@@ -314,11 +315,11 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, BaseEntry[]? entries)
{
builder.AppendLine(" COFF Symbol Table Information:");
builder.AppendLine(" Symbol Table Information:");
builder.AppendLine(" -------------------------");
if (entries == null || entries.Length == 0)
{
builder.AppendLine(" No COFF symbol table items");
builder.AppendLine(" No symbol table items");
builder.AppendLine();
return;
}
@@ -344,7 +345,7 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, StandardRecord entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (Standard Record)");
builder.AppendLine($" Symbol Table Entry {i} (Standard Record)");
if (entry.ShortName != null)
{
builder.AppendLine(entry.ShortName, " Short name", Encoding.ASCII);
@@ -355,7 +356,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(entry.Offset, " Offset");
}
builder.AppendLine(entry.Value, " Value");
builder.AppendLine(entry.SectionNumber, " Section number");
builder.AppendLine($" Section number: {entry.SectionNumber} (0x{entry.SectionNumber:X})");
builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})");
builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})");
builder.AppendLine(entry.NumberOfAuxSymbols, " Number of aux symbols");
@@ -363,7 +364,7 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, FunctionDefinition entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (Function Definition)");
builder.AppendLine($" Symbol Table Entry {i} (Function Definition)");
builder.AppendLine(entry.TagIndex, " Tag index");
builder.AppendLine(entry.TotalSize, " Total size");
builder.AppendLine(entry.PointerToLinenumber, " Pointer to linenumber");
@@ -373,7 +374,7 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, Descriptor entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (.bf and .ef Symbol)");
builder.AppendLine($" Symbol Table Entry {i} (.bf and .ef Symbol)");
builder.AppendLine(entry.Unused1, " Unused");
builder.AppendLine(entry.Linenumber, " Linenumber");
builder.AppendLine(entry.Unused2, " Unused");
@@ -383,7 +384,7 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, WeakExternal entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (Weak External)");
builder.AppendLine($" Symbol Table Entry {i} (Weak External)");
builder.AppendLine(entry.TagIndex, " Tag index");
builder.AppendLine(entry.Characteristics, " Characteristics");
builder.AppendLine(entry.Unused, " Unused");
@@ -391,13 +392,13 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, FileRecord entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (File)");
builder.AppendLine($" Symbol Table Entry {i} (File)");
builder.AppendLine(entry.FileName, " File name", Encoding.ASCII);
}
private static void Print(StringBuilder builder, SectionDefinition entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (Section Defintion)");
builder.AppendLine($" Symbol Table Entry {i} (Section Defintion)");
builder.AppendLine(entry.Length, " Length");
builder.AppendLine(entry.NumberOfRelocations, " Number of relocations");
builder.AppendLine(entry.NumberOfLinenumbers, " Number of linenumbers");
@@ -409,20 +410,20 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, CLRTokenDefinition entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (CLR Token Defintion)");
builder.AppendLine($" Symbol Table Entry {i} (CLR Token Defintion)");
builder.AppendLine(entry.AuxType, " Aux type");
builder.AppendLine(entry.Reserved1, " Reserved");
builder.AppendLine(entry.SymbolTableIndex, " Symbol table index");
builder.AppendLine(entry.Reserved2, " Reserved");
}
private static void Print(StringBuilder builder, COFFStringTable? stringTable)
private static void Print(StringBuilder builder, Models.COFF.StringTable? stringTable)
{
builder.AppendLine(" COFF String Table Information:");
builder.AppendLine(" String Table Information:");
builder.AppendLine(" -------------------------");
if (stringTable?.Strings == null || stringTable.Strings.Length == 0)
{
builder.AppendLine(" No COFF string table items");
builder.AppendLine(" No string table items");
builder.AppendLine();
return;
}
@@ -431,14 +432,14 @@ namespace SabreTools.Serialization.Printers
for (int i = 0; i < stringTable.Strings.Length; i++)
{
string? entry = stringTable.Strings[i];
builder.AppendLine($" COFF String Table Entry {i})");
builder.AppendLine($" String Table Entry {i})");
builder.AppendLine(entry, " Value");
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, AttributeCertificateTableEntry[]? entries)
private static void Print(StringBuilder builder, Models.PortableExecutable.AttributeCertificate.Entry[]? entries)
{
builder.AppendLine(" Attribute Certificate Table Information:");
builder.AppendLine(" -------------------------");
@@ -449,6 +450,9 @@ namespace SabreTools.Serialization.Printers
return;
}
// Create the deserializer
var deserializer = new Deserializers.AbstractSyntaxNotationOne();
for (int i = 0; i < entries.Length; i++)
{
var entry = entries[i];
@@ -468,7 +472,7 @@ namespace SabreTools.Serialization.Printers
}
else
{
var topLevelValues = AbstractSyntaxNotationOne.Parse(entry.Certificate, 0);
var topLevelValues = deserializer.Deserialize(entry.Certificate, 0);
if (topLevelValues == null)
{
builder.AppendLine(" INVALID DATA FOUND");
@@ -476,7 +480,7 @@ namespace SabreTools.Serialization.Printers
}
else
{
foreach (TypeLengthValue tlv in topLevelValues)
foreach (Models.ASN1.TypeLengthValue tlv in topLevelValues)
{
string tlvString = tlv.Format(paddingLevel: 4);
builder.AppendLine(tlvString);
@@ -502,7 +506,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void Print(StringBuilder builder, DelayLoadDirectoryTable? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.DelayLoad.DirectoryTable? table, SectionHeader[]? sections)
{
builder.AppendLine(" Delay-Load Directory Table Information:");
builder.AppendLine(" -------------------------");
@@ -514,17 +518,22 @@ namespace SabreTools.Serialization.Printers
}
builder.AppendLine(table.Attributes, " Attributes");
builder.AppendLine(table.Name, " Name RVA");
builder.AppendLine(table.NameRVA, " Name RVA");
builder.AppendLine(table.NameRVA.ConvertVirtualAddress(sections), " Name physical address");
builder.AppendLine(table.ModuleHandle, " Module handle");
builder.AppendLine(table.DelayImportAddressTable, " Delay import address table RVA");
builder.AppendLine(table.DelayImportAddressTable.ConvertVirtualAddress(sections), " Delay import address table physical address");
builder.AppendLine(table.DelayImportNameTable, " Delay import name table RVA");
builder.AppendLine(table.DelayImportNameTable.ConvertVirtualAddress(sections), " Delay import name table physical address");
builder.AppendLine(table.BoundDelayImportTable, " Bound delay import table RVA");
builder.AppendLine(table.BoundDelayImportTable.ConvertVirtualAddress(sections), " Bound delay import table physical address");
builder.AppendLine(table.UnloadDelayImportTable, " Unload delay import table RVA");
builder.AppendLine(table.UnloadDelayImportTable.ConvertVirtualAddress(sections), " Unload delay import table physical address");
builder.AppendLine(table.TimeStamp, " Timestamp");
builder.AppendLine();
}
private static void Print(StringBuilder builder, BaseRelocationBlock[]? entries, SectionHeader[]? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.BaseRelocation.Block[]? entries, SectionHeader[]? sections)
{
builder.AppendLine(" Base Relocation Table Information:");
builder.AppendLine(" -------------------------");
@@ -541,7 +550,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine($" Base Relocation Table Entry {i}");
builder.AppendLine(baseRelocationTableEntry.PageRVA, " Page RVA");
builder.AppendLine(baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(table), " Page physical address");
builder.AppendLine(baseRelocationTableEntry.PageRVA.ConvertVirtualAddress(sections), " Page physical address");
builder.AppendLine(baseRelocationTableEntry.BlockSize, " Block size");
builder.AppendLine();
@@ -567,7 +576,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
private static void Print(StringBuilder builder, DebugTable? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.DebugData.Table? table)
{
builder.AppendLine(" Debug Table Information:");
builder.AppendLine(" -------------------------");
@@ -578,7 +587,6 @@ namespace SabreTools.Serialization.Printers
return;
}
// TODO: If more sections added, model this after the Export Table
for (int i = 0; i < table.DebugDirectoryTable.Length; i++)
{
var entry = table.DebugDirectoryTable[i];
@@ -597,174 +605,176 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
private static void Print(StringBuilder builder, ExportTable? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.Export.DirectoryTable? table, SectionHeader[]? sections)
{
builder.AppendLine(" Export Table Information:");
builder.AppendLine(value: " Export Directory Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
{
builder.AppendLine(" No export table");
builder.AppendLine();
return;
}
builder.AppendLine(" Export Directory Table Information:");
builder.AppendLine(" -------------------------");
if (table.ExportDirectoryTable == null)
{
builder.AppendLine(" No export directory table");
}
else
{
builder.AppendLine(table.ExportDirectoryTable.ExportFlags, " Export flags");
builder.AppendLine(table.ExportDirectoryTable.TimeDateStamp, " Time/Date stamp");
builder.AppendLine(table.ExportDirectoryTable.MajorVersion, " Major version");
builder.AppendLine(table.ExportDirectoryTable.MinorVersion, " Minor version");
builder.AppendLine(table.ExportDirectoryTable.NameRVA, " Name RVA");
builder.AppendLine(table.ExportDirectoryTable.Name, " Name");
builder.AppendLine(table.ExportDirectoryTable.OrdinalBase, " Ordinal base");
builder.AppendLine(table.ExportDirectoryTable.AddressTableEntries, " Address table entries");
builder.AppendLine(table.ExportDirectoryTable.NumberOfNamePointers, " Number of name pointers");
builder.AppendLine(table.ExportDirectoryTable.ExportAddressTableRVA, " Export address table RVA");
builder.AppendLine(table.ExportDirectoryTable.NamePointerRVA, " Name pointer table RVA");
builder.AppendLine(table.ExportDirectoryTable.OrdinalTableRVA, " Ordinal table RVA");
builder.AppendLine(table.ExportFlags, " Export flags");
builder.AppendLine(table.TimeDateStamp, " Time/Date stamp");
builder.AppendLine(table.MajorVersion, " Major version");
builder.AppendLine(table.MinorVersion, " Minor version");
builder.AppendLine(table.NameRVA, " Name RVA");
builder.AppendLine(table.NameRVA.ConvertVirtualAddress(sections), " Name physical address");
builder.AppendLine(table.Name, " Name");
builder.AppendLine(table.OrdinalBase, " Ordinal base");
builder.AppendLine(table.AddressTableEntries, " Address table entries");
builder.AppendLine(table.NumberOfNamePointers, " Number of name pointers");
builder.AppendLine(table.ExportAddressTableRVA, " Export address table RVA");
builder.AppendLine(table.ExportAddressTableRVA.ConvertVirtualAddress(sections), " Export address table physical address");
builder.AppendLine(table.NamePointerRVA, " Name pointer table RVA");
builder.AppendLine(table.NamePointerRVA.ConvertVirtualAddress(sections), " Name pointer table physical address");
builder.AppendLine(table.OrdinalTableRVA, " Ordinal table RVA");
builder.AppendLine(table.OrdinalTableRVA.ConvertVirtualAddress(sections), " Ordinal table physical address");
}
builder.AppendLine();
}
builder.AppendLine(" Export Address Table Information:");
builder.AppendLine(" -------------------------");
if (table.ExportAddressTable == null || table.ExportAddressTable.Length == 0)
private static void Print(StringBuilder builder, Models.PortableExecutable.Export.AddressTableEntry[]? table, SectionHeader[]? sections)
{
builder.AppendLine(" Export Address Table Information:");
builder.AppendLine(" -------------------------");
if (table == null || table.Length == 0)
{
builder.AppendLine(" No export address table items");
builder.AppendLine(" No export address table items");
}
else
{
for (int i = 0; i < table.ExportAddressTable.Length; i++)
for (int i = 0; i < table.Length; i++)
{
var entry = table.ExportAddressTable[i];
var entry = table[i];
builder.AppendLine($" Export Address Table Entry {i}");
builder.AppendLine(entry.ExportRVA, " Export RVA / Forwarder RVA");
}
}
builder.AppendLine();
builder.AppendLine(" Name Pointer Table Information:");
builder.AppendLine(" -------------------------");
if (table.NamePointerTable?.Pointers == null || table.NamePointerTable.Pointers.Length == 0)
{
builder.AppendLine(" No name pointer table items");
}
else
{
for (int i = 0; i < table.NamePointerTable.Pointers.Length; i++)
{
var entry = table.NamePointerTable.Pointers[i];
builder.AppendLine($" Name Pointer Table Entry {i}");
builder.AppendLine(entry, " Pointer");
}
}
builder.AppendLine();
builder.AppendLine(" Ordinal Table Information:");
builder.AppendLine(" -------------------------");
if (table.OrdinalTable?.Indexes == null || table.OrdinalTable.Indexes.Length == 0)
{
builder.AppendLine(" No ordinal table items");
}
else
{
for (int i = 0; i < table.OrdinalTable.Indexes.Length; i++)
{
var entry = table.OrdinalTable.Indexes[i];
builder.AppendLine($" Ordinal Table Entry {i}");
builder.AppendLine(entry, " Index");
}
}
builder.AppendLine();
builder.AppendLine(" Export Name Table Information:");
builder.AppendLine(" -------------------------");
if (table.ExportNameTable?.Strings == null || table.ExportNameTable.Strings.Length == 0)
{
builder.AppendLine(" No export name table items");
}
else
{
for (int i = 0; i < table.ExportNameTable.Strings.Length; i++)
{
var entry = table.ExportNameTable.Strings[i];
builder.AppendLine($" Export Name Table Entry {i}");
builder.AppendLine(entry, " String");
builder.AppendLine($" Export Address Table Entry {i}");
builder.AppendLine(entry.ExportRVA, " Export / Forwarder RVA");
builder.AppendLine(entry.ExportRVA.ConvertVirtualAddress(sections), " Export / Forwarder physical address");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ImportTable? table, SectionHeader[]? sectionTable)
private static void Print(StringBuilder builder, Models.PortableExecutable.Export.NamePointerTable? table)
{
builder.AppendLine(" Import Table Information:");
builder.AppendLine(" Export Name Pointer Table Information:");
builder.AppendLine(" -------------------------");
if (table == null)
if (table?.Pointers == null || table.Pointers.Length == 0)
{
builder.AppendLine(" No import table");
builder.AppendLine();
return;
}
builder.AppendLine();
builder.AppendLine(" Import Directory Table Information:");
builder.AppendLine(" -------------------------");
if (table.ImportDirectoryTable == null || table.ImportDirectoryTable.Length == 0)
{
builder.AppendLine(" No import directory table items");
builder.AppendLine(" No export name pointer table items");
}
else
{
for (int i = 0; i < table.ImportDirectoryTable.Length; i++)
for (int i = 0; i < table.Pointers.Length; i++)
{
var entry = table.ImportDirectoryTable[i];
var entry = table.Pointers[i];
builder.AppendLine($" Import Directory Table Entry {i}");
builder.AppendLine(entry.ImportLookupTableRVA, " Import lookup table RVA");
builder.AppendLine(entry.ImportLookupTableRVA.ConvertVirtualAddress(sectionTable), " Import lookup table Physical Address");
builder.AppendLine(entry.TimeDateStamp, " Time/Date stamp");
builder.AppendLine(entry.ForwarderChain, " Forwarder chain");
builder.AppendLine(entry.NameRVA, " Name RVA");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.ImportAddressTableRVA, " Import address table RVA");
builder.AppendLine(entry.ImportAddressTableRVA.ConvertVirtualAddress(sectionTable), " Import address table Physical Address");
builder.AppendLine($" Export Name Pointer Table Entry {i}");
builder.AppendLine(entry, " Pointer");
}
}
builder.AppendLine();
}
builder.AppendLine(" Import Lookup Tables Information:");
builder.AppendLine(" -------------------------");
if (table.ImportLookupTables == null || table.ImportLookupTables.Count == 0)
private static void Print(StringBuilder builder, Models.PortableExecutable.Export.OrdinalTable? table)
{
builder.AppendLine(" Export Ordinal Table Information:");
builder.AppendLine(" -------------------------");
if (table?.Indexes == null || table.Indexes.Length == 0)
{
builder.AppendLine(" No import lookup tables");
builder.AppendLine(" No export ordinal table items");
}
else
{
foreach (var kvp in table.ImportLookupTables)
for (int i = 0; i < table.Indexes.Length; i++)
{
var entry = table.Indexes[i];
builder.AppendLine($" Export Ordinal Table Entry {i}");
builder.AppendLine(entry, " Index");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Models.PortableExecutable.Export.NameTable? table)
{
builder.AppendLine(" Export Name Table Information:");
builder.AppendLine(" -------------------------");
if (table?.Strings == null || table.Strings.Length == 0)
{
builder.AppendLine(" No export name table items");
}
else
{
for (int i = 0; i < table.Strings.Length; i++)
{
var entry = table.Strings[i];
builder.AppendLine($" Export Name Table Entry {i}");
builder.AppendLine(entry, " String");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Models.PortableExecutable.Import.DirectoryTableEntry[]? table, SectionHeader[]? sections)
{
builder.AppendLine(" Import Directory Table Information:");
builder.AppendLine(" -------------------------");
if (table == null || table.Length == 0)
{
builder.AppendLine(" No import directory table items");
}
else
{
for (int i = 0; i < table.Length; i++)
{
var entry = table[i];
builder.AppendLine($" Import Directory Table Entry {i}");
builder.AppendLine(entry.ImportLookupTableRVA, " Import lookup table RVA");
builder.AppendLine(entry.ImportLookupTableRVA.ConvertVirtualAddress(sections), " Import lookup table physical address");
builder.AppendLine(entry.TimeDateStamp, " Time/Date stamp");
builder.AppendLine(entry.ForwarderChain, " Forwarder chain");
builder.AppendLine(entry.NameRVA, " Name RVA");
builder.AppendLine(entry.NameRVA.ConvertVirtualAddress(sections), " Name physical address");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine(entry.ImportAddressTableRVA, " Import address table RVA");
builder.AppendLine(entry.ImportAddressTableRVA.ConvertVirtualAddress(sections), " Import address table physical address");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, Dictionary<int, Models.PortableExecutable.Import.LookupTableEntry[]?>? tables, SectionHeader[]? sections)
{
builder.AppendLine(" Import Lookup Tables Information:");
builder.AppendLine(" -------------------------");
if (tables == null || tables.Count == 0)
{
builder.AppendLine(" No import lookup tables");
}
else
{
foreach (var kvp in tables)
{
int index = kvp.Key;
var importLookupTable = kvp.Value;
builder.AppendLine();
builder.AppendLine($" Import Lookup Table {index} Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine($" Import Lookup Table {index} Information:");
builder.AppendLine(" -------------------------");
if (importLookupTable == null || importLookupTable.Length == 0)
{
builder.AppendLine(" No import lookup table items");
builder.AppendLine(" No import lookup table items");
continue;
}
@@ -772,42 +782,45 @@ namespace SabreTools.Serialization.Printers
{
var entry = importLookupTable[i];
builder.AppendLine($" Import Lookup Table {index} Entry {i}");
builder.AppendLine(entry.OrdinalNameFlag, " Ordinal/Name flag");
builder.AppendLine($" Import Lookup Table {index} Entry {i}");
builder.AppendLine(entry.OrdinalNameFlag, " Ordinal/Name flag");
if (entry.OrdinalNameFlag)
{
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
else
{
builder.AppendLine(entry.HintNameTableRVA, " Hint/Name table RVA");
builder.AppendLine(entry.HintNameTableRVA.ConvertVirtualAddress(sectionTable), " Hint/Name table Physical Address");
builder.AppendLine(entry.HintNameTableRVA, " Hint/Name table RVA");
builder.AppendLine(entry.HintNameTableRVA.ConvertVirtualAddress(sections), " Hint/Name table physical address");
}
}
}
}
builder.AppendLine();
}
builder.AppendLine(" Import Address Tables Information:");
builder.AppendLine(" -------------------------");
if (table.ImportAddressTables == null || table.ImportAddressTables.Count == 0)
private static void Print(StringBuilder builder, Dictionary<int, Models.PortableExecutable.Import.AddressTableEntry[]?>? tables, SectionHeader[]? sections)
{
builder.AppendLine(" Import Address Tables Information:");
builder.AppendLine(" -------------------------");
if (tables == null || tables.Count == 0)
{
builder.AppendLine(" No import address tables");
builder.AppendLine(" No import address tables");
}
else
{
foreach (var kvp in table.ImportAddressTables)
foreach (var kvp in tables)
{
int index = kvp.Key;
var importAddressTable = kvp.Value;
builder.AppendLine();
builder.AppendLine($" Import Address Table {index} Information:");
builder.AppendLine(" -------------------------");
builder.AppendLine($" Import Address Table {index} Information:");
builder.AppendLine(" -------------------------");
if (importAddressTable == null || importAddressTable.Length == 0)
{
builder.AppendLine(" No import address table items");
builder.AppendLine(" No import address table items");
continue;
}
@@ -815,45 +828,48 @@ namespace SabreTools.Serialization.Printers
{
var entry = importAddressTable[i];
builder.AppendLine($" Import Address Table {index} Entry {i}");
builder.AppendLine(entry.OrdinalNameFlag, " Ordinal/Name flag");
builder.AppendLine($" Import Address Table {index} Entry {i}");
builder.AppendLine(entry.OrdinalNameFlag, " Ordinal/Name flag");
if (entry.OrdinalNameFlag)
{
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
builder.AppendLine(entry.OrdinalNumber, " Ordinal number");
}
else
{
builder.AppendLine(entry.HintNameTableRVA, " Hint/Name table RVA");
builder.AppendLine(entry.HintNameTableRVA.ConvertVirtualAddress(sectionTable), " Hint/Name table Physical Address");
builder.AppendLine(entry.HintNameTableRVA, " Hint/Name table RVA");
builder.AppendLine(entry.HintNameTableRVA.ConvertVirtualAddress(sections), " Hint/Name table physical address");
}
}
}
}
builder.AppendLine();
}
builder.AppendLine(" Hint/Name Table Information:");
builder.AppendLine(" -------------------------");
if (table.HintNameTable == null || table.HintNameTable.Length == 0)
private static void Print(StringBuilder builder, Models.PortableExecutable.Import.HintNameTableEntry[]? table)
{
builder.AppendLine(" Import Hint/Name Table Information:");
builder.AppendLine(" -------------------------");
if (table == null || table.Length == 0)
{
builder.AppendLine(" No hint/name table items");
builder.AppendLine(" No import hint/name table items");
}
else
{
for (int i = 0; i < table.HintNameTable.Length; i++)
for (int i = 0; i < table.Length; i++)
{
var entry = table.HintNameTable[i];
var entry = table[i];
builder.AppendLine($" Hint/Name Table Entry {i}");
builder.AppendLine(entry.Hint, " Hint");
builder.AppendLine(entry.Name, " Name");
builder.AppendLine($" Hint/Name Table Entry {i}");
builder.AppendLine(entry.Hint, " Hint");
builder.AppendLine(entry.Name, " Name");
}
}
builder.AppendLine();
}
private static void Print(StringBuilder builder, ResourceDirectoryTable? table)
private static void Print(StringBuilder builder, Models.PortableExecutable.Resource.DirectoryTable? table, SectionHeader[]? sections)
{
builder.AppendLine(" Resource Directory Table Information:");
builder.AppendLine(" -------------------------");
@@ -864,11 +880,11 @@ namespace SabreTools.Serialization.Printers
return;
}
Print(table, level: 0, types: [], builder);
Print(builder, table, level: 0, types: [], sections);
builder.AppendLine();
}
private static void Print(ResourceDirectoryTable table, int level, List<object> types, StringBuilder builder)
private static void Print(StringBuilder builder, Models.PortableExecutable.Resource.DirectoryTable table, int level, List<object> types, SectionHeader[]? sections)
{
string padding = new(' ', (level + 1) * 2);
@@ -903,12 +919,12 @@ namespace SabreTools.Serialization.Printers
else
newTypes.Add(entry.IntegerID);
PrintResourceDirectoryEntry(entry, level + 1, newTypes, builder);
Print(builder, entry, level + 1, newTypes, sections);
}
}
}
private static void PrintResourceDirectoryEntry(ResourceDirectoryEntry entry, int level, List<object> types, StringBuilder builder)
private static void Print(StringBuilder builder, Models.PortableExecutable.Resource.DirectoryEntry entry, int level, List<object> types, SectionHeader[]? sections)
{
string padding = new(' ', (level + 1) * 2);
@@ -924,12 +940,12 @@ namespace SabreTools.Serialization.Printers
}
if (entry.DataEntry != null)
PrintResourceDataEntry(entry.DataEntry, level: level + 1, types, builder);
Print(builder, entry.DataEntry, level: level + 1, types, sections);
else if (entry.Subdirectory != null)
Print(entry.Subdirectory, level: level + 1, types, builder);
Print(builder, entry.Subdirectory, level: level + 1, types, sections);
}
private static void PrintResourceDataEntry(ResourceDataEntry entry, int level, List<object> types, StringBuilder builder)
private static void Print(StringBuilder builder, Models.PortableExecutable.Resource.DataEntry entry, int level, List<object> types, SectionHeader[]? sections)
{
string padding = new(' ', (level + 1) * 2);
@@ -939,6 +955,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(level, $"{padding}Entry level");
builder.AppendLine(entry.DataRVA, $"{padding}Data RVA");
builder.AppendLine(entry.DataRVA.ConvertVirtualAddress(sections), $"{padding}Data physical address");
builder.AppendLine(entry.Size, $"{padding}Size");
builder.AppendLine(entry.Codepage, $"{padding}Codepage");
builder.AppendLine(entry.Reserved, $"{padding}Reserved");
@@ -1024,25 +1041,25 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine();
}
private static void PrintResourceRT_CURSOR(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_CURSOR(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Hardware-dependent cursor resource found, not parsed yet");
}
private static void PrintResourceRT_BITMAP(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_BITMAP(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Bitmap resource found, not parsed yet");
}
private static void PrintResourceRT_ICON(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_ICON(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Hardware-dependent icon resource found, not parsed yet");
}
private static void PrintResourceRT_MENU(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_MENU(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1120,7 +1137,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_DIALOG(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_DIALOG(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1255,7 +1272,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_STRING(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_STRING(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1275,19 +1292,19 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_FONTDIR(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_FONTDIR(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Font directory resource found, not parsed yet");
}
private static void PrintResourceRT_FONT(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_FONT(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Font resource found, not parsed yet");
}
private static void PrintResourceRT_ACCELERATOR(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_ACCELERATOR(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1310,7 +1327,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_RCDATA(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_RCDATA(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Application-defined resource found, not parsed yet");
@@ -1369,7 +1386,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_MESSAGETABLE(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_MESSAGETABLE(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1436,19 +1453,19 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_GROUP_CURSOR(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_GROUP_CURSOR(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Hardware-independent cursor resource found, not parsed yet");
}
private static void PrintResourceRT_GROUP_ICON(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_GROUP_ICON(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Hardware-independent icon resource found, not parsed yet");
}
private static void PrintResourceRT_VERSION(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_VERSION(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1569,37 +1586,37 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceRT_DLGINCLUDE(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_DLGINCLUDE(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}External header resource found, not parsed yet");
}
private static void PrintResourceRT_PLUGPLAY(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_PLUGPLAY(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Plug and Play resource found, not parsed yet");
}
private static void PrintResourceRT_VXD(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_VXD(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}VXD found, not parsed yet");
}
private static void PrintResourceRT_ANICURSOR(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_ANICURSOR(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Animated cursor found, not parsed yet");
}
private static void PrintResourceRT_ANIICON(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_ANIICON(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}Animated icon found, not parsed yet");
}
private static void PrintResourceRT_HTML(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_HTML(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
builder.AppendLine($"{padding}HTML resource found, not parsed yet");
@@ -1612,7 +1629,7 @@ namespace SabreTools.Serialization.Printers
// builder.AppendLine(Encoding.Unicode.GetString(entry.Data), $"{padding}Value (Unicode)");
}
private static void PrintResourceRT_MANIFEST(ResourceDataEntry entry, int level, StringBuilder builder)
private static void PrintResourceRT_MANIFEST(Models.PortableExecutable.Resource.DataEntry entry, int level, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);
@@ -1789,7 +1806,7 @@ namespace SabreTools.Serialization.Printers
}
}
private static void PrintResourceUNKNOWN(ResourceDataEntry entry, int level, object resourceType, StringBuilder builder)
private static void PrintResourceUNKNOWN(Models.PortableExecutable.Resource.DataEntry entry, int level, object resourceType, StringBuilder builder)
{
string padding = new(' ', (level + 1) * 2);

View File

@@ -1,6 +1,7 @@
using System.Text;
using SabreTools.Models.WiseInstaller;
using SabreTools.Models.WiseInstaller.Actions;
using SabreTools.Serialization.Extensions;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Printers

View File

@@ -0,0 +1,58 @@
using System;
namespace SabreTools.Models.ASN1
{
/// <summary>
/// ASN.1 type indicators
/// </summary>
[Flags]
public enum ASN1Type : byte
{
#region Types
V_ASN1_EOC = 0x00,
V_ASN1_BOOLEAN = 0x01,
V_ASN1_INTEGER = 0x02,
V_ASN1_BIT_STRING = 0x03,
V_ASN1_OCTET_STRING = 0x04,
V_ASN1_NULL = 0x05,
V_ASN1_OBJECT = 0x06,
V_ASN1_OBJECT_DESCRIPTOR = 0x07,
V_ASN1_EXTERNAL = 0x08,
V_ASN1_REAL = 0x09,
V_ASN1_ENUMERATED = 0x0A,
V_ASN1_UTF8STRING = 0x0C,
V_ASN1_SEQUENCE = 0x10,
V_ASN1_SET = 0x11,
V_ASN1_NUMERICSTRING = 0x12,
V_ASN1_PRINTABLESTRING = 0x13,
V_ASN1_T61STRING = 0x14,
V_ASN1_TELETEXSTRING = 0x14,
V_ASN1_VIDEOTEXSTRING = 0x15,
V_ASN1_IA5STRING = 0x16,
V_ASN1_UTCTIME = 0x17,
V_ASN1_GENERALIZEDTIME = 0x18,
V_ASN1_GRAPHICSTRING = 0x19,
V_ASN1_ISO64STRING = 0x1A,
V_ASN1_VISIBLESTRING = 0x1A,
V_ASN1_GENERALSTRING = 0x1B,
V_ASN1_UNIVERSALSTRING = 0x1C,
V_ASN1_BMPSTRING = 0x1E,
#endregion
#region Modifiers
// Commented out because it is the default
// and can interfere with V_ASN1_EOC
// V_ASN1_UNIVERSAL = 0x00,
V_ASN1_PRIMITIVE_TAG = 0x1F,
V_ASN1_CONSTRUCTED = 0x20,
V_ASN1_APPLICATION = 0x40,
V_ASN1_CONTEXT_SPECIFIC = 0x80,
V_ASN1_PRIVATE = 0xC0,
#endregion
}
}

View File

@@ -0,0 +1,23 @@
namespace SabreTools.Models.ASN1
{
/// <summary>
/// ASN.1 type/length/value class that all types are based on
/// </summary>
public class TypeLengthValue
{
/// <summary>
/// The ASN.1 type
/// </summary>
public ASN1Type Type { get; set; }
/// <summary>
/// Length of the value
/// </summary>
public ulong Length { get; set; }
/// <summary>
/// Generic value associated with <see cref="Type"/>
/// </summary>
public object? Value { get; set; }
}
}

View File

@@ -8,11 +8,13 @@
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<!-- Added due to StormLibSharp -->
<NoWarn>CS8600;CS8601;CS8603;CS8604;CS8618;CS8625;CS8634;IL3000</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.2</Version>
<Version>1.9.6</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
@@ -26,6 +28,28 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<!-- Support All Frameworks -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<!-- Set a build flag for Windows specifically -->
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
<DefineConstants>$(DefineConstants);WINX86</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
<DefineConstants>$(DefineConstants);WINX64</DefineConstants>
</PropertyGroup>
<!-- Exclude certain parts of external modules for by default -->
<PropertyGroup>
<DefaultItemExcludes>
@@ -36,9 +60,8 @@
</DefaultItemExcludes>
</PropertyGroup>
<!-- Exclude all external modules for .NET Framework 2.0, .NET Framework 3.5, or non-Windows
builds -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR !$(RuntimeIdentifier.StartsWith(`win-x86`))">
<!-- Exclude all external modules for .NET Framework 2.0, .NET Framework 3.5, or non-Windows builds -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR !($(RuntimeIdentifier.StartsWith(`win-x86`)) OR $(RuntimeIdentifier.StartsWith(`win-x64`)))">
<DefaultItemExcludes>
$(DefaultItemExcludes);
_EXTERNAL\**
@@ -62,12 +85,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.ASN1" Version="1.6.2" />
<PackageReference Include="NetLegacySupport.Numerics" Version="1.0.1" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="SabreTools.Models" Version="1.7.1" />
<PackageReference Include="SabreTools.Matching" Version="1.6.0" />
<PackageReference Include="SabreTools.IO" Version="1.7.5" />
<PackageReference Include="SabreTools.Models" Version="1.7.2" />
<PackageReference Include="SharpCompress" Version="0.40.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>

View File

@@ -112,8 +112,10 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}

View File

@@ -44,8 +44,10 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}

View File

@@ -57,8 +57,10 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}
@@ -270,7 +272,11 @@ namespace SabreTools.Serialization.Serializers
writer.WriteRequiredAttributeString("name", rom.Name, throwOnError: true);
writer.WriteRequiredAttributeString("size", rom.Size, throwOnError: true);
writer.WriteOptionalAttributeString("crc", rom.CRC);
writer.WriteOptionalAttributeString("md2", rom.MD2);
writer.WriteOptionalAttributeString("md4", rom.MD4);
writer.WriteOptionalAttributeString("md5", rom.MD5);
writer.WriteOptionalAttributeString("ripemd128", rom.RIPEMD128);
writer.WriteOptionalAttributeString("ripemd160", rom.RIPEMD160);
writer.WriteOptionalAttributeString("sha1", rom.SHA1);
writer.WriteOptionalAttributeString("sha256", rom.SHA256);
writer.WriteOptionalAttributeString("sha384", rom.SHA384);

View File

@@ -59,8 +59,10 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = System.IO.File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}
@@ -110,6 +112,12 @@ namespace SabreTools.Serialization.Serializers
case HashType.MD5:
WriteMD5(obj.MD5, writer);
break;
case HashType.RIPEMD128:
WriteRIPEMD128(obj.RIPEMD128, writer);
break;
case HashType.RIPEMD160:
WriteRIPEMD160(obj.RIPEMD160, writer);
break;
case HashType.SHA1:
WriteSHA1(obj.SHA1, writer);
break;
@@ -222,6 +230,50 @@ namespace SabreTools.Serialization.Serializers
}
}
/// <summary>
/// Write RIPEMD128 information to the current writer
/// </summary>
/// <param name="ripemd128s">Array of RIPEMD128 objects representing the files</param>
/// <param name="writer">SeparatedValueWriter representing the output</param>
private static void WriteRIPEMD128(RIPEMD128[]? ripemd128s, SeparatedValueWriter writer)
{
// If the item information is missing, we can't do anything
if (ripemd128s == null || ripemd128s.Length == 0)
return;
// Loop through and write out the items
foreach (var ripemd128 in ripemd128s)
{
if (string.IsNullOrEmpty(ripemd128.Hash) || string.IsNullOrEmpty(ripemd128.File))
continue;
writer.WriteValues([ripemd128.Hash!, ripemd128.File!]);
writer.Flush();
}
}
/// <summary>
/// Write RIPEMD160 information to the current writer
/// </summary>
/// <param name="ripemd160s">Array of RIPEMD160 objects representing the files</param>
/// <param name="writer">SeparatedValueWriter representing the output</param>
private static void WriteRIPEMD160(RIPEMD160[]? ripemd160s, SeparatedValueWriter writer)
{
// If the item information is missing, we can't do anything
if (ripemd160s == null || ripemd160s.Length == 0)
return;
// Loop through and write out the items
foreach (var ripemd160 in ripemd160s)
{
if (string.IsNullOrEmpty(ripemd160.Hash) || string.IsNullOrEmpty(ripemd160.File))
continue;
writer.WriteValues([ripemd160.Hash!, ripemd160.File!]);
writer.Flush();
}
}
/// <summary>
/// Write SHA1 information to the current writer
/// </summary>

View File

@@ -59,8 +59,9 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}

View File

@@ -100,8 +100,10 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}

View File

@@ -64,8 +64,9 @@ namespace SabreTools.Serialization.Serializers
if (stream == null)
return false;
using var fs = File.OpenWrite(path);
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(fs);
fs.Flush();
return true;
}

View File

@@ -1,11 +1,10 @@
using System;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Interfaces;
using static SabreTools.Matching.Extensions;
using SabreTools.Serialization.Wrappers;
namespace SabreTools.Serialization.Wrappers
namespace SabreTools.Serialization
{
public static class WrapperFactory
{
@@ -30,7 +29,7 @@ namespace SabreTools.Serialization.Wrappers
WrapperType.IniFile => null,// TODO: Implement wrapper
WrapperType.InstallShieldArchiveV3 => InstallShieldArchiveV3.Create(data),
WrapperType.InstallShieldCAB => InstallShieldCabinet.Create(data),
WrapperType.LDSCRYPT => null,// TODO: Implement wrapper
WrapperType.LDSCRYPT => LDSCRYPT.Create(data),
WrapperType.LZKWAJ => LZKWAJ.Create(data),
WrapperType.LZQBasic => LZQBasic.Create(data),
WrapperType.LZSZDD => LZSZDD.Create(data),
@@ -47,11 +46,11 @@ namespace SabreTools.Serialization.Wrappers
WrapperType.PlayJPlaylist => PlayJPlaylist.Create(data),
WrapperType.Quantum => Quantum.Create(data),
WrapperType.RAR => RAR.Create(data),
WrapperType.RealArcadeInstaller => null,// TODO: Implement wrapper
WrapperType.RealArcadeMezzanine => null,// TODO: Implement wrapper
WrapperType.RealArcadeInstaller => RealArcadeInstaller.Create(data),
WrapperType.RealArcadeMezzanine => RealArcadeMezzanine.Create(data),
WrapperType.SecuROMDFA => SecuROMDFA.Create(data),
WrapperType.SevenZip => SevenZip.Create(data),
WrapperType.SFFS => null,// TODO: Implement wrapper
WrapperType.SFFS => SFFS.Create(data),
WrapperType.SGA => SGA.Create(data),
WrapperType.TapeArchive => TapeArchive.Create(data),
WrapperType.Textfile => null,// TODO: Implement wrapper
@@ -158,10 +157,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region BDPlusSVM
if (magic.StartsWith([0x42, 0x44, 0x53, 0x56, 0x4D, 0x5F, 0x43, 0x43]))
if (magic.StartsWith(Models.BDPlus.Constants.SignatureBytes))
return WrapperType.BDPlusSVM;
if (extension.Equals("svm", StringComparison.OrdinalIgnoreCase))
@@ -191,10 +189,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region BZip2
if (magic.StartsWith([0x42, 0x52, 0x68]))
if (magic.StartsWith(Models.BZip2.Constants.SignatureBytes))
return WrapperType.BZip2;
if (extension.Equals("bz2", StringComparison.OrdinalIgnoreCase))
@@ -229,10 +226,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region CHD
if (magic.StartsWith([0x4D, 0x43, 0x6F, 0x6D, 0x70, 0x72, 0x48, 0x44]))
if (magic.StartsWith(Models.CHD.Constants.SignatureBytes))
return WrapperType.CHD;
#endregion
@@ -315,10 +311,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region InstallShieldArchiveV3
if (magic.StartsWith([0x13, 0x5D, 0x65, 0x8C]))
if (magic.StartsWith(Models.InstallShieldArchiveV3.Constants.HeaderSignatureBytes))
return WrapperType.InstallShieldArchiveV3;
if (extension.Equals("z", StringComparison.OrdinalIgnoreCase))
@@ -335,10 +330,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region LDSCRYPT
if (magic.StartsWith([0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54]))
if (magic.StartsWith(Models.LDSCRYPT.Constants.SignatureBytes))
return WrapperType.LDSCRYPT;
#endregion
@@ -438,6 +432,10 @@ namespace SabreTools.Serialization.Wrappers
// TODO: Use constants from Models here
#region PFF
// Version 0
if (magic.StartsWith([0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x30]))
return WrapperType.PFF;
// Version 2
if (magic.StartsWith([0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x32]))
return WrapperType.PFF;
@@ -588,15 +586,14 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region RAR
// RAR archive version 1.50 onwards
if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00]))
if (magic.StartsWith(Models.RAR.Constants.OldSignatureBytes))
return WrapperType.RAR;
// RAR archive version 5.0 onwards
if (magic.StartsWith([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]))
if (magic.StartsWith(Models.RAR.Constants.NewSignatureBytes))
return WrapperType.RAR;
if (extension.Equals("rar", StringComparison.OrdinalIgnoreCase))
@@ -604,17 +601,16 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region RealArcade
// RASGI2.0
// Found in the ".rgs files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith([0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30]))
if (magic.StartsWith(Models.RealArcades.Constants.RgsSignatureBytes))
return WrapperType.RealArcadeInstaller;
// XZip2.0
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
if (magic.StartsWith([0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30]))
if (magic.StartsWith(Models.RealArcades.Constants.MezzanineSignatureBytes))
return WrapperType.RealArcadeMezzanine;
#endregion
@@ -626,10 +622,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region SevenZip
if (magic.StartsWith([0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]))
if (magic.StartsWith(Models.SevenZip.Constants.SignatureBytes))
return WrapperType.SevenZip;
if (extension.Equals("7z", StringComparison.OrdinalIgnoreCase))
@@ -640,7 +635,7 @@ namespace SabreTools.Serialization.Wrappers
#region SFFS
// Found in Redump entry 81756, confirmed to be "StarForce Filesystem" by PiD.
if (magic.StartsWith(Models.SFFS.Constants.SignatureBytes))
if (magic.StartsWith(Models.StarForce.Constants.SignatureBytes))
return WrapperType.SFFS;
#endregion
@@ -703,6 +698,14 @@ namespace SabreTools.Serialization.Wrappers
if (magic.StartsWith([0x3C, 0x3F, 0x78, 0x6D, 0x6C]))
return WrapperType.Textfile;
// "<?xml" in UTF-16 encoding
if (magic.StartsWith([0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
return WrapperType.Textfile;
// "<?xml" in UTF-16 encoding with byte order marks
if (magic.StartsWith([0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
return WrapperType.Textfile;
// "Description in Zip"
if (extension.Equals("diz", StringComparison.OrdinalIgnoreCase))
return WrapperType.Textfile;
@@ -784,10 +787,9 @@ namespace SabreTools.Serialization.Wrappers
#endregion
// TODO: Use constants from Models here
#region XZ
if (magic.StartsWith([0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]))
if (magic.StartsWith(Models.XZ.Constants.SignatureBytes))
return WrapperType.XZ;
if (extension.Equals("xz", StringComparison.OrdinalIgnoreCase))

View File

@@ -78,7 +78,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using FileStream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
// Read the data block
var data = ReadRangeFromSource(offset, compressedSize);

View File

@@ -79,7 +79,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -37,7 +37,7 @@ namespace SabreTools.Serialization.Wrappers
Directory.CreateDirectory(directoryName);
// Extract the file
using FileStream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
bz2File.CopyTo(fs);
fs.Flush();

View File

@@ -1,4 +1,5 @@
using System.IO;
using SabreTools.Models.BZip2;
namespace SabreTools.Serialization.Wrappers
{
@@ -7,7 +8,7 @@ namespace SabreTools.Serialization.Wrappers
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public partial class BZip2 : WrapperBase
public partial class BZip2 : WrapperBase<Archive>
{
#region Descriptive Properties
@@ -19,22 +20,22 @@ namespace SabreTools.Serialization.Wrappers
#region Constructors
/// <inheritdoc/>
public BZip2(byte[] data) : base(data) { }
public BZip2(Archive model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public BZip2(byte[] data, int offset) : base(data, offset) { }
public BZip2(Archive model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public BZip2(byte[] data, int offset, int length) : base(data, offset, length) { }
public BZip2(Archive model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public BZip2(Stream data) : base(data) { }
public BZip2(Archive model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public BZip2(Stream data, long offset) : base(data, offset) { }
public BZip2(Archive model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public BZip2(Stream data, long offset, long length) : base(data, offset, length) { }
public BZip2(Archive model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
@@ -72,7 +73,7 @@ namespace SabreTools.Serialization.Wrappers
if (data == null || !data.CanRead)
return null;
return new BZip2(data);
return new BZip2(new Archive(), data);
}
#endregion

View File

@@ -88,7 +88,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using FileStream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data);
fs.Flush();
}

View File

@@ -87,7 +87,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
// Now read the data sequentially and write out while we have data left
long fileSize = file.Size;

View File

@@ -111,7 +111,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = System.IO.File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -249,7 +249,7 @@ namespace SabreTools.Serialization.Wrappers
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
cabinet.FileSave(i, filename);
cabinet.FileSave(i, filename, includeDebug);
}
catch (Exception ex)
{
@@ -269,7 +269,7 @@ namespace SabreTools.Serialization.Wrappers
/// <summary>
/// Save the file at the given index to the filename specified
/// </summary>
public bool FileSave(int index, string filename, bool useOld = false)
public bool FileSave(int index, string filename, bool includeDebug, bool useOld = false)
{
// Get the file descriptor
if (!TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
@@ -277,7 +277,7 @@ namespace SabreTools.Serialization.Wrappers
// If the file is split
if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
return FileSave((int)fileDescriptor.LinkPrevious, filename, useOld);
return FileSave((int)fileDescriptor.LinkPrevious, filename, includeDebug, useOld);
// Get the reader at the index
var reader = Reader.Create(this, index, fileDescriptor);
@@ -285,7 +285,7 @@ namespace SabreTools.Serialization.Wrappers
return false;
// Create the output file and hasher
FileStream output = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
var md5 = new HashWrapper(HashType.MD5);
long readBytesLeft = (long)GetReadableBytes(fileDescriptor);
@@ -313,7 +313,7 @@ namespace SabreTools.Serialization.Wrappers
{
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
reader.Dispose();
output?.Close();
fs?.Close();
return false;
}
@@ -324,7 +324,7 @@ namespace SabreTools.Serialization.Wrappers
{
Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
reader.Dispose();
output?.Close();
fs?.Close();
return false;
}
@@ -343,7 +343,7 @@ namespace SabreTools.Serialization.Wrappers
{
Console.Error.WriteLine($"Decompression failed with code {result.ToZlibConstName()}. bytes_to_read={bytesToRead}, volume={fileDescriptor.Volume}, read_bytes={readBytes}");
reader.Dispose();
output?.Close();
fs?.Close();
return false;
}
@@ -360,7 +360,7 @@ namespace SabreTools.Serialization.Wrappers
{
Console.Error.WriteLine($"Failed to write {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
reader.Dispose();
output?.Close();
fs?.Close();
return false;
}
@@ -371,7 +371,7 @@ namespace SabreTools.Serialization.Wrappers
// Hash and write the next block
bytesToWrite = Math.Min(bytesToWrite, writeBytesLeft);
md5.Process(outputBuffer, 0, (int)bytesToWrite);
output?.Write(outputBuffer, 0, (int)bytesToWrite);
fs?.Write(outputBuffer, 0, (int)bytesToWrite);
totalWritten += bytesToWrite;
writeBytesLeft -= bytesToWrite;
@@ -379,12 +379,12 @@ namespace SabreTools.Serialization.Wrappers
// Validate the number of bytes written
if ((long)fileDescriptor.ExpandedSize != totalWritten)
Console.WriteLine($"Expanded size of file {index} ({GetFileName(index)}) expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
if (includeDebug) Console.WriteLine($"Expanded size of file {index} ({GetFileName(index)}) expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
// Finalize output values
md5.Terminate();
reader?.Dispose();
output?.Close();
fs?.Close();
// Validate the data written, if required
if (MajorVersion >= 6)
@@ -422,7 +422,7 @@ namespace SabreTools.Serialization.Wrappers
return false;
// Create the output file
FileStream output = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
ulong bytesLeft = GetReadableBytes(fileDescriptor);
byte[] outputBuffer = new byte[BUFFER_SIZE];
@@ -435,7 +435,7 @@ namespace SabreTools.Serialization.Wrappers
{
Console.Error.WriteLine($"Failed to read {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
reader.Dispose();
output?.Close();
fs?.Close();
return false;
}
@@ -443,12 +443,12 @@ namespace SabreTools.Serialization.Wrappers
bytesLeft -= (uint)bytesToWrite;
// Write the next block
output.Write(outputBuffer, 0, (int)bytesToWrite);
fs.Write(outputBuffer, 0, (int)bytesToWrite);
}
// Finalize output values
reader.Dispose();
output?.Close();
fs?.Close();
return true;
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using SabreTools.Models.InstallShieldCabinet;
using SabreTools.Serialization.Extensions;
namespace SabreTools.Serialization.Wrappers
{

View File

@@ -0,0 +1,91 @@
using System.IO;
using SabreTools.Models.LDSCRYPT;
namespace SabreTools.Serialization.Wrappers
{
/// <summary>
/// This is a shell wrapper; one that does not contain
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public class LDSCRYPT : WrapperBase<EncryptedFile>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "Link Data Security encrypted file";
#endregion
#region Constructors
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public LDSCRYPT(EncryptedFile model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
#region Static Constructors
/// <summary>
/// Create a LDSCRYPT file from a byte array and offset
/// </summary>
/// <param name="data">Byte array representing the archive</param>
/// <param name="offset">Offset within the array to parse</param>
/// <returns>A LDSCRYPT wrapper on success, null on failure</returns>
public static LDSCRYPT? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data == null || data.Length == 0)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and use that
var dataStream = new MemoryStream(data, offset, data.Length - offset);
return Create(dataStream);
}
/// <summary>
/// Create a LDSCRYPT file from a Stream
/// </summary>
/// <param name="data">Stream representing the archive</param>
/// <returns>A LDSCRYPT wrapper on success, null on failure</returns>
public static LDSCRYPT? Create(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
return new LDSCRYPT(new EncryptedFile(), data);
}
#endregion
#region JSON Export
#if NETCOREAPP
/// <inheritdoc/>
public override string ExportJSON() => throw new System.NotImplementedException();
#endif
#endregion
}
}

View File

@@ -21,7 +21,7 @@ namespace SabreTools.Serialization.Wrappers
return false;
// Get the decompressor
var decompressor = Decompressor.CreateKWAJ(contents, CompressionType);
var decompressor = Decompressor.CreateKWAJ(contents, (ushort)CompressionType);
if (decompressor == null)
return false;
@@ -50,7 +50,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
decompressor.CopyTo(fs);
fs.Flush();
}

View File

@@ -46,7 +46,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
decompressor.CopyTo(fs);
fs.Flush();
}

View File

@@ -57,7 +57,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
decompressor.CopyTo(fs);
fs.Flush();
}

View File

@@ -144,8 +144,8 @@ namespace SabreTools.Serialization.Wrappers
/// <inheritdoc/>
public bool Extract(string outputDirectory, bool includeDebug)
{
// Display warning
Console.WriteLine("WARNING: LZX and Quantum compression schemes are not supported so some files may be skipped!");
// Display warning in debug runs
if (includeDebug) Console.WriteLine("WARNING: LZX and Quantum compression schemes are not supported so some files may be skipped!");
// Open the full set if possible
var cabinet = this;
@@ -236,7 +236,7 @@ namespace SabreTools.Serialization.Wrappers
Directory.CreateDirectory(directoryName);
// Open the output file for writing
using var fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(fileData, 0, fileData.Length);
fs.Flush();
}

View File

@@ -1,6 +1,7 @@
using System;
using SabreTools.Serialization.Interfaces;
#if (NET452_OR_GREATER || NETCOREAPP) && (WINX86 || WINX64)
using System.IO;
using StormLibSharp;
#endif
@@ -11,11 +12,7 @@ namespace SabreTools.Serialization.Wrappers
/// <inheritdoc/>
public bool Extract(string outputDirectory, bool includeDebug)
{
#if NET20 || NET35 || !(WINX86 || WINX64)
Console.WriteLine("Extraction is not supported for this framework!");
Console.WriteLine();
return false;
#else
#if (NET452_OR_GREATER || NETCOREAPP) && (WINX86 || WINX64)
try
{
if (Filename == null || !File.Exists(Filename))
@@ -42,7 +39,6 @@ namespace SabreTools.Serialization.Wrappers
// Loop over each entry
foreach (string sub in listfileLines)
{
// Ensure directory separators are consistent
string filename = sub;
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
@@ -50,7 +46,7 @@ namespace SabreTools.Serialization.Wrappers
filename = filename.Replace('\\', '/');
// Ensure the full output directory exists
filename = Path.Combine(outDir, filename);
filename = Path.Combine(outputDirectory, filename);
var directoryName = Path.GetDirectoryName(filename);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
@@ -73,6 +69,10 @@ namespace SabreTools.Serialization.Wrappers
if (includeDebug) System.Console.WriteLine(ex);
return false;
}
#else
Console.WriteLine("Extraction is not supported for this framework!");
Console.WriteLine();
return false;
#endif
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Wrappers
@@ -42,7 +41,7 @@ namespace SabreTools.Serialization.Wrappers
string extension = string.Empty;
// Only process the overlay if it is recognized
for (; overlayOffset < 0x400 && overlayOffset < overlayData.Length; overlayOffset++)
for (; overlayOffset < 0x400 && overlayOffset < overlayData.Length - 0x10; overlayOffset++)
{
int temp = overlayOffset;
byte[] overlaySample = overlayData.ReadBytes(ref temp, 0x10);
@@ -113,6 +112,21 @@ namespace SabreTools.Serialization.Wrappers
extension = "uha";
break;
}
else if (overlaySample.StartsWith([0x3C, 0x3F, 0x78, 0x6D, 0x6C]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]))
{
extension = "xz";

View File

@@ -67,7 +67,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = System.IO.File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -65,7 +65,7 @@ namespace SabreTools.Serialization.Wrappers
Directory.CreateDirectory(directoryName);
// Create the output file
using FileStream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
// Read the data block
var data = ReadRangeFromSource(offset, size);

View File

@@ -2,7 +2,6 @@
using System.IO;
using SabreTools.IO.Compression.zlib;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Interfaces;
namespace SabreTools.Serialization.Wrappers
@@ -174,6 +173,21 @@ namespace SabreTools.Serialization.Wrappers
extension = "uha";
break;
}
else if (overlaySample.StartsWith([0x3C, 0x3F, 0x78, 0x6D, 0x6C]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (overlaySample.StartsWith([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]))
{
extension = "xz";
@@ -329,6 +343,21 @@ namespace SabreTools.Serialization.Wrappers
extension = "uha";
break;
}
else if (resourceSample.StartsWith([0x3C, 0x3F, 0x78, 0x6D, 0x6C]))
{
extension = "xml";
break;
}
else if (resourceSample.StartsWith([0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (resourceSample.StartsWith([0xFF, 0xFE, 0x3C, 0x00, 0x3F, 0x00, 0x78, 0x00, 0x6D, 0x00, 0x6C, 0x00]))
{
extension = "xml";
break;
}
else if (resourceSample.StartsWith([0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]))
{
extension = "xz";
@@ -375,7 +404,7 @@ namespace SabreTools.Serialization.Wrappers
return false;
}
}
/// <summary>
/// Extract data from a SecuROM Matroschka Package
/// </summary>

View File

@@ -3,9 +3,10 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Models.COFF;
using SabreTools.Models.PortableExecutable;
using SabreTools.Models.PortableExecutable.ResourceEntries;
using SabreTools.Models.PortableExecutable.Resource.Entries;
using SabreTools.Serialization.Extensions;
namespace SabreTools.Serialization.Wrappers
{
@@ -20,8 +21,8 @@ namespace SabreTools.Serialization.Wrappers
#region Extension Properties
/// <inheritdoc cref="Executable.COFFFileHeader"/>
public COFFFileHeader? COFFFileHeader => Model.COFFFileHeader;
/// <inheritdoc cref="Executable.FileHeader"/>
public FileHeader? COFFFileHeader => Model.FileHeader;
/// <summary>
/// Dictionary of debug data
@@ -47,8 +48,8 @@ namespace SabreTools.Serialization.Wrappers
}
}
/// <inheritdoc cref="DebugTable.DebugDirectoryTable"/>
public DebugDirectoryEntry[]? DebugDirectoryTable
/// <inheritdoc cref="Models.PortableExecutable.DebugData.Table.Table"/>
public Models.PortableExecutable.DebugData.Entry[]? DebugDirectoryTable
=> Model.DebugTable?.DebugDirectoryTable;
/// <summary>
@@ -101,8 +102,20 @@ namespace SabreTools.Serialization.Wrappers
}
}
/// <inheritdoc cref="Executable.ExportTable"/>
public ExportTable? ExportTable => Model.ExportTable;
/// <inheritdoc cref="Executable.ExportAddressTable"/>
public Models.PortableExecutable.Export.AddressTableEntry[]? ExportTable => Model.ExportAddressTable;
/// <inheritdoc cref="Executable.ExportDirectoryTable"/>
public Models.PortableExecutable.Export.DirectoryTable? ExportDirectoryTable => Model.ExportDirectoryTable;
/// <inheritdoc cref="Executable.NamePointerTable"/>
public Models.PortableExecutable.Export.NamePointerTable? ExportNamePointerTable => Model.NamePointerTable;
/// <inheritdoc cref="Executable.ExportNameTable"/>
public Models.PortableExecutable.Export.NameTable? ExportNameTable => Model.ExportNameTable;
/// <inheritdoc cref="Executable.OrdinalTable"/>
public Models.PortableExecutable.Export.OrdinalTable? ExportOrdinalTable => Model.OrdinalTable;
/// <summary>
/// Header padding data, if it exists
@@ -182,8 +195,17 @@ namespace SabreTools.Serialization.Wrappers
}
}
/// <inheritdoc cref="Executable.ImportTable"/>
public ImportTable? ImportTable => Model.ImportTable;
/// <inheritdoc cref="Executable.ImportAddressTables"/>
public Dictionary<int, Models.PortableExecutable.Import.AddressTableEntry[]?>? ImportAddressTables => Model.ImportAddressTables;
/// <inheritdoc cref="Executable.ImportDirectoryTable"/>
public Models.PortableExecutable.Import.DirectoryTableEntry[]? ImportDirectoryTable => Model.ImportDirectoryTable;
/// <inheritdoc cref="Executable.HintNameTable"/>
public Models.PortableExecutable.Import.HintNameTableEntry[]? ImportHintNameTable => Model.HintNameTable;
/// <inheritdoc cref="Executable.ImportLookupTables"/>
public Dictionary<int, Models.PortableExecutable.Import.LookupTableEntry[]?>? ImportLookupTables => Model.ImportLookupTables;
/// <summary>
/// SecuROM Matroschka package wrapper, if it exists
@@ -264,7 +286,7 @@ namespace SabreTools.Serialization.Wrappers
}
/// <inheritdoc cref="Executable.OptionalHeader"/>
public OptionalHeader? OptionalHeader => Model.OptionalHeader;
public Models.PortableExecutable.OptionalHeader? OptionalHeader => Model.OptionalHeader;
/// <summary>
/// Address of the overlay, if it exists
@@ -420,7 +442,7 @@ namespace SabreTools.Serialization.Wrappers
}
/// <inheritdoc cref="Executable.ResourceDirectoryTable"/>
public ResourceDirectoryTable? ResourceDirectoryTable => Model.ResourceDirectoryTable;
public Models.PortableExecutable.Resource.DirectoryTable? ResourceDirectoryTable => Model.ResourceDirectoryTable;
/// <summary>
/// Sanitized section names
@@ -493,7 +515,7 @@ namespace SabreTools.Serialization.Wrappers
// Get the offset from the end of the section table
long endOfSectionTable = Stub.Header.NewExeHeaderAddr
+ 24 // Signature size + COFF file header size
+ 24 // Signature size + file header size
+ COFFFileHeader.SizeOfOptionalHeader
+ (COFFFileHeader.NumberOfSections * 40); // Size of a section header
@@ -1174,14 +1196,14 @@ namespace SabreTools.Serialization.Wrappers
if (data == null)
continue;
if (data is NB10ProgramDatabase n)
if (data is Models.PortableExecutable.DebugData.NB10ProgramDatabase n)
{
if (n.PdbFileName == null || !n.PdbFileName.Contains(path))
continue;
debugFound.Add(n);
}
else if (data is RSDSProgramDatabase r)
else if (data is Models.PortableExecutable.DebugData.RSDSProgramDatabase r)
{
if (r.PathAndFileName == null || !r.PathAndFileName.Contains(path))
continue;
@@ -1635,7 +1657,7 @@ namespace SabreTools.Serialization.Wrappers
/// <summary>
/// Parse the resource directory table information
/// </summary>
private void ParseResourceDirectoryTable(ResourceDirectoryTable table, List<object> types)
private void ParseResourceDirectoryTable(Models.PortableExecutable.Resource.DirectoryTable table, List<object> types)
{
if (table?.Entries == null)
return;
@@ -1657,7 +1679,7 @@ namespace SabreTools.Serialization.Wrappers
/// <summary>
/// Parse the name resource directory entry information
/// </summary>
private void ParseResourceDirectoryEntry(ResourceDirectoryEntry entry, List<object> types)
private void ParseResourceDirectoryEntry(Models.PortableExecutable.Resource.DirectoryEntry entry, List<object> types)
{
if (entry.DataEntry != null)
ParseResourceDataEntry(entry.DataEntry, types);
@@ -1673,7 +1695,7 @@ namespace SabreTools.Serialization.Wrappers
/// of those resources in the entire exectuable. This means that only the last found version or manifest will
/// ever be cached.
/// </remarks>
private void ParseResourceDataEntry(ResourceDataEntry entry, List<object> types)
private void ParseResourceDataEntry(Models.PortableExecutable.Resource.DataEntry entry, List<object> types)
{
// Create the key and value objects
string key = types == null
@@ -2029,22 +2051,19 @@ namespace SabreTools.Serialization.Wrappers
/// <returns>Section strings on success, null on error</returns>
public List<string>? GetSectionStrings(int index)
{
// If we have no sections
if (SectionNames.Length == 0 || SectionTable == null || SectionTable.Length == 0)
return null;
// If the section doesn't exist
if (index < 0 || index >= SectionTable.Length)
return null;
lock (_sectionStringDataLock)
{
// If we have no sections
if (SectionNames.Length == 0 || SectionTable == null || SectionTable.Length == 0)
{
_sectionStringData = [];
return null;
}
// Create the section string array if we have to
_sectionStringData ??= new List<string>?[SectionNames.Length];
// If the section doesn't exist
if (index < 0 || index >= SectionTable.Length)
return null;
// If we already have cached data, just use that immediately
if (_sectionStringData[index] != null)
return _sectionStringData[index];

View File

@@ -100,10 +100,9 @@ namespace SabreTools.Serialization.Wrappers
// try
// {
// // Open the output file for writing
// using (Stream fs = File.OpenWrite(filename))
// {
// fs.Write(data, 0, data.Length);
// }
// using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
// fs.Write(data, 0, data.Length);
// fs.Flush();
// }
// catch
// {

View File

@@ -1,4 +1,5 @@
using System.IO;
using SabreTools.Models.RAR;
namespace SabreTools.Serialization.Wrappers
{
@@ -7,7 +8,7 @@ namespace SabreTools.Serialization.Wrappers
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public partial class RAR : WrapperBase
public partial class RAR : WrapperBase<Archive>
{
#region Descriptive Properties
@@ -19,22 +20,22 @@ namespace SabreTools.Serialization.Wrappers
#region Constructors
/// <inheritdoc/>
public RAR(byte[] data) : base(data) { }
public RAR(Archive model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public RAR(byte[] data, int offset) : base(data, offset) { }
public RAR(Archive model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RAR(byte[] data, int offset, int length) : base(data, offset, length) { }
public RAR(Archive model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public RAR(Stream data) : base(data) { }
public RAR(Archive model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public RAR(Stream data, long offset) : base(data, offset) { }
public RAR(Archive model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RAR(Stream data, long offset, long length) : base(data, offset, length) { }
public RAR(Archive model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
@@ -72,7 +73,7 @@ namespace SabreTools.Serialization.Wrappers
if (data == null || !data.CanRead)
return null;
return new RAR(data);
return new RAR(new Archive(), data);
}
#endregion

View File

@@ -0,0 +1,90 @@
using System.IO;
using SabreTools.Models.RealArcade;
namespace SabreTools.Serialization.Wrappers
{
/// <summary>
/// This is a shell wrapper; one that does not contain
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public class RealArcadeInstaller : WrapperBase<RgsFile>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "RealArcade Installer RGS File";
#endregion
#region Constructors
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RealArcadeInstaller(RgsFile model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
#region Static Constructors
/// <summary>
/// Create a RealArcade installer RGS file from a byte array and offset
/// </summary>
/// <param name="data">Byte array representing the archive</param>
/// <param name="offset">Offset within the array to parse</param>
/// <returns>A RealArcade installer RGS file wrapper on success, null on failure</returns>
public static RealArcadeInstaller? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data == null || data.Length == 0)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and use that
var dataStream = new MemoryStream(data, offset, data.Length - offset);
return Create(dataStream);
}
/// <summary>
/// Create a RealArcade installer RGS file from a Stream
/// </summary>
/// <param name="data">Stream representing the archive</param>
/// <returns>A RealArcade installer RGS file wrapper on success, null on failure</returns>
public static RealArcadeInstaller? Create(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
return new RealArcadeInstaller(new RgsFile(), data);
}
#endregion
#region JSON Export
#if NETCOREAPP
/// <inheritdoc/>
public override string ExportJSON() => throw new System.NotImplementedException();
#endif
#endregion
}
}

View File

@@ -0,0 +1,90 @@
using System.IO;
using SabreTools.Models.RealArcade;
namespace SabreTools.Serialization.Wrappers
{
/// <summary>
/// This is a shell wrapper; one that does not contain
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public class RealArcadeMezzanine : WrapperBase<Mezzanine>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "RealArcade Mezzanine";
#endregion
#region Constructors
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public RealArcadeMezzanine(Mezzanine model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
#region Static Constructors
/// <summary>
/// Create a RealArcade mezzanine from a byte array and offset
/// </summary>
/// <param name="data">Byte array representing the archive</param>
/// <param name="offset">Offset within the array to parse</param>
/// <returns>A RealArcade mezzanine wrapper on success, null on failure</returns>
public static RealArcadeMezzanine? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data == null || data.Length == 0)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and use that
var dataStream = new MemoryStream(data, offset, data.Length - offset);
return Create(dataStream);
}
/// <summary>
/// Create a RealArcade mezzanine from a Stream
/// </summary>
/// <param name="data">Stream representing the archive</param>
/// <returns>A RealArcade mezzanine wrapper on success, null on failure</returns>
public static RealArcadeMezzanine? Create(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
return new RealArcadeMezzanine(new Mezzanine(), data);
}
#endregion
#region JSON Export
#if NETCOREAPP
/// <inheritdoc/>
public override string ExportJSON() => throw new System.NotImplementedException();
#endif
#endregion
}
}

View File

@@ -0,0 +1,91 @@
using System.IO;
using SabreTools.Models.StarForce;
namespace SabreTools.Serialization.Wrappers
{
/// <summary>
/// This is a shell wrapper; one that does not contain
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
/// TODO: Hook up the models to a proper deserializer
public class SFFS : WrapperBase<FileSystem>
{
#region Descriptive Properties
/// <inheritdoc/>
public override string DescriptionString => "StarForce File System";
#endregion
#region Constructors
/// <inheritdoc/>
public SFFS(FileSystem model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public SFFS(FileSystem model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public SFFS(FileSystem model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public SFFS(FileSystem model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public SFFS(FileSystem model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public SFFS(FileSystem model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
#region Static Constructors
/// <summary>
/// Create a SFFS file from a byte array and offset
/// </summary>
/// <param name="data">Byte array representing the archive</param>
/// <param name="offset">Offset within the array to parse</param>
/// <returns>A SFFS wrapper on success, null on failure</returns>
public static SFFS? Create(byte[]? data, int offset)
{
// If the data is invalid
if (data == null || data.Length == 0)
return null;
// If the offset is out of bounds
if (offset < 0 || offset >= data.Length)
return null;
// Create a memory stream and use that
var dataStream = new MemoryStream(data, offset, data.Length - offset);
return Create(dataStream);
}
/// <summary>
/// Create a SFFS file (or derived format) from a Stream
/// </summary>
/// <param name="data">Stream representing the archive</param>
/// <returns>A SFFS wrapper on success, null on failure</returns>
public static SFFS? Create(Stream? data)
{
// If the data is invalid
if (data == null || !data.CanRead)
return null;
return new SFFS(new FileSystem(), data);
}
#endregion
#region JSON Export
#if NETCOREAPP
/// <inheritdoc/>
public override string ExportJSON() => throw new System.NotImplementedException();
#endif
#endregion
}
}

View File

@@ -136,7 +136,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = System.IO.File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -49,7 +49,7 @@ namespace SabreTools.Serialization.Wrappers
return false;
// Ensure directory separators are consistent
string filename = Encoding.ASCII.GetString(entry.Path).TrimEnd('\0');
string filename = entry.Path.TrimEnd('\0');
if (Path.DirectorySeparatorChar == '\\')
filename = filename.Replace('/', '\\');
else if (Path.DirectorySeparatorChar == '/')
@@ -72,7 +72,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}
@@ -122,7 +122,7 @@ namespace SabreTools.Serialization.Wrappers
// Do not return on a hash mismatch
if (actualMd5 != expectedMd5)
{
string filename = Encoding.ASCII.GetString(entry.Path).TrimEnd('\0');
string filename = entry.Path.TrimEnd('\0');
if (includeDebug) Console.Error.WriteLine($"MD5 checksum failure for file {filename})");
return null;
}

View File

@@ -6,7 +6,6 @@ using SabreTools.Serialization.Interfaces;
#if NET462_OR_GREATER || NETCOREAPP
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Common;
using SharpCompress.Readers;
#endif

View File

@@ -1,4 +1,5 @@
using System.IO;
using SabreTools.Models.SevenZip;
namespace SabreTools.Serialization.Wrappers
{
@@ -7,7 +8,7 @@ namespace SabreTools.Serialization.Wrappers
/// any actual parsing. It is used as a placeholder for
/// types that typically do not have models.
/// </summary>
public partial class SevenZip : WrapperBase
public partial class SevenZip : WrapperBase<Archive>
{
#region Descriptive Properties
@@ -19,22 +20,22 @@ namespace SabreTools.Serialization.Wrappers
#region Constructors
/// <inheritdoc/>
public SevenZip(byte[] data) : base(data) { }
public SevenZip(Archive model, byte[] data) : base(model, data) { }
/// <inheritdoc/>
public SevenZip(byte[] data, int offset) : base(data, offset) { }
public SevenZip(Archive model, byte[] data, int offset) : base(model, data, offset) { }
/// <inheritdoc/>
public SevenZip(byte[] data, int offset, int length) : base(data, offset, length) { }
public SevenZip(Archive model, byte[] data, int offset, int length) : base(model, data, offset, length) { }
/// <inheritdoc/>
public SevenZip(Stream data) : base(data) { }
public SevenZip(Archive model, Stream data) : base(model, data) { }
/// <inheritdoc/>
public SevenZip(Stream data, long offset) : base(data, offset) { }
public SevenZip(Archive model, Stream data, long offset) : base(model, data, offset) { }
/// <inheritdoc/>
public SevenZip(Stream data, long offset, long length) : base(data, offset, length) { }
public SevenZip(Archive model, Stream data, long offset, long length) : base(model, data, offset, length) { }
#endregion
@@ -72,7 +73,7 @@ namespace SabreTools.Serialization.Wrappers
if (data == null || !data.CanRead)
return null;
return new SevenZip(data);
return new SevenZip(new Archive(), data);
}
#endregion

View File

@@ -79,7 +79,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -130,7 +130,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

View File

@@ -67,7 +67,7 @@ namespace SabreTools.Serialization.Wrappers
try
{
// Open the output file for writing
using Stream fs = File.OpenWrite(filename);
using var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.None);
fs.Write(data, 0, data.Length);
fs.Flush();
}

Some files were not shown because too many files have changed in this diff Show More