T RunMethod <S, T>(T defaultIfNotExisting, params object[] parameters) { var methodName = typeof(S).Name.Replace("Harmony", ""); var paramList = new List <object> { instance }; paramList.AddRange(parameters); var paramTypes = AccessTools.GetTypes(paramList.ToArray()); var method = PatchTools.GetPatchMethod <S>(container, methodName, paramTypes); if (method != null && typeof(T).IsAssignableFrom(method.ReturnType)) { return((T)method.Invoke(null, paramList.ToArray())); } method = PatchTools.GetPatchMethod <S>(container, methodName, new Type[] { typeof(HarmonyInstance) }); if (method != null && typeof(T).IsAssignableFrom(method.ReturnType)) { return((T)method.Invoke(null, new object[] { instance })); } method = PatchTools.GetPatchMethod <S>(container, methodName, Type.EmptyTypes); if (method != null) { if (typeof(T).IsAssignableFrom(method.ReturnType)) { return((T)method.Invoke(null, Type.EmptyTypes)); } method.Invoke(null, Type.EmptyTypes); return(defaultIfNotExisting); } return(defaultIfNotExisting); }
void RunMethod <S>(params object[] parameters) { if (container == null) { return; } var methodName = typeof(S).Name.Replace("Harmony", ""); var paramList = new List <object> { instance }; paramList.AddRange(parameters); var paramTypes = AccessTools.GetTypes(paramList.ToArray()); var method = PatchTools.GetPatchMethod <S>(container, methodName, paramTypes); if (method != null) { method.Invoke(null, paramList.ToArray()); return; } method = PatchTools.GetPatchMethod <S>(container, methodName, new Type[] { typeof(HarmonyInstance) }); if (method != null) { method.Invoke(null, new object[] { instance }); return; } method = PatchTools.GetPatchMethod <S>(container, methodName, Type.EmptyTypes); if (method != null) { method.Invoke(null, Type.EmptyTypes); return; } }
/// <summary>Creates new dynamic method with the latest patches and detours the original method</summary> /// <param name="original">The original method</param> /// <param name="patchInfo">Information describing the patches</param> /// <param name="instanceID">Harmony ID</param> /// <returns>The newly created dynamic method</returns> /// internal static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID) { var sortedPrefixes = GetSortedPatchMethods(original, patchInfo.prefixes); var sortedPostfixes = GetSortedPatchMethods(original, patchInfo.postfixes); var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers); var replacement = MethodPatcher.CreatePatchedMethod(original, instanceID, sortedPrefixes, sortedPostfixes, sortedTranspilers); if (replacement == null) { throw new MissingMethodException("Cannot create dynamic replacement for " + original.FullDescription()); } var errorString = Memory.DetourMethod(original, replacement); if (errorString != null) { throw new FormatException("Method " + original.FullDescription() + " cannot be patched. Reason: " + errorString); } PatchTools.RememberObject(original, replacement); // no gc for new value + release old value to gc return(replacement); }
void PrepareType() { var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true); if (mainPrepareResult == false) { return; } var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null); if (customOriginals != null) { originals = customOriginals.ToList(); } else { var isPatchAll = Attribute.GetCustomAttribute(container, typeof(HarmonyPatchAll)) != null; if (isPatchAll) { var type = containerAttributes.declaringType; originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); } else { var original = GetOriginalMethod(); if (original == null) { original = RunMethod <HarmonyTargetMethod, MethodBase>(null); } if (original != null) { originals.Add(original); } else { throw new ArgumentException("No target method specified for class " + container.FullName); } } } PatchTools.GetPatches(container, out prefix.method, out postfix.method, out transpiler.method); if (prefix.method != null) { if (prefix.method.IsStatic == false) { throw new ArgumentException("Patch method " + prefix.method.FullDescription() + " must be static"); } var prefixAttributes = prefix.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix); } if (postfix.method != null) { if (postfix.method.IsStatic == false) { throw new ArgumentException("Patch method " + postfix.method.FullDescription() + " must be static"); } var postfixAttributes = postfix.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix); } if (transpiler.method != null) { if (transpiler.method.IsStatic == false) { throw new ArgumentException("Patch method " + transpiler.method.FullDescription() + " must be static"); } var infixAttributes = transpiler.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(infixAttributes)).CopyTo(transpiler); } }
void PrepareType() { var mainPrepareResult = RunMethod <HarmonyPrepare, bool>(true); if (mainPrepareResult == false) { return; } var customOriginals = RunMethod <HarmonyTargetMethods, IEnumerable <MethodBase> >(null); if (customOriginals != null) { originals = customOriginals.ToList(); } else { var originalMethodType = containerAttributes.methodType; // MethodType default is Normal if (containerAttributes.methodType == null) { containerAttributes.methodType = MethodType.Normal; } var isPatchAll = Attribute.GetCustomAttribute(container, typeof(HarmonyPatchAll)) != null; if (isPatchAll) { var type = containerAttributes.declaringType; originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast <MethodBase>()); originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast <MethodBase>()); var props = AccessTools.GetDeclaredProperties(type); originals.AddRange(props.Select(prop => prop.GetGetMethod(true)).Where(method => method != null).Cast <MethodBase>()); originals.AddRange(props.Select(prop => prop.GetSetMethod(true)).Where(method => method != null).Cast <MethodBase>()); } else { var original = RunMethod <HarmonyTargetMethod, MethodBase>(null); if (original == null) { original = GetOriginalMethod(); } if (original == null) { var info = "("; info += "declaringType=" + containerAttributes.declaringType + ", "; info += "methodName =" + containerAttributes.methodName + ", "; info += "methodType=" + originalMethodType + ", "; info += "argumentTypes=" + containerAttributes.argumentTypes.Description(); info += ")"; throw new ArgumentException("No target method specified for class " + container.FullName + " " + info); } originals.Add(original); } } PatchTools.GetPatches(container, out prefix.method, out postfix.method, out transpiler.method); if (prefix.method != null) { if (prefix.method.IsStatic == false) { throw new ArgumentException("Patch method " + prefix.method.FullDescription() + " must be static"); } var prefixAttributes = prefix.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix); } if (postfix.method != null) { if (postfix.method.IsStatic == false) { throw new ArgumentException("Patch method " + postfix.method.FullDescription() + " must be static"); } var postfixAttributes = postfix.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix); } if (transpiler.method != null) { if (transpiler.method.IsStatic == false) { throw new ArgumentException("Patch method " + transpiler.method.FullDescription() + " must be static"); } var infixAttributes = transpiler.method.GetHarmonyMethods(); containerAttributes.Merge(HarmonyMethod.Merge(infixAttributes)).CopyTo(transpiler); } }