Пример #1
0
        /// <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);
            }
        }
Пример #2
0
        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 _));
        }
Пример #3
0
        /// <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);
                }
            }
        }
Пример #4
0
        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());
        }
Пример #5
0
        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;
        }
Пример #6
0
        /// <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);
            }
        }
Пример #7
0
        /// <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);
            }
        }
Пример #8
0
        /// <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);
            }
        }
Пример #9
0
        /// <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);
            }
        }
Пример #10
0
        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);
            };
        }
Пример #11
0
        /// <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);
        }
Пример #12
0
        /// <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));
        }
Пример #13
0
        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;
        }
Пример #14
0
        /// <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);
            }
        }
Пример #15
0
        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);
        }
Пример #16
0
        /// <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);
            }
        }
Пример #17
0
        /// <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);
                }
            }
        }
Пример #18
0
 /// <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));
 }
Пример #19
0
 /// <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));
 }
Пример #20
0
 /// <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());
 }
Пример #21
0
 /// <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());
 }