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);
}
}
}