Decompiled source of Mod Menu v0.1.0

plugins/ModMenu.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
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.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Shared;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ModMenu")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ModMenu")]
[assembly: AssemblyTitle("ModMenu")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 Shared
{
	internal static class LegacyConfig
	{
		internal static void Adopt(BaseUnityPlugin plugin)
		{
			try
			{
				string configFilePath = plugin.Config.ConfigFilePath;
				string directoryName = Path.GetDirectoryName(configFilePath);
				if (File.Exists(configFilePath) || directoryName == null || !Directory.Exists(directoryName))
				{
					return;
				}
				string fileName = Path.GetFileName(configFilePath);
				if (fileName.StartsWith("crackmods"))
				{
					string text = "crack" + fileName.Substring("crackmods".Length);
					string text2 = Directory.GetFiles(directoryName, "com.*." + text).FirstOrDefault();
					if (text2 != null)
					{
						File.Move(text2, configFilePath);
						plugin.Config.Reload();
					}
				}
			}
			catch (IOException)
			{
			}
		}
	}
}
namespace ModMenu
{
	internal sealed class Attrs
	{
		public bool Hidden;

		public bool ReadOnly;

		public bool Advanced;

		public bool HideDefault;

		public bool Percent;

		public int Order;

		public string Name;

		public string Category;

		public string Description;

		public object Default;

		public Func<object, string> ObjToStr;

		public Func<string, object> StrToObj;

		public static Attrs Of(ConfigEntryBase entry)
		{
			Attrs attrs = new Attrs
			{
				Default = entry.DefaultValue
			};
			ConfigDescription description = entry.Description;
			object[] array = ((description != null) ? description.Tags : null);
			if (array == null)
			{
				return attrs;
			}
			object[] array2 = array;
			foreach (object obj in array2)
			{
				if (obj == null)
				{
					continue;
				}
				if (!(obj is DisplayNameAttribute displayNameAttribute))
				{
					if (!(obj is CategoryAttribute categoryAttribute))
					{
						if (!(obj is DescriptionAttribute descriptionAttribute))
						{
							if (!(obj is DefaultValueAttribute defaultValueAttribute))
							{
								if (!(obj is ReadOnlyAttribute readOnlyAttribute))
								{
									if (!(obj is BrowsableAttribute browsableAttribute))
									{
										if (obj is string text)
										{
											switch (text)
											{
											case "ReadOnly":
												attrs.ReadOnly = true;
												break;
											case "Browsable":
												attrs.Hidden = false;
												break;
											case "Unbrowsable":
											case "Hidden":
												attrs.Hidden = true;
												break;
											case "Advanced":
												attrs.Advanced = true;
												break;
											}
										}
										else if (obj.GetType().Name == "ConfigurationManagerAttributes")
										{
											attrs.Read(obj);
										}
									}
									else
									{
										attrs.Hidden = !browsableAttribute.Browsable;
									}
								}
								else
								{
									attrs.ReadOnly = readOnlyAttribute.IsReadOnly;
								}
							}
							else
							{
								attrs.Default = defaultValueAttribute.Value;
							}
						}
						else
						{
							attrs.Description = descriptionAttribute.Description;
						}
					}
					else
					{
						attrs.Category = categoryAttribute.Category;
					}
				}
				else
				{
					attrs.Name = displayNameAttribute.DisplayName;
				}
			}
			return attrs;
		}

		private void Read(object tag)
		{
			if (Get(tag, "Browsable") is bool flag)
			{
				Hidden = !flag;
			}
			if (Get(tag, "ReadOnly") is bool readOnly)
			{
				ReadOnly = readOnly;
			}
			if (Get(tag, "IsAdvanced") is bool advanced)
			{
				Advanced = advanced;
			}
			if (Get(tag, "HideDefaultButton") is bool hideDefault)
			{
				HideDefault = hideDefault;
			}
			if (Get(tag, "ShowRangeAsPercent") is bool percent)
			{
				Percent = percent;
			}
			if (Get(tag, "Order") is int order)
			{
				Order = order;
			}
			if (Get(tag, "DispName") is string name)
			{
				Name = name;
			}
			if (Get(tag, "Category") is string category)
			{
				Category = category;
			}
			if (Get(tag, "Description") is string description)
			{
				Description = description;
			}
			Default = Get(tag, "DefaultValue") ?? Default;
			ObjToStr = (Get(tag, "ObjToStr") as Func<object, string>) ?? ObjToStr;
			StrToObj = (Get(tag, "StrToObj") as Func<string, object>) ?? StrToObj;
		}

		private static object Get(object o, string name)
		{
			Type type = o.GetType();
			object obj = type.GetField(name)?.GetValue(o);
			if (obj == null)
			{
				PropertyInfo? property = type.GetProperty(name);
				if ((object)property == null)
				{
					return null;
				}
				obj = property.GetValue(o, null);
			}
			return obj;
		}

		public static bool HiddenPlugin(PluginInfo p)
		{
			if ((Object)(object)p.Instance != (Object)null)
			{
				return ((object)p.Instance).GetType().GetCustomAttributes(typeof(BrowsableAttribute), inherit: false).Cast<BrowsableAttribute>()
					.Any((BrowsableAttribute b) => !b.Browsable);
			}
			return false;
		}
	}
	internal sealed class ModsMenu : MonoBehaviour
	{
		private static readonly FieldRef<UIShatter, UnityEvent> ShatterEvent = AccessTools.FieldRefAccess<UIShatter, UnityEvent>("_onShatter");

		private static readonly MethodInfo ShowPanel = AccessTools.Method(typeof(PanelMenu), "Show", (Type[])null, (Type[])null);

		internal Rows Rows;

		private RectTransform _mods;

		private RectTransform _settings;

		private TMP_Text _info;

		private PluginInfo[] _plugins;

		private int _picked;

		private readonly List<TMP_Text> _modLabels = new List<TMP_Text>();

		internal const string Intro = "Pick a mod to see its settings, then point at a setting to see what it does.\n\nChanges save right away. Some only take effect after a restart, and some are decided by the host in multiplayer.\n\nDISCORD opens the unofficial CRACK modding community in your browser.";

		internal static void Attach(PanelMenu menu)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			try
			{
				CanvasGroup settings = Traverse.Create((object)menu).Field("_settingsPanel").GetValue<CanvasGroup>();
				Traverse val = Traverse.Create((object)menu).Field("_panels");
				if (!((Object)(object)settings == (Object)null) && val.GetValue<CanvasGroup[]>() != null)
				{
					GameObject val2 = Object.Instantiate<GameObject>(((Component)settings).gameObject, ((Component)settings).transform.parent, false);
					((Object)val2).name = "PF_ModsMenu";
					Object.DestroyImmediate((Object)(object)val2.GetComponent<SettingsPanel>());
					CanvasGroup cg = val2.GetComponent<CanvasGroup>();
					val.SetValue((object)val.GetValue<CanvasGroup[]>().Concat((IEnumerable<CanvasGroup>)(object)new CanvasGroup[1] { cg }).ToArray());
					ModsMenu mods = val2.AddComponent<ModsMenu>();
					mods.Build((UnityAction)delegate
					{
						ShowPanel.Invoke(menu, new object[1] { settings });
					});
					AddButton(((Component)settings).transform, (UnityAction)delegate
					{
						mods.Refresh();
						ShowPanel.Invoke(menu, new object[1] { cg });
					});
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Couldn't add the Mods screen to {((object)menu).GetType().Name}: {arg}");
			}
		}

		private static void AddButton(Transform settings, UnityAction open)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			Transform val = settings.Find("PF_MenuButton (2)");
			if (!((Object)(object)val == (Object)null))
			{
				RectTransform val2 = (RectTransform)Object.Instantiate<GameObject>(((Component)val).gameObject, settings, false).transform;
				((Object)val2).name = "PF_MenuButton (Mods)";
				Vector2 anchoredPosition = val2.anchoredPosition;
				Rect rect = val2.rect;
				float num;
				if (!(((Rect)(ref rect)).height > 1f))
				{
					num = 96f;
				}
				else
				{
					rect = val2.rect;
					num = ((Rect)(ref rect)).height + 14f;
				}
				val2.anchoredPosition = anchoredPosition + new Vector2(0f, num);
				Retarget(((Component)val2).gameObject, "MODS", open);
				SetIcon((Transform)(object)val2, "wrench");
			}
		}

		private static void SetIcon(Transform button, string sprite)
		{
			Transform obj = button.Find("Icon");
			Image val = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
			Sprite val2 = ((IEnumerable<Sprite>)Resources.FindObjectsOfTypeAll<Sprite>()).FirstOrDefault((Func<Sprite, bool>)((Sprite x) => ((Object)x).name == sprite));
			if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null)
			{
				val.sprite = val2;
			}
		}

		private void AddDiscordButton(Transform button)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			((Object)button).name = "PF_MenuButton (Discord)";
			UIShatter shatter = ((Component)button).GetComponent<UIShatter>();
			Retarget(((Component)button).gameObject, "DISCORD", (UnityAction)delegate
			{
				Plugin.Log.LogInfo((object)"Opening the community Discord invite");
				Application.OpenURL("https://discord.gg/crack-modding");
				((MonoBehaviour)this).StartCoroutine(Reassemble(shatter));
			});
			SetIcon(button, "globe");
		}

		private static IEnumerator Reassemble(UIShatter shatter)
		{
			yield return (object)new WaitForSecondsRealtime(0.8f);
			if ((Object)(object)shatter != (Object)null)
			{
				shatter.Reassemble();
			}
		}

		private static void Retarget(GameObject button, string label, UnityAction act)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			if (label != null)
			{
				SetText(button.transform.Find("Text"), label);
			}
			UIShatter component = button.GetComponent<UIShatter>();
			UnityEvent val = new UnityEvent();
			val.AddListener(act);
			ShatterEvent.Invoke(component) = val;
		}

		internal static TMP_Text SetText(Transform t, string text)
		{
			if ((Object)(object)t == (Object)null)
			{
				return null;
			}
			LocalizedText component = ((Component)t).GetComponent<LocalizedText>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			TMP_Text component2 = ((Component)t).GetComponent<TMP_Text>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.text = text;
			}
			return component2;
		}

		private void Build(UnityAction back)
		{
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Expected O, but got Unknown
			Transform val = ((Component)this).transform.Find("General");
			Transform val2 = ((Component)this).transform.Find("Video");
			Transform val3 = ((Component)this).transform.Find("Audio");
			Rows = new Rows(this, ((Component)this).transform, val2.Find("Content"), val.Find("Content"));
			Transform[] array = (Transform[])(object)new Transform[3] { val, val2, val3 };
			for (int i = 0; i < array.Length; i++)
			{
				foreach (Transform item in ((IEnumerable)array[i].Find("Content")).Cast<Transform>().ToList())
				{
					Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
				}
			}
			SetText(val.Find("Text (TMP)"), "MODS");
			SetText(val2.Find("Text (TMP)"), "SETTINGS");
			SetText(val3.Find("Text (TMP)"), "INFO");
			Retarget(((Component)((Component)this).transform.Find("PF_MenuButton (1)")).gameObject, null, back);
			Transform val4 = ((Component)this).transform.Find("PF_MenuButton (2)");
			if ((Object)(object)val4 != (Object)null)
			{
				AddDiscordButton(val4);
			}
			_mods = Scrollable((RectTransform)val.Find("Content"));
			_settings = Scrollable((RectTransform)val2.Find("Content"));
			_info = Rows.InfoText((RectTransform)val3.Find("Content"));
			ShowInfo("Pick a mod to see its settings, then point at a setting to see what it does.\n\nChanges save right away. Some only take effect after a restart, and some are decided by the host in multiplayer.\n\nDISCORD opens the unofficial CRACK modding community in your browser.");
		}

		internal void ShowInfo(string text)
		{
			if ((Object)(object)_info != (Object)null)
			{
				_info.text = text;
			}
		}

		private static RectTransform Scrollable(RectTransform content)
		{
			//IL_003f: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject("Viewport", new Type[4]
			{
				typeof(RectTransform),
				typeof(RectMask2D),
				typeof(Image),
				typeof(ScrollRect)
			}).GetComponent<RectTransform>();
			((Transform)component).SetParent(((Transform)content).parent, false);
			component.anchorMin = content.anchorMin;
			component.anchorMax = content.anchorMax;
			component.pivot = content.pivot;
			component.offsetMin = content.offsetMin;
			component.offsetMax = content.offsetMax;
			((Graphic)((Component)component).GetComponent<Image>()).color = Color.clear;
			((Transform)content).SetParent((Transform)(object)component, false);
			content.anchorMin = new Vector2(0f, 1f);
			content.anchorMax = Vector2.one;
			content.pivot = new Vector2(0.5f, 1f);
			Vector2 offsetMin = (content.offsetMax = Vector2.zero);
			content.offsetMin = offsetMin;
			((Component)content).gameObject.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			ScrollRect component2 = ((Component)component).GetComponent<ScrollRect>();
			component2.content = content;
			component2.viewport = component;
			component2.horizontal = false;
			component2.movementType = (MovementType)2;
			component2.scrollSensitivity = 40f;
			return content;
		}

		internal void Refresh()
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			_plugins = Chainloader.PluginInfos.Values.Where((PluginInfo p) => p != null && p.Metadata != null && !Attrs.HiddenPlugin(p)).OrderBy<PluginInfo, string>((PluginInfo p) => p.Metadata.Name, StringComparer.OrdinalIgnoreCase).ToArray();
			Clear(_mods);
			_modLabels.Clear();
			for (int num = 0; num < _plugins.Length; num++)
			{
				int index = num;
				_modLabels.Add(Rows.Mod(_mods, _plugins[num], (UnityAction)delegate
				{
					Pick(index);
				}));
			}
			Pick(Mathf.Clamp(_picked, 0, Math.Max(0, _plugins.Length - 1)));
		}

		private void Pick(int index, bool keepScroll = false)
		{
			//IL_0016: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			Vector2 anchoredPosition = (keepScroll ? _settings.anchoredPosition : Vector2.zero);
			_picked = index;
			for (int i = 0; i < _modLabels.Count; i++)
			{
				_modLabels[i].alpha = ((i == index) ? 1f : 0.55f);
			}
			Clear(_settings);
			ShowInfo("Pick a mod to see its settings, then point at a setting to see what it does.\n\nChanges save right away. Some only take effect after a restart, and some are decided by the host in multiplayer.\n\nDISCORD opens the unofficial CRACK modding community in your browser.");
			if (index >= _plugins.Length)
			{
				return;
			}
			PluginInfo val = _plugins[index];
			ConfigFile config = (((Object)(object)val.Instance != (Object)null) ? val.Instance.Config : null);
			List<KeyValuePair<ConfigEntryBase, Attrs>> list = (from e in (config != null) ? config.Keys.Select((ConfigDefinition k) => config[k]) : Enumerable.Empty<ConfigEntryBase>()
				select new KeyValuePair<ConfigEntryBase, Attrs>(e, Attrs.Of(e)) into e
				where !e.Value.Hidden
				select e).ToList();
			if (list.Count == 0)
			{
				Rows.Header(_settings, "No settings");
			}
			if (list.Any((KeyValuePair<ConfigEntryBase, Attrs> e) => e.Value.Advanced))
			{
				Rows.Setting(_settings, (ConfigEntryBase)(object)Plugin.ShowAdvanced, Attrs.Of((ConfigEntryBase)(object)Plugin.ShowAdvanced));
				if (!Plugin.ShowAdvanced.Value)
				{
					list.RemoveAll((KeyValuePair<ConfigEntryBase, Attrs> e) => e.Value.Advanced);
				}
			}
			bool flag = list.Select(Section).Distinct().Count() > 1;
			foreach (IGrouping<string, KeyValuePair<ConfigEntryBase, Attrs>> item in list.GroupBy(Section))
			{
				if (flag)
				{
					Rows.Header(_settings, item.Key);
				}
				foreach (KeyValuePair<ConfigEntryBase, Attrs> item2 in item.OrderByDescending((KeyValuePair<ConfigEntryBase, Attrs> e) => e.Value.Order))
				{
					Rows.Setting(_settings, item2.Key, item2.Value);
				}
			}
			_settings.anchoredPosition = anchoredPosition;
			static string Section(KeyValuePair<ConfigEntryBase, Attrs> e)
			{
				return e.Value.Category ?? e.Key.Definition.Section;
			}
		}

		private void OnEnable()
		{
			Plugin.ShowAdvanced.SettingChanged += Repick;
		}

		private void OnDisable()
		{
			Plugin.ShowAdvanced.SettingChanged -= Repick;
		}

		private void Repick(object sender, EventArgs e)
		{
			if (_plugins != null)
			{
				Pick(_picked, keepScroll: true);
			}
		}

		private static void Clear(RectTransform content)
		{
			foreach (Transform item in ((IEnumerable)content).Cast<Transform>().ToList())
			{
				item.SetParent((Transform)null, false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
		}
	}
	internal sealed class Hint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, ISelectHandler
	{
		internal ModsMenu Menu;

		internal string Text;

		public void OnPointerEnter(PointerEventData e)
		{
			Menu.ShowInfo(Text);
		}

		public void OnSelect(BaseEventData e)
		{
			Menu.ShowInfo(Text);
		}
	}
	internal sealed class Click : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
	{
		internal UnityAction Action;

		public void OnPointerClick(PointerEventData e)
		{
			UnityAction action = Action;
			if (action != null)
			{
				action.Invoke();
			}
		}
	}
	[BepInPlugin("crackmodsui.modmenu", "Mod Menu", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "crackmodsui.modmenu";

		public const string Name = "Mod Menu";

		public const string Version = "0.1.0";

		internal const string DiscordInvite = "https://discord.gg/crack-modding";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> ShowAdvanced;

		private void Awake()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			LegacyConfig.Adopt((BaseUnityPlugin)(object)this);
			Log = ((BaseUnityPlugin)this).Logger;
			ShowAdvanced = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowAdvanced", false, "Also show settings that mods mark as advanced. These are usually for troubleshooting or fine-tuning, and most players can leave them alone.");
			new Harmony("crackmodsui.modmenu").PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)"Mod Menu 0.1.0 loaded");
		}
	}
	[HarmonyPatch(typeof(MainMenuController), "Awake")]
	internal static class MainMenuPatch
	{
		private static void Postfix(MainMenuController __instance)
		{
			ModsMenu.Attach((PanelMenu)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(GameMenu), "Awake")]
	internal static class GameMenuPatch
	{
		private static void Postfix(GameMenu __instance)
		{
			ModsMenu.Attach((PanelMenu)(object)__instance);
		}
	}
	internal sealed class Rows
	{
		private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

		private readonly ModsMenu _menu;

		private readonly GameObject _toggle;

		private readonly GameObject _slider;

		private readonly GameObject _dropdown;

		private readonly GameObject _plain;

		private readonly Dictionary<Transform, float> _widths = new Dictionary<Transform, float>();

		public Rows(ModsMenu menu, Transform root, params Transform[] contents)
		{
			_menu = menu;
			List<Transform> source = contents.SelectMany((Transform c) => ((IEnumerable)c).Cast<Transform>()).ToList();
			foreach (Transform val in contents)
			{
				_widths[val] = (from Transform t in (IEnumerable)val
					select ((RectTransform)t).sizeDelta.x).DefaultIfEmpty(800f).Max();
			}
			_toggle = Template(root, source.First((Transform t) => (Object)(object)Control<Toggle>(t) != (Object)null), "Toggle");
			_slider = Template(root, source.First((Transform t) => (Object)(object)Control<Slider>(t) != (Object)null), "Slider");
			_dropdown = Template(root, source.First((Transform t) => (Object)(object)Control<TMP_Dropdown>(t) != (Object)null), "Dropdown");
			_plain = Template(root, _toggle.transform, "Plain");
			Object.DestroyImmediate((Object)(object)((Component)Control<Toggle>(_plain.transform)).gameObject);
		}

		private static T Control<T>(Transform row) where T : Component
		{
			return (from Transform c in (IEnumerable)row
				select ((Component)c).GetComponent<T>()).FirstOrDefault((T c) => (Object)(object)c != (Object)null);
		}

		private static GameObject Template(Transform root, Transform item, string kind)
		{
			GameObject obj = Object.Instantiate<GameObject>(((Component)item).gameObject, root, false);
			((Object)obj).name = "Template " + kind;
			obj.SetActive(false);
			ModsMenu.SetText(obj.transform.Find("Label"), "");
			return obj;
		}

		private GameObject Row(GameObject template, RectTransform content, string label, string hint)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_002d: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(template, (Transform)(object)content, false);
			obj.SetActive(true);
			RectTransform val = (RectTransform)obj.transform;
			val.sizeDelta = new Vector2(_widths.TryGetValue((Transform)(object)content, out var value) ? value : val.sizeDelta.x, val.sizeDelta.y);
			Label(obj).text = label;
			((Graphic)obj.AddComponent<Image>()).color = Color.clear;
			Hint hint2 = obj.AddComponent<Hint>();
			hint2.Menu = _menu;
			hint2.Text = hint;
			return obj;
		}

		private static TMP_Text Label(GameObject row)
		{
			return ((Component)row.transform.Find("Label")).GetComponent<TMP_Text>();
		}

		internal TMP_Text Mod(RectTransform content, PluginInfo p, UnityAction pick)
		{
			//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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			string fileName = Path.GetFileName(p.Location);
			GameObject val = Row(_plain, content, p.Metadata.Name, $"{p.Metadata.Name} {p.Metadata.Version}\n" + "<alpha=#99>" + p.Metadata.GUID + "\n" + fileName);
			TMP_Text val2 = Label(val);
			RectTransform val3 = (RectTransform)Object.Instantiate<GameObject>(((Component)val2).gameObject, val.transform, false).transform;
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(1f, 0.5f);
			val3.pivot = val4;
			Vector2 anchorMin = (val3.anchorMax = val4);
			val3.anchorMin = anchorMin;
			val3.anchoredPosition = new Vector2(-12f, 0f);
			Rect rect = ((RectTransform)val2.transform).rect;
			val3.sizeDelta = new Vector2(220f, ((Rect)(ref rect)).height);
			TMP_Text component = ((Component)val3).GetComponent<TMP_Text>();
			component.text = p.Metadata.Version.ToString();
			component.alignment = (TextAlignmentOptions)4100;
			component.alpha = 0.6f;
			val.AddComponent<Click>().Action = pick;
			return val2;
		}

		internal void Header(RectTransform content, string text)
		{
			Label(Row(_plain, content, text.ToUpperInvariant(), "Pick a mod to see its settings, then point at a setting to see what it does.\n\nChanges save right away. Some only take effect after a restart, and some are decided by the host in multiplayer.\n\nDISCORD opens the unofficial CRACK modding community in your browser.")).alpha = 0.6f;
		}

		internal TMP_Text InfoText(RectTransform content)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0057: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = Label(_plain);
			RectTransform val2 = (RectTransform)Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)content).parent, false).transform;
			((Object)val2).name = "Info";
			val2.anchorMin = content.anchorMin;
			val2.anchorMax = content.anchorMax;
			val2.pivot = content.pivot;
			val2.offsetMin = content.offsetMin + new Vector2(24f, 12f);
			val2.offsetMax = content.offsetMax - new Vector2(24f, 0f);
			TMP_Text component = ((Component)val2).GetComponent<TMP_Text>();
			component.alignment = (TextAlignmentOptions)257;
			component.textWrappingMode = (TextWrappingModes)1;
			component.overflowMode = (TextOverflowModes)1;
			component.enableAutoSizing = true;
			component.fontSizeMin = 14f;
			component.fontSizeMax = val.fontSize * 0.8f;
			component.richText = true;
			return component;
		}

		internal void Setting(RectTransform content, ConfigEntryBase entry, Attrs attrs)
		{
			string text = attrs.Name ?? Regex.Replace(entry.Definition.Key, "(?<=[a-z0-9])(?=[A-Z])", " ");
			object obj = attrs.Description;
			if (obj == null)
			{
				ConfigDescription description = entry.Description;
				obj = ((description != null) ? description.Description : null);
			}
			string text2 = (string)obj;
			object def;
			bool flag = TryDefault(entry, attrs, out def);
			string hint = text + "\n<alpha=#CC>" + text2 + (flag ? ("\n\n<alpha=#99>Default: " + Format(attrs, def)) : "");
			Type settingType = entry.SettingType;
			ConfigDescription description2 = entry.Description;
			AcceptableValueBase val = ((description2 != null) ? description2.AcceptableValues : null);
			Action sync = ((settingType == typeof(bool)) ? Toggle(content, entry, text, hint) : (settingType.IsEnum ? Choice(content, entry, text, hint, attrs, Enum.GetValues(settingType).Cast<object>().ToList()) : (IsList(val) ? Choice(content, entry, text, hint, attrs, ListOf(val)) : ((!IsNumber(settingType) || !IsRange(val)) ? Text(content, entry, text, hint, attrs) : Slide(content, entry, text, hint, attrs, val)))));
			GameObject gameObject = ((Component)((Transform)content).GetChild(((Transform)content).childCount - 1)).gameObject;
			RowSync rowSync = gameObject.AddComponent<RowSync>();
			rowSync.Init(entry, sync);
			if (attrs.ReadOnly)
			{
				Selectable[] componentsInChildren = gameObject.GetComponentsInChildren<Selectable>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					componentsInChildren[i].interactable = false;
				}
			}
			else
			{
				if (!flag || attrs.HideDefault)
				{
					return;
				}
				rowSync.AddReset(Label(gameObject), def, delegate
				{
					try
					{
						entry.BoxedValue = def;
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)$"{entry.Definition}: couldn't reset ({ex.Message})");
					}
				});
			}
		}

		private static bool TryDefault(ConfigEntryBase entry, Attrs attrs, out object def)
		{
			def = attrs.Default;
			Type settingType = entry.SettingType;
			if (def == null)
			{
				return false;
			}
			if (settingType.IsInstanceOfType(def))
			{
				return true;
			}
			try
			{
				def = ((!settingType.IsEnum) ? Convert.ChangeType(def, settingType, Inv) : ((def is string value) ? Enum.Parse(settingType, value) : Enum.ToObject(settingType, def)));
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static string Format(Attrs attrs, object v)
		{
			if (attrs.ObjToStr != null)
			{
				try
				{
					return attrs.ObjToStr(v);
				}
				catch (Exception)
				{
				}
			}
			return Show(v);
		}

		private Action Toggle(RectTransform content, ConfigEntryBase entry, string name, string hint)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			Toggle t = Control<Toggle>(Row(_toggle, content, name, hint).transform);
			t.onValueChanged = new ToggleEvent();
			Action obj = delegate
			{
				t.SetIsOnWithoutNotify((bool)entry.BoxedValue);
			};
			obj();
			((UnityEvent<bool>)(object)t.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
			{
				entry.BoxedValue = v;
			});
			return obj;
		}

		private Action Choice(RectTransform content, ConfigEntryBase entry, string name, string hint, Attrs attrs, List<object> values)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			TMP_Dropdown d = Control<TMP_Dropdown>(Row(_dropdown, content, name, hint).transform);
			d.onValueChanged = new DropdownEvent();
			d.ClearOptions();
			d.AddOptions(values.Select((object v) => Format(attrs, v)).ToList());
			Action obj = delegate
			{
				d.SetValueWithoutNotify(Math.Max(0, values.FindIndex((object v) => object.Equals(v, entry.BoxedValue))));
				d.RefreshShownValue();
			};
			obj();
			((UnityEvent<int>)(object)d.onValueChanged).AddListener((UnityAction<int>)delegate(int i)
			{
				entry.BoxedValue = values[i];
			});
			return obj;
		}

		private Action Slide(RectTransform content, ConfigEntryBase entry, string name, string hint, Attrs attrs, AcceptableValueBase range)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			GameObject val = Row(_slider, content, name, hint);
			Slider s = Control<Slider>(val.transform);
			TMP_Text label = Label(val);
			Type type = entry.SettingType;
			s.onValueChanged = new SliderEvent();
			s.wholeNumbers = type != typeof(float) && type != typeof(double) && type != typeof(decimal);
			s.minValue = Convert.ToSingle(((object)range).GetType().GetProperty("MinValue").GetValue(range), Inv);
			s.maxValue = Convert.ToSingle(((object)range).GetType().GetProperty("MaxValue").GetValue(range), Inv);
			Func<object, string> show = (object v) => (attrs.Percent && !(s.maxValue <= s.minValue)) ? (Mathf.RoundToInt((Convert.ToSingle(v, Inv) - s.minValue) / (s.maxValue - s.minValue) * 100f) + "%") : Format(attrs, v);
			Action obj = delegate
			{
				s.SetValueWithoutNotify(Convert.ToSingle(entry.BoxedValue, Inv));
				label.text = Named(name, show(entry.BoxedValue));
			};
			obj();
			((UnityEvent<float>)(object)s.onValueChanged).AddListener((UnityAction<float>)delegate(float v)
			{
				entry.BoxedValue = Convert.ChangeType(s.wholeNumbers ? Math.Round(v) : ((double)v), type, Inv);
				label.text = Named(name, show(entry.BoxedValue));
			});
			return obj;
		}

		private Action Text(RectTransform content, ConfigEntryBase entry, string name, string hint, Attrs attrs)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: 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_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			TMP_Dropdown obj = Control<TMP_Dropdown>(Row(_dropdown, content, name, hint).transform);
			GameObject gameObject = ((Component)obj).gameObject;
			TMP_Text component = ((Component)gameObject.transform.Find("Label")).GetComponent<TMP_Text>();
			Object.DestroyImmediate((Object)(object)obj);
			string[] array = new string[2] { "Arrow", "Template" };
			foreach (string text in array)
			{
				Transform val = gameObject.transform.Find(text);
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
				}
			}
			RectTransform component2 = new GameObject("Text Area", new Type[2]
			{
				typeof(RectTransform),
				typeof(RectMask2D)
			}).GetComponent<RectTransform>();
			((Transform)component2).SetParent(gameObject.transform, false);
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = new Vector2(14f, 4f);
			component2.offsetMax = new Vector2(-14f, -4f);
			RectTransform val2 = (RectTransform)component.transform;
			((Transform)val2).SetParent((Transform)(object)component2, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			Vector2 offsetMin = (val2.offsetMax = Vector2.zero);
			val2.offsetMin = offsetMin;
			bool custom = attrs.ObjToStr != null && attrs.StrToObj != null;
			Func<string> current = () => (!custom) ? entry.GetSerializedValue() : Format(attrs, entry.BoxedValue);
			gameObject.SetActive(false);
			TMP_InputField input = gameObject.AddComponent<TMP_InputField>();
			input.textViewport = component2;
			input.textComponent = component;
			((Selectable)input).targetGraphic = (Graphic)(object)gameObject.GetComponent<Image>();
			input.fontAsset = component.font;
			input.pointSize = component.fontSize;
			input.caretColor = ((Graphic)component).color;
			input.lineType = (LineType)0;
			input.SetTextWithoutNotify(current());
			component.text = current();
			gameObject.SetActive(true);
			((UnityEvent<string>)(object)input.onEndEdit).AddListener((UnityAction<string>)delegate(string s)
			{
				try
				{
					if (custom)
					{
						entry.BoxedValue = attrs.StrToObj(s);
					}
					else
					{
						entry.SetSerializedValue(s);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)$"{entry.Definition}: typed value rejected ({ex.GetType().Name})");
				}
				input.SetTextWithoutNotify(current());
			});
			return delegate
			{
				if (!input.isFocused)
				{
					input.SetTextWithoutNotify(current());
				}
			};
		}

		private static string Named(string name, string value)
		{
			return name + " <alpha=#99>" + value;
		}

		private static string Show(object v)
		{
			if (!(v is float num))
			{
				if (!(v is double num2))
				{
					return Convert.ToString(v, Inv);
				}
				return num2.ToString("0.##", Inv);
			}
			return num.ToString("0.##", Inv);
		}

		private static bool IsNumber(Type t)
		{
			if (!(t == typeof(int)) && !(t == typeof(float)) && !(t == typeof(double)) && !(t == typeof(long)) && !(t == typeof(short)) && !(t == typeof(byte)) && !(t == typeof(uint)) && !(t == typeof(ulong)))
			{
				return t == typeof(decimal);
			}
			return true;
		}

		private static bool IsGeneric(AcceptableValueBase a, Type def)
		{
			if (a != null && ((object)a).GetType().IsGenericType)
			{
				return ((object)a).GetType().GetGenericTypeDefinition() == def;
			}
			return false;
		}

		private static bool IsRange(AcceptableValueBase a)
		{
			return IsGeneric(a, typeof(AcceptableValueRange<>));
		}

		private static bool IsList(AcceptableValueBase a)
		{
			return IsGeneric(a, typeof(AcceptableValueList<>));
		}

		private static List<object> ListOf(AcceptableValueBase a)
		{
			return ((IEnumerable)((object)a).GetType().GetProperty("AcceptableValues").GetValue(a)).Cast<object>().ToList();
		}
	}
	internal sealed class RowSync : MonoBehaviour
	{
		private ConfigEntryBase _entry;

		private Action _sync;

		private object _last;

		private object _default;

		private TMP_Text _label;

		private TMP_Text _reset;

		private string _placedFor;

		internal void Init(ConfigEntryBase entry, Action sync)
		{
			_entry = entry;
			_sync = sync;
			_last = entry.BoxedValue;
		}

		internal void AddReset(TMP_Text label, object def, Action reset)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			_label = label;
			_default = def;
			RectTransform val = (RectTransform)Object.Instantiate<GameObject>(((Component)label).gameObject, label.transform, false).transform;
			((Object)val).name = "Reset";
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 0.5f);
			val.pivot = val2;
			Vector2 anchorMin = (val.anchorMax = val2);
			val.anchorMin = anchorMin;
			_reset = ((Component)val).GetComponent<TMP_Text>();
			_reset.text = "RESET";
			_reset.enableAutoSizing = false;
			_reset.fontSize = label.fontSize * 0.7f;
			_reset.textWrappingMode = (TextWrappingModes)0;
			_reset.alignment = (TextAlignmentOptions)4097;
			((Graphic)_reset).raycastTarget = true;
			float num = _reset.preferredWidth + 8f;
			Rect rect = label.rectTransform.rect;
			val.sizeDelta = new Vector2(num, ((Rect)(ref rect)).height);
			((Component)val).gameObject.SetActive(false);
			ResetLink resetLink = ((Component)val).gameObject.AddComponent<ResetLink>();
			resetLink.Text = _reset;
			resetLink.Action = reset;
		}

		private void Update()
		{
			if (_entry == null)
			{
				return;
			}
			object boxedValue = _entry.BoxedValue;
			if (!object.Equals(boxedValue, _last))
			{
				_last = boxedValue;
				try
				{
					_sync();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)$"{_entry.Definition}: couldn't show the new value ({ex.Message})");
				}
			}
			if (!((Object)(object)_reset == (Object)null))
			{
				bool flag = !object.Equals(boxedValue, _default);
				if (((Component)_reset).gameObject.activeSelf != flag)
				{
					((Component)_reset).gameObject.SetActive(flag);
				}
				if (flag && _label.text != _placedFor)
				{
					Place();
				}
			}
		}

		private void Place()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			_placedFor = _label.text;
			RectTransform rectTransform = _reset.rectTransform;
			Rect rect = _label.rectTransform.rect;
			float num = Mathf.Max(0f, ((Rect)(ref rect)).width - rectTransform.sizeDelta.x);
			rectTransform.anchoredPosition = new Vector2(Mathf.Min(_label.preferredWidth + 18f, num), 0f);
		}
	}
	internal sealed class ResetLink : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, IPointerEnterHandler, IPointerExitHandler
	{
		private const float Idle = 0.45f;

		internal TMP_Text Text;

		internal Action Action;

		private void OnEnable()
		{
			Text.alpha = 0.45f;
		}

		public void OnPointerEnter(PointerEventData e)
		{
			Text.alpha = 1f;
		}

		public void OnPointerExit(PointerEventData e)
		{
			Text.alpha = 0.45f;
		}

		public void OnPointerClick(PointerEventData e)
		{
			Action?.Invoke();
		}
	}
}