public void MethodRestorationTest() { var originalMethod = typeof(RestoreableClass).GetMethod("Method2"); Assert.IsNotNull(originalMethod); MethodInfo prefixMethod; MethodInfo postfixMethod; MethodInfo transpilerMethod; PatchTools.GetPatches(typeof(Class2Patch), out prefixMethod, out postfixMethod, out transpilerMethod); var instance = HarmonyInstance.Create("test"); var patcher = new PatchProcessor(instance, new List <MethodBase>() { originalMethod }, new HarmonyMethod(prefixMethod), new HarmonyMethod(postfixMethod), null); // Check if the class is clean before using it for patching Assert.AreEqual(null, instance.IsPatched(originalMethod), "Class already patched!"); Exception ex; var start = Memory.GetMethodStart(originalMethod, out ex); var oldBytes = new byte[12]; if (IntPtr.Size == sizeof(long)) { Marshal.Copy((IntPtr)start, oldBytes, 0, 12); } else { Marshal.Copy((IntPtr)start, oldBytes, 0, 6); } patcher.Patch(); patcher.Unpatch(originalMethod); var newBytes = new byte[12]; if (IntPtr.Size == sizeof(long)) { Marshal.Copy((IntPtr)start, newBytes, 0, 12); } else { Marshal.Copy((IntPtr)start, newBytes, 0, 6); } for (int i = 0; i < oldBytes.Length; i++) { Assert.AreEqual(oldBytes[i], newBytes[i], string.Format("Byte {0} differs after restoration", i)); } Class2Patch._reset(); new RestoreableClass().Method2(); Assert.IsFalse(Class2Patch.prefixed); Assert.IsTrue(Class2Patch.originalExecuted); Assert.IsFalse(Class2Patch.postfixed); Assert.AreEqual(0, instance.IsPatched(originalMethod).Postfixes.Count); Assert.AreEqual(0, instance.IsPatched(originalMethod).Prefixes.Count); Assert.AreEqual(0, instance.IsPatched(originalMethod).Transpilers.Count); }
/// <summary> /// Applies all patches specified in the type. /// </summary> /// <param name="type">The type to scan.</param> /// <param name="harmonyInstance">The HarmonyInstance to use.</param> public static HarmonyLib.Harmony PatchAll(Type type, HarmonyLib.Harmony harmonyInstance = null) { HarmonyLib.Harmony instance = harmonyInstance ?? new HarmonyLib.Harmony($"harmonywrapper-auto-{Guid.NewGuid()}"); type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Do(method => { List <HarmonyMethod> patchAttributeMethods = HarmonyMethodExtensions.GetFromMethod(method); if (patchAttributeMethods != null && patchAttributeMethods.Any()) { object[] attributes = method.GetCustomAttributes(true); HarmonyMethod combinedInfo = HarmonyMethod.Merge(patchAttributeMethods); if (attributes.Any(x => x is ParameterByRefAttribute)) { ParameterByRefAttribute byRefAttribute = (ParameterByRefAttribute)attributes.First(x => x is ParameterByRefAttribute); foreach (int index in byRefAttribute.ParameterIndices) { combinedInfo.argumentTypes[index] = combinedInfo.argumentTypes[index].MakeByRefType(); } } HarmonyMethod prefix = null; HarmonyMethod transpiler = null; HarmonyMethod postfix = null; if (attributes.Any(x => x is HarmonyPrefix)) { prefix = new HarmonyMethod(method); } if (attributes.Any(x => x is HarmonyTranspiler)) { transpiler = new HarmonyMethod(method); } if (attributes.Any(x => x is HarmonyPostfix)) { postfix = new HarmonyMethod(method); } List <HarmonyMethod> completeMethods = patchAttributeMethods.Where(x => x.declaringType != null && x.methodName != null).ToList(); if (patchAttributeMethods.All(x => x.declaringType != combinedInfo.declaringType && x.methodName != combinedInfo.methodName)) { completeMethods.Add(combinedInfo); } List <MethodBase> originalMethods = new List <MethodBase>(); foreach (HarmonyMethod methodToPatch in completeMethods) { if (!methodToPatch.methodType.HasValue) { methodToPatch.methodType = MethodType.Normal; } MethodBase originalMethod = GetOriginalMethod(methodToPatch); if (originalMethod == null) { throw new ArgumentException($"Null method for attribute: \n" + $"Type={methodToPatch.declaringType.FullName ?? "<null>"}\n" + $"Name={methodToPatch.methodName ?? "<null>"}\n" + $"MethodType={(methodToPatch.methodType.HasValue ? methodToPatch.methodType.Value.ToString() : "<null>")}\n" + $"Args={(methodToPatch.argumentTypes == null ? "<null>" : string.Join(",", methodToPatch.argumentTypes.Select(x => x.FullName).ToArray()))}"); } originalMethods.Add(originalMethod); } PatchProcessor processor = new PatchProcessor(instance) .SetOriginals(originalMethods) .AddPrefix(prefix) .AddPostfix(postfix) .AddTranspiler(transpiler); processor.Patch(); } }); return(instance); }
public void TestMethod1() { var originalClass = typeof(Class1); Assert.IsNotNull(originalClass); var originalMethod = originalClass.GetMethod("Method1"); Assert.IsNotNull(originalMethod); var patchClass = typeof(Class1Patch); var realPrefix = patchClass.GetMethod("Prefix"); var realPostfix = patchClass.GetMethod("Postfix"); var realTranspiler = patchClass.GetMethod("Transpiler"); Assert.IsNotNull(realPrefix); Assert.IsNotNull(realPostfix); Assert.IsNotNull(realTranspiler); Class1Patch._reset(); MethodInfo prefixMethod; MethodInfo postfixMethod; MethodInfo transpilerMethod; PatchTools.GetPatches(typeof(Class1Patch), out prefixMethod, out postfixMethod, out transpilerMethod); Assert.AreSame(realPrefix, prefixMethod); Assert.AreSame(realPostfix, postfixMethod); Assert.AreSame(realTranspiler, transpilerMethod); var instance = HarmonyInstance.Create("test"); Assert.IsNotNull(instance); var patcher = new PatchProcessor(instance, new List <MethodBase> { originalMethod }, new HarmonyMethod(prefixMethod), new HarmonyMethod(postfixMethod), new HarmonyMethod(transpilerMethod)); Assert.IsNotNull(patcher); var originalMethodStartPre = Memory.GetMethodStart(originalMethod, out var exception); patcher.Patch(); var originalMethodStartPost = Memory.GetMethodStart(originalMethod, out exception); Assert.AreEqual(originalMethodStartPre, originalMethodStartPost); unsafe { var patchedCode = *(byte *)originalMethodStartPre; if (IntPtr.Size == sizeof(long)) { Assert.IsTrue(patchedCode == 0x48); } else { Assert.IsTrue(patchedCode == 0x68); } } Class1.Method1(); Assert.IsTrue(Class1Patch.prefixed, "Prefix was not executed"); Assert.IsTrue(Class1Patch.originalExecuted, "Original was not executed"); Assert.IsTrue(Class1Patch.postfixed, "Postfix was not executed"); }
public void SetUp() { var testMethod = TestContext.CurrentContext.Test.Name; var parts = testMethod.Split('_'); var originalType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[1]); var patchType = AccessTools.TypeByName("HarmonyLibTests.Assets." + parts[2]); Assert.IsNotNull(originalType); var originalMethod = originalType.GetMethod("Method"); Assert.IsNotNull(originalMethod); var finalizer = patchType.GetMethod("Finalizer"); Assert.IsNotNull(finalizer); Harmony.SELF_PATCHING = false; if (Harmony.DEBUG) { FileLog.Reset(); FileLog.Log("### Original: " + parts[1]); FileLog.Log("### Patching: " + parts[2]); } var instance = new Harmony("test"); Assert.IsNotNull(instance); var patcher = new PatchProcessor(instance, new List <MethodBase> { originalMethod }, null, null, null, new HarmonyMethod(finalizer)); Assert.IsNotNull(patcher); patcher.Patch(); var trv = Traverse.Create(patchType); trv.Field("finalized").SetValue(false); trv.Field("exception").SetValue(new NullReferenceException("replace-me")); var obj = Activator.CreateInstance(originalType); var m_method = AccessTools.Method(originalType, "Method"); info = new Dictionary <string, object>(); try { if (m_method.ReturnType == typeof(void)) { m_method.Invoke(obj, null); } else { info["result"] = m_method.Invoke(obj, null); } info["outerexception"] = null; } catch (TargetInvocationException e) { info["outerexception"] = e.InnerException; } trv.Fields().ForEach(name => info[name] = trv.Field(name).GetValue()); instance.UnpatchAll(); Assert.IsTrue((bool)info["finalized"], "Finalizer not called"); }
public static bool IsCombinedDiff([CanBeNull] string diff) { return(PatchProcessor.IsCombinedDiff(diff)); }
public Hook(List <DynamicMethod> patches, PatchProcessor processor) { this.patches = patches; this.processor = processor; }
public void TestCorrectlyLoadsOneDeleteFile() { var patches = PatchProcessor.CreatePatchesFromString(_bigPatch, Encoding.UTF8); Assert.AreEqual(1, patches.Count(p => p.ChangeType == PatchChangeType.DeleteFile)); }
public void TestCorrectlyLoadsBinaryPatch() { var patches = PatchProcessor.CreatePatchesFromString(_bigBinPatch, Encoding.UTF8); Assert.AreEqual(248, patches.Count(p => p.FileType == PatchFileType.Binary)); }
public void TestCorrectlyLoadsTheRightNumberOfDiffsInAPatchFile() { var patches = PatchProcessor.CreatePatchesFromString(_bigPatch, Encoding.UTF8); Assert.AreEqual(17, patches.Count()); }
public bool GetErrorMods(string logString, string stackString, out Dictionary <UnityModManager.ModInfo, List <string> > result) { Dictionary <UnityModManager.ModInfo, List <string> > errorMods = new Dictionary <UnityModManager.ModInfo, List <string> >(); try { foreach (var mod in modsTypesNamesCache) { foreach (var name in mod.Value) { if (logString.Contains(name)) { ExceptionHelper.Instance.SetErrorMod(errorMods, mod.Key, name); } if (stackString.Contains(name)) { ExceptionHelper.Instance.SetErrorMod(errorMods, mod.Key, name); } } } string pattern = @"(?<= )\S+?_Patch\d+"; foreach (Match match in Regex.Matches(logString + stackString, pattern)) { string matchString = match.Groups[0].Value; string fullName = matchString.Substring(0, matchString.LastIndexOf('_')); int num = fullName.LastIndexOf('.'); string methodName = fullName.Substring(num + 1, fullName.Length - fullName.LastIndexOf('.') - 1); string typeName = fullName.Substring(0, fullName.LastIndexOf('.')); string index = matchString.Substring(matchString.LastIndexOf("_Patch") + 6, matchString.Length - (matchString.LastIndexOf("_Patch") + 6)); Type classtyp = AccessTools.TypeByName(typeName); if (classtyp == null) { UnityModManager.Logger.Log($"无法获取到{fullName}的类型"); continue; } MethodInfo methodInfo = classtyp.GetMethod(methodName, AccessTools.all); if (methodInfo == null) { UnityModManager.Logger.Log($"无法获取到{fullName}的方法"); continue; } var info = PatchProcessor.GetPatchInfo(methodInfo); if (info == null) { UnityModManager.Logger.Log($"无法获取到对{fullName}的补丁"); continue; } int patchIndex = int.Parse(index); foreach (var patch in info.Prefixes) { if (patch.index == patchIndex) { UnityModManager.ModInfo modInfo = UnityModManager.FindMod(patch.owner).Info; ExceptionHelper.Instance.SetErrorMod(errorMods, modInfo, matchString + ".Prefix()"); } } foreach (var patch in info.Postfixes) { if (patch.index == patchIndex) { UnityModManager.ModInfo modInfo = UnityModManager.FindMod(patch.owner).Info; ExceptionHelper.Instance.SetErrorMod(errorMods, modInfo, matchString + ".Postfix()"); } } } result = errorMods; return(true); } catch (Exception e) { errorMods.Clear(); errorMods.Add(UMMLoader.ModInfo, new List <string>() { e.Message, e.StackTrace }); result = errorMods; return(false); } }