Skip to content

Commit 8521657

Browse files
committed
reflection based deserialisation, binary only for now
1 parent 5c5ad6d commit 8521657

16 files changed

Lines changed: 3184 additions & 119 deletions

AttributeList.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
using AttrKVP = System.Collections.Generic.KeyValuePair<string, object>;
1111
using System.Reflection;
12+
using System.IO;
1213

1314
namespace Datamodel
1415
{
@@ -243,11 +244,21 @@ public virtual object this[string name]
243244
throw new AttributeTypeException("Elements are not supported as prefix attributes.");
244245

245246
var prop_attr = (PropertyInfo)PropertyInfos[name];
247+
246248
if (prop_attr != null)
247249
{
248-
throw new InvalidOperationException($"Cannot set the value of a property-derived attribute by key. Assign to '{prop_attr.Name}' directly instead.");
249-
}
250+
PropertyInfo prop = GetType().GetProperty(prop_attr.Name, BindingFlags.Public | BindingFlags.Instance);
250251

252+
if (null != prop && prop.CanWrite)
253+
{
254+
prop.SetValue(this, value);
255+
}
256+
else
257+
{
258+
throw new InvalidDataException("Property of deserialisation class must be writeable, make sure it's public and has a public setter");
259+
}
260+
}
261+
251262
Attribute old_attr;
252263
Attribute new_attr;
253264
int old_index = -1;

Codecs/Binary.cs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Numerics;
66
using System.IO;
77
using System.Diagnostics;
8+
using System.Reflection;
9+
using System.Runtime.CompilerServices;
810

911
namespace Datamodel.Codecs
1012
{
@@ -344,8 +346,15 @@ object ReadValue(Datamodel dm, Type type, bool raw_string)
344346
throw new ArgumentException(type == null ? "No type provided to GetValue()" : "Cannot read value of type " + type.Name);
345347
}
346348

347-
public Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode)
349+
public Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode, Assembly callingAssembly, bool attemptReflection)
348350
{
351+
Dictionary<string, Type> callingTypes = new();
352+
353+
foreach (var classType in callingAssembly.DefinedTypes)
354+
{
355+
callingTypes.Add(classType.Name, classType);
356+
}
357+
349358
stream.Seek(0, SeekOrigin.Begin);
350359
while (true)
351360
{
@@ -383,7 +392,39 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
383392
var id_bits = Reader.ReadBytes(16);
384393
var id = new Guid(BitConverter.IsLittleEndian ? id_bits : id_bits.Reverse().ToArray());
385394

386-
var elem = new Element(dm, name, id, type);
395+
Element? elem = null;
396+
var matchedType = callingTypes.TryGetValue(type, out var classType);
397+
398+
if(matchedType)
399+
{
400+
var isElementDerived = IsElementDerived(classType);
401+
if (isElementDerived && classType.Name == type)
402+
{
403+
Type derivedType = classType;
404+
405+
ConstructorInfo? constructor = typeof(Element).GetConstructor(
406+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
407+
null,
408+
new Type[] { typeof(Datamodel), typeof(string), typeof(Guid), typeof(string) },
409+
null
410+
);
411+
412+
if (constructor == null)
413+
{
414+
throw new InvalidOperationException("Failed to get constructor while attemption reflection based deserialisation");
415+
}
416+
417+
object uninitializedObject = RuntimeHelpers.GetUninitializedObject(derivedType);
418+
constructor.Invoke(uninitializedObject, new object[] { dm, name, id, type });
419+
420+
elem = (Element?)uninitializedObject;
421+
}
422+
}
423+
424+
if (elem == null)
425+
{
426+
elem = new Element(dm, name, id, type);
427+
}
387428
}
388429

389430
// read attributes (or not, if we're deferred)
@@ -396,8 +437,7 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
396437
foreach (var i in Enumerable.Range(0, num_attrs))
397438
{
398439
var name = StringDict.ReadString();
399-
400-
if (defer_mode == DeferredMode.Automatic)
440+
if (defer_mode == DeferredMode.Automatic && attemptReflection == false)
401441
{
402442
CodecUtilities.AddDeferredAttribute(elem, name, Reader.BaseStream.Position);
403443
SkipAttribute();
@@ -408,6 +448,7 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
408448
}
409449
}
410450
}
451+
411452
return dm;
412453
}
413454

@@ -762,6 +803,28 @@ void WriteAttribute(object value, bool in_array)
762803
}
763804
}
764805

806+
bool IsElementDerived(Type type)
807+
{
808+
var elementType = typeof(Element);
809+
810+
while (type.BaseType != elementType)
811+
{
812+
var baseType = type.BaseType;
813+
814+
if (baseType != null)
815+
{
816+
type = baseType;
817+
}
818+
else
819+
{
820+
return type == elementType ? true : false;
821+
}
822+
823+
}
824+
825+
return type.BaseType == elementType ? true : false;
826+
}
827+
765828
class DmxBinaryWriter : BinaryWriter
766829
{
767830
public DmxBinaryWriter(Stream output)

Codecs/KeyValues2.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Runtime.CompilerServices;
88
using System.Globalization;
9+
using System.Reflection;
910

1011
namespace Datamodel.Codecs
1112
{
@@ -597,7 +598,7 @@ object Decode_ParseValue(Type type, string value)
597598
else throw new ArgumentException($"Internal error: ParseValue passed unsupported type: {type}.");
598599
}
599600

600-
public Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode)
601+
public Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode, Assembly callingAssembly, bool attemptReflection)
601602
{
602603
DM = new Datamodel(format, format_version);
603604

Datamodel.NET.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<AssemblyOriginatorKeyFile>sgKey.snk</AssemblyOriginatorKeyFile>
2020
</PropertyGroup>
2121
<ItemGroup>
22-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
22+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
2323
</ItemGroup>
2424
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Documentation|AnyCPU'">
2525
<OutputPath>bin\Documentation\</OutputPath>
@@ -30,10 +30,11 @@
3030
</PropertyGroup>
3131
<PropertyGroup>
3232
<DelaySign>false</DelaySign>
33+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
3334
</PropertyGroup>
3435
<ItemGroup>
35-
<None Include="README.md" Pack="true" PackagePath="\"/>
36-
<None Include="COPYING" Pack="true" PackagePath="\"/>
36+
<None Include="README.md" Pack="true" PackagePath="\" />
37+
<None Include="COPYING" Pack="true" PackagePath="\" />
3738
</ItemGroup>
3839
<ItemGroup>
3940
<Compile Remove="DmxPad\**" />

Datamodel.NET.sln

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Datamodel.NET", "Datamodel.
66
EndProject
77
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{4C928D60-5E48-4C0D-9C7E-C75D9734CD58}"
88
EndProject
9-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DmxPad", "DmxPad\DmxPad.csproj", "{B49BF0E9-46DB-4BE3-9890-02FF5C098C7C}"
9+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp1", "..\ConsoleApp1\ConsoleApp1.csproj", "{B4982001-13F5-4CD3-953E-F438FC42699E}"
10+
ProjectSection(ProjectDependencies) = postProject
11+
{075743A9-B292-410C-B68F-6E6CF588D60A} = {075743A9-B292-410C-B68F-6E6CF588D60A}
12+
EndProjectSection
1013
EndProject
1114
Global
1215
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,6 +30,12 @@ Global
2730
{4C928D60-5E48-4C0D-9C7E-C75D9734CD58}.Documentation|Any CPU.Build.0 = Debug|Any CPU
2831
{4C928D60-5E48-4C0D-9C7E-C75D9734CD58}.Release|Any CPU.ActiveCfg = Release|Any CPU
2932
{4C928D60-5E48-4C0D-9C7E-C75D9734CD58}.Release|Any CPU.Build.0 = Release|Any CPU
33+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Debug|Any CPU.Build.0 = Debug|Any CPU
35+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Documentation|Any CPU.ActiveCfg = Release|Any CPU
36+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Documentation|Any CPU.Build.0 = Release|Any CPU
37+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{B4982001-13F5-4CD3-953E-F438FC42699E}.Release|Any CPU.Build.0 = Release|Any CPU
3039
EndGlobalSection
3140
GlobalSection(SolutionProperties) = preSolution
3241
HideSolutionNode = FALSE

Datamodel.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Security;
1010
using System.Numerics;
1111
using CodecRegistration = System.Tuple<string, int>;
12+
using System.Reflection;
1213

1314
namespace Datamodel
1415
{
@@ -231,32 +232,32 @@ public void Save(string path, string encoding, int encoding_version)
231232
/// </summary>
232233
/// <param name="stream">The input Stream.</param>
233234
/// <param name="defer_mode">How to handle deferred loading.</param>
234-
public static Datamodel Load(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic)
235+
public static Datamodel Load(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic, bool attemptReflection = false)
235236
{
236-
return Load_Internal(stream, defer_mode);
237+
return Load_Internal(stream, Assembly.GetCallingAssembly(), defer_mode, attemptReflection);
237238
}
238239
/// <summary>
239240
/// Loads a Datamodel from a byte array.
240241
/// </summary>
241242
/// <param name="stream">The input Stream.</param>
242243
/// <param name="defer_mode">How to handle deferred loading.</param>
243-
public static Datamodel Load(byte[] data, DeferredMode defer_mode = DeferredMode.Automatic)
244+
public static Datamodel Load(byte[] data, DeferredMode defer_mode = DeferredMode.Automatic, bool attemptReflection = false)
244245
{
245-
return Load_Internal(new MemoryStream(data, true), defer_mode);
246+
return Load_Internal(new MemoryStream(data, true), Assembly.GetCallingAssembly(), defer_mode, attemptReflection);
246247
}
247248

248249
/// <summary>
249250
/// Loads a Datamodel from a file path.
250251
/// </summary>
251252
/// <param name="path">The source file path.</param>
252253
/// <param name="defer_mode">How to handle deferred loading.</param>
253-
public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode.Automatic)
254+
public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode.Automatic, bool attemptReflection = false)
254255
{
255256
var stream = File.OpenRead(path);
256257
Datamodel dm = null;
257258
try
258259
{
259-
dm = Load_Internal(stream, defer_mode);
260+
dm = Load_Internal(stream, Assembly.GetCallingAssembly(), defer_mode, attemptReflection);
260261
return dm;
261262
}
262263
finally
@@ -265,7 +266,7 @@ public static Datamodel Load(string path, DeferredMode defer_mode = DeferredMode
265266
}
266267
}
267268

268-
private static Datamodel Load_Internal(Stream stream, DeferredMode defer_mode = DeferredMode.Automatic)
269+
private static Datamodel Load_Internal(Stream stream, Assembly callingAssembly, DeferredMode defer_mode = DeferredMode.Automatic, bool attemptReflection = false)
269270
{
270271
stream.Seek(0, SeekOrigin.Begin);
271272
var header = string.Empty;
@@ -292,7 +293,7 @@ private static Datamodel Load_Internal(Stream stream, DeferredMode defer_mode =
292293

293294
ICodec codec = GetCodec(encoding, encoding_version);
294295

295-
var dm = codec.Decode(encoding, encoding_version, format, format_version, stream, defer_mode);
296+
var dm = codec.Decode(encoding, encoding_version, format, format_version, stream, defer_mode, callingAssembly, attemptReflection);
296297
if (defer_mode == DeferredMode.Automatic && codec is IDeferredAttributeCodec deferredCodec)
297298
{
298299
dm.Stream = stream;

Element.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ internal set
161161

162162
#region Properties
163163

164+
// TODO: this could probably be sped up by caching the properties somehow
164165
protected override ICollection<(string Name, PropertyInfo Property)> GetPropertyDerivedAttributeList()
165166
{
166167
var type = GetType();
@@ -176,8 +177,6 @@ internal set
176177
if (property.GetIndexParameters().Length == 0 && property.DeclaringType.IsSubclassOf(typeof(Element)))
177178
{
178179
var name = property.Name;
179-
name = property.DeclaringType.GetCustomAttribute<Format.AttributeNamingConventionAttribute>()?
180-
.GetAttributeName(name, property.PropertyType) ?? name;
181180
name = property.GetCustomAttribute<Format.DMProperty>()?.Name ?? name;
182181

183182
properties.Add((name, property));

Format/Attribute.cs

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,67 +3,12 @@
33

44
namespace Datamodel.Format;
55

6-
/// <summary>
7-
/// Subclass this attribute to define a custom attribute name convention.
8-
/// </summary>
9-
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
10-
public abstract class AttributeNamingConventionAttribute : System.Attribute
11-
{
12-
public abstract string GetAttributeName(string propertyName, Type propertyType);
13-
}
14-
15-
/// <summary>
16-
/// This class' property names are mostly lowercase.
17-
/// </summary>
18-
public class LowercasePropertiesAttribute : AttributeNamingConventionAttribute
19-
{
20-
public override string GetAttributeName(string propertyName, Type _)
21-
=> propertyName.ToLower();
22-
}
23-
24-
/// <summary>
25-
/// This class' property names are mostly camelCase.
26-
/// </summary>
27-
public class CamelCasePropertiesAttribute : AttributeNamingConventionAttribute
28-
{
29-
public override string GetAttributeName(string propertyName, Type _)
30-
=> char.ToLowerInvariant(propertyName.AsSpan()[0]) + propertyName[1..];
31-
}
32-
33-
/// <summary>
34-
/// This class' property names are mostly m_hungarian.
35-
/// </summary>
36-
public class HungarianPropertiesAttribute : CamelCasePropertiesAttribute
37-
{
38-
public override string GetAttributeName(string propertyName, Type propertyType)
39-
{
40-
var typeAnnotation = propertyType switch
41-
{
42-
_ when propertyType == typeof(int) => "n",
43-
_ when propertyType == typeof(float) => "fl",
44-
_ when propertyType == typeof(bool) => "b",
45-
_ when propertyType == typeof(Vector2) => "v",
46-
_ when propertyType == typeof(Vector3) => "v",
47-
_ when propertyType == typeof(Vector4) => "v",
48-
_ when propertyType == typeof(Matrix4x4) => "mat",
49-
_ => string.Empty,
50-
};
51-
52-
if (typeAnnotation == string.Empty)
53-
{
54-
return "m_" + base.GetAttributeName(propertyName, propertyType);
55-
}
56-
57-
return "m_" + typeAnnotation + propertyName;
58-
}
59-
}
60-
616
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
627
public sealed class DMProperty : System.Attribute
638
{
649
/// <param name="name">The name to use for serialization.</param>
6510
/// <param name="optional">Ignore serialization if property is on the default value.</param>
66-
public DMProperty(string name = null, bool optional = false)
11+
public DMProperty(string name = "", bool optional = false)
6712
{
6813
Name = name;
6914
Optional = optional;

ICodec.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.IO;
44
using System.Numerics;
5+
using System.Reflection;
56

67
namespace Datamodel.Codecs
78
{
@@ -29,7 +30,7 @@ public interface ICodec
2930
/// <param name="stream">The input stream. Its position will always be 0. Do not dispose.</param>
3031
/// <param name="defer_mode">The deferred loading mode specified by the caller. Only relevant to implementers of <see cref="IDeferredAttributeCodec"/></param>
3132
/// <returns></returns>
32-
Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode);
33+
Datamodel Decode(string encoding, int encoding_version, string format, int format_version, Stream stream, DeferredMode defer_mode, Assembly callingAssembly, bool attemptReflection);
3334
}
3435

3536
/// <summary>

Tests/Resources/cs2_map.vmap.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191

9292
"nextDecalID" "int" "0"
9393
"fixupEntityNames" "bool" "1"
94-
"mapUsageType" "string" "standard"
94+
"mapUsageType" "string" "hellyea"
9595
"origin" "vector3" "0 0 0"
9696
"angles" "qangle" "0 0 0"
9797
"scales" "vector3" "1 1 1"

0 commit comments

Comments
 (0)