Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions DBCD.IO/Common/IPrimitiveSetter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Runtime.CompilerServices;

namespace DBCD.IO.Common;

public interface IPrimitiveSetter<T>
{
// Value type setter for value type objects
void SetValue<TValue>(T refObject, TValue value) where TValue : struct;

// Fallback setter for value type assignments
void SetObject(T target, object value);
}

public class PrimitiveSetter<T, TField> : IPrimitiveSetter<T>
{
private readonly Action<T, TField> _setter;

public PrimitiveSetter(Action<T, TField> setter)
{
_setter = setter;
}

public void SetValue<TValue>(T target, TValue value) where TValue : struct
{
if (typeof(TValue) == typeof(TField))
_setter(target, Unsafe.As<TValue, TField>(ref value));
else
_setter(target, (TField)Convert.ChangeType(value, typeof(TField)));
}

public void SetObject(T target, object value)
{
_setter(target, (TField)value);
}
}
23 changes: 16 additions & 7 deletions DBCD.IO/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using DBCD.IO.Common;

namespace DBCD.IO
{
static class Extensions
{
public static Action<T, object> GetSetter<T>(this FieldInfo fieldInfo)
public static IPrimitiveSetter<T> GetSetter<T>(this FieldInfo fieldInfo)
{
var paramExpression = Expression.Parameter(typeof(T));
var propertyExpression = Expression.Field(paramExpression, fieldInfo);
var valueExpression = Expression.Parameter(typeof(object));
var convertExpression = Expression.Convert(valueExpression, fieldInfo.FieldType);
var assignExpression = Expression.Assign(propertyExpression, convertExpression);
Type fieldType = fieldInfo.FieldType;

var target = Expression.Parameter(typeof(T), "target");
var value = Expression.Parameter(fieldType, "value");
var field = Expression.Field(target, fieldInfo);
var assign = Expression.Assign(field, value);

// Compile to exact Action<T, TField>
var actionType = typeof(Action<,>).MakeGenericType(typeof(T), fieldType);
var lambda = Expression.Lambda(actionType, assign, target, value);
object compiledDelegate = lambda.Compile();

return Expression.Lambda<Action<T, object>>(assignExpression, paramExpression, valueExpression).Compile();
var closedSetterType = typeof(PrimitiveSetter<,>).MakeGenericType(typeof(T), fieldType);
var ctor = closedSetterType.GetConstructor(new[] { actionType });
return (IPrimitiveSetter<T>)ctor?.Invoke(new[] { compiledDelegate });
}

public static Func<T, object> GetGetter<T>(this FieldInfo fieldInfo)
Expand Down
3 changes: 2 additions & 1 deletion DBCD.IO/FieldCache.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using DBCD.IO.Attributes;
using System;
using System.Reflection;
using DBCD.IO.Common;

namespace DBCD.IO
{
Expand All @@ -9,7 +10,7 @@ class FieldCache<T>
public readonly FieldInfo Field;
public readonly bool IsArray = false;
public readonly bool IsLocalisedString = false;
public readonly Action<T, object> Setter;
public readonly IPrimitiveSetter<T> Setter;
public readonly Func<T, object> Getter;
public readonly LocaleAttribute LocaleInfo;

Expand Down
56 changes: 29 additions & 27 deletions DBCD.IO/Readers/HTFXReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,22 @@ public HTFXRow(BitReader data, IHotfixEntry hotfixEntry)
Id = hotfixEntry.RecordId;
}

private static Dictionary<Type, Func<BitReader, object>> simpleReaders = new Dictionary<Type, Func<BitReader, object>>
private static class SingleReaderCache<T>
{
[typeof(ulong)] = (data) => GetFieldValue<ulong>(data),
[typeof(long)] = (data) => GetFieldValue<long>(data),
[typeof(float)] = (data) => GetFieldValue<float>(data),
[typeof(int)] = (data) => GetFieldValue<int>(data),
[typeof(uint)] = (data) => GetFieldValue<uint>(data),
[typeof(short)] = (data) => GetFieldValue<short>(data),
[typeof(ushort)] = (data) => GetFieldValue<ushort>(data),
[typeof(sbyte)] = (data) => GetFieldValue<sbyte>(data),
[typeof(byte)] = (data) => GetFieldValue<byte>(data),
[typeof(string)] = (data) => data.ReadCString(),
};
public static readonly Dictionary<Type, Action<T, FieldCache<T>, BitReader>> Readers = new()
{
{ typeof(long), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<ulong>(data)) },
{ typeof(ulong), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<long>(data)) },
{ typeof(float), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<float>(data)) },
{ typeof(uint), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<uint>(data)) },
{ typeof(int), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<int>(data)) },
{ typeof(ushort), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<ushort>(data)) },
{ typeof(short), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<short>(data)) },
{ typeof(byte), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<byte>(data)) },
{ typeof(sbyte), (record, info, data) => info.Setter.SetValue(record, GetFieldValue<sbyte>(data)) },
{ typeof(string), (record, info, data) => info.Setter.SetObject(record, data.ReadCString()) }
};
}

private static Dictionary<Type, Func<BitReader, int, object>> arrayReaders = new Dictionary<Type, Func<BitReader, int, object>>
{
Expand All @@ -68,36 +71,36 @@ public void GetFields<T>(FieldCache<T>[] fields, T entry)
FieldCache<T> info = fields[i];
if (info.IndexMapField)
{
info.Setter(entry, Convert.ChangeType(Id, info.FieldType));
if (info.FieldType == typeof(int))
info.Setter.SetValue(entry, Id);
else
info.Setter.SetObject(entry, Convert.ChangeType(Id, info.FieldType));
continue;
}

object value = null;


if (info.IsArray)
{
if (arrayReaders.TryGetValue(info.MetaDataFieldType, out var reader))
value = reader(m_data, info.Cardinality);
info.Setter.SetObject(entry, reader(m_data, info.Cardinality));
else
throw new Exception("Unhandled array type: " + typeof(T).Name);
}
else
{
if (simpleReaders.TryGetValue(info.MetaDataFieldType, out var reader))
value = reader(m_data);
if (SingleReaderCache<T>.Readers.TryGetValue(info.MetaDataFieldType, out var reader))
reader(entry, info, m_data);
else
throw new Exception("Unhandled field type: " + typeof(T).Name);
}

/*
The strongly typed Setter does take care of converting mismatching types already
if (info.IsNonInlineRelation)
{
var casted = Convert.ChangeType(value, info.FieldType);
info.Setter(entry, casted);
}
else
{
info.Setter(entry, value);
var casted = Convert.ChangeType(info.Getter(entry), info.FieldType);
info.Setter.SetObject(entry, casted);
}
*/
}
}

Expand All @@ -114,8 +117,7 @@ private static T[] GetFieldValueArray<T>(BitReader r, int cardinality) where T :

return array;
}



#region Interface Methods

public IDBRow Clone()
Expand Down
53 changes: 27 additions & 26 deletions DBCD.IO/Readers/WDB2Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,23 @@ public WDB2Row(BaseReader reader, BitReader data, int recordIndex)
Id = m_recordIndex = recordIndex + 1;
}

private static Dictionary<Type, Func<BitReader, Dictionary<long, string>, BaseReader, object>> simpleReaders = new Dictionary<Type, Func<BitReader, Dictionary<long, string>, BaseReader, object>>
private static class SingleReaderCache<T>
{
[typeof(long)] = (data, stringTable, header) => GetFieldValue<long>(data),
[typeof(float)] = (data, stringTable, header) => GetFieldValue<float>(data),
[typeof(int)] = (data, stringTable, header) => GetFieldValue<int>(data),
[typeof(uint)] = (data, stringTable, header) => GetFieldValue<uint>(data),
[typeof(short)] = (data, stringTable, header) => GetFieldValue<short>(data),
[typeof(ushort)] = (data, stringTable, header) => GetFieldValue<ushort>(data),
[typeof(sbyte)] = (data, stringTable, header) => GetFieldValue<sbyte>(data),
[typeof(byte)] = (data, stringTable, header) => GetFieldValue<byte>(data),
[typeof(string)] = (data, stringTable, header) => stringTable[GetFieldValue<int>(data)],
};

public static readonly Dictionary<Type, Action<T, FieldCache<T>, BitReader, Dictionary<long, string>>> Readers = new()
{
{ typeof(ulong), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<ulong>(data)) },
{ typeof(long), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<long>(data)) },
{ typeof(float), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<float>(data)) },
{ typeof(uint), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<uint>(data)) },
{ typeof(int), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<int>(data)) },
{ typeof(ushort), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<ushort>(data)) },
{ typeof(short), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<short>(data)) },
{ typeof(byte), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<byte>(data)) },
{ typeof(sbyte), (record, info, data, _) => info.Setter.SetValue(record, GetFieldValue<sbyte>(data)) },
{ typeof(string), (record, info, data, stringTable) => info.Setter.SetObject(record, stringTable[GetFieldValue<int>(data)]) }
};
}

private static Dictionary<Type, Func<BitReader, Dictionary<long, string>, int, object>> arrayReaders = new Dictionary<Type, Func<BitReader, Dictionary<long, string>, int, object>>
{
[typeof(ulong[])] = (data, stringTable, cardinality) => GetFieldValueArray<ulong>(data, cardinality),
Expand All @@ -60,34 +64,31 @@ public void GetFields<T>(FieldCache<T>[] fields, T entry)
if (info.IndexMapField)
{
Id = GetFieldValue<int>(Data);
info.Setter(entry, Convert.ChangeType(Id, info.FieldType));

if (info.FieldType == typeof(T))
info.Setter.SetValue(entry, Id);
else
info.Setter.SetObject(entry, Convert.ChangeType(Id, info.FieldType));
continue;
}

object value = null;

if (info.IsArray)
{
if (arrayReaders.TryGetValue(info.FieldType, out var reader))
value = reader(Data, m_reader.StringTable, info.Cardinality);
info.Setter.SetObject(entry, reader(Data, m_reader.StringTable, info.Cardinality));
else
throw new Exception("Unhandled array type: " + typeof(T).Name);
}
else if (info.IsLocalisedString)
{
Data.Position += 32 * info.LocaleInfo.Locale;
value = simpleReaders[typeof(string)](Data, m_reader.StringTable, m_reader);
info.Setter.SetObject(entry, m_reader.StringTable[GetFieldValue<int>(Data)]);
Data.Position += 32 * (info.LocaleInfo.LocaleCount - info.LocaleInfo.Locale);
}
else if (SingleReaderCache<T>.Readers.TryGetValue(info.FieldType, out var reader))
reader(entry, info, Data, m_reader.StringTable);
else
{
if (simpleReaders.TryGetValue(info.FieldType, out var reader))
value = reader(Data, m_reader.StringTable, m_reader);
else
throw new Exception("Unhandled field type: " + typeof(T).Name);
}

info.Setter(entry, value);
throw new Exception("Unhandled field type: " + typeof(T).Name);
}
}

Expand All @@ -104,7 +105,7 @@ private static T[] GetFieldValueArray<T>(BitReader r, int cardinality) where T :

return array;
}

public IDBRow Clone()
{
return (IDBRow)MemberwiseClone();
Expand Down
57 changes: 32 additions & 25 deletions DBCD.IO/Readers/WDB3Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,29 @@ public WDB3Row(BaseReader reader, BitReader data, int id, int recordIndex)
m_dataPosition = Data.Position;
}

private static Dictionary<Type, Func<BitReader, Dictionary<long, string>, BaseReader, object>> simpleReaders = new Dictionary<Type, Func<BitReader, Dictionary<long, string>, BaseReader, object>>
private static class SingleReaderCache<T>
{
[typeof(long)] = (data, stringTable, header) => GetFieldValue<long>(data),
[typeof(float)] = (data, stringTable, header) => GetFieldValue<float>(data),
[typeof(int)] = (data, stringTable, header) => GetFieldValue<int>(data),
[typeof(uint)] = (data, stringTable, header) => GetFieldValue<uint>(data),
[typeof(short)] = (data, stringTable, header) => GetFieldValue<short>(data),
[typeof(ushort)] = (data, stringTable, header) => GetFieldValue<ushort>(data),
[typeof(sbyte)] = (data, stringTable, header) => GetFieldValue<sbyte>(data),
[typeof(byte)] = (data, stringTable, header) => GetFieldValue<byte>(data),
[typeof(string)] = (data, stringTable, header) => header.Flags.HasFlagExt(DB2Flags.Sparse) ? data.ReadCString() : stringTable[GetFieldValue<int>(data)],
};

public static readonly Dictionary<Type, Action<T, FieldCache<T>, BitReader, Dictionary<long, string>, BaseReader>> Readers = new()
{
{ typeof(ulong), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<ulong>(data)) },
{ typeof(long), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<long>(data)) },
{ typeof(float), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<float>(data)) },
{ typeof(uint), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<uint>(data)) },
{ typeof(int), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<int>(data)) },
{ typeof(ushort), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<ushort>(data)) },
{ typeof(short), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<short>(data)) },
{ typeof(byte), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<byte>(data)) },
{ typeof(sbyte), (record, info, data, _, _) => info.Setter.SetValue(record, GetFieldValue<sbyte>(data)) },
{ typeof(string), (record, info, data, stringTable, header) =>
{
string stringValue = header.Flags.HasFlagExt(DB2Flags.Sparse) ?
data.ReadCString() : stringTable[GetFieldValue<int>(data)];
info.Setter.SetObject(record, stringValue);
}
}
};
}

private static Dictionary<Type, Func<BitReader, Dictionary<long, string>, int, object>> arrayReaders = new Dictionary<Type, Func<BitReader, Dictionary<long, string>, int, object>>
{
[typeof(ulong[])] = (data, stringTable, cardinality) => GetFieldValueArray<ulong>(data, cardinality),
Expand Down Expand Up @@ -74,36 +84,33 @@ public void GetFields<T>(FieldCache<T>[] fields, T entry)
else
Id = GetFieldValue<int>(Data);

info.Setter(entry, Convert.ChangeType(Id, info.FieldType));
if (info.FieldType == typeof(int))
info.Setter.SetValue(entry, Id);
else
info.Setter.SetObject(entry, Convert.ChangeType(Id, info.FieldType));
continue;
}

object value = null;
int fieldIndex = i - indexFieldOffSet;

// 0x2 SecondaryKey
if (fieldIndex >= m_reader.FieldsCount)
{
info.Setter(entry, Convert.ChangeType(m_reader.ForeignKeyData[Id - m_reader.MinIndex], info.FieldType));
info.Setter.SetObject(entry, Convert.ChangeType(m_reader.ForeignKeyData[Id - m_reader.MinIndex], info.FieldType));
continue;
}

if (info.IsArray)
{
if (arrayReaders.TryGetValue(info.FieldType, out var reader))
value = reader(Data, m_reader.StringTable, info.Cardinality);
info.Setter.SetObject(entry, reader(Data, m_reader.StringTable, info.Cardinality));
else
throw new Exception("Unhandled array type: " + typeof(T).Name);
}
else if (SingleReaderCache<T>.Readers.TryGetValue(info.FieldType, out var reader))
reader(entry, info, Data, m_reader.StringTable, m_reader);
else
{
if (simpleReaders.TryGetValue(info.FieldType, out var reader))
value = reader(Data, m_reader.StringTable, m_reader);
else
throw new Exception("Unhandled field type: " + typeof(T).Name);
}

info.Setter(entry, value);
throw new Exception("Unhandled field type: " + typeof(T).Name);
}
}

Expand All @@ -120,7 +127,7 @@ private static T[] GetFieldValueArray<T>(BitReader r, int cardinality) where T :

return array;
}

public IDBRow Clone()
{
return (IDBRow)MemberwiseClone();
Expand Down
Loading
Loading