/// <summary>
        /// Checks the specified type and all of its nested types for issues.
        /// </summary>
        /// <param name="type">The type to check.</param>
        internal static void CheckType(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            bool isAnnotated = false, hasMethods = false;

            foreach (var annotation in type.GetCustomAttributes(true))
            {
                if (annotation is HarmonyPatch patch)
                {
                    isAnnotated = true;
                    break;
                }
            }
            // Patchy method names?
            foreach (var method in type.GetMethods(ALL))
            {
                if (HARMONY_NAMES.Contains(method.Name))
                {
                    hasMethods = true;
                    break;
                }
            }
            if (hasMethods && !isAnnotated)
            {
                DebugLogger.LogWarning("Type " + type.FullName +
                                       " looks like a Harmony patch but does not have the annotation!");
            }
        }
Beispiel #2
0
        /// <summary>
        /// Opens the output log file.
        /// </summary>
        internal static void OpenOutputLog()
        {
            // Ugly but true!
            var    platform = Environment.OSVersion.Platform;
            string path     = "";

            switch (platform)
            {
            case PlatformID.MacOSX:
                // https://answers.unity.com/questions/1484445/how-do-i-find-the-player-log-file-from-code.html
                path = "~/Library/Logs/Unity/Player.log";
                break;

            case PlatformID.Unix:
                path = Path.Combine("~/.config/unity3d", Application.companyName, Application.
                                    productName, "Player.log");
                break;

            case PlatformID.Win32NT:
            case PlatformID.Win32S:
            case PlatformID.Win32Windows:
                path = Path.Combine(Environment.GetEnvironmentVariable("AppData"),
                                    "..", "LocalLow", Application.companyName, Application.productName,
                                    "output_log.txt");
                break;

            default:
                DebugLogger.LogWarning("Unable to open the output log on: {0}", platform);
                break;
            }
            if (!string.IsNullOrEmpty(path))
            {
                Application.OpenURL(Path.GetFullPath(path));
            }
        }
Beispiel #3
0
        /// <summary>
        /// Moves this mod to the first position and prompts to restart if necssary.
        /// </summary>
        /// <param name="parent">The parent of the dialog.</param>
        private static void MoveToFirst(GameObject parent)
        {
            var manager = Global.Instance.modManager;
            var mods    = manager?.mods;
            var target  = DebugNotIncludedPatches.ThisMod;

            if (mods != null && target != null)
            {
                int n = mods.Count, oldIndex = -1;
                // Search for this mod in the list
                for (int i = 0; i < n && oldIndex < 0; i++)
                {
                    if (mods[i].label.Match(target.label))
                    {
                        oldIndex = i;
                    }
                }
                if (oldIndex > 0)
                {
                    manager.Reinsert(oldIndex, 0, manager);
                    manager.Report(parent);
                }
                else
                {
                    DebugLogger.LogWarning("Unable to move Debug Not Included to top - uninstalled?");
                }
            }
        }
Beispiel #4
0
        internal static TranspiledMethod Transpiler(TranspiledMethod method)
        {
            var targetMethod = typeof(HoverTextDrawer).GetMethodSafe(nameof(HoverTextDrawer.
                                                                            EndDrawing), false);
            var addition = typeof(UpdateHoverElements_Patch).GetMethodSafe(nameof(
                                                                               DrawCoordinates), true, typeof(HoverTextDrawer), typeof(
                                                                               HoverTextConfiguration));

            if (targetMethod != null && addition != null)
            {
                foreach (var instr in method)
                {
                    if (instr.Is(OpCodes.Callvirt, targetMethod))
                    {
                        yield return(new CodeInstruction(OpCodes.Ldarg_0));

                        yield return(new CodeInstruction(OpCodes.Call, addition));
                    }
                    yield return(instr);
                }
            }
            else
            {
                DebugLogger.LogWarning("Unable to patch UpdateHoverElements");
                foreach (var instr in method)
                {
                    yield return(instr);
                }
            }
        }
Beispiel #5
0
            /// <summary>
            /// Transpiles LoadDLLs to grab the exception information when a mod fails to load.
            /// </summary>
            private static IEnumerable <CodeInstruction> Transpiler(
                IEnumerable <CodeInstruction> method)
            {
                var instructions = new List <CodeInstruction>(method);
                // HarmonyInstance.Create and Assembly.LoadFrom will be wrapped
                var harmonyCreate = typeof(HarmonyInstance).GetMethodSafe(nameof(
                                                                              HarmonyInstance.Create), true, typeof(string));
                var loadFrom = typeof(Assembly).GetMethodSafe(nameof(Assembly.LoadFrom), true,
                                                              typeof(string));
                bool patchException = PUtil.GameVersion >= 397125u, patchAssembly = false,
                     patchCreate = false;

                // Add call to our handler in exception block, and wrap harmony instances to
                // have more information on each mod
                for (int i = instructions.Count - 1; i > 0; i--)
                {
                    var instr = instructions[i];
                    if (instr.opcode == OpCodes.Pop && !patchException)
                    {
                        instr.opcode = OpCodes.Call;
                        // Call our method instead
                        instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof(
                                                                                 ModLoadHandler.HandleModException), true, typeof(object));
                        patchException = true;
                    }
                    else if (instr.opcode == OpCodes.Call)
                    {
                        var target = instr.operand as MethodInfo;
                        if (target == harmonyCreate && harmonyCreate != null)
                        {
                            // Reroute HarmonyInstance.Create
                            instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof(
                                                                                     ModLoadHandler.CreateHarmonyInstance), true, typeof(string));
                            patchCreate = true;
                        }
                        else if (target == loadFrom && loadFrom != null)
                        {
                            // Reroute Assembly.LoadFrom
                            instr.operand = typeof(ModLoadHandler).GetMethodSafe(nameof(
                                                                                     ModLoadHandler.LoadAssembly), true, typeof(string));
                            patchAssembly = true;
                        }
                    }
                }
                if (!patchException)
                {
                    DebugLogger.LogError("Unable to transpile LoadDLLs: Could not find exception handler");
                }
                if (!patchAssembly)
                {
                    DebugLogger.LogWarning("Unable to transpile LoadDLLs: No calls to Assembly.LoadFrom found");
                }
                if (!patchCreate)
                {
                    DebugLogger.LogWarning("Unable to transpile LoadDLLs: No calls to HarmonyInstance.Create found");
                }
                return(instructions);
            }
Beispiel #6
0
            /// <summary>
            /// Applied before DetourMethod runs.
            /// </summary>
            internal static void Prefix(MethodBase original, MethodBase replacement)
            {
                var body = original.GetMethodBody();

                if (body.GetILAsByteArray().Length < MIN_METHOD_SIZE)
                {
                    DebugLogger.LogWarning("Patch {0} targets empty method {1}.{2}".F(
                                               replacement.Name, original.DeclaringType, original.Name));
                }
            }
Beispiel #7
0
        /// <summary>
        /// Runs the required postload patches after all other mods load.
        /// </summary>
        /// <param name="instance">The Harmony instance to execute patches.</param>
        public override void OnAllModsLoaded(Harmony harmony, IReadOnlyList <Mod> mods)
        {
            var options = DebugNotIncludedOptions.Instance;

            if (options?.PowerUserMode == true)
            {
                harmony.Patch(typeof(ModsScreen), "BuildDisplay",
                              new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(HidePopups)),
                              new HarmonyMethod(typeof(DebugNotIncludedPatches), nameof(BuildDisplay)));
            }
            if (mods != null)
            {
                ModDebugRegistry.Instance.Populate(mods);
            }
            else
            {
                DebugLogger.LogWarning("Mods list is empty! Attribution will not work");
            }

            var runningCore = PRegistry.Instance.GetLatestVersion(
                "PeterHan.PLib.Core.PLibCorePatches")?.GetOwningAssembly();

            if (runningCore != null)
            {
                RunningPLibAssembly = runningCore;
            }
            // Log which mod is running PLib
            var latest = ModDebugRegistry.Instance.OwnerOfAssembly(RunningPLibAssembly);

            if (latest != null)
            {
                DebugLogger.LogDebug("Executing version of PLib is from: " + latest.ModName);
            }

            HarmonyPatchInspector.Check();
#if DEBUG
            harmony.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Load", false, typeof(
                                                                       IReader)));
            harmony.ProfileMethod(typeof(SaveLoader).GetMethodSafe("Save", false, typeof(
                                                                       BinaryWriter)));
            harmony.ProfileMethod(typeof(SaveManager).GetMethodSafe("Load", false,
                                                                    PPatchTools.AnyArguments));
            harmony.ProfileMethod(typeof(SaveManager).GetMethodSafe("Save", false,
                                                                    PPatchTools.AnyArguments));
#endif
            if (options?.LocalizeMods == true)
            {
                typeof(PLocalization).GetMethodSafe("DumpAll", false)?.Invoke(loc, null);
            }
        }
        /// <summary>
        /// Checks a declared Harmony annotation and warns if an inherited method is being
        /// patched, which "works" on Windows but fails on Mac OS X / Linux.
        /// </summary>
        /// <param name="target">The annotation to check.</param>
        /// <param name="patcher">The type which created the patch</param>
        internal static void CheckHarmonyMethod(HarmonyMethod target, Type patcher)
        {
            var                targetType = target.declaringType;
            string             name       = target.methodName;
            const BindingFlags ONLY_DEC   = ALL | BindingFlags.DeclaredOnly;

            if (targetType != null && !string.IsNullOrEmpty(name))
            {
                try {
                    PropertyInfo info;
                    string       targetName = targetType.FullName + "." + name;
                    switch (target.methodType)
                    {
                    case MethodType.Normal:
                        var argTypes = target.argumentTypes;
                        // If no argument types, provide what we can
                        if (((argTypes == null) ? targetType.GetMethod(name, ONLY_DEC) :
                             targetType.GetMethod(name, ONLY_DEC, null, argTypes, null)) ==
                            null)
                        {
                            DebugLogger.LogWarning("Patch {0} targets inherited method {1}",
                                                   patcher.FullName, targetName);
                        }
                        break;

                    case MethodType.Setter:
                        info = targetType.GetProperty(name, ONLY_DEC);
                        if (info?.GetSetMethod(true) == null)
                        {
                            DebugLogger.LogWarning("Patch {0} targets inherited property {1}",
                                                   patcher.FullName, targetName);
                        }
                        break;

                    case MethodType.Getter:
                        info = targetType.GetProperty(name, ONLY_DEC);
                        if (info?.GetGetMethod(true) == null)
                        {
                            DebugLogger.LogWarning("Patch {0} targets inherited property {1}",
                                                   patcher.FullName, targetName);
                        }
                        break;

                    default:
                        break;
                    }
                } catch (AmbiguousMatchException) { }
            }
        }
        public static void OnLoad(string path)
        {
            var inst = ModDebugRegistry.Instance;

            RunningPLibAssembly = typeof(PUtil).Assembly;
            PUtil.InitLibrary();
            if (DebugNotIncludedOptions.Instance?.DetailedBacktrace ?? true)
            {
                DebugLogger.InstallExceptionLogger();
            }
            POptions.RegisterOptions(typeof(DebugNotIncludedOptions));
            if (DebugNotIncludedOptions.Instance?.LogAsserts ?? true)
            {
                LogAllFailedAsserts();
            }
            // Patch the exception logger for state machines
            var logException = typeof(DebugUtil).GetMethodSafe("LogException", true,
                                                               PPatchTools.AnyArguments);

            if (logException != null)
            {
                inst.DebugInstance.Patch(logException, prefix: new HarmonyMethod(typeof(
                                                                                     DebugLogger), nameof(DebugLogger.LogException)));
            }
            foreach (var mod in Global.Instance.modManager?.mods)
            {
                if (mod.label.install_path == path)
                {
                    ThisMod = mod;
                    break;
                }
            }
            if (ThisMod == null)
            {
                DebugLogger.LogWarning("Unable to determine KMod instance!");
            }
            else
            {
                inst.RegisterModAssembly(Assembly.GetExecutingAssembly(), inst.GetDebugInfo(
                                             ThisMod));
            }
            // Default UI debug key is ALT+U
            UIDebugAction = PAction.Register("DebugNotIncluded.UIDebugAction",
                                             DebugNotIncludedStrings.KEY_SNAPSHOT, new PKeyBinding(KKeyCode.U,
                                                                                                   Modifier.Alt));
            // Must postload the mods dialog to come out after aki's mods, ony's mods, PLib
            // options, and so forth
            PUtil.RegisterPostload(PostloadHandler);
        }
Beispiel #10
0
 /// <summary>
 /// Profiles a method, outputting how many milliseconds it took to run on each use.
 /// </summary>
 /// <param name="instance">The Harmony instance to use for the patch.</param>
 /// <param name="target">The method to profile.</param>
 internal static void ProfileMethod(this HarmonyInstance instance, MethodBase target)
 {
     if (target == null)
     {
         DebugLogger.LogWarning("No method specified to profile!");
     }
     else
     {
         instance.Patch(target, new HarmonyMethod(typeof(DebugNotIncludedPatches),
                                                  nameof(ProfilerPrefix)), new HarmonyMethod(typeof(DebugNotIncludedPatches),
                                                                                             nameof(ProfilerPostfix)));
         DebugLogger.LogDebug("Profiling method {0}.{1}".F(target.DeclaringType, target.
                                                           Name));
     }
 }
Beispiel #11
0
        /// <summary>
        /// Opens the output log file.
        /// </summary>
        internal static void OpenOutputLog()
        {
            // Ugly but true!
            var    platform  = Application.platform;
            string verString = Application.unityVersion;

            // Get the major Unity version
            if (verString != null && verString.Length > 4)
            {
                verString = verString.Substring(0, 4);
            }
            if (!uint.TryParse(verString, out uint unityYear))
            {
                unityYear = 2000u;
            }
            string path = "";

            switch (platform)
            {
            case RuntimePlatform.OSXPlayer:
            case RuntimePlatform.OSXEditor:
                // https://answers.unity.com/questions/1484445/how-do-i-find-the-player-log-file-from-code.html
                path = "~/Library/Logs/Unity/Player.log";
                break;

            case RuntimePlatform.LinuxEditor:
            case RuntimePlatform.LinuxPlayer:
                path = Path.Combine("~/.config/unity3d", Application.companyName, Application.
                                    productName, "Player.log");
                break;

            case RuntimePlatform.WindowsEditor:
            case RuntimePlatform.WindowsPlayer:
                path = Path.Combine(Environment.GetEnvironmentVariable("AppData"),
                                    "..", "LocalLow", Application.companyName, Application.productName,
                                    unityYear < 2019 ? "output_log.txt" : "Player.log");
                break;

            default:
                DebugLogger.LogWarning("Unable to open the output log on: {0}", platform);
                break;
            }
            if (!string.IsNullOrEmpty(path))
            {
                Application.OpenURL(Path.GetFullPath(path));
            }
        }
Beispiel #12
0
        public override void OnLoad(Harmony harmony)
        {
            var inst = ModDebugRegistry.Instance;

            var method = typeof(Mod).GetMethodSafe("Crash", false);

            if (method == null)
            {
                method = typeof(Mod).GetMethodSafe("SetCrashed", false);
            }
            if (method != null)
            {
                harmony.Patch(method, prefix: new HarmonyMethod(typeof(
                                                                    DebugNotIncludedPatches), nameof(OnModCrash)));
            }

            base.OnLoad(harmony);
            RunningPLibAssembly = typeof(PUtil).Assembly;
            PUtil.InitLibrary();
            if (DebugNotIncludedOptions.Instance?.DetailedBacktrace ?? true)
            {
                DebugLogger.InstallExceptionLogger();
            }
            new POptions().RegisterOptions(this, typeof(DebugNotIncludedOptions));

            LocString.CreateLocStringKeys(typeof(DebugNotIncludedStrings.UI));
            LocString.CreateLocStringKeys(typeof(DebugNotIncludedStrings.INPUT_BINDINGS));
            loc.Register();

            if (DebugNotIncludedOptions.Instance?.LogAsserts ?? true)
            {
                LogAllFailedAsserts(harmony);
            }
            ThisMod = mod;
            if (ThisMod == null)
            {
                DebugLogger.LogWarning("Unable to determine KMod instance!");
            }

            new PPatchManager(harmony).RegisterPatchClass(typeof(DebugNotIncludedPatches));
            // Force class initialization to avoid crashes on the background thread if
            // an Assert fails later
            DebugUtils.RegisterUIDebug();
            new PLib.AVC.PVersionCheck().Register(this, new PLib.AVC.SteamVersionChecker());
        }
Beispiel #13
0
        public static void OnLoad(string path)
        {
            var inst = ModDebugRegistry.Instance;

            RunningPLibAssembly = typeof(PUtil).Assembly;
            PUtil.InitLibrary();
            if (DebugNotIncludedOptions.Instance?.DetailedBacktrace ?? true)
            {
                DebugLogger.InstallExceptionLogger();
            }
            POptions.RegisterOptions(typeof(DebugNotIncludedOptions));
            // Set up strings
            LocString.CreateLocStringKeys(typeof(DebugNotIncludedStrings.UI));
            LocString.CreateLocStringKeys(typeof(DebugNotIncludedStrings.INPUT_BINDINGS));
            PLocalization.Register();
            if (DebugNotIncludedOptions.Instance?.LogAsserts ?? true)
            {
                LogAllFailedAsserts();
            }
            foreach (var mod in Global.Instance.modManager?.mods)
            {
                if (mod.GetModBasePath() == path)
                {
                    ThisMod = mod;
                    break;
                }
            }
            if (ThisMod == null)
            {
                DebugLogger.LogWarning("Unable to determine KMod instance!");
            }
            else
            {
                inst.RegisterModAssembly(Assembly.GetExecutingAssembly(), inst.GetDebugInfo(
                                             ThisMod));
            }
            // Default UI debug key is ALT+U
            UIDebugAction = PAction.Register("DebugNotIncluded.UIDebugAction",
                                             DebugNotIncludedStrings.INPUT_BINDINGS.DEBUG.SNAPSHOT, new PKeyBinding(
                                                 KKeyCode.U, Modifier.Alt));
            // Must postload the mods dialog to come out after aki's mods, ony's mods, PLib
            // options, and so forth
            PUtil.RegisterPatchClass(typeof(DebugNotIncludedPatches));
        }