using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("GoblinStudio")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Read-only OnGUI KeyboardShortcut inspector runtime candidate.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("GoblinHotkeyInspector")] [assembly: AssemblyTitle("GoblinHotkeyInspector")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace GoblinStudio.GoblinHotkeyInspector { [BepInPlugin("GoblinStudio.GoblinHotkeyInspector", "Goblin Hotkey Inspector", "0.1.0")] public sealed class GoblinHotkeyInspectorPlugin : BaseUnityPlugin { public const string PluginGuid = "GoblinStudio.GoblinHotkeyInspector"; public const string PluginName = "Goblin Hotkey Inspector"; public const string PluginVersion = "0.1.0"; private ConfigEntry? _openInspector; private ConfigEntry? _showUnique; private ConfigEntry? _showDisabled; private bool _windowOpen; private Rect _windowRect; private Vector2 _scrollPosition; private ScanSnapshot? _snapshot; private GUIStyle? _titleStyle; private GUIStyle? _sectionStyle; private GUIStyle? _descriptionStyle; private GUIStyle? _mutedStyle; private void Awake() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) _openInspector = ((BaseUnityPlugin)this).Config.Bind("General", "OpenInspector", new KeyboardShortcut((KeyCode)290, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Open or close the Goblin Hotkey Inspector window."); _showUnique = ((BaseUnityPlugin)this).Config.Bind("General", "ShowUnique", true, "Show shortcuts that are used by only one configured entry."); _showDisabled = ((BaseUnityPlugin)this).Config.Bind("General", "ShowDisabled", true, "Show disabled KeyboardShortcut entries."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Goblin Hotkey Inspector 0.1.0 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)("OpenInspector shortcut: " + FormatShortcut(_openInspector.Value))); } private void Update() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (_openInspector == null) { return; } KeyboardShortcut value = _openInspector.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _windowOpen = !_windowOpen; if (_windowOpen) { EnsureWindowRect(); ScanNow(); } } } private void OnGUI() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (_windowOpen && _snapshot != null) { EnsureStyles(); EnsureWindowRect(); _windowRect = GUI.Window(((Object)this).GetInstanceID(), _windowRect, new WindowFunction(DrawWindow), "Goblin Hotkey Inspector"); _windowRect = ClampWindowRect(_windowRect); } } private void DrawWindow(int windowId) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) ScanSnapshot snapshot = _snapshot; GUILayout.BeginVertical(Array.Empty()); GUILayout.Label("Scan completed", _mutedStyle, Array.Empty()); GUILayout.Label($"Configured shortcuts: {snapshot.ActiveShortcuts} active / {snapshot.DisabledShortcuts} disabled " + $"Shared groups: {snapshot.SharedGroups} Unsupported entries: {snapshot.UnsupportedEntries}", _mutedStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Rescan", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { ScanNow(); } if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { _windowOpen = false; } GUILayout.EndHorizontal(); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawGroups("Shared Shortcuts", snapshot.Groups.Where((ShortcutGroupDto group) => group.Status == GroupStatus.Shared)); if (_showUnique.Value) { DrawGroups("Unique Shortcuts", snapshot.Groups.Where((ShortcutGroupDto group) => group.Status == GroupStatus.Unique)); } if (_showDisabled.Value) { DrawDisabled(snapshot.DisabledEntries); } if (snapshot.Groups.All((ShortcutGroupDto group) => group.Status != GroupStatus.Shared)) { GUILayout.Label("No shared shortcuts found", _mutedStyle, Array.Empty()); } if (snapshot.UnsupportedEntries > 0) { GUILayout.Label($"{snapshot.UnsupportedEntries} config entries were not KeyboardShortcut entries and were not inspected.", _mutedStyle, Array.Empty()); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 24f)); } private void DrawGroups(string heading, IEnumerable groups) { List list = groups.ToList(); if (list.Count == 0) { return; } GUILayout.Label(heading, _sectionStyle, Array.Empty()); foreach (ShortcutGroupDto item in list) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.Label(item.ShortcutText, Array.Empty()); GUILayout.Label($"{item.Status.ToString().ToUpperInvariant()} · {item.Entries.Count} entries", _mutedStyle, Array.Empty()); DrawEntries(item.Entries); if (item.Status == GroupStatus.Shared) { GUILayout.Label("Shared shortcut detected.", _descriptionStyle, Array.Empty()); GUILayout.Label("Runtime conflict depends on each mod's input handling and active context.", _descriptionStyle, Array.Empty()); } GUILayout.EndVertical(); } } private void DrawDisabled(IReadOnlyList entries) { if (entries.Count == 0) { return; } GUILayout.Label("Disabled Shortcuts", _sectionStyle, Array.Empty()); foreach (HotkeyEntryDto entry in entries) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.Label("DISABLED · None", _mutedStyle, Array.Empty()); DrawEntries(new HotkeyEntryDto[1] { entry }); GUILayout.EndVertical(); } } private static void DrawEntries(IReadOnlyList entries) { foreach (HotkeyEntryDto entry in entries) { GUILayout.Label(entry.PluginName + " " + entry.PluginVersion, Array.Empty()); GUILayout.Label(entry.Section + " / " + entry.ConfigKey, Array.Empty()); GUILayout.Label("GUID: " + entry.PluginGuid, Array.Empty()); GUILayout.Label(entry.Description, GUI.skin.label, Array.Empty()); } } private void ScanNow() { _snapshot = HotkeyScanner.Scan(((BaseUnityPlugin)this).Logger); HotkeyScanner.LogSummary(((BaseUnityPlugin)this).Logger, _snapshot); } private void EnsureWindowRect() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (((Rect)(ref _windowRect)).width <= 0f || ((Rect)(ref _windowRect)).height <= 0f) { _windowRect = new Rect(Mathf.Max(16f, ((float)Screen.width - 760f) / 2f), Mathf.Max(16f, ((float)Screen.height - 560f) / 2f), 760f, 560f); } _windowRect = ClampWindowRect(_windowRect); } private static Rect ClampWindowRect(Rect rect) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Min(((Rect)(ref rect)).width, Mathf.Max(360f, (float)Screen.width - 32f)); float num2 = Mathf.Min(((Rect)(ref rect)).height, Mathf.Max(260f, (float)Screen.height - 32f)); float num3 = Mathf.Clamp(((Rect)(ref rect)).x, 16f, Mathf.Max(16f, (float)Screen.width - num - 16f)); float num4 = Mathf.Clamp(((Rect)(ref rect)).y, 16f, Mathf.Max(16f, (float)Screen.height - num2 - 16f)); return new Rect(num3, num4, num, num2); } private void EnsureStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown if (_titleStyle == null) { _titleStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = 16 }; _sectionStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = 14 }; _descriptionStyle = new GUIStyle(GUI.skin.label) { wordWrap = true }; _mutedStyle = new GUIStyle(GUI.skin.label) { wordWrap = true }; } } private unsafe static string FormatShortcut(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) { return "None; OpenInspector must be configured before the window can open."; } string[] array = (from key in ((KeyboardShortcut)(ref shortcut)).Modifiers.Distinct() orderby (int)key select ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString()).ToArray(); if (array.Length != 0) { return string.Join(" + ", array) + " + " + ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString(); } return ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString(); } } internal static class HotkeyScanner { internal static ScanSnapshot Scan(ManualLogSource log) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); ScanSnapshot scanSnapshot = new ScanSnapshot(); Dictionary> dictionary = new Dictionary>(StringComparer.Ordinal); IReadOnlyDictionary pluginInfos = Chainloader.PluginInfos; scanSnapshot.ActivePlugins = pluginInfos.Count; foreach (KeyValuePair item in pluginInfos) { try { BaseUnityPlugin instance = item.Value.Instance; ConfigFile val = ((instance != null) ? instance.Config : null); if (val == null) { scanSnapshot.Errors++; log.LogWarning((object)("ghi_scan skipped plugin '" + item.Key + "': ConfigFile unavailable.")); continue; } ConfigEntryBase[] configEntries = val.GetConfigEntries(); foreach (ConfigEntryBase val2 in configEntries) { scanSnapshot.ConfigEntries++; try { if (val2.SettingType != typeof(KeyboardShortcut)) { scanSnapshot.UnsupportedEntries++; continue; } if (!(val2.BoxedValue is KeyboardShortcut shortcut)) { scanSnapshot.Errors++; log.LogWarning((object)$"ghi_scan skipped '{item.Key}/{val2.Definition}': invalid KeyboardShortcut value."); continue; } HotkeyEntryDto hotkeyEntryDto = CreateEntry(item.Key, item.Value, val2, shortcut); scanSnapshot.KeyboardShortcuts++; if (hotkeyEntryDto.IsDisabled) { scanSnapshot.DisabledShortcuts++; scanSnapshot.DisabledEntries.Add(hotkeyEntryDto); continue; } scanSnapshot.ActiveShortcuts++; if (!dictionary.TryGetValue(hotkeyEntryDto.ShortcutText, out var value)) { value = new List(); dictionary.Add(hotkeyEntryDto.ShortcutText, value); } value.Add(hotkeyEntryDto); } catch (Exception ex) { scanSnapshot.Errors++; log.LogWarning((object)("ghi_scan skipped entry from '" + item.Key + "': " + ex.GetType().Name + ": " + ex.Message)); } } } catch (Exception ex2) { scanSnapshot.Errors++; log.LogWarning((object)("ghi_scan skipped plugin '" + item.Key + "': " + ex2.GetType().Name + ": " + ex2.Message)); } } foreach (KeyValuePair> item2 in dictionary) { item2.Value.Sort(EntryComparer); scanSnapshot.Groups.Add(new ShortcutGroupDto(item2.Key, (item2.Value.Count <= 1) ? GroupStatus.Unique : GroupStatus.Shared, item2.Value)); } scanSnapshot.Groups.Sort(delegate(ShortcutGroupDto left, ShortcutGroupDto right) { int num = left.Status.CompareTo(right.Status); return (num == 0) ? StringComparer.Ordinal.Compare(left.ShortcutText, right.ShortcutText) : num; }); scanSnapshot.DisabledEntries.Sort(EntryComparer); scanSnapshot.SharedGroups = scanSnapshot.Groups.Count((ShortcutGroupDto group) => group.Status == GroupStatus.Shared); scanSnapshot.UniqueGroups = scanSnapshot.Groups.Count((ShortcutGroupDto group) => group.Status == GroupStatus.Unique); scanSnapshot.ElapsedMilliseconds = stopwatch.Elapsed.TotalMilliseconds; return scanSnapshot; } internal static void LogSummary(ManualLogSource log, ScanSnapshot snapshot) { log.LogInfo((object)($"ghi_scan_hotkeys completed: activePlugins={snapshot.ActivePlugins}, configEntries={snapshot.ConfigEntries}, " + $"keyboardShortcuts={snapshot.KeyboardShortcuts}, activeShortcuts={snapshot.ActiveShortcuts}, " + $"disabled={snapshot.DisabledShortcuts}, groups={snapshot.Groups.Count}, sharedGroups={snapshot.SharedGroups}, " + $"unsupported={snapshot.UnsupportedEntries}, errors={snapshot.Errors}, elapsedMs={snapshot.ElapsedMilliseconds:F3}")); } private unsafe static HotkeyEntryDto CreateEntry(string guid, PluginInfo pluginInfo, ConfigEntryBase entry, KeyboardShortcut shortcut) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Invalid comparison between Unknown and I4 string[] array = (from val in ((KeyboardShortcut)(ref shortcut)).Modifiers.Distinct() orderby (int)val select ((object)(*(KeyCode*)(&val))/*cast due to .constrained prefix*/).ToString()).ToArray(); string shortcutText = (((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0) ? "None" : ((array.Length == 0) ? ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString() : (string.Join(" + ", array) + " + " + ((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString()))); BepInPlugin metadata = pluginInfo.Metadata; object obj = ((metadata != null) ? metadata.Name : null); if (obj == null) { BepInPlugin metadata2 = pluginInfo.Metadata; obj = ((metadata2 != null) ? metadata2.GUID : null) ?? guid; } BepInPlugin metadata3 = pluginInfo.Metadata; string pluginVersion = ((metadata3 == null) ? null : metadata3.Version?.ToString()) ?? "unknown"; string section = entry.Definition.Section; string key = entry.Definition.Key; ConfigDescription description = entry.Description; return new HotkeyEntryDto((string)obj, guid, pluginVersion, section, key, ((description != null) ? description.Description : null) ?? string.Empty, shortcutText, (int)((KeyboardShortcut)(ref shortcut)).MainKey == 0); } private static int EntryComparer(HotkeyEntryDto left, HotkeyEntryDto right) { int num = StringComparer.Ordinal.Compare(left.PluginName, right.PluginName); if (num != 0) { return num; } num = StringComparer.Ordinal.Compare(left.Section, right.Section); if (num == 0) { return StringComparer.Ordinal.Compare(left.ConfigKey, right.ConfigKey); } return num; } } internal sealed class ScanSnapshot { internal int ActivePlugins { get; set; } internal int ConfigEntries { get; set; } internal int KeyboardShortcuts { get; set; } internal int ActiveShortcuts { get; set; } internal int DisabledShortcuts { get; set; } internal int UniqueGroups { get; set; } internal int SharedGroups { get; set; } internal int UnsupportedEntries { get; set; } internal int Errors { get; set; } internal double ElapsedMilliseconds { get; set; } internal List Groups { get; } = new List(); internal List DisabledEntries { get; } = new List(); } internal sealed class ShortcutGroupDto { internal string ShortcutText { get; } internal GroupStatus Status { get; } internal IReadOnlyList Entries { get; } internal ShortcutGroupDto(string shortcutText, GroupStatus status, IReadOnlyList entries) { ShortcutText = shortcutText; Status = status; Entries = entries; } } internal sealed class HotkeyEntryDto { internal string PluginName { get; } internal string PluginGuid { get; } internal string PluginVersion { get; } internal string Section { get; } internal string ConfigKey { get; } internal string Description { get; } internal string ShortcutText { get; } internal bool IsDisabled { get; } internal HotkeyEntryDto(string pluginName, string pluginGuid, string pluginVersion, string section, string configKey, string description, string shortcutText, bool isDisabled) { PluginName = pluginName; PluginGuid = pluginGuid; PluginVersion = pluginVersion; Section = section; ConfigKey = configKey; Description = description; ShortcutText = shortcutText; IsDisabled = isDisabled; } } internal enum GroupStatus { Shared, Unique } }