using Harmony.ILCopying; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace Harmony { public static class MethodPatcher { // special parameter names that can be used in prefix and postfix methods // public static string INSTANCE_PARAM = "__instance"; public static string ORIGINAL_METHOD_PARAM = "__originalMethod"; public static string RESULT_VAR = "__result"; public static string STATE_VAR = "__state"; public static string PARAM_INDEX_PREFIX = "__"; public static string INSTANCE_FIELD_PREFIX = "___"; // in case of trouble, set to true to write dynamic method to desktop as a dll // won't work for all methods because of the inability to extend a type compared // to the way DynamicTools.CreateDynamicMethod works // static readonly bool DEBUG_METHOD_GENERATION_BY_DLL_CREATION = false; // for fixing old harmony bugs [UpgradeToLatestVersion(1)] public static DynamicMethod CreatePatchedMethod(MethodBase original, List prefixes, List postfixes, List transpilers) { return CreatePatchedMethod(original, "HARMONY_PATCH_1.1.1", prefixes, postfixes, transpilers); } public static DynamicMethod CreatePatchedMethod(MethodBase original, string harmonyInstanceID, List prefixes, List postfixes, List transpilers) { try { if (HarmonyInstance.DEBUG) FileLog.LogBuffered("### Patch " + original.DeclaringType + ", " + original); var idx = prefixes.Count() + postfixes.Count(); var patch = DynamicTools.CreateDynamicMethod(original, "_Patch" + idx); if (patch == null) return null; var il = patch.GetILGenerator(); // for debugging AssemblyBuilder assemblyBuilder = null; TypeBuilder typeBuilder = null; if (DEBUG_METHOD_GENERATION_BY_DLL_CREATION) il = DynamicTools.CreateSaveableMethod(original, "_Patch" + idx, out assemblyBuilder, out typeBuilder); var originalVariables = DynamicTools.DeclareLocalVariables(original, il); var privateVars = new Dictionary(); LocalBuilder resultVariable = null; if (idx > 0) { resultVariable = DynamicTools.DeclareLocalVariable(il, AccessTools.GetReturnedType(original)); privateVars[RESULT_VAR] = resultVariable; } prefixes.ForEach(prefix => { prefix.GetParameters() .Where(patchParam => patchParam.Name == STATE_VAR) .Do(patchParam => { var privateStateVariable = DynamicTools.DeclareLocalVariable(il, patchParam.ParameterType); privateVars[prefix.DeclaringType.FullName] = privateStateVariable; }); }); var skipOriginalLabel = il.DefineLabel(); var canHaveJump = AddPrefixes(il, original, prefixes, privateVars, skipOriginalLabel); var copier = new MethodCopier(original, il, originalVariables); foreach (var transpiler in transpilers) copier.AddTranspiler(transpiler); var endLabels = new List