From a932502173bf3d276d8d7f482139812ce0d3bac7 Mon Sep 17 00:00:00 2001 From: Andreas Pardeike Date: Fri, 6 Jan 2017 21:31:20 +0100 Subject: [PATCH] Refactored for release. This is v1.0.0 --- Harmony/AccessTools.cs | 55 +++++++- Harmony/Attributes.cs | 65 ++++++---- Harmony/Harmony.csproj | 1 + Harmony/HarmonyInstance.cs | 16 ++- Harmony/HarmonyMethod.cs | 126 +++++++++++++++++++ Harmony/HarmonyRegistry.cs | 39 ++++-- Harmony/Patch.cs | 4 +- Harmony/PatchTools.cs | 68 ++++++---- Harmony/Patcher.cs | 99 ++++++++++----- Harmony/Priority.cs | 59 ++------- Harmony/Properties/AssemblyInfo.cs | 4 +- HarmonyTests/Tools/Assets/AttributesClass.cs | 10 +- HarmonyTests/Tools/TestAttributes.cs | 8 +- 13 files changed, 394 insertions(+), 160 deletions(-) create mode 100644 Harmony/HarmonyMethod.cs diff --git a/Harmony/AccessTools.cs b/Harmony/AccessTools.cs index c64ba9ea..6075c793 100644 --- a/Harmony/AccessTools.cs +++ b/Harmony/AccessTools.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -27,11 +28,11 @@ public static PropertyInfo Property(Type type, string name) return type.GetProperty(name, all); } - public static MethodInfo Method(Type type, string name, Type[] arguments = null) + public static MethodInfo Method(Type type, string name, Type[] parameters = null) { if (type == null || name == null) return null; - if (arguments == null) return type.GetMethod(name, all); - var result = type.GetMethod(name, all, null, arguments, null); + if (parameters == null) return type.GetMethod(name, all); + var result = type.GetMethod(name, all, null, parameters, null); return result; } @@ -41,10 +42,32 @@ public static Type Inner(Type type, string name) return type.GetNestedType(name, all); } - public static Type[] GetTypes(object[] arguments) + public static Type[] GetTypes(object[] parameters) { - if (arguments == null) return new Type[0]; - return arguments.Select(a => a == null ? typeof(object) : a.GetType()).ToArray(); + if (parameters == null) return new Type[0]; + return parameters.Select(p => p == null ? typeof(object) : p.GetType()).ToArray(); + } + + public static List GetFieldNames(Type type) + { + return type.GetFields(all).Select(f => f.Name).ToList(); + } + + public static List GetFieldNames(object instance) + { + if (instance == null) return new List(); + return GetFieldNames(instance.GetType()); + } + + public static List GetPropertyNames(Type type) + { + return type.GetProperties(all).Select(f => f.Name).ToList(); + } + + public static List GetPropertyNames(object instance) + { + if (instance == null) return new List(); + return GetPropertyNames(instance.GetType()); } public static object GetDefaultValue(Type type) @@ -55,6 +78,26 @@ public static object GetDefaultValue(Type type) return Activator.CreateInstance(type); return null; } + + public static bool isStruct(Type type) + { + return type.IsValueType && !isValue(type) && !isVoid(type); + } + + public static bool isClass(Type type) + { + return !type.IsValueType; + } + + public static bool isValue(Type type) + { + return type.IsPrimitive || type.IsEnum; + } + + public static bool isVoid(Type type) + { + return type == typeof(void); + } } public static class TypeExtensions diff --git a/Harmony/Attributes.cs b/Harmony/Attributes.cs index 051e17b6..e1f5e2a3 100644 --- a/Harmony/Attributes.cs +++ b/Harmony/Attributes.cs @@ -1,59 +1,82 @@ using System; -using System.Collections.Generic; namespace Harmony { - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class HarmonyPatch : Attribute + public class HarmonyAttribute : Attribute { - public Type type; - public string methodName; - public Type[] parameter; + public HarmonyMethod info = new HarmonyMethod(); + } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class HarmonyPatch : HarmonyAttribute + { public HarmonyPatch() { } public HarmonyPatch(Type type) { - this.type = type; + info.originalType = type; } public HarmonyPatch(string methodName) { - this.methodName = methodName; + info.methodName = methodName; } public HarmonyPatch(Type[] parameter) { - this.parameter = parameter; + info.parameter = parameter; + } + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyPriority : HarmonyAttribute + { + public HarmonyPriority(int prioritiy) + { + info.prioritiy = prioritiy; } + } - public static HarmonyPatch Merge(List attributes) + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyBefore : HarmonyAttribute + { + public HarmonyBefore(params string[] before) { - var result = new HarmonyPatch(); - attributes.ForEach(attr => - { - if (attr.type != null) result.type = attr.type; - if (attr.methodName != null) result.methodName = attr.methodName; - if (attr.parameter != null) result.parameter = attr.parameter; - }); - return result; + info.before = before; } } + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class HarmonyAfter : HarmonyAttribute + { + public HarmonyAfter(params string[] after) + { + info.after = after; + } + } + + // If you don't want to use the special method names you can annotate + // using the following attributes: + [AttributeUsage(AttributeTargets.Method)] - public class HarmonyPrefix : Attribute + public class HarmonyPrepare : Attribute { } [AttributeUsage(AttributeTargets.Method)] - public class HarmonyPostfix : Attribute + public class HarmonyTargetMethod : Attribute { } [AttributeUsage(AttributeTargets.Method)] - public class HarmonyPrepare : Attribute + public class HarmonyPrefix : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + public class HarmonyPostfix : Attribute { } } \ No newline at end of file diff --git a/Harmony/Harmony.csproj b/Harmony/Harmony.csproj index a90fbd40..971ce578 100644 --- a/Harmony/Harmony.csproj +++ b/Harmony/Harmony.csproj @@ -45,6 +45,7 @@ + diff --git a/Harmony/HarmonyInstance.cs b/Harmony/HarmonyInstance.cs index 401145fe..5a8978b0 100644 --- a/Harmony/HarmonyInstance.cs +++ b/Harmony/HarmonyInstance.cs @@ -5,7 +5,7 @@ namespace Harmony { - public delegate void PatchCallback(MethodInfo original, MethodInfo prefixPatch, MethodInfo postfixPatch); + public delegate void PatchCallback(MethodInfo original, HarmonyMethod prefixPatch, HarmonyMethod postfixPatch); public class HarmonyInstance { @@ -28,7 +28,7 @@ private HarmonyInstance(string id, ContactInfo contact) { this.id = id; this.contact = contact; - patcher = new Patcher(this, delegate (MethodInfo original, MethodInfo prefixPatch, MethodInfo postfixPatch) + patcher = new Patcher(this, delegate (MethodInfo original, HarmonyMethod prefixPatch, HarmonyMethod postfixPatch) { var register = registry.GetRegisterPatch(); register(id, original, prefixPatch, postfixPatch); @@ -77,7 +77,7 @@ public void PatchAll(Module module) patcher.PatchAll(module); } - public void Patch(MethodInfo original, MethodInfo prefix, MethodInfo postfix) + public void Patch(MethodInfo original, HarmonyMethod prefix, HarmonyMethod postfix) { patcher.Patch(original, prefix, postfix); } @@ -100,5 +100,15 @@ public class ContactInfo public string steamURL; public string website; + + public override string ToString() + { + var trv = Traverse.Create(this); + var parts = AccessTools.GetFieldNames(this) + .Select(f => trv.Field(f).GetValue().ToString()) + .Where(s => s != null && s != "") + .ToArray(); + return "[" + string.Join(",", parts) + "]"; + } } } \ No newline at end of file diff --git a/Harmony/HarmonyMethod.cs b/Harmony/HarmonyMethod.cs new file mode 100644 index 00000000..62f2c08c --- /dev/null +++ b/Harmony/HarmonyMethod.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Harmony +{ + public class HarmonyMethod + { + public MethodInfo method; // need to be called 'method' + + public Type originalType; + public string methodName; + public Type[] parameter; + public int prioritiy = -1; + public string[] before; + public string[] after; + + public HarmonyMethod() + { + } + + void ImportMethod(MethodInfo method) + { + this.method = method; + if (method != null) + { + var infos = method.GetHarmonyMethods(); + if (infos != null) + Merge(infos).CopyTo(this); + } + } + + public HarmonyMethod(MethodInfo method) + { + ImportMethod(method); + } + + public HarmonyMethod(Type type, string name, Type[] parameters = null) + { + ImportMethod(AccessTools.Method(type, name, parameters)); + } + + public static List HarmonyFields() + { + return AccessTools + .GetFieldNames(typeof(HarmonyMethod)) + .Where(s => s != "method") + .ToList(); + } + + public static HarmonyMethod Merge(List attributes) + { + var result = new HarmonyMethod(); + if (attributes == null) return result; + var resultTrv = Traverse.Create(result); + attributes.ForEach(attribute => + { + var trv = Traverse.Create(attribute); + HarmonyFields().ForEach(f => + { + var val = trv.Field(f).GetValue(); + if (val != null) + resultTrv.Field(f).SetValue(val); + }); + }); + return result; + } + } + + public static class HarmonyMethodExtensions + { + public static void CopyTo(this HarmonyMethod from, HarmonyMethod to) + { + if (to == null) return; + var fromTrv = Traverse.Create(from); + var toTrv = Traverse.Create(to); + HarmonyMethod.HarmonyFields().ForEach(f => + { + var val = fromTrv.Field(f).GetValue(); + if (val != null) toTrv.Field(f).SetValue(val); + }); + } + + public static HarmonyMethod Clone(this HarmonyMethod original) + { + var result = new HarmonyMethod(); + original.CopyTo(result); + return result; + } + + public static HarmonyMethod Merge(this HarmonyMethod master, HarmonyMethod detail) + { + if (detail == null) return master; + var result = new HarmonyMethod(); + var resultTrv = Traverse.Create(result); + var masterTrv = Traverse.Create(master); + var detailTrv = Traverse.Create(detail); + HarmonyMethod.HarmonyFields().ForEach(f => + { + var baseValue = masterTrv.Field(f).GetValue(); + var detailValue = detailTrv.Field(f).GetValue(); + resultTrv.Field(f).SetValue(detailValue != null ? detailValue : baseValue); + }); + return result; + } + + public static List GetHarmonyMethods(this Type type) + { + return type.GetCustomAttributes(true) + .Where(attr => attr is HarmonyAttribute) + .Cast() + .Select(attr => attr.info) + .ToList(); + } + + public static List GetHarmonyMethods(this MethodInfo method) + { + return method.GetCustomAttributes(true) + .Where(attr => attr is HarmonyAttribute) + .Cast() + .Select(attr => attr.info) + .ToList(); + } + } +} \ No newline at end of file diff --git a/Harmony/HarmonyRegistry.cs b/Harmony/HarmonyRegistry.cs index 8c57ec3e..2aa7aca4 100644 --- a/Harmony/HarmonyRegistry.cs +++ b/Harmony/HarmonyRegistry.cs @@ -7,7 +7,7 @@ namespace Harmony { - public delegate void RegisterPatch(string owner, MethodInfo original, MethodInfo prefixPatch, MethodInfo postfixPatch); + public delegate void RegisterPatch(string owner, MethodInfo original, HarmonyMethod prefixPatch, HarmonyMethod postfixPatch); public class PatchInfo { @@ -57,15 +57,25 @@ public Patches(MethodInfo method) postfixes = new List(); } - public void AddPrefix(string owner, MethodInfo method) + public void AddPrefix(string owner, HarmonyMethod info) { - var patch = new Patch(prefixes.Count, owner, method, Priority.Normal, null, null); + if (info == null || info.method == null) return; + + var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy; + var before = info.before != null ? new HashSet(info.before) : null; + var after = info.after != null ? new HashSet(info.after) : null; + var patch = new Patch(prefixes.Count, owner, info.method, priority, before, after); prefixes.Add(patch); } - public void AddPostfix(string owner, MethodInfo method) + public void AddPostfix(string owner, HarmonyMethod info) { - var patch = new Patch(postfixes.Count, owner, method, Priority.Normal, null, null); + if (info == null || info.method == null) return; + + var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy; + var before = info.before != null ? new HashSet(info.before) : null; + var after = info.after != null ? new HashSet(info.after) : null; + var patch = new Patch(postfixes.Count, owner, info.method, priority, before, after); postfixes.Add(patch); } @@ -94,10 +104,14 @@ public HarmonyRegistry() instances = new Dictionary(); allPatches = new Dictionary(); - registerCallback = delegate (string owner, MethodInfo original, MethodInfo prefix, MethodInfo postfix) - { - AddPatch(owner, original, prefix, postfix); - }; + registerCallback = delegate ( + string owner, + MethodInfo original, + HarmonyMethod prefix, + HarmonyMethod postfix) + { + AddPatch(owner, original, prefix, postfix); + }; } public RegisterPatch GetRegisterPatch() @@ -105,8 +119,10 @@ public RegisterPatch GetRegisterPatch() return registerCallback; } - void AddPatch(string owner, MethodInfo original, MethodInfo prefix, MethodInfo postfix) + void AddPatch(string owner, MethodInfo original, HarmonyMethod prefix, HarmonyMethod postfix) { + if (original == null) throw new ArgumentNullException("original"); + Patches patches; allPatches.TryGetValue(original, out patches); if (patches == null) @@ -128,7 +144,7 @@ public void Add(HarmonyInstance instance) if (instances.ContainsKey(id)) { var info = instance.Contact; - throw new ArgumentException("ID must be unique and is already registered by " + info); + throw new ArgumentException("ID " + id + " must be unique and is already registered by " + info); } instances.Add(id, instance); @@ -136,6 +152,7 @@ public void Add(HarmonyInstance instance) public PatchInfo IsPatched(MethodInfo method) { + if (method == null) throw new ArgumentNullException("method"); if (allPatches.ContainsKey(method) == false) return null; return allPatches[method].GetInfo(); } diff --git a/Harmony/Patch.cs b/Harmony/Patch.cs index 03c23fcc..6f7129f8 100644 --- a/Harmony/Patch.cs +++ b/Harmony/Patch.cs @@ -9,11 +9,11 @@ public class Patch : IComparable int index; public string owner; public MethodInfo patch; - public Priority.Value priority; + public int priority; public HashSet before; public HashSet after; - public Patch(int index, string owner, MethodInfo patch, Priority.Value priority, HashSet before, HashSet after) + public Patch(int index, string owner, MethodInfo patch, int priority, HashSet before, HashSet after) { this.index = index; this.owner = owner; diff --git a/Harmony/PatchTools.cs b/Harmony/PatchTools.cs index 9c88f9b1..f7f166cf 100644 --- a/Harmony/PatchTools.cs +++ b/Harmony/PatchTools.cs @@ -107,11 +107,13 @@ public static void GetPatches(Type patchType, MethodInfo original, out MethodInf // void Postfix([TYPE instance,] [ref TYPE result,], ref p1, ref p2, ref p3 ...) // - "instance" only for non-static original methods // - "result" only for original methods that do not return void - // - prefix will receive all parameters EXCEPT "out" parameters + // - prefix will receive all parameters EXCEPT "out" parameters !! // - // static RTYPE Patch(TYPE instance, TYPE p1, TYPE p2, TYPE p3 ...) + // The wrapper will create roughly like this: + // + // static RTYPE ORIGINAL_wrapper(TYPE instance, TYPE p1, TYPE p2, TYPE p3 ...) // { - // object result = default(RTYPE); + // RTYPE result = default(RTYPE); // // bool run = true; // @@ -138,32 +140,48 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o var isInstance = original.IsStatic == false; var returnType = original.ReturnType; - var hasReturnValue = returnType != typeof(void); + var returnsSomething = AccessTools.isVoid(returnType) == false; + var returnsClassType = AccessTools.isClass(returnType); + var returnsStructType = AccessTools.isStruct(returnType); + var returnsValueType = AccessTools.isValue(returnType); + var parameters = original.GetParameters(); - var resultType = hasReturnValue ? returnType : typeof(object); - g.DeclareLocal(resultType); // v0 - result - g.DeclareLocal(typeof(bool)); // v1 - run + g.DeclareLocal(typeof(bool)); // v0 - run + if (returnsSomething) + g.DeclareLocal(returnType); // v1 - result (if not void) + // for debugging g.Emit(OpCodes.Nop); - // ResultType result = [default value for ResultType]; - if (returnType.IsValueType && hasReturnValue) - g.Emit(OpCodes.Ldc_I4, 0); - else + // ResultType result = [default value for ResultType] + // + if (returnsClassType) + { g.Emit(OpCodes.Ldnull); - g.Emit(OpCodes.Stloc_0); // to v0 + g.Emit(OpCodes.Stloc_1); // to v1 + } + if (returnsStructType) + { + g.Emit(OpCodes.Ldloca_S, 1); // v1 ref + g.Emit(OpCodes.Initobj, returnType); // init + } + if (returnsValueType) + { + g.Emit(OpCodes.Ldc_I4, 0); // 0 + g.Emit(OpCodes.Stloc_1); // to v1 + } // bool run = true; g.Emit(OpCodes.Ldc_I4, 1); // true - g.Emit(OpCodes.Stloc_1); // to v1 + g.Emit(OpCodes.Stloc_0); // to v0 prefixPatches.ForEach(prefix => { var ifRunPrepatch = g.DefineLabel(); // if (run) - g.Emit(OpCodes.Ldloc_1); // v1 + g.Emit(OpCodes.Ldloc_0); // v0 g.Emit(OpCodes.Ldc_I4, 0); // false g.Emit(OpCodes.Ceq); // compare g.Emit(OpCodes.Brtrue, ifRunPrepatch); // jump to (A) @@ -171,8 +189,8 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o // run = Prefix[n](instance, ref result, ref p1, ref p2, ref p3 ...); if (isInstance) g.Emit(OpCodes.Ldarg_0); // instance - if (hasReturnValue) - g.Emit(OpCodes.Ldloca_S, 0); // ref result + if (returnsSomething) + g.Emit(OpCodes.Ldloca_S, 1); // ref v1 for (int j = 0; j < parameters.Count(); j++) { if (parameters[j].IsOut) continue; // out parameter make no sense for prefix methods @@ -180,13 +198,13 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o g.Emit(OpCodes.Ldarga_S, j2); // ref p[1..n] } g.Emit(OpCodes.Call, prefix); // call prefix patch - g.Emit(OpCodes.Stloc_1); // to v1 + g.Emit(OpCodes.Stloc_0); // to v0 g.MarkLabel(ifRunPrepatch); // (A) }); // if (run) - g.Emit(OpCodes.Ldloc_1); // v1 + g.Emit(OpCodes.Ldloc_0); // v0 g.Emit(OpCodes.Ldc_I4, 0); // false g.Emit(OpCodes.Ceq); // compare var ifRunOriginal = g.DefineLabel(); @@ -206,8 +224,8 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o if (j2 > 3) g.Emit(OpCodes.Ldarg_S, j2); } g.Emit(OpCodes.Call, originalCopy); // call copy of original - if (hasReturnValue) - g.Emit(OpCodes.Stloc_0); // to v0 + if (returnsSomething) + g.Emit(OpCodes.Stloc_1); // to v1 g.MarkLabel(ifRunOriginal); // (B) @@ -216,8 +234,8 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o // Postfix[n](instance, ref result, ref p1, ref p2, ref p3 ...); if (isInstance) g.Emit(OpCodes.Ldarg_0); // instance - if (hasReturnValue) - g.Emit(OpCodes.Ldloca_S, 0); // ref result + if (returnsSomething) + g.Emit(OpCodes.Ldloca_S, 1); // ref v1 for (int j = 0; j < parameters.Count(); j++) { var j2 = isInstance ? j + 1 : j; @@ -226,9 +244,9 @@ public static DynamicMethod CreatePatchWrapper(MethodInfo original, MethodInfo o g.Emit(OpCodes.Call, postfix); // call prefix patch }); - if (hasReturnValue) - g.Emit(OpCodes.Ldloc_0); // v0 - g.Emit(OpCodes.Ret); // v0 + if (returnsSomething) + g.Emit(OpCodes.Ldloc_1); // v1 + g.Emit(OpCodes.Ret); return method; } diff --git a/Harmony/Patcher.cs b/Harmony/Patcher.cs index 107a1691..81477fc2 100644 --- a/Harmony/Patcher.cs +++ b/Harmony/Patcher.cs @@ -17,49 +17,82 @@ public Patcher(HarmonyInstance instance, PatchCallback patchCallback) public void PatchAll(Module module) { - module.GetTypes().ToList() - .ForEach(type => + module.GetTypes().ToList().ForEach(ownerType => + { + var baseMethodInfos = ownerType.GetHarmonyMethods(); + if (baseMethodInfos != null && baseMethodInfos.Count() > 0) { - var attrList = type.GetCustomAttributes(true) - .Where(attr => attr is HarmonyPatch) - .Cast().ToList(); - if (attrList != null && attrList.Count() > 0) - { + var baseInfo = HarmonyMethod.Merge(baseMethodInfos); - var prepare = PatchTools.GetPatchMethod(type, "Prepare", new Type[] { typeof(HarmonyInstance) }); - if (prepare != null) - prepare.Invoke(null, new object[] { instance }); + RunMethod(ownerType); - prepare = PatchTools.GetPatchMethod(type, "Prepare", Type.EmptyTypes); - if (prepare != null) - prepare.Invoke(null, Type.EmptyTypes); + MethodInfo original = RunMethod(ownerType); + if (original == null) + original = GetOriginalMethod(ownerType, baseInfo); + if (original != null) + { + HarmonyMethod prefixInfo = baseInfo.Clone(); + HarmonyMethod postfixInfo = baseInfo.Clone(); + PatchTools.GetPatches(ownerType, original, out prefixInfo.method, out postfixInfo.method); - var info = HarmonyPatch.Merge(attrList); - if (info.type != null || info.methodName != null || info.parameter != null) + if (prefixInfo.method != null) { - if (info.type == null) throw new ArgumentException("HarmonyPatch(type) not specified for class " + type.FullName); - if (info.methodName == null) throw new ArgumentException("HarmonyPatch(string) not specified for class " + type.FullName); - if (info.parameter == null) throw new ArgumentException("HarmonyPatch(Type[]) not specified for class " + type.FullName); - - var methodName = info.methodName; - var paramTypes = info.parameter; - var original = type.GetMethod(methodName, AccessTools.all, null, paramTypes, null); - if (original == null) - { - var paramList = "(" + string.Join(",", paramTypes.Select(t => t.FullName).ToArray()) + ")"; - throw new ArgumentException("No method found for " + type.FullName + "." + methodName + paramList); - } + var prefixAttributes = prefixInfo.method.GetHarmonyMethods(); + baseInfo.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefixInfo); + } - MethodInfo prefix; - MethodInfo postfix; - PatchTools.GetPatches(type, original, out prefix, out postfix); - Patch(original, prefix, postfix); + if (postfixInfo.method != null) + { + var postfixAttributes = postfixInfo.method.GetHarmonyMethods(); + baseInfo.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfixInfo); } + + Patch(original, prefixInfo, postfixInfo); } - }); + } + }); + } + + public T RunMethod(Type type) + { + var name = typeof(S).Name.Replace("Harmony", ""); + + var method = PatchTools.GetPatchMethod(type, name, new Type[] { typeof(HarmonyInstance) }); + if (method != null && typeof(T).IsAssignableFrom(method.ReturnType)) + return (T)method.Invoke(null, new object[] { instance }); + + method = PatchTools.GetPatchMethod(type, name, Type.EmptyTypes); + if (method != null && typeof(T).IsAssignableFrom(method.ReturnType)) + return (T)method.Invoke(null, Type.EmptyTypes); + + return default(T); + } + + private static MethodInfo GetOriginalMethod(Type ownerType, HarmonyMethod baseInfo) + { + if (baseInfo.originalType == null) throw new ArgumentException("HarmonyPatch(type) not specified for class " + ownerType.FullName); + if (baseInfo.methodName == null) throw new ArgumentException("HarmonyPatch(string) not specified for class " + ownerType.FullName); + + if (baseInfo.parameter == null) + { + var original = baseInfo.originalType.GetMethod(baseInfo.methodName, AccessTools.all); + if (original == null) + throw new ArgumentException("No method found for " + baseInfo.originalType.FullName + "." + baseInfo.methodName); + return original; + } + else + { + var original = baseInfo.originalType.GetMethod(baseInfo.methodName, AccessTools.all, null, baseInfo.parameter, null); + if (original == null) + { + var paramList = "(" + string.Join(",", baseInfo.parameter.Select(t => t.FullName).ToArray()) + ")"; + throw new ArgumentException("No method found for " + baseInfo.originalType.FullName + "." + baseInfo.methodName + paramList); + } + return original; + } } - public void Patch(MethodInfo original, MethodInfo prefix, MethodInfo postfix) + public void Patch(MethodInfo original, HarmonyMethod prefix, HarmonyMethod postfix) { patchCallback(original, prefix, postfix); } diff --git a/Harmony/Priority.cs b/Harmony/Priority.cs index 9d40dd18..6ba8d761 100644 --- a/Harmony/Priority.cs +++ b/Harmony/Priority.cs @@ -4,55 +4,14 @@ namespace Harmony { public static class Priority { - public static Value Last = new Value(0); - public static Value VeryLow = new Value(100); - public static Value Low = new Value(200); - public static Value LowerThanNormal = new Value(300); - public static Value Normal = new Value(400); - public static Value HigherThanNormal = new Value(500); - public static Value High = new Value(600); - public static Value VeryHigh = new Value(700); - public static Value First = new Value(800); - - public static Value Plus(this Value self, int value) - { - return new Value(self.value + value); - } - - public static Value Minus(this Value self, int value) - { - return new Value(self.value - value); - } - - public class Value : IComparable - { - public int value; - - public Value(int value) - { - this.value = value; - } - - public override bool Equals(object obj) - { - return ((obj != null) && (obj is Value) && (value == ((Value)obj).value)); - } - - public int CompareTo(object obj) - { - var other = obj as Value; - return value.CompareTo(other.value); - } - - public override int GetHashCode() - { - return value.GetHashCode(); - } - } - - public static Value For(int priority) - { - return new Value(priority); - } + public const int Last = 0; + public const int VeryLow = 100; + public const int Low = 200; + public const int LowerThanNormal = 300; + public const int Normal = 400; + public const int HigherThanNormal = 500; + public const int High = 600; + public const int VeryHigh = 700; + public const int First = 800; } } \ No newline at end of file diff --git a/Harmony/Properties/AssemblyInfo.cs b/Harmony/Properties/AssemblyInfo.cs index 58756c00..1e6d319a 100644 --- a/Harmony/Properties/AssemblyInfo.cs +++ b/Harmony/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.1")] -[assembly: AssemblyFileVersion("1.0.0.1")] +[assembly: AssemblyVersion("1.0.0.2")] +[assembly: AssemblyFileVersion("1.0.0.2")] diff --git a/HarmonyTests/Tools/Assets/AttributesClass.cs b/HarmonyTests/Tools/Assets/AttributesClass.cs index cecb101d..96752a8f 100644 --- a/HarmonyTests/Tools/Assets/AttributesClass.cs +++ b/HarmonyTests/Tools/Assets/AttributesClass.cs @@ -18,10 +18,16 @@ public class AllAttributesClass [HarmonyPrepare] public void Method1() { } - [HarmonyPrefix] + [HarmonyTargetMethod] public void Method2() { } - [HarmonyPostfix] + [HarmonyPrefix] + [HarmonyPriority(Priority.High)] public void Method3() { } + + [HarmonyPostfix] + [HarmonyBefore("foo", "bar")] + [HarmonyAfter("test")] + public void Method4() { } } } \ No newline at end of file diff --git a/HarmonyTests/Tools/TestAttributes.cs b/HarmonyTests/Tools/TestAttributes.cs index 5528a8f5..c1c32e7c 100644 --- a/HarmonyTests/Tools/TestAttributes.cs +++ b/HarmonyTests/Tools/TestAttributes.cs @@ -12,17 +12,15 @@ public class Test_Attributes public void TestAttributes() { var type = typeof(AllAttributesClass); - var attr = type.GetCustomAttributes(false); - var attributes = attr.Cast().ToList(); - var info = HarmonyPatch.Merge(attributes); + var infos = type.GetHarmonyMethods(); + var info = HarmonyMethod.Merge(infos); Assert.IsNotNull(info); - Assert.AreEqual(typeof(string), info.type); + Assert.AreEqual(typeof(string), info.originalType); Assert.AreEqual("foobar", info.methodName); Assert.IsNotNull(info.parameter); Assert.AreEqual(2, info.parameter.Length); Assert.AreEqual(typeof(float), info.parameter[0]); Assert.AreEqual(typeof(string), info.parameter[1]); - } } } \ No newline at end of file