Decompiled source of ControllerChords v0.2.0

ControllerChords.dll

Decompiled 11 hours 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[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.2.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 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_0017: 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)
			Enabled = enabled;
			Modifier = modifier;
			Button = button;
			TargetKey = targetKey;
			BlockModifierDefaultAction = blockModifierDefaultAction;
			VirtualPressFrames = virtualPressFrames;
		}
	}

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

	public const string PluginName = "Controller Chords";

	public const string PluginVersion = "0.2.0";

	private const int SlotCount = 12;

	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 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 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.2.0 loaded. Chord detection uses Valheim ZInput when available.");
	}

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

	private void Update()
	{
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Invalid comparison between Unknown and I4
		if (!enabledConfig.Value || !zInputReady || (!allowInMenus.Value && IsMenuInputContext()))
		{
			return;
		}
		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);
	}

	private void BindConfig()
	{
		//IL_010a: 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 chords while Valheim menus/text input are open.");
		logChordEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogChordEvents", false, "Log each fired chord to BepInEx/LogOutput.log.");
		for (int i = 1; i <= 12; i++)
		{
			ChordDefaults defaults = GetDefaults(i);
			string text = "Chord " + i.ToString("00");
			ChordSlot item = new ChordSlot
			{
				Index = i,
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "Enabled", defaults.Enabled, "Enable this controller chord slot."),
				Modifier = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text, "Modifier", defaults.Modifier, "Held modifier button. Supported modifiers are RB, LB, RT, and LT."),
				Button = ((BaseUnityPlugin)this).Config.Bind<ControllerButton>(text, "Button", defaults.Button, "Trigger button that fires on button-down while the modifier is held."),
				TargetKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>(text, "TargetKey", defaults.TargetKey, "Keyboard KeyCode exposed virtually to other mods."),
				BlockModifierDefaultAction = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "BlockModifierDefaultAction", defaults.BlockModifierDefaultAction, "Temporarily suppress this modifier's ZInput action when the chord fires. Defaults false."),
				VirtualPressFrames = ((BaseUnityPlugin)this).Config.Bind<int>(text, "VirtualPressFrames", defaults.VirtualPressFrames, "Number of frames the virtual key remains visible to input queries (tap mode only)."),
				HoldMode = ((BaseUnityPlugin)this).Config.Bind<bool>(text, "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(item);
		}
	}

	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.DPadUp, (KeyCode)287, blockModifierDefaultAction: false, 2), 
			10 => new ChordDefaults(enabled: false, ControllerButton.RT, ControllerButton.DPadDown, (KeyCode)288, blockModifierDefaultAction: false, 2), 
			11 => new ChordDefaults(enabled: false, ControllerButton.LT, ControllerButton.DPadLeft, (KeyCode)289, blockModifierDefaultAction: false, 2), 
			_ => new ChordDefaults(enabled: false, ControllerButton.LT, ControllerButton.DPadRight, (KeyCode)290, blockModifierDefaultAction: false, 2), 
		};
	}

	private void ResolveZInput()
	{
		zInputType = FindType("ZInput");
		if (zInputType == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: ZInput type was not found. Controller chord detection is disabled, but Unity key patches can still load safely.");
			return;
		}
		zGetButton = CreateStringBoolDelegate(zInputType, "GetButton");
		zGetButtonDown = CreateStringBoolDelegate(zInputType, "GetButtonDown");
		zVirtualKeyboardOpenProperty = zInputType.GetProperty("VirtualKeyboardOpen", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		zInputReady = zGetButton != null && zGetButtonDown != null;
		if (!zInputReady)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Controller Chords: ZInput.GetButton/GetButtonDown were not found. Controller chord detection is disabled.");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Controller Chords: ZInput button reading resolved OK (GetButton/GetButtonDown bound). Chord detection active.");
		}
	}

	private void ResolveMenuGuards()
	{
		menuIsVisibleMethod = FindStaticBoolMethod("Menu", "IsVisible");
		inventoryGuiIsVisibleMethod = FindStaticBoolMethod("InventoryGui", "IsVisible");
		storeGuiIsVisibleMethod = FindStaticBoolMethod("StoreGui", "IsVisible");
		textInputIsVisibleMethod = FindStaticBoolMethod("TextInput", "IsVisible");
		consoleIsVisibleMethod = FindStaticBoolMethod("Console", "IsVisible");
	}

	private void InstallPatches()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		harmony = new Harmony("marc.valheim.controllerchords");
		TryPatchPostfix(typeof(Input).GetMethod("GetKeyDown", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null), "InputKeyPostfix", "UnityEngine.Input.GetKeyDown(KeyCode)");
		TryPatchPostfix(typeof(Input).GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(KeyCode) }, null), "InputKeyPostfix", "UnityEngine.Input.GetKey(KeyCode)");
		if (zInputType != null)
		{
			TryPatchPostfix(zInputType.GetMethod("GetKeyDown", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(KeyCode),
				typeof(bool)
			}, null), "ZInputKeyPostfix", "ZInput.GetKeyDown(KeyCode,bool)");
			TryPatchPostfix(zInputType.GetMethod("GetKey", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(KeyCode),
				typeof(bool)
			}, null), "ZInputKeyPostfix", "ZInput.GetKey(KeyCode,bool)");
			TryPatchPostfix(zInputType.GetMethod("GetButtonDown", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null), "ZInputButtonPostfix", "ZInput.GetButtonDown(string)");
			TryPatchPostfix(zInputType.GetMethod("GetButton", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null), "ZInputButtonPostfix", "ZInput.GetButton(string)");
		}
	}

	private void TryPatchPostfix(MethodInfo target, string postfixName, string label)
	{
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		if (target == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: could not find " + label + "; this compatibility patch is disabled."));
			return;
		}
		try
		{
			MethodInfo method = typeof(ControllerChordsPlugin).GetMethod(postfixName, BindingFlags.Static | BindingFlags.NonPublic);
			harmony.Patch((MethodBase)target, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			patchesInstalled = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: patched " + label + "."));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: failed to patch " + label + ": " + ex.Message));
		}
	}

	private unsafe void HoldChord(ChordSlot slot, string modifierName)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: 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)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		KeyCode value = slot.TargetKey.Value;
		SetMaxFrameCount(virtualKeyFrames, value, 2);
		AddVirtualButtonName(value, 2);
		if (slot.BlockModifierDefaultAction.Value && !string.IsNullOrEmpty(modifierName))
		{
			SetMaxFrameCount(blockedButtonFrames, modifierName, 2);
		}
		if (logChordEvents.Value && !heldChordActive.Contains(slot.Index))
		{
			heldChordActive.Add(slot.Index);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " HOLD started " + slot.Modifier.Value.ToString() + "+" + slot.Button.Value.ToString() + " -> " + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + " (held until released)."));
		}
	}

	private unsafe void FireChord(ChordSlot slot, string modifierName)
	{
		//IL_001a: 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_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		int frames = Mathf.Clamp(slot.VirtualPressFrames.Value, 1, 8);
		KeyCode value = slot.TargetKey.Value;
		SetMaxFrameCount(virtualKeyFrames, value, frames);
		AddVirtualButtonName(value, frames);
		if (slot.BlockModifierDefaultAction.Value && !string.IsNullOrEmpty(modifierName))
		{
			SetMaxFrameCount(blockedButtonFrames, modifierName, frames);
		}
		chordsFired++;
		if (logChordEvents.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Controller Chords: chord " + slot.Index + " fired " + slot.Modifier.Value.ToString() + "+" + slot.Button.Value.ToString() + " -> " + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + " for " + frames + " frame(s)."));
		}
	}

	private unsafe void AddVirtualButtonName(KeyCode key, int frames)
	{
		string text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
		SetMaxFrameCount(virtualButtonFrames, text, frames);
		SetMaxFrameCount(virtualButtonFrames, text.ToLowerInvariant(), frames);
	}

	private bool IsVirtualKeyActive(KeyCode key)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		int value;
		return enabledConfig.Value && virtualKeyFrames.TryGetValue(key, out value) && value > 0;
	}

	private bool IsVirtualButtonActive(string name)
	{
		if (string.IsNullOrEmpty(name) || !enabledConfig.Value)
		{
			return false;
		}
		int value;
		return virtualButtonFrames.TryGetValue(name, out value) && value > 0;
	}

	private bool IsButtonBlocked(string name)
	{
		if (string.IsNullOrEmpty(name) || !enabledConfig.Value)
		{
			return false;
		}
		int value;
		return blockedButtonFrames.TryGetValue(name, out value) && value > 0;
	}

	private bool SafeGetZButton(string name)
	{
		try
		{
			return zGetButton != null && zGetButton(name);
		}
		catch (Exception ex)
		{
			zInputReady = false;
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: ZInput.GetButton failed and chord detection was disabled: " + ex.Message));
			return false;
		}
	}

	private bool SafeGetZButtonDown(string name)
	{
		try
		{
			return zGetButtonDown != null && zGetButtonDown(name);
		}
		catch (Exception ex)
		{
			zInputReady = false;
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: ZInput.GetButtonDown failed and chord detection was disabled: " + ex.Message));
			return false;
		}
	}

	private bool IsMenuInputContext()
	{
		if (TryReadStaticBoolProperty(zVirtualKeyboardOpenProperty))
		{
			return true;
		}
		return TryInvokeBool(menuIsVisibleMethod) || TryInvokeBool(inventoryGuiIsVisibleMethod) || TryInvokeBool(storeGuiIsVisibleMethod) || TryInvokeBool(textInputIsVisibleMethod) || TryInvokeBool(consoleIsVisibleMethod);
	}

	private static bool TryInvokeBool(MethodInfo method)
	{
		if (method == null)
		{
			return false;
		}
		try
		{
			object obj = method.Invoke(null, null);
			return obj is bool && (bool)obj;
		}
		catch
		{
			return false;
		}
	}

	private static bool TryReadStaticBoolProperty(PropertyInfo property)
	{
		if (property == null)
		{
			return false;
		}
		try
		{
			object value = property.GetValue(null, null);
			return value is bool && (bool)value;
		}
		catch
		{
			return false;
		}
	}

	private static Type FindType(string typeName)
	{
		Type type = Type.GetType(typeName, throwOnError: false);
		if (type != null)
		{
			return type;
		}
		Type type2 = Type.GetType(typeName + ", assembly_utils", throwOnError: false);
		if (type2 != null)
		{
			return type2;
		}
		Type type3 = Type.GetType(typeName + ", assembly_valheim", throwOnError: false);
		if (type3 != null)
		{
			return type3;
		}
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			Type type4 = assemblies[i].GetType(typeName, throwOnError: false);
			if (type4 != null)
			{
				return type4;
			}
		}
		return null;
	}

	private static MethodInfo FindStaticBoolMethod(string typeName, string methodName)
	{
		Type type = FindType(typeName);
		if (type == null)
		{
			return null;
		}
		MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
		return (method != null && method.ReturnType == typeof(bool)) ? method : null;
	}

	private Func<string, bool> CreateStringBoolDelegate(Type type, string methodName)
	{
		MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
		if (method != null && method.ReturnType == typeof(bool))
		{
			try
			{
				return (Func<string, bool>)Delegate.CreateDelegate(typeof(Func<string, bool>), method);
			}
			catch
			{
			}
		}
		MethodInfo instanceMethod = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null);
		if (instanceMethod == null || instanceMethod.ReturnType != typeof(bool))
		{
			return null;
		}
		PropertyInfo instanceProperty = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		FieldInfo instanceField = ((instanceProperty == null) ? type.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null);
		if (instanceProperty == null && instanceField == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Controller Chords: found instance method ZInput." + methodName + " but no static 'instance' accessor; chord detection disabled."));
			return null;
		}
		return delegate(string name)
		{
			object obj2 = ((instanceProperty != null) ? instanceProperty.GetValue(null, null) : instanceField.GetValue(null));
			if (obj2 == null)
			{
				return false;
			}
			object obj3 = instanceMethod.Invoke(obj2, new object[1] { name });
			bool flag = default(bool);
			int num;
			if (obj3 is bool)
			{
				flag = (bool)obj3;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		};
	}

	private void AgeKeyFrames()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: 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)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: 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 static void SetMaxFrameCount(Dictionary<KeyCode, int> dictionary, KeyCode key, int frames)
	{
		//IL_0002: 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)
		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)
	{
		return button == ControllerButton.RB || button == ControllerButton.LB || button == ControllerButton.RT || button == ControllerButton.LT;
	}

	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_0028: 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_0028: 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;
			}
		}
	}
}
public enum ControllerButton
{
	None,
	A,
	B,
	X,
	Y,
	DPadLeft,
	DPadRight,
	DPadUp,
	DPadDown,
	LB,
	RB,
	LT,
	RT,
	LeftStick,
	RightStick,
	Back,
	Start
}