/// <summary>Applies the patch</summary> /// <returns>A list of all created dynamic methods</returns> /// public List <DynamicMethod> Patch() { lock (locker) { var dynamicMethods = new List <DynamicMethod>(); foreach (var original in originals) { if (original == null) { throw new NullReferenceException("Null method for " + instance.Id); } 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); PatchFunctions.AddFinalizer(patchInfo, instance.Id, finalizer); dynamicMethods.Add(PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id)); HarmonySharedState.UpdatePatchInfo(original, patchInfo); RunMethod <HarmonyCleanup>(original); } } return(dynamicMethods); } }
internal static List <CodeInstruction> GetInstructions(ILGenerator generator, MethodBase method, int maxTranspilers) { if (generator == null) { throw new ArgumentNullException(nameof(generator)); } if (method == null) { throw new ArgumentNullException(nameof(method)); } var originalVariables = MethodPatcher.DeclareLocalVariables(generator, method); var useStructReturnBuffer = StructReturnBuffer.NeedsFix(method); var copier = new MethodCopier(method, generator, originalVariables); copier.SetArgumentShift(useStructReturnBuffer); var info = Harmony.GetPatchInfo(method); if (info != null) { var sortedTranspilers = PatchFunctions.GetSortedPatchMethods(method, info.Transpilers.ToArray(), false); for (var i = 0; i < maxTranspilers && i < sortedTranspilers.Count; i++) { copier.AddTranspiler(sortedTranspilers[i]); } } return(copier.Finalize(null, null, out var _)); }
/// <summary>Unpatches patches of a given type and/or Harmony ID</summary> /// <param name="type">The patch type</param> /// <param name="harmonyID">Harmony ID or (*) for any</param> /// 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); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.Finalizer) { PatchFunctions.RemoveFinalizer(patchInfo, harmonyID); } PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id); HarmonySharedState.UpdatePatchInfo(original, patchInfo); } } }
internal static List <CodeInstruction> GetInstructions(ILGenerator generator, MethodBase method, int maxTranspilers) { if (generator == null) { throw new ArgumentNullException(nameof(generator)); } if (method == null) { throw new ArgumentNullException(nameof(method)); } var originalVariables = MethodPatcher.DeclareLocalVariables(generator, method); var useStructReturnBuffer = StructReturnBuffer.NeedsFix(method); var copier = new MethodCopier(method, generator, originalVariables); copier.SetArgumentShift(useStructReturnBuffer); var info = Harmony.GetPatchInfo(method); if (info != null) { var sortedTranspilers = PatchFunctions.GetSortedPatchMethods(method, info.Transpilers.ToArray(), false); for (var i = 0; i < maxTranspilers && i < sortedTranspilers.Count; i++) { copier.AddTranspiler(sortedTranspilers[i]); } } var endLabels = new List <Label>(); var emitter = new Emitter(generator, false); copier.Finalize(emitter, endLabels, out var hasReturnCode); return(emitter.GetInstructions().OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList()); }
void ProcessPatchJob(PatchJobs <MethodInfo> .Job job) { MethodInfo replacement = default; var individualPrepareResult = RunMethod <HarmonyPrepare, bool>(true, false, null, job.original); Exception exception = null; if (individualPrepareResult) { lock (PatchProcessor.locker) { try { var patchInfo = HarmonySharedState.GetPatchInfo(job.original) ?? new PatchInfo(); patchInfo.AddPrefixes(instance.Id, job.prefixes.ToArray()); patchInfo.AddPostfixes(instance.Id, job.postfixes.ToArray()); patchInfo.AddTranspilers(instance.Id, job.transpilers.ToArray()); patchInfo.AddFinalizers(instance.Id, job.finalizers.ToArray()); replacement = PatchFunctions.UpdateWrapper(job.original, patchInfo); HarmonySharedState.UpdatePatchInfo(job.original, replacement, patchInfo); } catch (Exception ex) { exception = ex; } } } RunMethod <HarmonyCleanup>(ref exception, job.original, exception); ReportException(exception, job.original); job.replacement = replacement; }
/// <summary>Applies all registered patches</summary> /// <returns>The generated replacement method</returns> /// public MethodInfo Patch() { if (original == null) { throw new NullReferenceException($"Null method for {instance.Id}"); } if (original.IsDeclaredMember() == false) { var declaredMember = original.GetDeclaredMember(); throw new ArgumentException($"You can only patch implemented methods/constructors. Path the declared method {declaredMember.FullDescription()} instead."); } lock (locker) { 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); PatchFunctions.AddFinalizer(patchInfo, instance.Id, finalizer); var replacement = PatchFunctions.UpdateWrapper(original, patchInfo); HarmonySharedState.UpdatePatchInfo(original, patchInfo); return(replacement); } }
/// <summary>Unpatches patches of a given type and/or Harmony ID</summary> /// <param name="type">The <see cref="HarmonyPatchType"/> patch type</param> /// <param name="harmonyID">Harmony ID or <c>*</c> for any</param> /// <returns>A <see cref="PatchProcessor"/> for chaining calls</returns> /// public PatchProcessor Unpatch(HarmonyPatchType type, string harmonyID) { lock (locker) { 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); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.Finalizer) { PatchFunctions.RemoveFinalizer(patchInfo, harmonyID); } _ = PatchFunctions.UpdateWrapper(original, patchInfo); HarmonySharedState.UpdatePatchInfo(original, patchInfo); return(this); } }
/// <summary>Applies all registered patches</summary> /// <returns>The generated replacement method</returns> /// public MethodInfo Patch() { if (original is null) { throw new NullReferenceException($"Null method for {instance.Id}"); } if (original.IsDeclaredMember() is false) { Logger.Log(Logger.LogChannel.Warn, () => $"{instance.Id}: You should only patch implemented methods/constructors to avoid issues. Patch the declared method {original.GetDeclaredMember().FullDescription()} instead of {original.FullDescription()}."); } lock (locker) { var patchInfo = original.ToPatchInfo(); patchInfo.AddPrefixes(instance.Id, prefix); patchInfo.AddPostfixes(instance.Id, postfix); patchInfo.AddTranspilers(instance.Id, transpiler); patchInfo.AddFinalizers(instance.Id, finalizer); patchInfo.AddILManipulators(instance.Id, ilmanipulator); var replacement = PatchFunctions.UpdateWrapper(original, patchInfo); PatchManager.AddReplacementOriginal(original, replacement); return(replacement); } }
/// <summary>Unpatches patches of a given type and/or Harmony ID</summary> /// <param name="type">The <see cref="HarmonyPatchType"/> patch type</param> /// <param name="harmonyID">Harmony ID or <c>*</c> for any</param> /// <returns>A <see cref="PatchProcessor"/> for chaining calls</returns> /// public PatchProcessor Unpatch(HarmonyPatchType type, string harmonyID) { lock (locker) { var patchInfo = original.ToPatchInfo(); if (type == HarmonyPatchType.All || type == HarmonyPatchType.Prefix) { patchInfo.RemovePrefix(harmonyID); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.Postfix) { patchInfo.RemovePostfix(harmonyID); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.Transpiler) { patchInfo.RemoveTranspiler(harmonyID); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.Finalizer) { patchInfo.RemoveFinalizer(harmonyID); } if (type == HarmonyPatchType.All || type == HarmonyPatchType.ILManipulator) { patchInfo.RemoveILManipulator(harmonyID); } var replacement = PatchFunctions.UpdateWrapper(original, patchInfo); PatchManager.AddReplacementOriginal(original, replacement); return(this); } }
static HarmonySharedState() { // create singleton type var type = GetOrCreateSharedStateType(); // copy 'actualVersion' over to our fields var versionField = type.GetField("version"); if ((int)versionField.GetValue(null) == 0) { versionField.SetValue(null, internalVersion); } actualVersion = (int)versionField.GetValue(null); // get or initialize global 'state' field var stateField = type.GetField("state"); if (stateField.GetValue(null) is null) { stateField.SetValue(null, new Dictionary <MethodBase, byte[]>()); } // get or initialize global 'originals' field var originalsField = type.GetField("originals"); if (originalsField != null && originalsField.GetValue(null) is null) { originalsField.SetValue(null, new Dictionary <MethodInfo, MethodBase>()); } // copy 'state' over to our fields state = (Dictionary <MethodBase, byte[]>)stateField.GetValue(null); // copy 'originals' over to our fields originals = new Dictionary <MethodInfo, MethodBase>(); if (originalsField != null) // may not exist in older versions { originals = (Dictionary <MethodInfo, MethodBase>)originalsField.GetValue(null); } // newer .NET versions can re-jit methods so we need to patch them after that happens DetourHelper.Runtime.OnMethodCompiled += (MethodBase method, IntPtr codeStart, ulong codeLen) => { if (method == null) { return; } var info = GetPatchInfo(method); if (info == null) { return; } PatchFunctions.UpdateRecompiledMethod(method, codeStart, info); }; }
/// <summary>Applies the patch</summary> /// public void Patch(HarmonyReversePatchType type = HarmonyReversePatchType.Original) { if (original == null) { throw new NullReferenceException("Null method for " + instance.Id); } var transpiler = GetTranspiler(standin); PatchFunctions.ReversePatch(standin, original, instance.Id, transpiler); }
/// <summary>Applies the patch</summary> /// <param name="type">The type of patch, see <see cref="HarmonyReversePatchType"/></param> /// <returns>The generated replacement method</returns> /// public MethodInfo Patch(HarmonyReversePatchType type = HarmonyReversePatchType.Original) { if (original == null) { throw new NullReferenceException($"Null method for {instance.Id}"); } var transpiler = GetTranspiler(standin.method); return(PatchFunctions.ReversePatch(standin, original, transpiler)); }
void ProcessPatchJob(PatchJobs <MethodInfo> .Job job) { MethodInfo replacement = default; var individualPrepareResult = RunMethod <HarmonyPrepare, bool>(true, false, null, job.original); Exception exception = null; if (individualPrepareResult) { lock (PatchProcessor.locker) { try { var patchInfo = HarmonySharedState.GetPatchInfo(job.original); if (patchInfo == null) { patchInfo = new PatchInfo(); } foreach (var prefix in job.prefixes) { PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix); } foreach (var postfix in job.postfixes) { PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix); } foreach (var transpiler in job.transpilers) { PatchFunctions.AddTranspiler(patchInfo, instance.Id, transpiler); } foreach (var finalizer in job.finalizers) { PatchFunctions.AddFinalizer(patchInfo, instance.Id, finalizer); } replacement = PatchFunctions.UpdateWrapper(job.original, patchInfo); HarmonySharedState.UpdatePatchInfo(job.original, patchInfo); } catch (Exception ex) { exception = ex; } } } RunMethod <HarmonyCleanup>(ref exception, job.original, exception); if (exception != null) { ReportException(exception, job.original); } job.replacement = replacement; }
/// <summary>Unpatches a specific patch</summary> /// <param name="patch">The method of the patch</param> /// <returns>A <see cref="PatchProcessor"/> for chaining calls</returns> /// public PatchProcessor Unpatch(MethodInfo patch) { lock (locker) { var patchInfo = original.ToPatchInfo(); patchInfo.RemovePatch(patch); var replacement = PatchFunctions.UpdateWrapper(original, patchInfo); PatchManager.AddReplacementOriginal(original, replacement); return(this); } }
static void OnCompileMethod(MethodBase method, IntPtr codeStart, ulong codeLen) { if (method == null) { return; } var info = GetPatchInfo(method); if (info == null) { return; } PatchFunctions.UpdateRecompiledMethod(method, codeStart, info); }
/// <summary>Unpatches a specific patch</summary> /// <param name="patch">The method of the patch</param> /// <returns>A <see cref="PatchProcessor"/> for chaining calls</returns> /// public PatchProcessor Unpatch(MethodInfo patch) { lock (locker) { var patchInfo = HarmonySharedState.GetPatchInfo(original); if (patchInfo == null) { patchInfo = new PatchInfo(); } PatchFunctions.RemovePatch(patchInfo, patch); _ = PatchFunctions.UpdateWrapper(original, patchInfo); HarmonySharedState.UpdatePatchInfo(original, patchInfo); return(this); } }
/// <summary>Unpatches the given patch</summary> /// <param name="patch">The patch</param> /// 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); PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id); HarmonySharedState.UpdatePatchInfo(original, patchInfo); } } }
/// <summary>Patches a foreign method onto a stub method of yours and optionally applies transpilers during the process</summary> /// <param name="original">The original method/constructor you want to duplicate</param> /// <param name="standin">Your stub method as <see cref="HarmonyMethod"/> that will become the original. Needs to have the correct signature (either original or whatever your transpilers generates)</param> /// <param name="transpiler">An optional transpiler as method that will be applied during the process</param> /// <returns>The replacement method that was created to patch the stub method</returns> /// public static MethodInfo ReversePatch(MethodBase original, HarmonyMethod standin, MethodInfo transpiler = null) { return(PatchFunctions.ReversePatch(standin, original, transpiler)); }
/// <summary>Sort patch methods by their priority rules</summary> /// <param name="original">The original method</param> /// <param name="patches">Patches to sort</param> /// <returns>The sorted patch methods</returns> /// public static List <MethodInfo> GetSortedPatchMethods(MethodBase original, Patch[] patches) { return(PatchFunctions.GetSortedPatchMethods(original, patches, false)); }
/// <summary>Returns the methods current list of code instructions after all existing transpilers have been applied</summary> /// <param name="original">The original method/constructor</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <param name="maxTranspilers">Apply only the first count of transpilers</param> /// <returns>A list of <see cref="CodeInstruction"/></returns> /// public static List <CodeInstruction> GetCurrentInstructions(MethodBase original, out ILGenerator generator, int maxTranspilers = int.MaxValue) { generator = CreateILGenerator(original); return(PatchFunctions.ApplyTranspilers(original, generator, maxTranspilers).ToList()); }
/// <summary>Returns the methods unmodified list of code instructions</summary> /// <param name="original">The original method/constructor</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original <see cref="CodeInstruction"/></returns> /// public static List <CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { generator = CreateILGenerator(original); return(PatchFunctions.ApplyTranspilers(original, generator).ToList()); }