Add "correct order" inheritence serialization

This commit is contained in:
Matt Nadareski
2024-04-29 00:36:55 -04:00
parent 8c19ad712a
commit a94d2c8c64
7 changed files with 252 additions and 5 deletions

View File

@@ -530,5 +530,57 @@ namespace SabreTools.IO.Test.Extensions
Assert.NotNull(read.LPByteArray);
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
}
[Fact]
public void ReadTypeInheritanceTest()
{
byte[] structBytes1 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, 0xAA, 0x55, // FieldA
0x55, 0xAA, 0x55, 0xAA, // FieldB
];
var stream1 = new MemoryStream(structBytes1);
var br1 = new BinaryReader(stream1);
var expected1 = new TestStructInheritanceChild1
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA55AA,
FieldB = 0xAA55AA55,
};
var read1 = br1.ReadType<TestStructInheritanceChild1>();
Assert.NotNull(read1?.Signature);
Assert.Equal(expected1.Signature, read1.Signature);
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
Assert.Equal(expected1.FieldA, read1.FieldA);
Assert.Equal(expected1.FieldB, read1.FieldB);
byte[] structBytes2 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, // FieldA
0x55, 0xAA, // FieldB
];
var stream2 = new MemoryStream(structBytes2);
var br2 = new BinaryReader(stream2);
var expected2 = new TestStructInheritanceChild2
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA,
FieldB = 0xAA55,
};
var read2 = br2.ReadType<TestStructInheritanceChild2>();
Assert.NotNull(read2?.Signature);
Assert.Equal(expected2.Signature, read2.Signature);
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
Assert.Equal(expected2.FieldA, read2.FieldA);
Assert.Equal(expected2.FieldB, read2.FieldB);
}
}
}

View File

@@ -441,7 +441,7 @@ namespace SabreTools.IO.Test.Extensions
Assert.Equal(expected.LPStr, read.LPStr);
Assert.Equal(expected.LPWStr, read.LPWStr);
}
[Fact]
public void ReadTypeArraysTest()
{
@@ -493,5 +493,55 @@ namespace SabreTools.IO.Test.Extensions
Assert.NotNull(read.LPByteArray);
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
}
[Fact]
public void ReadTypeInheritanceTest()
{
byte[] structBytes1 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, 0xAA, 0x55, // FieldA
0x55, 0xAA, 0x55, 0xAA, // FieldB
];
int offset1 = 0;
var expected1 = new TestStructInheritanceChild1
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA55AA,
FieldB = 0xAA55AA55,
};
var read1 = structBytes1.ReadType<TestStructInheritanceChild1>(ref offset1);
Assert.NotNull(read1?.Signature);
Assert.Equal(expected1.Signature, read1.Signature);
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
Assert.Equal(expected1.FieldA, read1.FieldA);
Assert.Equal(expected1.FieldB, read1.FieldB);
byte[] structBytes2 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, // FieldA
0x55, 0xAA, // FieldB
];
int offset2 = 0;
var expected2 = new TestStructInheritanceChild2
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA,
FieldB = 0xAA55,
};
var read2 = structBytes2.ReadType<TestStructInheritanceChild2>(ref offset2);
Assert.NotNull(read2?.Signature);
Assert.Equal(expected2.Signature, read2.Signature);
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
Assert.Equal(expected2.FieldA, read2.FieldA);
Assert.Equal(expected2.FieldB, read2.FieldB);
}
}
}

View File

@@ -435,7 +435,7 @@ namespace SabreTools.IO.Test.Extensions
Assert.Equal(expected.LPStr, read.LPStr);
Assert.Equal(expected.LPWStr, read.LPWStr);
}
[Fact]
public void ReadTypeArraysTest()
{
@@ -487,5 +487,55 @@ namespace SabreTools.IO.Test.Extensions
Assert.NotNull(read.LPByteArray);
Assert.True(expected.LPByteArray.SequenceEqual(read.LPByteArray));
}
[Fact]
public void ReadTypeInheritanceTest()
{
byte[] structBytes1 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, 0xAA, 0x55, // FieldA
0x55, 0xAA, 0x55, 0xAA, // FieldB
];
var stream1 = new MemoryStream(structBytes1);
var expected1 = new TestStructInheritanceChild1
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA55AA,
FieldB = 0xAA55AA55,
};
var read1 = stream1.ReadType<TestStructInheritanceChild1>();
Assert.NotNull(read1?.Signature);
Assert.Equal(expected1.Signature, read1.Signature);
Assert.Equal(expected1.IdentifierType, read1.IdentifierType);
Assert.Equal(expected1.FieldA, read1.FieldA);
Assert.Equal(expected1.FieldB, read1.FieldB);
byte[] structBytes2 =
[
0x41, 0x42, 0x43, 0x44, // Signature
0x00, 0xFF, 0x00, 0xFF, // IdentifierType
0xAA, 0x55, // FieldA
0x55, 0xAA, // FieldB
];
var stream2 = new MemoryStream(structBytes2);
var expected2 = new TestStructInheritanceChild2
{
Signature = [0x41, 0x42, 0x43, 0x44],
IdentifierType = 0xFF00FF00,
FieldA = 0x55AA,
FieldB = 0xAA55,
};
var read2 = stream2.ReadType<TestStructInheritanceChild2>();
Assert.NotNull(read2?.Signature);
Assert.Equal(expected2.Signature, read2.Signature);
Assert.Equal(expected2.IdentifierType, read2.IdentifierType);
Assert.Equal(expected2.FieldA, read2.FieldA);
Assert.Equal(expected2.FieldB, read2.FieldB);
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
namespace SabreTools.IO.Test.Extensions
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class TestStructInheritanceParent
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[]? Signature;
public uint IdentifierType;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class TestStructInheritanceChild1 : TestStructInheritanceParent
{
public uint FieldA;
public uint FieldB;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class TestStructInheritanceChild2 : TestStructInheritanceParent
{
public ushort FieldA;
public ushort FieldB;
}
}

View File

@@ -536,8 +536,30 @@ namespace SabreTools.IO.Extensions
// Cache the current offset
long currentOffset = reader.BaseStream.Position;
// Get the type hierarchy for ensuring serialization order
var lineage = new List<Type>();
Type currentType = type;
while (currentType != typeof(object) && currentType != typeof(ValueType))
{
lineage.Add(currentType);
currentType = currentType.BaseType ?? typeof(object);
}
// Generate the fields by parent first
lineage.Reverse();
var fieldsList = new List<FieldInfo>();
foreach (var nextType in lineage)
{
var nextFields = nextType.GetFields();
foreach (var field in nextFields)
{
if (!fieldsList.Any(f => f.Name == field.Name && f.FieldType == field.FieldType))
fieldsList.Add(field);
}
}
// Loop through the fields and set them
var fields = type.GetFields();
var fields = fieldsList.ToArray();
foreach (var fi in fields)
{
// If we have an explicit layout, move accordingly

View File

@@ -663,8 +663,30 @@ namespace SabreTools.IO.Extensions
// Cache the current offset
int currentOffset = offset;
// Get the type hierarchy for ensuring serialization order
var lineage = new List<Type>();
Type currentType = type;
while (currentType != typeof(object) && currentType != typeof(ValueType))
{
lineage.Add(currentType);
currentType = currentType.BaseType ?? typeof(object);
}
// Generate the fields by parent first
lineage.Reverse();
var fieldsList = new List<FieldInfo>();
foreach (var nextType in lineage)
{
var nextFields = nextType.GetFields();
foreach (var field in nextFields)
{
if (!fieldsList.Any(f => f.Name == field.Name && f.FieldType == field.FieldType))
fieldsList.Add(field);
}
}
// Loop through the fields and set them
var fields = type.GetFields();
var fields = fieldsList.ToArray();
foreach (var fi in fields)
{
// If we have an explicit layout, move accordingly

View File

@@ -647,8 +647,30 @@ namespace SabreTools.IO.Extensions
// Cache the current offset
long currentOffset = stream.Position;
// Get the type hierarchy for ensuring serialization order
var lineage = new List<Type>();
Type currentType = type;
while (currentType != typeof(object) && currentType != typeof(ValueType))
{
lineage.Add(currentType);
currentType = currentType.BaseType ?? typeof(object);
}
// Generate the fields by parent first
lineage.Reverse();
var fieldsList = new List<FieldInfo>();
foreach (var nextType in lineage)
{
var nextFields = nextType.GetFields();
foreach (var field in nextFields)
{
if (!fieldsList.Any(f => f.Name == field.Name && f.FieldType == field.FieldType))
fieldsList.Add(field);
}
}
// Loop through the fields and set them
var fields = type.GetFields();
var fields = fieldsList.ToArray();
foreach (var fi in fields)
{
// If we have an explicit layout, move accordingly