Example #1
0
        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);
        }
Example #3
0
        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");
        }
Example #4
0
        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");
        }
Example #5
0
 public static bool IsCombinedDiff([CanBeNull] string diff)
 {
     return(PatchProcessor.IsCombinedDiff(diff));
 }
Example #6
0
 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());
        }
Example #10
0
        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);
            }
        }