/// <summary>Gets Harmony version for all active Harmony instances</summary> /// <param name="currentVersion">[out] The current Harmony version</param> /// <returns>A dictionary containing assembly version keyed by Harmony ID</returns> /// public static Dictionary <string, Version> VersionInfo(out Version currentVersion) { currentVersion = typeof(Harmony).Assembly.GetName().Version; var assemblies = new Dictionary <string, Assembly>(); GetAllPatchedMethods().Do(method => { PatchInfo info; lock (locker) { info = HarmonySharedState.GetPatchInfo(method); } info.prefixes.Do(fix => assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly); info.postfixes.Do(fix => assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly); info.transpilers.Do(fix => assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly); info.finalizers.Do(fix => assemblies[fix.owner] = fix.PatchMethod.DeclaringType.Assembly); }); var result = new Dictionary <string, Version>(); assemblies.Do(info => { var assemblyName = info.Value.GetReferencedAssemblies().FirstOrDefault(a => a.FullName.StartsWith("0Harmony, Version", StringComparison.Ordinal) || a.FullName.StartsWith("CitiesHarmony.Harmony, Version", StringComparison.Ordinal)); if (assemblyName is object) { result[info.Key] = assemblyName.Version; } }); return(result); }
/// <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) { 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) ?? new PatchInfo(); patchInfo.AddPrefixes(instance.Id, prefix); patchInfo.AddPostfixes(instance.Id, postfix); patchInfo.AddTranspilers(instance.Id, transpiler); patchInfo.AddFinalizers(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 is null) { patchInfo = new PatchInfo(); } 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); } _ = PatchFunctions.UpdateWrapper(original, patchInfo); HarmonySharedState.UpdatePatchInfo(original, patchInfo); return(this); } }
/// <summary>Gets all patched original methods in the appdomain</summary> /// <returns>An enumeration of patched method/constructor</returns> /// public static IEnumerable <MethodBase> GetAllPatchedMethods() { lock (locker) { return(HarmonySharedState.GetPatchedMethods()); } }
/// <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 PatchProcessor 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); } } return(this); }
/// <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); } }
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, patchInfo); } catch (Exception ex) { exception = ex; } } } RunMethod <HarmonyCleanup>(ref exception, job.original, exception); ReportException(exception, job.original); job.replacement = replacement; }
/// <summary>Tries to get the method from a stackframe including dynamic replacement methods</summary> /// <param name="frame">The <see cref="StackFrame"/></param> /// <returns>For normal frames, <c>frame.GetMethod()</c> is returned. For frames containing patched methods, the replacement method is returned or <c>null</c> if no method can be found</returns> /// public static MethodBase GetMethodFromStackframe(StackFrame frame) { if (frame == null) { throw new ArgumentNullException(nameof(frame)); } return(HarmonySharedState.FindReplacement(frame) ?? frame.GetMethod()); }
/// <summary>Gets the original method from a given replacement method</summary> /// <param name="replacement">A replacement method, for example from a stacktrace</param> /// <returns>The original method/constructor or <c>null</c> if not found</returns> /// public static MethodBase GetOriginalMethod(MethodInfo replacement) { if (replacement == null) { throw new ArgumentNullException(nameof(replacement)); } return(HarmonySharedState.GetOriginal(replacement)); }
/// <summary>Gets patch information on an original</summary> /// <param name="method">The original method/constructor</param> /// <returns>The patch information as <see cref="Patches"/></returns> /// public static Patches GetPatchInfo(MethodBase method) { PatchInfo patchInfo; lock (locker) { patchInfo = HarmonySharedState.GetPatchInfo(method); } if (patchInfo == null) { return(null); } return(new Patches(patchInfo.prefixes, patchInfo.postfixes, patchInfo.transpilers, patchInfo.finalizers)); }
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 = 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); } } }