Decompiled source of SaveKeyBinds v1.0.5

SaveKeyBinds.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppScheduleOne;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using ScheduleIMod;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Keybinder), "SaveKeyBinds", "1.0.0", "arc", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("SaveKeyBinds")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SaveKeyBinds")]
[assembly: AssemblyTitle("Schedule I Keybind Mod")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ScheduleIMod
{
	public class Keybinder : MelonMod
	{
		private static class ConsoleMethodCache
		{
			private static bool _initialized;

			private static readonly object _lock = new object();

			public static Action<Console, KeyCode, string>? Bind;

			public static Action<Console, KeyCode>? Unbind;

			public static Action<Console, KeyCode, string>? AddBinding;

			public static Action<Console, KeyCode>? RemoveBinding;

			public static void EnsureInitialized(Console c)
			{
				if (_initialized)
				{
					return;
				}
				lock (_lock)
				{
					if (_initialized)
					{
						return;
					}
					Type type = ((object)c).GetType();
					BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
					MethodInfo method = type.GetMethod("Bind", bindingAttr, null, new Type[2]
					{
						typeof(KeyCode),
						typeof(string)
					}, null);
					if (method != null)
					{
						try
						{
							Bind = (Action<Console, KeyCode, string>)Delegate.CreateDelegate(typeof(Action<Console, KeyCode, string>), null, method);
						}
						catch
						{
						}
					}
					MethodInfo method2 = type.GetMethod("Unbind", bindingAttr, null, new Type[1] { typeof(KeyCode) }, null);
					if (method2 != null)
					{
						try
						{
							Unbind = (Action<Console, KeyCode>)Delegate.CreateDelegate(typeof(Action<Console, KeyCode>), null, method2);
						}
						catch
						{
						}
					}
					MethodInfo method3 = type.GetMethod("AddBinding", bindingAttr, null, new Type[2]
					{
						typeof(KeyCode),
						typeof(string)
					}, null);
					if (method3 != null)
					{
						try
						{
							AddBinding = (Action<Console, KeyCode, string>)Delegate.CreateDelegate(typeof(Action<Console, KeyCode, string>), null, method3);
						}
						catch
						{
						}
					}
					MethodInfo method4 = type.GetMethod("RemoveBinding", bindingAttr, null, new Type[1] { typeof(KeyCode) }, null);
					if (method4 != null)
					{
						try
						{
							RemoveBinding = (Action<Console, KeyCode>)Delegate.CreateDelegate(typeof(Action<Console, KeyCode>), null, method4);
						}
						catch
						{
						}
					}
					_initialized = true;
				}
			}
		}

		private MelonPreferences_Category? _cat;

		private MelonPreferences_Entry<bool>? _actLoad;

		private MelonPreferences_Entry<bool>? _actReset;

		private MelonPreferences_Entry<bool>? _actClear;

		private int _actionFrameLatch = -1;

		private bool bindingsAdded;

		private string configPath = string.Empty;

		private static bool _typesRegistered = false;

		private List<(string command, KeyCode key)> keybinds = new List<(string, KeyCode)>();

		private static Keybinder? instance;

		private Console? console;

		private bool consoleCommandsRegistered;

		private int _rebindJobToken;

		private bool _rebindInFlight;

		private HashSet<KeyCode> _lastAppliedKeys = new HashSet<KeyCode>();

		private DateTime _configTimestampUtc = DateTime.MinValue;

		private Harmony? _harmony;

		private readonly object _bindingsSync = new object();

		private static bool _hasModManager = false;

		private static bool _mmTriedCache = false;

		private static Action? _mmRequestRefresh;

		private static readonly string[] ConfigHeader = new string[4] { "# Schedule I SaveKeyBinds Config", "# Format = 'command: KeyCode'", "# Available KeyCodes: https://docs.unity3d.com/ScriptReference/KeyCode.html", "" };

		public static Keybinder? Instance => instance;

		public int KeybindCount
		{
			get
			{
				lock (_bindingsSync)
				{
					return keybinds.Count;
				}
			}
		}

		public string ConfigPath => configPath;

		internal static void Info(string msg)
		{
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Msg(msg);
			}
			else
			{
				MelonLogger.Msg(msg);
			}
		}

		internal static void Warn(string msg)
		{
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Warning(msg);
			}
			else
			{
				MelonLogger.Warning(msg);
			}
		}

		internal static void Err(string msg)
		{
			if (instance != null)
			{
				((MelonBase)instance).LoggerInstance.Error(msg);
			}
			else
			{
				MelonLogger.Error(msg);
			}
		}

		private static string Q(string? s)
		{
			if (s != null)
			{
				return "\"" + s + "\"";
			}
			return "null";
		}

		internal static string ExMsg(Exception ex)
		{
			Exception innerException = ex.InnerException;
			if (innerException != null)
			{
				return $"{ex.GetType().Name}: {ex.Message} | Inner {innerException.GetType().Name}: {innerException.Message}";
			}
			return ex.GetType().Name + ": " + ex.Message;
		}

		private void RefreshIfStale()
		{
			try
			{
				if (File.Exists(configPath) && File.GetLastWriteTimeUtc(configPath) > _configTimestampUtc)
				{
					LoadConfig();
				}
			}
			catch (Exception ex)
			{
				Err("RefreshIfStale failed: " + ExMsg(ex));
			}
		}

		[Obsolete]
		public override void OnApplicationStart()
		{
			instance = this;
			_hasModManager = DetectModManager();
			Info("Loaded successfully!");
			configPath = Path.Combine(MelonUtils.GameDirectory, "UserData", "keyBinds.ini");
			string directoryName = Path.GetDirectoryName(configPath);
			if (!string.IsNullOrEmpty(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			_cat = MelonPreferences.CreateCategory("SaveKeyBinds", "Manage Local Config");
			_actLoad = _cat.CreateEntry<bool>("Load", false, "Reload keyBinds.ini", (string)null, false, false, (ValueValidator)null, (string)null);
			_actReset = _cat.CreateEntry<bool>("Reset", false, "Reset keyBinds.ini to default", (string)null, false, false, (ValueValidator)null, (string)null);
			_actClear = _cat.CreateEntry<bool>("Clear", false, "Clear keyBinds.ini", (string)null, false, false, (ValueValidator)null, (string)null);
			MelonPreferences.Save();
			if (_hasModManager)
			{
				Info("ModManagerPhoneApp found!");
			}
			if (_hasModManager)
			{
				EnsureModManagerRefreshCached();
			}
			LoadConfig();
			SetupHarmonyPatch();
		}

		public override void OnUpdate()
		{
			if (_hasModManager)
			{
				TryRunActionToggles();
			}
		}

		private void TryRunActionToggles()
		{
			if (Time.frameCount == _actionFrameLatch)
			{
				return;
			}
			bool flag = false;
			if (_actLoad != null && _actLoad.Value)
			{
				try
				{
					LoadConfig();
					ApplyKeybindingsReplaceManagedPublic();
					Info("Loaded " + Path.GetFileName(ConfigPath) + "!");
				}
				catch (Exception ex)
				{
					Err("LoadNow failed: " + ExMsg(ex));
				}
				finally
				{
					_actLoad.Value = false;
					flag = true;
				}
			}
			if (_actReset != null && _actReset.Value)
			{
				try
				{
					ResetToDefaultsAndApply();
					Info("Keybinds reset to default!");
				}
				catch (Exception ex2)
				{
					Err("ResetToDefaults failed: " + ExMsg(ex2));
				}
				finally
				{
					_actReset.Value = false;
					flag = true;
				}
			}
			if (_actClear != null && _actClear.Value)
			{
				try
				{
					ClearAllKeybinds();
					Info("All keybinds cleared!");
				}
				catch (Exception ex3)
				{
					Err("ClearAll failed: " + ExMsg(ex3));
				}
				finally
				{
					_actClear.Value = false;
					flag = true;
				}
			}
			if (flag)
			{
				MelonPreferences.Save();
				_actionFrameLatch = Time.frameCount;
				TryRequestSettingsUIRefresh();
			}
		}

		public override void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			bool hasModManager = _hasModManager;
			_hasModManager = _hasModManager || DetectModManager();
			if (!hasModManager && _hasModManager)
			{
				Info("ModManagerPhoneApp found!");
			}
			if (!hasModManager && _hasModManager)
			{
				_mmTriedCache = false;
				EnsureModManagerRefreshCached();
			}
			Console val = Object.FindObjectOfType<Console>();
			if ((Object)(object)console == (Object)null || (Object)(object)val == (Object)null || (Object)(object)console != (Object)(object)val)
			{
				console = val;
				bindingsAdded = false;
				consoleCommandsRegistered = false;
			}
			ScheduleRebindAndRegister();
		}

		public override void OnApplicationQuit()
		{
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				Err("Harmony UnpatchSelf failed: " + ExMsg(ex));
			}
			instance = null;
			console = null;
		}

		private static void TryRequestSettingsUIRefresh()
		{
			if (_hasModManager)
			{
				EnsureModManagerRefreshCached();
				if (_mmRequestRefresh != null)
				{
					MelonCoroutines.Start(RequestSettingsUIRefreshAfter(0.6f));
				}
			}
		}

		private static IEnumerator RequestSettingsUIRefreshAfter(float seconds)
		{
			if (seconds > 0f)
			{
				yield return (object)new WaitForSecondsRealtime(seconds);
			}
			try
			{
				_mmRequestRefresh?.Invoke();
			}
			catch (Exception ex)
			{
				Warn("RequestUIRefresh failed: " + ExMsg(ex));
			}
		}

		private static bool DetectModManager()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if ((assembly.GetName().Name ?? string.Empty).Contains("ModManagerPhoneApp", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (assembly.GetType("ModManagerPhoneApp.ModSettingsEvents", throwOnError: false) != null)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static void EnsureModManagerRefreshCached()
		{
			if (_mmTriedCache)
			{
				return;
			}
			_mmTriedCache = true;
			try
			{
				MethodInfo i = AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly a)
				{
					try
					{
						return a.GetTypes();
					}
					catch
					{
						return Type.EmptyTypes;
					}
				}).FirstOrDefault((Type x) => x.FullName == "ModManagerPhoneApp.ModSettingsEvents")?.GetMethod("RequestUIRefresh", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(i != null))
				{
					return;
				}
				try
				{
					_mmRequestRefresh = (Action)Delegate.CreateDelegate(typeof(Action), i);
				}
				catch
				{
					_mmRequestRefresh = delegate
					{
						i.Invoke(null, null);
					};
				}
			}
			catch
			{
			}
		}

		private void ScheduleRebindAndRegister()
		{
			_rebindJobToken++;
			if (!_rebindInFlight)
			{
				_rebindInFlight = true;
				MelonCoroutines.Start(RebindAndRegisterWhenReady(_rebindJobToken));
			}
		}

		private IEnumerator RebindAndRegisterWhenReady(int token)
		{
			yield return null;
			for (int i = 0; i < 120; i++)
			{
				if (token != _rebindJobToken)
				{
					_rebindInFlight = false;
					yield break;
				}
				Console val = console ?? Object.FindObjectOfType<Console>();
				if ((Object)(object)val != (Object)null)
				{
					console = val;
					break;
				}
				yield return null;
			}
			if ((Object)(object)console == (Object)null)
			{
				_rebindInFlight = false;
				yield break;
			}
			try
			{
				if (!bindingsAdded)
				{
					Info("Loaded " + configPath);
					ApplyKeybindings();
					if (bindingsAdded)
					{
						RegisterLoadKeysCommand();
					}
				}
			}
			catch (Exception ex)
			{
				Err("RebindAndRegisterWhenReady failed: " + ExMsg(ex));
			}
			finally
			{
				_rebindInFlight = false;
			}
		}

		private void RegisterLoadKeysCommand()
		{
			if (!_typesRegistered)
			{
				try
				{
					ClassInjector.RegisterTypeInIl2Cpp<LoadKeysCommand>();
					ClassInjector.RegisterTypeInIl2Cpp<ClearKeysCommand>();
					ClassInjector.RegisterTypeInIl2Cpp<ResetKeysCommand>();
					_typesRegistered = true;
				}
				catch (Exception ex)
				{
					Err("Error registering Il2Cpp types (once): " + ExMsg(ex));
				}
			}
			if (consoleCommandsRegistered)
			{
				return;
			}
			try
			{
				Console val = console ?? Object.FindObjectOfType<Console>();
				if ((Object)(object)val != (Object)null)
				{
					RegisterConsoleCommand(val, (ConsoleCommand)(object)new LoadKeysCommand());
					RegisterConsoleCommand(val, (ConsoleCommand)(object)new ClearKeysCommand());
					RegisterConsoleCommand(val, (ConsoleCommand)(object)new ResetKeysCommand());
					consoleCommandsRegistered = true;
				}
			}
			catch (Exception ex2)
			{
				Err("Error registering console commands: " + ExMsg(ex2));
			}
		}

		private void RegisterConsoleCommand(Console console, ConsoleCommand cmd)
		{
			try
			{
				Dictionary<string, ConsoleCommand> commands = Console.commands;
				if (commands != null)
				{
					string text = cmd.CommandWord?.ToLowerInvariant() ?? string.Empty;
					if (!string.IsNullOrEmpty(text))
					{
						commands[text] = cmd;
					}
				}
			}
			catch (Exception ex)
			{
				Err("RegisterConsoleCommand(dict) failed: " + ExMsg(ex));
			}
			try
			{
				List<ConsoleCommand> commands2 = Console.Commands;
				if (commands2 != null && !commands2.Contains(cmd))
				{
					commands2.Add(cmd);
				}
			}
			catch (Exception ex2)
			{
				Err("RegisterConsoleCommand(list) failed: " + ExMsg(ex2));
			}
		}

		private void SetupHarmonyPatch()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			try
			{
				_harmony = new Harmony("ScheduleIMod.Keybinder");
				BindingFlags bindingAttr = BindingFlags.Public | BindingFlags.NonPublic;
				BindingFlags bindingAttr2 = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
				Type nestedType = typeof(Console).GetNestedType("Bind", bindingAttr);
				if (nestedType != null)
				{
					MethodInfo method = nestedType.GetMethod("Execute", bindingAttr2);
					if (method != null)
					{
						MethodInfo method2 = typeof(Keybinder).GetMethod("BindExecutePostfix", BindingFlags.Static | BindingFlags.NonPublic);
						_harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
				Type nestedType2 = typeof(Console).GetNestedType("Unbind", bindingAttr);
				if (nestedType2 != null)
				{
					MethodInfo method3 = nestedType2.GetMethod("Execute", bindingAttr2);
					if (method3 != null)
					{
						MethodInfo method4 = typeof(Keybinder).GetMethod("UnbindExecutePostfix", BindingFlags.Static | BindingFlags.NonPublic);
						_harmony.Patch((MethodBase)method3, (HarmonyMethod)null, new HarmonyMethod(method4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
			}
			catch (Exception ex)
			{
				Err("Harmony patch setup failed: " + ExMsg(ex));
			}
		}

		private static void BindExecutePostfix(List<string> args)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (args.Count >= 2)
				{
					string value = args[0];
					List<string> list = new List<string>();
					for (int i = 1; i < args.Count; i++)
					{
						list.Add(args[i]);
					}
					string command = string.Join(" ", list);
					if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result) && instance != null)
					{
						instance.RefreshIfStale();
						instance.SaveBindingToConfig(result, command);
					}
				}
			}
			catch (Exception ex)
			{
				Err("BindExecutePostfix failed: " + ExMsg(ex));
			}
		}

		private static void UnbindExecutePostfix(List<string> args)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (args.Count >= 1 && Enum.TryParse<KeyCode>(args[0], ignoreCase: true, out KeyCode result) && instance != null)
				{
					instance.RefreshIfStale();
					instance.RemoveBindingFromConfig(result);
				}
			}
			catch (Exception ex)
			{
				Err("UnbindExecutePostfix failed: " + ExMsg(ex));
			}
		}

		private static string[] ReadAllLinesShared(string path)
		{
			using FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			using StreamReader streamReader = new StreamReader(stream);
			List<string> list = new List<string>();
			while (!streamReader.EndOfStream)
			{
				list.Add(streamReader.ReadLine() ?? string.Empty);
			}
			return list.ToArray();
		}

		public void LoadConfig()
		{
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!File.Exists(configPath))
				{
					CreateDefaultConfig();
				}
				DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(configPath);
				string[] array = ReadAllLinesShared(configPath);
				List<(string, KeyCode, int)> list = new List<(string, KeyCode, int)>();
				List<string> list2 = new List<string>();
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i]?.Trim();
					if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#"))
					{
						continue;
					}
					string[] array2 = text.Split(new char[1] { ':' }, 2);
					if (array2.Length == 2)
					{
						string item = array2[0].Trim();
						string value = array2[1].Trim();
						if (Enum.TryParse<KeyCode>(value, ignoreCase: true, out KeyCode result))
						{
							list.Add((item, result, i));
							continue;
						}
						list2.Add($"Line {i + 1}: Invalid key '{value}'");
						Warn($"Config parse: invalid KeyCode at {Path.GetFileName(configPath)}:{i + 1} -> '{value}'");
					}
					else
					{
						list2.Add($"Line {i + 1}: '{text}'");
						Warn($"Config parse: malformed line at {Path.GetFileName(configPath)}:{i + 1} -> '{text}'");
					}
				}
				Dictionary<KeyCode, (string, int)> dictionary = new Dictionary<KeyCode, (string, int)>();
				foreach (var (text2, val, num) in list)
				{
					if (!dictionary.ContainsKey(val))
					{
						dictionary[val] = (text2, num);
					}
					else if (dictionary[val].Item2 > num)
					{
						list2.Add($"Duplicate key {val}: removed '{dictionary[val].Item1}' (kept '{text2}')");
						dictionary[val] = (text2, num);
					}
					else
					{
						list2.Add($"Duplicate key {val}: removed '{text2}' (kept '{dictionary[val].Item1}')");
					}
				}
				List<(string, KeyCode)> collection = (from x in dictionary
					orderby x.Value.lineNumber
					select x into kvp
					select (kvp.Value.command, kvp.Key)).ToList();
				lock (_bindingsSync)
				{
					keybinds.Clear();
					keybinds.AddRange(collection);
				}
				if (list2.Count > 0 || dictionary.Count != list.Count)
				{
					if (File.GetLastWriteTimeUtc(configPath) == lastWriteTimeUtc)
					{
						SafeWriteConfig();
						foreach (string item2 in list2)
						{
							Info("Removed: " + item2);
						}
					}
					else
					{
						Info("Config changed on disk during load; skipping clean write-back to avoid clobbering external edits.");
					}
				}
				try
				{
					_configTimestampUtc = File.GetLastWriteTimeUtc(configPath);
				}
				catch
				{
				}
			}
			catch (Exception ex)
			{
				Err("LoadConfig failed: " + ExMsg(ex));
				CreateDefaultConfig();
			}
		}

		private void SafeWriteConfig()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				List<(string, KeyCode)> list;
				lock (_bindingsSync)
				{
					list = keybinds.ToList();
				}
				List<string> list2 = new List<string>(ConfigHeader);
				foreach (var item in list)
				{
					list2.Add($"{item.Item1}: {item.Item2}");
				}
				string text = configPath + ".tmp";
				File.WriteAllLines(text, list2);
				if (File.Exists(configPath))
				{
					string text2 = configPath + ".bak";
					try
					{
						File.Replace(text, configPath, text2, ignoreMetadataErrors: true);
						try
						{
							if (File.Exists(text2))
							{
								File.Delete(text2);
							}
						}
						catch
						{
						}
					}
					catch
					{
						File.Delete(configPath);
						File.Move(text, configPath);
					}
				}
				else
				{
					File.Move(text, configPath);
				}
				try
				{
					_configTimestampUtc = File.GetLastWriteTimeUtc(configPath);
				}
				catch
				{
				}
			}
			catch (Exception ex)
			{
				Err("SafeWriteConfig failed at path=" + Q(configPath) + ": " + ExMsg(ex));
			}
		}

		public void ApplyKeybindingsPublic()
		{
			ApplyKeybindings();
		}

		public void ApplyKeybindingsReplaceManagedPublic()
		{
			ApplyKeybindingsReplaceManaged();
		}

		private void ApplyKeybindings()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Console val = console ?? Object.FindObjectOfType<Console>();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				List<(string, KeyCode)> list;
				lock (_bindingsSync)
				{
					list = keybinds.ToList();
				}
				foreach (var (command, key) in list)
				{
					GameUnbind(key, val);
					GameBind(key, command, val);
				}
				lock (_bindingsSync)
				{
					_lastAppliedKeys = list.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) k) => k.key).ToHashSet();
				}
				bindingsAdded = true;
				Info($"Applied {list.Count} keybinds from {Path.GetFileName(configPath)}!");
			}
			catch (Exception ex)
			{
				Err("ApplyKeybindings failed: " + ExMsg(ex));
			}
		}

		private void ApplyKeybindingsReplaceManaged()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Console val = console ?? Object.FindObjectOfType<Console>();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				HashSet<KeyCode> hashSet;
				List<(string, KeyCode)> list;
				lock (_bindingsSync)
				{
					hashSet = new HashSet<KeyCode>(_lastAppliedKeys);
					list = keybinds.ToList();
				}
				foreach (KeyCode item in hashSet)
				{
					GameUnbind(item, val);
				}
				foreach (var (command, key) in list)
				{
					GameBind(key, command, val);
				}
				lock (_bindingsSync)
				{
					_lastAppliedKeys = list.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) k) => k.key).ToHashSet();
				}
				bindingsAdded = true;
				Info($"Applied {list.Count} keybinds from {Path.GetFileName(configPath)}!");
			}
			catch (Exception ex)
			{
				Err("ApplyKeybindingsReplaceManaged failed: " + ExMsg(ex));
			}
		}

		private void GameUnbind(KeyCode key, Console c)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			ConsoleMethodCache.EnsureInitialized(c);
			try
			{
				if (ConsoleMethodCache.Unbind != null)
				{
					ConsoleMethodCache.Unbind(c, key);
					return;
				}
			}
			catch (Exception ex)
			{
				Err($"Unbind(key={key}) threw: {ExMsg(ex)}; will try RemoveBinding.");
			}
			try
			{
				if (ConsoleMethodCache.RemoveBinding != null)
				{
					ConsoleMethodCache.RemoveBinding(c, key);
				}
				else
				{
					c.RemoveBinding(key);
				}
			}
			catch (Exception ex2)
			{
				Err($"RemoveBinding(key={key}) failed: {ExMsg(ex2)}");
			}
		}

		private void GameBind(KeyCode key, string command, Console c)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			ConsoleMethodCache.EnsureInitialized(c);
			try
			{
				if (ConsoleMethodCache.Bind != null)
				{
					ConsoleMethodCache.Bind(c, key, command);
					return;
				}
			}
			catch (Exception ex)
			{
				Err($"Bind(key={key}, cmd={Q(command)}) threw: {ExMsg(ex)}; will try AddBinding.");
			}
			try
			{
				if (ConsoleMethodCache.AddBinding != null)
				{
					ConsoleMethodCache.AddBinding(c, key, command);
				}
				else
				{
					c.AddBinding(key, command);
				}
			}
			catch (Exception ex2)
			{
				Err($"AddBinding(key={key}, cmd={Q(command)}) failed: {ExMsg(ex2)}");
			}
		}

		public void ClearAllKeybinds()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RefreshIfStale();
				List<KeyCode> list;
				lock (_bindingsSync)
				{
					list = keybinds.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) k) => k.key).Distinct().ToList();
				}
				Console val = console ?? Object.FindObjectOfType<Console>();
				if ((Object)(object)val != (Object)null)
				{
					foreach (KeyCode item in list)
					{
						GameUnbind(item, val);
					}
				}
				lock (_bindingsSync)
				{
					keybinds.Clear();
					_lastAppliedKeys.Clear();
				}
				SafeWriteConfig();
			}
			catch (Exception ex)
			{
				Err("ClearAllKeybinds failed: " + ExMsg(ex));
			}
		}

		private List<(string command, KeyCode key)> GetDefaultBinds()
		{
			return new List<(string, KeyCode)>
			{
				("cleartrash", (KeyCode)282),
				("save", (KeyCode)283),
				("showfps", (KeyCode)284),
				("hidefps", (KeyCode)285)
			};
		}

		private void CreateDefaultConfig()
		{
			try
			{
				lock (_bindingsSync)
				{
					keybinds = GetDefaultBinds();
				}
				SafeWriteConfig();
			}
			catch (Exception ex)
			{
				Err("CreateDefaultConfig failed: " + ExMsg(ex));
			}
		}

		public void ResetToDefaultsAndApply()
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RefreshIfStale();
				Console val = console ?? Object.FindObjectOfType<Console>();
				List<(string, KeyCode)> defaultBinds = GetDefaultBinds();
				HashSet<KeyCode> defaultKeys = defaultBinds.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) d) => d.key).ToHashSet();
				List<KeyCode> source;
				lock (_bindingsSync)
				{
					source = keybinds.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) k) => k.key).ToList();
				}
				List<KeyCode> list = source.Where((KeyCode k) => !defaultKeys.Contains(k)).ToList();
				if ((Object)(object)val != (Object)null)
				{
					foreach (KeyCode item in list)
					{
						GameUnbind(item, val);
					}
				}
				lock (_bindingsSync)
				{
					keybinds = defaultBinds;
				}
				SafeWriteConfig();
				if (!((Object)(object)val != (Object)null))
				{
					return;
				}
				foreach (var (command, key) in defaultBinds)
				{
					GameBind(key, command, val);
				}
				lock (_bindingsSync)
				{
					_lastAppliedKeys = defaultBinds.Select<(string, KeyCode), KeyCode>(((string command, KeyCode key) k) => k.key).ToHashSet();
				}
				bindingsAdded = true;
			}
			catch (Exception ex)
			{
				Err("ResetToDefaultsAndApply failed: " + ExMsg(ex));
			}
		}

		private void SaveBindingToConfig(KeyCode key, string command)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RefreshIfStale();
				lock (_bindingsSync)
				{
					keybinds.RemoveAll(((string command, KeyCode key) kb) => kb.key == key);
					keybinds.Add((command, key));
				}
				SafeWriteConfig();
			}
			catch (Exception ex)
			{
				Err($"SaveBindingToConfig failed for key={key}, cmd={Q(command)}: {ExMsg(ex)}");
			}
		}

		private void RemoveBindingFromConfig(KeyCode keyCode)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RefreshIfStale();
				lock (_bindingsSync)
				{
					keybinds.RemoveAll(((string command, KeyCode key) kb) => kb.key == keyCode);
				}
			}
			catch (Exception ex)
			{
				Err($"RemoveBindingFromConfig failed for key={keyCode}: {ExMsg(ex)}");
			}
			finally
			{
				SafeWriteConfig();
			}
		}
	}
	public class LoadKeysCommand : ConsoleCommand
	{
		public override string CommandWord => "loadkeys";

		public override string CommandDescription => "Reloads keybinds from keyBinds.ini and reapplies them in-game (replacing previous custom binds)";

		public override string ExampleUsage => "loadkeys";

		public LoadKeysCommand(IntPtr ptr)
			: base(ptr)
		{
		}

		public LoadKeysCommand()
			: base(ClassInjector.DerivedConstructorPointer<LoadKeysCommand>())
		{
			ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this);
		}

		public override void Execute(List<string> args)
		{
			try
			{
				if (Keybinder.Instance != null)
				{
					Keybinder.Instance.LoadConfig();
					Keybinder.Instance.ApplyKeybindingsReplaceManagedPublic();
					Keybinder.Info("Reloaded " + Path.GetFileName(Keybinder.Instance.ConfigPath) + "!");
				}
				else
				{
					Keybinder.Info("Not found!");
				}
			}
			catch (Exception ex)
			{
				Keybinder.Err("loadkeys failed: " + Keybinder.ExMsg(ex));
				Keybinder.Info("Error: " + ex.Message);
			}
		}
	}
	public class ClearKeysCommand : ConsoleCommand
	{
		public override string CommandWord => "clearkeys";

		public override string CommandDescription => "Clears all keybinds from keyBinds.ini and unbinds those keys in-game";

		public override string ExampleUsage => "clearkeys";

		public ClearKeysCommand(IntPtr ptr)
			: base(ptr)
		{
		}

		public ClearKeysCommand()
			: base(ClassInjector.DerivedConstructorPointer<ClearKeysCommand>())
		{
			ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this);
		}

		public override void Execute(List<string> args)
		{
			try
			{
				if (Keybinder.Instance != null)
				{
					Keybinder.Instance.ClearAllKeybinds();
					Keybinder.Info("All keybinds cleared!");
				}
				else
				{
					Keybinder.Info("Not found!");
				}
			}
			catch (Exception ex)
			{
				Keybinder.Err("clearkeys failed: " + Keybinder.ExMsg(ex));
				Keybinder.Info("Error: " + ex.Message);
			}
		}
	}
	public class ResetKeysCommand : ConsoleCommand
	{
		public override string CommandWord => "resetkeys";

		public override string CommandDescription => "Unbinds non-default keys, resets keyBinds.ini to defaults, and binds defaults in-game";

		public override string ExampleUsage => "resetkeys";

		public ResetKeysCommand(IntPtr ptr)
			: base(ptr)
		{
		}

		public ResetKeysCommand()
			: base(ClassInjector.DerivedConstructorPointer<ResetKeysCommand>())
		{
			ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this);
		}

		public override void Execute(List<string> args)
		{
			try
			{
				if (Keybinder.Instance != null)
				{
					Keybinder.Instance.ResetToDefaultsAndApply();
					Keybinder.Info("Keybinds reset to defaults!");
				}
				else
				{
					Keybinder.Info("Not found!");
				}
			}
			catch (Exception ex)
			{
				Keybinder.Err("resetkeys failed: " + Keybinder.ExMsg(ex));
				Keybinder.Info("Error: " + ex.Message);
			}
		}
	}
}