123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- namespace Harmony
- {
- public class PatchProcessor
- {
- static object locker = new object();
- readonly HarmonyInstance instance;
- readonly Type container;
- readonly HarmonyMethod containerAttributes;
- List<MethodBase> originals = new List<MethodBase>();
- HarmonyMethod prefix;
- HarmonyMethod postfix;
- HarmonyMethod transpiler;
- public PatchProcessor(HarmonyInstance instance, Type type, HarmonyMethod attributes)
- {
- this.instance = instance;
- container = type;
- containerAttributes = attributes ?? new HarmonyMethod(null);
- prefix = containerAttributes.Clone();
- postfix = containerAttributes.Clone();
- transpiler = containerAttributes.Clone();
- PrepareType();
- }
- public PatchProcessor(HarmonyInstance instance, List<MethodBase> originals, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
- {
- this.instance = instance;
- this.originals = originals;
- this.prefix = prefix ?? new HarmonyMethod(null);
- this.postfix = postfix ?? new HarmonyMethod(null);
- this.transpiler = transpiler ?? new HarmonyMethod(null);
- }
- public static Patches GetPatchInfo(MethodBase method)
- {
- lock (locker)
- {
- var patchInfo = HarmonySharedState.GetPatchInfo(method);
- if (patchInfo == null) return null;
- return new Patches(patchInfo.prefixes, patchInfo.postfixes, patchInfo.transpilers);
- }
- }
- public static IEnumerable<MethodBase> AllPatchedMethods()
- {
- lock (locker)
- {
- return HarmonySharedState.GetPatchedMethods();
- }
- }
- public List<DynamicMethod> Patch()
- {
- lock (locker)
- {
- var dynamicMethods = new List<DynamicMethod>();
- foreach (var original in originals)
- {
- if (original == null)
- throw new NullReferenceException("original");
- var individualPrepareResult = RunMethod<HarmonyPrepare, bool>(true, original);
- if (individualPrepareResult)
- {
- var patchInfo = HarmonySharedState.GetPatchInfo(original);
- if (patchInfo == null) patchInfo = new PatchInfo();
- PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix);
- PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix);
- PatchFunctions.AddTranspiler(patchInfo, instance.Id, transpiler);
-
- PatchHandler.Get(original).Apply();
- RunMethod<HarmonyCleanup>(original);
- }
- }
- return dynamicMethods;
- }
- }
- public void Unpatch(HarmonyPatchType type, string harmonyID)
- {
- lock (locker)
- {
- foreach (var original in originals)
- {
- var patchInfo = HarmonySharedState.GetPatchInfo(original);
- if (patchInfo == null) patchInfo = new PatchInfo();
- if (type == HarmonyPatchType.All || type == HarmonyPatchType.Prefix)
- PatchFunctions.RemovePrefix(patchInfo, harmonyID);
- if (type == HarmonyPatchType.All || type == HarmonyPatchType.Postfix)
- PatchFunctions.RemovePostfix(patchInfo, harmonyID);
- if (type == HarmonyPatchType.All || type == HarmonyPatchType.Transpiler)
- PatchFunctions.RemoveTranspiler(patchInfo, harmonyID);
-
- PatchHandler.Get(original).Apply();
- }
- }
- }
- public void Unpatch(MethodInfo patch)
- {
- lock (locker)
- {
- foreach (var original in originals)
- {
- var patchInfo = HarmonySharedState.GetPatchInfo(original);
- if (patchInfo == null) patchInfo = new PatchInfo();
- PatchFunctions.RemovePatch(patchInfo, patch);
- PatchHandler.Get(original).Apply();
- }
- }
- }
- void PrepareType()
- {
- var mainPrepareResult = RunMethod<HarmonyPrepare, bool>(true);
- if (mainPrepareResult == false)
- return;
- var customOriginals = RunMethod<HarmonyTargetMethods, IEnumerable<MethodBase>>(null);
- if (customOriginals != null)
- {
- originals = customOriginals.ToList();
- }
- else
- {
- var originalMethodType = containerAttributes.methodType;
- // MethodType default is Normal
- if (containerAttributes.methodType == null)
- containerAttributes.methodType = MethodType.Normal;
- var isPatchAll = Attribute.GetCustomAttribute(container, typeof(HarmonyPatchAll)) != null;
- if (isPatchAll)
- {
- var type = containerAttributes.declaringType;
- originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast<MethodBase>());
- originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast<MethodBase>());
- }
- else
- {
- var original = RunMethod<HarmonyTargetMethod, MethodBase>(null);
- if (original == null)
- original = GetOriginalMethod();
- if (original == null)
- {
- var info = "(";
- info += "declaringType=" + containerAttributes.declaringType + ", ";
- info += "methodName =" + containerAttributes.methodName + ", ";
- info += "methodType=" + originalMethodType + ", ";
- info += "argumentTypes=" + containerAttributes.argumentTypes.Description();
- info += ")";
- throw new ArgumentException("No target method specified for class " + container.FullName + " " + info);
- }
- originals.Add(original);
- }
- }
- PatchTools.GetPatches(container, out prefix.method, out postfix.method, out transpiler.method);
- if (prefix.method != null)
- {
- if (prefix.method.IsStatic == false)
- throw new ArgumentException("Patch method " + prefix.method.FullDescription() + " must be static");
- var prefixAttributes = prefix.method.GetHarmonyMethods();
- containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix);
- }
- if (postfix.method != null)
- {
- if (postfix.method.IsStatic == false)
- throw new ArgumentException("Patch method " + postfix.method.FullDescription() + " must be static");
- var postfixAttributes = postfix.method.GetHarmonyMethods();
- containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix);
- }
- if (transpiler.method != null)
- {
- if (transpiler.method.IsStatic == false)
- throw new ArgumentException("Patch method " + transpiler.method.FullDescription() + " must be static");
- var infixAttributes = transpiler.method.GetHarmonyMethods();
- containerAttributes.Merge(HarmonyMethod.Merge(infixAttributes)).CopyTo(transpiler);
- }
- }
- MethodBase GetOriginalMethod()
- {
- var attr = containerAttributes;
- if (attr.declaringType == null) return null;
- switch (attr.methodType)
- {
- case MethodType.Normal:
- if (attr.methodName == null)
- return null;
- return AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
- case MethodType.Getter:
- if (attr.methodName == null)
- return null;
- return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true);
- case MethodType.Setter:
- if (attr.methodName == null)
- return null;
- return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true);
- case MethodType.Constructor:
- return AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes);
- case MethodType.StaticConstructor:
- return AccessTools.GetDeclaredConstructors(attr.declaringType)
- .Where(c => c.IsStatic)
- .FirstOrDefault();
- }
- return null;
- }
- T RunMethod<S, T>(T defaultIfNotExisting, params object[] parameters)
- {
- if (container == null)
- return defaultIfNotExisting;
- var methodName = typeof(S).Name.Replace("Harmony", "");
- var paramList = new List<object> { instance };
- paramList.AddRange(parameters);
- var paramTypes = AccessTools.GetTypes(paramList.ToArray());
- var method = PatchTools.GetPatchMethod<S>(container, methodName, paramTypes);
- if (method != null && typeof(T).IsAssignableFrom(method.ReturnType))
- return (T)method.Invoke(null, paramList.ToArray());
- method = PatchTools.GetPatchMethod<S>(container, methodName, new Type[] { typeof(HarmonyInstance) });
- if (method != null && typeof(T).IsAssignableFrom(method.ReturnType))
- return (T)method.Invoke(null, new object[] { instance });
- method = PatchTools.GetPatchMethod<S>(container, methodName, Type.EmptyTypes);
- if (method != null)
- {
- if (typeof(T).IsAssignableFrom(method.ReturnType))
- return (T)method.Invoke(null, Type.EmptyTypes);
- method.Invoke(null, Type.EmptyTypes);
- return defaultIfNotExisting;
- }
- return defaultIfNotExisting;
- }
- void RunMethod<S>(params object[] parameters)
- {
- if (container == null)
- return;
- var methodName = typeof(S).Name.Replace("Harmony", "");
- var paramList = new List<object> { instance };
- paramList.AddRange(parameters);
- var paramTypes = AccessTools.GetTypes(paramList.ToArray());
- var method = PatchTools.GetPatchMethod<S>(container, methodName, paramTypes);
- if (method != null)
- {
- method.Invoke(null, paramList.ToArray());
- return;
- }
- method = PatchTools.GetPatchMethod<S>(container, methodName, new Type[] { typeof(HarmonyInstance) });
- if (method != null)
- {
- method.Invoke(null, new object[] { instance });
- return;
- }
- method = PatchTools.GetPatchMethod<S>(container, methodName, Type.EmptyTypes);
- if (method != null)
- {
- method.Invoke(null, Type.EmptyTypes);
- return;
- }
- }
- }
- }
|