Decompiled source of ControllerChords v0.3.0

ControllerChords.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ControllerChords;

[BepInPlugin("marc.valheim.controllerchords", "Controller Chords", "0.4.0")]
[BepInProcess("valheim.exe")]
public sealed class ControllerChordsPlugin : BaseUnityPlugin
{
	private sealed class ChordSlot
	{
		public int Index;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<ControllerButton> Modifier;

		public ConfigEntry<ControllerButton> Button;

		public ConfigEntry<KeyCode> TargetKey;

		public ConfigEntry<bool> BlockModifierDefaultAction;

		public ConfigEntry<int> VirtualPressFrames;

		public ConfigEntry<bool> HoldMode;
	}

	private sealed class SingleButtonSlot
	{
		public int Index;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<ControllerButton> Button;

		public ConfigEntry<KeyCode> TargetKey;

		public ConfigEntry<bool> HoldMode;

		public ConfigEntry<bool> BlockButtonDefaultAction;

		public ConfigEntry<int> VirtualPressFrames;
	}

	private sealed class ScrollSlot
	{
		public int Index;

		public ConfigEntry<bool> Enabled;

		public ConfigEntry<ControllerButton> Modifier;

		public ConfigEntry<ControllerButton> Button;

		public ConfigEntry<ScrollDirection> Direction;

		public ConfigEntry<float> ScrollAmount;

		public ConfigEntry<int> PulseFrames;

		public ConfigEntry<ScrollFireMode> FireMode;

		public ConfigEntry<bool> BlockTriggerDefaultAction;

		public ConfigEntry<bool> BlockModifierDefaultAction;
	}

	private struct ChordDefaults
	{
		public readonly bool Enabled;

		public readonly ControllerButton Modifier;

		public readonly ControllerButton Button;

		public readonly KeyCode TargetKey;

		public readonly bool BlockModifierDefaultAction;

		public readonly int VirtualPressFrames;

		public ChordDefaults(bool enabled, ControllerButton modifier, ControllerButton button, KeyCode targetKey, bool blockModifierDefaultAction, int virtualPressFrames)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Enabled = enabled;
			Modifier = modifier;
			Button = button;
			TargetKey = targetKey;
			BlockModifierDefaultAction = blockModifierDefaultAction;
			VirtualPressFrames = virtualPressFrames;
		}
	}

	private struct SingleButtonDefaults
	{
		public readonly bool Enabled;

		public readonly ControllerButton Button;

		public readonly KeyCode TargetKey;

		public readonly bool HoldMode;

		public readonly bool BlockButtonDefaultAction;

		public readonly int VirtualPressFrames;

		public SingleButtonDefaults(bool enabled, ControllerButton button, KeyCode targetKey, bool holdMode, bool blockButtonDefaultAction, int virtualPressFrames)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Enabled = enabled;
			Button = button;
			TargetKey = targetKey;
			HoldMode = holdMode;
			BlockButtonDefaultAction = blockButtonDefaultAction;
			VirtualPressFrames = virtualPressFrames;
		}
	}

	private struct ScrollDefaults
	{
		public readonly bool Enabled;

		public readonly ControllerButton Modifier;

		public readonly ControllerButton Button;

		public readonly ScrollDirection Direction;

		public readonly float ScrollAmount;

		public readonly int PulseFrames;

		public readonly ScrollFireMode FireMode;

		public readonly bool BlockTriggerDefaultAction;

		public readonly bool BlockModifierDefaultAction;

		public ScrollDefaults(bool enabled, ControllerButton modifier, ControllerButton button, ScrollDirection direction, float scrollAmount, int pulseFrames, ScrollFireMode fireMode, bool blockTriggerDefaultAction, bool blockModifierDefaultAction)
		{
			Enabled = enabled;
			Modifier = modifier;
			Button = button;
			Direction = direction;
			ScrollAmount = scrollAmount;
			PulseFrames = pulseFrames;
			FireMode = fireMode;
			BlockTriggerDefaultAction = blockTriggerDefaultAction;
			BlockModifierDefaultAction = blockModifierDefaultAction;
		}
	}

	public const string PluginGuid = "marc.valheim.controllerchords";

	public const string PluginName = "Controller Chords";

	public const string PluginVersion = "0.4.0";

	private const int SlotCount = 12;

	private const int SingleButtonSlotCount = 8;

	private const int ScrollSlotCount = 4;

	private const int MinimumVirtualPressFrames = 1;

	private const int MaximumVirtualPressFrames = 8;

	internal static ControllerChordsPlugin Instance;

	private ConfigEntry<bool> enabledConfig;

	private ConfigEntry<bool> allowInMenus;

	private ConfigEntry<bool> logChordEvents;

	private readonly List<ChordSlot> slots = new List<ChordSlot>(12);

	private readonly List<SingleButtonSlot> singleSlots = new List<SingleButtonSlot>(8);

	private readonly List<ScrollSlot> scrollSlots = new List<ScrollSlot>(4);

	private readonly Dictionary<KeyCode, int> virtualKeyFrames = new Dictionary<KeyCode, int>();

	private readonly Dictionary<string, int> virtualButtonFrames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

	private readonly Dictionary<string, int> blockedButtonFrames = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

	private readonly List<KeyCode> keyScratch = new List<KeyCode>();

	private readonly List<string> stringScratch = new List<string>();

	private readonly HashSet<int> heldChordActive = new HashSet<int>();

	private readonly HashSet<int> heldSingleActive = new HashSet<int>();

	private float virtualScrollValue;

	private int virtualScrollFrames;

	private Harmony harmony;

	private Type zInputType;

	private Func<string, bool> zGetButton;

	private Func<string, bool> zGetButtonDown;

	private PropertyInfo zVirtualKeyboardOpenProperty;

	private MethodInfo menuIsVisibleMethod;

	private MethodInfo inventoryGuiIsVisibleMethod;

	private MethodInfo storeGuiIsVisibleMethod;

	private MethodInfo textInputIsVisibleMethod;

	private MethodInfo consoleIsVisibleMethod;

	private bool zInputReady;

	private bool patchesInstalled;

	private int chordsFired;

	private void Awake()
	{
		Instance = this;
		BindConfig();
		if (!enabledConfig.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords is disabled by config. No Harmony patches were installed.");
			return;
		}
		ResolveZInput();
		ResolveMenuGuards();
		InstallPatches();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords 0.4.0 loaded. Supports single-button, double-button (chord), and scroll-wheel controller mappings.");
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
			harmony = null;
		}
		virtualKeyFrames.Clear();
		virtualButtonFrames.Clear();
		blockedButtonFrames.Clear();
		virtualScrollValue = 0f;
		virtualScrollFrames = 0;
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void Update()
	{
		if (enabledConfig.Value && zInputReady && (allowInMenus.Value || !IsMenuInputContext()))
		{
			ProcessSingleButtonSlots();
			ProcessScrollSlots();
			ProcessChordSlots();
		}
	}

	private unsafe void ProcessSingleButtonSlots()
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < singleSlots.Count; i++)
		{
			SingleButtonSlot singleButtonSlot = singleSlots[i];
			if (singleButtonSlot == null || !singleButtonSlot.Enabled.Value)
			{
				continue;
			}
			ControllerButton value = singleButtonSlot.Button.Value;
			if (value == ControllerButton.None || (int)singleButtonSlot.TargetKey.Value == 0)
			{
				continue;
			}
			string text = ToZInputButtonName(value);
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			KeyCode value2 = singleButtonSlot.TargetKey.Value;
			if (singleButtonSlot.HoldMode.Value)
			{
				if (SafeGetZButton(text))
				{
					SetMaxFrameCount(virtualKeyFrames, value2, 2);
					if (singleButtonSlot.BlockButtonDefaultAction.Value)
					{
						SetMaxFrameCount(blockedButtonFrames, text, 2);
					}
					if (!heldSingleActive.Contains(singleButtonSlot.Index))
					{
						heldSingleActive.Add(singleButtonSlot.Index);
						if (logChordEvents.Value)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " (" + text + " HOLD -> " + ((object)(*(KeyCode*)(&value2))/*cast due to .constrained prefix*/).ToString() + ") ENGAGED."));
						}
					}
				}
				else if (heldSingleActive.Contains(singleButtonSlot.Index))
				{
					heldSingleActive.Remove(singleButtonSlot.Index);
					if (logChordEvents.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " HOLD released."));
					}
				}
			}
			else if (SafeGetZButtonDown(text))
			{
				int num = singleButtonSlot.VirtualPressFrames.Value;
				if (num < 1 || num > 8)
				{
					num = 2;
				}
				SetMaxFrameCount(virtualKeyFrames, value2, num);
				if (singleButtonSlot.BlockButtonDefaultAction.Value)
				{
					SetMaxFrameCount(blockedButtonFrames, text, num);
				}
				if (logChordEvents.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: single-button " + singleButtonSlot.Index + " (" + text + " -> " + ((object)(*(KeyCode*)(&value2))/*cast due to .constrained prefix*/).ToString() + ") FIRED."));
				}
				chordsFired++;
			}
		}
	}

	private void ProcessScrollSlots()
	{
		for (int i = 0; i < scrollSlots.Count; i++)
		{
			ScrollSlot scrollSlot = scrollSlots[i];
			if (scrollSlot == null || !scrollSlot.Enabled.Value)
			{
				continue;
			}
			ControllerButton value = scrollSlot.Button.Value;
			if (value == ControllerButton.None)
			{
				continue;
			}
			string text = ToZInputButtonName(value);
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			ControllerButton value2 = scrollSlot.Modifier.Value;
			string text2 = string.Empty;
			if (value2 != ControllerButton.None)
			{
				text2 = ToZInputButtonName(value2);
				if (string.IsNullOrEmpty(text2) || !SafeGetZButton(text2))
				{
					continue;
				}
			}
			if ((scrollSlot.FireMode.Value != ScrollFireMode.WhileHeld) ? SafeGetZButtonDown(text) : SafeGetZButton(text))
			{
				EmitScroll(scrollSlot.ScrollAmount.Value, scrollSlot.Direction.Value, scrollSlot.PulseFrames.Value);
				if (scrollSlot.BlockTriggerDefaultAction.Value)
				{
					SetMaxFrameCount(blockedButtonFrames, text, Math.Max(2, scrollSlot.PulseFrames.Value));
				}
				if (value2 != ControllerButton.None && scrollSlot.BlockModifierDefaultAction.Value)
				{
					SetMaxFrameCount(blockedButtonFrames, text2, Math.Max(2, scrollSlot.PulseFrames.Value));
				}
				if (logChordEvents.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: scroll " + scrollSlot.Index + " (" + text + " -> scroll " + scrollSlot.Direction.Value.ToString() + " " + virtualScrollValue.ToString("0.###") + ") FIRED."));
				}
				chordsFired++;
			}
		}
	}

	private void ProcessChordSlots()
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		for (int i = 0; i < slots.Count; i++)
		{
			ChordSlot chordSlot = slots[i];
			if (chordSlot == null || !chordSlot.Enabled.Value)
			{
				continue;
			}
			ControllerButton value = chordSlot.Modifier.Value;
			if (!IsSupportedModifier(value))
			{
				continue;
			}
			ControllerButton value2 = chordSlot.Button.Value;
			if (value2 == ControllerButton.None || value2 == value || (int)chordSlot.TargetKey.Value == 0)
			{
				continue;
			}
			string text = ToZInputButtonName(value);
			string text2 = ToZInputButtonName(value2);
			if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2))
			{
				continue;
			}
			bool flag = SafeGetZButton(text);
			if (chordSlot.HoldMode.Value)
			{
				bool flag2 = SafeGetZButton(text2);
				if (flag && flag2)
				{
					HoldChord(chordSlot, text);
				}
				else if (heldChordActive.Contains(chordSlot.Index))
				{
					heldChordActive.Remove(chordSlot.Index);
					if (logChordEvents.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + chordSlot.Index + " HOLD released."));
					}
				}
			}
			else
			{
				bool flag3 = SafeGetZButtonDown(text2);
				if (flag && flag3)
				{
					FireChord(chordSlot, text);
				}
			}
		}
	}

	private void LateUpdate()
	{
		AgeKeyFrames();
		AgeStringFrames(virtualButtonFrames);
		AgeStringFrames(blockedButtonFrames);
		AgeScroll();
	}

	private void BindConfig()
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
		enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. If false at startup, Controller Chords installs no active input patches.");
		allowInMenus = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowInMenus", false, "Allow controller mappings while Valheim menus/text input are open.");
		logChordEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogChordEvents", false, "Log each fired mapping to BepInEx/LogOutput.log.");
		for (int i = 1; i <= 8; i++)
		{
			SingleButtonDefaults singleButtonDefaults = GetSingleButtonDefaults(i);
			string text = "SingleButton " + i.ToString("00");
			SingleButtonSlot item = new SingleButtonSlot
			{
				Index = i,
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "Enabled", singleButtonDefaults.Enabled, "Enable this single-button mapping."),
				Button = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text, "Button", singleButtonDefaults.Button, "Controller button that triggers this action."),
				TargetKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>(text, "TargetKey", singleButtonDefaults.TargetKey, "Keyboard KeyCode exposed virtually to other mods."),
				HoldMode = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "HoldMode", singleButtonDefaults.HoldMode, "When true, the target key is held DOWN for as long as you hold the controller button, and released when you let go. Use this for hold-to-act mods (e.g. FrostJack Zoom's hold-to-zoom key). When false, sends a brief tap."),
				BlockButtonDefaultAction = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "BlockButtonDefaultAction", singleButtonDefaults.BlockButtonDefaultAction, "Temporarily suppress this controller button's normal Valheim action while the mapping is active. Defaults false."),
				VirtualPressFrames = ((BaseUnityPlugin)this).Config.Bind<int>(text, "VirtualPressFrames", singleButtonDefaults.VirtualPressFrames, "Number of frames the virtual key remains visible to input queries (tap mode only).")
			};
			singleSlots.Add(item);
		}
		for (int j = 1; j <= 4; j++)
		{
			ScrollDefaults scrollDefaults = GetScrollDefaults(j);
			string text2 = "Scroll " + j.ToString("00");
			ScrollSlot item2 = new ScrollSlot
			{
				Index = j,
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "Enabled", scrollDefaults.Enabled, "Enable this scroll-wheel mapping."),
				Modifier = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text2, "Modifier", scrollDefaults.Modifier, "Optional held gate button. When set, the scroll only fires while this button is held. Set to None for no gate."),
				Button = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text2, "Button", scrollDefaults.Button, "Controller button that emits a scroll notch."),
				Direction = ((BaseUnityPlugin)this).Config.Bind<ScrollDirection>(text2, "Direction", scrollDefaults.Direction, "In = scroll up (usually zoom in). Out = scroll down (usually zoom out). Swap these if your zoom feels reversed."),
				ScrollAmount = ((BaseUnityPlugin)this).Config.Bind<float>(text2, "ScrollAmount", scrollDefaults.ScrollAmount, "Magnitude of the emitted scroll value. Most zoom mods only care about the sign, so 1.0 is fine. Lower it if a single press zooms too far."),
				PulseFrames = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "PulseFrames", scrollDefaults.PulseFrames, "How many frames the scroll value is emitted per press. 1 = one notch per press (recommended). Raise to 2 only if presses are being missed; note that may step two levels at once."),
				FireMode = ((BaseUnityPlugin)this).Config.Bind<ScrollFireMode>(text2, "FireMode", scrollDefaults.FireMode, "OnPress = one scroll notch each time you press the button (recommended for stepped zoom). WhileHeld = emit continuously while the button is held."),
				BlockTriggerDefaultAction = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "BlockTriggerDefaultAction", scrollDefaults.BlockTriggerDefaultAction, "Suppress the trigger button's normal Valheim action while scrolling (e.g. stop the D-pad from also moving the hotbar). Defaults true."),
				BlockModifierDefaultAction = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "BlockModifierDefaultAction", scrollDefaults.BlockModifierDefaultAction, "Suppress the gate button's normal Valheim action while scrolling. Defaults false.")
			};
			scrollSlots.Add(item2);
		}
		for (int k = 1; k <= 12; k++)
		{
			ChordDefaults defaults = GetDefaults(k);
			string text3 = "Chord " + k.ToString("00");
			ChordSlot item3 = new ChordSlot
			{
				Index = k,
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text3, "Enabled", defaults.Enabled, "Enable this controller chord slot."),
				Modifier = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text3, "Modifier", defaults.Modifier, "Held modifier button. Supported modifiers are RB, LB, RT, and LT."),
				Button = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text3, "Button", defaults.Button, "Trigger button that fires on button-down while the modifier is held."),
				TargetKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>(text3, "TargetKey", defaults.TargetKey, "Keyboard KeyCode exposed virtually to other mods."),
				BlockModifierDefaultAction = ((BaseUnityPlugin)this).Config.Bind<bool>(text3, "BlockModifierDefaultAction", defaults.BlockModifierDefaultAction, "Temporarily suppress this modifier's ZInput action when the chord fires. Defaults false."),
				VirtualPressFrames = ((BaseUnityPlugin)this).Config.Bind<int>(text3, "VirtualPressFrames", defaults.VirtualPressFrames, "Number of frames the virtual key remains visible to input queries (tap mode only)."),
				HoldMode = ((BaseUnityPlugin)this).Config.Bind<bool>(text3, "HoldMode", false, "When true, the target key is held DOWN for as long as you hold the chord (modifier + trigger), and released when you let go. Use this for press-and-hold mods like climbing. When false (default), the chord sends a brief tap suitable for toggle hotkeys.")
			};
			slots.Add(item3);
		}
	}

	private static SingleButtonDefaults GetSingleButtonDefaults(int index)
	{
		return index switch
		{
			1 => new SingleButtonDefaults(enabled: true, ControllerButton.LB, (KeyCode)99, holdMode: true, blockButtonDefaultAction: false, 2), 
			2 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)101, holdMode: false, blockButtonDefaultAction: false, 2), 
			3 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)103, holdMode: false, blockButtonDefaultAction: false, 2), 
			4 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
			5 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
			6 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
			7 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
			8 => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
			_ => new SingleButtonDefaults(enabled: false, ControllerButton.None, (KeyCode)0, holdMode: false, blockButtonDefaultAction: false, 2), 
		};
	}

	private static ScrollDefaults GetScrollDefaults(int index)
	{
		return index switch
		{
			1 => new ScrollDefaults(enabled: true, ControllerButton.LB, ControllerButton.DPadUp, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 
			2 => new ScrollDefaults(enabled: true, ControllerButton.LB, ControllerButton.DPadDown, ScrollDirection.Out, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 
			3 => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 
			4 => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 
			_ => new ScrollDefaults(enabled: false, ControllerButton.None, ControllerButton.None, ScrollDirection.In, 1f, 1, ScrollFireMode.OnPress, blockTriggerDefaultAction: true, blockModifierDefaultAction: false), 
		};
	}

	private static ChordDefaults GetDefaults(int index)
	{
		return index switch
		{
			1 => new ChordDefaults(enabled: true, ControllerButton.RB, ControllerButton.A, (KeyCode)306, blockModifierDefaultAction: false, 2), 
			2 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.B, (KeyCode)308, blockModifierDefaultAction: false, 2), 
			3 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.X, (KeyCode)103, blockModifierDefaultAction: false, 2), 
			4 => new ChordDefaults(enabled: false, ControllerButton.RB, ControllerButton.Y, (KeyCode)104, blockModifierDefaultAction: false, 2), 
			5 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.A, (KeyCode)106, blockModifierDefaultAction: false, 2), 
			6 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.B, (KeyCode)107, blockModifierDefaultAction: false, 2), 
			7 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.X, (KeyCode)108, blockModifierDefaultAction: false, 2), 
			8 => new ChordDefaults(enabled: false, ControllerButton.LB, ControllerButton.Y, (KeyCode)59, blockModifierDefaultAction: false, 2), 
			9 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.A, (KeyCode)117, blockModifierDefaultAction: false, 2), 
			10 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.B, (KeyCode)105, blockModifierDefaultAction: false, 2), 
			11 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.X, (KeyCode)111, blockModifierDefaultAction: false, 2), 
			12 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.Y, (KeyCode)112, blockModifierDefaultAction: false, 2), 
			_ => new ChordDefaults(enabled: false, ControllerButton.None, ControllerButton.None, (KeyCode)0, blockModifierDefaultAction: false, 2), 
		};
	}

	private void EmitScroll(float amount, ScrollDirection direction, int frames)
	{
		float num = Mathf.Abs(amount);
		if (num <= 0f)
		{
			num = 1f;
		}
		virtualScrollValue = ((direction == ScrollDirection.Out) ? (0f - num) : num);
		if (frames < 1)
		{
			frames = 1;
		}
		else if (frames > 8)
		{
			frames = 8;
		}
		if (virtualScrollFrames < frames)
		{
			virtualScrollFrames = frames;
		}
	}

	private unsafe void FireChord(ChordSlot slot, string modifierName)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		int num = slot.VirtualPressFrames.Value;
		if (num < 1 || num > 8)
		{
			num = 2;
		}
		KeyCode value = slot.TargetKey.Value;
		SetMaxFrameCount(virtualKeyFrames, value, num);
		if (slot.BlockModifierDefaultAction.Value)
		{
			string key = ToZInputButtonName(slot.Modifier.Value);
			SetMaxFrameCount(blockedButtonFrames, key, num);
		}
		if (logChordEvents.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " (" + modifierName + " + trigger -> " + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + ") FIRED."));
		}
		chordsFired++;
	}

	private void HoldChord(ChordSlot slot, string modifierName)
	{
		//IL_0049: 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)
		if (heldChordActive.Contains(slot.Index))
		{
			SetMaxFrameCount(virtualKeyFrames, slot.TargetKey.Value, 2);
			return;
		}
		heldChordActive.Add(slot.Index);
		SetMaxFrameCount(virtualKeyFrames, slot.TargetKey.Value, 2);
		if (slot.BlockModifierDefaultAction.Value)
		{
			string key = ToZInputButtonName(slot.Modifier.Value);
			SetMaxFrameCount(blockedButtonFrames, key, 2);
		}
		if (logChordEvents.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " (" + modifierName + " + trigger HOLD) ENGAGED."));
		}
		chordsFired++;
	}

	private bool IsVirtualKeyActive(KeyCode key)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return virtualKeyFrames.ContainsKey(key);
	}

	private bool IsVirtualButtonActive(string buttonName)
	{
		return virtualButtonFrames.ContainsKey(buttonName);
	}

	private bool IsButtonBlocked(string buttonName)
	{
		return blockedButtonFrames.ContainsKey(buttonName);
	}

	private bool SafeGetZButton(string buttonName)
	{
		if (!zInputReady || string.IsNullOrEmpty(buttonName) || zGetButton == null)
		{
			return false;
		}
		try
		{
			return zGetButton(buttonName);
		}
		catch
		{
			return false;
		}
	}

	private bool SafeGetZButtonDown(string buttonName)
	{
		if (!zInputReady || string.IsNullOrEmpty(buttonName) || zGetButtonDown == null)
		{
			return false;
		}
		try
		{
			return zGetButtonDown(buttonName);
		}
		catch
		{
			return false;
		}
	}

	private void ResolveZInput()
	{
		zInputReady = false;
		try
		{
			zInputType = Type.GetType("ZInput, Assembly-CSharp", throwOnError: false);
			if (zInputType == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: could not find ZInput type.");
				return;
			}
			MethodInfo method = zInputType.GetMethod("GetButton", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
			MethodInfo method2 = zInputType.GetMethod("GetButtonDown", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
			if (method != null)
			{
				zGetButton = (Func<string, bool>)Delegate.CreateDelegate(typeof(Func<string, bool>), method);
			}
			if (method2 != null)
			{
				zGetButtonDown = (Func<string, bool>)Delegate.CreateDelegate(typeof(Func<string, bool>), method2);
			}
			zVirtualKeyboardOpenProperty = zInputType.GetProperty("IsKeyboardOpen", BindingFlags.Static | BindingFlags.Public);
			if (zGetButton != null && zGetButtonDown != null)
			{
				zInputReady = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords: ZInput initialized successfully.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: could not initialize ZInput methods.");
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: exception during ZInput resolution: " + ex.Message));
		}
	}

	private void ResolveMenuGuards()
	{
		try
		{
			Type type = Type.GetType("GUI, Assembly-CSharp", throwOnError: false);
			if (type != null)
			{
				menuIsVisibleMethod = type.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public);
			}
			Type type2 = Type.GetType("InventoryGui, Assembly-CSharp", throwOnError: false);
			if (type2 != null)
			{
				inventoryGuiIsVisibleMethod = type2.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public);
			}
			Type type3 = Type.GetType("StoreGui, Assembly-CSharp", throwOnError: false);
			if (type3 != null)
			{
				storeGuiIsVisibleMethod = type3.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public);
			}
			Type type4 = Type.GetType("TextInput, Assembly-CSharp", throwOnError: false);
			if (type4 != null)
			{
				textInputIsVisibleMethod = type4.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public);
			}
			Type type5 = Type.GetType("Console, Assembly-CSharp", throwOnError: false);
			if (type5 != null)
			{
				consoleIsVisibleMethod = type5.GetMethod("IsVisible", BindingFlags.Static | BindingFlags.Public);
			}
		}
		catch
		{
		}
	}

	private bool IsMenuInputContext()
	{
		if (menuIsVisibleMethod != null)
		{
			try
			{
				if ((bool)menuIsVisibleMethod.Invoke(null, null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		if (inventoryGuiIsVisibleMethod != null)
		{
			try
			{
				if ((bool)inventoryGuiIsVisibleMethod.Invoke(null, null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		if (storeGuiIsVisibleMethod != null)
		{
			try
			{
				if ((bool)storeGuiIsVisibleMethod.Invoke(null, null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		if (textInputIsVisibleMethod != null)
		{
			try
			{
				if ((bool)textInputIsVisibleMethod.Invoke(null, null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		if (consoleIsVisibleMethod != null)
		{
			try
			{
				if ((bool)consoleIsVisibleMethod.Invoke(null, null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		if (zVirtualKeyboardOpenProperty != null)
		{
			try
			{
				if ((bool)zVirtualKeyboardOpenProperty.GetValue(null))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		return false;
	}

	private void InstallPatches()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Expected O, but got Unknown
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Expected O, but got Unknown
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Expected O, but got Unknown
		//IL_025c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0269: Expected O, but got Unknown
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Expected O, but got Unknown
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Expected O, but got Unknown
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Expected O, but got Unknown
		if (patchesInstalled)
		{
			return;
		}
		harmony = new Harmony("marc.valheim.controllerchords");
		try
		{
			Type type = Type.GetType("UnityEngine.Input, UnityEngine.InputLegacyModule", throwOnError: false);
			if (type != null)
			{
				MethodInfo method = type.GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null);
				if (method != null)
				{
					harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputKeyPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				MethodInfo method2 = type.GetMethod("GetAxis", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				if (method2 != null)
				{
					harmony.Patch((MethodBase)method2, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputGetAxisPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				MethodInfo method3 = type.GetMethod("GetAxisRaw", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				if (method3 != null)
				{
					harmony.Patch((MethodBase)method3, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputGetAxisRawPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				PropertyInfo property = type.GetProperty("mouseScrollDelta", BindingFlags.Static | BindingFlags.Public);
				if (property != null)
				{
					MethodInfo getMethod = property.GetGetMethod();
					if (getMethod != null)
					{
						harmony.Patch((MethodBase)getMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("InputMouseScrollDeltaPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
			}
		}
		catch
		{
		}
		try
		{
			Type type2 = Type.GetType("ZInput, Assembly-CSharp", throwOnError: false);
			if (type2 != null)
			{
				MethodInfo method4 = type2.GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null);
				if (method4 != null)
				{
					harmony.Patch((MethodBase)method4, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputKeyPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				MethodInfo method5 = type2.GetMethod("GetButton", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				if (method5 != null)
				{
					harmony.Patch((MethodBase)method5, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputButtonPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				MethodInfo method6 = type2.GetMethod("GetMouseScrollWheel", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method6 != null && method6.ReturnType == typeof(float))
				{
					harmony.Patch((MethodBase)method6, (HarmonyMethod)null, new HarmonyMethod(typeof(ControllerChordsPlugin).GetMethod("ZInputMouseScrollWheelPostfix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}
		catch
		{
		}
		patchesInstalled = true;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords: Harmony patches installed.");
	}

	private void AgeKeyFrames()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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_0086: Unknown result type (might be due to invalid IL or missing references)
		if (virtualKeyFrames.Count == 0)
		{
			return;
		}
		keyScratch.Clear();
		foreach (KeyValuePair<KeyCode, int> virtualKeyFrame in virtualKeyFrames)
		{
			keyScratch.Add(virtualKeyFrame.Key);
		}
		for (int i = 0; i < keyScratch.Count; i++)
		{
			KeyCode key = keyScratch[i];
			int num = virtualKeyFrames[key] - 1;
			if (num <= 0)
			{
				virtualKeyFrames.Remove(key);
			}
			else
			{
				virtualKeyFrames[key] = num;
			}
		}
	}

	private void AgeStringFrames(Dictionary<string, int> framesByName)
	{
		if (framesByName.Count == 0)
		{
			return;
		}
		stringScratch.Clear();
		foreach (KeyValuePair<string, int> item in framesByName)
		{
			stringScratch.Add(item.Key);
		}
		for (int i = 0; i < stringScratch.Count; i++)
		{
			string key = stringScratch[i];
			int num = framesByName[key] - 1;
			if (num <= 0)
			{
				framesByName.Remove(key);
			}
			else
			{
				framesByName[key] = num;
			}
		}
	}

	private void AgeScroll()
	{
		if (virtualScrollFrames > 0)
		{
			virtualScrollFrames--;
			if (virtualScrollFrames <= 0)
			{
				virtualScrollFrames = 0;
				virtualScrollValue = 0f;
			}
		}
	}

	private static void SetMaxFrameCount(Dictionary<KeyCode, int> dictionary, KeyCode key, int frames)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		if (!dictionary.TryGetValue(key, out var value) || value < frames)
		{
			dictionary[key] = frames;
		}
	}

	private static void SetMaxFrameCount(Dictionary<string, int> dictionary, string key, int frames)
	{
		if (!string.IsNullOrEmpty(key) && (!dictionary.TryGetValue(key, out var value) || value < frames))
		{
			dictionary[key] = frames;
		}
	}

	private static bool IsSupportedModifier(ControllerButton button)
	{
		if (button != ControllerButton.RB && button != ControllerButton.LB && button != ControllerButton.RT)
		{
			return button == ControllerButton.LT;
		}
		return true;
	}

	private static string ToZInputButtonName(ControllerButton button)
	{
		return button switch
		{
			ControllerButton.A => "JoyButtonA", 
			ControllerButton.B => "JoyButtonB", 
			ControllerButton.X => "JoyButtonX", 
			ControllerButton.Y => "JoyButtonY", 
			ControllerButton.DPadLeft => "JoyDPadLeft", 
			ControllerButton.DPadRight => "JoyDPadRight", 
			ControllerButton.DPadUp => "JoyDPadUp", 
			ControllerButton.DPadDown => "JoyDPadDown", 
			ControllerButton.LB => "JoyLBumper", 
			ControllerButton.RB => "JoyRBumper", 
			ControllerButton.LT => "JoyLTrigger", 
			ControllerButton.RT => "JoyRTrigger", 
			ControllerButton.LeftStick => "JoyLStick", 
			ControllerButton.RightStick => "JoyRStick", 
			ControllerButton.Back => "JoyBack", 
			ControllerButton.Start => "JoyStart", 
			_ => string.Empty, 
		};
	}

	private static void InputKeyPostfix(KeyCode __0, ref bool __result)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		ControllerChordsPlugin instance = Instance;
		if (!__result && !((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.IsVirtualKeyActive(__0))
		{
			__result = true;
		}
	}

	private static void ZInputKeyPostfix(KeyCode __0, ref bool __result)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		ControllerChordsPlugin instance = Instance;
		if (!__result && !((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.IsVirtualKeyActive(__0))
		{
			__result = true;
		}
	}

	private static void ZInputButtonPostfix(string __0, ref bool __result)
	{
		ControllerChordsPlugin instance = Instance;
		if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled)
		{
			if (instance.IsButtonBlocked(__0))
			{
				__result = false;
			}
			else if (!__result && instance.IsVirtualButtonActive(__0))
			{
				__result = true;
			}
		}
	}

	private static void InputGetAxisPostfix(string __0, ref float __result)
	{
		ControllerChordsPlugin instance = Instance;
		if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0 && string.Equals(__0, "Mouse ScrollWheel", StringComparison.OrdinalIgnoreCase))
		{
			__result += instance.virtualScrollValue;
		}
	}

	private static void InputGetAxisRawPostfix(string __0, ref float __result)
	{
		ControllerChordsPlugin instance = Instance;
		if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0 && string.Equals(__0, "Mouse ScrollWheel", StringComparison.OrdinalIgnoreCase))
		{
			__result += instance.virtualScrollValue;
		}
	}

	private static void InputMouseScrollDeltaPostfix(ref Vector2 __result)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		ControllerChordsPlugin instance = Instance;
		if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0)
		{
			Vector2 val = __result;
			val.y += ((instance.virtualScrollValue > 0f) ? 1f : (-1f));
			__result = val;
		}
	}

	private static void ZInputMouseScrollWheelPostfix(ref float __result)
	{
		ControllerChordsPlugin instance = Instance;
		if (!((Object)(object)instance == (Object)null) && instance.patchesInstalled && instance.virtualScrollFrames > 0)
		{
			__result += instance.virtualScrollValue;
		}
	}
}
public enum ControllerButton
{
	None,
	A,
	B,
	X,
	Y,
	DPadLeft,
	DPadRight,
	DPadUp,
	DPadDown,
	LB,
	RB,
	LT,
	RT,
	LeftStick,
	RightStick,
	Back,
	Start
}
public enum ScrollDirection
{
	In,
	Out
}
public enum ScrollFireMode
{
	OnPress,
	WhileHeld
}