using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Panik;
using TMPro;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ModSettingsExtender")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+417e853f06e65c92646aab7d0a27ccf43c0f04e0")]
[assembly: AssemblyProduct("ModSettingsExtender")]
[assembly: AssemblyTitle("ModSettingsExtender")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 ModSettingsExtender
{
[BepInPlugin("com.pharmacomaniac.modsettingsextender", "Mod Settings Extender", "0.1.1")]
public class ModSettingsExtenderPlugin : BaseUnityPlugin
{
internal static ManualLogSource? Log;
internal static Harmony? Harmony;
private void Awake()
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
Harmony = new Harmony("com.pharmacomaniac.modsettingsextender");
Harmony.UnpatchSelf();
Harmony.PatchAll(typeof(MainMenu_Patches));
Log.LogInfo((object)"Mod Settings Extender loaded (v0.1.0)");
}
private void OnDestroy()
{
Harmony? harmony = Harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
}
internal static class HubState
{
public enum View
{
None,
HubIndex,
Page
}
public static View Current = View.None;
public static int HubPageOffset = 0;
public static int PageItemOffset = 0;
public static int ActivePage = -1;
public static void Reset()
{
Current = View.None;
HubPageOffset = 0;
PageItemOffset = 0;
ActivePage = -1;
}
}
internal static class MainMenu_Patches
{
private readonly struct HubLayout
{
public int Capacity { get; }
public int ListSlots { get; }
public int NextRow { get; }
public int BackRow { get; }
public int Step { get; }
public HubLayout(int capacity)
{
Capacity = capacity;
BackRow = capacity - 1;
NextRow = ((capacity >= 2) ? (capacity - 2) : (-1));
ListSlots = Mathf.Max(0, capacity - 2);
Step = Math.Max(1, ListSlots);
}
}
private const int ModsSlotWithoutTwitch = 4;
private const int ModsSlotWithTwitch = 5;
private static bool _rowValidationLogged;
private static readonly FieldInfo DesiredNavigationIndexField = AccessTools.Field(typeof(MainMenuScript), "desiredNavigationIndex");
private static readonly FieldInfo ControllerElementsField = AccessTools.Field(typeof(DiegeticMenuController), "elements");
private static readonly FieldInfo RightNavigationPressField = AccessTools.Field(typeof(MainMenuScript), "rightNavigationPress");
private static readonly FieldInfo LeftNavigationPressField = AccessTools.Field(typeof(MainMenuScript), "leftNavigationPress");
private static readonly FieldInfo MenuIndexField = AccessTools.Field(typeof(MainMenuScript), "menuIndex");
private static bool IsInHub()
{
return HubState.Current != HubState.View.None;
}
private static bool HasPages()
{
return ModSettingsRegistry.Pages.Count > 0;
}
private static int GetModsSlotIndex()
{
if (!TwitchMaster.IsTwitchSupported())
{
return 4;
}
return 5;
}
private static int GetBackSlotIndex()
{
return GetModsSlotIndex() + 1;
}
private static void SetDesiredNavigationIndex(MainMenuScript menu, int value)
{
DesiredNavigationIndexField?.SetValue(menu, value);
}
private static MenuIndex GetMenuIndex(MainMenuScript menu)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
if (MenuIndexField == null)
{
return (MenuIndex)(-1);
}
object value = MenuIndexField.GetValue(menu);
if (value is MenuIndex)
{
return (MenuIndex)value;
}
return (MenuIndex)(-1);
}
private static void SetMenuIndex(MainMenuScript menu, MenuIndex value)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
MenuIndexField?.SetValue(menu, value);
}
private static List<DiegeticMenuElement>? GetElementsList(DiegeticMenuController? controller)
{
if ((Object)(object)controller == (Object)null || ControllerElementsField == null)
{
return null;
}
return ControllerElementsField.GetValue(controller) as List<DiegeticMenuElement>;
}
private static bool GetFlag(FieldInfo field, MainMenuScript menu)
{
bool flag = default(bool);
int num;
if (field != null && (Object)(object)menu != (Object)null)
{
object value = field.GetValue(menu);
if (value is bool)
{
flag = (bool)value;
num = 1;
}
else
{
num = 0;
}
}
else
{
num = 0;
}
return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
}
private static void SetTextSafe(TextMeshProUGUI[] texts, int index, string? value)
{
if (texts != null && index >= 0 && index < texts.Length && (Object)(object)texts[index] != (Object)null)
{
((TMP_Text)texts[index]).text = value ?? string.Empty;
}
}
private static DiegeticMenuElement? GetMenuElement(MainMenuScript? menu, int index)
{
if (menu?.menuElements == null || index < 0 || index >= menu.menuElements.Length)
{
return null;
}
return menu.menuElements[index];
}
private static bool TryGetOptionTexts(MainMenuScript menu, out TextMeshProUGUI[] optionTexts)
{
if (menu?.optionTexts == null || menu.optionTexts.Length == 0)
{
optionTexts = Array.Empty<TextMeshProUGUI>();
return false;
}
optionTexts = menu.optionTexts;
return true;
}
private static bool TryGetHubLayout(MainMenuScript menu, out HubLayout layout)
{
layout = default(HubLayout);
if ((Object)(object)menu == (Object)null)
{
return false;
}
int num = VisibleCapacity(menu);
if (num <= 0)
{
return false;
}
layout = new HubLayout(num);
return true;
}
private static bool TryGetActivePage(out ModSettingsRegistry.Page page)
{
int activePage = HubState.ActivePage;
if (activePage >= 0 && activePage < ModSettingsRegistry.Pages.Count)
{
page = ModSettingsRegistry.Pages[activePage];
return true;
}
page = null;
return false;
}
private static void RenderListEntries(TextMeshProUGUI[] optionTexts, int listSlots, int start, int total, Func<int, string> labelSelector)
{
int i = 0;
int num = start;
while (num < total && i < listSlots)
{
SetTextSafe(optionTexts, i, labelSelector(num));
num++;
i++;
}
for (; i < listSlots; i++)
{
SetTextSafe(optionTexts, i, string.Empty);
}
}
private static void RenderPaginationFooter(TextMeshProUGUI[] optionTexts, HubLayout layout, int renderedEnd, int total)
{
if (layout.NextRow >= 0)
{
SetTextSafe(optionTexts, layout.NextRow, (renderedEnd < total) ? "NEXT" : string.Empty);
}
SetTextSafe(optionTexts, layout.BackRow, "BACK");
}
private static int ClampPageOffset(int requested, int total)
{
if (total <= 0)
{
return 0;
}
int num = Math.Max(0, total - 1);
return Mathf.Clamp(requested, 0, num);
}
private static int GetDesiredNavigationIndex(MainMenuScript menu)
{
if (DesiredNavigationIndexField == null)
{
return -1;
}
object value = DesiredNavigationIndexField.GetValue(menu);
if (value is int)
{
return (int)value;
}
return -1;
}
private static void EnsureSlotState(MainMenuScript menu, int index, bool enabled)
{
if ((Object)(object)menu?.menuController == (Object)null)
{
return;
}
DiegeticMenuElement menuElement = GetMenuElement(menu, index);
if ((Object)(object)menuElement == (Object)null)
{
return;
}
Transform transform = ((Component)menuElement).transform;
object obj;
if (transform == null)
{
obj = null;
}
else
{
Transform parent = transform.parent;
obj = ((parent != null) ? ((Component)parent).gameObject : null);
}
GameObject val = (GameObject)obj;
if (val != null)
{
val.SetActive(enabled);
}
List<DiegeticMenuElement> elementsList = GetElementsList(menu.menuController);
if (elementsList == null)
{
return;
}
if (enabled)
{
if (!elementsList.Contains(menuElement))
{
int index2 = Mathf.Clamp(index, 0, elementsList.Count);
elementsList.Insert(index2, menuElement);
menuElement.SetMyController(menu.menuController);
}
if (!VirtualCursors.IsCursorVisible(0, true) && GetDesiredNavigationIndex(menu) == index)
{
menu.menuController.HoveredElement = menuElement;
}
}
else
{
elementsList.Remove(menuElement);
if (menu.menuController.HoveredElement == menuElement)
{
menu.menuController.HoveredElement = null;
}
}
}
private static void EnsureSlotEnabled(MainMenuScript menu, int index)
{
EnsureSlotState(menu, index, enabled: true);
}
private static void EnsureSlotDisabled(MainMenuScript menu, int index)
{
EnsureSlotState(menu, index, enabled: false);
}
private static bool ValidateMenuRows(MainMenuScript menu, int requiredIndex)
{
if ((Object)(object)menu == (Object)null)
{
return false;
}
DiegeticMenuElement[] menuElements = menu.menuElements;
int num = ((menuElements != null) ? menuElements.Length : 0);
TextMeshProUGUI[] optionTexts = menu.optionTexts;
int num2 = ((optionTexts != null) ? optionTexts.Length : 0);
bool flag = num > requiredIndex;
bool flag2 = num2 > requiredIndex;
if ((!flag || !flag2) && !_rowValidationLogged)
{
_rowValidationLogged = true;
ManualLogSource? log = ModSettingsExtenderPlugin.Log;
if (log != null)
{
log.LogFatal((object)$"ModSettingsExtender detected missing menu rows. Expected index {requiredIndex} but found menuElements={num}, optionTexts={num2}. The base menu prefab likely changed; mods menu injection is disabled.");
}
}
return flag && flag2;
}
private static int VisibleCapacity(MainMenuScript menu)
{
return Mathf.Max(0, GetElementsList(menu?.menuController)?.Count ?? 0);
}
private static bool InputSuppressed(MainMenuScript menu)
{
bool flag = Controls.MouseButton_PressedGet(0, (MouseElement)1);
bool flag2 = GetFlag(RightNavigationPressField, menu);
bool flag3 = GetFlag(LeftNavigationPressField, menu);
return flag || flag2 || flag3;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(MainMenuScript), "OptionsUpdateText_Desktop")]
private static void OptionsUpdateText_Desktop_Postfix(MainMenuScript __instance)
{
AfterOptionsUpdateText(__instance);
}
private static void AfterOptionsUpdateText(MainMenuScript menu)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Invalid comparison between Unknown and I4
if ((Object)(object)menu == (Object)null)
{
return;
}
try
{
if ((int)GetMenuIndex(menu) == 1)
{
EnsureModsRow(menu);
}
else
{
DisableExtraRow(menu);
}
if (IsInHub())
{
RenderHub(menu);
}
else if (HubState.Current != 0 && !HasPages())
{
HubState.Reset();
}
}
catch (Exception arg)
{
ManualLogSource? log = ModSettingsExtenderPlugin.Log;
if (log != null)
{
log.LogError((object)$"OptionsUpdate postfix failed: {arg}");
}
}
}
private static void DisableExtraRow(MainMenuScript menu)
{
if (!((Object)(object)menu == (Object)null))
{
int backSlotIndex = GetBackSlotIndex();
if (ValidateMenuRows(menu, backSlotIndex))
{
SetTextSafe(menu.optionTexts, backSlotIndex, string.Empty);
EnsureSlotDisabled(menu, backSlotIndex);
}
}
}
private static void EnsureModsRow(MainMenuScript menu)
{
TextMeshProUGUI[] optionTexts = menu.optionTexts;
if (optionTexts == null)
{
return;
}
int modsSlotIndex = GetModsSlotIndex();
int backSlotIndex = GetBackSlotIndex();
bool flag = HasPages();
if (!ValidateMenuRows(menu, Math.Max(modsSlotIndex, backSlotIndex)))
{
return;
}
if (!flag)
{
SetTextSafe(optionTexts, modsSlotIndex, Translation.Get("MENU_OPTION_BACK"));
SetTextSafe(optionTexts, backSlotIndex, string.Empty);
EnsureSlotDisabled(menu, backSlotIndex);
}
else if (modsSlotIndex < optionTexts.Length && backSlotIndex < optionTexts.Length)
{
TextMeshProUGUI obj = optionTexts[modsSlotIndex];
string value = ((obj != null) ? ((TMP_Text)obj).text : null) ?? Translation.Get("MENU_OPTION_BACK");
if (string.IsNullOrEmpty(value))
{
value = Translation.Get("MENU_OPTION_BACK");
}
EnsureSlotEnabled(menu, modsSlotIndex);
EnsureSlotEnabled(menu, backSlotIndex);
SetTextSafe(optionTexts, backSlotIndex, value);
SetTextSafe(optionTexts, modsSlotIndex, "MODS");
}
}
private static void RenderHub(MainMenuScript menu)
{
if (TryGetOptionTexts(menu, out TextMeshProUGUI[] optionTexts) && TryGetHubLayout(menu, out var layout))
{
ModSettingsRegistry.Page page;
if (HubState.Current == HubState.View.HubIndex)
{
RenderHubIndex(menu, optionTexts, layout);
}
else if (HubState.Current == HubState.View.Page && TryGetActivePage(out page))
{
RenderHubPage(menu, optionTexts, layout, page);
}
}
}
private static void RenderHubIndex(MainMenuScript menu, TextMeshProUGUI[] optionTexts, HubLayout layout)
{
if ((Object)(object)menu.titleText != (Object)null)
{
((TMP_Text)menu.titleText).text = "MOD SETTINGS";
}
int hubPageOffset = HubState.HubPageOffset;
int count = ModSettingsRegistry.Pages.Count;
int renderedEnd = Mathf.Min(hubPageOffset + layout.ListSlots, count);
RenderListEntries(optionTexts, layout.ListSlots, hubPageOffset, count, (int index) => ModSettingsRegistry.GetDisplayTitle(ModSettingsRegistry.Pages[index]));
RenderPaginationFooter(optionTexts, layout, renderedEnd, count);
}
private static void RenderHubPage(MainMenuScript menu, TextMeshProUGUI[] optionTexts, HubLayout layout, ModSettingsRegistry.Page page)
{
ModSettingsRegistry.Page page2 = page;
if ((Object)(object)menu.titleText != (Object)null)
{
((TMP_Text)menu.titleText).text = ModSettingsRegistry.GetDisplayTitle(page2);
}
int pageItemOffset = HubState.PageItemOffset;
int count = page2.Items.Count;
int renderedEnd = Mathf.Min(pageItemOffset + layout.ListSlots, count);
RenderListEntries(optionTexts, layout.ListSlots, pageItemOffset, count, (int index) => page2.Items[index].Label?.Invoke() ?? "(item)");
RenderPaginationFooter(optionTexts, layout, renderedEnd, count);
}
[HarmonyPrefix]
[HarmonyPatch(typeof(MainMenuScript), "Select_Desktop")]
private static bool Select_Desktop_Prefix(MainMenuScript __instance, MenuIndex _menuIndex, int selectionIndex)
{
//IL_0007: 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_007d: Invalid comparison between Unknown and I4
if (IsInHub() && (int)_menuIndex == 0 && selectionIndex == 0)
{
if (HubState.Current == HubState.View.Page)
{
Sound.Play("SoundMenuBack", 1f, 1f);
HubState.Current = HubState.View.HubIndex;
HubState.PageItemOffset = 0;
}
else
{
Sound.Play("SoundMenuBack", 1f, 1f);
HubState.Reset();
SetDesiredNavigationIndex(__instance, GetModsSlotIndex());
}
__instance.OptionsUpdate();
return false;
}
if (IsInHub())
{
HandleHubSelection(__instance, selectionIndex);
__instance.OptionsUpdate();
return false;
}
if ((int)_menuIndex == 1 && HasPages())
{
int modsSlotIndex = GetModsSlotIndex();
int backSlotIndex = GetBackSlotIndex();
bool flag = InputSuppressed(__instance);
if (selectionIndex == modsSlotIndex && !flag)
{
Sound.Play("SoundMenuSelect", 1f, 1f);
HubState.Current = HubState.View.HubIndex;
HubState.HubPageOffset = 0;
HubState.ActivePage = -1;
HubState.PageItemOffset = 0;
SetDesiredNavigationIndex(__instance, 0);
__instance.OptionsUpdate();
return false;
}
if (selectionIndex == backSlotIndex && !flag)
{
Sound.Play("SoundMenuBack", 1f, 1f);
SetMenuIndex(__instance, (MenuIndex)0);
int value = ((!Master.IsDemo) ? 2 : 3);
SetDesiredNavigationIndex(__instance, value);
HubState.Reset();
__instance.OptionsUpdate();
return false;
}
}
return true;
}
private static void HandleHubSelection(MainMenuScript menu, int selectionIndex)
{
if (TryGetHubLayout(menu, out var layout))
{
if (HubState.Current == HubState.View.HubIndex)
{
HandleHubIndexSelection(menu, selectionIndex, layout);
}
else if (HubState.Current == HubState.View.Page)
{
int navigationDirection = GetNavigationDirection(menu);
HandleHubPageSelection(menu, selectionIndex, layout, navigationDirection);
}
}
}
private static void HandleHubIndexSelection(MainMenuScript menu, int selectionIndex, HubLayout layout)
{
int hubPageOffset = HubState.HubPageOffset;
int count = ModSettingsRegistry.Pages.Count;
int num = Mathf.Min(hubPageOffset + layout.ListSlots, count);
if (selectionIndex >= 0 && selectionIndex < layout.ListSlots)
{
int num2 = hubPageOffset + selectionIndex;
if (num2 >= hubPageOffset && num2 < num)
{
Sound.Play("SoundMenuSelect", 1f, 1f);
HubState.ActivePage = num2;
HubState.PageItemOffset = 0;
HubState.Current = HubState.View.Page;
SetDesiredNavigationIndex(menu, 0);
}
}
else if (selectionIndex == layout.NextRow && num < count)
{
Sound.Play("SoundMenuSelect", 1f, 1f);
HubState.HubPageOffset = ClampPageOffset(HubState.HubPageOffset + layout.Step, count);
}
else if (selectionIndex == layout.BackRow)
{
Sound.Play("SoundMenuBack", 1f, 1f);
HubState.Reset();
SetDesiredNavigationIndex(menu, GetModsSlotIndex());
}
}
private static void HandleHubPageSelection(MainMenuScript menu, int selectionIndex, HubLayout layout, int direction)
{
if (!TryGetActivePage(out ModSettingsRegistry.Page page))
{
return;
}
int pageItemOffset = HubState.PageItemOffset;
int count = page.Items.Count;
int num = Mathf.Min(pageItemOffset + layout.ListSlots, count);
if (selectionIndex >= 0 && selectionIndex < layout.ListSlots)
{
int num2 = pageItemOffset + selectionIndex;
if (num2 >= pageItemOffset && num2 < num)
{
ModSettingsRegistry.Item item = page.Items[num2];
if ((direction != 0) ? TryInvokeAdjust(page, num2, item, direction) : TryInvokeSelect(page, num2, item))
{
Sound.Play("SoundMenuSelect", 1f, 1f);
}
}
}
else if (selectionIndex == layout.NextRow && num < count)
{
Sound.Play("SoundMenuSelect", 1f, 1f);
HubState.PageItemOffset = ClampPageOffset(HubState.PageItemOffset + layout.Step, count);
}
else if (selectionIndex == layout.BackRow)
{
Sound.Play("SoundMenuBack", 1f, 1f);
HubState.Current = HubState.View.HubIndex;
HubState.PageItemOffset = 0;
HubState.ActivePage = -1;
SetDesiredNavigationIndex(menu, 0);
}
}
private static int GetNavigationDirection(MainMenuScript menu)
{
bool flag = GetFlag(RightNavigationPressField, menu);
bool flag2 = GetFlag(LeftNavigationPressField, menu);
int num = (flag ? 1 : (flag2 ? (-1) : 0));
if (num == 0 && Controls.MouseButton_PressedGet(0, (MouseElement)1))
{
num = -1;
}
return num;
}
private static bool TryInvokeSelect(ModSettingsRegistry.Page page, int itemIndex, ModSettingsRegistry.Item item)
{
if (item == null || item.OnSelect == null)
{
return true;
}
return TryInvokeItemHandler(page, itemIndex, "OnSelect", item.OnSelect);
}
private static bool TryInvokeAdjust(ModSettingsRegistry.Page page, int itemIndex, ModSettingsRegistry.Item item, int direction)
{
ModSettingsRegistry.Item item2 = item;
if (item2 == null || item2.OnAdjust == null)
{
return true;
}
return TryInvokeItemHandler(page, itemIndex, "OnAdjust", delegate
{
item2.OnAdjust(direction);
});
}
private static bool TryInvokeItemHandler(ModSettingsRegistry.Page page, int itemIndex, string handler, Action invoke)
{
try
{
invoke();
return true;
}
catch (Exception ex)
{
LogHandlerException(handler, page, itemIndex, ex);
return false;
}
}
private static void LogHandlerException(string handler, ModSettingsRegistry.Page page, int itemIndex, Exception ex)
{
string text = page?.Name ?? "(unnamed page)";
ManualLogSource? log = ModSettingsExtenderPlugin.Log;
if (log != null)
{
log.LogError((object)$"Exception during {handler} for page '{text}' item index {itemIndex}: {ex}");
}
}
}
public static class ModSettingsRegistry
{
public enum ToggleAdjustMode
{
Toggle,
Directional
}
public sealed class Item
{
public Func<string>? Label { get; set; }
public Action? OnSelect { get; set; }
public Action<int>? OnAdjust { get; set; }
public Item()
{
}
internal Item(Func<string>? label, Action? onSelect, Action<int>? onAdjust)
{
Label = label;
OnSelect = onSelect;
OnAdjust = onAdjust;
}
}
public sealed class Page
{
private readonly List<Item> items = new List<Item>();
public string Name { get; }
internal string OwnerGuid { get; }
internal string OwnerName { get; }
internal string NormalizedName { get; }
public List<Item> Items => items;
internal Page(string name, string ownerGuid, string ownerName, string normalizedName)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Page name must not be empty.", "name");
}
if (string.IsNullOrWhiteSpace(ownerGuid))
{
throw new ArgumentException("Page owner GUID must not be empty.", "ownerGuid");
}
if (string.IsNullOrWhiteSpace(normalizedName))
{
throw new ArgumentException("Normalized page name must not be empty.", "normalizedName");
}
Name = name;
OwnerGuid = ownerGuid;
OwnerName = ownerName;
NormalizedName = normalizedName;
}
internal Item AddItem(Func<string>? label, Action? onSelect, Action<int>? onAdjust)
{
return AddItem(new Item(label, onSelect, onAdjust));
}
internal Item AddItem(Item item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
items.Add(item);
return item;
}
}
public sealed class PageBuilder
{
private static readonly IReadOnlyList<float> DefaultMultiplierSteps = Array.AsReadOnly(new float[6] { 0f, 0.5f, 1f, 2f, 3f, 4f });
private readonly Page page;
public Page BuiltPage => page;
internal PageBuilder(Page page)
{
this.page = page ?? throw new ArgumentNullException("page");
}
public PageBuilder AddItem(Func<string>? label, Action? onSelect = null, Action<int>? onAdjust = null)
{
page.AddItem(label, onSelect, onAdjust);
return this;
}
public PageBuilder AddToggle(string label, Func<bool> getter, Action<bool> setter, string onLabel = "On", string offLabel = "Off", Action<bool>? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle)
{
string label2 = label;
Func<bool> getter2 = getter;
string onLabel2 = onLabel;
string offLabel2 = offLabel;
Action<bool> setter2 = setter;
Action<bool> onChanged2 = onChanged;
if (label2 == null)
{
throw new ArgumentNullException("label");
}
if (getter2 == null)
{
throw new ArgumentNullException("getter");
}
if (setter2 == null)
{
throw new ArgumentNullException("setter");
}
return AddItem(Format, delegate
{
SetValue(!getter2());
}, delegate(int dir)
{
int num = NormalizeDirection(dir);
if (num != 0)
{
if (adjustMode == ToggleAdjustMode.Directional)
{
SetValue(num > 0);
}
else
{
SetValue(!getter2());
}
}
});
string Format()
{
return label2 + ": " + (getter2() ? onLabel2 : offLabel2);
}
void SetValue(bool value)
{
UpdateIfChanged(getter2, setter2, value, onChanged2);
}
}
public PageBuilder AddToggle(string label, ConfigEntry<bool> entry, string onLabel = "On", string offLabel = "Off", Action<bool>? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle)
{
ConfigEntry<bool> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return AddToggle(label, () => entry2.Value, delegate(bool value)
{
entry2.Value = value;
}, onLabel, offLabel, onChanged, adjustMode);
}
public PageBuilder OnOff(string label, Func<bool> getter, Action<bool> setter, string onLabel = "On", string offLabel = "Off", Action<bool>? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle)
{
return AddToggle(label, getter, setter, onLabel, offLabel, onChanged, adjustMode);
}
public PageBuilder OnOff(string label, ConfigEntry<bool> entry, string onLabel = "On", string offLabel = "Off", Action<bool>? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle)
{
ConfigEntry<bool> entry2 = entry;
return OnOff(label, () => entry2.Value, delegate(bool value)
{
entry2.Value = value;
}, onLabel, offLabel, onChanged, adjustMode);
}
public PageBuilder AddIntStepper(string label, Func<int> getter, Action<int> setter, int step = 1, int? min = null, int? max = null, bool wrap = false, Func<int, int>? normalizer = null, Func<int, string>? valueFormatter = null, Action<int>? onChanged = null)
{
Func<int, int> normalizer2 = normalizer;
Func<int> getter2 = getter;
Action<int> setter2 = setter;
Action<int> onChanged2 = onChanged;
Func<int, string> valueFormatter2 = valueFormatter;
string label2 = label;
if (label2 == null)
{
throw new ArgumentNullException("label");
}
if (getter2 == null)
{
throw new ArgumentNullException("getter");
}
if (setter2 == null)
{
throw new ArgumentNullException("setter");
}
if (step <= 0)
{
throw new ArgumentOutOfRangeException("step", "Step must be positive.");
}
return AddItem(Format, delegate
{
SetValue(getter2() + step);
}, delegate(int dir)
{
int num5 = NormalizeDirection(dir);
if (num5 != 0)
{
SetValue(getter2() + num5 * step);
}
});
int ApplyBounds(int value)
{
if (wrap && min.HasValue && max.HasValue && min.Value <= max.Value)
{
int num = Math.Max(1, step);
int num2 = max.Value - min.Value + num;
if (num2 > 0)
{
int num3 = ((value - min.Value) % num2 + num2) % num2;
return min.Value + num3;
}
}
if (min.HasValue && value < min.Value)
{
value = min.Value;
}
if (max.HasValue && value > max.Value)
{
value = max.Value;
}
return value;
}
string Format()
{
int arg = getter2();
string text = valueFormatter2?.Invoke(arg) ?? arg.ToString();
return label2 + ": " + text;
}
int Normalize(int value)
{
int num4 = ApplyBounds(value);
if (normalizer2 != null)
{
num4 = ApplyBounds(normalizer2(num4));
}
return num4;
}
void SetValue(int rawValue)
{
int value2 = Normalize(rawValue);
UpdateIfChanged(getter2, setter2, value2, onChanged2);
}
}
public PageBuilder AddIntStepper(string label, ConfigEntry<int> entry, int step = 1, int? min = null, int? max = null, bool wrap = false, Func<int, int>? normalizer = null, Func<int, string>? valueFormatter = null, Action<int>? onChanged = null)
{
ConfigEntry<int> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return AddIntStepper(label, () => entry2.Value, delegate(int value)
{
entry2.Value = value;
}, step, min, max, wrap, normalizer, valueFormatter, onChanged);
}
public PageBuilder Int(string label, Func<int> getter, Action<int> setter, int? min = null, int? max = null, int step = 1, bool wrap = false, Func<int, int>? normalizer = null, Func<int, string>? valueFormatter = null, Action<int>? onChanged = null)
{
return AddIntStepper(label, getter, setter, step, min, max, wrap, normalizer, valueFormatter, onChanged);
}
public PageBuilder Int(string label, ConfigEntry<int> entry, int? min = null, int? max = null, int step = 1, bool wrap = false, Func<int, int>? normalizer = null, Func<int, string>? valueFormatter = null, Action<int>? onChanged = null)
{
ConfigEntry<int> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Int(label, () => entry2.Value, delegate(int value)
{
entry2.Value = value;
}, min, max, step, wrap, normalizer, valueFormatter, onChanged);
}
public PageBuilder Percent(string label, Func<int> getter, Action<int> setter, int minPercent = 0, int maxPercent = 100, int step = 5, bool wrap = false, Action<int>? onChanged = null)
{
if (maxPercent < minPercent)
{
throw new ArgumentOutOfRangeException("maxPercent", "maxPercent must be greater than or equal to minPercent.");
}
return AddIntStepper(label, getter, setter, step, minPercent, maxPercent, wrap, null, Formatter, onChanged);
static string Formatter(int value)
{
return $"{value}%";
}
}
public PageBuilder Percent(string label, ConfigEntry<int> entry, int minPercent = 0, int maxPercent = 100, int step = 5, bool wrap = false, Action<int>? onChanged = null)
{
ConfigEntry<int> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Percent(label, () => entry2.Value, delegate(int value)
{
entry2.Value = value;
}, minPercent, maxPercent, step, wrap, onChanged);
}
public PageBuilder Percent(string label, Func<float> getter, Action<float> setter, float minPercent = 0f, float maxPercent = 100f, float step = 5f, bool wrap = false, int decimalPlaces = 1, Action<float>? onChanged = null)
{
Action<float> onChanged2 = onChanged;
Func<float> getter2 = getter;
Action<float> setter2 = setter;
if (label == null)
{
throw new ArgumentNullException("label");
}
if (getter2 == null)
{
throw new ArgumentNullException("getter");
}
if (setter2 == null)
{
throw new ArgumentNullException("setter");
}
if (step <= 0f)
{
throw new ArgumentOutOfRangeException("step", "Step must be positive.");
}
if (decimalPlaces < 0)
{
throw new ArgumentOutOfRangeException("decimalPlaces", "decimalPlaces must be zero or greater.");
}
float num = getter2();
int requiredDecimalPlaces = GetRequiredDecimalPlaces(minPercent, maxPercent, step, num);
int effectiveDecimalPlaces = Math.Max(decimalPlaces, requiredDecimalPlaces);
int scale = Pow10(effectiveDecimalPlaces);
int num2 = Scale(step);
if (num2 <= 0)
{
throw new ArgumentOutOfRangeException("step", "Step is too small for the configured decimalPlaces.");
}
int num3 = Scale(minPercent);
int num4 = Scale(maxPercent);
if (num4 < num3)
{
throw new ArgumentOutOfRangeException("maxPercent", "maxPercent must be greater than or equal to minPercent.");
}
Action<int> onChanged3 = null;
if (onChanged2 != null)
{
onChanged3 = delegate(int value)
{
onChanged2(Unscale(value));
};
}
return AddIntStepper(label, () => Scale(getter2()), delegate(int value)
{
setter2(Unscale(value));
}, num2, num3, num4, wrap, null, FormatPercentLabel, onChanged3);
string FormatPercentLabel(int scaledValue)
{
float num5 = Unscale(scaledValue);
string text = ((effectiveDecimalPlaces > 0) ? $"F{effectiveDecimalPlaces}" : "F0");
string text2 = num5.ToString(text, CultureInfo.InvariantCulture);
if (effectiveDecimalPlaces > 0)
{
text2 = text2.TrimEnd('0').TrimEnd('.');
}
return text2 + "%";
}
int Scale(float value)
{
decimal d = (decimal)value * (decimal)scale;
return (int)Math.Round(d, MidpointRounding.AwayFromZero);
}
float Unscale(int value)
{
return (float)value / (float)scale;
}
}
public PageBuilder Percent(string label, ConfigEntry<float> entry, float minPercent = 0f, float maxPercent = 100f, float step = 5f, bool wrap = false, int decimalPlaces = 1, Action<float>? onChanged = null)
{
ConfigEntry<float> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Percent(label, () => entry2.Value, delegate(float value)
{
entry2.Value = value;
}, minPercent, maxPercent, step, wrap, decimalPlaces, onChanged);
}
public PageBuilder Multiplier(string label, Func<float> getter, Action<float> setter, float minMultiplier, float maxMultiplier, float step, bool wrap = false, int decimalPlaces = 2, Func<float, string>? valueFormatter = null, Action<float>? onChanged = null)
{
Func<float, string> valueFormatter2 = valueFormatter;
Action<float> onChanged2 = onChanged;
Func<float> getter2 = getter;
Action<float> setter2 = setter;
if (label == null)
{
throw new ArgumentNullException("label");
}
if (getter2 == null)
{
throw new ArgumentNullException("getter");
}
if (setter2 == null)
{
throw new ArgumentNullException("setter");
}
if (step <= 0f)
{
throw new ArgumentOutOfRangeException("step", "Step must be positive.");
}
if (decimalPlaces < 0)
{
throw new ArgumentOutOfRangeException("decimalPlaces", "decimalPlaces must be zero or greater.");
}
float num = getter2();
int requiredDecimalPlaces = GetRequiredDecimalPlaces(minMultiplier, maxMultiplier, step, num);
int effectiveDecimalPlaces = Math.Max(decimalPlaces, requiredDecimalPlaces);
int scale = Pow10(effectiveDecimalPlaces);
int num2 = Scale(step);
if (num2 <= 0)
{
throw new ArgumentOutOfRangeException("step", "Step is too small for the configured decimalPlaces.");
}
int num3 = Scale(minMultiplier);
int num4 = Scale(maxMultiplier);
if (num4 < num3)
{
throw new ArgumentOutOfRangeException("maxMultiplier", "maxMultiplier must be greater than or equal to minMultiplier.");
}
Action<int> onChanged3 = null;
if (onChanged2 != null)
{
onChanged3 = delegate(int value)
{
onChanged2(Unscale(value));
};
}
return AddIntStepper(label, () => Scale(getter2()), delegate(int value)
{
setter2(Unscale(value));
}, num2, num3, num4, wrap, null, FormatMultiplierLabel, onChanged3);
string FormatMultiplierLabel(int scaledValue)
{
float arg = Unscale(scaledValue);
if (valueFormatter2 != null)
{
return valueFormatter2(arg);
}
string text = ((effectiveDecimalPlaces > 0) ? $"F{effectiveDecimalPlaces}" : "F0");
string text2 = arg.ToString(text, CultureInfo.InvariantCulture);
if (effectiveDecimalPlaces > 0)
{
text2 = text2.TrimEnd('0').TrimEnd('.');
}
return text2 + "x";
}
int Scale(float value)
{
decimal d = (decimal)value * (decimal)scale;
return (int)Math.Round(d, MidpointRounding.AwayFromZero);
}
float Unscale(int value)
{
return (float)value / (float)scale;
}
}
public PageBuilder Multiplier(string label, ConfigEntry<float> entry, float minMultiplier, float maxMultiplier, float step, bool wrap = false, int decimalPlaces = 2, Func<float, string>? valueFormatter = null, Action<float>? onChanged = null)
{
ConfigEntry<float> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Multiplier(label, () => entry2.Value, delegate(float value)
{
entry2.Value = value;
}, minMultiplier, maxMultiplier, step, wrap, decimalPlaces, valueFormatter, onChanged);
}
public PageBuilder Multiplier(string label, Func<float> getter, Action<float> setter, IReadOnlyList<float>? options = null, Func<float, string>? valueFormatter = null, Action<float>? onChanged = null)
{
Func<float, string> valueFormatter2 = valueFormatter;
IReadOnlyList<float> values = options ?? DefaultMultiplierSteps;
int optionDecimalPlaces = ((valueFormatter2 == null) ? GetRequiredDecimalPlaces(values) : 0);
return Cycle(label, getter, setter, values, formatter, onChanged);
string formatter(float value)
{
if (valueFormatter2 != null)
{
return valueFormatter2(value);
}
string text = ((optionDecimalPlaces > 0) ? $"F{optionDecimalPlaces}" : "F0");
string text2 = value.ToString(text, CultureInfo.InvariantCulture);
if (optionDecimalPlaces > 0)
{
text2 = text2.TrimEnd('0').TrimEnd('.');
}
return text2 + "x";
}
}
public PageBuilder Multiplier(string label, ConfigEntry<float> entry, IReadOnlyList<float>? options = null, Func<float, string>? valueFormatter = null, Action<float>? onChanged = null)
{
ConfigEntry<float> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Multiplier(label, () => entry2.Value, delegate(float value)
{
entry2.Value = value;
}, options, valueFormatter, onChanged);
}
public PageBuilder Cycle<T>(string label, Func<T> getter, Action<T> setter, IReadOnlyList<T> values, Func<T, string>? valueFormatter = null, Action<T>? onChanged = null, IEqualityComparer<T>? comparer = null)
{
return CycleInternal(label, getter, setter, values, valueFormatter, onChanged, comparer);
}
public PageBuilder Cycle<T>(string label, Func<T> getter, Action<T> setter, params T[] values)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
return CycleInternal(label, getter, setter, Array.AsReadOnly(values), null, null, null);
}
public PageBuilder Cycle<T>(string label, ConfigEntry<T> entry, IReadOnlyList<T> values, Func<T, string>? valueFormatter = null, Action<T>? onChanged = null, IEqualityComparer<T>? comparer = null)
{
ConfigEntry<T> entry2 = entry;
if (entry2 == null)
{
throw new ArgumentNullException("entry");
}
return Cycle(label, () => entry2.Value, delegate(T value)
{
entry2.Value = value;
}, values, valueFormatter, onChanged, comparer);
}
public PageBuilder Cycle<T>(string label, ConfigEntry<T> entry, params T[] values)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
return Cycle(label, entry, (IReadOnlyList<T>)Array.AsReadOnly(values), (Func<T, string>?)null, (Action<T>?)null, (IEqualityComparer<T>?)null);
}
private PageBuilder CycleInternal<T>(string label, Func<T> getter, Action<T> setter, IReadOnlyList<T> values, Func<T, string>? valueFormatter, Action<T>? onChanged, IEqualityComparer<T>? comparer)
{
IReadOnlyList<T> values2 = values;
Func<T, string> valueFormatter2 = valueFormatter;
Func<T> getter2 = getter;
string label2 = label;
Action<T> setter2 = setter;
Action<T> onChanged2 = onChanged;
if (label2 == null)
{
throw new ArgumentNullException("label");
}
if (getter2 == null)
{
throw new ArgumentNullException("getter");
}
if (setter2 == null)
{
throw new ArgumentNullException("setter");
}
if (values2 == null)
{
throw new ArgumentNullException("values");
}
if (values2.Count == 0)
{
throw new ArgumentException("At least one value must be supplied.", "values");
}
IEqualityComparer<T> equalityComparer = comparer ?? EqualityComparer<T>.Default;
return AddItem(Format, delegate
{
Advance(1);
}, delegate(int dir)
{
Advance(dir);
});
void Advance(int direction)
{
int num = NormalizeDirection(direction);
if (num != 0 && values2.Count != 1)
{
T candidate2 = getter2();
int num2 = FindIndex(candidate2);
int index = ((num2 >= 0) ? ((num2 + num + values2.Count) % values2.Count) : ((num <= 0) ? (values2.Count - 1) : 0));
T value2 = values2[index];
UpdateIfChanged(getter2, setter2, value2, onChanged2);
}
}
int FindIndex(T candidate)
{
for (int i = 0; i < values2.Count; i++)
{
if (equalityComparer.Equals(values2[i], candidate))
{
return i;
}
}
return -1;
}
string Format()
{
T value3 = getter2();
return label2 + ": " + RenderValue(value3);
}
string RenderValue(T value)
{
if (valueFormatter2 != null)
{
return valueFormatter2(value);
}
if (!((object)value is IFormattable formattable))
{
object obj = value?.ToString();
if (obj == null)
{
obj = string.Empty;
}
return (string)obj;
}
return formattable.ToString(null, CultureInfo.InvariantCulture);
}
}
private static int NormalizeDirection(int value)
{
if (value == 0)
{
return 0;
}
if (value <= 0)
{
return -1;
}
return 1;
}
private static int Pow10(int exponent)
{
if (exponent < 0)
{
throw new ArgumentOutOfRangeException("exponent");
}
int num = 1;
for (int i = 0; i < exponent; i++)
{
num = checked(num * 10);
}
return num;
}
private static int GetRequiredDecimalPlaces(IReadOnlyList<float> values)
{
if (values == null || values.Count == 0)
{
return 0;
}
int num = 0;
for (int i = 0; i < values.Count; i++)
{
int num2 = CountDecimalPlaces(values[i]);
if (num2 > num)
{
num = num2;
}
}
return num;
}
private static int GetRequiredDecimalPlaces(params float[] values)
{
if (values == null || values.Length == 0)
{
return 0;
}
int num = 0;
foreach (float value in values)
{
int num2 = CountDecimalPlaces(value);
if (num2 > num)
{
num = num2;
}
}
return num;
}
private static int CountDecimalPlaces(float value)
{
decimal value2 = decimal.Round((decimal)value, 6, MidpointRounding.AwayFromZero);
value2 = Math.Abs(value2);
for (int i = 0; i <= 6; i++)
{
decimal num = value2 * (decimal)Pow10(i);
if (decimal.Truncate(num) == num)
{
return i;
}
}
return 6;
}
private static void UpdateIfChanged<T>(Func<T> getter, Action<T> setter, T value, Action<T>? onChanged)
{
if (getter == null)
{
throw new ArgumentNullException("getter");
}
if (setter == null)
{
throw new ArgumentNullException("setter");
}
T x = getter();
if (!EqualityComparer<T>.Default.Equals(x, value))
{
setter(value);
onChanged?.Invoke(value);
}
}
}
private const int MaxAutoDecimalPlaces = 6;
private static readonly List<Page> pages = new List<Page>();
private static readonly Dictionary<string, Dictionary<string, Page>> pagesByOwner = new Dictionary<string, Dictionary<string, Page>>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, HashSet<string>> ownersByDisplayName = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
public static IReadOnlyList<Page> Pages => pages;
internal static string GetDisplayTitle(Page page)
{
if (page == null)
{
return string.Empty;
}
string text = page.Name ?? string.Empty;
string key = page.NormalizedName ?? NormalizePageName(text);
if (!ownersByDisplayName.TryGetValue(key, out HashSet<string> value) || value.Count <= 1)
{
return text;
}
string text2 = page.OwnerName;
if (string.IsNullOrWhiteSpace(text2))
{
text2 = page.OwnerGuid;
}
if (!string.IsNullOrEmpty(text2) && text2.Length > 8)
{
text2 = text2.Substring(0, 8);
}
if (!string.IsNullOrWhiteSpace(text2))
{
return text + " (" + text2 + ")";
}
return text;
}
private static Page AddOrReplacePage(Page page)
{
if (page == null)
{
throw new ArgumentNullException("page");
}
if (string.IsNullOrWhiteSpace(page.OwnerGuid))
{
throw new ArgumentException("Page must have an owner GUID.", "page");
}
if (string.IsNullOrWhiteSpace(page.NormalizedName))
{
throw new ArgumentException("Page must have a normalized name.", "page");
}
TrackDisplayName(page);
string ownerGuid = page.OwnerGuid;
string normalizedName = page.NormalizedName;
if (!pagesByOwner.TryGetValue(ownerGuid, out Dictionary<string, Page> value))
{
value = new Dictionary<string, Page>(StringComparer.OrdinalIgnoreCase);
pagesByOwner[ownerGuid] = value;
}
if (value.TryGetValue(normalizedName, out var value2))
{
int num = pages.IndexOf(value2);
if (num >= 0)
{
pages.RemoveAt(num);
pages.Insert(num, page);
}
else
{
pages.Add(page);
}
value[normalizedName] = page;
return page;
}
value[normalizedName] = page;
pages.Add(page);
return page;
}
private static void TrackDisplayName(Page page)
{
string key = page.NormalizedName ?? NormalizePageName(page.Name ?? string.Empty);
string text = page.OwnerGuid ?? string.Empty;
if (!ownersByDisplayName.TryGetValue(key, out HashSet<string> value))
{
value = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
ownersByDisplayName[key] = value;
}
if (!string.IsNullOrWhiteSpace(text))
{
value.Add(text);
}
}
private static string NormalizePageName(string name)
{
if (name == null)
{
throw new ArgumentNullException("name");
}
string text = name.Trim();
if (text.Length == 0)
{
throw new ArgumentException("Page name must not be empty.", "name");
}
return text;
}
private static string ResolveOwnerGuid(BaseUnityPlugin owner)
{
if ((Object)(object)owner == (Object)null)
{
throw new ArgumentNullException("owner");
}
PluginInfo info = owner.Info;
object obj;
if (info == null)
{
obj = null;
}
else
{
BepInPlugin metadata = info.Metadata;
obj = ((metadata != null) ? metadata.GUID : null);
}
string text = (string)obj;
if (string.IsNullOrWhiteSpace(text))
{
throw new InvalidOperationException("Plugin '" + ((object)owner).GetType().FullName + "' must define a BepInEx GUID.");
}
return text;
}
private static string ResolveOwnerName(BaseUnityPlugin owner)
{
if ((Object)(object)owner == (Object)null)
{
throw new ArgumentNullException("owner");
}
PluginInfo info = owner.Info;
object obj;
if (info == null)
{
obj = null;
}
else
{
BepInPlugin metadata = info.Metadata;
obj = ((metadata != null) ? metadata.Name : null);
}
string text = (string)obj;
if (!string.IsNullOrWhiteSpace(text))
{
return text;
}
return ((object)owner).GetType().Name;
}
private static Page CreatePageShell(BaseUnityPlugin owner, string name)
{
if ((Object)(object)owner == (Object)null)
{
throw new ArgumentNullException("owner");
}
if (name == null)
{
throw new ArgumentNullException("name");
}
string normalizedName = NormalizePageName(name);
string name2 = name.Trim();
string ownerGuid = ResolveOwnerGuid(owner);
string ownerName = ResolveOwnerName(owner);
return new Page(name2, ownerGuid, ownerName, normalizedName);
}
public static Page RegisterPage(BaseUnityPlugin owner, string name)
{
Page page = CreatePageShell(owner, name);
return AddOrReplacePage(page);
}
public static Page RegisterPage(string name)
{
BaseUnityPlugin owner = ResolveOwnerForLegacyCall(Assembly.GetCallingAssembly());
return RegisterPage(owner, name);
}
public static PageBuilder CreatePage(BaseUnityPlugin owner, string name)
{
Page page = CreatePageShell(owner, name);
AddOrReplacePage(page);
return new PageBuilder(page);
}
public static PageBuilder CreatePage(string name)
{
BaseUnityPlugin owner = ResolveOwnerForLegacyCall(Assembly.GetCallingAssembly());
return CreatePage(owner, name);
}
public static PageBuilder RegisterPage(BaseUnityPlugin owner, string name, Action<PageBuilder> configure)
{
if (configure == null)
{
throw new ArgumentNullException("configure");
}
Page page = CreatePageShell(owner, name);
PageBuilder pageBuilder = new PageBuilder(page);
try
{
configure(pageBuilder);
}
catch (Exception arg)
{
ManualLogSource? log = ModSettingsExtenderPlugin.Log;
if (log != null)
{
log.LogError((object)$"Exception while configuring settings page '{page.Name}': {arg}");
}
throw;
}
AddOrReplacePage(page);
return pageBuilder;
}
public static PageBuilder RegisterPage(string name, Action<PageBuilder> configure)
{
if (configure == null)
{
throw new ArgumentNullException("configure");
}
BaseUnityPlugin owner = ResolveOwnerForLegacyCall(Assembly.GetCallingAssembly());
return RegisterPage(owner, name, configure);
}
public static Item RegisterItem(Page page, Func<string>? label, Action? onSelect = null, Action<int>? onAdjust = null)
{
if (page == null)
{
throw new ArgumentNullException("page");
}
return page.AddItem(label, onSelect, onAdjust);
}
private static BaseUnityPlugin ResolveOwnerForLegacyCall(Assembly caller)
{
if (caller == null)
{
throw new ArgumentNullException("caller");
}
BaseUnityPlugin val = TryResolveOwnerFromAssembly(caller);
if ((Object)(object)val != (Object)null)
{
return val;
}
throw new InvalidOperationException("Unable to infer the plugin owning this settings page. Call the overload that accepts a BaseUnityPlugin instance.");
}
private static BaseUnityPlugin? TryResolveOwnerFromAssembly(Assembly assembly)
{
if (assembly == null)
{
return null;
}
foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
{
PluginInfo value = pluginInfo.Value;
if (!((Object)(object)((value != null) ? value.Instance : null) == (Object)null) && ((object)value.Instance).GetType().Assembly == assembly)
{
return value.Instance;
}
}
return null;
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ConstantExpectedAttribute : Attribute
{
public object? Min { get; set; }
public object? Max { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ExperimentalAttribute : Attribute
{
public string DiagnosticId { get; }
public string? UrlFormat { get; set; }
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class StringSyntaxAttribute : Attribute
{
public const string CompositeFormat = "CompositeFormat";
public const string DateOnlyFormat = "DateOnlyFormat";
public const string DateTimeFormat = "DateTimeFormat";
public const string EnumFormat = "EnumFormat";
public const string GuidFormat = "GuidFormat";
public const string Json = "Json";
public const string NumericFormat = "NumericFormat";
public const string Regex = "Regex";
public const string TimeOnlyFormat = "TimeOnlyFormat";
public const string TimeSpanFormat = "TimeSpanFormat";
public const string Uri = "Uri";
public const string Xml = "Xml";
public string Syntax { get; }
public object?[] Arguments { get; }
public StringSyntaxAttribute(string syntax)
{
Syntax = syntax;
Arguments = new object[0];
}
public StringSyntaxAttribute(string syntax, params object?[] arguments)
{
Syntax = syntax;
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class UnscopedRefAttribute : Attribute
{
}
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresPreviewFeaturesAttribute : Attribute
{
public string? Message { get; }
public string? Url { get; set; }
public RequiresPreviewFeaturesAttribute()
{
}
public RequiresPreviewFeaturesAttribute(string? message)
{
Message = message;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public string ParameterName { get; }
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CollectionBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public string MethodName { get; }
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public const string RefStructs = "RefStructs";
public const string RequiredMembers = "RequiredMembers";
public string FeatureName { get; }
public bool IsOptional { get; set; }
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public string[] Arguments { get; }
public InterpolatedStringHandlerArgumentAttribute(string argument)
{
Arguments = new string[1] { argument };
}
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
{
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerAttribute : Attribute
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal static class IsExternalInit
{
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class OverloadResolutionPriorityAttribute : Attribute
{
public int Priority { get; }
public OverloadResolutionPriorityAttribute(int priority)
{
Priority = priority;
}
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
[ExcludeFromCodeCoverage]
internal sealed class ParamCollectionAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiredMemberAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresLocationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}