diff --git a/src/Imcodec.CoreObject/BehaviorInstance.cs b/src/Imcodec.CoreObject/BehaviorInstance.cs
index 1185e61..419f10d 100644
--- a/src/Imcodec.CoreObject/BehaviorInstance.cs
+++ b/src/Imcodec.CoreObject/BehaviorInstance.cs
@@ -18,10 +18,12 @@ contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
*/
+using Imcodec.ObjectProperty;
+
namespace Imcodec.CoreObject;
-public partial record BehaviorInstance {
+public partial record BehaviorInstance : PropertyClass {
+ public override uint GetHash() => 1578701589;
-
}
\ No newline at end of file
diff --git a/src/Imcodec.CoreObject/BehaviorTemplate.cs b/src/Imcodec.CoreObject/BehaviorTemplate.cs
index db9cd98..a29d145 100644
--- a/src/Imcodec.CoreObject/BehaviorTemplate.cs
+++ b/src/Imcodec.CoreObject/BehaviorTemplate.cs
@@ -18,10 +18,12 @@ contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
*/
+using Imcodec.ObjectProperty;
+
namespace Imcodec.CoreObject;
-public partial record BehaviorTemplate {
+public partial record BehaviorTemplate : PropertyClass {
-
+ public override uint GetHash() => 360231646;
}
\ No newline at end of file
diff --git a/src/Imcodec.CoreObject/CoreObject.cs b/src/Imcodec.CoreObject/CoreObject.cs
index ffc1f8c..e340798 100644
--- a/src/Imcodec.CoreObject/CoreObject.cs
+++ b/src/Imcodec.CoreObject/CoreObject.cs
@@ -31,12 +31,4 @@ public partial record CoreObject : PropertyClass {
public CoreObject(CoreTemplate coreTemplate)
=> m_coreTemplate = coreTemplate;
- public override void EncodeIdentifier(BitWriter writer) {
- var id = m_coreTemplate.m_templateID;
-
- writer.WriteUInt8(id.MParts.Block);
- writer.WriteUInt8(id.MParts.Type);
- writer.WriteUInt32(id.MParts.TemplateId);
- }
-
}
diff --git a/src/Imcodec.CoreObject/CoreObjectSerializer.cs b/src/Imcodec.CoreObject/CoreObjectSerializer.cs
index 1e05aae..3f9b7ea 100644
--- a/src/Imcodec.CoreObject/CoreObjectSerializer.cs
+++ b/src/Imcodec.CoreObject/CoreObjectSerializer.cs
@@ -32,11 +32,23 @@ namespace Imcodec.CoreObject;
/// States whether the object is versionable.
/// States the behaviors of the serializer.
/// The type registry to use for serialization.
-public sealed class CoreObjectSerializer(bool Versionable = false,
- SerializerFlags Behaviors = SerializerFlags.UseFlags | SerializerFlags.Compress,
- TypeRegistry? typeRegistry = null) : ObjectSerializer(Versionable, Behaviors, typeRegistry) {
-
- protected override bool PreloadObject(BitReader inputBuffer, out PropertyClass? propertyClass) {
+public sealed class CoreObjectSerializer(
+ bool versionable = false,
+ SerializerFlags behaviors = SerializerFlags.UseFlags | SerializerFlags.Compress,
+ TypeRegistry? typeRegistry = null
+) : ObjectSerializer(versionable, behaviors, typeRegistry) {
+
+ private static readonly Dictionary s_blockAndTypeMap = new() {
+ { 350837933, (2, 2) }, // ClientObject
+ { 766500222, (104, 2) }, // WizClientObject
+ { 1653772158, (115, 9) }, // WizClientObjectItem
+ { 1167581154, (106, 2) }, // WizClientPet
+ { 2109552587, (108, 2) }, // WizClientMount
+ { 398229815, (132, 9) }, // ClientReagentItem
+ { 958775582, (131, 131) } // ClientRecipe
+ };
+
+ public override bool PreloadObject(BitReader inputBuffer, out PropertyClass? propertyClass) {
propertyClass = null;
var block = inputBuffer.ReadUInt8();
var type = inputBuffer.ReadUInt8();
@@ -49,18 +61,20 @@ protected override bool PreloadObject(BitReader inputBuffer, out PropertyClass?
return propertyClass != null;
}
- // Otherwise, treat it as a CoreObject.
// We can dispatch the type based on the template ID.
- return false; // todo: fixme
+ var hash = GetHashFromBlockAndType(block, type);
+ propertyClass = DispatchType(hash);
+
+ return propertyClass != null;
}
- protected override bool PreWriteObject(BitWriter writer, PropertyClass propertyClass) {
+ public override bool PreWriteObject(BitWriter writer, PropertyClass propertyClass) {
if (propertyClass is null) {
writer.WriteUInt8(0);
writer.WriteUInt8(0);
writer.WriteUInt32(0);
- return true;
+ return false;
}
var (block, type) = GetBlockAndType(propertyClass);
@@ -84,23 +98,17 @@ private static (byte, byte) GetBlockAndType(PropertyClass propClass) {
return (0, 0);
}
- return propClass.GetHash() switch {
- 350837933 => // ClientObject
- (2, 2),
- 766500222 => // WizClientObject
- (104, 2),
- 1653772158 => // WizClientObjectItem
- (115, 9),
- 1167581154 => // WizClientPet
- (106, 2),
- 2109552587 => // WizClientMount
- (108, 2),
- 398229815 => // ClientReagentItem
- (132, 9),
- 958775582 => // ClientRecipe
- (131, 131),
- _ => (0, 0)
- };
+ return ((byte, byte)) (s_blockAndTypeMap.TryGetValue((int) propClass.GetHash(), out var blockAndType) ? blockAndType : (0, 0));
+ }
+
+ private static uint GetHashFromBlockAndType(byte block, byte type) {
+ foreach (var (key, value) in s_blockAndTypeMap) {
+ if (value == (block, type)) {
+ return (uint) key;
+ }
+ }
+
+ return 0;
}
}
\ No newline at end of file
diff --git a/src/Imcodec.CoreObject/CoreTemplate.cs b/src/Imcodec.CoreObject/CoreTemplate.cs
index f32e0dd..02d3f9a 100644
--- a/src/Imcodec.CoreObject/CoreTemplate.cs
+++ b/src/Imcodec.CoreObject/CoreTemplate.cs
@@ -17,11 +17,14 @@ 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
*/
+using Imcodec.ObjectProperty;
using Imcodec.Types;
namespace Imcodec.CoreObject;
-public partial record CoreTemplate {
+public partial record CoreTemplate : PropertyClass {
+
+ public override uint GetHash() => 1068918295;
public GID m_templateID { get; init; }
diff --git a/src/Imcodec.ObjectProperty/BindSerializer.cs b/src/Imcodec.ObjectProperty/BindSerializer.cs
index 7b4d74c..d3351ba 100644
--- a/src/Imcodec.ObjectProperty/BindSerializer.cs
+++ b/src/Imcodec.ObjectProperty/BindSerializer.cs
@@ -74,8 +74,8 @@ public override bool Serialize(PropertyClass input, PropertyFlags propertyMask,
return false;
}
- // Create a new buffer with the size of the output buffer plus the size of the magic.
- var buffer = new byte[baseOutput!.Length + sizeof(uint)];
+ // Create a new buffer with the size of the output buffer plus the size of the magic and flags.
+ var buffer = new byte[baseOutput!.Length + sizeof(uint) * 2];
// Write the magic header and serializer flags.
BinaryPrimitives.WriteUInt32LittleEndian(buffer, BiNDMagic);
@@ -114,7 +114,7 @@ public override bool Deserialize(byte[] inputBuffer, PropertyFlags propertyMa
var reader = new BitReader(inputBuffer);
// Check if the input buffer is too small to contain the magic header.
- if (inputBuffer.Length < sizeof(uint)) {
+ if (inputBuffer.Length < sizeof(uint) * 2) {
return false;
}
diff --git a/src/Imcodec.ObjectProperty/ObjectSerializer.cs b/src/Imcodec.ObjectProperty/ObjectSerializer.cs
index bf0ea55..6b6b03a 100644
--- a/src/Imcodec.ObjectProperty/ObjectSerializer.cs
+++ b/src/Imcodec.ObjectProperty/ObjectSerializer.cs
@@ -34,7 +34,7 @@ public enum SerializerFlags {
///
/// States the serializer should use these flags for deserialization.
///
- UseFlags = 1 << 0,
+ UseFlags = 1 << 0,
///
/// States the serializer should use compact length prefixes.
@@ -44,17 +44,17 @@ public enum SerializerFlags {
///
/// States the serializer should use string enums.
///
- StringEnums = 1 << 2,
+ StringEnums = 1 << 2,
///
/// States the serializer should use ZLib compression.
///
- Compress = 1 << 3,
+ Compress = 1 << 3,
///
/// Properties are dirty encoded.
///
- DirtyEncode = 1 << 4,
+ DirtyEncode = 1 << 4,
}
@@ -202,7 +202,47 @@ public virtual bool Deserialize(byte[] inputBuffer,
propertyClass?.Decode(reader, this);
- output = (T)propertyClass!;
+ output = (T) propertyClass!;
+
+ return true;
+ }
+
+ ///
+ /// Preloads an object from the input buffer based on the provided hash value.
+ ///
+ /// The input buffer containing the serialized data.
+ /// The loaded property class, if found.
+ /// true if the object was preloaded successfully; otherwise, false.
+ public virtual bool PreloadObject(BitReader inputBuffer,
+ out PropertyClass? propertyClass) {
+ var hash = inputBuffer.ReadUInt32();
+ if (hash == 0) {
+ propertyClass = null;
+
+ return false;
+ }
+
+ propertyClass = DispatchType(hash);
+
+ return propertyClass != null;
+ }
+
+ ///
+ /// Writes the object identifier to the specified .
+ ///
+ /// The to write to.
+ /// The to write.
+ /// true if the object identifier was written successfully; otherwise, false.
+ public virtual bool PreWriteObject(BitWriter writer,
+ PropertyClass propertyClass) {
+ if (propertyClass == null) {
+ writer.WriteUInt32(0);
+
+ return false;
+ }
+
+ writer.WriteUInt32(propertyClass.GetHash());
+
return true;
}
@@ -247,33 +287,6 @@ protected virtual BitWriter Compress(BitWriter writer) {
return new BitReader(decompressedData);
}
- ///
- /// Preloads an object from the input buffer based on the provided hash value.
- ///
- /// The input buffer containing the serialized data.
- /// The loaded property class, if found.
- /// true if the object was preloaded successfully; otherwise, false.
- protected virtual bool PreloadObject(BitReader inputBuffer,
- out PropertyClass? propertyClass) {
- var hash = inputBuffer.ReadUInt32();
- propertyClass = DispatchType(hash);
-
- return propertyClass != null;
- }
-
- ///
- /// Writes the object identifier to the specified .
- ///
- /// The to write to.
- /// The to write.
- /// true if the object identifier was written successfully; otherwise, false.
- protected virtual bool PreWriteObject(BitWriter writer,
- PropertyClass propertyClass) {
- propertyClass.EncodeIdentifier(writer);
-
- return true;
- }
-
///
/// Dispatches the type based on the provided hash value.
///
@@ -285,7 +298,7 @@ protected virtual bool PreWriteObject(BitWriter writer,
return null;
}
- return (PropertyClass)Activator.CreateInstance(lookupType)!;
+ return (PropertyClass) Activator.CreateInstance(lookupType)!;
}
}
diff --git a/src/Imcodec.ObjectProperty/Property.cs b/src/Imcodec.ObjectProperty/Property.cs
index c8667ee..a2d7b51 100644
--- a/src/Imcodec.ObjectProperty/Property.cs
+++ b/src/Imcodec.ObjectProperty/Property.cs
@@ -105,17 +105,11 @@ private static Type InnerType
bool IProperty.Encode(BitWriter writer, ObjectSerializer serializer) {
if (IsVector) {
var list = Getter?.Invoke(TargetObject, null);
- if (!EncodeVector(writer, list, serializer)) {
- return false;
- }
+ return EncodeVector(writer, list, serializer);
}
else {
- if (!EncodeElement(writer, serializer, Getter?.Invoke(TargetObject, null))) {
- return false;
- }
+ return EncodeElement(writer, serializer, Getter?.Invoke(TargetObject, null));
}
-
- return true;
}
bool IProperty.Decode(BitReader reader, ObjectSerializer serializer) {
@@ -159,7 +153,7 @@ private static bool EncodeVector(BitWriter writer, object? val, ObjectSerializer
private static bool EncodeElement(BitWriter writer, ObjectSerializer serializer, object? val) {
if (InnerType.IsSubclassOf(typeof(PropertyClass))) {
- return Property.EncodePropertyClass(writer,(PropertyClass) val!, serializer);
+ return Property.EncodePropertyClass(writer, (PropertyClass) val!, serializer);
}
else if (IsEnum) {
return Property.EncodeEnum(writer, val!, serializer);
@@ -188,10 +182,8 @@ private static bool EncodeEnum(BitWriter writer, object val, ObjectSerializer se
private static bool EncodePropertyClass(BitWriter writer,
PropertyClass propertyClass,
ObjectSerializer serializer) {
- if (propertyClass == null) {
- writer.WriteUInt32(0);
-
- return true;
+ if (!serializer.PreWriteObject(writer, propertyClass)) {
+ return false;
}
return propertyClass.Encode(writer, serializer);
@@ -297,23 +289,11 @@ private static bool DecodeEnum(BitReader reader, out object val, ObjectSerialize
private static bool DecodePropertyClass(BitReader reader,
ObjectSerializer serializer,
out PropertyClass? propertyClass) {
- propertyClass = null;
-
- var hash = reader.ReadUInt32();
- if (hash == 0) {
- return true;
- }
-
- // Dispatch this hash and see what property class we need to create.
- var fetchedType = serializer.TypeRegistry.LookupType(hash);
- if (fetchedType == null) {
+ if (!serializer.PreloadObject(reader, out propertyClass)) {
return false;
}
- // Create a new instance of the property class.
- propertyClass = (PropertyClass) Activator.CreateInstance(fetchedType)!;
-
- return propertyClass.Decode(reader, serializer);
+ return propertyClass!.Decode(reader, serializer);
}
private static object? CastDecodedValue(object? value) {
diff --git a/src/Imcodec.ObjectProperty/PropertyClass.cs b/src/Imcodec.ObjectProperty/PropertyClass.cs
index 30ff267..a8271fe 100644
--- a/src/Imcodec.ObjectProperty/PropertyClass.cs
+++ b/src/Imcodec.ObjectProperty/PropertyClass.cs
@@ -132,14 +132,6 @@ internal bool Decode(BitReader reader, ObjectSerializer serializer) {
return true;
}
- ///
- /// Encodes the object identifier using the specified .
- ///
- /// The used to write the
- /// encoded identifier.
- public virtual void EncodeIdentifier(BitWriter writer)
- => writer.WriteUInt32(GetHash());
-
private bool EncodeVersionable(BitWriter writer, ObjectSerializer serializer) {
writer.WriteUInt32(0); // Placeholder for the size.
diff --git a/test/CodeGen/CodeGenTest.cs b/test/CodeGen/CodeGenTest.cs
index 530745e..fe9bdb8 100644
--- a/test/CodeGen/CodeGenTest.cs
+++ b/test/CodeGen/CodeGenTest.cs
@@ -35,6 +35,11 @@ public void GenerateFromJsonManifestTest() {
var classDefinitions = compiler.Compile(jsonDump);
Assert.NotEmpty(classDefinitions);
+ Assert.Equal(2239, classDefinitions.Length);
+
+ var classDefinition = classDefinitions[23];
+ Assert.Equal("PetSnackBehaviorTemplate", classDefinition.Name);
+ Assert.Equal((double) 1956226406, classDefinition.Hash);
}
private static string? GetJsonDump() {
diff --git a/test/CoreObjectTest/CoreObjectTests.cs b/test/CoreObjectTest/CoreObjectTests.cs
new file mode 100644
index 0000000..6cc0ef8
--- /dev/null
+++ b/test/CoreObjectTest/CoreObjectTests.cs
@@ -0,0 +1,61 @@
+/*
+BSD 3-Clause License
+
+Copyright (c) 2024, Jooty
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+*/
+
+using Imcodec.IO;
+using Imcodec.ObjectProperty;
+using Imcodec.CoreObject;
+using Xunit;
+using Imcodec.Test.CoreObjectTest;
+
+namespace Imcodec.CoreObject.Tests;
+
+public class CoreObjectSerializerTest {
+
+ private const string CoreObjectBlob
+ = """
+ 70 00 00 00 78 DA 2B E6 3C CC CE C2 C0 C8 00 02 3B 97 F4 BB
+ 27 4D 99 29 C6 72 6F 93 CF 17 7D 6F E5 C4 2B CB 97 33 E0 04
+ 0D F6 20 9D A4 02 00 57 AC 0C A5
+ """;
+
+ [Fact]
+ public void Serializer_Deserialize() {
+
+ // Arrange
+ var blobBytes = CoreObjectBlob.Split([' ', '\n', '\r'], StringSplitOptions.RemoveEmptyEntries)
+ .Select(hex => Convert.ToByte(hex, 16))
+ .ToArray();
+ var serializer = new CoreObjectSerializer(typeRegistry: new DummyTypeRegistry());
+
+ // Act
+ if (!serializer.Deserialize(blobBytes, 0u, out var coreObject)) {
+ Assert.True(false, "Failed to serialize object.");
+ }
+
+ // Assert
+ Assert.NotNull(coreObject);
+ Assert.Equal(1, coreObject.m_fScale);
+ Assert.Equal((ulong) 264131, coreObject.m_templateID);
+ Assert.NotNull(coreObject.m_inactiveBehaviors);
+ Assert.Single(coreObject.m_inactiveBehaviors);
+
+ }
+
+}
\ No newline at end of file
diff --git a/test/CoreObjectTest/DummyTypeRegistry.cs b/test/CoreObjectTest/DummyTypeRegistry.cs
new file mode 100644
index 0000000..5c5f047
--- /dev/null
+++ b/test/CoreObjectTest/DummyTypeRegistry.cs
@@ -0,0 +1,88 @@
+/*
+BSD 3-Clause License
+
+Copyright (c) 2024, Jooty
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+*/
+
+using Imcodec.ObjectProperty;
+using Imcodec.IO;
+using Imcodec.Types;
+using Imcodec.Math;
+using Imcodec.CoreObject;
+
+namespace Imcodec.Test.CoreObjectTest;
+
+public class DummyTypeRegistry : TypeRegistry {
+
+ private static Dictionary TypeMap { get; set; } = new() {
+ { 350837933, typeof(ClientObject) },
+ { 1152306685, typeof(CoreObject) },
+ { 1200596153, typeof(RenderBehavior) },
+ { 1653772158, typeof(WizClientObjectItem) }
+ };
+
+ public override void RegisterType(uint hash, System.Type t)
+ => TypeMap[hash] = t;
+
+ public override System.Type? LookupType(uint type)
+ => TypeMap.TryGetValue(type, out var t) ? t : null;
+
+}
+
+public partial record WizClientObjectItem : ClientObject {
+
+ public override uint GetHash() => 1653772158;
+
+ [AutoProperty(900965981, 31)] public int m_primaryColor { get; set; }
+ [AutoProperty(1337683384, 31)] public int m_pattern { get; set; }
+ [AutoProperty(1616550081, 63)] public int m_secondaryColor { get; set; }
+ [AutoProperty(1022427803, 63)] public GID m_displayID { get; set; }
+ [AutoProperty(2004128457, 31)] public uint m_itemFlags { get; set; }
+
+}
+
+public partial record ClientObject : CoreObject {
+
+ public override uint GetHash() => 350837933;
+
+ [AutoProperty(210498418, 31)] public GID m_characterId { get; set; }
+}
+
+public partial record CoreObject : PropertyClass {
+
+ public override uint GetHash() => 1152306685;
+
+ [AutoProperty(1850812559, 31)] public List? m_inactiveBehaviors { get; set; }
+ [AutoProperty(2312465444, 16777247)] public ulong m_globalID { get; set; }
+ [AutoProperty(1298909658, 16777247)] public ulong m_permID { get; set; }
+ [AutoProperty(2239683611, 31)] public Vector3 m_location { get; set; }
+ [AutoProperty(2344058766, 31)] public Vector3 m_orientation { get; set; }
+ [AutoProperty(503137701, 31)] public float m_fScale { get; set; }
+ [AutoProperty(633907631, 31)] public ulong m_templateID { get; set; }
+ [AutoProperty(3553984419, 31)] public ByteString m_debugName { get; set; }
+ [AutoProperty(3023276954, 31)] public ByteString m_displayKey { get; set; }
+ [AutoProperty(965291410, 31)] public uint m_zoneTagID { get; set; }
+ [AutoProperty(123130076, 31)] public short m_speedMultiplier { get; set; }
+ [AutoProperty(1054318939, 31)] public ushort m_nMobileID { get; set; }
+
+}
+
+public partial record RenderBehavior : BehaviorInstance {
+
+ public override uint GetHash() => 1200596153;
+
+}
\ No newline at end of file
diff --git a/test/Imcodec.Test.csproj b/test/Imcodec.Test.csproj
index b09408a..65c0854 100644
--- a/test/Imcodec.Test.csproj
+++ b/test/Imcodec.Test.csproj
@@ -27,6 +27,7 @@
+
diff --git a/test/ObjectPropertyTest/DummyTypeRegistry.cs b/test/ObjectPropertyTest/DummyTypeRegistry.cs
index 304d1f1..fbdab85 100644
--- a/test/ObjectPropertyTest/DummyTypeRegistry.cs
+++ b/test/ObjectPropertyTest/DummyTypeRegistry.cs
@@ -82,8 +82,7 @@ public partial record LootInfoBase : PropertyClass {
}
-public enum LOOT_TYPE
-{
+public enum LOOT_TYPE {
LOOT_TYPE_NONE = 0,
LOOT_TYPE_GOLD = 1,