Decompiled source of Emote Deck v2.0.3

plugins/Emote_Deck/Emote_Deck.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Project too-many-emotes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Project too-many-emotes")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a36e8062-eaaf-433e-bd67-e49ec781b591")]
[assembly: AssemblyFileVersion("2.0.3.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("2.0.3.0")]
namespace ATLYSSEmoteDeck;

[BepInPlugin("com.eleen.atlyss.emotedeck", "Emote Deck", "2.0.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInProcess("ATLYSS.exe")]
[DefaultExecutionOrder(-9000)]
public class EmoteDeckPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.eleen.atlyss.emotedeck";

	public const string PluginName = "Emote Deck";

	public const string PluginVersion = "2.0.3";

	public const int MaxPages = 10;

	public const int MaxSlots = 100;

	internal static EmoteDeckPlugin Instance;

	internal static ManualLogSource Log;

	internal static ConfigEntry<string> CfgMainWindowKey;

	internal static ConfigEntry<string> CfgGridWindowKey;

	internal static ConfigEntry<float> CfgMainX;

	internal static ConfigEntry<float> CfgMainY;

	internal static ConfigEntry<float> CfgMainW;

	internal static ConfigEntry<float> CfgMainH;

	internal static ConfigEntry<float> CfgMainOpacity;

	internal static ConfigEntry<float> CfgGridX;

	internal static ConfigEntry<float> CfgGridY;

	internal static ConfigEntry<float> CfgGridW;

	internal static ConfigEntry<float> CfgGridH;

	internal static ConfigEntry<float> CfgGridOpacity;

	internal static ConfigEntry<int> CfgGridColumns;

	internal static ConfigEntry<float> CfgGridIconAspect;

	internal static ConfigEntry<bool> CfgGridAutoColumns;

	internal static ConfigEntry<bool> CfgGridAutoIconAspect;

	internal static ConfigEntry<bool> CfgCloseGridAfterEmote;

	internal static ConfigEntry<string> CfgGridViewMode;

	internal static ConfigEntry<string> CfgGridMouseMode;

	internal static ConfigEntry<bool> CfgGridShowHeaderControls;

	internal static ConfigEntry<int> CfgActiveSlotCount;

	internal static ConfigEntry<bool> CfgBlockGameInputWhileMainWindowOpen;

	internal static ConfigEntry<bool> CfgBlockGameInputWhileGridWindowOpen;

	internal static ConfigEntry<bool> CfgDebugLogging;

	internal static ConfigEntry<bool> CfgIncludeNativeVanillaWhenWheelBaseExists;

	internal static ConfigEntry<string> CfgHiddenPackages;

	internal static ConfigEntry<bool> CfgEnableChatCommands;

	internal static ConfigEntry<bool> CfgEnableNamedSlotCommands;

	internal static ConfigEntry<bool> CfgEnableClosestNameMatch;

	internal static ConfigEntry<bool> CfgClosestMatchIncludeCustomCommands;

	internal static ConfigEntry<bool> CfgEnableCustomChatPrefix;

	internal static ConfigEntry<string> CfgChatCommandPrefix;

	internal static ConfigEntry<bool> CfgEnableCustomSlotCommands;

	internal static ConfigEntry<string> CfgDeckSlotKeys;

	internal static ConfigEntry<string> CfgDeckCustomCommands;

	internal static ConfigEntry<bool> CfgMigratedCompactSlotStorage;

	internal static readonly ConfigEntry<string>[] CfgSlotCustomCommands = new ConfigEntry<string>[100];

	internal static readonly ConfigEntry<string>[] CfgPageCustomCommands = new ConfigEntry<string>[10];

	internal static ConfigEntry<bool> CfgEnablePages;

	internal static ConfigEntry<int> CfgPageCount;

	internal static ConfigEntry<int> CfgCurrentPage;

	internal static ConfigEntry<string>[] CfgPageSlotKeys = new ConfigEntry<string>[10];

	internal static ConfigEntry<bool> CfgEnableRecent;

	internal static ConfigEntry<int> CfgRecentLimit;

	internal static ConfigEntry<string> CfgRecentKeys;

	internal static ConfigEntry<bool> CfgEnableFavorites;

	internal static ConfigEntry<string> CfgFavoriteKeys;

	internal static ConfigEntry<bool> CfgAutoRescanEnabled;

	internal static ConfigEntry<bool> CfgPickerAutoAdvanceSlot;

	internal static ConfigEntry<bool> CfgPickerHideAssigned;

	internal static ConfigEntry<bool> CfgMigratedAutoAdvanceDefaultOff;

	internal static ConfigEntry<bool> CfgSettingsDeckOpen;

	internal static ConfigEntry<bool> CfgSettingsSystemsOpen;

	internal static ConfigEntry<bool> CfgSettingsEmotesOpen;

	internal static ConfigEntry<bool> CfgSettingsChatOpen;

	internal static ConfigEntry<bool> CfgSettingsGridOpen;

	internal static ConfigEntry<bool> CfgSettingsPickerOpen;

	internal static ConfigEntry<bool> CfgSettingsKeybindsOpen;

	internal static ConfigEntry<bool> CfgSettingsAdvancedOpen;

	internal static readonly ConfigEntry<string>[] CfgSlotEmoteKeys = new ConfigEntry<string>[100];

	private KeyCode _mainWindowKey = (KeyCode)121;

	private KeyCode _gridWindowKey = (KeyCode)116;

	private EmoteDeckWindow _window;

	private Harmony _harmony;

	private float _nextAutoRefreshTime;

	private bool _forceNextAutoRefresh;

	private bool _configSaveQueued;

	private float _configSaveAt;

	private readonly HashSet<KeyCode> _suppressedHotkeyActivations = new HashSet<KeyCode>();

	private int _suppressHotkeysUntilFrame;

	internal readonly List<EmoteEntry> AllEmotes = new List<EmoteEntry>();

	internal readonly Dictionary<string, EmoteEntry> EmotesByKey = new Dictionary<string, EmoteEntry>(StringComparer.OrdinalIgnoreCase);

	internal readonly List<string> PackageIds = new List<string>();

	internal readonly HashSet<string> HiddenPackages = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	internal readonly string[][] PageSlotKeys = new string[10][];

	internal readonly string[][] PageCustomCommands = new string[10][];

	internal readonly List<string> RecentKeys = new List<string>();

	internal readonly List<string> FavoriteKeys = new List<string>();

	private int _lastRegistryFingerprint;

	private static FieldInfo _chatEmoteBufferField;

	internal bool EmoteWheelBridgeAvailable { get; private set; }

	internal int LastWheelEmoteCount { get; private set; }

	internal int LastNativeEmoteCount { get; private set; }

	internal float LastRegistryRefreshTime { get; private set; }

	internal int RegistryRevision { get; private set; }

	internal int FilterRevision { get; private set; }

	internal int SlotRevision { get; private set; }

	internal int RecentRevision { get; private set; }

	internal int FavoriteRevision { get; private set; }

	internal KeyCode MainWindowKey => _mainWindowKey;

	internal KeyCode GridWindowKey => _gridWindowKey;

	private void Awake()
	{
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfig();
		ReloadHotkeysFromConfig();
		LoadHiddenPackagesFromConfig();
		LoadPageSlotsFromConfig();
		LoadCustomCommandsFromConfig();
		LoadRecentAndFavoritesFromConfig();
		RefreshEmoteRegistry();
		ScheduleAutoRescan(1f, force: false);
		TryPatchHarmonyHooks();
		_window = new EmoteDeckWindow(this);
		TrySetupEasySettingsBridge();
		Log.LogInfo((object)("[EmoteDeck] Loaded 2.0.3 mainKey=" + ((object)Unsafe.As<KeyCode, KeyCode>(ref _mainWindowKey)/*cast due to .constrained prefix*/).ToString() + " gridKey=" + ((object)Unsafe.As<KeyCode, KeyCode>(ref _gridWindowKey)/*cast due to .constrained prefix*/).ToString() + " emotes=" + AllEmotes.Count));
	}

	private void BindConfig()
	{
		CfgMainWindowKey = ((BaseUnityPlugin)this).Config.Bind<string>("Input", "MainWindowKey", "Y", "Open/close the Emote Deck main window. Set to None to disable the global main-window hotkey.");
		CfgGridWindowKey = ((BaseUnityPlugin)this).Config.Bind<string>("Input", "GridWindowKey", "T", "Open/close the Emote Grid window. Set to None to disable the global grid hotkey.");
		CfgMainX = ((BaseUnityPlugin)this).Config.Bind<float>("MainWindow", "X", 240f, "Main window X position.");
		CfgMainY = ((BaseUnityPlugin)this).Config.Bind<float>("MainWindow", "Y", 120f, "Main window Y position.");
		CfgMainW = ((BaseUnityPlugin)this).Config.Bind<float>("MainWindow", "Width", 620f, "Main window width.");
		CfgMainH = ((BaseUnityPlugin)this).Config.Bind<float>("MainWindow", "Height", 640f, "Main window height.");
		CfgMainOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("MainWindow", "ContentOpacity", 0.92f, "Opacity for main window content/background below the header. 0 = transparent, 1 = opaque.");
		CfgGridX = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "X", 260f, "Grid window X position.");
		CfgGridY = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "Y", 160f, "Grid window Y position.");
		CfgGridW = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "Width", 720f, "Grid window width.");
		CfgGridH = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "Height", 480f, "Grid window height.");
		CfgGridOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "Opacity", 0.88f, "Grid window content/background opacity. 0 = transparent, 1 = opaque.");
		CfgGridColumns = ((BaseUnityPlugin)this).Config.Bind<int>("GridWindow", "Columns", 8, "Column count when AutoColumns is off.");
		CfgGridIconAspect = ((BaseUnityPlugin)this).Config.Bind<float>("GridWindow", "IconAspectRatio", 1f, "Icon width/height ratio when AutoIconAspectRatio is off. 1.0 = square, 1.3 = wider, 0.8 = taller.");
		CfgGridAutoColumns = ((BaseUnityPlugin)this).Config.Bind<bool>("GridWindow", "AutoColumns", true, "Choose columns from the current grid width. This prevents clipped columns and horizontal scrolling.");
		CfgGridAutoIconAspect = ((BaseUnityPlugin)this).Config.Bind<bool>("GridWindow", "AutoIconAspectRatio", true, "Use icon cells that scale with the grid width.");
		CfgCloseGridAfterEmote = ((BaseUnityPlugin)this).Config.Bind<bool>("GridWindow", "CloseAfterEmote", true, "Close the grid window after clicking an emote.");
		CfgGridViewMode = ((BaseUnityPlugin)this).Config.Bind<string>("GridWindow", "View", "Standard", "Grid view: Standard, Compact, Mini, or Names.");
		CfgGridViewMode.Value = NormalizeGridViewModeValue(CfgGridViewMode.Value);
		CfgGridMouseMode = ((BaseUnityPlugin)this).Config.Bind<string>("GridWindow", "MouseMode", "Auto", "Grid mouse mode: Auto, Always, or Off. Auto unlocks only when Close grid after emote is on.");
		CfgGridMouseMode.Value = NormalizeGridMouseModeValue(CfgGridMouseMode.Value);
		CfgGridShowHeaderControls = ((BaseUnityPlugin)this).Config.Bind<bool>("GridWindow", "ShowHeaderControls", true, "Show the second header row in the grid window.");
		CfgActiveSlotCount = ((BaseUnityPlugin)this).Config.Bind<int>("Behavior", "ActiveSlotCount", 24, "Number of slots shown on each deck page. Range 1-100.");
		CfgBlockGameInputWhileMainWindowOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "BlockGameInputWhileMainWindowOpen", false, "Block player movement, combat, hotbar, and interact input while the main window is open.");
		CfgBlockGameInputWhileGridWindowOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "BlockGameInputWhileGridWindowOpen", false, "Block player movement, combat, hotbar, and interact input while the grid window is open.");
		CfgIncludeNativeVanillaWhenWheelBaseExists = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "IncludeNativeVanillaWhenWheelBaseExists", false, "Show native /emote commands even when Emote Wheel Base is available. Keeping this off usually avoids duplicates.");
		CfgHiddenPackages = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "HiddenPackages", "", "Semicolon-separated package IDs hidden in Picker and Grid.");
		CfgEnableChatCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "EnableChatCommands", true, "Allow commands like /ed 1 to play deck slots.");
		CfgEnableNamedSlotCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "EnableNamedSlotCommands", true, "Allow /ed commands that use emote names, like /ed point or /ed fist bump.");
		CfgEnableClosestNameMatch = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "EnableClosestNameMatch", false, "If an /ed name command is close enough, play the nearest matching emote.");
		CfgClosestMatchIncludeCustomCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "ClosestMatchIncludeCustomCommands", false, "Let closest match also check custom slot commands.");
		CfgEnableCustomChatPrefix = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "EnableCustomPrefix", false, "Allow the /ed command prefix to be changed.");
		CfgChatCommandPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("ChatCommands", "CommandPrefix", "/ed", "Prefix used for Emote Deck chat commands.");
		CfgChatCommandPrefix.Value = NormalizeChatCommandPrefix(CfgChatCommandPrefix.Value);
		CfgEnableCustomSlotCommands = ((BaseUnityPlugin)this).Config.Bind<bool>("ChatCommands", "EnableCustomSlotCommands", false, "Allow direct custom commands like /pnt. These can overlap with other slash commands.");
		CfgEnablePages = ((BaseUnityPlugin)this).Config.Bind<bool>("Pages", "EnablePages", false, "Enable separate Emote Deck pages.");
		CfgPageCount = ((BaseUnityPlugin)this).Config.Bind<int>("Pages", "PageCount", 4, "Number of deck pages when pages are enabled. Range 1-10.");
		CfgCurrentPage = ((BaseUnityPlugin)this).Config.Bind<int>("Pages", "CurrentPage", 1, "Current deck page. Range 1-10.");
		for (int i = 1; i < 10; i++)
		{
			CfgPageSlotKeys[i] = ((BaseUnityPlugin)this).Config.Bind<string>("Pages", "Page" + (i + 1).ToString("D2", CultureInfo.InvariantCulture) + "Slots", "", "Packed slot keys for page " + (i + 1) + ".");
			CfgPageCustomCommands[i] = ((BaseUnityPlugin)this).Config.Bind<string>("CustomCommands", "Page" + (i + 1).ToString("D2", CultureInfo.InvariantCulture) + "Commands", "", "Packed custom chat commands for page " + (i + 1) + ".");
		}
		CfgEnableRecent = ((BaseUnityPlugin)this).Config.Bind<bool>("Recent", "EnableRecent", false, "Show a Recent grid mode.");
		CfgRecentLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Recent", "RecentLimit", 24, "Maximum recent emotes to keep. Range 1-100.");
		CfgRecentKeys = ((BaseUnityPlugin)this).Config.Bind<string>("Recent", "RecentKeys", "", "Packed recent emote keys.");
		CfgEnableFavorites = ((BaseUnityPlugin)this).Config.Bind<bool>("Favorites", "EnableFavorites", false, "Show a Favorites grid mode and star buttons.");
		CfgFavoriteKeys = ((BaseUnityPlugin)this).Config.Bind<string>("Favorites", "FavoriteKeys", "", "Packed favorite emote keys.");
		CfgAutoRescanEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "AutoRescan", true, "Rescan after windows open so newly loaded emotes can appear.");
		CfgPickerAutoAdvanceSlot = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "PickerAutoAdvanceSlot", false, "After setting an emote in Picker, move to the next slot.");
		CfgPickerHideAssigned = ((BaseUnityPlugin)this).Config.Bind<bool>("Behavior", "PickerHideAssigned", false, "Hide emotes already assigned on any enabled page.");
		CfgMigratedAutoAdvanceDefaultOff = ((BaseUnityPlugin)this).Config.Bind<bool>("Migrations", "AutoAdvanceDefaultOff_0110", false, "Internal migration flag. Do not edit.");
		if (!CfgMigratedAutoAdvanceDefaultOff.Value)
		{
			CfgPickerAutoAdvanceSlot.Value = false;
			CfgMigratedAutoAdvanceDefaultOff.Value = true;
		}
		CfgDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Enable debug logs.");
		CfgSettingsDeckOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "DeckOpen", true, "Show the Deck settings section.");
		CfgSettingsSystemsOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "SystemsOpen", true, "Show the Pages/Recent/Favorites settings section.");
		CfgSettingsEmotesOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "EmotesOpen", true, "Show the Emotes and pack filters settings section.");
		CfgSettingsChatOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "ChatOpen", true, "Show the Chat commands settings section.");
		CfgSettingsGridOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "GridOpen", true, "Show the Grid settings section.");
		CfgSettingsPickerOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "PickerOpen", true, "Show the Picker settings section.");
		CfgSettingsKeybindsOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "KeybindsOpen", true, "Show the Keybinds settings section.");
		CfgSettingsAdvancedOpen = ((BaseUnityPlugin)this).Config.Bind<bool>("SettingsUI", "AdvancedOpen", true, "Show the Advanced settings section.");
		CfgDeckSlotKeys = ((BaseUnityPlugin)this).Config.Bind<string>("Deck", "Page01Slots", DefaultPageOneSlots(), "Packed slot keys for page 1. Semicolon-separated. Empty trailing slots are omitted.");
		CfgDeckCustomCommands = ((BaseUnityPlugin)this).Config.Bind<string>("Deck", "Page01Commands", "", "Packed custom slot commands for page 1. Semicolon-separated. Empty trailing slots are omitted.");
		CfgMigratedCompactSlotStorage = ((BaseUnityPlugin)this).Config.Bind<bool>("Migrations", "CompactSlotStorage_0201", false, "Internal migration flag. Do not edit.");
		MigrateLegacySlotConfigIfNeeded();
	}

	private static string DefaultPageOneSlots()
	{
		return "Base:Point;Base:Clap;Base:Think;Base:Shrug;Base:Dance;Base:Taunt";
	}

	private void MigrateLegacySlotConfigIfNeeded()
	{
		string[] array = new string[100];
		string[] array2 = new string[100];
		UnpackConfigList((CfgDeckSlotKeys != null) ? CfgDeckSlotKeys.Value : DefaultPageOneSlots(), array);
		UnpackConfigList((CfgDeckCustomCommands != null) ? CfgDeckCustomCommands.Value : string.Empty, array2);
		bool flag = false;
		bool flag2 = false;
		for (int i = 0; i < 100; i++)
		{
			string key = "Slot" + (i + 1).ToString("D3", CultureInfo.InvariantCulture);
			if (TryGetRawConfigValue("Slots", key, out var value))
			{
				array[i] = value ?? string.Empty;
				flag = true;
				if (!string.IsNullOrEmpty(array[i]))
				{
					flag2 = true;
				}
			}
			if (TryGetRawConfigValue("CustomCommands", key, out var value2))
			{
				array2[i] = (TryNormalizeCustomSlotCommand(value2, out var normalized, out var _) ? normalized : string.Empty);
				flag = true;
				if (!string.IsNullOrEmpty(array2[i]))
				{
					flag2 = true;
				}
			}
		}
		if (flag)
		{
			BackupLegacyConfigFileIfNeeded();
			if (CfgDeckSlotKeys != null)
			{
				CfgDeckSlotKeys.Value = PackConfigList(array);
			}
			if (CfgDeckCustomCommands != null)
			{
				CfgDeckCustomCommands.Value = PackConfigList(array2);
			}
			RemoveLegacySlotConfigEntries();
			if (CfgMigratedCompactSlotStorage != null)
			{
				CfgMigratedCompactSlotStorage.Value = true;
			}
			SavePluginConfig();
			if (flag2 && Log != null)
			{
				Log.LogInfo((object)"[EmoteDeck] Legacy slot config migrated into compact storage.");
			}
		}
		else if (CfgMigratedCompactSlotStorage != null && !CfgMigratedCompactSlotStorage.Value)
		{
			CfgMigratedCompactSlotStorage.Value = true;
			SavePluginConfig();
		}
	}

	private bool TryGetRawConfigValue(string section, string key, out string value)
	{
		value = null;
		if (TryGetRawConfigValueFromLoadedConfig(section, key, out value))
		{
			return true;
		}
		if (TryGetRawConfigValueFromFile(section, key, out value))
		{
			return true;
		}
		return false;
	}

	private bool TryGetRawConfigValueFromLoadedConfig(string section, string key, out string value)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Expected O, but got Unknown
		value = null;
		try
		{
			ConfigDefinition val = new ConfigDefinition(section, key);
			PropertyInfo property = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				object value2 = property.GetValue(((BaseUnityPlugin)this).Config, null);
				if (value2 is IDictionary dictionary)
				{
					if (dictionary.Contains(val))
					{
						object obj = dictionary[val];
						value = ((obj != null) ? obj.ToString() : string.Empty);
						return true;
					}
					foreach (DictionaryEntry item in dictionary)
					{
						if (ConfigDefinitionMatches(item.Key, section, key))
						{
							object value3 = item.Value;
							value = ((value3 != null) ? value3.ToString() : string.Empty);
							return true;
						}
					}
				}
			}
			MethodInfo method = typeof(ConfigFile).GetMethod("TryGetEntry", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method != null)
			{
				object[] array = new object[2] { val, null };
				object obj2 = method.Invoke(((BaseUnityPlugin)this).Config, array);
				if (obj2 is bool && (bool)obj2 && array.Length > 1 && array[1] != null)
				{
					object obj3 = array[1];
					PropertyInfo property2 = obj3.GetType().GetProperty("BoxedValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					object obj4 = ((property2 != null) ? property2.GetValue(obj3, null) : obj3);
					value = ((obj4 != null) ? obj4.ToString() : string.Empty);
					return true;
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static bool ConfigDefinitionMatches(object definition, string section, string key)
	{
		if (definition == null)
		{
			return false;
		}
		try
		{
			ConfigDefinition val = (ConfigDefinition)((definition is ConfigDefinition) ? definition : null);
			if (val != (ConfigDefinition)null)
			{
				return string.Equals(val.Section, section, StringComparison.OrdinalIgnoreCase) && string.Equals(val.Key, key, StringComparison.OrdinalIgnoreCase);
			}
			Type type = definition.GetType();
			PropertyInfo property = type.GetProperty("Section", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			PropertyInfo property2 = type.GetProperty("Key", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			string a = ((property != null) ? (property.GetValue(definition, null) as string) : null);
			string a2 = ((property2 != null) ? (property2.GetValue(definition, null) as string) : null);
			return string.Equals(a, section, StringComparison.OrdinalIgnoreCase) && string.Equals(a2, key, StringComparison.OrdinalIgnoreCase);
		}
		catch
		{
		}
		return false;
	}

	private bool TryGetRawConfigValueFromFile(string section, string key, out string value)
	{
		value = null;
		try
		{
			string configFilePath = GetConfigFilePath();
			if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
			{
				return false;
			}
			string a = string.Empty;
			string[] array = File.ReadAllLines(configFilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i] ?? string.Empty;
				string text2 = text.Trim();
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]") && text2.Length >= 2)
				{
					a = text2.Substring(1, text2.Length - 2).Trim();
				}
				else
				{
					if (!string.Equals(a, section, StringComparison.OrdinalIgnoreCase))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num >= 0)
					{
						string a2 = text.Substring(0, num).Trim();
						if (string.Equals(a2, key, StringComparison.OrdinalIgnoreCase))
						{
							value = text.Substring(num + 1).Trim();
							return true;
						}
					}
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static string GetConfigFilePath()
	{
		try
		{
			string text = Path.Combine(Paths.ConfigPath, "com.eleen.atlyss.emotedeck.cfg");
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
		}
		catch
		{
		}
		return null;
	}

	private void BackupLegacyConfigFileIfNeeded()
	{
		try
		{
			string configFilePath = GetConfigFilePath();
			if (!string.IsNullOrEmpty(configFilePath) && File.Exists(configFilePath))
			{
				string text = configFilePath + ".legacy-slot-backup";
				if (!File.Exists(text))
				{
					File.Copy(configFilePath, text, overwrite: false);
				}
			}
		}
		catch
		{
		}
	}

	private void RemoveLegacySlotConfigEntries()
	{
		for (int i = 0; i < 100; i++)
		{
			string key = "Slot" + (i + 1).ToString("D3", CultureInfo.InvariantCulture);
			RemoveConfigEntryIfPresent("Slots", key);
			RemoveConfigEntryIfPresent("CustomCommands", key);
		}
	}

	private void RemoveConfigEntryIfPresent(string section, string key)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Expected O, but got Unknown
		try
		{
			ConfigDefinition val = new ConfigDefinition(section, key);
			bool flag = false;
			MethodInfo method = typeof(ConfigFile).GetMethod("Remove", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(ConfigDefinition) }, null);
			if (method != null)
			{
				object obj = method.Invoke(((BaseUnityPlugin)this).Config, new object[1] { val });
				flag = !(obj is bool) || (bool)obj;
			}
			PropertyInfo property = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				object value = property.GetValue(((BaseUnityPlugin)this).Config, null);
				if (value is IDictionary dictionary)
				{
					if (dictionary.Contains(val))
					{
						dictionary.Remove(val);
						flag = true;
					}
					else
					{
						ArrayList arrayList = new ArrayList();
						foreach (DictionaryEntry item in dictionary)
						{
							if (ConfigDefinitionMatches(item.Key, section, key))
							{
								arrayList.Add(item.Key);
							}
						}
						for (int i = 0; i < arrayList.Count; i++)
						{
							dictionary.Remove(arrayList[i]);
							flag = true;
						}
					}
				}
			}
			if (flag)
			{
			}
		}
		catch
		{
		}
	}

	private static void UnpackConfigList(string raw, string[] target)
	{
		if (target != null)
		{
			for (int i = 0; i < target.Length; i++)
			{
				target[i] = string.Empty;
			}
			string[] array = SplitConfigList(raw);
			int num = Math.Min(target.Length, array.Length);
			for (int j = 0; j < num; j++)
			{
				target[j] = array[j] ?? string.Empty;
			}
		}
	}

	private static string[] SplitConfigList(string raw)
	{
		if (string.IsNullOrEmpty(raw))
		{
			return new string[0];
		}
		return raw.Split(new char[1] { ';' }, StringSplitOptions.None);
	}

	private static string PackConfigList(string[] values)
	{
		if (values == null || values.Length == 0)
		{
			return string.Empty;
		}
		int num = -1;
		for (int num2 = values.Length - 1; num2 >= 0; num2--)
		{
			if (!string.IsNullOrEmpty(values[num2]))
			{
				num = num2;
				break;
			}
		}
		if (num < 0)
		{
			return string.Empty;
		}
		string[] array = new string[num + 1];
		for (int i = 0; i <= num; i++)
		{
			array[i] = values[i] ?? string.Empty;
		}
		return string.Join(";", array);
	}

	internal static string NormalizeGridViewModeValue(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return "Standard";
		}
		if (string.Equals(value, "Standard", StringComparison.OrdinalIgnoreCase))
		{
			return "Standard";
		}
		if (string.Equals(value, "Compact", StringComparison.OrdinalIgnoreCase))
		{
			return "Compact";
		}
		if (string.Equals(value, "Mini", StringComparison.OrdinalIgnoreCase))
		{
			return "Mini";
		}
		if (string.Equals(value, "Names", StringComparison.OrdinalIgnoreCase))
		{
			return "Names";
		}
		if (string.Equals(value, "Names only", StringComparison.OrdinalIgnoreCase))
		{
			return "Names";
		}
		return "Standard";
	}

	internal static string GridViewModeLabel(string value)
	{
		value = NormalizeGridViewModeValue(value);
		if (value == "Names")
		{
			return "Names only";
		}
		return value;
	}

	internal static string NormalizeGridMouseModeValue(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return "Auto";
		}
		if (string.Equals(value, "Auto", StringComparison.OrdinalIgnoreCase))
		{
			return "Auto";
		}
		if (string.Equals(value, "Always", StringComparison.OrdinalIgnoreCase))
		{
			return "Always";
		}
		if (string.Equals(value, "Off", StringComparison.OrdinalIgnoreCase))
		{
			return "Off";
		}
		if (string.Equals(value, "Never", StringComparison.OrdinalIgnoreCase))
		{
			return "Off";
		}
		return "Auto";
	}

	internal static string GridMouseModeLabel(string value)
	{
		return NormalizeGridMouseModeValue(value);
	}

	internal static string NormalizeChatCommandPrefix(string value)
	{
		string text = (value ?? string.Empty).Trim();
		if (text.Length == 0)
		{
			return "/ed";
		}
		if (!text.StartsWith("/"))
		{
			text = "/" + text;
		}
		int num = text.IndexOf(' ');
		if (num >= 0)
		{
			text = text.Substring(0, num);
		}
		text = text.Replace("<", string.Empty).Replace(">", string.Empty).Replace("\\0", string.Empty);
		if (text.Length < 2)
		{
			return "/ed";
		}
		if (text.Length > 24)
		{
			text = text.Substring(0, 24);
		}
		return text;
	}

	internal static string GetActiveChatCommandPrefix()
	{
		if (CfgEnableCustomChatPrefix != null && CfgEnableCustomChatPrefix.Value)
		{
			return NormalizeChatCommandPrefix((CfgChatCommandPrefix != null) ? CfgChatCommandPrefix.Value : "/ed");
		}
		return "/ed";
	}

	internal static bool TryNormalizeCustomSlotCommand(string value, out string normalized, out string error)
	{
		normalized = string.Empty;
		error = null;
		string text = (value ?? string.Empty).Trim();
		if (text.Length == 0)
		{
			return true;
		}
		if (!text.StartsWith("/"))
		{
			text = "/" + text;
		}
		if (text.Length < 2)
		{
			error = "Command is too short.";
			return false;
		}
		if (text.Length > 32)
		{
			error = "Command is too long.";
			return false;
		}
		if (text.IndexOfAny(new char[4] { ' ', '\t', '\r', '\n' }) >= 0)
		{
			error = "Use one command word only.";
			return false;
		}
		if (text.IndexOf('<') >= 0 || text.IndexOf('>') >= 0)
		{
			error = "Rich text characters are not allowed.";
			return false;
		}
		string activeChatCommandPrefix = GetActiveChatCommandPrefix();
		if (string.Equals(text, "/ed", StringComparison.OrdinalIgnoreCase) || string.Equals(text, activeChatCommandPrefix, StringComparison.OrdinalIgnoreCase))
		{
			error = "That command is already used by the slot command prefix.";
			return false;
		}
		normalized = text;
		return true;
	}

	internal static bool IsBlockedMouseBindKey(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Invalid comparison between Unknown and I4
		return (int)key == 323 || (int)key == 324;
	}

	internal static string KeyLabel(string value)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(value))
		{
			return "None";
		}
		if (!Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
		{
			return value;
		}
		return KeyLabel(result);
	}

	internal unsafe static string KeyLabel(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Invalid comparison between Unknown and I4
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Invalid comparison between Unknown and I4
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Invalid comparison between Unknown and I4
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Invalid comparison between Unknown and I4
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Invalid comparison between Unknown and I4
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Invalid comparison between Unknown and I4
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Invalid comparison between Unknown and I4
		if ((int)key == 0)
		{
			return "None";
		}
		if ((int)key == 323)
		{
			return "Left Mouse";
		}
		if ((int)key == 324)
		{
			return "Right Mouse";
		}
		if ((int)key == 325)
		{
			return "Middle Mouse";
		}
		if ((int)key == 326)
		{
			return "Mouse 4";
		}
		if ((int)key == 327)
		{
			return "Mouse 5";
		}
		if ((int)key == 328)
		{
			return "Mouse 6";
		}
		if ((int)key == 329)
		{
			return "Mouse 7";
		}
		return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
	}

	internal void SavePluginConfig()
	{
		try
		{
			_configSaveQueued = false;
			((BaseUnityPlugin)this).Config.Save();
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Config save failed: " + ex.Message));
		}
	}

	internal void QueueConfigSave(float delaySeconds)
	{
		if (delaySeconds < 0.05f)
		{
			delaySeconds = 0.05f;
		}
		_configSaveQueued = true;
		_configSaveAt = Time.unscaledTime + delaySeconds;
	}

	internal void QueueConfigSave()
	{
		QueueConfigSave(0.6f);
	}

	internal void ScheduleAutoRescan(float delaySeconds, bool force)
	{
		if (CfgAutoRescanEnabled != null && CfgAutoRescanEnabled.Value)
		{
			if (delaySeconds < 0.05f)
			{
				delaySeconds = 0.05f;
			}
			float num = Time.unscaledTime + delaySeconds;
			if (_nextAutoRefreshTime <= 0f || num < _nextAutoRefreshTime)
			{
				_nextAutoRefreshTime = num;
			}
			if (force)
			{
				_forceNextAutoRefresh = true;
			}
		}
	}

	private void FlushQueuedConfigSaveIfDue()
	{
		if (_configSaveQueued && Time.unscaledTime >= _configSaveAt)
		{
			SavePluginConfig();
		}
	}

	private void OnDestroy()
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		EmoteDeckInputBlocker.SetWindowState(mainOpen: false, default(Rect), gridOpen: false, default(Rect), blockGameplayInput: false);
		try
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
			}
		}
		catch
		{
		}
		if (_window != null)
		{
			_window.SaveOpenWindowRects();
		}
		if (_configSaveQueued)
		{
			SavePluginConfig();
		}
	}

	private void TryPatchHarmonyHooks()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		try
		{
			if (_harmony == null)
			{
				_harmony = new Harmony("com.eleen.atlyss.emotedeck");
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Harmony init failed. Guards were skipped: " + ex.Message));
			return;
		}
		TryPatchScrollRectOnScroll();
		TryPatchGuiWindowPointerGuard();
		TryPatchUiRaycastGuards();
		TryPatchChatCommandGuard();
		TryPatchGameplayInputGuards();
	}

	private void TryPatchChatCommandGuard()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Expected O, but got Unknown
		try
		{
			if (_harmony == null)
			{
				_harmony = new Harmony("com.eleen.atlyss.emotedeck");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(ChatBehaviour), "Cmd_SendChatMessage", new Type[2]
			{
				typeof(string),
				typeof(ChatChannel)
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(EmoteDeckChatCommandPatch), "Prefix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Log.LogWarning((object)"[EmoteDeck] Chat command guard skipped: method lookup failed.");
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"[EmoteDeck] Chat command guard patched.");
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Chat command guard failed and was skipped: " + ex.Message));
		}
	}

	private void TryPatchScrollRectOnScroll()
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Expected O, but got Unknown
		try
		{
			Type type = FindType("UnityEngine.UI.ScrollRect");
			Type type2 = FindType("UnityEngine.EventSystems.PointerEventData");
			if (type == null || type2 == null)
			{
				Log.LogWarning((object)"[EmoteDeck] ScrollRect.OnScroll guard skipped: Unity UI/EventSystems types were not found.");
				return;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "OnScroll", new Type[1] { type2 }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(EmoteDeckScrollRectOnScrollPatch), "Prefix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Log.LogWarning((object)"[EmoteDeck] ScrollRect.OnScroll guard skipped: method lookup failed.");
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"[EmoteDeck] ScrollRect.OnScroll guard patched.");
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] ScrollRect.OnScroll guard failed and was skipped: " + ex.Message));
		}
	}

	private void TryPatchGuiWindowPointerGuard()
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Expected O, but got Unknown
		try
		{
			if (_harmony == null)
			{
				_harmony = new Harmony("com.eleen.atlyss.emotedeck");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(EmoteDeckGuiWindowPointerPatch), "Prefix", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)"[EmoteDeck] GUI.Window pointer guard skipped: prefix lookup failed.");
				return;
			}
			int num = 0;
			MethodInfo[] methods = typeof(GUI).GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (!(methodInfo2 == null) && !(methodInfo2.Name != "Window") && !(methodInfo2.ReturnType != typeof(Rect)))
				{
					ParameterInfo[] parameters = methodInfo2.GetParameters();
					if (parameters.Length >= 2 && !(parameters[0].ParameterType != typeof(int)) && !(parameters[1].ParameterType != typeof(Rect)))
					{
						_harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
					}
				}
			}
			Log.LogInfo((object)("[EmoteDeck] GUI.Window pointer guards patched: " + num + "."));
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] GUI.Window pointer guard failed and was skipped: " + ex.Message));
		}
	}

	private void TryPatchUiRaycastGuards()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Expected O, but got Unknown
		try
		{
			if (_harmony == null)
			{
				_harmony = new Harmony("com.eleen.atlyss.emotedeck");
			}
			Type type = FindType("UnityEngine.EventSystems.EventSystem");
			Type type2 = FindType("UnityEngine.EventSystems.PointerEventData");
			Type type3 = FindType("UnityEngine.EventSystems.RaycastResult");
			if (type == null || type2 == null || type3 == null)
			{
				Log.LogWarning((object)"[EmoteDeck] EventSystem.RaycastAll guard skipped: EventSystem types were not found.");
				return;
			}
			Type type4 = typeof(List<>).MakeGenericType(type3);
			MethodInfo methodInfo = AccessTools.Method(type, "RaycastAll", new Type[2] { type2, type4 }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(EmoteDeckEventSystemRaycastAllPatch), "Postfix", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Log.LogWarning((object)"[EmoteDeck] EventSystem.RaycastAll guard skipped: method lookup failed.");
				return;
			}
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)"[EmoteDeck] EventSystem.RaycastAll pointer guard patched.");
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] EventSystem.RaycastAll guard failed and was skipped: " + ex.Message));
		}
	}

	private void TryPatchGameplayInputGuards()
	{
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Expected O, but got Unknown
		try
		{
			int patched = 0;
			MethodInfo prefix = AccessTools.Method(typeof(EmoteDeckGameplayInputPatch), "Prefix", (Type[])null, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(typeof(EmoteDeckPlayerLocalParamsPatch), "Postfix", (Type[])null, (Type[])null);
			TryPatchPrefix(typeof(PlayerMove), "Handle_MovementControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerMove), "Handle_DashControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerMove), "Handle_JumpControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerCasting), "Client_SkillControl", prefix, ref patched);
			TryPatchPrefix(typeof(ActionBarManager), "OnActionkeyPress", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerCombat), "Client_Handle_WeaponSheatheControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerCombat), "Client_Handle_QuickSwapWeaponControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerCombat), "Client_Handle_CombatControl", prefix, ref patched);
			TryPatchPrefix(typeof(PlayerInteract), "Handle_InteractControl", prefix, ref patched);
			MethodInfo methodInfo2 = FindPlayerLocalParamsMethod();
			if (methodInfo2 != null && methodInfo != null)
			{
				_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				patched++;
			}
			else
			{
				Log.LogWarning((object)"[EmoteDeck] Player._inUI postfix guard skipped: method lookup failed.");
			}
			Log.LogInfo((object)("[EmoteDeck] Gameplay input guards patched: " + patched + "."));
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Gameplay input guard patch failed and was skipped: " + ex.Message));
		}
	}

	private void TryPatchPrefix(Type type, string methodName, MethodInfo prefix, ref int patched)
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Expected O, but got Unknown
		try
		{
			if (!(type == null) && !(prefix == null))
			{
				MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Log.LogWarning((object)("[EmoteDeck] Input guard skipped: " + type.Name + "." + methodName + " not found."));
				}
				else
				{
					_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					patched++;
				}
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Input guard failed: " + type.Name + "." + methodName + ": " + ex.Message));
		}
	}

	private static MethodInfo FindPlayerLocalParamsMethod()
	{
		try
		{
			MethodInfo[] methods = typeof(Player).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo == null))
				{
					string text = methodInfo.Name ?? string.Empty;
					if (text.IndexOf("Handle_LocalParams", StringComparison.Ordinal) >= 0)
					{
						return methodInfo;
					}
				}
			}
		}
		catch
		{
		}
		return null;
	}

	internal void ReloadHotkeysFromConfig()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		_mainWindowKey = ParseOptionalKeyCode(CfgMainWindowKey.Value, "MainWindowKey");
		_gridWindowKey = ParseOptionalKeyCode(CfgGridWindowKey.Value, "GridWindowKey");
	}

	private void TrySetupEasySettingsBridge()
	{
		EmoteDeckEasySettingsBridge.TryInstall(this);
		((MonoBehaviour)this).StartCoroutine(EmoteDeckEasySettingsBridge.DelayedInstall(this));
	}

	internal unsafe void SetMainWindowKeyFromEasySettings(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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)
		if (IsBlockedMouseBindKey(key))
		{
			EmoteDeckEasySettingsBridge.ResetMainKeyButton(this);
			return;
		}
		CfgMainWindowKey.Value = (((int)key == 0) ? "None" : ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString());
		ReloadHotkeysFromConfig();
		EmoteDeckEasySettingsBridge.SyncButtonsFromConfig(this);
		SuppressHotkeyActivationUntilReleased(key);
		QueueConfigSave(0.15f);
	}

	internal unsafe void SetGridWindowKeyFromEasySettings(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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)
		if (IsBlockedMouseBindKey(key))
		{
			EmoteDeckEasySettingsBridge.ResetGridKeyButton(this);
			return;
		}
		CfgGridWindowKey.Value = (((int)key == 0) ? "None" : ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString());
		ReloadHotkeysFromConfig();
		EmoteDeckEasySettingsBridge.SyncButtonsFromConfig(this);
		SuppressHotkeyActivationUntilReleased(key);
		QueueConfigSave(0.15f);
	}

	internal unsafe static KeyCode ParseRequiredKeyCode(string value, KeyCode fallback, string name)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (!string.IsNullOrWhiteSpace(value) && Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result) && (int)result != 0 && !IsBlockedMouseBindKey(result))
		{
			return result;
		}
		if (Log != null)
		{
			Log.LogWarning((object)("[EmoteDeck] Invalid or disabled required key " + name + "='" + value + "'. Falling back to " + ((object)(*(KeyCode*)(&fallback))/*cast due to .constrained prefix*/).ToString() + "."));
		}
		return fallback;
	}

	internal static KeyCode ParseOptionalKeyCode(string value, string name)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrWhiteSpace(value) || string.Equals(value, "None", StringComparison.OrdinalIgnoreCase))
		{
			return (KeyCode)0;
		}
		if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result) && !IsBlockedMouseBindKey(result))
		{
			return result;
		}
		if (Log != null)
		{
			Log.LogWarning((object)("[EmoteDeck] Invalid optional key " + name + "='" + value + "'. Disabling it."));
		}
		return (KeyCode)0;
	}

	internal void SuppressHotkeyActivationUntilReleased(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		if ((int)key != 0 && !IsBlockedMouseBindKey(key))
		{
			_suppressedHotkeyActivations.Add(key);
			int num = Time.frameCount + 2;
			if (num > _suppressHotkeysUntilFrame)
			{
				_suppressHotkeysUntilFrame = num;
			}
		}
	}

	private void UpdateSuppressedHotkeyActivations()
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		if (_suppressedHotkeyActivations.Count == 0 || Time.frameCount <= _suppressHotkeysUntilFrame)
		{
			return;
		}
		List<KeyCode> list = null;
		foreach (KeyCode suppressedHotkeyActivation in _suppressedHotkeyActivations)
		{
			if (!IsKeyCurrentlyHeld(suppressedHotkeyActivation))
			{
				if (list == null)
				{
					list = new List<KeyCode>();
				}
				list.Add(suppressedHotkeyActivation);
			}
		}
		if (list != null)
		{
			for (int i = 0; i < list.Count; i++)
			{
				_suppressedHotkeyActivations.Remove(list[i]);
			}
		}
	}

	private bool IsHotkeyActivationSuppressed(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		if ((int)key == 0)
		{
			return false;
		}
		return _suppressedHotkeyActivations.Contains(key);
	}

	private static bool IsKeyCurrentlyHeld(KeyCode key)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		if ((int)key == 0)
		{
			return false;
		}
		try
		{
			return Input.GetKey(key);
		}
		catch
		{
			return false;
		}
	}

	private void Update()
	{
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		if (_window == null)
		{
			return;
		}
		UpdateSuppressedHotkeyActivations();
		EmoteDeckEasySettingsBridge.PollSync(this);
		if (_window.IsCapturingKeyBind)
		{
			return;
		}
		if (CfgAutoRescanEnabled.Value && _nextAutoRefreshTime > 0f && Time.unscaledTime >= _nextAutoRefreshTime)
		{
			bool flag = _window.IsMainOpen || _window.IsGridOpen;
			bool flag2 = _forceNextAutoRefresh || AllEmotes.Count == 0;
			_forceNextAutoRefresh = false;
			if (flag2 || flag)
			{
				if (flag2)
				{
					RefreshEmoteRegistry();
				}
				else
				{
					RefreshEmoteRegistryIfChanged();
				}
				_nextAutoRefreshTime = (flag ? (Time.unscaledTime + ((AllEmotes.Count == 0) ? 3f : 10f)) : 0f);
			}
			else
			{
				_nextAutoRefreshTime = 0f;
			}
		}
		if (!IsChatTyping())
		{
			if ((int)_mainWindowKey != 0 && Input.GetKeyDown(_mainWindowKey) && !IsHotkeyActivationSuppressed(_mainWindowKey))
			{
				_window.ToggleMainWindow();
				return;
			}
			if ((int)_gridWindowKey != 0 && _gridWindowKey != _mainWindowKey && Input.GetKeyDown(_gridWindowKey) && !IsHotkeyActivationSuppressed(_gridWindowKey))
			{
				_window.ToggleGridWindow();
				return;
			}
		}
		FlushQueuedConfigSaveIfDue();
	}

	private void LateUpdate()
	{
		if (_window != null)
		{
			_window.MaintainInputState();
		}
	}

	private void OnGUI()
	{
		if (_window != null)
		{
			_window.OnGUI();
		}
	}

	internal int GetActiveSlotCount()
	{
		int num = CfgActiveSlotCount.Value;
		if (num < 1)
		{
			num = 1;
		}
		if (num > 100)
		{
			num = 100;
		}
		return num;
	}

	internal int GetPageCount()
	{
		int num = CfgPageCount.Value;
		if (num < 1)
		{
			num = 1;
		}
		if (num > 10)
		{
			num = 10;
		}
		return num;
	}

	internal int GetCurrentPageIndex()
	{
		if (!CfgEnablePages.Value)
		{
			return 0;
		}
		int pageCount = GetPageCount();
		int num = CfgCurrentPage.Value - 1;
		if (num < 0)
		{
			num = 0;
		}
		if (num >= pageCount)
		{
			num = pageCount - 1;
		}
		return num;
	}

	internal int GetEffectiveDeckPageCount()
	{
		return (!CfgEnablePages.Value) ? 1 : GetPageCount();
	}

	internal int GetTotalDeckSlotCapacity()
	{
		return GetActiveSlotCount() * GetEffectiveDeckPageCount();
	}

	internal string GetSlotKeyForPage(int pageIndex, int index)
	{
		if (index < 0 || index >= 100)
		{
			return string.Empty;
		}
		if (!CfgEnablePages.Value || pageIndex <= 0)
		{
			pageIndex = 0;
		}
		if (pageIndex >= 10)
		{
			return string.Empty;
		}
		EnsurePageSlotArray(pageIndex);
		return PageSlotKeys[pageIndex][index] ?? string.Empty;
	}

	internal int CountAssignedSlotsInPage(int pageIndex)
	{
		int num = 0;
		int activeSlotCount = GetActiveSlotCount();
		for (int i = 0; i < activeSlotCount; i++)
		{
			if (TryGetSlotEmoteForPage(pageIndex, i, out var _))
			{
				num++;
			}
		}
		return num;
	}

	internal int CountAssignedSlotsAcrossEnabledPages()
	{
		int num = 0;
		int effectiveDeckPageCount = GetEffectiveDeckPageCount();
		for (int i = 0; i < effectiveDeckPageCount; i++)
		{
			num += CountAssignedSlotsInPage(i);
		}
		return num;
	}

	internal void SetCurrentPageIndex(int pageIndex)
	{
		int pageCount = GetPageCount();
		if (pageIndex < 0)
		{
			pageIndex = 0;
		}
		if (pageIndex >= pageCount)
		{
			pageIndex = pageCount - 1;
		}
		CfgCurrentPage.Value = pageIndex + 1;
		QueueConfigSave();
	}

	internal string GetSlotKey(int index)
	{
		return GetSlotKeyForPage(GetCurrentPageIndex(), index);
	}

	internal void TouchSlotRevision()
	{
		SlotRevision++;
	}

	internal void SetSlotKey(int index, string key)
	{
		if (index >= 0 && index < 100)
		{
			int num = GetCurrentPageIndex();
			if (!CfgEnablePages.Value || num <= 0)
			{
				num = 0;
			}
			EnsurePageSlotArray(num);
			PageSlotKeys[num][index] = key ?? string.Empty;
			SavePageSlotConfig(num);
			TouchSlotRevision();
			SavePluginConfig();
		}
	}

	internal bool TryGetSlotEmote(int index, out EmoteEntry entry)
	{
		return TryGetSlotEmoteForPage(GetCurrentPageIndex(), index, out entry);
	}

	internal bool TryGetSlotEmoteForPage(int pageIndex, int index, out EmoteEntry entry)
	{
		entry = null;
		string slotKeyForPage = GetSlotKeyForPage(pageIndex, index);
		if (string.IsNullOrEmpty(slotKeyForPage))
		{
			return false;
		}
		return EmotesByKey.TryGetValue(slotKeyForPage, out entry) && entry != null;
	}

	internal bool IsAssignedOnCurrentDeck(string key)
	{
		if (string.IsNullOrEmpty(key))
		{
			return false;
		}
		int activeSlotCount = GetActiveSlotCount();
		for (int i = 0; i < activeSlotCount; i++)
		{
			if (string.Equals(GetSlotKey(i), key, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	internal bool IsAssignedOnAnyEnabledPage(string key)
	{
		if (string.IsNullOrEmpty(key))
		{
			return false;
		}
		int activeSlotCount = GetActiveSlotCount();
		int effectiveDeckPageCount = GetEffectiveDeckPageCount();
		for (int i = 0; i < effectiveDeckPageCount; i++)
		{
			for (int j = 0; j < activeSlotCount; j++)
			{
				if (string.Equals(GetSlotKeyForPage(i, j), key, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
		}
		return false;
	}

	private void LoadPageSlotsFromConfig()
	{
		for (int i = 0; i < 10; i++)
		{
			EnsurePageSlotArray(i);
			string packedSlotConfigValue = GetPackedSlotConfigValue(i);
			UnpackConfigList(packedSlotConfigValue, PageSlotKeys[i]);
		}
	}

	private string GetPackedSlotConfigValue(int page)
	{
		if (page <= 0)
		{
			return (CfgDeckSlotKeys != null) ? (CfgDeckSlotKeys.Value ?? string.Empty) : string.Empty;
		}
		return (CfgPageSlotKeys[page] != null) ? (CfgPageSlotKeys[page].Value ?? string.Empty) : string.Empty;
	}

	private void EnsurePageSlotArray(int page)
	{
		if (page >= 0 && page < 10 && (PageSlotKeys[page] == null || PageSlotKeys[page].Length != 100))
		{
			PageSlotKeys[page] = new string[100];
		}
	}

	private void SavePageSlotConfig(int page)
	{
		if (page < 0 || page >= 10)
		{
			return;
		}
		EnsurePageSlotArray(page);
		string value = PackConfigList(PageSlotKeys[page]);
		if (page <= 0)
		{
			if (CfgDeckSlotKeys != null)
			{
				CfgDeckSlotKeys.Value = value;
			}
		}
		else if (CfgPageSlotKeys[page] != null)
		{
			CfgPageSlotKeys[page].Value = value;
		}
	}

	private void LoadCustomCommandsFromConfig()
	{
		for (int i = 0; i < 10; i++)
		{
			EnsurePageCustomCommandArray(i);
			string packedCustomCommandConfigValue = GetPackedCustomCommandConfigValue(i);
			string[] array = SplitConfigList(packedCustomCommandConfigValue);
			for (int j = 0; j < 100; j++)
			{
				string value = ((j < array.Length) ? array[j] : string.Empty);
				PageCustomCommands[i][j] = (TryNormalizeCustomSlotCommand(value, out var normalized, out var _) ? normalized : string.Empty);
			}
		}
	}

	private string GetPackedCustomCommandConfigValue(int page)
	{
		if (page <= 0)
		{
			return (CfgDeckCustomCommands != null) ? (CfgDeckCustomCommands.Value ?? string.Empty) : string.Empty;
		}
		return (CfgPageCustomCommands[page] != null) ? (CfgPageCustomCommands[page].Value ?? string.Empty) : string.Empty;
	}

	private void EnsurePageCustomCommandArray(int page)
	{
		if (page >= 0 && page < 10 && (PageCustomCommands[page] == null || PageCustomCommands[page].Length != 100))
		{
			PageCustomCommands[page] = new string[100];
		}
	}

	private void SavePageCustomCommandConfig(int page)
	{
		if (page < 0 || page >= 10)
		{
			return;
		}
		EnsurePageCustomCommandArray(page);
		string value = PackConfigList(PageCustomCommands[page]);
		if (page <= 0)
		{
			if (CfgDeckCustomCommands != null)
			{
				CfgDeckCustomCommands.Value = value;
			}
		}
		else if (CfgPageCustomCommands[page] != null)
		{
			CfgPageCustomCommands[page].Value = value;
		}
	}

	internal string GetCustomCommandForPage(int pageIndex, int index)
	{
		if (index < 0 || index >= 100)
		{
			return string.Empty;
		}
		if (!CfgEnablePages.Value || pageIndex <= 0)
		{
			pageIndex = 0;
		}
		if (pageIndex >= 10)
		{
			return string.Empty;
		}
		EnsurePageCustomCommandArray(pageIndex);
		return PageCustomCommands[pageIndex][index] ?? string.Empty;
	}

	internal string GetCustomCommand(int index)
	{
		return GetCustomCommandForPage(GetCurrentPageIndex(), index);
	}

	internal bool SetCustomCommand(int index, string rawCommand, out string normalized, out string error)
	{
		normalized = string.Empty;
		if (index < 0 || index >= 100)
		{
			error = "Slot is out of range.";
			return false;
		}
		if (!TryNormalizeCustomSlotCommand(rawCommand, out normalized, out error))
		{
			return false;
		}
		if (normalized.Length > 0 && TryFindCustomCommand(normalized, GetCurrentPageIndex(), index, out var pageIndex, out var slotIndex))
		{
			error = "Already used on page " + (pageIndex + 1).ToString(CultureInfo.InvariantCulture) + ", slot " + (slotIndex + 1).ToString(CultureInfo.InvariantCulture) + ".";
			return false;
		}
		int num = GetCurrentPageIndex();
		if (!CfgEnablePages.Value || num <= 0)
		{
			num = 0;
		}
		EnsurePageCustomCommandArray(num);
		PageCustomCommands[num][index] = normalized;
		SavePageCustomCommandConfig(num);
		SavePluginConfig();
		return true;
	}

	internal bool TryFindCustomCommand(string command, int ignorePage, int ignoreSlot, out int pageIndex, out int slotIndex)
	{
		pageIndex = -1;
		slotIndex = -1;
		if (!TryNormalizeCustomSlotCommand(command, out var normalized, out var _) || normalized.Length == 0)
		{
			return false;
		}
		int effectiveDeckPageCount = GetEffectiveDeckPageCount();
		int activeSlotCount = GetActiveSlotCount();
		for (int i = 0; i < effectiveDeckPageCount; i++)
		{
			for (int j = 0; j < activeSlotCount; j++)
			{
				if (i != ignorePage || j != ignoreSlot)
				{
					string customCommandForPage = GetCustomCommandForPage(i, j);
					if (string.Equals(customCommandForPage, normalized, StringComparison.OrdinalIgnoreCase))
					{
						pageIndex = i;
						slotIndex = j;
						return true;
					}
				}
			}
		}
		return false;
	}

	internal bool TryFindCustomCommandSlot(string message, out int pageIndex, out int slotIndex)
	{
		pageIndex = -1;
		slotIndex = -1;
		if (!CfgEnableCustomSlotCommands.Value)
		{
			return false;
		}
		if (!TryNormalizeCustomSlotCommand(message, out var normalized, out var _) || normalized.Length == 0)
		{
			return false;
		}
		return TryFindCustomCommand(normalized, -1, -1, out pageIndex, out slotIndex);
	}

	internal void MoveToNextPage()
	{
		if (CfgEnablePages.Value)
		{
			int pageCount = GetPageCount();
			int currentPageIndex = GetCurrentPageIndex();
			if (currentPageIndex + 1 < pageCount)
			{
				SetCurrentPageIndex(currentPageIndex + 1);
			}
		}
	}

	internal void MoveToPreviousPage()
	{
		if (CfgEnablePages.Value)
		{
			int currentPageIndex = GetCurrentPageIndex();
			if (currentPageIndex > 0)
			{
				SetCurrentPageIndex(currentPageIndex - 1);
			}
		}
	}

	private static List<string> ParseKeyList(string raw)
	{
		List<string> list = new List<string>();
		string[] array = (raw ?? string.Empty).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length > 0 && !ContainsString(list, text))
			{
				list.Add(text);
			}
		}
		return list;
	}

	private static bool ContainsString(List<string> list, string key)
	{
		for (int i = 0; i < list.Count; i++)
		{
			if (string.Equals(list[i], key, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private static void RemoveString(List<string> list, string key)
	{
		for (int num = list.Count - 1; num >= 0; num--)
		{
			if (string.Equals(list[num], key, StringComparison.OrdinalIgnoreCase))
			{
				list.RemoveAt(num);
			}
		}
	}

	private void LoadRecentAndFavoritesFromConfig()
	{
		RecentKeys.Clear();
		FavoriteKeys.Clear();
		RecentKeys.AddRange(ParseKeyList(CfgRecentKeys.Value));
		FavoriteKeys.AddRange(ParseKeyList(CfgFavoriteKeys.Value));
	}

	private void SaveRecentKeys()
	{
		CfgRecentKeys.Value = string.Join(";", RecentKeys.ToArray());
		RecentRevision++;
		QueueConfigSave();
	}

	private void SaveFavoriteKeys()
	{
		CfgFavoriteKeys.Value = string.Join(";", FavoriteKeys.ToArray());
		FavoriteRevision++;
		QueueConfigSave();
	}

	internal void NoteRecent(EmoteEntry entry)
	{
		if (entry != null && CfgEnableRecent.Value && !string.IsNullOrEmpty(entry.Key))
		{
			RemoveString(RecentKeys, entry.Key);
			RecentKeys.Insert(0, entry.Key);
			int num = Mathf.Clamp(CfgRecentLimit.Value, 1, 100);
			while (RecentKeys.Count > num)
			{
				RecentKeys.RemoveAt(RecentKeys.Count - 1);
			}
			SaveRecentKeys();
		}
	}

	internal bool IsFavorite(string key)
	{
		if (string.IsNullOrEmpty(key))
		{
			return false;
		}
		return ContainsString(FavoriteKeys, key);
	}

	internal void ToggleFavorite(EmoteEntry entry)
	{
		if (entry != null && !string.IsNullOrEmpty(entry.Key))
		{
			if (IsFavorite(entry.Key))
			{
				RemoveString(FavoriteKeys, entry.Key);
			}
			else
			{
				FavoriteKeys.Add(entry.Key);
			}
			SaveFavoriteKeys();
		}
	}

	internal void BuildEntryListFromKeys(List<string> keys, List<EmoteEntry> dest)
	{
		dest.Clear();
		for (int i = 0; i < keys.Count; i++)
		{
			if (EmotesByKey.TryGetValue(keys[i], out var value) && value != null)
			{
				dest.Add(value);
			}
		}
	}

	internal List<EmoteEntry> GetVisibleEmotes()
	{
		List<EmoteEntry> list = new List<EmoteEntry>();
		for (int i = 0; i < AllEmotes.Count; i++)
		{
			EmoteEntry emoteEntry = AllEmotes[i];
			if (emoteEntry != null && IsPackageVisible(emoteEntry.PackageId))
			{
				list.Add(emoteEntry);
			}
		}
		return list;
	}

	internal bool IsPackageVisible(string packageId)
	{
		string item = (string.IsNullOrEmpty(packageId) ? "Unknown" : packageId);
		return !HiddenPackages.Contains(item);
	}

	internal void SetPackageVisible(string packageId, bool visible)
	{
		string item = (string.IsNullOrEmpty(packageId) ? "Unknown" : packageId);
		if (visible)
		{
			HiddenPackages.Remove(item);
		}
		else
		{
			HiddenPackages.Add(item);
		}
		SaveHiddenPackagesToConfig();
	}

	internal void ShowAllPackages()
	{
		HiddenPackages.Clear();
		SaveHiddenPackagesToConfig();
	}

	internal void HideAllPackages()
	{
		HiddenPackages.Clear();
		for (int i = 0; i < PackageIds.Count; i++)
		{
			HiddenPackages.Add(PackageIds[i]);
		}
		SaveHiddenPackagesToConfig();
	}

	private void LoadHiddenPackagesFromConfig()
	{
		HiddenPackages.Clear();
		string text = CfgHiddenPackages.Value ?? string.Empty;
		string[] array = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim();
			if (text2.Length > 0)
			{
				HiddenPackages.Add(text2);
			}
		}
	}

	private void SaveHiddenPackagesToConfig()
	{
		List<string> list = new List<string>(HiddenPackages);
		list.Sort(StringComparer.OrdinalIgnoreCase);
		CfgHiddenPackages.Value = string.Join(";", list.ToArray());
		FilterRevision++;
		SavePluginConfig();
	}

	private bool IsChatTyping()
	{
		try
		{
			Player mainPlayer = Player._mainPlayer;
			if ((Object)(object)mainPlayer != (Object)null)
			{
				if (mainPlayer._inChat)
				{
					return true;
				}
				if ((Object)(object)mainPlayer._chatBehaviour != (Object)null && mainPlayer._chatBehaviour._focusedInChat)
				{
					return true;
				}
			}
		}
		catch
		{
		}
		try
		{
			ChatBehaviour current = ChatBehaviour._current;
			if ((Object)(object)current != (Object)null && current._focusedInChat)
			{
				return true;
			}
		}
		catch
		{
		}
		return IsUnityTextInputFocused();
	}

	private static bool IsUnityTextInputFocused()
	{
		try
		{
			Type type = FindType("UnityEngine.EventSystems.EventSystem");
			if (type == null)
			{
				return false;
			}
			PropertyInfo property = type.GetProperty("current", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null, null) : null);
			if (obj == null)
			{
				return false;
			}
			PropertyInfo property2 = type.GetProperty("currentSelectedGameObject", BindingFlags.Instance | BindingFlags.Public);
			GameObject val = (GameObject)((property2 != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Type type2 = FindType("UnityEngine.UI.InputField");
			if (type2 != null && (Object)(object)val.GetComponent(type2) != (Object)null)
			{
				return true;
			}
			Type type3 = FindType("TMPro.TMP_InputField");
			if (type3 != null && (Object)(object)val.GetComponent(type3) != (Object)null)
			{
				return true;
			}
		}
		catch
		{
		}
		return false;
	}

	private void RefreshEmoteRegistryIfChanged()
	{
		int lastRegistryFingerprint = _lastRegistryFingerprint;
		int num = ComputeRegistryFingerprintCheap();
		if (num != lastRegistryFingerprint)
		{
			RefreshEmoteRegistry();
		}
	}

	private int ComputeRegistryFingerprintCheap()
	{
		int num = 17;
		try
		{
			Type type = FindType("AtlyssEmotes.EmoteManager");
			if (type != null)
			{
				PropertyInfo property = type.GetProperty("allEmotes", BindingFlags.Static | BindingFlags.Public);
				object obj = ((property != null) ? property.GetValue(null, null) : null);
				if (obj is IDictionary dictionary)
				{
					num = num * 31 + dictionary.Count;
				}
			}
		}
		catch
		{
		}
		try
		{
			ScriptableEmoteList nativeScriptableEmoteList = GetNativeScriptableEmoteList();
			if ((Object)(object)nativeScriptableEmoteList != (Object)null && nativeScriptableEmoteList._emoteCommandList != null)
			{
				num = num * 31 + nativeScriptableEmoteList._emoteCommandList.Length;
			}
		}
		catch
		{
		}
		return num;
	}

	internal void RefreshEmoteRegistry()
	{
		Dictionary<string, EmoteEntry> dictionary = new Dictionary<string, EmoteEntry>(StringComparer.OrdinalIgnoreCase);
		EmoteWheelBridgeAvailable = FindType("AtlyssEmotes.EmoteManager") != null;
		int lastWheelEmoteCount = CollectEmoteWheelEntries(dictionary);
		bool flag = false;
		foreach (EmoteEntry value in dictionary.Values)
		{
			if (value != null && string.Equals(value.PackageId, "Base", StringComparison.OrdinalIgnoreCase))
			{
				flag = true;
				break;
			}
		}
		int lastNativeEmoteCount = 0;
		if (!flag || CfgIncludeNativeVanillaWhenWheelBaseExists.Value)
		{
			lastNativeEmoteCount = CollectNativeVanillaEntries(dictionary);
		}
		AllEmotes.Clear();
		foreach (EmoteEntry value2 in dictionary.Values)
		{
			if (value2 != null && !string.Equals(value2.Key, "None", StringComparison.OrdinalIgnoreCase))
			{
				AllEmotes.Add(value2);
			}
		}
		AllEmotes.Sort(CompareEmotes);
		EmotesByKey.Clear();
		PackageIds.Clear();
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		for (int i = 0; i < AllEmotes.Count; i++)
		{
			EmoteEntry emoteEntry = AllEmotes[i];
			if (!EmotesByKey.ContainsKey(emoteEntry.Key))
			{
				EmotesByKey.Add(emoteEntry.Key, emoteEntry);
			}
			string item = (string.IsNullOrEmpty(emoteEntry.PackageId) ? "Unknown" : emoteEntry.PackageId);
			if (hashSet.Add(item))
			{
				PackageIds.Add(item);
			}
		}
		PackageIds.Sort(StringComparer.OrdinalIgnoreCase);
		LastWheelEmoteCount = lastWheelEmoteCount;
		LastNativeEmoteCount = lastNativeEmoteCount;
		LastRegistryRefreshTime = Time.unscaledTime;
		RegistryRevision++;
		_lastRegistryFingerprint = ComputeRegistryFingerprintCheap();
		if (CfgDebugLogging.Value)
		{
			Log.LogInfo((object)("[EmoteDeck] Registry refreshed. total=" + AllEmotes.Count + " wheel=" + lastWheelEmoteCount + " native=" + lastNativeEmoteCount + " packages=" + PackageIds.Count));
		}
	}

	private static int CompareEmotes(EmoteEntry a, EmoteEntry b)
	{
		int num = string.Compare(a.PackageId, b.PackageId, StringComparison.OrdinalIgnoreCase);
		if (num != 0)
		{
			return num;
		}
		return string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase);
	}

	private int CollectEmoteWheelEntries(Dictionary<string, EmoteEntry> merged)
	{
		int num = 0;
		try
		{
			Type type = FindType("AtlyssEmotes.EmoteManager");
			if (type == null)
			{
				return 0;
			}
			PropertyInfo property = type.GetProperty("allEmotes", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null, null) : null);
			if (!(obj is IDictionary dictionary))
			{
				return 0;
			}
			foreach (DictionaryEntry item in dictionary)
			{
				string text = item.Key as string;
				object value = item.Value;
				if (!string.IsNullOrEmpty(text) && value != null && !string.Equals(text, "None", StringComparison.OrdinalIgnoreCase))
				{
					Type type2 = value.GetType();
					string text2 = ReadFieldString(type2, value, "emoteName");
					string text3 = ReadFieldString(type2, value, "emoteID");
					string text4 = ReadFieldString(type2, value, "packageName");
					bool flag = ReadFieldBool(type2, value, "isVanilla");
					Sprite icon = ReadFieldSprite(type2, value, "icon");
					if (string.IsNullOrEmpty(text4))
					{
						text4 = ExtractPackageFromKey(text);
					}
					if (string.IsNullOrEmpty(text2))
					{
						text2 = ExtractNameFromKey(text);
					}
					EmoteEntry emoteEntry = new EmoteEntry();
					emoteEntry.Key = text;
					emoteEntry.DisplayName = text2;
					emoteEntry.PackageId = text4;
					emoteEntry.Source = "Emote Wheel";
					emoteEntry.AnimationTag = (flag ? text3 : text);
					emoteEntry.IsVanilla = flag;
					emoteEntry.Icon = icon;
					emoteEntry.SortName = text4 + ":" + text2;
					if (!string.IsNullOrEmpty(emoteEntry.AnimationTag) && !merged.ContainsKey(emoteEntry.Key))
					{
						merged.Add(emoteEntry.Key, emoteEntry);
						num++;
					}
				}
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Emote Wheel reflection failed: " + ex.GetType().Name + ": " + ex.Message));
		}
		return num;
	}

	private int CollectNativeVanillaEntries(Dictionary<string, EmoteEntry> merged)
	{
		int num = 0;
		try
		{
			ScriptableEmoteList nativeScriptableEmoteList = GetNativeScriptableEmoteList();
			if ((Object)(object)nativeScriptableEmoteList == (Object)null || nativeScriptableEmoteList._emoteCommandList == null)
			{
				return 0;
			}
			for (int i = 0; i < nativeScriptableEmoteList._emoteCommandList.Length; i++)
			{
				EmoteCommand val = nativeScriptableEmoteList._emoteCommandList[i];
				if (val != null && !string.IsNullOrEmpty(val._emoteChatCommand) && !string.IsNullOrEmpty(val._emoteAnimationTag))
				{
					string key = "Vanilla:" + val._emoteChatCommand;
					if (!merged.ContainsKey(key))
					{
						EmoteEntry emoteEntry = new EmoteEntry();
						emoteEntry.Key = key;
						emoteEntry.DisplayName = PrettyCommandName(val._emoteChatCommand);
						emoteEntry.PackageId = "Vanilla";
						emoteEntry.Source = "Game";
						emoteEntry.AnimationTag = val._emoteAnimationTag;
						emoteEntry.IsVanilla = true;
						emoteEntry.Icon = null;
						emoteEntry.SortName = "Vanilla:" + emoteEntry.DisplayName;
						merged.Add(key, emoteEntry);
						num++;
					}
				}
			}
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("[EmoteDeck] Native emote reflection failed: " + ex.GetType().Name + ": " + ex.Message));
		}
		return num;
	}

	private static ScriptableEmoteList GetNativeScriptableEmoteList()
	{
		ChatBehaviour current = ChatBehaviour._current;
		if ((Object)(object)current == (Object)null)
		{
			return null;
		}
		FieldInfo field = typeof(ChatBehaviour).GetField("_scriptableEmoteList", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null)
		{
			return null;
		}
		object? value = field.GetValue(current);
		return (ScriptableEmoteList)((value is ScriptableEmoteList) ? value : null);
	}

	private static Type FindType(string fullName)
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			Type type = null;
			try
			{
				type = assemblies[i].GetType(fullName, throwOnError: false);
			}
			catch
			{
			}
			if (type != null)
			{
				return type;
			}
		}
		return null;
	}

	private static string ReadFieldString(Type t, object obj, string fieldName)
	{
		try
		{
			FieldInfo field = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return string.Empty;
			}
			object value = field.GetValue(obj);
			return (value as string) ?? string.Empty;
		}
		catch
		{
			return string.Empty;
		}
	}

	private static bool ReadFieldBool(Type t, object obj, string fieldName)
	{
		try
		{
			FieldInfo field = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return false;
			}
			object value = field.GetValue(obj);
			return value is bool && (bool)value;
		}
		catch
		{
			return false;
		}
	}

	private static Sprite ReadFieldSprite(Type t, object obj, string fieldName)
	{
		try
		{
			FieldInfo field = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return null;
			}
			object? value = field.GetValue(obj);
			return (Sprite)((value is Sprite) ? value : null);
		}
		catch
		{
			return null;
		}
	}

	private static string ExtractPackageFromKey(string key)
	{
		int num = key.IndexOf(':');
		if (num > 0)
		{
			return key.Substring(0, num);
		}
		return "Unknown";
	}

	private static string ExtractNameFromKey(string key)
	{
		int num = key.IndexOf(':');
		if (num >= 0 && num + 1 < key.Length)
		{
			return key.Substring(num + 1);
		}
		return key;
	}

	private static string PrettyCommandName(string cmd)
	{
		if (string.IsNullOrEmpty(cmd))
		{
			return "Emote";
		}
		string text = cmd.Trim();
		if (text.StartsWith("/"))
		{
			text = text.Substring(1);
		}
		if (text.Length == 0)
		{
			return cmd;
		}
		return char.ToUpperInvariant(text[0]) + ((text.Length > 1) ? text.Substring(1) : string.Empty);
	}

	internal bool TryHandleChatCommand(string message)
	{
		if (string.IsNullOrWhiteSpace(message))
		{
			return false;
		}
		string text = message.Trim();
		if (TryHandleWindowChatCommand(text))
		{
			return true;
		}
		if (TryFindCustomCommandSlot(text, out var pageIndex, out var slotIndex))
		{
			return TryPlaySlotFromChat(pageIndex, slotIndex);
		}
		if (CfgEnableChatCommands == null || !CfgEnableChatCommands.Value)
		{
			return false;
		}
		string activeChatCommandPrefix = GetActiveChatCommandPrefix();
		if (!text.Equals(activeChatCommandPrefix, StringComparison.OrdinalIgnoreCase) && !text.StartsWith(activeChatCommandPrefix + " ", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		string text2 = ((text.Length > activeChatCommandPrefix.Length) ? text.Substring(activeChatCommandPrefix.Length).Trim() : string.Empty);
		if (text2.Length == 0 || text2.Equals("help", StringComparison.OrdinalIgnoreCase) || text2 == "?")
		{
			ShowLocalChatNotice("Use " + activeChatCommandPrefix + " 1, " + activeChatCommandPrefix + " p2 1, or " + activeChatCommandPrefix + " point.");
			return true;
		}
		if (TryParseDeckCommandArgs(text2, out var pageIndex2, out var slotIndex2, out var error))
		{
			return TryPlaySlotFromChat(pageIndex2, slotIndex2);
		}
		if (CfgEnableNamedSlotCommands != null && CfgEnableNamedSlotCommands.Value)
		{
			if (TryFindNamedSlotCommand(text2, out pageIndex2, out slotIndex2, out var error2))
			{
				return TryPlaySlotFromChat(pageIndex2, slotIndex2);
			}
			ShowLocalChatNotice(error2 ?? error ?? "No matching emote slot.");
			return true;
		}
		ShowLocalChatNotice(error);
		return true;
	}

	private bool TryHandleWindowChatCommand(string trimmed)
	{
		if (string.IsNullOrEmpty(trimmed))
		{
			return false;
		}
		string activeChatCommandPrefix = GetActiveChatCommandPrefix();
		string activeCommand = activeChatCommandPrefix + "set";
		string activeCommand2 = activeChatCommandPrefix + "mod";
		string activeCommand3 = activeChatCommandPrefix + "grid";
		if (IsWindowCommand(trimmed, "/edset", activeCommand))
		{
			if (_window != null)
			{
				_window.ToggleSettingsTab();
			}
			return true;
		}
		if (IsWindowCommand(trimmed, "/edmod", activeCommand2))
		{
			if (_window != null)
			{
				_window.ToggleMainWindow();
			}
			return true;
		}
		if (IsWindowCommand(trimmed, "/edgrid", activeCommand3))
		{
			if (_window != null)
			{
				_window.ToggleGridWindow();
			}
			return true;
		}
		return false;
	}

	private static bool IsWindowCommand(string trimmed, string defaultCommand, string activeCommand)
	{
		return string.Equals(trimmed, defaultCommand, StringComparison.OrdinalIgnoreCase) || string.Equals(trimmed, activeCommand, StringComparison.OrdinalIgnoreCase);
	}

	private bool TryPlaySlotFromChat(int pageIndex, int slotIndex)
	{
		int activeSlotCount = GetActiveSlotCount();
		if (slotIndex < 0 || slotIndex >= activeSlotCount)
		{
			ShowLocalChatNotice("Slot " + (slotIndex + 1).ToString(CultureInfo.InvariantCulture) + " is outside the active slot count.");
			return true;
		}
		int effectiveDeckPageCount = GetEffectiveDeckPageCount();
		if (pageIndex < 0 || pageIndex >= effectiveDeckPageCount)
		{
			ShowLocalChatNotice("Page " + (pageIndex + 1).ToString(CultureInfo.InvariantCulture) + " is not available.");
			return true;
		}
		string slotKeyForPage = GetSlotKeyForPage(pageIndex, slotIndex);
		if (string.IsNullOrEmpty(slotKeyForPage))
		{
			ShowLocalChatNotice("Slot " + (slotIndex + 1).ToString(CultureInfo.InvariantCulture) + " is empty.");
			return true;
		}
		if (!EmotesByKey.TryGetValue(slotKeyForPage, out var value) || value == null)
		{
			ShowLocalChatNotice("That emote is not loaded. Try Rescan.");
			return true;
		}
		if (TryPlayEmote(value, out var status))
		{
			TryCancelProfessionActionAfterEmote();
		}
		else if (!string.IsNullOrEmpty(status) && status.IndexOf("cooling down", StringComparison.OrdinalIgnoreCase) < 0)
		{
			ShowLocalChatNotice(status);
		}
		return true;
	}

	private bool TryParseDeckCommandArgs(string args, out int pageIndex, out int slotIndex, out string error)
	{
		pageIndex = GetCurrentPageIndex();
		slotIndex = -1;
		error = null;
		string[] array = args.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 1)
		{
			if (!int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				error = "Expected a slot number or emote name.";
				return false;
			}
			slotIndex = result - 1;
			return true;
		}
		if (array.Length == 2)
		{
			if (!TryParsePageToken(array[0], out var page) || !int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				error = "Use /ed 1, /ed p2 1, or /ed point.";
				return false;
			}
			if (!CfgEnablePages.Value && page > 1)
			{
				error = "Pages are not enabled.";
				return false;
			}
			int effectiveDeckPageCount = GetEffectiveDeckPageCount();
			if (page < 1 || page > effectiveDeckPageCount)
			{
				error = "Page " + page.ToString(CultureInfo.InvariantCulture) + " is not available.";
				return false;
			}
			pageIndex = page - 1;
			slotIndex = result2 - 1;
			return true;
		}
		if (array.Length == 3 && array[0].Equals("page", StringComparison.OrdinalIgnoreCase))
		{
			if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3) || !int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result4))
			{
				error = "Use /ed page 2 1.";
				return false;
			}
			if (!CfgEnablePages.Value && result3 > 1)
			{
				error = "Pages are not enabled.";
				return false;
			}
			int effectiveDeckPageCount2 = GetEffectiveDeckPageCount();
			if (result3 < 1 || result3 > effectiveDeckPageCount2)
			{
				error = "Page " + result3.ToString(CultureInfo.InvariantCulture) + " is not available.";
				return false;
			}
			pageIndex = result3 - 1;
			slotIndex = result4 - 1;
			return true;
		}
		error = "Use /ed 1, /ed p2 1, or /ed point.";
		return false;
	}

	private bool TryFindNamedSlotCommand(string args, out int pageIndex, out int slotIndex, out string error)
	{
		pageIndex = -1;
		slotIndex = -1;
		error = null;
		int pageIndex2;
		string query;
		bool flag = TryExtractPageFromNamedArgs(args, out pageIndex2, out query);
		if (!flag)
		{
			query = args;
		}
		query = (query ?? string.Empty).Trim();
		string text = NormalizeEmoteCommandName(query);
		if (text.Length == 0)
		{
			error = "Expected an emote name.";
			return false;
		}
		if (!ValidateNamedCommandPage(flag, pageIndex2, out error))
		{
			return false;
		}
		int activeSlotCount = GetActiveSlotCount();
		int num = (flag ? pageIndex2 : 0);
		int num2 = (flag ? (pageIndex2 + 1) : GetEffectiveDeckPageCount());
		int num3 = -1;
		int num4 = -1;
		int num5 = 0;
		for (int i = num; i < num2; i++)
		{
			for (int j = 0; j < activeSlotCount; j++)
			{
				string slotKeyForPage = GetSlotKeyForPage(i, j);
				if (string.IsNullOrEmpty(slotKeyForPage) || !EmotesByKey.TryGetValue(slotKeyForPage, out var value) || value == null)
				{
					continue;
				}
				string customCommandForPage = GetCustomCommandForPage(i, j);
				if (!(NormalizeEmoteCommandName(customCommandForPage) != text) || DoesEntryMatchNameCommand(value, text))
				{
					num5++;
					num3 = i;
					num4 = j;
					if (num5 > 1)
					{
						error = "More than one slot matches \"" + query + "\". Try " + GetActiveChatCommandPrefix() + " p" + (i + 1).ToString(CultureInfo.InvariantCulture) + " " + query + ".";
						return false;
					}
				}
			}
		}
		if (num5 == 1)
		{
			pageIndex = num3;
			slotIndex = num4;
			return true;
		}
		if (CfgEnableClosestNameMatch != null && CfgEnableClosestNameMatch.Value)
		{
			if (TryFindClosestNamedSlotCommand(text, query, flag, pageIndex2, out pageIndex, out slotIndex, out var error2))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(error2))
			{
				error = error2;
				return false;
			}
		}
		error = "No slot matches \"" + query + "\".";
		return false;
	}

	private bool ValidateNamedCommandPage(bool hasExplicitPage, int explicitPage, out string error)
	{
		error = null;
		if (!hasExplicitPage)
		{
			return true;
		}
		if (!CfgEnablePages.Value && explicitPage > 0)
		{
			error = "Pages are not enabled.";
			return false;
		}
		int effectiveDeckPageCount = GetEffectiveDeckPageCount();
		if (explicitPage < 0 || explicitPage >= effectiveDeckPageCount)
		{
			error = "Page " + (explicitPage + 1).ToString(CultureInfo.InvariantCulture) + " is not available.";
			return false;
		}
		return true;
	}

	private bool TryFindClosestNamedSlotCommand(string normalizedQuery, string rawQuery, bool hasExplicitPage, int explicitPage, out int pageIndex, out int slotIndex, out string error)
	{
		pageIndex = -1;
		slotIndex = -1;
		error = null;
		if (string.IsNullOrEmpty(normalizedQuery))
		{
			return false;
		}
		int activeSlotCount = GetActiveSlotCount();
		int num = (hasExplicitPage ? explicitPage : 0);
		int num2 = (hasExplicitPage ? (explicitPage + 1) : GetEffectiveDeckPageCount());
		bool flag = CfgClosestMatchIncludeCustomCommands != null && CfgClosestMatchIncludeCustomCommands.Value;
		int num3 = -1;
		int num4 = -1;
		int num5 = int.MaxValue;
		int num6 = 0;
		for (int i = num; i < num2; i++)
		{
			for (int j = 0; j < activeSlotCount; j++)
			{
				string slotKeyForPage = GetSlotKeyForPage(i, j);
				if (!string.IsNullOrEmpty(slotKeyForPage) && EmotesByKey.TryGetValue(slotKeyForPage, out var value) && value != null && TryGetClosestNameScore(value, flag ? GetCustomCommandForPage(i, j) : string.Empty, normalizedQuery, out var score))
				{
					if (score < num5)
					{
						num5 = score;
						num3 = i;
						num4 = j;
						num6 = 1;
					}
					else if (score == num5 && (num3 != i || num4 != j))
					{
						num6++;
					}
				}
			}
		}
		if (num3 >= 0 && num4 >= 0)
		{
			pageIndex = num3;
			slotIndex = num4;
			if (num6 > 1)
			{
				ShowLocalChatNotice("Several close matches. Playing the closest one.");
			}
			return true;
		}
		return false;
	}

	private static bool TryGetClosestNameScore(EmoteEntry entry, string customCommand, string normalizedQuery, out int score)
	{
		score = int.MaxValue;
		if (entry == null || string.IsNullOrEmpty(normalizedQuery))
		{
			return false;
		}
		bool matched = false;
		AddClosestScore(NormalizeEmoteCommandName(entry.DisplayName), normalizedQuery, ref score, ref matched);
		AddClosestScore(NormalizeEmoteCommandName(ExtractNameFromKey(entry.Key)), normalizedQuery, ref score, ref matched);
		AddClosestScore(NormalizeEmoteCommandName(entry.AnimationTag), normalizedQuery, ref score, ref matched);
		if (!string.IsNullOrEmpty(customCommand))
		{
			AddClosestScore(NormalizeEmoteCommandName(customCommand), normalizedQuery, ref score, ref matched);
		}
		return matched;
	}

	private static void AddClosestScore(string candidate, string query, ref int bestScore, ref bool matched)
	{
		if (string.IsNullOrEmpty(candidate) || string.IsNullOrEmpty(query))
		{
			return;
		}
		int num;
		if (candidate.StartsWith(query, StringComparison.Ordinal))
		{
			num = candidate.Length - query.Length;
		}
		else if (query.Length >= 3 && candidate.IndexOf(query, StringComparison.Ordinal) >= 0)
		{
			num = 100 + candidate.IndexOf(query, StringComparison.Ordinal) + Math.Abs(candidate.Length - query.Length);
		}
		else
		{
			if (query.Length < 3)
			{
				return;
			}
			int num2 = LevenshteinDistance(candidate, query);
			int num3 = Math.Min(3, Math.Max(1, query.Length / 3));
			if (num2 > num3)
			{
				return;
			}
			num = 200 + num2 * 10 + Math.Abs(candidate.Length - query.Length);
		}
		if (num < bestScore)
		{
			bestScore = num;
		}
		matched = true;
	}

	private static int LevenshteinDistance(string a, string b)
	{
		if (a == null)
		{
			a = string.Empty;
		}
		if (b == null)
		{
			b = string.Empty;
		}
		int length = a.Length;
		int length2 = b.Length;
		if (length == 0)
		{
			return length2;
		}
		if (length2 == 0)
		{
			return length;
		}
		int[] array = new int[length2 + 1];
		int[] array2 = new int[length2 + 1];
		for (int i = 0; i <= length2; i++)
		{
			array[i] = i;
		}
		for (int j = 1; j <= length; j++)
		{
			array2[0] = j;
			char c = a[j - 1];
			for (int k = 1; k <= length2; k++)
			{
				int num = ((c != b[k - 1]) ? 1 : 0);
				int val = array[k] + 1;
				int val2 = array2[k - 1] + 1;
				int val3 = array[k - 1] + num;
				array2[k] = Math.Min(Math.Min(val, val2), val3);
			}
			int[] array3 = array;
			array = array2;
			array2 = array3;
		}
		return array[length2];
	}

	private static bool DoesEntryMatchNameCommand(EmoteEntry entry, string normalizedQuery)
	{
		if (entry == null || string.IsNullOrEmpty(normalizedQuery))
		{
			return false;
		}
		if (NormalizeEmoteCommandName(entry.DisplayName) == normalizedQuery)
		{
			return true;
		}
		if (NormalizeEmoteCommandName(ExtractNameFromKey(entry.Key)) == normalizedQuery)
		{
			return true;
		}
		if (NormalizeEmoteCommandName(entry.AnimationTag) == normalizedQuery)
		{
			return true;
		}
		return false;
	}

	private static bool TryExtractPageFromNamedArgs(string args, out int pageIndex, out string query)
	{
		pageIndex = -1;
		query = args ?? string.Empty;
		string text = (args ?? string.Empty).Trim();
		if (text.Length == 0)
		{
			return false;
		}
		string[] array = text.Split(new char[2] { ' ', '\t' }, 3, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length >= 2 && TryParsePageToken(array[0], out var page))
		{
			pageIndex = page - 1;
			query = ((array.Length >= 2) ? array[1] : string.Empty);
			if (array.Length == 3)
			{
				query = array[1] + " " + array[2];
			}
			return true;
		}
		if (array.Length >= 3 && array[0].Equals("page", StringComparison.OrdinalIgnoreCase) && int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
		{
			pageIndex = result - 1;
			query = array[2];
			return true;
		}
		return false;
	}

	private static string NormalizeEmoteCommandName(string value)
	{
		string text = (value ?? string.Empty).Trim();
		if (text.StartsWith("/"))
		{
			text = text.Substring(1);
		}
		StringBuilder stringBuilder = new StringBuilder(text.Length);
		bool flag = false;
		foreach (char c in text)
		{
			if (c == '_' || c == '-' || char.IsWhiteSpace(c))
			{
				if (!flag && stringBuilder.Length > 0)
				{
					stringBuilder.Append(' ');
					flag = true;
				}
			}
			else if (char.IsLetterOrDigit(c))
			{
				stringBuilder.Append(char.ToLowerInvariant(c));
				flag = false;
			}
		}
		return stringBuilder.ToString().Trim();
	}

	private static bool TryParsePageToken(string token, out int page)
	{
		page = 0;
		if (string.IsNullOrEmpty(token))
		{
			return false;
		}
		string text = token.Trim();
		if (text.StartsWith("p", StringComparison.OrdinalIgnoreCase))
		{
			text = text.Substring(1);
		}
		return int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out page);
	}

	private static void ShowLocalChatNotice(string text)
	{
		try
		{
			ChatBehaviour current = ChatBehaviour._current;
			if ((Object)(object)current != (Object)null)
			{
				current.New_ChatMessage("<color=yellow>[Emote Deck]</color> " + (text ?? string.Empty));
			}
		}
		catch
		{
			try
			{
				if (Log != null)
				{
					Log.LogInfo((object)("[EmoteDeck] " + text));
				}
			}
			catch
			{
			}
		}
	}

	private static void TryCancelProfessionActionAfterEmote()
	{
		try
		{
			Player mainPlayer = Player._mainPlayer;
			if ((Object)(object)mainPlayer != (Object)null && (Object)(object)mainPlayer._pProfession != (Object)null && mainPlayer._pProfession._isProfessionAction)
			{
				mainPlayer._pProfession.Cancel_ProfessionAction();
			}
		}
		catch
		{
		}
	}

	internal bool TryPlayEmote(EmoteEntry entry, out string status)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Invalid comparison between Unknown and I4
		status = string.Empty;
		if (entry == null)
		{
			status = "No emote selected.";
			return false;
		}
		if (string.IsNullOrEmpty(entry.AnimationTag))
		{
			status = "That emote has no animation tag.";
			return false;
		}
		try
		{
			Player mainPlayer = Player._mainPlayer;
			if ((Object)(object)mainPlayer == (Object)null || (Object)(object)mainPlayer._pVisual == (Object)null)
			{
				status = "Local player not found.";
				return false;
			}
			if ((int)mainPlayer._currentPlayerCondition != 2)
			{
				status = "Player is not active.";
				return false;
			}
			ChatBehaviour current = ChatBehaviour._current;
			if ((Object)(object)current != (Object)null)
			{
				float num = ReadChatEmoteBuffer(current);
				if (num > 0f)
				{
					status = "Emotes are cooling down.";
					return false;
				}
			}
			mainPlayer._pVisual.Cmd_CrossFadeAnim(entry.AnimationTag, 0.1f, 11);
			mainPlayer._pVisual.Local_CrossFadeAnim(entry.AnimationTag, 0.1f, 11);
			if ((Object)(object)current != (Object)null)
			{
				WriteChatEmoteBuffer(current, 0.85f);
			}
			NoteRecent(entry);
			status = "Played " + entry.DisplayName + ".";
			if (CfgDebugLogging.Value)
			{
				Log.LogInfo((object)("[EmoteDeck] Play " + entry.Key + " tag=" + entry.AnimationTag + " source=" + entry.Source));
			}
			return true;
		}
		catch (Exception ex)
		{
			status = "Play failed: " + ex.GetType().Name + ": " + ex.Message;
			Log.LogWarning((object)("[EmoteDeck] " + status));
			return false;
		}
	}

	private static float ReadChatEmoteBuffer(ChatBehaviour chat)
	{
		try
		{
			if (_chatEmoteBufferField == null)
			{
				_chatEmoteBufferField = typeof(ChatBehaviour).GetField("_emoteBuffer", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (_chatEmoteBufferField == null)
			{
				return 0f;
			}
			if (_chatEmoteBufferField.GetValue(chat) is float result)
			{
				return result;
			}
		}
		catch
		{
		}
		return 0f;
	}

	private static void WriteChatEmoteBuffer(ChatBehaviour chat, float value)
	{
		try
		{
			if (_chatEmoteBufferField == null)
			{
				_chatEmoteBufferField = typeof(ChatBehaviour).GetField("_emoteBuffer", BindingFlags.Instance | BindingFlags.NonPublic);
			}
			if (_chatEmoteBufferField != null)
			{
				_chatEmoteBufferField.SetValue(chat, value);
			}
		}
		catch
		{
		}
	}
}
internal sealed class EmoteEntry
{
	public string Key;

	public string DisplayName;

	public string PackageId;

	public string Source;

	public string AnimationTag;

	public bool IsVanilla;

	public Sprite Icon;

	public string SortName;
}
internal enum BindTarget
{
	None,
	MainWindow,
	GridWindow
}
internal enum GridMode
{
	Deck,
	Recent,
	Favorites
}
internal static class EmoteDeckEasySettingsBridge
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__14_0;

		internal void <TrySubscribe>b__14_0()
		{
			TryRegisterIfReady(EmoteDeckPlugin.Instance);
		}
	}

	private const string EasySettingsTypeName = "Nessie.ATLYSS.EasySettings.Settings";

	private static bool _subscribed;

	private static bool _registered;

	private static bool _registering;

	private static bool _loggedMissing;

	private static bool _syncingButtons;

	private static int _activeEasySettingsBindTarget;

	private static float _activeEasySettingsBindUntil;

	private static KeyCode _lastSyncedMainKey;

	private static KeyCode _lastSyncedGridKey;

	private static object _mainKeyButton;

	private static object _gridKeyButton;

	internal static void TryInstall(EmoteDeckPlugin plugin)
	{
		if (!((Object)(object)plugin == (Object)null))
		{
			TrySubscribe(plugin);
			TryRegisterIfReady(plugin);
		}
	}

	internal static IEnumerator DelayedInstall(EmoteDeckPlugin plugin)
	{
		yield return (object)new WaitForSeconds(0.5f);
		TryInstall(plugin);
		yield return (object)new WaitForSeconds(1.5f);
		TryRegisterIfReady(plugin);
		yield return (object)new WaitForSeconds(4f);
		TryRegisterIfReady(plugin);
	}

	private static void TrySubscribe(EmoteDeckPlugin plugin)
	{
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Expected O, but got Unknown
		if (_subscribed)
		{
			return;
		}
		Type type = FindType("Nessie.ATLYSS.EasySettings.Settings");
		if (type == null)
		{
			LogMissingOnce(plugin);
			return;
		}
		try
		{
			PropertyInfo property = type.GetProperty("OnInitialized", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null, null) : null);
			if (obj == null)
			{
				return;
			}
			MethodInfo method = obj.GetType().GetMethod("AddListener", new Type[1] { typeof(UnityAction) });
			if (method == null)
			{
				return;
			}
			object obj2 = <>c.<>9__14_0;
			if (obj2 == null)
			{
				UnityAction val = delegate
				{
					TryRegisterIfReady(EmoteDeckPlugin.Instance);
				};
				<>c.<>9__14_0 = val;
				obj2 = (object)val;
			}
			UnityAction val2 = (UnityAction)obj2;
			method.Invoke(obj, new object[1] { val2 });
			_subscribed = true;
		}
		catch (Exception ex)
		{
			if (EmoteDeckPlugin.CfgDebugLogging != null && EmoteDeckPlugin.CfgDebugLogging.Value && EmoteDeckPlugin.Log != null)
			{
				EmoteDeckPlugin.Log.LogWarning((object)("[EmoteDeck] EasySettings hook failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}

	private static void TryRegisterIfReady(EmoteDeckPlugin plugin)
	{
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)plugin == (Object)null || _registered || _registering)
		{
			return;
		}
		Type type = FindType("Nessie.ATLYSS.EasySettings.Settings");
		if (type == null)
		{
			LogMissingOnce(plugin);
		}
		else
		{
			if (!IsEasySettingsReady(type))
			{
				return;
			}
			_registering = true;
			try
			{
				MethodInfo method = type.GetMethod("GetOrAddCustomTab", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				if (method == null)
				{
					return;
				}
				object obj = method.Invoke(null, new object[1] { "Eleen's Lab" });
				if (obj == null)
				{
					return;
				}
				Type type2 = obj.GetType();
				MethodInfo method2 = type2.GetMethod("AddHeader", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				MethodInfo method3 = type2.GetMethod("AddKeyButton", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(string),
					typeof(KeyCode)
				}, null);
				if (!(method3 == null))
				{
					if (method2 != null)
					{
						method2.Invoke(obj, new object[1] { "Emote Deck" });
					}
					_mainKeyButton = method3.Invoke(obj, new object[2] { "Main window", plugin.MainWindowKey });
					_gridKeyButton = method3.Invoke(obj, new object[2] { "Emote grid", plugin.GridWindowKey });
					HookKeyButton(_mainKeyButton, main: true);
					HookKeyButton(_gridKeyButton, main: false);
					_registered = true;
					SyncButtonsFromConfig(plugin);
					TryUpdateEasySettingsVisibility(type);
					if (EmoteDeckPlugin.CfgDebugLogging != null && EmoteDeckPlugin.CfgDebugLogging.Value && EmoteDeckPlugin.Log != null)
					{
						EmoteDeckPlugin.Log.LogInfo((object)"[EmoteDeck] EasySettings keys registered.");
					}
				}
			}
			catch (Exception ex)
			{
				if (EmoteDeckPlugin.CfgDebugLogging != null && EmoteDeckPlugin.CfgDebugLogging.Value && EmoteDeckPlugin.Log != null)
				{
					EmoteDeckPlugin.Log.LogWarning((object)("[EmoteDeck] EasySettings registration failed: " + ex.GetType().Name + ": " + ex.Message));
				}
			}
			finally
			{
				_registering = false;
			}
		}
	}

	private static bool IsEasySettingsReady(Type settingsType)
	{
		try
		{
			if ((Object)(object)SettingsManager._current == (Object)null)
			{
				return false;
			}
			PropertyInfo property = settingsType.GetProperty("ModTab", BindingFlags.Static | BindingFlags.Public);
			object obj = ((property != null) ? property.GetValue(null, null) : null);
			if (obj == null)
			{
				return false;
			}
			FieldInfo field = obj.GetType().GetField("Content", BindingFlags.Instance | BindingFlags.Public);
			object obj2 = ((field != null) ? field.GetValue(obj) : null);
			if (obj2 == null)
			{
				return false;
			}
			Type type = FindType("Nessie.ATLYSS.EasySettings.TemplateManager");
			if (type != null)
			{
				FieldInfo field2 = ty