FOff.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("FOff")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+4357acda50447985cb03fdb3fe2e2a87952d7684")]
[assembly: AssemblyProduct("FOff")]
[assembly: AssemblyTitle("FOff")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.0.0")]
[module: UnverifiableCode]
[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 FOff
{
	[BepInPlugin("com.Jello.Foff", "F Off", "0.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class FOffPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.Jello.Foff";

		public const string PluginName = "F Off";

		public const string PluginVersion = "0.3.0";

		public const string ConfigurationManagerGuid = "com.bepis.bepinex.configurationmanager";

		internal static ConfigEntry<KeyboardShortcut> FilterKey;

		internal static ConfigEntry<KeyboardShortcut> PreviousTabKey;

		internal static ConfigEntry<KeyboardShortcut> NextTabKey;

		internal static ManualLogSource Log;

		private Harmony? _harmony;

		private static bool _uiReflectionInitialized;

		private static bool _uiReflectionAvailable;

		private static bool _uiFailureLogged;

		private static Type? _textMeshProType;

		private static FieldInfo? _mouseKeyboardHintField;

		private static PropertyInfo? _tmpTextProperty;

		private void Awake()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			PreviousTabKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Build Menu", "Previous Tab Key", new KeyboardShortcut((KeyCode)113, Array.Empty<KeyCode>()), new ConfigDescription("Key used to move to the previous tab in Valheim's build menu.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 3
				}
			}));
			NextTabKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Build Menu", "Next Tab Key", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), new ConfigDescription("Key used to move to the next tab in Valheim's build menu.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 2
				}
			}));
			FilterKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Build Menu", "Filter Key", new KeyboardShortcut((KeyCode)102, Array.Empty<KeyCode>()), new ConfigDescription("Key used to focus the Filter field in Valheim's build menu.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 1
				}
			}));
			FilterKey.SettingChanged += OnFilterKeyChanged;
			PreviousTabKey.SettingChanged += OnTabKeyChanged;
			NextTabKey.SettingChanged += OnTabKeyChanged;
			_harmony = new Harmony("com.Jello.Foff");
			_harmony.PatchAll(typeof(FOffPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("F Off 0.3.0 loaded. " + $"Filter: {FilterKey.Value}, " + $"Previous Tab: {PreviousTabKey.Value}, " + $"Next Tab: {NextTabKey.Value}"));
		}

		private void OnDestroy()
		{
			if (FilterKey != null)
			{
				FilterKey.SettingChanged -= OnFilterKeyChanged;
			}
			if (PreviousTabKey != null)
			{
				PreviousTabKey.SettingChanged -= OnTabKeyChanged;
			}
			if (NextTabKey != null)
			{
				NextTabKey.SettingChanged -= OnTabKeyChanged;
			}
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private static void OnFilterKeyChanged(object sender, EventArgs args)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Filter key changed to: {FilterKey.Value}");
			}
			RefreshVisibleFilterHints();
		}

		private static void OnTabKeyChanged(object sender, EventArgs args)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)("Build tab keys changed. " + $"Previous: {PreviousTabKey.Value}, " + $"Next: {NextTabKey.Value}"));
			}
			RefreshVisibleTabHints();
		}

		internal static bool IsFilterKeyPressed()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (FilterKey == null)
			{
				return false;
			}
			KeyboardShortcut value = FilterKey.Value;
			if ((int)((KeyboardShortcut)(ref value)).MainKey == 0)
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, true))
				{
					return false;
				}
			}
			return ZInput.GetKey(((KeyboardShortcut)(ref value)).MainKey, true);
		}

		internal static bool IsShortcutDown(KeyboardShortcut shortcut)
		{
			return ((KeyboardShortcut)(ref shortcut)).IsDown();
		}

		internal static void UpdateBuildMenuTabKeys(BuildUi buildUi)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)buildUi == (Object)null || (Object)(object)buildUi.m_tabHandler == (Object)null || PreviousTabKey == null || NextTabKey == null)
			{
				return;
			}
			TabHandler tabHandler = buildUi.m_tabHandler;
			bool flag = IsShortcutDown(PreviousTabKey.Value);
			bool flag2 = IsShortcutDown(NextTabKey.Value);
			int num = 0;
			if (flag)
			{
				num = -1;
			}
			else if (flag2)
			{
				num = 1;
			}
			if (num == 0 || tabHandler.m_tabs.Count == 0)
			{
				return;
			}
			int activeTab = tabHandler.GetActiveTab();
			int num2 = activeTab + num;
			if (tabHandler.m_cycling)
			{
				if (num2 < 0)
				{
					num2 = tabHandler.m_tabs.Count - 1;
				}
				else if (num2 >= tabHandler.m_tabs.Count)
				{
					num2 = 0;
				}
			}
			else
			{
				num2 = Math.Max(0, Math.Min(tabHandler.m_tabs.Count - 1, num2));
			}
			int num3 = num2;
			while (!Object.op_Implicit((Object)(object)tabHandler.m_tabs[num2].m_button))
			{
				num2 += num;
				if (tabHandler.m_cycling)
				{
					if (num2 < 0)
					{
						num2 = tabHandler.m_tabs.Count - 1;
					}
					else if (num2 >= tabHandler.m_tabs.Count)
					{
						num2 = 0;
					}
					if (num2 == num3)
					{
						return;
					}
					continue;
				}
				return;
			}
			tabHandler.SetActiveTabViaButtonIfAvailable(num2);
		}

		private static bool EnsureUiReflection()
		{
			if (_uiReflectionInitialized)
			{
				return _uiReflectionAvailable;
			}
			_uiReflectionInitialized = true;
			try
			{
				_textMeshProType = AccessTools.TypeByName("TMPro.TextMeshProUGUI");
				if (_textMeshProType == null)
				{
					LogUiWarningOnce("Could not resolve TextMeshPro. Keybinds will still work.");
					return false;
				}
				_mouseKeyboardHintField = AccessTools.Field(typeof(UIInputHint), "m_mouseKeyboardHint");
				_tmpTextProperty = AccessTools.Property(_textMeshProType, "text");
				if (_mouseKeyboardHintField == null || _tmpTextProperty == null || !_tmpTextProperty.CanWrite)
				{
					LogUiWarningOnce("Valheim's key hint UI no longer matches the expected layout. Keybinds will still work.");
					return false;
				}
				_uiReflectionAvailable = true;
				return true;
			}
			catch (Exception ex)
			{
				LogUiWarningOnce("Could not initialize key hint support: " + ex.Message);
				return false;
			}
		}

		internal static void UpdateFilterHint(UIInputHint inputHint)
		{
			if (!EnsureUiReflection())
			{
				return;
			}
			try
			{
				GameObject gameObject = ((Component)inputHint).gameObject;
				if (((Object)gameObject).name != "SearchBarHint")
				{
					return;
				}
				Transform parent = gameObject.transform.parent;
				if ((Object)(object)parent == (Object)null || ((Object)parent).name != "SearchBar")
				{
					return;
				}
				object? value = _mouseKeyboardHintField.GetValue(inputHint);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Transform val2 = val.transform.Find("Text");
				if (!((Object)(object)val2 == (Object)null))
				{
					Component component = ((Component)val2).GetComponent(_textMeshProType);
					if (!((Object)(object)component == (Object)null))
					{
						_tmpTextProperty.SetValue(component, GetFilterHintText(), null);
					}
				}
			}
			catch (Exception ex)
			{
				LogUiWarningOnce("Could not update Filter key hint: " + ex.Message);
			}
		}

		private static void RefreshVisibleFilterHints()
		{
			if (!EnsureUiReflection())
			{
				return;
			}
			try
			{
				UIInputHint[] array = Object.FindObjectsByType<UIInputHint>((FindObjectsSortMode)0);
				UIInputHint[] array2 = array;
				foreach (UIInputHint inputHint in array2)
				{
					UpdateFilterHint(inputHint);
				}
			}
			catch (Exception ex)
			{
				LogUiWarningOnce("Could not refresh Filter key hint: " + ex.Message);
			}
		}

		internal static void UpdateTabHints(BuildUi buildUi)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)buildUi == (Object)null || PreviousTabKey == null || NextTabKey == null || !EnsureUiReflection())
			{
				return;
			}
			try
			{
				Transform transform = ((Component)buildUi).transform;
				Transform val = transform.Find("bar/SelectionWindow/TabContainer/InputHelp/MK hints/Left");
				Transform val2 = transform.Find("bar/SelectionWindow/TabContainer/InputHelp/MK hints/Right");
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
				{
					SetTabHintText(val, GetShortcutHintText(PreviousTabKey.Value));
					SetTabHintText(val2, GetShortcutHintText(NextTabKey.Value));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not update build tab key hints: " + ex.Message));
				}
			}
		}

		private static void RefreshVisibleTabHints()
		{
			if (!EnsureUiReflection())
			{
				return;
			}
			try
			{
				BuildUi[] array = Object.FindObjectsByType<BuildUi>((FindObjectsSortMode)0);
				BuildUi[] array2 = array;
				foreach (BuildUi buildUi in array2)
				{
					UpdateTabHints(buildUi);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not refresh build tab key hints: " + ex.Message));
				}
			}
		}

		private static void SetTabHintText(Transform hint, string text)
		{
			Component componentInChildren = ((Component)hint).GetComponentInChildren(_textMeshProType, true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				_tmpTextProperty.SetValue(componentInChildren, text, null);
			}
		}

		private unsafe static string GetShortcutHintText(KeyboardShortcut shortcut)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			KeyCode mainKey = ((KeyboardShortcut)(ref shortcut)).MainKey;
			if ((int)mainKey == 0)
			{
				return string.Empty;
			}
			string text = ((object)(*(KeyCode*)(&mainKey))/*cast due to .constrained prefix*/).ToString();
			if (text.StartsWith("Alpha", StringComparison.Ordinal) && text.Length == 6)
			{
				return text.Substring(5);
			}
			return text;
		}

		private static string GetFilterHintText()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return GetShortcutHintText(FilterKey.Value);
		}

		private static void LogUiWarningOnce(string message)
		{
			if (!_uiFailureLogged)
			{
				_uiFailureLogged = true;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)message);
				}
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "Awake")]
	internal static class BuildUiAwakePatch
	{
		private static void Postfix(BuildUi __instance)
		{
			if ((Object)(object)__instance.m_tabHandler != (Object)null)
			{
				__instance.m_tabHandler.m_keybaordInput = false;
				ManualLogSource log = FOffPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Disabled vanilla BuildUi Q/E tab handling.");
				}
			}
			FOffPlugin.UpdateTabHints(__instance);
		}
	}
	[HarmonyPatch(typeof(TabHandler), "Update")]
	internal static class TabHandlerUpdatePatch
	{
		private static void Prefix(TabHandler __instance)
		{
			if (!((Object)(object)((Component)__instance).GetComponentInParent<BuildUi>() == (Object)null))
			{
				__instance.m_keybaordInput = false;
			}
		}
	}
	[HarmonyPatch(typeof(BuildUi), "NavigationUpdate")]
	internal static class BuildUiNavigationUpdatePatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo methodInfo = AccessTools.Method(typeof(ZInput), "GetKey", new Type[2]
			{
				typeof(KeyCode),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(FOffPlugin), "IsFilterKeyPressed", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				ManualLogSource log = FOffPlugin.Log;
				if (log != null)
				{
					log.LogError((object)"Could not resolve Filter patch methods. BuildUi was left untouched.");
				}
				return list;
			}
			List<int> list2 = new List<int>();
			for (int i = 0; i <= list.Count - 3; i++)
			{
				if (LoadsFilterKey(list[i]) && LoadsTrue(list[i + 1]) && CallsMethod(list[i + 2], methodInfo))
				{
					list2.Add(i);
				}
			}
			if (list2.Count != 1)
			{
				ManualLogSource log2 = FOffPlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Expected one hardcoded BuildUi Filter-key " + $"pattern, but found {list2.Count}. " + "Vanilla behavior was preserved."));
				}
				return list;
			}
			int num = list2[0];
			CodeInstruction val = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
			for (int j = num; j < num + 3; j++)
			{
				val.labels.AddRange(list[j].labels);
				val.blocks.AddRange(list[j].blocks);
			}
			list.RemoveRange(num, 3);
			list.Insert(num, val);
			ManualLogSource log3 = FOffPlugin.Log;
			if (log3 != null)
			{
				log3.LogInfo((object)"Replaced the hardcoded F check in BuildUi.NavigationUpdate().");
			}
			return list;
		}

		private static void Postfix(BuildUi __instance)
		{
			FOffPlugin.UpdateBuildMenuTabKeys(__instance);
			FOffPlugin.UpdateTabHints(__instance);
		}

		private static bool LoadsFilterKey(CodeInstruction instruction)
		{
			return instruction.opcode == OpCodes.Ldc_I4_S && instruction.operand is sbyte b && b == 102;
		}

		private static bool LoadsTrue(CodeInstruction instruction)
		{
			return instruction.opcode == OpCodes.Ldc_I4_1;
		}

		private static bool CallsMethod(CodeInstruction instruction, MethodInfo method)
		{
			return (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo == method;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;
	}
	[HarmonyPatch(typeof(UIInputHint), "UpdateInputHints")]
	internal static class SearchBarInputHintPatch
	{
		private static void Postfix(UIInputHint __instance)
		{
			FOffPlugin.UpdateFilterHint(__instance);
		}
	}
}