Created
April 29, 2019 15:54
-
-
Save barncastle/8ccb23dec431c57331f56c3ffcfcab27 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class DefinitionManager | |
{ | |
public static readonly Dictionary<string, Structs.DBDefinition> DefinitionLookup; | |
public static readonly Dictionary<(string, string), Type> DefinitionCache; | |
private static readonly ModuleBuilder moduleBuilder; | |
static DefinitionManager() | |
{ | |
DefinitionCache = new Dictionary<(string, string), Type>(); | |
DefinitionLookup = new Dictionary<string, Structs.DBDefinition>(StringComparer.OrdinalIgnoreCase); | |
var assemblyName = new AssemblyName("DBDefinitions"); | |
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); | |
moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); | |
LoadDefinitions(); | |
} | |
public static IDictionary LoadDB(string name, string build, bool fromCache = false) | |
{ | |
DBReader reader = new DBReader(File.Open(name, FileMode.Open, FileAccess.Read, FileShare.Read)); | |
var rawType = CompileDefinition(name, build, reader.LayoutHash); | |
var generic = typeof(DBReader).GetMethod("GetRecords").MakeGenericMethod(rawType); | |
return (IDictionary)generic.Invoke(reader, null); | |
} | |
private static void LoadDefinitions() | |
{ | |
// Reset cache | |
DefinitionCache.Clear(); | |
DefinitionLookup.Clear(); | |
var definitionsDir = @"C:\Users\spear\Desktop\WoWDBDefs\definitions"; | |
var reader = new DBDReader(); | |
foreach (var file in Directory.GetFiles(definitionsDir)) | |
DefinitionLookup.Add(Path.GetFileNameWithoutExtension(file), reader.Read(file)); | |
} | |
private static Type CompileDefinition(string filename, string build, uint layoutHash, bool force = false) | |
{ | |
var dbName = Path.GetFileNameWithoutExtension(filename); | |
var cleanDBName = dbName.ToLower(); | |
var randomSuffix = Path.GetRandomFileName().Replace(".", ""); | |
if (!force && DefinitionCache.TryGetValue((cleanDBName, build), out var knownType)) | |
return knownType; | |
if (!File.Exists(filename)) | |
throw new Exception("Input DB2 file does not exist!"); | |
if (!DefinitionLookup.ContainsKey(cleanDBName)) | |
throw new KeyNotFoundException("Definition for " + cleanDBName); | |
var defs = DefinitionLookup[cleanDBName]; | |
if (!Utils.GetVersionDefinitionByLayoutHash(defs, layoutHash.ToString("X8"), out Structs.VersionDefinitions? versionToUse)) | |
{ | |
if (!string.IsNullOrWhiteSpace(build)) | |
{ | |
if (!Utils.GetVersionDefinitionByBuild(defs, new Build(build), out versionToUse)) | |
throw new Exception("No valid definition found for this layouthash or build!"); | |
} | |
else | |
{ | |
throw new Exception("No valid definition found for this layouthash and was not able to search by build!"); | |
} | |
} | |
var typeBuilder = moduleBuilder.DefineType(dbName + randomSuffix + "Struct", TypeAttributes.Public); | |
foreach (var field in versionToUse.Value.definitions) | |
{ | |
var nativetype = DBDefTypeToType(defs.columnDefinitions[field.name].type, field.size, field.isSigned, field.arrLength); | |
var fieldBuilder = typeBuilder.DefineField(field.name, nativetype, FieldAttributes.Public); | |
if (field.isID) | |
{ | |
var constructorParameters = new Type[] { }; | |
var constructorInfo = typeof(IndexAttribute).GetConstructor(constructorParameters); | |
var indexAttributeBuilder = new CustomAttributeBuilder(constructorInfo, new object[] { }); | |
fieldBuilder.SetCustomAttribute(indexAttributeBuilder); | |
} | |
if (field.arrLength > 1) | |
{ | |
var constructorParameters = new Type[] { typeof(int) }; | |
var constructorInfo = typeof(CardinalityAttribute).GetConstructor(constructorParameters); | |
var cardinalityAttributeBuilder = new CustomAttributeBuilder(constructorInfo, new object[] { field.arrLength }); | |
fieldBuilder.SetCustomAttribute(cardinalityAttributeBuilder); | |
} | |
} | |
return DefinitionCache[(cleanDBName, build)] = typeBuilder.CreateType(); | |
} | |
private static Type DBDefTypeToType(string type, int size, bool signed, int arrLength) | |
{ | |
Type nativetype = null; | |
switch (type) | |
{ | |
case "int" when size == 8: | |
nativetype = signed ? typeof(sbyte) : typeof(byte); | |
break; | |
case "int" when size == 16: | |
nativetype = signed ? typeof(short) : typeof(ushort); | |
break; | |
case "int" when size == 32: | |
nativetype = signed ? typeof(int) : typeof(uint); | |
break; | |
case "int" when size == 64: | |
nativetype = signed ? typeof(long) : typeof(ulong); | |
break; | |
case "string": | |
case "locstring": | |
nativetype = typeof(string); | |
break; | |
case "float": | |
nativetype = typeof(float); | |
break; | |
default: | |
throw new Exception("oh lord jesus have mercy i don't know about type " + type); | |
} | |
return arrLength <= 1 ? nativetype : nativetype.MakeArrayType(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment