Skip to content

Instantly share code, notes, and snippets.

@barncastle
Created April 29, 2019 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barncastle/8ccb23dec431c57331f56c3ffcfcab27 to your computer and use it in GitHub Desktop.
Save barncastle/8ccb23dec431c57331f56c3ffcfcab27 to your computer and use it in GitHub Desktop.
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