Decompiled source of ModMenu v1.0.2

plugins/ModMenu.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FMODUnity;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModMenu.Configs;
using SuperFantasyKingdom;
using SuperFantasyKingdom.TitleScreen;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ModMenu")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ModMenu")]
[assembly: AssemblyTitle("ModMenu")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ModMenu
{
	[BepInPlugin("ilm.modmenu", "ModMenu", "1.0.2")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ilm.modmenu";

		public const string PluginName = "ModMenu";

		public const string PluginVersion = "1.0.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> MenuScale;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			MenuScale = ((BaseUnityPlugin)this).Config.Bind<float>("Menu", "Scale", 1f, new ConfigDescription("Size of the mod settings window and its text. 1 = default.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			ConfigFileTracker.Track(((BaseUnityPlugin)this).Config);
			new Harmony("ilm.modmenu").PatchAll();
			Log.LogInfo((object)"ModMenu 1.0.2 loaded");
		}
	}
}
namespace ModMenu.UI
{
	internal sealed class MenuWindow : MonoBehaviour
	{
		private const string FieldPrefix = "modmenu_field_";

		private const string SearchControl = "modmenu_search";

		private static MenuWindow _instance;

		private bool _open;

		private Theme _theme;

		private float _themeScale = -1f;

		private GameObject _blocker;

		private List<ModConfig> _mods = new List<ModConfig>();

		private ModConfig _selected;

		private string _lastSelectedPath;

		private Vector2 _listScroll;

		private Vector2 _settingsScroll;

		private string _search = "";

		private readonly Dictionary<Setting, string> _drafts = new Dictionary<Setting, string>();

		private readonly Dictionary<Setting, string> _errors = new Dictionary<Setting, string>();

		private readonly List<Setting> _visible = new List<Setting>();

		private string _focused = "";

		private string _notice;

		private float _noticeUntil;

		private float _confirmResetUntil;

		private bool _loggedFirstDraw;

		private bool _loggedDrawError;

		private bool _warnedNoDraw;

		private float _openedAt;

		private int _lastDrawFrame = -1;

		internal static MenuWindow Instance
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Expected O, but got Unknown
				if ((Object)(object)_instance == (Object)null)
				{
					GameObject val = new GameObject("ModMenuWindow");
					Object.DontDestroyOnLoad((Object)val);
					_instance = val.AddComponent<MenuWindow>();
				}
				return _instance;
			}
		}

		internal static bool IsOpen
		{
			get
			{
				if ((Object)(object)_instance != (Object)null)
				{
					return _instance._open;
				}
				return false;
			}
		}

		private void Awake()
		{
			_instance = this;
		}

		internal void Open()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!_open)
			{
				Sfx.Click();
				try
				{
					_mods = ConfigCatalog.Build();
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Couldn't list config files: {arg}");
					_mods = new List<ModConfig>();
				}
				_selected = _mods.FirstOrDefault((ModConfig m) => m.FilePath == _lastSelectedPath) ?? _mods.FirstOrDefault();
				_drafts.Clear();
				_errors.Clear();
				_search = "";
				_settingsScroll = Vector2.zero;
				_open = true;
				_openedAt = Time.unscaledTime;
				_warnedNoDraw = false;
				CallTitleScreen("OpenOverlay");
				CreateBlocker();
				Plugin.Log.LogInfo((object)$"Opened mod settings: {_mods.Count} config files, window active={((Behaviour)this).isActiveAndEnabled}");
			}
		}

		internal void Close()
		{
			if (!_open)
			{
				return;
			}
			CommitAllDrafts();
			foreach (ModConfig mod in _mods)
			{
				mod.Save();
			}
			_open = false;
			GUIUtility.keyboardControl = 0;
			if ((Object)(object)_blocker != (Object)null)
			{
				Object.Destroy((Object)(object)_blocker);
			}
			_blocker = null;
			Sfx.Cancel();
			CallTitleScreen("CloseOverlay");
			if ((Object)(object)TitleScreenButton.Button != (Object)null && (Object)(object)GamepadManager.Instance != (Object)null)
			{
				GamepadManager.Instance.SetGameObjectToSelect(TitleScreenButton.Button, true);
			}
		}

		private static void CallTitleScreen(string method)
		{
			TitleScreenManager instance = TitleScreenManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				AccessTools.Method(typeof(TitleScreenManager), method, (Type[])null, (Type[])null)?.Invoke(instance, null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("TitleScreenManager." + method + " failed: " + ex.Message));
			}
		}

		private void CreateBlocker()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			_blocker = new GameObject("ModMenuBlocker", new Type[2]
			{
				typeof(Canvas),
				typeof(GraphicRaycaster)
			});
			Canvas component = _blocker.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.sortingOrder = 32767;
			GameObject val = new GameObject("Dim", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent(_blocker.transform, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			Vector2 offsetMin = (val2.offsetMax = Vector2.zero);
			val2.offsetMin = offsetMin;
			((Graphic)val.GetComponent<Image>()).color = Color.clear;
		}

		private void Update()
		{
			if (!_open)
			{
				return;
			}
			if ((Object)(object)TitleScreenManager.Instance == (Object)null)
			{
				Close();
				return;
			}
			foreach (ModConfig mod in _mods)
			{
				mod.SaveIfIdle();
			}
			if (!_warnedNoDraw && _lastDrawFrame < 0 && Time.unscaledTime - _openedAt > 1f)
			{
				_warnedNoDraw = true;
				Plugin.Log.LogWarning((object)"The settings window has been open for a second but OnGUI hasn't run");
			}
		}

		private void OnGUI()
		{
			if (!_open)
			{
				return;
			}
			_lastDrawFrame = Time.frameCount;
			if (!_loggedFirstDraw)
			{
				_loggedFirstDraw = true;
				Plugin.Log.LogInfo((object)$"Drawing settings window at {Screen.width}x{Screen.height}");
			}
			try
			{
				Draw();
			}
			catch (ExitGUIException)
			{
				throw;
			}
			catch (Exception arg)
			{
				if (!_loggedDrawError)
				{
					_loggedDrawError = true;
					Plugin.Log.LogError((object)$"Settings window failed to draw: {arg}");
				}
			}
		}

		private void Draw()
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Invalid comparison between Unknown and I4
			//IL_014d: 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)
			float num = (float)Screen.height / 1080f * Plugin.MenuScale.Value;
			if (_theme == null || !Mathf.Approximately(num, _themeScale))
			{
				_theme?.Destroy();
				_theme = new Theme(num);
				_themeScale = num;
				Plugin.Log.LogInfo((object)string.Format("Built window theme: scale {0}, game font {1}", _theme.Unit, _theme.PixelFont ? "found" : "not found"));
			}
			GUI.skin = _theme.Skin;
			GUI.depth = -1000;
			HandleKeys();
			Theme theme = _theme;
			if ((int)Event.current.type == 7)
			{
				GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)theme.Dimmer);
			}
			float num2 = Mathf.Min((float)Screen.width - theme.S(60f), theme.S(1400f));
			float num3 = Mathf.Min((float)Screen.height - theme.S(60f), theme.S(940f));
			GUILayout.BeginArea(new Rect(((float)Screen.width - num2) / 2f, ((float)Screen.height - num3) / 2f, num2, num3), theme.Window);
			DrawTitleBar();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawModList();
			GUILayout.Space(theme.S(12f));
			DrawSettings();
			GUILayout.EndHorizontal();
			DrawFooter();
			GUILayout.EndArea();
			TrackFocus();
		}

		private void HandleKeys()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			Event current = Event.current;
			if ((int)current.type != 4)
			{
				return;
			}
			KeyCode keyCode = current.keyCode;
			if (((int)keyCode == 13 || (int)keyCode == 271) ? true : false)
			{
				string nameOfFocusedControl = GUI.GetNameOfFocusedControl();
				if (nameOfFocusedControl.StartsWith("modmenu_field_") || nameOfFocusedControl == "modmenu_search")
				{
					CommitField(nameOfFocusedControl);
					GUI.FocusControl((string)null);
					current.Use();
				}
			}
		}

		private void TrackFocus()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if ((int)Event.current.type == 7)
			{
				string nameOfFocusedControl = GUI.GetNameOfFocusedControl();
				if (nameOfFocusedControl != _focused)
				{
					CommitField(_focused);
					_focused = nameOfFocusedControl;
				}
			}
		}

		private void DrawTitleBar()
		{
			Theme theme = _theme;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Mod Settings", theme.Title, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			if (Time.unscaledTime < _noticeUntil && _notice != null)
			{
				GUILayout.Label(_notice, theme.Status, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			}
			if (GUILayout.Button("Close", theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(theme.S(8f));
		}

		private void DrawModList()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Theme theme = _theme;
			GUILayout.BeginVertical(theme.Panel, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(theme.S(360f)),
				GUILayout.ExpandHeight(true)
			});
			_listScroll = GUILayout.BeginScrollView(_listScroll, false, false, theme.Skin.horizontalScrollbar, theme.ScrollV, theme.None, Array.Empty<GUILayoutOption>());
			if (_mods.Count == 0)
			{
				GUILayout.Label("No config files found.", theme.Dim, Array.Empty<GUILayoutOption>());
			}
			int num = -1;
			foreach (ModConfig mod in _mods)
			{
				int num2 = (mod.IsCore ? 1 : ((!mod.IsLive) ? 2 : 0));
				if (num2 != num && num2 == 2)
				{
					GUILayout.Label("Not loaded", theme.Section, Array.Empty<GUILayoutOption>());
				}
				num = num2;
				bool flag = mod == _selected;
				GUIStyle val = ((!mod.IsLive) ? (flag ? theme.ListItemOffSelected : theme.ListItemOff) : (flag ? theme.ListItemSelected : theme.ListItem));
				if (GUILayout.Button(mod.Name, val, Array.Empty<GUILayoutOption>()) && !flag)
				{
					Select(mod);
				}
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
		}

		private void Select(ModConfig mod)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			CommitAllDrafts();
			Sfx.Click();
			_selected = mod;
			_lastSelectedPath = mod.FilePath;
			_settingsScroll = Vector2.zero;
			_errors.Clear();
			_confirmResetUntil = 0f;
		}

		private void DrawSettings()
		{
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: 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)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			Theme theme = _theme;
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.ExpandWidth(true),
				GUILayout.ExpandHeight(true)
			});
			if (_selected == null)
			{
				GUILayout.FlexibleSpace();
				GUILayout.Label("Pick a mod on the left to change its settings.", theme.Hint, Array.Empty<GUILayoutOption>());
				GUILayout.FlexibleSpace();
				GUILayout.EndVertical();
				return;
			}
			DrawModHeader(_selected);
			GUILayout.Space(theme.S(6f));
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUI.SetNextControlName("modmenu_search");
			_search = GUILayout.TextField(_search, theme.Field, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (_search.Length == 0 && GUI.GetNameOfFocusedControl() != "modmenu_search")
			{
				GUI.Label(GUILayoutUtility.GetLastRect(), "Search settings...", theme.Placeholder);
			}
			if (_search.Length > 0 && GUILayout.Button("Clear", theme.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
			{
				_search = "";
				GUI.FocusControl((string)null);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(theme.S(6f));
			_settingsScroll = GUILayout.BeginScrollView(_settingsScroll, false, false, theme.Skin.horizontalScrollbar, theme.ScrollV, theme.Panel, Array.Empty<GUILayoutOption>());
			string text = null;
			_visible.Clear();
			foreach (Setting setting in _selected.Settings)
			{
				if (Matches(setting))
				{
					if (setting.Section != text)
					{
						text = setting.Section;
						GUILayout.Label(string.IsNullOrEmpty(text) ? "General" : text, theme.Section, Array.Empty<GUILayoutOption>());
					}
					DrawSetting(setting);
				}
			}
			if (text == null)
			{
				GUILayout.Label((_search.Length > 0) ? "No settings match your search." : "This file has no settings.", theme.Dim, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.EndVertical();
		}

		private void DrawModHeader(ModConfig mod)
		{
			Theme theme = _theme;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(mod.Name, theme.Header, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			if (!string.IsNullOrEmpty(mod.Version))
			{
				GUILayout.Label("v" + mod.Version, theme.Dim, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			}
			GUILayout.FlexibleSpace();
			GUILayout.EndHorizontal();
			if (mod.IsCore)
			{
				GUILayout.Label("BepInEx reads most of these at startup. Restart the game after changing them.", theme.StatusWarn, Array.Empty<GUILayoutOption>());
			}
			else if (mod.IsLive)
			{
				GUILayout.Label("Loaded. Changes are applied to the mod right away.", theme.StatusLive, Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label("This mod isn't loaded. Changes are saved and used the next time it loads.", theme.StatusOff, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Label(mod.RelativePath, theme.Dim, Array.Empty<GUILayoutOption>());
			GUILayout.EndVertical();
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) });
			bool flag = Time.unscaledTime < _confirmResetUntil;
			if (GUILayout.Button(flag ? "Really reset?" : "Reset all", theme.Button, Array.Empty<GUILayoutOption>()))
			{
				if (flag)
				{
					CommitAllDrafts();
					int num = mod.Settings.Count((Setting s) => !s.ReadOnly && s.Reset() != null);
					_errors.Clear();
					_confirmResetUntil = 0f;
					Notify((num == 0) ? "Reset to defaults" : $"Reset, {num} couldn't be changed");
					Sfx.Click();
				}
				else
				{
					_confirmResetUntil = Time.unscaledTime + 3f;
					Sfx.Click();
				}
			}
			if (GUILayout.Button("Reload file", theme.Button, Array.Empty<GUILayoutOption>()))
			{
				_drafts.Clear();
				_errors.Clear();
				string text = mod.Reload();
				Notify((text == null) ? "Reloaded from disk" : ("Reload failed: " + text));
				Sfx.Click();
			}
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
		}

		private bool Matches(Setting s)
		{
			if (_search.Length == 0)
			{
				return true;
			}
			if (!Contains(s.DisplayName) && !Contains(s.Key) && !Contains(s.Section))
			{
				return Contains(s.Description);
			}
			return true;
			bool Contains(string text)
			{
				if (text != null)
				{
					return text.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0;
				}
				return false;
			}
		}

		private void DrawSetting(Setting s)
		{
			Theme theme = _theme;
			int count = _visible.Count;
			_visible.Add(s);
			GUILayout.BeginVertical(theme.Row, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label(s.DisplayName, theme.Label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(theme.S(320f)) });
			bool enabled = GUI.enabled;
			GUI.enabled = enabled && !s.ReadOnly;
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			switch (s.Kind)
			{
			case SettingKind.Toggle:
				DrawToggle(s);
				break;
			case SettingKind.Choice:
				DrawChoice(s);
				break;
			case SettingKind.Flags:
				DrawFlags(s);
				break;
			case SettingKind.Slider:
				DrawSlider(s, count);
				break;
			default:
				DrawText(s, count, GUILayout.ExpandWidth(true));
				break;
			}
			GUILayout.EndVertical();
			GUI.enabled = enabled && !s.ReadOnly && !s.IsDefault;
			if (GUILayout.Button("Reset", theme.ButtonSmall, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(theme.S(90f)) }))
			{
				_drafts.Remove(s);
				Apply(s, s.DefaultValue);
				Sfx.Click();
			}
			GUI.enabled = enabled;
			GUILayout.EndHorizontal();
			if (_errors.TryGetValue(s, out var value))
			{
				GUILayout.Label(value, theme.Error, Array.Empty<GUILayoutOption>());
			}
			if (!string.IsNullOrEmpty(s.Description))
			{
				GUILayout.Label(s.Description, theme.Dim, Array.Empty<GUILayoutOption>());
			}
			string text = Details(s);
			if (text != null)
			{
				GUILayout.Label(text, theme.Dim, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndVertical();
		}

		private static string Details(Setting s)
		{
			List<string> list = new List<string>();
			if (s.DefaultValue != null)
			{
				list.Add("Default: " + s.DefaultValue);
			}
			if (s.Kind == SettingKind.Slider)
			{
				list.Add("Range: " + Setting.FormatNumber(s.Min, s.IsInteger) + " to " + Setting.FormatNumber(s.Max, s.IsInteger));
			}
			if (s.ReadOnly)
			{
				list.Add("Read-only");
			}
			if (list.Count <= 0)
			{
				return null;
			}
			return string.Join("   ", list);
		}

		private void DrawToggle(Setting s)
		{
			Theme theme = _theme;
			bool isOn = s.IsOn;
			if (GUILayout.Button(isOn ? "On" : "Off", isOn ? theme.ToggleOn : theme.ToggleOff, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(theme.S(120f)) }))
			{
				Apply(s, isOn ? "false" : "true");
				Sfx.Click();
			}
		}

		private void DrawChoice(Setting s)
		{
			Theme theme = _theme;
			string current = s.Value?.Trim();
			int num = Array.FindIndex(s.Choices, (string c) => string.Equals(c, current, StringComparison.OrdinalIgnoreCase));
			if (s.Choices.Length <= 4 && s.Choices.Sum((string c) => c.Length) <= 32)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				for (int num2 = 0; num2 < s.Choices.Length; num2++)
				{
					if (GUILayout.Button(s.Choices[num2], (num2 == num) ? theme.ChoiceSelected : theme.Choice, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && num2 != num)
					{
						Apply(s, s.Choices[num2]);
						Sfx.Click();
					}
				}
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
				return;
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("<", theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(theme.S(48f)) }))
			{
				Apply(s, s.Choices[(num - 1 + s.Choices.Length) % s.Choices.Length]);
				Sfx.Click();
			}
			GUILayout.Label((num >= 0) ? s.Choices[num] : current, theme.ChoiceSelected, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if (GUILayout.Button(">", theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(theme.S(48f)) }))
			{
				Apply(s, s.Choices[(num + 1) % s.Choices.Length]);
				Sfx.Click();
			}
			GUILayout.EndHorizontal();
		}

		private void DrawFlags(Setting s)
		{
			Theme theme = _theme;
			HashSet<string> hashSet = new HashSet<string>(s.SelectedFlags, StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < s.Choices.Length; i += 3)
			{
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				foreach (string item in s.Choices.Skip(i).Take(3))
				{
					bool flag = hashSet.Contains(item);
					if (GUILayout.Button(item, flag ? theme.ChoiceSelected : theme.Choice, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }))
					{
						if (flag)
						{
							hashSet.Remove(item);
						}
						else
						{
							hashSet.Add(item);
						}
						string text = string.Join(", ", s.Choices.Where(hashSet.Contains));
						Apply(s, (text.Length > 0) ? text : "0");
						Sfx.Click();
					}
				}
				GUILayout.FlexibleSpace();
				GUILayout.EndHorizontal();
			}
		}

		private void DrawSlider(Setting s, int index)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_006b: Invalid comparison between Unknown and I4
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			Theme theme = _theme;
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			float num = (float)Math.Max(s.Min, Math.Min(s.Max, s.NumberValue));
			Rect rect = GUILayoutUtility.GetRect(theme.S(120f), theme.S(28f), theme.SliderTrack, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			if ((int)Event.current.type == 7)
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height / 2f - theme.S(5f), ((Rect)(ref rect)).width, theme.S(10f));
				theme.SliderBar.Draw(val, false, false, false, false);
			}
			float num2 = GUI.HorizontalSlider(rect, num, (float)s.Min, (float)s.Max, theme.SliderTrack, theme.SliderThumb);
			if (!Mathf.Approximately(num2, num))
			{
				_drafts.Remove(s);
				Apply(s, Setting.FormatNumber(Snap(num2, s), s.IsInteger));
			}
			DrawText(s, index, GUILayout.Width(theme.S(130f)));
			GUILayout.EndHorizontal();
		}

		private static double Snap(double value, Setting s)
		{
			if (s.IsInteger)
			{
				return Math.Round(value);
			}
			double num = s.Max - s.Min;
			if (num <= 0.0)
			{
				return value;
			}
			double num2 = Math.Pow(10.0, Math.Floor(Math.Log10(num)) - 3.0);
			return Math.Round(Math.Round(value / num2) * num2, 6);
		}

		private void DrawText(Setting s, int index, params GUILayoutOption[] options)
		{
			Theme theme = _theme;
			string nextControlName = "modmenu_field_" + index;
			string value;
			string text = (_drafts.TryGetValue(s, out value) ? value : s.Value);
			GUI.SetNextControlName(nextControlName);
			string text2 = GUILayout.TextField(text ?? "", _errors.ContainsKey(s) ? theme.FieldError : theme.Field, options);
			if (text2 != text)
			{
				_drafts[s] = text2;
			}
		}

		private void CommitField(string controlName)
		{
			if (controlName != null && controlName.StartsWith("modmenu_field_") && int.TryParse(controlName.Substring("modmenu_field_".Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result >= 0 && result < _visible.Count)
			{
				Setting setting = _visible[result];
				if (_drafts.TryGetValue(setting, out var value))
				{
					_drafts.Remove(setting);
					Apply(setting, value);
				}
			}
		}

		private void CommitAllDrafts()
		{
			foreach (KeyValuePair<Setting, string> item in _drafts.ToList())
			{
				Apply(item.Key, item.Value);
			}
			_drafts.Clear();
		}

		private void Apply(Setting s, string value)
		{
			if (value != null)
			{
				string text = s.TrySet(value);
				if (text == null)
				{
					_errors.Remove(s);
				}
				else
				{
					_errors[s] = text;
				}
			}
		}

		private void DrawFooter()
		{
			Theme theme = _theme;
			GUILayout.Space(theme.S(8f));
			GUILayout.Label("Changes are saved automatically. Most mods use them right away, but a few only read their settings when the game starts.", theme.Hint, Array.Empty<GUILayoutOption>());
		}

		private void Notify(string text)
		{
			_notice = text;
			_noticeUntil = Time.unscaledTime + 3f;
		}

		private void OnDestroy()
		{
			if (_open)
			{
				foreach (ModConfig mod in _mods)
				{
					mod.Save();
				}
			}
			_theme?.Destroy();
		}

		private void OnApplicationQuit()
		{
			foreach (ModConfig mod in _mods)
			{
				mod.Save();
			}
		}
	}
	internal static class Sfx
	{
		internal static void Click()
		{
			Play("event:/SFX/UI/Click");
		}

		internal static void Cancel()
		{
			Play("event:/SFX/UI/Cancel");
		}

		private static void Play(string path)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RuntimeManager.PlayOneShot(path, default(Vector3));
			}
			catch (Exception)
			{
			}
		}
	}
	internal sealed class Theme
	{
		private static readonly Color Text = Hex(16182226);

		private static readonly Color TextDim = Hex(12166533);

		private static readonly Color Gold = Hex(15909968);

		private static readonly Color Green = Hex(9425258);

		private static readonly Color Amber = Hex(15769675);

		private static readonly Color Red = Hex(15756122);

		private static readonly Color Outline = Hex(1840144);

		public readonly float Unit;

		public readonly bool PixelFont;

		private readonly int _px;

		private readonly List<Texture2D> _textures = new List<Texture2D>();

		public GUIStyle Window;

		public GUIStyle Panel;

		public GUIStyle Row;

		public GUIStyle Title;

		public GUIStyle Header;

		public GUIStyle Section;

		public GUIStyle Label;

		public GUIStyle Dim;

		public GUIStyle Status;

		public GUIStyle Error;

		public GUIStyle Hint;

		public GUIStyle Button;

		public GUIStyle ButtonSmall;

		public GUIStyle ListItem;

		public GUIStyle ListItemSelected;

		public GUIStyle ListItemOff;

		public GUIStyle ListItemOffSelected;

		public GUIStyle ToggleOn;

		public GUIStyle ToggleOff;

		public GUIStyle Choice;

		public GUIStyle ChoiceSelected;

		public GUIStyle Field;

		public GUIStyle FieldError;

		public GUISkin Skin;

		public Texture2D Dimmer;

		public GUIStyle SliderTrack;

		public GUIStyle SliderBar;

		public GUIStyle SliderThumb;

		public GUIStyle ScrollV;

		public GUIStyle ScrollVThumb;

		public GUIStyle None;

		public GUIStyle Placeholder;

		public GUIStyle StatusLive;

		public GUIStyle StatusWarn;

		public GUIStyle StatusOff;

		private static Font _bodyFont;

		private static Font _headerFont;

		private static bool _fontsSearched;

		private RectOffset PixelBorder => new RectOffset(3 * _px, 3 * _px, 3 * _px, 3 * _px);

		public Theme(float scale)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: 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)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Expected O, but got Unknown
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_0464: Expected O, but got Unknown
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0470: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0485: Expected O, but got Unknown
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0531: Unknown result type (might be due to invalid IL or missing references)
			//IL_0549: Unknown result type (might be due to invalid IL or missing references)
			//IL_0576: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0604: Unknown result type (might be due to invalid IL or missing references)
			//IL_061b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0621: Unknown result type (might be due to invalid IL or missing references)
			//IL_0630: Expected O, but got Unknown
			//IL_0637: Unknown result type (might be due to invalid IL or missing references)
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_0659: Unknown result type (might be due to invalid IL or missing references)
			//IL_0668: Expected O, but got Unknown
			//IL_0671: Unknown result type (might be due to invalid IL or missing references)
			//IL_0689: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0707: Unknown result type (might be due to invalid IL or missing references)
			//IL_0747: Unknown result type (might be due to invalid IL or missing references)
			//IL_074c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0763: Unknown result type (might be due to invalid IL or missing references)
			//IL_0769: Unknown result type (might be due to invalid IL or missing references)
			//IL_0778: Expected O, but got Unknown
			//IL_0779: Unknown result type (might be due to invalid IL or missing references)
			//IL_077e: Unknown result type (might be due to invalid IL or missing references)
			//IL_078f: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a4: Expected O, but got Unknown
			//IL_07ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_07da: Expected O, but got Unknown
			//IL_07db: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_080e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0825: Unknown result type (might be due to invalid IL or missing references)
			//IL_0831: Unknown result type (might be due to invalid IL or missing references)
			//IL_0842: Unknown result type (might be due to invalid IL or missing references)
			//IL_0858: Expected O, but got Unknown
			//IL_0859: Unknown result type (might be due to invalid IL or missing references)
			//IL_085e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0869: Unknown result type (might be due to invalid IL or missing references)
			//IL_0875: Unknown result type (might be due to invalid IL or missing references)
			//IL_088e: Unknown result type (might be due to invalid IL or missing references)
			//IL_089f: Unknown result type (might be due to invalid IL or missing references)
			//IL_08b4: Expected O, but got Unknown
			//IL_08b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_08ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_08dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_090a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0916: Unknown result type (might be due to invalid IL or missing references)
			//IL_092c: Expected O, but got Unknown
			//IL_0976: Unknown result type (might be due to invalid IL or missing references)
			//IL_09ae: Unknown result type (might be due to invalid IL or missing references)
			FindFonts();
			PixelFont = (Object)(object)_bodyFont != (Object)null;
			int num = Mathf.Max(1, Mathf.RoundToInt(2f * scale));
			Unit = (float)num / 2f;
			_px = Mathf.Max(1, Mathf.RoundToInt(Unit * 2f));
			int size = (PixelFont ? (16 * num) : Mathf.RoundToInt(19f * Unit));
			int size2 = (PixelFont ? (16 * Mathf.Max(2, Mathf.RoundToInt(3f * scale))) : Mathf.RoundToInt(28f * Unit));
			GUIStyle frame = Frame(Hex(3811356), Outline, Hex(7227948));
			GUIStyle frame2 = Frame(Hex(2234383), Outline, Hex(3023126));
			GUIStyle frame3 = Frame(Hex(2826005), Hex(2234383), Hex(3351834));
			GUIStyle val = Frame(Hex(8016690), Outline, Hex(10515528));
			GUIStyle val2 = Frame(Hex(9265984), Outline, Hex(12159576));
			GUIStyle down = Frame(Hex(6176549), Outline, Hex(7227948));
			GUIStyle val3 = Frame(Hex(13199658), Outline, Hex(15243344));
			GUIStyle val4 = Frame(Hex(14187064), Outline, Hex(15969896));
			GUIStyle val5 = Frame(Hex(5147194), Outline, Hex(7648855));
			GUIStyle hover = Frame(Hex(6069318), Outline, Hex(8832872));
			GUIStyle val6 = Frame(Hex(3877662), Outline, Hex(4863526));
			GUIStyle hover2 = Frame(Hex(4863526), Outline, Hex(5915440));
			GUIStyle val7 = Frame(Hex(1248521), Hex(7227948), Hex(1248521));
			GUIStyle val8 = Frame(Hex(1248521), Gold, Hex(1248521));
			GUIStyle val9 = Frame(Hex(1969418), Red, Hex(1969418));
			GUIStyle val10 = Solid(new Color(0f, 0f, 0f, 0f));
			GUIStyle val11 = new GUIStyle();
			val11.normal.background = null;
			None = val11;
			Dimmer = Solid(new Color(0.05f, 0.03f, 0.02f, 0.7f)).normal.background;
			Window = Box(frame, 16);
			Panel = Box(frame2, 8);
			Row = Box(frame3, 10);
			Row.margin = Offsets(0, 0, 0, 6);
			Title = Text_(size2, Gold, _headerFont ?? _bodyFont);
			Header = Text_(size, Gold, _headerFont ?? _bodyFont);
			Section = Text_(size, Gold, _headerFont ?? _bodyFont);
			Section.margin = Offsets(4, 4, 14, 6);
			Label = Text_(size, Text, _bodyFont);
			Label.wordWrap = true;
			Dim = Text_(size, TextDim, _bodyFont);
			Dim.wordWrap = true;
			Status = Text_(size, Green, _bodyFont);
			Status.wordWrap = true;
			StatusLive = Status;
			GUIStyle val12 = new GUIStyle(Status);
			val12.normal.textColor = Amber;
			StatusWarn = val12;
			GUIStyle val13 = new GUIStyle(Status);
			val13.normal.textColor = TextDim;
			StatusOff = val13;
			Error = Text_(size, Red, _bodyFont);
			Error.wordWrap = true;
			Hint = Text_(size, TextDim, _bodyFont);
			Hint.wordWrap = true;
			Hint.alignment = (TextAnchor)4;
			Placeholder = Text_(size, new Color(TextDim.r, TextDim.g, TextDim.b, 0.6f), _bodyFont);
			Placeholder.padding = Offsets(10, 10, 6, 6);
			Button = Clickable(val, val2, down, size, Text);
			ButtonSmall = Clickable(val, val2, down, size, Text);
			ButtonSmall.padding = Offsets(8, 8, 4, 4);
			ListItem = Clickable(val10, val6, down, size, Text);
			ListItem.alignment = (TextAnchor)3;
			ListItem.wordWrap = true;
			ListItem.margin = Offsets(0, 0, 0, 4);
			ListItemSelected = Clickable(val3, val4, val3, size, Color.white);
			ListItemSelected.alignment = (TextAnchor)3;
			ListItemSelected.wordWrap = true;
			ListItemSelected.margin = ListItem.margin;
			GUIStyle val14 = new GUIStyle(ListItem);
			val14.normal.background = val10.normal.background;
			val14.normal.textColor = TextDim;
			ListItemOff = val14;
			GUIStyle val15 = new GUIStyle(ListItemSelected);
			val15.normal.background = val6.normal.background;
			val15.normal.textColor = Text;
			ListItemOffSelected = val15;
			ToggleOn = Clickable(val5, hover, val5, size, Color.white);
			ToggleOff = Clickable(val6, hover2, val6, size, TextDim);
			Choice = Clickable(val6, hover2, down, size, TextDim);
			ChoiceSelected = Clickable(val3, val4, val3, size, Color.white);
			Field = Clickable(val7, val7, val7, size, Text);
			Field.focused.background = val8.normal.background;
			Field.focused.textColor = Text;
			Field.alignment = (TextAnchor)3;
			Field.clipping = (TextClipping)1;
			Field.padding = Offsets(10, 10, 6, 6);
			GUIStyle val16 = new GUIStyle(Field);
			val16.normal.background = val9.normal.background;
			val16.normal.textColor = Red;
			FieldError = val16;
			SliderTrack = new GUIStyle
			{
				fixedHeight = S(28f),
				margin = Offsets(6, 6, 6, 6)
			};
			SliderBar = new GUIStyle(Frame(Hex(1248521), Outline, Hex(1248521)))
			{
				border = PixelBorder
			};
			GUIStyle val17 = new GUIStyle();
			val17.normal.background = val3.normal.background;
			val17.hover.background = val4.normal.background;
			val17.active.background = val4.normal.background;
			val17.border = PixelBorder;
			val17.fixedWidth = S(18f);
			val17.fixedHeight = S(28f);
			SliderThumb = val17;
			GUIStyle val18 = new GUIStyle
			{
				name = "verticalscrollbar"
			};
			val18.normal.background = Solid(Hex(1708556)).normal.background;
			val18.fixedWidth = S(12f);
			val18.margin = Offsets(6, 0, 0, 0);
			ScrollV = val18;
			GUIStyle val19 = new GUIStyle
			{
				name = "verticalscrollbarthumb"
			};
			val19.normal.background = val.normal.background;
			val19.hover.background = val2.normal.background;
			val19.active.background = val2.normal.background;
			val19.border = PixelBorder;
			val19.fixedWidth = S(12f);
			ScrollVThumb = val19;
			Skin = Object.Instantiate<GUISkin>(GUI.skin);
			((Object)Skin).hideFlags = (HideFlags)61;
			Skin.verticalScrollbar = ScrollV;
			Skin.verticalScrollbarThumb = ScrollVThumb;
			Skin.settings.cursorColor = Text;
			Skin.settings.selectionColor = new Color(Gold.r, Gold.g, Gold.b, 0.35f);
		}

		public float S(float v)
		{
			return Mathf.Round(v * Unit);
		}

		private static void FindFonts()
		{
			if (!_fontsSearched || !((Object)(object)_bodyFont != (Object)null))
			{
				_fontsSearched = true;
				Font[] fonts = Resources.FindObjectsOfTypeAll<Font>();
				TMP_FontAsset[] assets = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
				_bodyFont = Find(fonts, assets, "Passage7");
				_headerFont = Find(fonts, assets, "Compass 9") ?? _bodyFont;
				if ((Object)(object)_bodyFont == (Object)null)
				{
					Plugin.Log.LogDebug((object)"Game font not found yet, using Unity's default font");
				}
			}
		}

		private static Font Find(Font[] fonts, TMP_FontAsset[] assets, string name)
		{
			return ((IEnumerable<Font>)fonts).FirstOrDefault((Func<Font, bool>)((Font f) => (Object)(object)f != (Object)null && ((Object)f).name == name && f.dynamic)) ?? (from a in assets
				where (Object)(object)a != (Object)null && ((Object)a).name == name
				select a.sourceFontFile).FirstOrDefault((Func<Font, bool>)((Font f) => (Object)(object)f != (Object)null && f.dynamic));
		}

		private GUIStyle Box(GUIStyle frame, int padding)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			return new GUIStyle(frame)
			{
				border = PixelBorder,
				padding = Offsets(padding, padding, padding, padding)
			};
		}

		public void Destroy()
		{
			foreach (Texture2D texture in _textures)
			{
				if ((Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
			}
			_textures.Clear();
			if ((Object)(object)Skin != (Object)null)
			{
				Object.Destroy((Object)(object)Skin);
			}
		}

		private GUIStyle Text_(int size, Color color, Font font)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			GUIStyle val = new GUIStyle
			{
				font = font,
				fontSize = size
			};
			val.normal.textColor = color;
			val.padding = Offsets(2, 2, 2, 2);
			val.margin = Offsets(0, 0, 2, 2);
			val.richText = false;
			return val;
		}

		private GUIStyle Clickable(GUIStyle normal, GUIStyle hover, GUIStyle down, int size, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			GUIStyle val = new GUIStyle
			{
				font = _bodyFont,
				fontSize = size,
				alignment = (TextAnchor)4
			};
			val.normal.background = normal.normal.background;
			val.normal.textColor = color;
			val.hover.background = hover.normal.background;
			val.hover.textColor = color;
			val.active.background = down.normal.background;
			val.active.textColor = color;
			val.onNormal.background = normal.normal.background;
			val.onNormal.textColor = color;
			val.border = PixelBorder;
			val.padding = Offsets(14, 14, 7, 7);
			val.margin = Offsets(3, 3, 3, 3);
			return val;
		}

		private RectOffset Offsets(int l, int r, int t, int b)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			return new RectOffset((int)S(l), (int)S(r), (int)S(t), (int)S(b));
		}

		private GUIStyle Frame(Color fill, Color outline, Color highlight)
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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)
			int num = 8 * _px;
			Texture2D val = NewTexture(num);
			Color[] array = (Color[])(object)new Color[num * num];
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < num; j++)
				{
					int num2 = j / _px;
					int num3 = i / _px;
					bool flag = num2 == 0 || num3 == 0 || num2 == 7 || num3 == 7;
					bool flag2 = (num2 == 0 || num2 == 7) && (num3 == 0 || num3 == 7);
					array[i * num + j] = (flag2 ? Color.clear : (flag ? outline : ((num3 == 6) ? highlight : fill)));
				}
			}
			val.SetPixels(array);
			val.Apply();
			GUIStyle val2 = new GUIStyle();
			val2.normal.background = val;
			val2.border = PixelBorder;
			return val2;
		}

		private GUIStyle Solid(Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			Texture2D val = NewTexture(1);
			val.SetPixel(0, 0, color);
			val.Apply();
			GUIStyle val2 = new GUIStyle();
			val2.normal.background = val;
			return val2;
		}

		private Texture2D NewTexture(int size)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1,
				hideFlags = (HideFlags)61
			};
			_textures.Add(val);
			return val;
		}

		private static Color Hex(int rgb)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)((rgb >> 16) & 0xFF) / 255f, (float)((rgb >> 8) & 0xFF) / 255f, (float)(rgb & 0xFF) / 255f, 1f);
		}
	}
	internal static class TitleScreenButton
	{
		[HarmonyPatch(typeof(TitleScreenManager), "Awake")]
		private static class TitleScreenManager_Awake
		{
			private static void Postfix(TitleScreenManager __instance)
			{
				try
				{
					Create(__instance);
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Couldn't add the Mods button to the title screen: {arg}");
				}
			}
		}

		[HarmonyPatch(typeof(TitleScreenManager), "Close", new Type[] { typeof(CallbackContext) })]
		private static class TitleScreenManager_Close
		{
			private static bool Prefix(CallbackContext context)
			{
				if (!MenuWindow.IsOpen)
				{
					return true;
				}
				if (((CallbackContext)(ref context)).performed)
				{
					MenuWindow.Instance.Close();
				}
				return false;
			}
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static Func<RectTransform, float> <>9__8_1;

			public static UnityAction <>9__8_2;

			public static Func<MonoBehaviour, bool> <>9__8_4;

			internal float <Create>b__8_1(RectTransform t)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return t.anchoredPosition.y;
			}

			internal void <Create>b__8_2()
			{
				MenuWindow.Instance.Open();
			}

			internal bool <Create>b__8_4(MonoBehaviour c)
			{
				if ((Object)(object)c != (Object)null)
				{
					return ((object)c).GetType().Name.Contains("Localize");
				}
				return false;
			}
		}

		private const string ButtonName = "ModMenuButton";

		private static Sprite _icon;

		internal static GameObject Button { get; private set; }

		private static void Create(TitleScreenManager manager)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: 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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Expected O, but got Unknown
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Expected O, but got Unknown
			GameObject value = Traverse.Create((object)manager).Field<GameObject>("hyperlinks").Value;
			RectTransform val = (RectTransform)(((Object)(object)value != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Discord button not found on the title screen; the Mods button wasn't added");
				return;
			}
			Transform parent = ((Transform)val).parent;
			GameObject clone = Object.Instantiate<GameObject>(((Component)val).gameObject, parent, false);
			((Object)clone).name = "ModMenuButton";
			clone.SetActive(true);
			RectTransform val2 = (RectTransform)clone.transform;
			Transform obj = parent.Find("Wiki");
			RectTransform val3 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			float num = (((Object)(object)val3 != (Object)null) ? Mathf.Abs(val3.anchoredPosition.y - val.anchoredPosition.y) : 80f);
			if (num < 1f)
			{
				num = 80f;
			}
			float num2 = (from t in (from Transform t in (IEnumerable)parent
					where (Object)(object)t != (Object)(object)clone.transform && ((Component)t).gameObject.activeSelf
					select t).OfType<RectTransform>()
				select t.anchoredPosition.y).DefaultIfEmpty(val.anchoredPosition.y).Min();
			val2.anchoredPosition = new Vector2(val.anchoredPosition.x, num2 - num);
			Button component = clone.GetComponent<Button>();
			component.onClick = new ButtonClickedEvent();
			ButtonClickedEvent onClick = component.onClick;
			object obj2 = <>c.<>9__8_2;
			if (obj2 == null)
			{
				UnityAction val4 = delegate
				{
					MenuWindow.Instance.Open();
				};
				<>c.<>9__8_2 = val4;
				obj2 = (object)val4;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj2);
			Image val5 = ((IEnumerable<Image>)clone.GetComponentsInChildren<Image>(true)).FirstOrDefault((Func<Image, bool>)((Image i) => (Object)(object)((Component)i).gameObject != (Object)(object)clone));
			Sprite val6 = LoadIcon(((Object)(object)val5 != (Object)null && (Object)(object)val5.sprite != (Object)null) ? val5.sprite.pixelsPerUnit : 100f);
			if ((Object)(object)val5 != (Object)null && (Object)(object)val6 != (Object)null)
			{
				val5.sprite = val6;
			}
			TMP_Text[] componentsInChildren = clone.GetComponentsInChildren<TMP_Text>(true);
			foreach (TMP_Text val7 in componentsInChildren)
			{
				foreach (MonoBehaviour item in from c in ((Component)val7).GetComponents<MonoBehaviour>()
					where (Object)(object)c != (Object)null && ((object)c).GetType().Name.Contains("Localize")
					select c)
				{
					Object.DestroyImmediate((Object)(object)item);
				}
				val7.text = "Mods";
			}
			Button = clone;
		}

		private static Sprite LoadIcon(float pixelsPerUnit)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_icon != (Object)null)
			{
				return _icon;
			}
			using Stream stream = typeof(TitleScreenButton).Assembly.GetManifestResourceStream("ModMenu.ModMenuIcon.png");
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1,
				name = "ModMenuIcon"
			};
			if (!ImageConversion.LoadImage(val, memoryStream.ToArray()))
			{
				return null;
			}
			Object.DontDestroyOnLoad((Object)(object)val);
			_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), pixelsPerUnit);
			((Object)_icon).name = "ModMenuIcon";
			Object.DontDestroyOnLoad((Object)(object)_icon);
			return _icon;
		}
	}
}
namespace ModMenu.Configs
{
	internal sealed class CfgDocument
	{
		private sealed class FileSetting : Setting
		{
			private readonly CfgDocument _doc;

			private readonly int _line;

			public override string Value => _doc.GetValue(_line);

			public FileSetting(CfgDocument doc, int line, string section, string key, List<string> comments)
			{
				_doc = doc;
				_line = line;
				Section = section;
				Key = key;
				DisplayName = key;
				List<string> list = new List<string>();
				bool hasRange = false;
				bool isFlags = false;
				foreach (string comment in comments)
				{
					if (comment.StartsWith("##"))
					{
						list.Add(comment.Substring(2).Trim());
					}
					else if (comment.StartsWith("# Setting type:"))
					{
						TypeName = comment.Substring("# Setting type:".Length).Trim();
					}
					else if (comment.StartsWith("# Default value:"))
					{
						DefaultValue = comment.Substring("# Default value:".Length).Trim();
					}
					else if (comment.StartsWith("# Acceptable values:"))
					{
						Choices = (from s in comment.Substring("# Acceptable values:".Length).Split(',')
							select s.Trim() into s
							where s.Length > 0
							select s).ToArray();
					}
					else if (comment.StartsWith("# Multiple values can be set"))
					{
						isFlags = true;
					}
					else if (comment.StartsWith("# Acceptable value range: From "))
					{
						hasRange = TryParseRange(comment.Substring("# Acceptable value range: From ".Length));
					}
				}
				Description = ((list.Count > 0) ? string.Join("\n", list) : null);
				Classify(hasRange, isFlags);
			}

			public override string TrySet(string serialized)
			{
				string text = Validate(serialized);
				if (text != null)
				{
					return text;
				}
				string text2 = serialized.Trim();
				if (Kind == SettingKind.Slider)
				{
					text2 = Setting.FormatNumber(Math.Max(Min, Math.Min(Max, double.Parse(text2, CultureInfo.InvariantCulture))), IsInteger);
				}
				_doc.SetValue(_line, Key, text2);
				return null;
			}

			private bool TryParseRange(string text)
			{
				string[] array = text.Split(new string[1] { " to " }, StringSplitOptions.None);
				if (array.Length == 2 && TryParse(array[0], out Min))
				{
					return TryParse(array[1], out Max);
				}
				return false;
			}

			private static bool TryParse(string text, out double value)
			{
				if (!double.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value))
				{
					return double.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.CurrentCulture, out value);
				}
				return true;
			}
		}

		private const string TypePrefix = "# Setting type:";

		private const string DefaultPrefix = "# Default value:";

		private const string RangePrefix = "# Acceptable value range: From ";

		private const string ValuesPrefix = "# Acceptable values:";

		private const string FlagsPrefix = "# Multiple values can be set";

		private readonly string _path;

		private readonly ModConfig _owner;

		private List<string> _lines = new List<string>();

		private string _newLine = Environment.NewLine;

		private bool _trailingNewLine = true;

		public readonly List<Setting> Settings = new List<Setting>();

		public string Header;

		public CfgDocument(string path, ModConfig owner)
		{
			_path = path;
			_owner = owner;
			Load();
		}

		public void Load()
		{
			string text = File.ReadAllText(_path);
			_newLine = (text.Contains("\r\n") ? "\r\n" : "\n");
			_trailingNewLine = text.EndsWith("\n");
			_lines = (from l in text.Split('\n')
				select l.TrimEnd('\r')).ToList();
			if (_trailingNewLine)
			{
				_lines.RemoveAt(_lines.Count - 1);
			}
			Parse();
		}

		public void Save()
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < _lines.Count; i++)
			{
				stringBuilder.Append(_lines[i]);
				if (i < _lines.Count - 1 || _trailingNewLine)
				{
					stringBuilder.Append(_newLine);
				}
			}
			File.WriteAllText(_path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private void Parse()
		{
			Settings.Clear();
			Header = null;
			string section = "";
			List<string> list = new List<string>();
			for (int i = 0; i < _lines.Count; i++)
			{
				string text = _lines[i].Trim();
				if (text.StartsWith("## Settings file was created by plugin"))
				{
					if (Header == null)
					{
						Header = text.Substring("## Settings file was created by plugin".Length).Trim();
					}
					continue;
				}
				if (text.StartsWith("#"))
				{
					list.Add(text);
					continue;
				}
				if (text.StartsWith("[") && text.EndsWith("]"))
				{
					section = text.Substring(1, text.Length - 2).Trim();
					list.Clear();
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0)
				{
					Settings.Add(new FileSetting(this, i, section, text.Substring(0, num).Trim(), list));
					list.Clear();
				}
			}
		}

		internal string GetValue(int line)
		{
			string text = _lines[line];
			int num = text.IndexOf('=');
			if (num >= 0)
			{
				return text.Substring(num + 1).Trim();
			}
			return "";
		}

		internal void SetValue(int line, string key, string value)
		{
			_lines[line] = key + " = " + value;
			_owner.MarkDirty();
		}
	}
	internal static class ConfigCatalog
	{
		public static List<ModConfig> Build()
		{
			Dictionary<string, ConfigFile> dictionary = new Dictionary<string, ConfigFile>(StringComparer.OrdinalIgnoreCase);
			foreach (ConfigFile item in ConfigFileTracker.All())
			{
				string key = Normalize(item.ConfigFilePath);
				if (!dictionary.TryGetValue(key, out var value) || item.Count > value.Count)
				{
					dictionary[key] = item;
				}
			}
			IEnumerable<string> second = (Directory.Exists(Paths.ConfigPath) ? Directory.GetFiles(Paths.ConfigPath, "*.cfg", SearchOption.AllDirectories).Select(Normalize) : Enumerable.Empty<string>());
			List<PluginInfo> source = Chainloader.PluginInfos.Values.Where((PluginInfo p) => p.Instance != null).ToList();
			ConfigFile coreConfig = ConfigFileTracker.CoreConfig;
			List<ModConfig> list = new List<ModConfig>();
			foreach (string item2 in dictionary.Keys.Concat(second).Distinct<string>(StringComparer.OrdinalIgnoreCase))
			{
				try
				{
					ModConfig modConfig;
					if (dictionary.TryGetValue(item2, out var file))
					{
						modConfig = new ModConfig(file);
						modConfig.IsCore = file == coreConfig;
						object obj = ConfigFileTracker.GetOwner(file);
						if (obj == null)
						{
							PluginInfo? obj2 = ((IEnumerable<PluginInfo>)source).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo p) => p.Instance.Config == file));
							obj = ((obj2 != null) ? obj2.Metadata : null);
						}
						BepInPlugin val = (BepInPlugin)obj;
						if (val != null)
						{
							modConfig.Name = val.Name;
							modConfig.Version = val.Version?.ToString();
						}
						else if (modConfig.IsCore)
						{
							modConfig.Name = "BepInEx";
							modConfig.Version = typeof(Paths).Assembly.GetName().Version?.ToString();
						}
					}
					else
					{
						modConfig = new ModConfig(item2);
					}
					if (string.IsNullOrEmpty(modConfig.Name))
					{
						string title = ModConfig.FileTitle(item2);
						PluginInfo val2 = ((IEnumerable<PluginInfo>)source).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo p) => string.Equals(p.Metadata.GUID, title, StringComparison.OrdinalIgnoreCase)));
						modConfig.Name = ((val2 != null) ? val2.Metadata.Name : null) ?? title;
						ModConfig modConfig2 = modConfig;
						if (modConfig2.Version == null)
						{
							modConfig2.Version = ((val2 == null) ? null : val2.Metadata.Version?.ToString());
						}
					}
					modConfig.RelativePath = Relative(item2);
					if (modConfig.Settings.Count > 0)
					{
						list.Add(modConfig);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Skipping " + item2 + ": " + ex.Message));
				}
			}
			return list.OrderBy((ModConfig m) => m.IsCore ? 1 : ((!m.IsLive) ? 2 : 0)).ThenBy<ModConfig, string>((ModConfig m) => m.Name, StringComparer.OrdinalIgnoreCase).ThenBy<ModConfig, string>((ModConfig m) => m.RelativePath, StringComparer.OrdinalIgnoreCase)
				.ToList();
		}

		private static string Normalize(string path)
		{
			return Path.GetFullPath(path);
		}

		private static string Relative(string path)
		{
			string text = Normalize(Paths.BepInExRootPath).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			if (!path.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
			{
				return path;
			}
			return "BepInEx/" + path.Substring(text.Length + 1).Replace('\\', '/');
		}
	}
	internal static class ConfigFileTracker
	{
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static class ConfigFile_Ctor
		{
			private static void Postfix(ConfigFile __instance)
			{
				Track(__instance);
			}
		}

		private static readonly List<WeakReference> Created = new List<WeakReference>();

		private static readonly FieldInfo OwnerField = AccessTools.Field(typeof(ConfigFile), "_ownerMetadata");

		private static readonly PropertyInfo CoreConfigProperty = AccessTools.Property(typeof(ConfigFile), "CoreConfig");

		internal static ConfigFile CoreConfig
		{
			get
			{
				object? obj = CoreConfigProperty?.GetValue(null);
				return (ConfigFile)((obj is ConfigFile) ? obj : null);
			}
		}

		internal static void Track(ConfigFile file)
		{
			lock (Created)
			{
				Created.Add(new WeakReference(file));
			}
		}

		internal static BepInPlugin GetOwner(ConfigFile file)
		{
			object? obj = OwnerField?.GetValue(file);
			return (BepInPlugin)((obj is BepInPlugin) ? obj : null);
		}

		internal static IEnumerable<ConfigFile> All()
		{
			HashSet<ConfigFile> hashSet = new HashSet<ConfigFile>();
			lock (Created)
			{
				Created.RemoveAll((WeakReference r) => !r.IsAlive);
				foreach (WeakReference item in Created)
				{
					object? target = item.Target;
					ConfigFile val = (ConfigFile)((target is ConfigFile) ? target : null);
					if (val != null)
					{
						hashSet.Add(val);
					}
				}
			}
			try
			{
				if (CoreConfig != null)
				{
					hashSet.Add(CoreConfig);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Couldn't read BepInEx core config: " + ex.Message));
			}
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				BaseUnityPlugin instance = value.Instance;
				if (instance != null)
				{
					if (instance.Config != null)
					{
						hashSet.Add(instance.Config);
					}
					ScanForConfigFiles(instance, hashSet);
				}
			}
			return hashSet;
		}

		private static void ScanForConfigFiles(BaseUnityPlugin instance, HashSet<ConfigFile> found)
		{
			try
			{
				foreach (FieldInfo item in ((object)instance).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(IsConfigFileField))
				{
					object? value = item.GetValue(instance);
					ConfigFile val = (ConfigFile)((value is ConfigFile) ? value : null);
					if (val != null)
					{
						found.Add(val);
					}
				}
				Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)instance).GetType().Assembly);
				foreach (Type type in typesFromAssembly)
				{
					if (type.IsGenericTypeDefinition)
					{
						continue;
					}
					foreach (FieldInfo item2 in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Where(IsConfigFileField))
					{
						object? value2 = item2.GetValue(null);
						ConfigFile val2 = (ConfigFile)((value2 is ConfigFile) ? value2 : null);
						if (val2 != null)
						{
							found.Add(val2);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Couldn't scan " + ((object)instance).GetType().Assembly.GetName().Name + " for config files: " + ex.Message));
			}
		}

		private static bool IsConfigFileField(FieldInfo field)
		{
			if (typeof(ConfigFile).IsAssignableFrom(field.FieldType))
			{
				return !field.IsLiteral;
			}
			return false;
		}
	}
	internal sealed class LiveSetting : Setting
	{
		private readonly ConfigEntryBase _entry;

		private readonly ModConfig _owner;

		public override string Value => Serialize(_entry.BoxedValue);

		public LiveSetting(ConfigEntryBase entry, ModConfig owner)
		{
			_entry = entry;
			_owner = owner;
			Type settingType = entry.SettingType;
			Section = entry.Definition.Section;
			Key = entry.Definition.Key;
			DisplayName = Key;
			ConfigDescription description = entry.Description;
			Description = ((description != null) ? description.Description : null);
			TypeName = settingType.Name;
			DefaultValue = Serialize(entry.DefaultValue);
			ConfigDescription description2 = entry.Description;
			AcceptableValueBase val = ((description2 != null) ? description2.AcceptableValues : null);
			bool hasRange = false;
			bool isFlags = false;
			if (val != null && IsGeneric(((object)val).GetType(), typeof(AcceptableValueList<>)))
			{
				Choices = (((object)val).GetType().GetProperty("AcceptableValues")?.GetValue(val) as IEnumerable)?.Cast<object>().Select(Serialize).ToArray();
			}
			else if (val != null && IsGeneric(((object)val).GetType(), typeof(AcceptableValueRange<>)))
			{
				try
				{
					Min = Convert.ToDouble(((object)val).GetType().GetProperty("MinValue")?.GetValue(val));
					Max = Convert.ToDouble(((object)val).GetType().GetProperty("MaxValue")?.GetValue(val));
					hasRange = true;
				}
				catch (Exception)
				{
				}
			}
			else if (settingType.IsEnum)
			{
				Choices = Enum.GetNames(settingType);
				isFlags = settingType.IsDefined(typeof(FlagsAttribute), inherit: false);
			}
			Classify(hasRange, isFlags);
			ConfigDescription description3 = entry.Description;
			ApplyConfigurationManagerTags((description3 != null) ? description3.Tags : null);
		}

		public override string TrySet(string serialized)
		{
			if (ReadOnly)
			{
				return "This setting is read-only";
			}
			object boxedValue;
			try
			{
				boxedValue = TomlTypeConverter.ConvertToValue(serialized.Trim(), _entry.SettingType);
			}
			catch (Exception)
			{
				return Validate(serialized) ?? ("Not a valid " + TypeName);
			}
			ConfigFile configFile = _entry.ConfigFile;
			bool saveOnConfigSet = configFile.SaveOnConfigSet;
			configFile.SaveOnConfigSet = false;
			try
			{
				_entry.BoxedValue = boxedValue;
			}
			catch (Exception ex2)
			{
				return ex2.Message;
			}
			finally
			{
				configFile.SaveOnConfigSet = saveOnConfigSet;
			}
			_owner.MarkDirty();
			return null;
		}

		private string Serialize(object value)
		{
			try
			{
				return TomlTypeConverter.ConvertToString(value, _entry.SettingType);
			}
			catch (Exception)
			{
				return value?.ToString() ?? "";
			}
		}

		private static bool IsGeneric(Type type, Type definition)
		{
			while (type != null)
			{
				if (type.IsGenericType && type.GetGenericTypeDefinition() == definition)
				{
					return true;
				}
				type = type.BaseType;
			}
			return false;
		}

		private void ApplyConfigurationManagerTags(object[] tags)
		{
			if (tags == null)
			{
				return;
			}
			foreach (object item in tags.Where((object t) => t != null && t.GetType().Name == "ConfigurationManagerAttributes"))
			{
				object obj = Read(item, "Browsable");
				if (obj is bool && !(bool)obj)
				{
					Hidden = true;
				}
				obj = Read(item, "ReadOnly");
				if (obj is bool && (bool)obj)
				{
					ReadOnly = true;
				}
				if (Read(item, "Order") is int order)
				{
					Order = order;
				}
				if (Read(item, "DispName") is string { Length: >0 } text)
				{
					DisplayName = text;
				}
				if (Read(item, "Description") is string { Length: >0 } text2)
				{
					Description = text2;
				}
				if (Read(item, "Category") is string { Length: >0 } text3)
				{
					Section = text3;
				}
			}
		}

		private static object Read(object tag, string member)
		{
			Type type = tag.GetType();
			object obj = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(tag);
			if (obj == null)
			{
				PropertyInfo? property = type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if ((object)property == null)
				{
					return null;
				}
				obj = property.GetValue(tag);
			}
			return obj;
		}
	}
	internal sealed class ModConfig
	{
		private const float SaveDelay = 0.4f;

		public string Name;

		public string Version;

		public string FilePath;

		public string RelativePath;

		public bool IsCore;

		public List<Setting> Settings = new List<Setting>();

		private readonly ConfigFile _file;

		private readonly CfgDocument _doc;

		private float _dirtySince = -1f;

		public bool IsLive => _file != null;

		public bool IsDirty => _dirtySince >= 0f;

		public ModConfig(ConfigFile file)
		{
			_file = file;
			FilePath = file.ConfigFilePath;
			BuildLiveSettings();
		}

		public ModConfig(string path)
		{
			FilePath = path;
			_doc = new CfgDocument(path, this);
			Settings = Sort(_doc.Settings);
			ParseHeader(_doc.Header);
		}

		public void MarkDirty()
		{
			_dirtySince = Time.unscaledTime;
		}

		public void SaveIfIdle()
		{
			if (IsDirty && Time.unscaledTime - _dirtySince >= 0.4f)
			{
				Save();
			}
		}

		public void Save()
		{
			if (!IsDirty)
			{
				return;
			}
			_dirtySince = -1f;
			try
			{
				if (_file != null)
				{
					_file.Save();
				}
				else
				{
					_doc.Save();
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Couldn't save " + FilePath + ": " + ex.Message));
			}
		}

		public string Reload()
		{
			try
			{
				Save();
				if (_file != null)
				{
					bool saveOnConfigSet = _file.SaveOnConfigSet;
					_file.SaveOnConfigSet = false;
					try
					{
						_file.Reload();
					}
					finally
					{
						_file.SaveOnConfigSet = saveOnConfigSet;
					}
				}
				else
				{
					_doc.Load();
					Settings = Sort(_doc.Settings);
				}
				return null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Couldn't reload " + FilePath + ": " + ex.Message));
				return ex.Message;
			}
		}

		private void BuildLiveSettings()
		{
			Settings = Sort(((IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)_file).Select((Func<KeyValuePair<ConfigDefinition, ConfigEntryBase>, Setting>)((KeyValuePair<ConfigDefinition, ConfigEntryBase> pair) => new LiveSetting(pair.Value, this))));
		}

		private static List<Setting> Sort(IEnumerable<Setting> settings)
		{
			return (from x in settings.Where((Setting s) => !s.Hidden).Select((Setting s, int i) => (s: s, i: i)).OrderBy<(Setting, int), string>(((Setting s, int i) x) => x.s.Section, StringComparer.OrdinalIgnoreCase)
					.ThenByDescending<(Setting, int), int>(((Setting s, int i) x) => x.s.Order)
					.ThenBy<(Setting, int), int>(((Setting s, int i) x) => x.i)
				select x.s).ToList();
		}

		private void ParseHeader(string header)
		{
			if (!string.IsNullOrEmpty(header))
			{
				int num = header.LastIndexOf(" v", StringComparison.Ordinal);
				if (num > 0)
				{
					Name = header.Substring(0, num).Trim();
					Version = header.Substring(num + 2).Trim();
				}
				else
				{
					Name = header.Trim();
				}
			}
		}

		public static string FileTitle(string path)
		{
			return Path.GetFileNameWithoutExtension(path);
		}
	}
	internal enum SettingKind
	{
		Toggle,
		Choice,
		Flags,
		Slider,
		Text
	}
	internal abstract class Setting
	{
		public string Section;

		public string Key;

		public string DisplayName;

		public string Description;

		public string TypeName;

		public string DefaultValue;

		public SettingKind Kind = SettingKind.Text;

		public string[] Choices;

		public double Min;

		public double Max;

		public bool IsInteger;

		public bool IsNumber;

		public bool ReadOnly;

		public bool Hidden;

		public int Order;

		public abstract string Value { get; }

		public bool IsDefault
		{
			get
			{
				if (DefaultValue != null)
				{
					return SameValue(Value, DefaultValue);
				}
				return true;
			}
		}

		public bool IsOn => string.Equals(Value?.Trim(), "true", StringComparison.OrdinalIgnoreCase);

		public double NumberValue
		{
			get
			{
				if (!double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					return Min;
				}
				return result;
			}
		}

		public string[] SelectedFlags => (from s in (Value ?? "").Split(',')
			select s.Trim() into s
			where s.Length > 0
			select s).ToArray();

		public abstract string TrySet(string serialized);

		public string Reset()
		{
			if (DefaultValue != null)
			{
				return TrySet(DefaultValue);
			}
			return null;
		}

		protected void Classify(bool hasRange, bool isFlags)
		{
			IsInteger = IsIntegerType(TypeName);
			IsNumber = IsInteger || IsFloatType(TypeName);
			if (TypeName == "Boolean")
			{
				Kind = SettingKind.Toggle;
				return;
			}
			string[] choices = Choices;
			if (choices != null && choices.Length > 0)
			{
				Kind = ((!isFlags) ? SettingKind.Choice : SettingKind.Flags);
			}
			else if (hasRange && IsNumber && Max > Min)
			{
				Kind = SettingKind.Slider;
			}
			else
			{
				Kind = SettingKind.Text;
			}
		}

		protected string Validate(string serialized)
		{
			string text = serialized.Trim();
			if (TypeName == "Boolean" && !bool.TryParse(text, out var _))
			{
				return "Must be true or false";
			}
			if (IsInteger && !long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _))
			{
				return "Must be a whole number";
			}
			if (IsNumber && !IsInteger && !double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var _))
			{
				return "Must be a number";
			}
			if (Kind == SettingKind.Choice && !ClassExtensions.Contains<string>((Array)Choices, text, (IEqualityComparer<string>)StringComparer.OrdinalIgnoreCase))
			{
				return "Must be one of: " + string.Join(", ", Choices);
			}
			return null;
		}

		internal static bool IsIntegerType(string typeName)
		{
			switch (typeName)
			{
			case "Int64":
			case "SByte":
			case "Int16":
			case "Int32":
			case "UInt16":
			case "UInt32":
			case "UInt64":
			case "Byte":
				return true;
			default:
				return false;
			}
		}

		internal static bool IsFloatType(string typeName)
		{
			switch (typeName)
			{
			case "Single":
			case "Double":
			case "Decimal":
				return true;
			default:
				return false;
			}
		}

		internal static string FormatNumber(double value, bool integer)
		{
			if (!integer)
			{
				return value.ToString("0.###", CultureInfo.InvariantCulture);
			}
			return Math.Round(value).ToString("0", CultureInfo.InvariantCulture);
		}

		private bool SameValue(string a, string b)
		{
			a = a?.Trim() ?? "";
			b = b?.Trim() ?? "";
			if (IsNumber && double.TryParse(a, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && double.TryParse(b, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
			{
				return Math.Abs(result - result2) < 1E-09;
			}
			if (Kind == SettingKind.Flags)
			{
				return FlagSet(a).SetEquals(FlagSet(b));
			}
			return string.Equals(a, b, (Kind == SettingKind.Text) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
		}

		private static HashSet<string> FlagSet(string flags)
		{
			return new HashSet<string>(from s in flags.Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s, StringComparer.OrdinalIgnoreCase);
		}
	}
}