static Main() { var harmony = new Harmony("Garthor.More_Traits"); harmony.PatchAll(); // Get all types in the Compatibility namespace var types = from x in Assembly.GetExecutingAssembly().GetTypes() where x.IsClass && x.Namespace == "Garthor_More_Traits.Compatibility" select x; // Iterate through types tagged with the PatchIfMod attribute foreach (var t in types) { var attr = t.GetCustomAttribute <Compatibility.PatchIfModAttribute>(); if (attr != null && attr.IsModLoaded()) { Dictionary <MethodBase, PatchProcessor> patches = new Dictionary <MethodBase, PatchProcessor>(); foreach (var method in t.GetMethods()) { foreach (var harmonyPatch in method.GetCustomAttributes <HarmonyPatch>()) { // First add a PatchProcessor for the method we're targeting to the patches list, if not already present MethodBase mb = harmonyPatch.info.declaringType.GetMethod(harmonyPatch.info.methodName); if (!patches.ContainsKey(mb)) { patches.Add(mb, new PatchProcessor(harmony, mb)); } PatchProcessor patch = patches[mb]; // Next add this method to the prefix, postfix, transpiler, or finalizer lists, as appropriate // (bit repetitive, but it's short and would be a bit convoluted to improve it) if (method.GetCustomAttributes <HarmonyPrefix>().Any()) { patch.AddPrefix(new HarmonyMethod(method)); } if (method.GetCustomAttributes <HarmonyPostfix>().Any()) { patch.AddPostfix(new HarmonyMethod(method)); } if (method.GetCustomAttributes <HarmonyTranspiler>().Any()) { patch.AddTranspiler(new HarmonyMethod(method)); } if (method.GetCustomAttributes <HarmonyFinalizer>().Any()) { patch.AddFinalizer(new HarmonyMethod(method)); } } } // Apply the patches for this mod foreach (PatchProcessor processor in patches.Values) { processor.Patch(); } } } }
public void SetUp() { var testMethod = TestContext.CurrentContext.Test.Name; var parts = testMethod.Split('_'); var originalType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[1]); var patchType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[2]); Assert.IsNotNull(originalType); var originalMethod = originalType.GetMethod("Method"); Assert.IsNotNull(originalMethod); var finalizer = patchType.GetMethod("Finalizer"); Assert.IsNotNull(finalizer); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = new PatchProcessor(instance, originalMethod); Assert.IsNotNull(patcher); patcher.AddFinalizer(finalizer); patcher.Patch(); var trv = Traverse.Create(patchType); trv.Field("finalized").SetValue(false); trv.Field("exception").SetValue(new NullReferenceException("replace-me")); var obj = Activator.CreateInstance(originalType); var m_method = AccessTools.Method(originalType, "Method"); info = new Dictionary <string, object>(); try { if (m_method.ReturnType == typeof(void)) { m_method.Invoke(obj, null); } else { info["result"] = m_method.Invoke(obj, null); } info["outerexception"] = null; } catch (TargetInvocationException e) { info["outerexception"] = e.InnerException; } trv.Fields().ForEach(name => info[name] = trv.Field(name).GetValue()); instance.UnpatchAll(); Assert.IsTrue((bool)info["finalized"], "Finalizer not called"); }
static HarmonyLoader() { // load directly embedded patches var harmony = new Harmony("harmony-loader-" + Guid.NewGuid().ToString("N")); var assembly = Assembly.GetExecutingAssembly(); foreach (var type in assembly.GetTypes()) { foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { var harmonyMethods = HarmonyMethodExtensions.GetFromMethod(method); if (harmonyMethods is null || !harmonyMethods.Any()) { continue; } var merged = new HarmonyMethod() { methodType = MethodType.Normal }.Merge(HarmonyMethod.Merge(harmonyMethods)); Logger.LogDebug($"found method: {method} / {merged}"); #if BepInEx var proc = new PatchProcessor(harmony); proc.AddOriginal(PatchProcessor.GetOriginalMethod(merged)); #elif UMM var proc = new PatchProcessor(harmony, merged.GetOriginalMethod()); #endif if (method.GetCustomAttributes <HarmonyTranspiler>().Any()) { proc.AddTranspiler(method); } if (method.GetCustomAttributes <HarmonyPrefix>().Any()) { proc.AddPrefix(method); } if (method.GetCustomAttributes <HarmonyPostfix>().Any()) { proc.AddPostfix(method); } if (method.GetCustomAttributes <HarmonyFinalizer>().Any()) { proc.AddFinalizer(method); } proc.Patch(); } } }
static HarmonyLoader() { // load directly embedded patches var harmony = new Harmony("harmony-loader-" + Guid.NewGuid().ToString("N")); var assembly = Assembly.GetExecutingAssembly(); foreach (var type in assembly.GetTypes()) { foreach (var method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { var harmonyMethods = HarmonyMethodExtensions.GetFromMethod(method); if (harmonyMethods is null || !harmonyMethods.Any()) { continue; } var merged = new HarmonyMethod() { methodType = MethodType.Normal }.Merge(HarmonyMethod.Merge(harmonyMethods)); Logger.LogDebug($"found method: {method} / {merged}"); var proc = new PatchProcessor(harmony); proc.AddOriginal(PatchProcessor.GetOriginalMethod(merged) /*(MethodBase) Info.OfMethod<PatchProcessor>("GetOriginalMethod").Invoke(null, new[] {merged})*/); if (method.GetCustomAttributes <HarmonyTranspiler>().Any()) { proc.AddTranspiler(method); } if (method.GetCustomAttributes <HarmonyPrefix>().Any()) { proc.AddPrefix(method); } if (method.GetCustomAttributes <HarmonyPostfix>().Any()) { proc.AddPostfix(method); } if (method.GetCustomAttributes <HarmonyFinalizer>().Any()) { proc.AddFinalizer(method); } proc.Patch(); } } // load the normal style of patches Harmony.CreateAndPatchAll(typeof(AutoLoadManager).Assembly); }
public bool CompileAndGenerateProcessor(string patchSource) { Unpatch(); try { patchProcessor = ExplorerCore.Harmony.CreateProcessor(TargetMethod); // Dynamically compile the patch method var codeBuilder = new StringBuilder(); codeBuilder.AppendLine($"public class DynamicPatch_{DateTime.Now.Ticks}"); codeBuilder.AppendLine("{"); codeBuilder.AppendLine(patchSource); codeBuilder.AppendLine("}"); scriptEvaluator.Run(codeBuilder.ToString()); if (ScriptEvaluator._reportPrinter.ErrorsCount > 0) { throw new FormatException($"Unable to compile the generated patch!"); } // TODO: Publicize MCS to avoid this reflection // Get the most recent Patch type in the source file var typeContainer = ((CompilationSourceFile)fi_sourceFile.GetValue(scriptEvaluator)) .Containers .Last(it => it.MemberName.Name.StartsWith("DynamicPatch_")); // Get the TypeSpec from the TypeDefinition, then get its "MetaInfo" (System.Type) var patchClass = ((TypeSpec)pi_Definition.GetValue((Class)typeContainer, null)).GetMetaInfo(); // Create the harmony patches as defined postfix = patchClass.GetMethod("Postfix", ReflectionUtility.FLAGS); if (postfix != null) { patchProcessor.AddPostfix(new HarmonyMethod(postfix)); } prefix = patchClass.GetMethod("Prefix", ReflectionUtility.FLAGS); if (prefix != null) { patchProcessor.AddPrefix(new HarmonyMethod(prefix)); } finalizer = patchClass.GetMethod("Finalizer", ReflectionUtility.FLAGS); if (finalizer != null) { patchProcessor.AddFinalizer(new HarmonyMethod(finalizer)); } transpiler = patchClass.GetMethod("Transpiler", ReflectionUtility.FLAGS); if (transpiler != null) { patchProcessor.AddTranspiler(new HarmonyMethod(transpiler)); } return(true); } catch (Exception ex) { ExplorerCore.LogWarning($"Exception creating patch processor for target method {TargetMethod.FullDescription()}!\r\n{ex}"); return(false); } }
/// <summary> /// Apply a finalizer to the original method /// </summary> /// <param name="action">Finalizer to apply</param> /// <typeparam name="T">Finalizer delegate type</typeparam> /// <returns>Current patch processor</returns> public PatchComposer Finalizer <T>(T action) where T : Delegate { processor.AddFinalizer(GetPatchMethod(action)); return(this); }