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(); } } } }
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 void TestMethod2() { var originalClass = typeof(Class2); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method2"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class2Patch); var prefix = patchClass.GetMethod("Prefix"); var postfix = patchClass.GetMethod("Postfix"); var transpiler = patchClass.GetMethod("Transpiler"); Assert.IsNotNull(prefix); Assert.IsNotNull(postfix); Assert.IsNotNull(transpiler); Class2Patch.ResetTest(); var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = new PatchProcessor(instance, originalMethod); Assert.IsNotNull(patcher); patcher.AddPrefix(prefix); patcher.AddPostfix(postfix); patcher.AddTranspiler(transpiler); patcher.Patch(); // unsafe // { // var patchedCode = *(byte*) originalMethodStartPre; // if (IntPtr.Size == sizeof(long)) // Assert.IsTrue(patchedCode == 0x48); // else // Assert.IsTrue(patchedCode == 0x68); // } new Class2().Method2(); Assert.IsTrue(Class2Patch.prefixed, "Prefix was not executed"); Assert.IsTrue(Class2Patch.originalExecuted, "Original was not executed"); Assert.IsTrue(Class2Patch.postfixed, "Postfix was not executed"); }
public void TestNativePatch() { // Currently NativeDetours don't work properly on .NET Core (except when running in debug mode) // ¯\_(ツ)_/¯ #if NETCOREAPP3_0 return; #endif var originalClass = typeof(NativeClass); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Rand"); Assert.IsNotNull(originalMethod); var patchClass = typeof(NativeClassPatch); var prefix = patchClass.GetMethod("Prefix"); Assert.IsNotNull(prefix); var postfix = patchClass.GetMethod("Postfix"); Assert.IsNotNull(postfix); var transpiler = patchClass.GetMethod("Transpiler"); Assert.IsNotNull(transpiler); var instance = new Harmony("test-native"); Assert.IsNotNull(instance); var patcher = new PatchProcessor(instance, originalMethod); Assert.IsNotNull(patcher); patcher.AddPrefix(prefix); patcher.AddPostfix(postfix); patcher.AddTranspiler(transpiler); patcher.Patch(); var result = NativeClass.Rand(); Assert.AreEqual(-1, result); Assert.IsTrue(NativeClassPatch.prefixCalled, "Prefix wasn't run"); Assert.IsTrue(NativeClassPatch.postfixCalled, "Postfix wasn't run"); Assert.IsTrue(NativeClassPatch.transpilerCalled, "Transpiler wasn't run"); }
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 transpiler to the original method /// </summary> /// <param name="action">Transpiler to apply</param> /// <typeparam name="T">Transpiler delegate type</typeparam> /// <returns>Current patch processor</returns> public PatchComposer Transpiler <T>(T action) where T : Delegate { processor.AddTranspiler(GetPatchMethod(action)); return(this); }
public ProcessorInfo AddTranspiler(HarmonyMethod transpiler) { processor.AddTranspiler(transpiler); return(this); }