Decompiled source of ControllerGamepadRemapping v3.0.2

plugins/ControllerGamepadRemapping.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;

[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("nussbacker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Controller / Gamepad Remapping for Valheim.")]
[assembly: AssemblyFileVersion("3.0.2.0")]
[assembly: AssemblyInformationalVersion("3.0.2")]
[assembly: AssemblyProduct("ControllerGamepadRemapping")]
[assembly: AssemblyTitle("ControllerGamepadRemapping")]
[assembly: AssemblyVersion("3.0.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 ControllerGamepadRemapping
{
	[BepInPlugin("nussbacker.valheim.controllergamepadremapping", "Controller / Gamepad Remapping", "3.0.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class RuntimeSetting<T>
		{
			private T value;

			public T Value
			{
				get
				{
					return value;
				}
				set
				{
					if (!EqualityComparer<T>.Default.Equals(this.value, value))
					{
						this.value = value;
						this.SettingChanged?.Invoke(this, EventArgs.Empty);
					}
				}
			}

			public event EventHandler SettingChanged;

			public RuntimeSetting(T initialValue)
			{
				value = initialValue;
			}
		}

		private sealed class CustomLayoutBinding
		{
			public string Path;

			public bool AlternateFunction;

			public bool Unbound;
		}

		private sealed class PresetBehaviorState
		{
			public bool CrouchChord;

			public bool BlockJumpToDodge;

			public bool MoveWhileGui;

			public bool ProximityInteraction;
		}

		private sealed class SavedCustomPreset
		{
			public string Name;

			public string StoragePath;

			public int StorageOrder = -1;

			public readonly Dictionary<string, CustomLayoutBinding> Bindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase);

			public PresetBehaviorState Behaviors = new PresetBehaviorState();

			public bool HasBehaviorSnapshot;
		}

		private enum CustomPresetLoadSource
		{
			None,
			PerPresetFiles,
			PerPresetFilesWithoutState
		}

		private sealed class EditorSuppressedUiModuleState
		{
			public Behaviour Module;

			public bool WasEnabled;
		}

		private sealed class GamepadMapPresentationCacheState
		{
			public int MapIdentity;

			public string Signature;
		}

		private sealed class OriginalBinding
		{
			public string Path;

			public string AltPath;

			public bool AltKey;

			public bool ShowHints;

			public bool Rebindable;

			public float RepeatDelay;

			public float RepeatInterval;
		}

		private sealed class LayoutRegistrationSnapshot
		{
			public FieldInfo Field;

			public object Owner;

			public object Value;

			public object Key;
		}

		[Flags]
		private enum ConflictContext
		{
			None = 0,
			Gameplay = 1,
			RadialMenu = 2,
			Building = 4,
			Interface = 8,
			Chat = 0x10
		}

		private sealed class ActionInfo
		{
			public readonly string Name;

			public readonly string Description;

			public ActionInfo(string name, string description)
			{
				Name = name;
				Description = description;
			}
		}

		private sealed class GamepadMapActionLabel
		{
			public string Action;

			public bool Alternate;

			public bool Conflicting;
		}

		public const string PluginGuid = "nussbacker.valheim.controllergamepadremapping";

		public const string PluginName = "Controller / Gamepad Remapping";

		public const string PluginVersion = "3.0.2";

		public const string PluginAuthor = "nussbacker";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static bool EditorInputBlocked;

		private Harmony harmony;

		private Harmony editorInputHarmony;

		private bool editorGamepadInputHooksActive;

		private Harmony buildInputHarmony;

		private bool buildGamepadInputHooksActive;

		private Harmony movementGuiInputHarmony;

		private bool movementGuiInputHooksActive;

		private MethodInfo movementGuiPlayerControllerFixedUpdate;

		private bool movementGuiRunToggleBaselineReady;

		private bool movementGuiRunTogglePreviousHeld;

		private bool movementGuiRunToggleValueValid;

		private bool movementGuiRunToggleValue;

		private Harmony customAltConsumerHarmony;

		private string customAltConsumerPatchSignature = string.Empty;

		private bool appliedOnce;

		private bool applying;

		private bool dirty;

		private float applyAt;

		private float nextReadyPoll;

		private const string UnboundToken = "__UNBOUND__";

		private RuntimeSetting<bool> debug;

		private RuntimeSetting<KeyCode> editorKey;

		private RuntimeSetting<bool> showEditor;

		private RuntimeSetting<bool> proximityInteractionEnabled;

		private RuntimeSetting<string> crouchInput;

		private RuntimeSetting<bool> crouchUseChord;

		private RuntimeSetting<bool> blockJumpToDodge;

		private RuntimeSetting<bool> moveWhileContainerGuiOpen;

		private RuntimeSetting<string> gamepadUiSelection;

		private RuntimeSetting<bool> nintendoFaceButtonNormalization;

		private RuntimeSetting<string> buildCopyInput;

		private RuntimeSetting<bool> buildCopyRequiresAlt;

		private RuntimeSetting<string> buildHammerInput;

		private string customPresetDirectoryPath;

		private string customPresetStateFilePath;

		private string globalSettingsFilePath;

		private const string CustomPresetDirectoryName = "ControllerPresets";

		private const string CustomPresetStateFileName = "_ControllerPresets.state";

		private const string GlobalSettingsFileName = "_ControllerGamepadRemapping.settings";

		private const string GlobalSettingsFileFormatVersion = "2";

		private const string CustomPresetFileExtension = ".preset";

		private const string CustomPresetFileFormatVersion = "3";

		private const string LegacyCustomPresetFileFormatVersion = "2";

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

		private string lastSavedGlobalSettingsSignature = string.Empty;

		private bool globalSettingsFileExisted;

		private readonly HashSet<string> pendingPresetFileDeletes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal const float ProximityInteractionRadius = 2f;

		internal const bool ProximityInteractionRequireLineOfSight = true;

		internal const bool ProximityInteractionDisableWhileBuilding = true;

		private readonly Dictionary<string, RuntimeSetting<string>> bindings = new Dictionary<string, RuntimeSetting<string>>();

		private readonly Dictionary<string, RuntimeSetting<string>> guiBindings = new Dictionary<string, RuntimeSetting<string>>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, RuntimeSetting<bool>> alternateFunctionRequirements = new Dictionary<string, RuntimeSetting<bool>>(StringComparer.OrdinalIgnoreCase);

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

		private bool nativePlayerModifierOwnershipPossible;

		private readonly Dictionary<string, OriginalBinding> originalBindings = new Dictionary<string, OriginalBinding>();

		private bool originalsCaptured;

		private string nativeControllerPresetSignature;

		private bool suppressPresetRuntimeStateEvents;

		private bool controllerPresetSelectionCallActive;

		private bool controllerPresetPreviewActive;

		private bool controllerPresetPreviewDetectedThisRefresh;

		private bool controllerPresetPreviewCancelInProgress;

		private string controllerPresetSignatureBeforePreview;

		private string pendingControllerPresetSignature;

		private readonly List<SavedCustomPreset> savedCustomPresets = new List<SavedCustomPreset>();

		private readonly Dictionary<string, CustomLayoutBinding> workingLayoutBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, CustomLayoutBinding> activePresetBaselineBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase);

		private bool workingLayoutInitialized;

		private bool workingLayoutDirty;

		private string activePresetBaselineStateSignature = string.Empty;

		private string workingLayoutStateSignature = string.Empty;

		private string activeCustomPresetName = string.Empty;

		private PresetBehaviorState activePresetBaselineBehaviors = new PresetBehaviorState();

		private PresetBehaviorState workingPresetBehaviors = new PresetBehaviorState();

		private bool presetBehaviorStateInitialized;

		private bool settingsCustomLayoutPreviewActive;

		private string settingsCustomPresetPreviewName = string.Empty;

		private bool settingsControllerLayoutSelectionTouched;

		private readonly Dictionary<string, CustomLayoutBinding> settingsPresetPreviewBindings = new Dictionary<string, CustomLayoutBinding>(StringComparer.OrdinalIgnoreCase);

		private PresetBehaviorState settingsPresetPreviewBehaviors = new PresetBehaviorState();

		private bool settingsPresetPreviewActive;

		private object settingsNativePresetPreviewLayout;

		private object settingsCommittedNativeLayout;

		private bool settingsControllerTabActive;

		private int settingsControllerTabIndex = -1;

		private int settingsPendingTabIndex = -1;

		private bool settingsPreviewPresentationRefreshInProgress;

		private bool settingsNativePresetCommitInProgress;

		private object activeGamepadSettingsInstance;

		private Type cachedGamepadSettingsType;

		private FieldInfo gamepadSettingsCurrentControllerLayoutField;

		private FieldInfo gamepadSettingsControllerLayoutTextField;

		private MethodInfo gamepadSettingsOnZInputLayoutChangedMethod;

		private const int CustomPresetNameMaxLength = 20;

		private bool savePresetPromptOpen;

		private bool savePresetPromptNeedsFocus;

		private string savePresetNameInput = "Custom Layout";

		private Rect savePresetPromptRect = new Rect(0f, 0f, 360f, 128f);

		private bool deletePresetPromptOpen;

		private string deletePresetPromptName = string.Empty;

		private Rect deletePresetPromptRect = new Rect(0f, 0f, 330f, 104f);

		private readonly HashSet<MethodBase> patchedControllerPresetLifecycleMethods = new HashSet<MethodBase>();

		private bool controllerPresetLifecyclePatchDiscoveryComplete;

		private Type zInputType;

		private MethodInfo addButton;

		private MethodInfo unsubscribeButton;

		private FieldInfo buttonsField;

		private FieldInfo buttonActionField;

		private PropertyInfo buttonActionProperty;

		private MethodInfo zInputUpdate;

		private MethodInfo zInputGetButton;

		private MethodInfo zInputGetButtonDown;

		private MethodInfo zInputGetButtonUp;

		private MethodInfo zInputGetKey;

		private MethodInfo zInputGetKeyDown;

		private MethodInfo zInputGetKeyUp;

		private MethodInfo zInputGetMouseButton;

		private MethodInfo zInputGetMouseButtonDown;

		private MethodInfo zInputGetMouseButtonUp;

		private MethodInfo zInputGetMouseDelta;

		private MethodInfo zInputChangeInputLayout;

		private MethodInfo zInputUpdateGamepadInputLayout;

		private MethodInfo zInputIsGamepadEnabled;

		private MethodInfo zInputSetGamepadEnabled;

		private MethodInfo zInputGetBoundKeyString;

		private FieldInfo zInputGamepadEnabledField;

		private FieldInfo zInputInstanceField;

		private PropertyInfo zInputInstanceProperty;

		private bool zInputInstanceAccessorPrepared;

		private bool zInputReflectionDiscoveryComplete;

		private int gamepadEnabledSampleFrame = -1;

		private bool cachedGamepadEnabledKnown;

		private bool cachedGamepadEnabled = true;

		private bool? lastLoggedNativeGamepadEnabled;

		private int lastAllowedNativeGamepadLayoutFrame = -1;

		private float lastAllowedNativeGamepadLayoutAt = -10f;

		[ThreadStatic]
		private static bool skippedRedundantNativeGamepadLayoutCall;

		private bool gamepadEnabledLifecycleInitialized;

		private bool lastObservedGamepadEnabled = true;

		private bool gamepadReenableRepairPending;

		private MethodInfo zInputGetJoyLeftStick;

		private MethodInfo zInputGetJoyRightStick;

		private MethodInfo zInputGetJoyLeftStickX;

		private MethodInfo zInputGetJoyLeftStickY;

		private MethodInfo zInputGetJoyRightStickX;

		private MethodInfo zInputGetJoyRightStickY;

		private MethodInfo zInputGetJoyLTrigger;

		private MethodInfo zInputGetJoyRTrigger;

		private MethodInfo zInputGetMouseScrollWheel;

		private bool patchedChangeInputLayout;

		private bool patchedGamepadLayout;

		private bool patchedSetGamepadEnabled;

		private bool patchedBoundKeyString;

		private readonly HashSet<MethodBase> patchedEditorExclusiveBoolInputMethods = new HashSet<MethodBase>();

		private bool editorExclusiveInputOwnershipActive;

		private CursorLockMode editorPreviousCursorLockState;

		private bool editorPreviousCursorVisible;

		private Type editorBaseInputModuleType;

		private Object editorObservedEventSystem;

		private readonly List<EditorSuppressedUiModuleState> editorSuppressedUiModules = new List<EditorSuppressedUiModuleState>();

		private readonly HashSet<MethodBase> patchedInventoryGuiShortcutMethods = new HashSet<MethodBase>();

		private bool patchedInventoryGuiShortcutUpdate;

		private bool patchedInventoryGuiCraftQuantityContext;

		[ThreadStatic]
		private static int inventoryGuiCraftQuantityContextDepth;

		[ThreadStatic]
		private static object inventoryGuiCraftQuantityContextInstance;

		[ThreadStatic]
		private static bool inventoryGuiCraftQuantityPaneOwnsFocus;

		[ThreadStatic]
		private static bool inventoryGuiCraftQuantityActiveGroupAvailable;

		[ThreadStatic]
		private static int inventoryGuiCraftQuantityActiveGroup;

		private bool craftQuantitySuppressionLogged;

		private bool craftQuantityHeldDecisionLogged;

		private readonly Dictionary<string, MethodInfo> contextualGuiShortcutMethods = new Dictionary<string, MethodInfo>(StringComparer.Ordinal);

		private Type inventoryGuiType;

		private bool contextualGuiCustomBindingActive;

		private bool movementGuiInventoryOpen;

		private object activeInventoryGuiInstance;

		private bool movementGuiStoreOpen;

		private bool movementGuiBuildSelectionOpen;

		private bool movementGuiFullMapOpen;

		private bool movementGuiSplitDialogOpen;

		private GameObject movementGuiSplitDialogRoot;

		private readonly HashSet<MethodBase> patchedMovementGuiLifecycleMethods = new HashSet<MethodBase>();

		private bool movementGuiLifecyclePatchDiscoveryComplete;

		private bool movementGuiMenuOpen;

		private bool movementGuiSettingsOpen;

		private bool movementGuiTextInputOpen;

		private int settingsLifecycleTransitionDepth;

		private float lastSettingsLifecycleTransitionAt = -10f;

		private bool settingsControllerStateDirty;

		private string keyboardMouseSettingsBaselineSignature;

		private readonly HashSet<string> keyboardMouseSettingsActionNames = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<MethodBase> patchedKeyboardMouseSettingsMethods = new HashSet<MethodBase>();

		private bool keyboardMouseSettingsPatchDiscoveryComplete;

		private readonly ConditionalWeakTable<object, GamepadMapPresentationCacheState> gamepadMapPresentationCache = new ConditionalWeakTable<object, GamepadMapPresentationCacheState>();

		private readonly List<Object> observedGamepadMapControllers = new List<Object>();

		private MethodInfo cachedGamepadMapUpdateMethod;

		private float tabSoundTraceUntil = -10f;

		private readonly HashSet<MethodBase> patchedTabSoundTraceMethods = new HashSet<MethodBase>();

		private bool tabSoundTracePatchDiscoveryComplete;

		private FieldInfo inventoryGuiActiveGroupField;

		private bool inventoryGuiCraftFocusFieldsPrepared;

		private readonly HashSet<MethodBase> patchedBuildUiMoveMethods = new HashSet<MethodBase>();

		private bool patchedBuildUiSelectableNavigation;

		private Type buildUiSelectableType;

		private Type buildUiEventSystemType;

		private PropertyInfo buildUiEventSystemCurrentProperty;

		private PropertyInfo buildUiCurrentSelectedGameObjectProperty;

		private MethodInfo buildUiSelectableFindUp;

		private MethodInfo buildUiSelectableFindDown;

		private MethodInfo buildUiSelectableFindLeft;

		private MethodInfo buildUiSelectableFindRight;

		private MethodInfo buildUiSelectableSelect;

		private string buildUiManualNavigationDirection;

		private float buildUiManualNavigationNextRepeatAt;

		private string splitDialogManualNavigationDirection;

		private float splitDialogManualNavigationNextRepeatAt;

		private Type splitDialogAxisEventDataType;

		private PropertyInfo splitDialogMoveVectorProperty;

		private PropertyInfo splitDialogMoveDirectionProperty;

		private ConstructorInfo splitDialogAxisEventDataConstructor;

		private readonly Dictionary<Type, MethodInfo> splitDialogOnMoveMethodCache = new Dictionary<Type, MethodInfo>();

		[ThreadStatic]
		private static bool manualUnityUiMoveDispatchInProgress;

		[ThreadStatic]
		private static bool nativeGuiDirectionQueryInProgress;

		private const float BuildUiNavigationInitialRepeatDelay = 0.3f;

		private const float BuildUiNavigationRepeatInterval = 0.12f;

		private const float BuildUiNavigationDeadzone = 0.5f;

		[ThreadStatic]
		private static bool composingAlternateFunctionHintGlyph;

		[ThreadStatic]
		private static int keyHintGenerationDepth;

		[ThreadStatic]
		private static bool unboundHintMarkerEmittedThisGeneration;

		[ThreadStatic]
		private static int radialHintGenerationDepth;

		[ThreadStatic]
		private static bool radialUnboundHintMarkerEmittedThisGeneration;

		private const string UnboundHintMarker = "__CGR_UNBOUND_HINT__:";

		private int alternateFunctionGlyphCacheFrame = -1;

		private bool alternateFunctionGlyphCacheNormalReady;

		private bool alternateFunctionGlyphCacheAlternateReady;

		private string alternateFunctionGlyphCacheNormal;

		private string alternateFunctionGlyphCacheAlternate;

		private readonly HashSet<MethodBase> patchedControllerPresentationMethods = new HashSet<MethodBase>();

		private readonly HashSet<MethodBase> patchedKeyHintsRotateSourceMethods = new HashSet<MethodBase>();

		private readonly HashSet<MethodBase> patchedUniversalUnboundHintSourceMethods = new HashSet<MethodBase>();

		private readonly HashSet<MethodBase> patchedRadialUnboundHintSourceMethods = new HashSet<MethodBase>();

		private readonly Dictionary<int, List<object>> universalUnboundHintTextCache = new Dictionary<int, List<object>>();

		private bool universalUnboundHintDiscoveryComplete;

		private bool radialUnboundHintDiscoveryComplete;

		private object cachedKeyHintsOwner;

		private bool keyHintPresentationCacheInitialized;

		private readonly List<Object> observedKeyHintsInstances = new List<Object>();

		private MethodInfo cachedKeyHintsRefreshMethod;

		private readonly List<object> cachedKeyHintTextObjects = new List<object>();

		private readonly List<object> cachedBuildCopyHintTextObjects = new List<object>();

		private object cachedBuildCopyHintTextObject;

		private object cachedDodgeHintTextObject;

		private object cachedSecondaryAttackHintTextObject;

		private object cachedSnapCycleHintTextObject;

		private object cachedBuildMenuHintTextObject;

		private readonly Dictionary<string, List<GameObject>> hiddenUnboundKeyHintObjects = new Dictionary<string, List<GameObject>>(StringComparer.OrdinalIgnoreCase);

		private int keyHintPresentationCacheValidationFrame = -1;

		private bool buildRotateHintPresentationLogged;

		private bool buildCopyHintPresentationLogged;

		private object cachedBuildUiHammerOwner;

		private object cachedBuildUiHammerTextObject;

		private string cachedBuildUiHammerOriginalGlyph;

		private bool gamepadUiDropdownOpen;

		private Rect gamepadUiDropdownScreenAnchor;

		private bool gamepadUiDropdownAnchorValid;

		private bool presetDropdownOpen;

		private Rect presetDropdownScreenAnchor;

		private bool presetDropdownAnchorValid;

		private bool dropdownPointerCaptureUntilMouseUp;

		private bool editorDeferredPresentationRefresh;

		private bool gamepadPresentationStartupApplied;

		private string scheduledFullApplyReason;

		private string scheduledIncrementalApplyReason;

		private bool fullApplyScheduled;

		private bool logicalUnbindNativeRefreshPending;

		private float logicalUnbindNativeRefreshAt = -1f;

		private readonly HashSet<string> logicalUnbindRefreshActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool nativeInitialGamepadLayoutObserved;

		private bool startupApplyFallbackConsumed;

		private float startupApplyFallbackAt = -1f;

		private Player radialRepairObservedLocalPlayer;

		private bool radialWorldReadyRepairPending;

		private float radialWorldReadyRepairAt = -1f;

		private readonly HashSet<string> pendingApplyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> pendingNeutralApplyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private readonly HashSet<string> loggedNeutralDeferredActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool physicalGamepadLifecycleInitialized;

		private object lastPhysicalGamepadObject;

		private int lastPhysicalGamepadDeviceId = int.MinValue;

		private string lastLegacyJoystickLifecycleSignature;

		private const float LegacyJoystickLifecyclePollInterval = 0.5f;

		private float nextLegacyJoystickLifecyclePoll;

		private string cachedLegacyJoystickLifecycleSignature;

		private readonly HashSet<MethodInfo> patchedFloatMethods = new HashSet<MethodInfo>();

		private readonly HashSet<MethodInfo> patchedVector2Methods = new HashSet<MethodInfo>();

		private bool loggedReflectionStatus;

		private readonly Dictionary<string, bool> customPressedState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, bool> customDownState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, bool> customUpState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

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

		private int physicalPathSampleCacheFrame = -1;

		private readonly Dictionary<string, bool> physicalPathPressedCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> unresolvedPhysicalPathSampleCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private int customDigitalStatesUpdatedFrame = -1;

		private int plainLogicalEdgesUpdatedFrame = -1;

		private int routedLogicalEdgesUpdatedFrame = -1;

		private int customRoutingDecisionCacheFrame = -1;

		private readonly Dictionary<string, bool> customRoutingDecisionFrameCache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private const float RoutedEdgeLatchLifetime = 0.25f;

		private readonly Dictionary<string, bool> routedEffectiveHeldState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> routedDownLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> routedUpLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, float> routedDownLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, float> routedUpLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

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

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

		private readonly Dictionary<string, bool> plainEffectiveHeldState = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> plainDownLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> plainUpLatch = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, float> plainDownLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, float> plainUpLatchTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

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

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

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

		private readonly HashSet<string> reconstructedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> loggedNativeHeldDivergenceActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool previousNativeCrouchHeld;

		private bool previousJoyDodgeChordHeld;

		private MethodInfo characterJumpMethod;

		private PropertyInfo playerToggleBlockProperty;

		private FieldInfo characterBlockingField;

		private bool previousAuthoritativeBlockJumpJumpHeld;

		private bool previousAlternateJumpHandoffHeld;

		private readonly HashSet<string> gameplayUiQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private bool gameplayUiWasBlocking;

		private bool gameplayUiPostExitReleaseGateActive;

		private int gameplayUiQuarantineUpdatedFrame = -1;

		private bool gameplayUiQuarantineLastBlockingState;

		private readonly HashSet<string> guiEntryQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

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

		private MethodInfo playerInPlaceModeMethod;

		private FieldInfo playerLocalPlayerField;

		private readonly HashSet<MethodBase> patchedPlayerPlaceModeMethods = new HashSet<MethodBase>();

		private bool buildPlacementStateInitialized;

		private bool cachedBuildPlacementModeActive;

		private bool routedContextInitialized;

		private bool routedContextBuildPlacement;

		private readonly HashSet<string> buildPriorityReleaseQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

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

		private readonly HashSet<string> activeBuildPriorityPathSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private int activeBuildPriorityPathsFrame = -1;

		private int buildPriorityOwnershipGateUpdatedFrame = -1;

		private bool buildPriorityOwnershipGateLastActiveState;

		private bool buildPriorityContextInitialized;

		private bool buildPriorityWasActive;

		private const string BuildCopyAction = "BuildCopy";

		private const string BuildHammerAction = "BuildHammer";

		private const string SnapCycleHintKey = "__SnapCycleHint";

		private readonly HashSet<string> buildCopyQuarantinedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private bool buildCopyChordWasHeld;

		private bool buildCopyClearQuarantineAfterPlayerUpdate;

		private MethodInfo playerCopyPieceMethod;

		private FieldInfo playerBlockRemoveField;

		private bool patchedBuildCopyPlayerUpdate;

		private MethodInfo playerSetControls;

		private bool patchedPlayerSetControls;

		private readonly HashSet<MethodInfo> patchedNativeAltGuardMethods = new HashSet<MethodInfo>();

		private readonly Dictionary<MethodBase, string[]> nativeAltGuardActionsByMethod = new Dictionary<MethodBase, string[]>();

		[ThreadStatic]
		private static Stack<MethodBase> nativeAltGuardScopeStack;

		private readonly HashSet<string> activeModOwnedAlternateActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> activeModOwnedAlternatePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> activeModOwnedAlternateShadowedActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> modOwnedAlternateOwnedPathsUntilRelease = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private bool glyphMethodDiscoveryLogged;

		private Type gamepadType;

		private PropertyInfo gamepadCurrentProperty;

		private MethodInfo inputSystemFindControl;

		private MethodInfo inputSystemFindControls;

		private readonly Dictionary<string, PropertyInfo> controlProperties = new Dictionary<string, PropertyInfo>();

		private readonly Dictionary<string, string[]> controlPathSegmentsCache = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, MethodInfo> actionBoolMethodCache = new Dictionary<string, MethodInfo>(StringComparer.Ordinal);

		private readonly Dictionary<Type, PropertyInfo> gamepadDeviceIdPropertyCache = new Dictionary<Type, PropertyInfo>();

		private readonly Dictionary<Type, FieldInfo> gamepadDeviceIdFieldCache = new Dictionary<Type, FieldInfo>();

		private readonly Dictionary<string, object> resolvedControlCache = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> unresolvedControlCache = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private string selectedAction = "JoyUse";

		private bool waitingForBinding;

		private Rect windowRect = new Rect(40f, 40f, 650f, 700f);

		private Vector2 actionScroll;

		private string statusMessage = "";

		private readonly Dictionary<string, List<string>> editorConflictCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);

		private readonly List<string> editorNoConflicts = new List<string>(0);

		private string editorConflictCacheWorkingSignature = string.Empty;

		private string editorConflictCacheBaselineSignature = string.Empty;

		private string editorConflictCacheGuiSource = string.Empty;

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

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

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

		private string editorCrouchChordTooltip = string.Empty;

		private string editorBlockJumpToDodgeTooltip = string.Empty;

		private string editorPresentationCacheWorkingSignature = string.Empty;

		private string editorPresentationCacheBaselineSignature = string.Empty;

		private string editorPresentationCacheGamepadUi = string.Empty;

		private string editorPresentationCacheLastGuiSource = string.Empty;

		private bool editorPresentationCacheNintendoNormalization;

		private string actionSearchText = string.Empty;

		private string normalizedActionSearchText = string.Empty;

		private bool actionSearchFilterDirty;

		private readonly HashSet<string> actionSearchMatches = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private GUIStyle windowStyle;

		private GUIStyle windowTitleStyle;

		private GUIStyle rowStyle;

		private GUIStyle groupedRowStyle;

		private GUIStyle groupedRowLineStyle;

		private GUIStyle actionStyle;

		private GUIStyle actionConflictStyle;

		private GUIStyle sectionHeaderStyle;

		private GUIStyle subsectionHeaderStyle;

		private GUIStyle bindingStyle;

		private GUIStyle smallButtonStyle;

		private GUIStyle deletePresetButtonStyle;

		private GUIStyle tooltipStyle;

		private GUIStyle crouchToggleStyle;

		private GUIStyle unsavedChangesStyle;

		private GUIStyle footerStatusStyle;

		private GUIStyle promptTextStyle;

		private GUIStyle presetNativeItemStyle;

		private GUIStyle presetCustomItemStyle;

		private GUIStyle toggleLabelStyle;

		private GUIStyle searchLabelStyle;

		private HashSet<string> verifiedStockIntentionalOverlapSignatures;

		private Texture2D toggleBoxTexture;

		private Texture2D toggleBoxHoverTexture;

		private Texture2D toggleInnerTexture;

		private Texture2D darkBackground;

		private Texture2D darkerBackground;

		private Texture2D buttonBackground;

		private Texture2D buttonHoverBackground;

		private Texture2D buttonActiveBackground;

		private Texture2D buttonSelectedBackground;

		private Texture2D deleteButtonBackground;

		private Texture2D deleteButtonHoverBackground;

		private Texture2D deleteButtonActiveBackground;

		private static readonly HashSet<string> VirtualLayerActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyCrouch", "JoyRun", "JoyDodge" };

		private static readonly HashSet<string> NativeAlternateFunctionDefaults = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyAutoPickup", "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut", "JoyChat", "JoyToggleHUD" };

		private static readonly HashSet<string> ForcedUnboundInternalActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyBuildMenu", "JoyRotateRight" };

		private static readonly string[] DigitalActions = new string[43]
		{
			"JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoySit",
			"JoyHide", "JoyAutoPickup", "JoyGP", "JoyHotbarUse", "JoyHotbarLeft", "JoyHotbarRight", "JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack",
			"JoyRadialClose", "JoyBuildMenu", "JoyPlace", "JoyRemove", "JoyRotate", "JoyRotateRight", "JoyNextSnap", "JoyPrevSnap", "JoyCamZoomIn", "JoyCamZoomOut",
			"JoyMap", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut", "JoyToggleHUD", "JoyMenu", "JoyBack", "JoyTabLeft", "JoyTabRight", "JoyChat", "JoyScrollChatUp",
			"JoyScrollChatDown", "JoyAltKeys", "JoyAltPlace"
		};

		private static readonly HashSet<string> DigitalActionSet = new HashSet<string>(DigitalActions, StringComparer.OrdinalIgnoreCase);

		private static readonly string[] GameplayMenuActions = new string[18]
		{
			"JoyMenu", "JoyMap", "JoyInventory", "JoyUse", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch",
			"JoyDodge", "JoySit", "JoyHide", "JoyAutoPickup", "JoyHotbarLeft", "JoyHotbarRight", "JoyHotbarUse", "JoyGP"
		};

		private static readonly string[] BuildingPrimaryMenuActions = new string[8] { "JoyPlace", "JoyRemove", "JoyRotate", "BuildCopy", "BuildHammer", "JoyAltPlace", "JoyNextSnap", "JoyPrevSnap" };

		private static readonly HashSet<string> NativeOwnedFullMapActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyMapZoomIn", "JoyMapZoomOut", "MapTogglePins", "MapPing", "MapRemovePin", "MapCrossOffPin", "MapAddPin", "MapVisiblePlayers" };

		private static readonly HashSet<string> NativeOwnedReadOnlyActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyBack", "JoyTabLeft", "JoyTabRight" };

		private static readonly string[] PresetContextualActions = new string[4] { "BuildCopy", "BuildHammer", "GuiRepairItem", "GuiCraftUpgrade" };

		private static readonly string[] PresetOwnedActions = (from action in DigitalActions.Concat(PresetContextualActions)
			where !NativeOwnedReadOnlyActions.Contains(action) && !IsForcedInternalUnbound(action)
			select action).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();

		private static readonly string[] ConflictCandidateActions = DigitalActions.Concat(PresetContextualActions).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();

		private static readonly string[] ContextualNativeSourceActions = new string[1] { "JoyButtonY" };

		private static readonly string[] CameraMinimapMenuActions = new string[4] { "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut" };

		private static readonly string[] RadialMenuActions = new string[5] { "JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose" };

		private static readonly string[] RadialRuntimeInputActions = new string[4] { "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose" };

		private static readonly HashSet<string> RadialMenuActionSet = new HashSet<string>(RadialMenuActions, StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<string> VanillaControllerUiActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoySit",
			"JoyHide", "JoyAutoPickup", "JoyGP", "JoyHotbarUse", "JoyHotbarLeft", "JoyHotbarRight", "JoyCamZoomIn", "JoyCamZoomOut", "JoyMiniMapZoomIn", "JoyMiniMapZoomOut",
			"JoyMap", "JoyMenu", "JoyToggleHUD", "JoyChat", "JoyPlace", "JoyRemove", "JoyRotate", "JoyAltPlace", "JoyNextSnap", "JoyPrevSnap",
			"JoyRadial", "JoyRadialInteract", "JoyRadialSecondaryInteract", "JoyRadialBack", "JoyRadialClose"
		};

		private const string GuiRepairItemAction = "GuiRepairItem";

		private const string GuiCraftUpgradeAction = "GuiCraftUpgrade";

		private const string GuiRepairNativeAction = "JoyRStick";

		private const string GuiCraftUpgradeNativeAction = "JoyButtonX";

		private const string NativeCraftQuantityModifierAction = "JoyLStick";

		private const int InventoryGuiCraftGamepadGroup = 3;

		private static readonly string[] ContextualGuiActions = new string[2] { "GuiRepairItem", "GuiCraftUpgrade" };

		private static readonly string[] GuiMenuActions = new string[2] { "GuiRepairItem", "GuiCraftUpgrade" };

		private static readonly string[] HudMenuActions = new string[1] { "JoyToggleHUD" };

		private static readonly string[] ChatMenuActions = new string[3] { "JoyChat", "JoyScrollChatUp", "JoyScrollChatDown" };

		private static readonly HashSet<string> BuildPriorityActions = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "JoyPlace", "JoyRemove", "JoyRotate", "JoyNextSnap", "JoyPrevSnap", "JoyAltPlace" };

		private static readonly string[] PlayerControlGameplayActions = new string[10] { "JoyUse", "JoyInventory", "JoyAttack", "JoySecondaryAttack", "JoyBlock", "JoyJump", "JoyRun", "JoyCrouch", "JoyDodge", "JoyGP" };

		private static readonly HashSet<string> PlayerControlGameplayActionSet = new HashSet<string>(PlayerControlGameplayActions, StringComparer.OrdinalIgnoreCase);

		private const string GuiNavigationDpad = "D-pad";

		private const string GuiNavigationRightStick = "Right Stick";

		private const string GuiNavigationBoth = "Both";

		private const string GuiNavigationLeftStick = "Left Stick";

		private static readonly Dictionary<string, ActionInfo> ActionInfos = new Dictionary<string, ActionInfo>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"JoyAttack",
				new ActionInfo("Attack", "Perform your equipped weapon's primary attack.")
			},
			{
				"JoySecondaryAttack",
				new ActionInfo("Secondary attack", "Perform your equipped weapon's secondary attack, when available.")
			},
			{
				"JoyBlock",
				new ActionInfo("Block", "Raise your equipped shield or weapon to block incoming attacks.")
			},
			{
				"JoyDodge",
				new ActionInfo("Dodge", "Dodge.")
			},
			{
				"JoyJump",
				new ActionInfo("Jump", "Make your character jump.")
			},
			{
				"JoyRun",
				new ActionInfo("Run", "Sprint while moving.")
			},
			{
				"JoyCrouch",
				new ActionInfo("Crouch", "Crouch.")
			},
			{
				"JoySit",
				new ActionInfo("Sit", "Sit down or stand up.")
			},
			{
				"JoyUse",
				new ActionInfo("Use", "Use or interact with nearby objects and characters.")
			},
			{
				"JoyInventory",
				new ActionInfo("Inventory", "Open or close your inventory.")
			},
			{
				"JoyHide",
				new ActionInfo("Hide/show weapon", "Toggle hiding or showing your currently equipped weapon or shield.")
			},
			{
				"JoyAutoPickup",
				new ActionInfo("Toggle auto-pickup", "Toggle Valheim's automatic pickup behavior.")
			},
			{
				"JoyGP",
				new ActionInfo("Forsaken power", "Activate the forsaken power.")
			},
			{
				"JoyHotbarLeft",
				new ActionInfo("Prv. (Hotbar)", "Move the hotbar selection one slot to the left.")
			},
			{
				"JoyHotbarRight",
				new ActionInfo("Next (Hotbar)", "Move the hotbar selection one slot to the right.")
			},
			{
				"JoyHotbarUse",
				new ActionInfo("Use (Hotbar)", "Use or equip the currently selected hotbar item.")
			},
			{
				"JoyCamZoomIn",
				new ActionInfo("Zoom in view", "Move the camera closer to your character.")
			},
			{
				"JoyCamZoomOut",
				new ActionInfo("Zoom out view", "Move the camera farther from your character.")
			},
			{
				"JoyMap",
				new ActionInfo("Map", "Open or close the map.")
			},
			{
				"JoyPlace",
				new ActionInfo("Place", "Place the currently selected build piece.")
			},
			{
				"JoyRemove",
				new ActionInfo("Deconstruct", "Remove the currently targeted build piece.")
			},
			{
				"JoyRotate",
				new ActionInfo("Rotate", "Rotate the selected build piece.")
			},
			{
				"BuildCopy",
				new ActionInfo("Copy", "Copy the targeted build piece. Alt is optional; when enabled, Alternate Function + Copy input form the chord.")
			},
			{
				"BuildHammer",
				new ActionInfo("Hammer", "Select the Hammer/build tool control used by the build menu.")
			},
			{
				"JoyNextSnap",
				new ActionInfo("Next snap point", "Cycle to the next available snap point while building.")
			},
			{
				"JoyPrevSnap",
				new ActionInfo("Previous snap point", "Cycle to the previous available snap point while building.")
			},
			{
				"JoyAltPlace",
				new ActionInfo("Alternative placement", "Switch to Valheim's alternative build-placement mode where supported.")
			},
			{
				"JoyTabLeft",
				new ActionInfo("Previous tab", "Move to the previous tab in interfaces that support controller tab navigation.")
			},
			{
				"JoyTabRight",
				new ActionInfo("Next tab", "Move to the next tab in interfaces that support controller tab navigation.")
			},
			{
				"JoyToggleHUD",
				new ActionInfo("Toggle HUD", "Show or hide the game's HUD.")
			},
			{
				"JoyChat",
				new ActionInfo("Chat", "Open or activate the chat interface.")
			},
			{
				"JoyScrollChatUp",
				new ActionInfo("Scroll chat up", "Scroll the chat history upward.")
			},
			{
				"JoyScrollChatDown",
				new ActionInfo("Scroll chat down", "Scroll the chat history downward.")
			},
			{
				"JoyMenu",
				new ActionInfo("Menu", "Open the game's pause menu.")
			},
			{
				"JoyBack",
				new ActionInfo("Back / Select", "Perform the controller's back or select function in supported interfaces.")
			},
			{
				"GuiRepairItem",
				new ActionInfo("Repair Item", "Trigger Valheim's inventory repair action when repair is available.")
			},
			{
				"GuiCraftUpgrade",
				new ActionInfo("Craft / Upgrade", "Trigger Valheim's Craft / Upgrade action for the currently selected recipe.")
			},
			{
				"JoyRadial",
				new ActionInfo("Radial Menu", "Open or activate the radial menu.")
			},
			{
				"JoyRadialInteract",
				new ActionInfo("Interact", "Perform the radial menu's primary selection action.")
			},
			{
				"JoyRadialSecondaryInteract",
				new ActionInfo("Drop", "Perform the radial menu's secondary interaction action.")
			},
			{
				"JoyRadialBack",
				new ActionInfo("Back", "Go back from the radial-menu selection.")
			},
			{
				"JoyRadialClose",
				new ActionInfo("Close", "Close the radial menu.")
			},
			{
				"JoyAltKeys",
				new ActionInfo("Alternate function", "Hold this input to access Valheim's alternate controller functions.")
			},
			{
				"JoyStart",
				new ActionInfo("Start", "Perform the controller's Start function in supported interfaces.")
			},
			{
				"JoyButtonA",
				new ActionInfo("A Button", "Represent the controller's A button.")
			},
			{
				"JoyButtonB",
				new ActionInfo("B Button", "Represent the controller's B button.")
			},
			{
				"JoyButtonX",
				new ActionInfo("X Button", "Represent the controller's X button.")
			},
			{
				"JoyButtonY",
				new ActionInfo("Y Button", "Represent the controller's Y button.")
			},
			{
				"JoyLBumper",
				new ActionInfo("Left Bumper", "Represent the controller's left bumper.")
			},
			{
				"JoyRBumper",
				new ActionInfo("Right Bumper", "Represent the controller's right bumper.")
			},
			{
				"JoyLTrigger",
				new ActionInfo("Left Trigger", "Represent the controller's left trigger.")
			},
			{
				"JoyRTrigger",
				new ActionInfo("Right Trigger", "Represent the controller's right trigger.")
			},
			{
				"JoyDPadUp",
				new ActionInfo("D-pad Up", "Represent the controller's D-pad up direction.")
			},
			{
				"JoyDPadDown",
				new ActionInfo("D-pad Down", "Represent the controller's D-pad down direction.")
			},
			{
				"JoyDPadLeft",
				new ActionInfo("D-pad Left", "Represent the controller's D-pad left direction.")
			},
			{
				"JoyDPadRight",
				new ActionInfo("D-pad Right", "Represent the controller's D-pad right direction.")
			},
			{
				"JoyLStickUp",
				new ActionInfo("Left Stick Up", "Represent the controller's left stick up direction.")
			},
			{
				"JoyLStickDown",
				new ActionInfo("Left Stick Down", "Represent the controller's left stick down direction.")
			},
			{
				"JoyLStickLeft",
				new ActionInfo("Left Stick Left", "Represent the controller's left stick left direction.")
			},
			{
				"JoyLStickRight",
				new ActionInfo("Left Stick Right", "Represent the controller's left stick right direction.")
			},
			{
				"JoyRStickUp",
				new ActionInfo("Right Stick Up", "Represent the controller's right stick up direction.")
			},
			{
				"JoyRStickDown",
				new ActionInfo("Right Stick Down", "Represent the controller's right stick down direction.")
			},
			{
				"JoyRStickLeft",
				new ActionInfo("Right Stick Left", "Represent the controller's right stick left direction.")
			},
			{
				"JoyRStickRight",
				new ActionInfo("Right Stick Right", "Represent the controller's right stick right direction.")
			},
			{
				"JoyMiniMapZoomIn",
				new ActionInfo("Minimap Zoom In", "Increase the minimap's zoom level.")
			},
			{
				"JoyMiniMapZoomOut",
				new ActionInfo("Minimap Zoom Out", "Decrease the minimap's zoom level.")
			}
		};

		private static readonly Dictionary<string, string> ControlNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "<Gamepad>/buttonSouth", "A" },
			{ "<Gamepad>/buttonEast", "B" },
			{ "<Gamepad>/buttonWest", "X" },
			{ "<Gamepad>/buttonNorth", "Y" },
			{ "<Gamepad>/leftShoulder", "LB" },
			{ "<Gamepad>/rightShoulder", "RB" },
			{ "<Gamepad>/leftTrigger", "LT" },
			{ "<Gamepad>/rightTrigger", "RT" },
			{ "<Gamepad>/leftStickPress", "L3" },
			{ "<Gamepad>/rightStickPress", "R3" },
			{ "<Gamepad>/dpad/up", "D-pad Up" },
			{ "<Gamepad>/dpad/down", "D-pad Down" },
			{ "<Gamepad>/dpad/left", "D-pad Left" },
			{ "<Gamepad>/dpad/right", "D-pad Right" },
			{ "<Gamepad>/start", "Start" },
			{ "<Gamepad>/select", "Select" },
			{ "<Gamepad>/leftStick/up", "Left Stick Up" },
			{ "<Gamepad>/leftStick/down", "Left Stick Down" },
			{ "<Gamepad>/leftStick/left", "Left Stick Left" },
			{ "<Gamepad>/leftStick/right", "Left Stick Right" },
			{ "<Gamepad>/rightStick/up", "Right Stick Up" },
			{ "<Gamepad>/rightStick/down", "Right Stick Down" },
			{ "<Gamepad>/rightStick/left", "Right Stick Left" },
			{ "<Gamepad>/rightStick/right", "Right Stick Right" }
		};

		private static readonly Dictionary<string, string> ControlPropertyAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "leftStickPress", "leftStickButton" },
			{ "rightStickPress", "rightStickButton" },
			{ "start", "startButton" },
			{ "select", "selectButton" }
		};

		private static readonly string[] CaptureOrder = new string[24]
		{
			"<Gamepad>/buttonSouth", "<Gamepad>/buttonEast", "<Gamepad>/buttonWest", "<Gamepad>/buttonNorth", "<Gamepad>/leftShoulder", "<Gamepad>/rightShoulder", "<Gamepad>/leftTrigger", "<Gamepad>/rightTrigger", "<Gamepad>/leftStickPress", "<Gamepad>/rightStickPress",
			"<Gamepad>/dpad/up", "<Gamepad>/dpad/down", "<Gamepad>/dpad/left", "<Gamepad>/dpad/right", "<Gamepad>/start", "<Gamepad>/select", "<Gamepad>/leftStick/up", "<Gamepad>/leftStick/down", "<Gamepad>/leftStick/left", "<Gamepad>/leftStick/right",
			"<Gamepad>/rightStick/up", "<Gamepad>/rightStick/down", "<Gamepad>/rightStick/left", "<Gamepad>/rightStick/right"
		};

		private bool showEditorWindow;

		private static readonly string[] GamepadUiOptions = new string[8] { "Xbox", "Steam Deck (Xbox)", "PlayStation 5 / DualSense", "PlayStation 4 / DualShock", "Steam Deck (PlayStation)", "Switch Pro", "Switch Joy-Con", "Switch 2 Joy-Con / Mouse" };

		private static readonly Dictionary<string, string> GamepadUiInstanceFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Xbox", "xboxMapInstance" },
			{ "Steam Deck (Xbox)", "steamDeckXboxMapInstance" },
			{ "PlayStation 5 / DualSense", "dualSenseMapInstance" },
			{ "PlayStation 4 / DualShock", "dualShockMapInstance" },
			{ "Steam Deck (PlayStation)", "steamDeckPSMapInstance" },
			{ "Switch Pro", "switchProMapInstance" },
			{ "Switch Joy-Con", "switchJoyConMapInstance" },
			{ "Switch 2 Joy-Con / Mouse", "switchJoyCon2MouseMapInstance" }
		};

		private static readonly Dictionary<string, string> GamepadUiPrefabFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "Xbox", "xboxMapPrefab" },
			{ "Steam Deck (Xbox)", "steamDeckXboxMapPrefab" },
			{ "PlayStation 5 / DualSense", "ps5MapPrefab" },
			{ "PlayStation 4 / DualShock", "oldPsMapPrefab" },
			{ "Steam Deck (PlayStation)", "steamDeckPSMapPrefab" },
			{ "Switch Pro", "switchProMapPrefab" },
			{ "Switch Joy-Con", "switchJoyConMapPrefab" },
			{ "Switch 2 Joy-Con / Mouse", "switchJoyCon2MouseMapPrefab" }
		};

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

		private static readonly Dictionary<string, string> GamepadMapFieldByPath = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
		{
			{ "<Gamepad>/buttonSouth", "joyButton0" },
			{ "<Gamepad>/buttonEast", "joyButton1" },
			{ "<Gamepad>/buttonWest", "joyButton2" },
			{ "<Gamepad>/buttonNorth", "joyButton3" },
			{ "<Gamepad>/leftShoulder", "joyButton4" },
			{ "<Gamepad>/rightShoulder", "joyButton5" },
			{ "<Gamepad>/select", "joyButton6" },
			{ "<Gamepad>/start", "joyButton7" },
			{ "<Gamepad>/leftTrigger", "joyAxis9" },
			{ "<Gamepad>/rightTrigger", "joyAxis10" },
			{ "<Gamepad>/leftStickPress", "joyButton8" },
			{ "<Gamepad>/rightStickPress", "joyButton9" },
			{ "<Gamepad>/dpad/up", "joyAxis7Up" },
			{ "<Gamepad>/dpad/down", "joyAxis7Down" },
			{ "<Gamepad>/dpad/left", "joyAxis6Left" },
			{ "<Gamepad>/dpad/right", "joyAxis6Right" }
		};

		private static readonly string[] LeftStickDirectionPaths = new string[4] { "<Gamepad>/leftStick/up", "<Gamepad>/leftStick/down", "<Gamepad>/leftStick/left", "<Gamepad>/leftStick/right" };

		private static readonly string[] RightStickDirectionPaths = new string[4] { "<Gamepad>/rightStick/up", "<Gamepad>/rightStick/down", "<Gamepad>/rightStick/left", "<Gamepad>/rightStick/right" };

		private static readonly Dictionary<string, Type> SuccessfulTypeLookupCache = new Dictionary<string, Type>(StringComparer.Ordinal);

		internal static bool ProximityInteractionFeatureEnabled
		{
			get
			{
				if ((Object)(object)Instance != (Object)null)
				{
					return Instance.IsProximityInteractionEnabled();
				}
				return false;
			}
		}

		internal static bool ProximityInteractionDebugLogging
		{
			get
			{
				if ((Object)(object)Instance != (Object)null && Instance.debug != null)
				{
					return Instance.debug.Value;
				}
				return false;
			}
		}

		private static bool DefaultRequiresAlternateFunction(string action)
		{
			if (string.Equals(action, "JoyDodge", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (!NativeAlternateFunctionDefaults.Contains(action) && !string.Equals(action, "JoySit", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(action, "JoyHide", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool IsForcedInternalUnbound(string action)
		{
			if (!string.IsNullOrEmpty(action))
			{
				return ForcedUnboundInternalActions.Contains(action);
			}
			return false;
		}

		private static bool IsNativeOwnedFullMapAction(string action)
		{
			if (!string.IsNullOrEmpty(action))
			{
				return NativeOwnedFullMapActions.Contains(action);
			}
			return false;
		}

		private static bool IsNativeOwnedReadOnlyAction(string action)
		{
			if (!string.IsNullOrEmpty(action))
			{
				return NativeOwnedReadOnlyActions.Contains(action);
			}
			return false;
		}

		private void LoadGlobalSettingsFile()
		{
			loadedGlobalSettings.Clear();
			globalSettingsFileExisted = !string.IsNullOrWhiteSpace(globalSettingsFilePath) && File.Exists(globalSettingsFilePath);
			if (!globalSettingsFileExisted)
			{
				return;
			}
			try
			{
				string[] array = File.ReadAllLines(globalSettingsFilePath);
				for (int i = 0; i < array.Length; i++)
				{
					string text = (array[i] ?? string.Empty).Trim();
					if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal) || text.StartsWith(";", StringComparison.Ordinal))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num > 0)
					{
						string text2 = text.Substring(0, num).Trim();
						string value = text.Substring(num + 1).Trim();
						if (text2.Length > 0)
						{
							loadedGlobalSettings[text2] = value;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not read mod settings file '" + globalSettingsFilePath + "': " + ex.Message));
			}
			if (!loadedGlobalSettings.TryGetValue("SettingsFormatVersion", out var value2) || !string.Equals(value2, "2", StringComparison.Ordinal))
			{
				loadedGlobalSettings.Clear();
				globalSettingsFileExisted = false;
			}
		}

		private string GetInitialGlobalString(string settingsKey, string defaultValue)
		{
			if (!loadedGlobalSettings.TryGetValue(settingsKey, out var value))
			{
				return defaultValue;
			}
			return value ?? defaultValue;
		}

		private bool GetInitialGlobalBool(string settingsKey, bool defaultValue)
		{
			if (!bool.TryParse(GetInitialGlobalString(settingsKey, defaultValue.ToString()), out var result))
			{
				return defaultValue;
			}
			return result;
		}

		private unsafe KeyCode GetInitialGlobalKeyCode(string settingsKey, KeyCode defaultValue)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!Enum.TryParse<KeyCode>(GetInitialGlobalString(settingsKey, ((object)(*(KeyCode*)(&defaultValue))/*cast due to .constrained prefix*/).ToString()), ignoreCase: true, out KeyCode result))
			{
				return defaultValue;
			}
			return result;
		}

		private void InitializeRuntimeSettings()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			LoadGlobalSettingsFile();
			debug = new RuntimeSetting<bool>(GetInitialGlobalBool("DebugLogging", defaultValue: false));
			editorKey = new RuntimeSetting<KeyCode>(GetInitialGlobalKeyCode("OpenEditorKey", (KeyCode)289));
			showEditor = new RuntimeSetting<bool>(GetInitialGlobalBool("ShowEditor", defaultValue: true));
			proximityInteractionEnabled = new RuntimeSetting<bool>(initialValue: false);
			string text = GetInitialGlobalString("GamepadUI", "Xbox");
			if (string.Equals(text, "Auto / Game default", StringComparison.OrdinalIgnoreCase))
			{
				text = "Xbox";
			}
			gamepadUiSelection = new RuntimeSetting<string>(string.IsNullOrWhiteSpace(text) ? "Xbox" : text);
			nintendoFaceButtonNormalization = new RuntimeSetting<bool>(GetInitialGlobalBool("NintendoFaceButtonNormalization", defaultValue: true));
			crouchInput = new RuntimeSetting<string>(string.Empty);
			crouchUseChord = new RuntimeSetting<bool>(initialValue: false);
			blockJumpToDodge = new RuntimeSetting<bool>(initialValue: true);
			moveWhileContainerGuiOpen = new RuntimeSetting<bool>(initialValue: false);
			buildCopyInput = new RuntimeSetting<string>(string.Empty);
			buildCopyRequiresAlt = new RuntimeSetting<bool>(initialValue: true);
			buildHammerInput = new RuntimeSetting<string>(string.Empty);
		}

		private string BuildGlobalSettingsText()
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: 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)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# Controller / Gamepad Remapping mod settings");
			stringBuilder.AppendLine("# Controller layouts, bindings, and layout-owned behaviors are stored only in *.preset files.");
			stringBuilder.AppendLine("# This is the mod-level config. Layout data belongs in individual *.preset files.");
			stringBuilder.AppendLine("SettingsFormatVersion=2");
			stringBuilder.AppendLine("DebugLogging=" + (debug != null && debug.Value));
			stringBuilder.AppendLine("OpenEditorKey=" + ((editorKey != null) ? ((object)editorKey.Value/*cast due to .constrained prefix*/).ToString() : ((object)(KeyCode)289/*cast due to .constrained prefix*/).ToString()));
			stringBuilder.AppendLine("ShowEditor=" + (showEditor == null || showEditor.Value));
			stringBuilder.AppendLine("GamepadUI=" + ((gamepadUiSelection != null) ? (gamepadUiSelection.Value ?? "Xbox") : "Xbox"));
			stringBuilder.AppendLine("NintendoFaceButtonNormalization=" + (nintendoFaceButtonNormalization == null || nintendoFaceButtonNormalization.Value));
			return stringBuilder.ToString();
		}

		private void SaveGlobalSettingsIfChanged()
		{
			if (string.IsNullOrWhiteSpace(globalSettingsFilePath) || debug == null || editorKey == null || showEditor == null || gamepadUiSelection == null || nintendoFaceButtonNormalization == null)
			{
				return;
			}
			string text = BuildGlobalSettingsText();
			if (string.Equals(text, lastSavedGlobalSettingsSignature, StringComparison.Ordinal))
			{
				return;
			}
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(globalSettingsFilePath) ?? Paths.ConfigPath);
				string text2 = globalSettingsFilePath + ".tmp";
				File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				File.Copy(text2, globalSettingsFilePath, overwrite: true);
				File.Delete(text2);
				lastSavedGlobalSettingsSignature = text;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not save mod settings file '" + globalSettingsFilePath + "': " + ex.Message));
			}
		}

		private void FinalizeGlobalSettingsInitialization()
		{
			EventHandler value = delegate
			{
				SaveGlobalSettingsIfChanged();
			};
			debug.SettingChanged += value;
			editorKey.SettingChanged += value;
			showEditor.SettingChanged += value;
			gamepadUiSelection.SettingChanged += value;
			nintendoFaceButtonNormalization.SettingChanged += value;
			string text = BuildGlobalSettingsText();
			HashSet<string> canonicalKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "SettingsFormatVersion", "DebugLogging", "OpenEditorKey", "ShowEditor", "GamepadUI", "NintendoFaceButtonNormalization" };
			if (globalSettingsFileExisted && !loadedGlobalSettings.Keys.Any((string key) => !canonicalKeys.Contains(key)) && loadedGlobalSettings.ContainsKey("SettingsFormatVersion") && loadedGlobalSettings.ContainsKey("DebugLogging") && loadedGlobalSettings.ContainsKey("OpenEditorKey") && loadedGlobalSettings.ContainsKey("ShowEditor") && loadedGlobalSettings.ContainsKey("GamepadUI") && loadedGlobalSettings.ContainsKey("NintendoFaceButtonNormalization"))
			{
				lastSavedGlobalSettingsSignature = text;
			}
			else
			{
				SaveGlobalSettingsIfChanged();
			}
		}

		private void Awake()
		{
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			string path = Paths.ConfigPath ?? string.Empty;
			customPresetDirectoryPath = Path.Combine(path, "ControllerPresets");
			customPresetStateFilePath = Path.Combine(customPresetDirectoryPath, "_ControllerPresets.state");
			globalSettingsFilePath = Path.Combine(customPresetDirectoryPath, "_ControllerGamepadRemapping.settings");
			try
			{
				Directory.CreateDirectory(customPresetDirectoryPath);
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not create custom preset directory '" + customPresetDirectoryPath + "': " + ex.Message));
			}
			InitializeRuntimeSettings();
			if (LoadSavedCustomPresets() == CustomPresetLoadSource.PerPresetFilesWithoutState)
			{
				SaveNamedPresetStore();
			}
			SavedCustomPreset savedCustomPreset = FindSavedCustomPreset(activeCustomPresetName);
			if (savedCustomPreset != null)
			{
				SetWorkingPresetBehaviors(savedCustomPreset.Behaviors, markDirty: false);
			}
			else if (savedCustomPresets.Count > 0)
			{
				SetWorkingPresetBehaviors(CreateNativePresetBehaviorDefaults(), markDirty: false);
			}
			crouchInput.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					ScheduleActionApply("JoyCrouch", 0.05f, "Crouch binding changed", waitForPhysicalNeutral: true);
				}
			};
			crouchUseChord.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					SetWorkingCrouchChord(crouchUseChord.Value);
				}
			};
			blockJumpToDodge.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					SetWorkingBlockJumpToDodge(blockJumpToDodge.Value);
				}
			};
			moveWhileContainerGuiOpen.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					SetWorkingMoveWhileGui(moveWhileContainerGuiOpen.Value);
				}
			};
			proximityInteractionEnabled.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					SetWorkingProximityInteraction(proximityInteractionEnabled.Value);
				}
			};
			buildCopyInput.SettingChanged += OnBuildCopyBindingChanged;
			buildCopyRequiresAlt.SettingChanged += OnBuildCopyBindingChanged;
			buildHammerInput.SettingChanged += OnBuildHammerBindingChanged;
			string[] digitalActions = DigitalActions;
			foreach (string action in digitalActions)
			{
				if (!IsForcedInternalUnbound(action) && !IsNativeOwnedReadOnlyAction(action))
				{
					EnsureBindingEntry(action);
				}
			}
			digitalActions = DigitalActions;
			foreach (string action2 in digitalActions)
			{
				if (!IsForcedInternalUnbound(action2) && !IsNativeOwnedReadOnlyAction(action2) && IsAlternateFunctionEligible(action2))
				{
					EnsureAlternateFunctionEntry(action2);
				}
			}
			digitalActions = ContextualGuiActions;
			foreach (string action3 in digitalActions)
			{
				EnsureGuiBindingEntry(action3);
			}
			FinalizeGlobalSettingsInitialization();
			harmony = new Harmony("nussbacker.valheim.controllergamepadremapping");
			harmony.PatchAll(typeof(Plugin).Assembly);
			TryPrepareZInputReflection();
			TryPrepareInputSystemReflection();
			TryPatchZInput();
			TryPatchEditorExclusiveCursorOwnership();
			TryPatchBuildRotateHintSource();
			TryPatchUniversalUnboundHintSources();
			TryPatchRadialUnboundHintSources();
			TryPatchContextualGuiActions();
			TryPatchNativeCraftQuantityContext();
			TryPatchMovementGuiLifecycleEvents();
			TryPatchControllerPresetLifecycle();
			TryPatchKeyboardMouseSettingsRollback();
			TryPatchTabSoundTrace();
			TryPatchBuildUiSelectableNavigation();
			TryPatchPlayerSetControls();
			TryPatchPlayerPlaceModeLifecycle();
			TryPatchBuildCopyPlayerUpdate();
			TryPatchNativeAltGuardPaths();
			TryPatchAuthoritativeGamepadMap();
			Log.LogInfo((object)"Controller / Gamepad Remapping 3.0.2 by nussbacker loaded. General controller remapping enabled. Optional behaviors: Crouch Chord, Block & Jump to Dodge, Move while in GUI, Proximity Interaction 1.0.7.");
			if (debug.Value)
			{
				Log.LogInfo((object)"Native-first controller engine enabled: Valheim ButtonDefs own normal remaps, Unbound and Alternate Function; custom runtime code is limited to contextual features.");
			}
		}

		private void OnBuildCopyBindingChanged(object sender, EventArgs e)
		{
			InvalidateResolvedControlCache();
			if (buildCopyInput == null || !string.Equals(buildCopyInput.Value, "__UNBOUND__", StringComparison.OrdinalIgnoreCase))
			{
				RestoreHiddenUnboundKeyHintsForAction("BuildCopy");
			}
			buildCopyChordWasHeld = false;
			buildCopyClearQuarantineAfterPlayerUpdate = false;
			buildCopyQuarantinedPaths.Clear();
			cachedBuildCopyHintTextObject = null;
			cachedBuildCopyHintTextObjects.Clear();
			keyHintPresentationCacheInitialized = false;
			buildCopyHintPresentationLogged = false;
			keyHintPresentationCacheValidationFrame = -1;
			if (!suppressPresetRuntimeStateEvents)
			{
				UpdateWorkingPresetContextualActionFromRuntimeState("BuildCopy");
				if (debug != null && debug.Value)
				{
					Log.LogInfo((object)("Copy binding presentation invalidated -> " + ((GetBuildCopyInputPath() != null) ? PrettyPath(GetBuildCopyInputPath()) : "Unbound") + (BuildCopyUsesAlternateFunction() ? " + Alternate Function" : "")));
				}
				if (showEditorWindow)
				{
					editorDeferredPresentationRefresh = true;
				}
				else
				{
					RefreshKeyHints();
				}
			}
		}

		private void OnBuildHammerBindingChanged(object sender, EventArgs e)
		{
			InvalidateResolvedControlCache();
			cachedBuildUiHammerTextObject = null;
			cachedBuildUiHammerOriginalGlyph = null;
			if (!suppressPresetRuntimeStateEvents)
			{
				UpdateWorkingPresetContextualActionFromRuntimeState("BuildHammer");
				if (movementGuiBuildSelectionOpen && cachedBuildUiHammerOwner != null)
				{
					TryRewriteBuildHammerPresentation(cachedBuildUiHammerOwner);
				}
			}
		}

		private void RefreshControllerPresentationViews()
		{
			RefreshKeyHints();
			RefreshGamepadGlyphEvents();
			RefreshVisibleGamepadMapLabelsOnly();
		}

		private void EnsureBindingEntry(string action)
		{
			if (bindings.ContainsKey(action))
			{
				return;
			}
			RuntimeSetting<string> runtimeSetting = new RuntimeSetting<string>(string.Empty);
			bindings[action] = runtimeSetting;
			string trackedAction = action;
			runtimeSetting.SettingChanged += delegate
			{
				InvalidateResolvedControlCache();
				bool flag = string.Equals(trackedAction, "JoyAltKeys", StringComparison.OrdinalIgnoreCase);
				if (flag)
				{
					InvalidateAlternateFunctionGlyphCache();
				}
				if (!IsExplicitlyUnbound(trackedAction))
				{
					RestoreHiddenUnboundKeyHintsForAction(trackedAction);
				}
				if (!suppressPresetRuntimeStateEvents)
				{
					ScheduleActionApply(trackedAction, 0.05f, "binding runtime state changed", waitForPhysicalNeutral: true);
					if (flag)
					{
						ResetRoutedLogicalStateForConfigurationChange("Alternate Function input changed");
					}
				}
			};
		}

		private void EnsureGuiBindingEntry(string action)
		{
			if (!IsContextualGuiAction(action) || guiBindings.ContainsKey(action))
			{
				return;
			}
			RuntimeSetting<string> runtimeSetting = new RuntimeSetting<string>(string.Empty);
			guiBindings[action] = runtimeSetting;
			RefreshContextualGuiCustomBindingFlag();
			runtimeSetting.SettingChanged += delegate
			{
				InvalidateResolvedControlCache();
				RefreshContextualGuiCustomBindingFlag();
				if (contextualGuiCustomBindingActive && harmony != null && !patchedInventoryGuiShortcutUpdate)
				{
					TryPatchContextualGuiActions();
				}
				if (!suppressPresetRuntimeStateEvents)
				{
					UpdateWorkingPresetContextualActionFromRuntimeState(action);
				}
			};
		}

		private void RefreshContextualGuiCustomBindingFlag()
		{
			contextualGuiCustomBindingActive = guiBindings.Values.Any((RuntimeSetting<string> entry) => entry != null && !string.IsNullOrWhiteSpace(entry.Value) && !string.Equals(entry.Value, "__UNBOUND__", StringComparison.OrdinalIgnoreCase));
		}

		private static bool IsContextualGuiAction(string action)
		{
			if (!string.IsNullOrEmpty(action))
			{
				return ContextualGuiActions.Any((string a) => string.Equals(a, action, StringComparison.OrdinalIgnoreCase));
			}
			return false;
		}

		private static bool IsAlternateFunctionEligible(string action)
		{
			if (string.IsNullOrEmpty(action) || IsForcedInternalUnbound(action) || IsNativeOwnedReadOnlyAction(action) || IsContextualGuiAction(action))
			{
				return false;
			}
			if (!string.Equals(action, "JoyAltKeys", StringComparison.OrdinalIgnoreCase))
			{
				return !string.Equals(action, "JoyAltPlace", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool IsMenuNavigationAction(string action)
		{
			if (!string.Equals(action, "JoyTabLeft", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyTabRight", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyMenu", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyBack", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyChat", StringComparison.OrdinalIgnoreCase) && !string.Equals(action, "JoyScrollChatUp", StringComparison.OrdinalIgnoreCase))
			{
				return string.Equals(action, "JoyScrollChatDown", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private void EnsureAlternateFunctionEntry(string action)
		{
			if (!IsAlternateFunctionEligible(action) || alternateFunctionRequirements.ContainsKey(action))
			{
				return;
			}
			bool initialValue = DefaultRequiresAlternateFunction(action);
			RuntimeSetting<bool> entry = new RuntimeSetting<bool>(initialValue);
			alternateFunctionRequirements[action] = entry;
			string trackedAction = action;
			entry.SettingChanged += delegate
			{
				if (!suppressPresetRuntimeStateEvents)
				{
					if (string.Equals(trackedAction, "JoyCrouch", StringComparison.OrdinalIgnoreCase) && entry.Value && IsCrouchChordEnabled())
					{
						SetWorkingCrouchChord(enabled: false);
						previousNativeCrouchHeld = false;
					}
					ScheduleActionApply(trackedAction, 0.01f, "Alternate Function requirement changed");
					RefreshControllerPresentationViews();
				}
			};
		}

		private bool RequiresAlternateFunction(string action)
		{
			if (string.Equals(action, "BuildCopy", StringComparison.OrdinalIgnoreCase))
			{
				return BuildCopyUsesAlternateFunction();
			}
			if (IsNativeOwnedReadOnlyAction(action))
			{
				if (originalBindings.TryGetValue(action, out var value) && value != null)
				{
					return value.AltKey;
				}
				return NativeAlternateFunctionDefaults.Contains(action);
			}
			if (!IsAlternateFunctionEligible(action))
			{
				return false;
			}
			if (TryGetWorkingLayoutBinding(action, out var binding))
			{
				return binding.AlternateFunction;
			}
			if (alternateFunctionRequirements.TryGetValue(action, out var value2) && value2 != null)
			{
				return value2.Value;
			}
			return false;
		}

		private bool IsValheimNativeAlternateAction(string action)
		{
			if (string.IsNullOrEmpty(action))
			{
				return false;
			}
			if (originalBindings.TryGetValue(action, out var value) && value != null)
			{
				return value.AltKey;
			}
			return false;
		}

		private bool IsModOwnedAlternateAction(string action)
		{
			if (!string.IsNullOrEmpty(action) && IsAlternateFunctionEligible(action) && !IsExplicitlyUnbound(action) && RequiresAlternateFunction(action))
			{
				return !IsValheimNativeAlternateAction(action);
			}
			return false;
		}

		private void RefreshActiveNativeAlternateActions()
		{
			activeNativeAlternateActions.Clear();
			activeModOwnedAlternateActions.Clear();
			string[] digitalActions = DigitalActions;
			foreach (string text in digitalActions)
			{
				if (IsAlternateFunctionEligible(text) && !IsExplicitlyUnbound(text) && RequiresAlternateFunction(text))
				{
					activeNativeAlternateActions.Add(text);
					if (IsModOwnedAlternateAction(text))
					{
						activeModOwnedAlternateActions.Add(text);
					}
				}
			}
			nativePlayerModifierOwnershipPossible = IsNormalActionOnAlternateModifierPath("JoyAttack") || IsNormalActionOnAlternateModifierPath("JoySecondaryAttack") || IsNormalActionOnAlternateModifierPath("JoyBlock") || IsNormalActionOnAlternateModifierPath("JoyJump") || IsNormalActionOnAlternateModifierPath("JoyRun") || IsNormalActionOnAlternateModifierPath("JoyCrouch") || IsNormalActionOnAlternateModifierPath("JoyDodge");
			RefreshModOwnedAlternateOwnershipCache();
			RefreshModOwnedAlternateConsumerPatches();
		}

		private void RefreshModOwnedAlternateOwnershipCache()
		{
			activeModOwnedAlternatePaths.Clear();
			activeModOwnedAlternateShadowedActions.Clear();
			foreach (string activeModOwnedAlternateAction in activeModOwnedAlternateActions)
			{
				string text = NormalizeBindingPath(GetConfiguredRuntimePath(activeModOwnedAlternateAction));
				if (text != null)
				{
					activeModOwnedAlternatePaths.Add(text);
				}
			}
			if (activeModOwnedAlternatePaths.Count == 0)
			{
				modOwnedAlternateOwnedPathsUntilRelease.Clear();
				modOwnedAlternateOwnedPathLatchFrame.Clear();
				return;
			}
			string[] digitalActions = DigitalActions;
			foreach (string text2 in digitalActions)
			{
				if (!string.IsNullOrEmpty(text2) && !activeModOwnedAlternateActions.Contains(text2) && !IsExplicitlyUnbound(text2) && !RequiresAlternateFunction(text2) && !IsMenuNavigationAction(text2) && !string.Equals(text2, "JoyAltKeys", StringComparison.OrdinalIgnoreCase) && !string.Equals(text2, "JoyAltPlace", StringComparison.OrdinalIgnoreCase))
				{
					string path = NormalizeBindingPath(GetConfiguredRuntimePath(text2));
					if (path != null && activeModOwnedAlternatePaths.Contains(path) && (!string.Equals(text2, "JoyRun", StringComparison.OrdinalIgnoreCase) || activeModOwnedAlternateActions.Any((string owner) => (GetConflictContexts(owner) & ConflictContext.Building) == 0 && string.Equals(NormalizeBindingPath(GetConfiguredRuntimePath(owner)), path, StringComparison.OrdinalIgnoreCase))))
					{
						activeModOwnedAlternateShadowedActions.Add(text2);
					}
				}
			}
			modOwnedAlternateOwnedPathsUntilRelease.RemoveWhere((string item) => !activeModOwnedAlternatePaths.Contains(item));
			digitalActions = modOwnedAlternateOwnedPathLatchFrame.Keys.ToArray();
			foreach (string text3 in digitalActions)
			{
				if (!activeModOwnedAlternatePaths.Contains(text3))
				{
					modOwnedAlternateOwnedPathLatchFrame.Remove(text3);
				}
			}
		}

		private bool IsAnyNativeAlternateChordHeld()
		{
			string text = NormalizeBindingPath(GetConfiguredRuntimePath("JoyAltKeys"));
			if (text == null || !IsCurrentPhysicalPathPressed(text))
			{
				return false;
			}
			foreach (string activeNativeAlternateAction in activeNativeAlternateActions)
			{
				if (activeModOwnedAlternateActions.Contains(activeNativeAlternateAction))
				{
					string text2 = NormalizeBindingPath(GetConfiguredRuntimePath(activeNativeAlternateAction));
					if (text2 != null && !string.Equals(text2, text, StringComparison.OrdinalIgnoreCase) && IsCurrentPhysicalPathPressed(text2))
					{
						return true;
					}
				}
				else if (GetNativeActionState(activeNativeAlternateAction, 0))
				{
					return true;
				}
			}
			return false;
		}

		private bool IsAnyNonBuildingAlternateChordHeld()
		{
			string text = NormalizeBindingPath(GetConfiguredRuntimePath("JoyAltKeys"));
			if (text == null || !IsCurrentPhysicalPathPressed(text))
			{
				return false;
			}
			foreach (string activeNativeAlternateAction in activeNativeAlternateActions)
			{
				if ((GetConflictContexts(activeNativeAlternateAction) & ConflictContext.Building) != ConflictContext.None)
				{
					continue;
				}
				if (activeModOwnedAlternateActions.Contains(activeNativeAlternateAction))
				{
					string text2 = NormalizeBindingPath(GetConfiguredRuntimePath(activeNativeAlternateAction));
					if (text2 != null && !string.Equals(text2, text, StringComparison.OrdinalIgnoreCase) && IsCurrentPhysicalPathPressed(text2))
					{
						return true;
					}
				}
				else if (GetNativeActionState(activeNativeAlternateAction, 0))
				{
					return true;
				}
			}
			return false;
		}

		private bool IsGameplayBlockingUiVisible()
		{
			if (!movementGuiMenuOpen && !movementGuiSettingsOpen && !movementGuiInventoryOpen && !movementGuiStoreOpen && !movementGuiBuildSelectionOpen && !movementGuiTextInputOpen)
			{
				return movementGuiFullMapOpen;
			}
			return true;
		}

		private bool IsContainerGuiMovementActive()
		{
			if (EditorInputBlocked || !IsMoveWhileGuiEnabled())
			{
				return false;
			}
			if (!movementGuiInventoryOpen && !movementGuiStoreOpen && !movementGuiBuildSelectionOpen)
			{
				return movementGuiFullMapOpen;
			}
			return true;
		}

		private string GetContainerGuiNavigationSource()
		{
			if (movementGuiFullMapOpen)
			{
				return "Right Stick";
			}
			if (movementGuiInventoryOpen && !movementGuiSplitDialogOpen)
			{
				return "Right Stick";
			}
			return "Both";
		}

		private static string GetPhysicalVectorPathPrefix(string source)
		{
			if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase))
			{
				return "<Gamepad>/dpad/";
			}
			if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase))
			{
				return "<Gamepad>/rightStick/";
			}
			if (string.Equals(source, "Left Stick", StringComparison.OrdinalIgnoreCase))
			{
				return "<Gamepad>/leftStick/";
			}
			return null;
		}

		private bool TryReadNativeZInputRightStick(out Vector2 value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			value = Vector2.zero;
			if (zInputGetJoyRightStickX == null || zInputGetJoyRightStickY == null)
			{
				return false;
			}
			try
			{
				object obj = (zInputGetJoyRightStickX.IsStatic ? null : GetZInputInstance());
				object obj2 = (zInputGetJoyRightStickY.IsStatic ? null : GetZInputInstance());
				if ((!zInputGetJoyRightStickX.IsStatic && obj == null) || (!zInputGetJoyRightStickY.IsStatic && obj2 == null))
				{
					return false;
				}
				float num = Convert.ToSingle(zInputGetJoyRightStickX.Invoke(obj, null));
				float num2 = Convert.ToSingle(zInputGetJoyRightStickY.Invoke(obj2, null));
				value = new Vector2(num, num2);
				if (((Vector2)(ref value)).sqrMagnitude < 0.01f)
				{
					value = Vector2.zero;
				}
				if (((Vector2)(ref value)).sqrMagnitude > 1f)
				{
					((Vector2)(ref value)).Normalize();
				}
				return true;
			}
			catch
			{
				value = Vector2.zero;
				return false;
			}
		}

		private bool TryReadPhysicalGamepadVector(string source, out Vector2 value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			value = Vector2.zero;
			if (string.Equals(source, "Both", StringComparison.OrdinalIgnoreCase))
			{
				Vector2 value2;
				bool num = TryReadPhysicalGamepadVector("D-pad", out value2);
				Vector2 value3;
				bool flag = TryReadPhysicalGamepadVector("Right Stick", out value3);
				if (!num && !flag)
				{
					return false;
				}
				value = value2 + value3;
				if (((Vector2)(ref value)).sqrMagnitude < 0.01f)
				{
					value = Vector2.zero;
				}
				if (((Vector2)(ref value)).sqrMagnitude > 1f)
				{
					((Vector2)(ref value)).Normalize();
				}
				return true;
			}
			if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase) && TryReadNativeZInputRightStick(out value))
			{
				return true;
			}
			try
			{
				Gamepad current = Gamepad.current;
				if (current != null)
				{
					if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase))
					{
						value = ((InputControl<Vector2>)(object)current.dpad).ReadValue();
					}
					else if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase))
					{
						value = ((InputControl<Vector2>)(object)current.rightStick).ReadValue();
					}
					else
					{
						value = ((InputControl<Vector2>)(object)current.leftStick).ReadValue();
					}
					if (((Vector2)(ref value)).sqrMagnitude < 0.01f)
					{
						value = Vector2.zero;
					}
					if (((Vector2)(ref value)).sqrMagnitude > 1f)
					{
						((Vector2)(ref value)).Normalize();
					}
					return true;
				}
			}
			catch
			{
			}
			string physicalVectorPathPrefix = GetPhysicalVectorPathPrefix(source);
			if (physicalVectorPathPrefix == null)
			{
				return false;
			}
			float num2 = (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "right") ? 1f : 0f) - (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "left") ? 1f : 0f);
			float num3 = (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "up") ? 1f : 0f) - (IsCurrentPhysicalPathPressed(physicalVectorPathPrefix + "down") ? 1f : 0f);
			value = new Vector2(num2, num3);
			if (((Vector2)(ref value)).sqrMagnitude > 1f)
			{
				((Vector2)(ref value)).Normalize();
			}
			return true;
		}

		private bool TryGetContainerGuiNavigationAxis(MethodBase originalMethod, out float axis)
		{
			//IL_0084: 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)
			axis = 0f;
			bool flag = originalMethod != null && zInputGetJoyLeftStickX != null && originalMethod == zInputGetJoyLeftStickX;
			bool flag2 = originalMethod != null && zInputGetJoyLeftStickY != null && originalMethod == zInputGetJoyLeftStickY;
			if (!flag && !flag2)
			{
				return false;
			}
			if (!IsContainerGuiMovementActive())
			{
				return false;
			}
			string containerGuiNavigationSource = GetContainerGuiNavigationSource();
			TryReadPhysicalGamepadVector(containerGuiNavigationSource, out var value);
			axis = (flag ? value.x : value.y);
			return true;
		}

		private bool TryGetContainerGuiNavigationVector(MethodBase originalMethod, out Vector2 vector)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			vector = Vector2.zero;
			if (originalMethod == null || zInputGetJoyLeftStick == null || originalMethod != zInputGetJoyLeftStick)
			{
				return false;
			}
			if (!IsContainerGuiMovementActive())
			{
				return false;
			}
			string containerGuiNavigationSource = GetContainerGuiNavigationSource();
			return TryReadPhysicalGamepadVector(containerGuiNavigationSource, out vector);
		}

		private static bool TryGetGuiDirectionFromActionName(string action, out string direction)
		{
			direction = null;
			if (string.IsNullOrEmpty(action))
			{
				return false;
			}
			if (string.Equals(action, "JoyDPadUp", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickUp", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickUp", StringComparison.OrdinalIgnoreCase))
			{
				direction = "up";
			}
			else if (string.Equals(action, "JoyDPadDown", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickDown", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickDown", StringComparison.OrdinalIgnoreCase))
			{
				direction = "down";
			}
			else if (string.Equals(action, "JoyDPadLeft", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickLeft", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickLeft", StringComparison.OrdinalIgnoreCase))
			{
				direction = "left";
			}
			else if (string.Equals(action, "JoyDPadRight", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyLStickRight", StringComparison.OrdinalIgnoreCase) || string.Equals(action, "JoyRStickRight", StringComparison.OrdinalIgnoreCase))
			{
				direction = "right";
			}
			return direction != null;
		}

		private bool TryReadPhysicalPathState(string path, int stateKind, out bool result)
		{
			result = false;
			object controlForPath = GetControlForPath(path);
			if (controlForPath == null)
			{
				return false;
			}
			switch (stateKind)
			{
			case 1:
				return TryGetControlBoolProperty(controlForPath, "wasPressedThisFrame", out result);
			case 2:
				return TryGetControlBoolProperty(controlForPath, "wasReleasedThisFrame", out result);
			default:
				result = IsControlPressed(controlForPath);
				return true;
			}
		}

		private bool TryGetPhysicalGuiDirectionState(string source, string direction, int stateKind, out bool result)
		{
			//IL_0067: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			result = false;
			string physicalVectorPathPrefix = GetPhysicalVectorPathPrefix(source);
			if (physicalVectorPathPrefix != null && TryReadPhysicalPathState(physicalVectorPathPrefix + direction, stateKind, out result))
			{
				return true;
			}
			if (!TryReadPhysicalGamepadVector(source, out var value))
			{
				return false;
			}
			switch (direction)
			{
			case "up":
				result = value.y > 0.5f;
				break;
			case "down":
				result = value.y < -0.5f;
				break;
			case "left":
				result = value.x < -0.5f;
				break;
			case "right":
				result = value.x > 0.5f;
				break;
			}
			return true;
		}

		private static string GetNativeGuiDirectionAction(string source, string direction)
		{
			if (string.IsNullOrEmpty(direction))
			{
				return null;
			}
			string text;
			switch (direction)
			{
			case "up":
				text = "Up";
				break;
			case "down":
				text = "Down";
				break;
			case "left":
				text = "Left";
				break;
			case "right":
				text = "Right";
				break;
			default:
				return null;
			}
			if (string.Equals(source, "D-pad", StringComparison.OrdinalIgnoreCase))
			{
				return "JoyDPad" + text;
			}
			if (string.Equals(source, "Right Stick", StringComparison.OrdinalIgnoreCase))
			{
				return "JoyRStick" + text;
			}
			return null;
		}

		private bool TryInvokeNativeGuiDirectionDown(string source, string direction, out bool result)
		{
			result = false;
			if (zInputGetButtonDown == null)
			{
				return false;
			}
			if (string.Equals(source, "Both", StringComparison.OrdinalIgnoreCase))
			{
				bool result2;
				bool num = TryInvokeNativeGuiDirectionDown("D-pad", direction, out result2);
				bool result3;
				bool flag = TryInvokeNativeGuiDirectionDown("Right Stick", direction, out result3);
				if (!num && !flag)
				{
					return false;
				}
				result = result2 || result3;
				return true;
			}
			string nativeGuiDirectionAction = GetNativeGuiDirectionAction(source, direction);
			if (string.IsNullOrEmpty(nativeGuiDirectionAction))
			{
				return false;
			}
			try
			{
				object obj = (zInputGetButtonDown.IsStatic ? null : GetZInputInstance());
				if (!zInputGetButtonDown.IsStatic && obj == null)
				{
					return false;
				}
				nativeGuiDirectionQueryInProgress = true;
				result = Convert.ToBoolean(zInputGetButtonDown.Invoke(obj, new object[1] { nativeGuiDirectionAction }));
				return true;
			}
			catch
			{
				result = false;
				return false;
			}
			finally
			{
				nativeGuiDirectionQueryInProgress = false;
			}
		}

		private bool TryGetContainerGuiDirectionalActionState(string action, int stateKind, out bool result)
		{
			result = false;
			if (!TryGetGuiDirectionFromActionName(action, out var direction))
			{
				return false;
			}
			if (!IsContainerGuiMovementActive())
			{
				return false;
			}
			string containerGuiNavigationSource = GetContainerGuiNavigationSource();
			if (movementGuiFullMapOpen && action.StartsWith("JoyDPad", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (stateKind == 1 && movementGuiInventoryOpen && !movementGuiSplitDialogOpen && TryInvokeNativeGuiDirectionDown(containerGuiNavigationSource, direction, out result))
			{
				return true;
			}
			if (!string.Equals(containerGuiNavigationSource, "Both", StringComparison.OrdinalIgnoreCase))
			{
				return TryGetPhysicalGuiDirectionState(containerGuiNavigationSource, direction, stateKind, out result);
			}
			bool result2;
			bool num = TryGetPhysicalGuiDirectionState("D-pad", direction, stateKind, out result2);
			bool result3;
			bool flag = TryGetPhysicalGuiDirectionState("Right Stick", direction, stateKind, out result3);
			if (!num && !flag)
			{
				return false;
			}
			if (stateKind == 0)
			{
				result = result2 || result3;
				return true;
			}
			TryGetPhysicalGuiDirectionState("D-pad", direction, 0, out var result4);
			TryGetPhysicalGuiDirectionState("Right Stick", direction, 0, out var result5);
			if (stateKind == 1)
			{
				result = (result2 && (!result5 || result3)) || (result3 && (!result4 || result2));
			}
			else
			{
				result = (result2 && !result5) || (result3 && !result4);
			}
			return true;
		}

		private bool IsBuildUiManualNavigationActive()
		{
			if (!EditorInputBlocked && IsMoveWhileGuiEnabled())
			{
				return movementGuiBuildSelectionOpen;
			}
			return false;
		}

		private static bool BuildUiSelectableOnMovePrefix(object __instance)
		{
			Plugin instance = Instance;
			if ((Object)(object)instance == (Object)null || manualUnityUiMoveDispatchInProgress)
			{
				return true;
			}
			if (instance.IsBuildUiManualNavigationActive())
			{
				return false;
			}
			if (instance.IsSplitDialogManualNavigationActive() && instance.IsSplitDialogMoveTarget(__instance))
			{
				return false;
			}
			return true;
		}

		private void TryPatchBuildUiSelectableNavigation()
		{
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			if (patchedBuildUiSelectableNavigation || harmony == null)
			{
				return;
			}
			try
			{
				buildUiSelectableType = buildUiSelectableType ?? FindType("UnityEngine.UI.Selectable") ?? FindType("Selectable");
				buildUiEventSystemType = buildUiEventSystemType ?? FindType("UnityEngine.EventSystems.EventSystem") ?? FindType("EventSystem");
				if (buildUiSelectableType == null || buildUiEventSystemType == null)
				{
					return;
				}
				bool flag = false;
				string[] array = new string[4] { "UnityEngine.UI.Selectable", "UnityEngine.UI.Slider", "BuildUiPieceButton", "BuildUiTagButton" };
				for (int i = 0; i < array.Length; i++)
				{
					Type type = FindType(array[i]);
					if (type == null)
					{
						continue;
					}
					foreach (MethodInfo item in from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
						where string.Equals(m.Name, "OnMove", StringComparison.Ordinal) && m.GetParameters().Length == 1
						select m)
					{
						flag = true;
						if (!patchedBuildUiMoveMethods.Contains(item))
						{
							harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(Plugin), "BuildUiSelectableOnMovePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							patchedBuildUiMoveMethods.Add(item);
						}
					}
				}
				if (flag)
				{
					buildUiEventSystemCurrentProperty = buildUiEventSystemType.GetProperty("current", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					buildUiCurrentSelectedGameObjectProperty = buildUiEventSystemType.GetProperty("currentSelectedGameObject", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					buildUiSelectableFindUp = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnUp", 0);
					buildUiSelectableFindDown = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnDown", 0);
					buildUiSelectableFindLeft = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnLeft", 0);
					buildUiSelectableFindRight = FindMethodByArityAndName(buildUiSelectableType, "FindSelectableOnRight", 0);
					buildUiSelectableSelect = FindMethodByArityAndName(buildUiSelectableType, "Select", 0);
					patchedBuildUiSelectableNavigation = buildUiEventSystemCurrentProperty != null && buildUiCurrentSelectedGameObjectProperty != null && buildUiSelectableFindUp != null && buildUiSelectableFindDown != null && buildUiSelectableFindLeft != null && buildUiSelectableFindRight != null && buildUiSelectableSelect != null;
					if (patchedBuildUiSelectableNavigation && debug != null && debug.Value)
					{
						Log.LogInfo((object)"Patched Unity Selectable.OnMove for BuildUi and split-stack automatic GUI Navigation source control.");
					}
				}
			}
			catch (Exception ex)
			{
				if (debug != null && debug.Value)
				{
					Log.LogWarning((object)("Could not patch BuildUi Selectable navigation: " + ex.GetBaseException().Message));
				}
			}
		}

		private void TryPatchBuildRotateHintSource()
		{
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Expected O, but got Unknown
			if (harmony == null)
			{
				return;
			}
			List<MethodInfo> list = new List<MethodInfo>();
			Type type = FindType("KeyHints");
			if (type != null)
			{
				if (cachedKeyHintsRefreshMethod == null)
				{
					cachedKeyHintsRefreshMethod = FindMethodByArityAndName(type, "UpdateGamepadHints", 0) ?? FindMethodByArityAndName(type, "UpdateHints", 0);
				}
				try
				{
					MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo methodInfo in methods)
					{
						if (!(methodInfo == null) && !(methodInfo.DeclaringType != type) && !methodInfo.ContainsGenericParameters && (string.Equals(methodInfo.Name, "UpdateHints", StringComparison.Ordinal) || string.Equals(methodInfo.Name, "UpdateGamepadHints", StringComparison.Ordinal)))
						{
							list.Add(methodInfo);
						}
					}
				}
				catch
				{
				}
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type[] array = null;
				try
				{
					array = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex)
				{
					array = ex.Types;
				}
				catch
				{
					continue;
				}
				if (array == null)
				{
					continue;
				}
				Type[] array2 = array;
				foreach (Type type2 in array2)
				{
					if (type2 == null)
					{
						continue;
					}
					MethodInfo[] methods2;
					try
					{
						methods2 = type2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					}
					catch
					{
						continue;
					}
					MethodInfo[] methods = methods2;
					foreach (MethodInfo methodInfo2 in methods)
					{
						if (!(methodInfo2 == null) && !methodInfo2.ContainsGenericParameters && string.Equals(methodInfo2.Name, "SetGamepadRotateText", StringComparison.Ordinal))
						{
							list.Add(methodInfo2);
						}
					}
				}
			}
			int num = 0;
			foreach (MethodInfo item in list.Distinct())
			{
				if (item == null || patchedKeyHintsRotateSourceMethods.Contains(item))
				{
					continue;
				}
				try
				{
					bool num2 = type != null && item.DeclaringType == type && (string.Equals(item.Name, "UpdateHints", StringComparison.Ordinal) || string.Equals(item.Name, "UpdateGamepadHints", StringComparison.Ordinal));
					HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "BuildRotateHintActionTranspiler", (Type[])null);
					HarmonyMethod val2 = ((!num2) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), "KeyHintsGenerationPrefix", (Type[])null));
					HarmonyMethod val3 = ((!num2) ? (item.IsStatic ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), "BuildRotateHintPresentationPostfix", (Type[])null)) : (item.IsStatic ? new HarmonyMethod(typeof(Plugin), "KeyHintsGenerationStaticPostfix", (Type[])null) : new HarmonyMethod(typeof(Plugin), "KeyHintsUpdatePostfix", (Type[])null)));
					harmony.Patch((MethodBase)item, val2, val3, val, (HarmonyMethod)null, (HarmonyMethod)null);
					patchedKeyHintsRotateSourceMethods.Add(item);
					num++;
					if (debug != null && debug.Value)
					{
						Log.LogInfo((object)("Patched build Rotate hint source method: " + ((item.DeclaringType != null) ? item.DeclaringType.FullName : "<unknown>") + "." + item.Name + "."));
					}
				}
				catch (Exception ex2)
				{
					if (debug != null && debug.Value)
					{
						Log.LogWarning((object)("Could not patch build Rotate hint source method " + item.Name + ": " + ex2.GetBaseException().Message));
					}
				}
			}
			if (num == 0 && debug != null && debug.Value)
			{
				Log.LogWarning((object)"No build Rotate hint source methods were patched.");
			}
		}

		private bool MethodCallsBoundKeyString(MethodInfo method)
		{
			if (method == null || zInputGetBoundKeyString == null)
			{
				return false;
			}
			try
			{
				byte[] array = method.GetMethodBody()?.GetILAsByteArray();
				if (array == null || array.Length < 5)
				{
					return false;
				}
				Module module = method.Module;
				for (int i = 0; i <= array.Length - 5; i++)
				{
					if (array[i] != 40 && array[i] != 111)
					{
						continue;
					}
					int metadataToken = array[i + 1] | (array[i + 2] << 8) | (array[i + 3] << 16) | (array[i + 4] << 24);
					MethodBase methodBase;
					try
					{
						methodBase = module.ResolveMethod(metadataToken);
					}
					catch
					{
						continue;
					}
					if (!(methodBase == null) && !(methodBase.DeclaringType != zInputGetBoundKeyString.DeclaringType) && string.Equals(methodBase.Name, zInputGetBoundKeyString.Name, StringComparison.Ordinal))
					{
						ParameterInfo[] parameters;
						try
						{
							parameters = methodBase.GetParameters();
						}
						catch
						{
							continue;
						}
						if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(bool))
						{
							return true;
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private void TryPatchUniversalUnboundHintSources()
		{
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			//IL_0188: Expected O, but got Unknown
			if (universalUnboundHintDiscoveryComplete || harmony == null || zInputGetBoundKeyString == null)
			{
				return;
			}
			universalUnboundHintDiscoveryComplete = true;
			int num = 0;
			Ass