Decompiled source of LittleLooneys LocalPerformance v0.3.1

Mods\ScheduleI.LocalPerformance.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.UI.MainMenu;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using ScheduleILocalPerformance;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "LittleLooney's Local Performance", "0.3.1", "LittleLooney", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("ScheduleI.LocalPerformance")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("ScheduleI.LocalPerformance")]
[assembly: AssemblyTitle("ScheduleI.LocalPerformance")]
[assembly: AssemblyVersion("0.3.1.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ScheduleILocalPerformance
{
	internal static class GraphicsTuning
	{
		private static UniversalRenderPipelineAsset? asset;

		private static readonly OwnedFloat scale = new OwnedFloat();

		private static readonly OwnedFloat shadows = new OwnedFloat();

		private static readonly OwnedFloat lod = new OwnedFloat();

		private static float nextPoll;

		private static bool hasOverrides;

		internal static string Status { get; private set; } = "Original graphics";

		internal static void Tick(bool active)
		{
			Mod instance = Mod.Instance;
			if (!active || !instance.graphics.Value)
			{
				Restore();
			}
			else
			{
				if (Time.unscaledTime < nextPoll)
				{
					return;
				}
				nextPoll = Time.unscaledTime + 0.5f;
				RenderPipelineAsset currentRenderPipeline = GraphicsSettings.currentRenderPipeline;
				UniversalRenderPipelineAsset val = ((currentRenderPipeline != null) ? ((Il2CppObjectBase)currentRenderPipeline).TryCast<UniversalRenderPipelineAsset>() : null);
				if ((Object)(object)val != (Object)(object)asset)
				{
					Restore();
					asset = val;
				}
				if ((Object)(object)asset == (Object)null)
				{
					Status = "URP not detected; graphics overrides skipped";
					return;
				}
				hasOverrides = true;
				float num = scale.Apply(asset.renderScale, RenderPolicy.Scale(instance.scale.Value));
				float num2 = shadows.Apply(asset.shadowDistance, RenderPolicy.ShadowDistance(instance.shadows.Value));
				float num3 = lod.Apply(QualitySettings.lodBias, RenderPolicy.Lod(instance.lod.Value));
				bool num4 = asset.renderScale != num || asset.shadowDistance != num2 || QualitySettings.lodBias != num3;
				asset.renderScale = num;
				asset.shadowDistance = num2;
				QualitySettings.lodBias = num3;
				Status = $"Applied: scale {num * 100f:F0}%, shadows {num2:F0} m, LOD {num3:F2}";
				if (num4)
				{
					instance.Log(Status + ". Local rendering only.");
				}
			}
		}

		internal static void Refresh()
		{
			nextPoll = 0f;
		}

		internal static void Restore()
		{
			if (hasOverrides)
			{
				if ((Object)(object)asset != (Object)null)
				{
					asset.renderScale = scale.Restore(asset.renderScale);
					asset.shadowDistance = shadows.Restore(asset.shadowDistance);
				}
				else
				{
					scale.Restore(0f);
					shadows.Restore(0f);
				}
				QualitySettings.lodBias = lod.Restore(QualitySettings.lodBias);
				asset = null;
				hasOverrides = false;
				Status = "Original graphics";
			}
		}
	}
	internal static class MenuLayout
	{
		public static float TabWidth(float left, float right, int count, float gap)
		{
			if (count <= 0)
			{
				return 0f;
			}
			return Math.Max(0f, (right - left - gap * (float)(count - 1)) / (float)count);
		}

		public static float TabCenter(float left, float width, int index, float gap)
		{
			return left + width / 2f + (float)index * (width + gap);
		}
	}
	public sealed class Mod : MelonMod
	{
		internal MelonPreferences_Entry<bool> enabled;

		internal MelonPreferences_Entry<bool> logging;

		internal MelonPreferences_Entry<bool> statistics;

		internal MelonPreferences_Entry<float> distance;

		internal MelonPreferences_Entry<float> padding;

		internal MelonPreferences_Entry<string> toggle;

		internal MelonPreferences_Entry<string> excluded;

		internal MelonPreferences_Entry<bool> graphics;

		internal MelonPreferences_Entry<float> scale;

		internal MelonPreferences_Entry<float> shadows;

		internal MelonPreferences_Entry<float> lod;

		private MelonPreferences_Category category;

		private Camera? camera;

		private readonly float[] original = new float[32];

		private readonly float[] applied = new float[32];

		private readonly float[] samples = new float[1800];

		private bool hasDistances;

		private bool ownsMatrix;

		private bool originalAutomatic;

		private bool originalSpherical;

		private Matrix4x4 originalMatrix;

		private Matrix4x4 lastMatrix;

		private int sampleCount;

		private int sampleIndex;

		private float nextPoll;

		private float nextReport;

		private float nextError;

		internal static Mod Instance { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			category = MelonPreferences.CreateCategory("LittleLooneysLocalPerformance");
			enabled = category.CreateEntry<bool>("Enabled", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			distance = category.CreateEntry<float>("RenderDistanceMeters", 200f, "World render distance", "50–1000 m. Shorter existing layer limits are preserved. Does not change simulation distance.", false, false, (ValueValidator)null, (string)null);
			padding = category.CreateEntry<float>("VisibilityPaddingDegrees", 0f, "Extra culling FOV", "0–30 degrees. Set 10 for a 100-degree culling view at FOV 90. Does not change visible FOV; widening can cost performance.", false, false, (ValueValidator)null, (string)null);
			excluded = category.CreateEntry<string>("ExcludedLayers", "UI,ViewModel,Viewmodel,FirstPerson,Overlay", "Layers without a distance limit", "Comma-separated Unity layer names. The separate overlay camera is always untouched.", false, false, (ValueValidator)null, (string)null);
			toggle = category.CreateEntry<string>("ToggleKey", "F6", "Comparison toggle", "Unity Input System key name. Changes Enabled for this session.", false, false, (ValueValidator)null, (string)null);
			logging = category.CreateEntry<bool>("EnableLogging", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null);
			statistics = category.CreateEntry<bool>("LogFrameStatistics", false, "Frame-time reports", "Reports the most recent 1800 frames every 30 seconds. Resets when toggled or settings change.", false, false, (ValueValidator)null, (string)null);
			graphics = category.CreateEntry<bool>("EnableGraphicsOverrides", false, "Additional graphics optimization", "Opt-in local URP render-scale, shadow-distance and LOD limits. F6 restores these along with camera limits.", false, false, (ValueValidator)null, (string)null);
			scale = category.CreateEntry<float>("RenderScale", 1f, "Rendering resolution scale", "0.5–1.0. Lower values render fewer pixels and soften the image; existing lower scales are preserved.", false, false, (ValueValidator)null, (string)null);
			shadows = category.CreateEntry<float>("ShadowDistanceMeters", 75f, "Maximum shadow distance", "0–300 m. Zero removes distance-based realtime shadows; existing shorter limits are preserved.", false, false, (ValueValidator)null, (string)null);
			lod = category.CreateEntry<float>("LODBias", 1f, "Maximum LOD bias", "0.25–1.0. Smaller values select simpler existing LOD meshes earlier. Only affects objects with LOD groups.", false, false, (ValueValidator)null, (string)null);
			if (logging.Value)
			{
				MelonLogger.Msg(ConsoleColor.Magenta, "+----------------------------------------+");
				MelonLogger.Msg(ConsoleColor.Cyan, "|          LittleLooney's Mods           |");
				MelonLogger.Msg(ConsoleColor.Magenta, "|           LOCAL PERFORMANCE            |");
				MelonLogger.Msg(ConsoleColor.Cyan, "+----------------------------------------+");
			}
			Log("Experimental 0.3.1: Settings > Performance includes General and Graphics pages with hover help. F6 toggles by default.");
		}

		public override void OnUpdate()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PerformanceMenu.Tick();
				if (Enum.TryParse<Key>(toggle.Value, ignoreCase: true, out Key result) && (int)result != 0 && Keyboard.current != null && ((ButtonControl)Keyboard.current[result]).wasPressedThisFrame)
				{
					enabled.Value = !enabled.Value;
					nextPoll = 0f;
					ResetStatistics();
					Log($"Comparison toggle: enabled={enabled.Value}.");
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		public override void OnLateUpdate()
		{
			try
			{
				LoadManager instance = Singleton<LoadManager>.Instance;
				object obj;
				if (!enabled.Value || !((Object)(object)instance != (Object)null) || !instance.IsGameLoaded || instance.IsLoading)
				{
					obj = null;
				}
				else
				{
					PlayerCamera instance2 = PlayerSingleton<PlayerCamera>.Instance;
					obj = ((instance2 != null) ? instance2.Camera : null);
				}
				Camera val = (Camera)obj;
				if ((Object)(object)camera != (Object)(object)val)
				{
					Restore();
					camera = val;
					nextPoll = 0f;
					ResetStatistics();
				}
				if ((Object)(object)camera != (Object)null && Time.unscaledTime >= nextPoll)
				{
					nextPoll = Time.unscaledTime + 0.5f;
					ApplyDistances();
				}
				if ((Object)(object)camera != (Object)null)
				{
					ApplyPadding();
				}
				GraphicsTuning.Tick((Object)(object)camera != (Object)null);
				if (!statistics.Value || !((Object)(object)instance != (Object)null) || !instance.IsGameLoaded || instance.IsLoading)
				{
					return;
				}
				samples[sampleIndex] = Time.unscaledDeltaTime * 1000f;
				sampleIndex = (sampleIndex + 1) % samples.Length;
				sampleCount = Math.Min(sampleCount + 1, samples.Length);
				if (Time.unscaledTime >= nextReport)
				{
					nextReport = Time.unscaledTime + 30f;
					float num = 0f;
					for (int i = 0; i < sampleCount; i++)
					{
						num += samples[i];
					}
					Log($"Frame window: enabled={enabled.Value}, frames={sampleCount}, meanFPS={((num > 0f) ? ((float)(sampleCount * 1000) / num) : 0f):F1}, p95FrameMs={RenderPolicy.Percentile95(samples, sampleCount):F2}, radius={RenderPolicy.Distance(distance.Value):F0}m, padding={RenderPolicy.Padding(padding.Value):F0}deg. Includes CPU/GPU stalls, not a GPU-only measurement.");
				}
			}
			catch (Exception error)
			{
				Fail(error);
				Restore();
				GraphicsTuning.Restore();
			}
		}

		private void ApplyDistances()
		{
			Il2CppStructArray<float> layerCullDistances = camera.layerCullDistances;
			if (!hasDistances)
			{
				originalSpherical = camera.layerCullSpherical;
			}
			string[] source = excluded.Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
			float[] array = new float[32];
			bool flag = !hasDistances;
			for (int i = 0; i < 32; i++)
			{
				if (!hasDistances || ((Il2CppArrayBase<float>)(object)layerCullDistances)[i] != applied[i])
				{
					original[i] = ((Il2CppArrayBase<float>)(object)layerCullDistances)[i];
				}
				string name = LayerMask.LayerToName(i);
				bool flag2 = (camera.cullingMask & (1 << i)) == 0 || source.Any((string item) => item.Equals(name, StringComparison.OrdinalIgnoreCase));
				array[i] = (flag2 ? original[i] : RenderPolicy.CullDistance(original[i], distance.Value));
				flag |= array[i] != applied[i];
				applied[i] = array[i];
			}
			if (flag)
			{
				camera.layerCullDistances = Il2CppStructArray<float>.op_Implicit(array);
				camera.layerCullSpherical = true;
				ResetStatistics();
				Log($"Camera '{((Object)camera).name}': distance={RenderPolicy.Distance(distance.Value):F0}m, culling mask=0x{camera.cullingMask:X8}, excluded='{excluded.Value}', spherical={camera.layerCullSpherical}. Overlay camera unchanged.");
			}
			hasDistances = true;
		}

		private void ApplyPadding()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			float num = RenderPolicy.Padding(padding.Value);
			if (num == 0f || camera.orthographic)
			{
				RestoreMatrix();
				return;
			}
			if (!ownsMatrix)
			{
				originalMatrix = camera.cullingMatrix;
				originalAutomatic = Approximately(originalMatrix, camera.projectionMatrix * camera.worldToCameraMatrix);
				if (!originalAutomatic)
				{
					return;
				}
			}
			else if (!Approximately(camera.cullingMatrix, lastMatrix))
			{
				ownsMatrix = false;
				return;
			}
			Matrix4x4 projectionMatrix = camera.projectionMatrix;
			float num2 = Mathf.Tan(camera.fieldOfView * ((float)Math.PI / 180f) * 0.5f) / Mathf.Tan(RenderPolicy.PaddedFov(camera.fieldOfView, num) * ((float)Math.PI / 180f) * 0.5f);
			projectionMatrix.m00 *= num2;
			projectionMatrix.m11 *= num2;
			lastMatrix = projectionMatrix * camera.worldToCameraMatrix;
			camera.cullingMatrix = lastMatrix;
			ownsMatrix = true;
		}

		private void RestoreMatrix()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)camera != (Object)null && ownsMatrix && Approximately(camera.cullingMatrix, lastMatrix))
			{
				if (originalAutomatic)
				{
					camera.ResetCullingMatrix();
				}
				else
				{
					camera.cullingMatrix = originalMatrix;
				}
			}
			ownsMatrix = false;
		}

		private void Restore()
		{
			if ((Object)(object)camera != (Object)null)
			{
				RestoreMatrix();
				if (hasDistances)
				{
					Il2CppStructArray<float> layerCullDistances = camera.layerCullDistances;
					float[] array = new float[32];
					for (int i = 0; i < 32; i++)
					{
						array[i] = RenderPolicy.Restore(((Il2CppArrayBase<float>)(object)layerCullDistances)[i], applied[i], original[i]);
					}
					camera.layerCullDistances = Il2CppStructArray<float>.op_Implicit(array);
					if (camera.layerCullSpherical)
					{
						camera.layerCullSpherical = originalSpherical;
					}
					Log("Restored owned world-camera distance settings.");
				}
			}
			camera = null;
			hasDistances = false;
			ownsMatrix = false;
		}

		private static bool Approximately(Matrix4x4 a, Matrix4x4 b)
		{
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					if (Mathf.Abs(((Matrix4x4)(ref a))[i, j] - ((Matrix4x4)(ref b))[i, j]) > 0.0001f)
					{
						return false;
					}
				}
			}
			return true;
		}

		internal void SettingsChanged()
		{
			nextPoll = 0f;
			GraphicsTuning.Refresh();
			ResetStatistics();
		}

		internal void BalancedPreset()
		{
			MelonPreferences_Entry<bool> obj = enabled;
			bool value = (graphics.Value = true);
			obj.Value = value;
			distance.Value = 200f;
			padding.Value = 0f;
			scale.Value = 0.85f;
			shadows.Value = 50f;
			lod.Value = 0.75f;
			SettingsChanged();
		}

		internal void SaveSettings()
		{
			category.SaveToFile(true);
			Log("Performance settings saved.");
		}

		internal void ResetDefaults()
		{
			enabled.Value = true;
			distance.Value = 200f;
			padding.Value = 0f;
			logging.Value = true;
			statistics.Value = false;
			toggle.Value = "F6";
			excluded.Value = "UI,ViewModel,Viewmodel,FirstPerson,Overlay";
			graphics.Value = false;
			scale.Value = 1f;
			shadows.Value = 75f;
			lod.Value = 1f;
			SettingsChanged();
		}

		private void ResetStatistics()
		{
			sampleCount = (sampleIndex = 0);
			nextReport = Time.unscaledTime + 30f;
		}

		internal void Log(string message)
		{
			if (logging.Value)
			{
				MelonLogger.Msg("[LocalPerformance] " + message);
			}
		}

		private void Fail(Exception error)
		{
			if (logging.Value && Time.unscaledTime >= nextError)
			{
				nextError = Time.unscaledTime + 10f;
				MelonLogger.Error("[LocalPerformance] " + error);
			}
		}

		public override void OnApplicationQuit()
		{
			Restore();
			GraphicsTuning.Restore();
		}

		public override void OnDeinitializeMelon()
		{
			PerformanceMenu.Cleanup();
			Restore();
			GraphicsTuning.Restore();
		}
	}
	[HarmonyPatch(typeof(SettingsScreen), "OnEnable")]
	internal static class AddPerformanceCategory
	{
		private static void Postfix(SettingsScreen __instance)
		{
			PerformanceMenu.Ensure(__instance);
		}
	}
	internal static class PerformanceMenu
	{
		private sealed class State
		{
			internal SettingsScreen Screen;

			internal Toggle Tab;

			internal GameObject Panel;

			internal readonly List<Action> Refreshers = new List<Action>();

			internal TMP_FontAsset Font;

			internal TextMeshProUGUI Status;

			internal bool GraphicsPage;

			internal TextMeshProUGUI Help;

			internal string Hint = "";

			internal string DefaultHint = "";

			internal RectTransform? Hovered;

			internal float HoverStart;

			internal readonly List<TabRect> OriginalTabs = new List<TabRect>();

			internal Behaviour? Layout;

			internal bool LayoutEnabled;

			internal float Left;

			internal float Right;

			internal RectTransform TabParent;
		}

		private sealed class TabRect
		{
			internal RectTransform Rect;

			internal Vector2 Min;

			internal Vector2 Max;

			internal Vector2 Pivot;

			internal Vector2 Position;

			internal Vector2 Size;

			internal TextMeshProUGUI? Text;

			internal bool AutoSize;

			internal float FontMin;

			internal float FontMax;

			internal float FontSize;

			internal Vector2 TextMin;

			internal Vector2 TextMax;

			internal Vector2 TextOffsetsMin;

			internal Vector2 TextOffsetsMax;

			internal Vector2 TextPivot;

			internal TabRect(RectTransform rect)
			{
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: 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_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: 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_0038: 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_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00be: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d1: 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_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_00e9: 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)
				Rect = rect;
				Min = rect.anchorMin;
				Max = rect.anchorMax;
				Pivot = rect.pivot;
				Position = rect.anchoredPosition;
				Size = rect.sizeDelta;
				Text = ((Component)rect).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)Text != (Object)null)
				{
					AutoSize = ((TMP_Text)Text).enableAutoSizing;
					FontMin = ((TMP_Text)Text).fontSizeMin;
					FontMax = ((TMP_Text)Text).fontSizeMax;
					FontSize = ((TMP_Text)Text).fontSize;
					RectTransform rectTransform = ((TMP_Text)Text).rectTransform;
					TextMin = rectTransform.anchorMin;
					TextMax = rectTransform.anchorMax;
					TextOffsetsMin = rectTransform.offsetMin;
					TextOffsetsMax = rectTransform.offsetMax;
					TextPivot = rectTransform.pivot;
				}
			}

			internal void Restore()
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0049: Unknown result type (might be due to invalid IL or missing references)
				//IL_005a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00de: 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)
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)Rect == (Object)null))
				{
					Rect.anchorMin = Min;
					Rect.anchorMax = Max;
					Rect.pivot = Pivot;
					Rect.sizeDelta = Size;
					Rect.anchoredPosition = Position;
					if ((Object)(object)Text != (Object)null)
					{
						((TMP_Text)Text).enableAutoSizing = AutoSize;
						((TMP_Text)Text).fontSizeMin = FontMin;
						((TMP_Text)Text).fontSizeMax = FontMax;
						((TMP_Text)Text).fontSize = FontSize;
						RectTransform rectTransform = ((TMP_Text)Text).rectTransform;
						rectTransform.anchorMin = TextMin;
						rectTransform.anchorMax = TextMax;
						rectTransform.pivot = TextPivot;
						rectTransform.offsetMin = TextOffsetsMin;
						rectTransform.offsetMax = TextOffsetsMax;
					}
				}
			}
		}

		private static readonly Dictionary<IntPtr, State> states = new Dictionary<IntPtr, State>();

		private static float nextRefresh;

		private static float nextDiscovery;

		internal static void Tick()
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < nextRefresh)
			{
				return;
			}
			nextRefresh = Time.unscaledTime + 0.25f;
			KeyValuePair<IntPtr, State>[] array = states.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				KeyValuePair<IntPtr, State> keyValuePair = array[i];
				if ((Object)(object)keyValuePair.Value.Screen == (Object)null || (Object)(object)keyValuePair.Value.Panel == (Object)null)
				{
					states.Remove(keyValuePair.Key);
					continue;
				}
				if (((Behaviour)keyValuePair.Value.Screen).isActiveAndEnabled)
				{
					FitTabs(keyValuePair.Value);
				}
				if (keyValuePair.Value.Panel.activeInHierarchy)
				{
					Refresh(keyValuePair.Value);
				}
				else
				{
					keyValuePair.Value.Hovered = null;
				}
			}
			if (Time.unscaledTime < nextDiscovery)
			{
				return;
			}
			nextDiscovery = Time.unscaledTime + 2f;
			foreach (SettingsScreen item in Resources.FindObjectsOfTypeAll<SettingsScreen>())
			{
				if ((Object)(object)item != (Object)null)
				{
					Scene scene = ((Component)item).gameObject.scene;
					if (((Scene)(ref scene)).IsValid() && ((Behaviour)item).isActiveAndEnabled)
					{
						Ensure(item);
					}
				}
			}
		}

		internal static void Ensure(SettingsScreen screen)
		{
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Expected O, but got Unknown
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Expected O, but got Unknown
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0390: Expected O, but got Unknown
			//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0481: Expected O, but got Unknown
			if (states.ContainsKey(((Il2CppObjectBase)screen).Pointer))
			{
				return;
			}
			State state = null;
			try
			{
				Il2CppReferenceArray<SettingsCategory> categories = screen.Categories;
				if (categories == null || ((Il2CppArrayBase<SettingsCategory>)(object)categories).Length == 0)
				{
					return;
				}
				SettingsCategory val = ((Il2CppArrayBase<SettingsCategory>)(object)categories)[((Il2CppArrayBase<SettingsCategory>)(object)categories).Length - 1];
				if ((Object)(object)((val != null) ? val.Toggle : null) == (Object)null || (Object)(object)val.Panel == (Object)null)
				{
					return;
				}
				TextMeshProUGUI componentInChildren = ((Component)val.Toggle).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren == (Object)null)
				{
					throw new InvalidOperationException("Settings category has no TMP label to reuse.");
				}
				state = new State
				{
					Screen = screen,
					Font = ((TMP_Text)componentInChildren).font
				};
				state.TabParent = ((Il2CppObjectBase)((Component)val.Toggle).transform.parent).TryCast<RectTransform>() ?? throw new InvalidOperationException("Settings tabs need a RectTransform parent.");
				LayoutRebuilder.ForceRebuildLayoutImmediate(state.TabParent);
				Il2CppStructArray<Vector3> val2 = new Il2CppStructArray<Vector3>(4L);
				state.Left = float.PositiveInfinity;
				state.Right = float.NegativeInfinity;
				foreach (SettingsCategory item in (Il2CppArrayBase<SettingsCategory>)(object)categories)
				{
					RectTransform component = ((Component)item.Toggle).GetComponent<RectTransform>();
					state.OriginalTabs.Add(new TabRect(component));
					component.GetWorldCorners(val2);
					foreach (Vector3 item2 in (Il2CppArrayBase<Vector3>)(object)val2)
					{
						float x = ((Transform)state.TabParent).InverseTransformPoint(item2).x;
						state.Left = Math.Min(state.Left, x);
						state.Right = Math.Max(state.Right, x);
					}
				}
				State state2 = state;
				HorizontalLayoutGroup component2 = ((Component)state.TabParent).GetComponent<HorizontalLayoutGroup>();
				object obj = ((component2 != null) ? ((Il2CppObjectBase)component2).TryCast<Behaviour>() : null);
				if (obj == null)
				{
					GridLayoutGroup component3 = ((Component)state.TabParent).GetComponent<GridLayoutGroup>();
					obj = ((component3 != null) ? ((Il2CppObjectBase)component3).TryCast<Behaviour>() : null);
				}
				state2.Layout = (Behaviour?)obj;
				if ((Object)(object)state.Layout != (Object)null)
				{
					state.LayoutEnabled = state.Layout.enabled;
					state.Layout.enabled = false;
				}
				GameObject val3 = new GameObject("LocalPerformanceStaging");
				val3.SetActive(false);
				val3.transform.SetParent(((Component)val.Toggle).transform.parent, false);
				try
				{
					GameObject val4 = Object.Instantiate<GameObject>(((Component)val.Toggle).gameObject, val3.transform, false);
					((Object)val4).name = "LittleLooneyPerformanceTab";
					val4.SetActive(false);
					val4.transform.SetParent(((Component)val.Toggle).transform.parent, false);
					state.Tab = val4.GetComponent<Toggle>();
					state.Tab.onValueChanged = new ToggleEvent();
					state.Tab.SetIsOnWithoutNotify(false);
					((Selectable)state.Tab).interactable = true;
					TextMeshProUGUI componentInChildren2 = val4.GetComponentInChildren<TextMeshProUGUI>(true);
					((TMP_Text)componentInChildren2).text = "Performance";
					((TMP_Text)componentInChildren2).enableAutoSizing = true;
					((TMP_Text)componentInChildren2).fontSizeMin = 12f;
					((TMP_Text)componentInChildren2).fontSizeMax = ((TMP_Text)componentInChildren).fontSize;
				}
				finally
				{
					Object.Destroy((Object)(object)val3);
				}
				state.Panel = new GameObject("LittleLooneyPerformancePanel");
				state.Panel.SetActive(false);
				RectTransform obj2 = state.Panel.AddComponent<RectTransform>();
				RectTransform component4 = val.Panel.GetComponent<RectTransform>();
				((Transform)obj2).SetParent(((Transform)component4).parent, false);
				obj2.anchorMin = component4.anchorMin;
				obj2.anchorMax = component4.anchorMax;
				obj2.pivot = component4.pivot;
				obj2.sizeDelta = component4.sizeDelta;
				obj2.anchoredPosition = component4.anchoredPosition;
				Build(state);
				Il2CppReferenceArray<SettingsCategory> val5 = new Il2CppReferenceArray<SettingsCategory>((long)(((Il2CppArrayBase<SettingsCategory>)(object)categories).Length + 1));
				for (int i = 0; i < ((Il2CppArrayBase<SettingsCategory>)(object)categories).Length; i++)
				{
					((Il2CppArrayBase<SettingsCategory>)(object)val5)[i] = ((Il2CppArrayBase<SettingsCategory>)(object)categories)[i];
				}
				((Il2CppArrayBase<SettingsCategory>)(object)val5)[((Il2CppArrayBase<SettingsCategory>)(object)categories).Length] = new SettingsCategory
				{
					Toggle = state.Tab,
					Panel = state.Panel
				};
				screen.Categories = val5;
				State captured = state;
				((UnityEvent<bool>)(object)state.Tab.onValueChanged).AddListener(UnityAction<bool>.op_Implicit((Action<bool>)delegate(bool value)
				{
					if (value && !((Object)(object)captured.Screen == (Object)null))
					{
						Il2CppReferenceArray<SettingsCategory> categories2 = captured.Screen.Categories;
						for (int j = 0; j < ((Il2CppArrayBase<SettingsCategory>)(object)categories2).Length; j++)
						{
							bool flag = (Object)(object)((Il2CppArrayBase<SettingsCategory>)(object)categories2)[j].Panel == (Object)(object)captured.Panel;
							((Selectable)((Il2CppArrayBase<SettingsCategory>)(object)categories2)[j].Toggle).interactable = !flag;
							((Il2CppArrayBase<SettingsCategory>)(object)categories2)[j].Toggle.SetIsOnWithoutNotify(flag);
							((Il2CppArrayBase<SettingsCategory>)(object)categories2)[j].Panel.SetActive(flag);
						}
						((TMP_Text)captured.Status).text = "Changes apply now. Save to keep them after restarting.";
						Refresh(captured);
					}
				}));
				((Component)state.Tab).gameObject.SetActive(true);
				states.Add(((Il2CppObjectBase)screen).Pointer, state);
				FitTabs(state);
				Mod.Instance.Log($"Registered Performance category in settings '{((Object)screen).name}', category count={((Il2CppArrayBase<SettingsCategory>)(object)val5).Length}.");
			}
			catch (Exception ex)
			{
				if (state != null)
				{
					RestoreTabs(state);
				}
				if ((Object)(object)state?.Panel != (Object)null && screen.Categories != null)
				{
					SettingsCategory[] array = ((IEnumerable<SettingsCategory>)screen.Categories).Where((SettingsCategory category) => (Object)(object)category.Panel != (Object)(object)state.Panel).ToArray();
					screen.Categories = new Il2CppReferenceArray<SettingsCategory>(array);
				}
				if ((Object)(object)state?.Tab != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)state.Tab).gameObject);
				}
				if ((Object)(object)state?.Panel != (Object)null)
				{
					Object.Destroy((Object)(object)state.Panel);
				}
				Mod.Instance.Log("Settings category creation failed: " + ex);
			}
		}

		private static void Build(State state)
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0647: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = state.Panel.transform;
			state.Refreshers.Clear();
			state.Hovered = null;
			for (int num = transform.childCount - 1; num >= 0; num--)
			{
				GameObject gameObject = ((Component)transform.GetChild(num)).gameObject;
				gameObject.SetActive(false);
				Object.Destroy((Object)(object)gameObject);
			}
			Label(state, transform, "Performance", 0.04f, 0.7f, 0.89f, 0.99f, 27f);
			TextMeshProUGUI obj = Label(state, transform, "LittleLooney", 0.72f, 0.96f, 0.89f, 0.99f, 16f);
			((TMP_Text)obj).alignment = (TextAlignmentOptions)516;
			((Graphic)obj).color = new Color(0.65f, 0.68f, 0.72f);
			TextMeshProUGUI val = Button(state, transform, "General", 0.04f, 0.49f, 0.78f, 0.86f, delegate
			{
				state.GraphicsPage = false;
				Build(state);
			});
			TextMeshProUGUI val2 = Button(state, transform, "Graphics", 0.51f, 0.96f, 0.78f, 0.86f, delegate
			{
				state.GraphicsPage = true;
				Build(state);
			});
			((Graphic)(state.GraphicsPage ? val2 : val)).color = new Color(0.65f, 0.86f, 0.89f);
			Mod mod = Mod.Instance;
			if (state.GraphicsPage)
			{
				BooleanRow(state, "Use graphics tuning", 0.67f, () => mod.graphics.Value, delegate(bool value)
				{
					mod.graphics.Value = value;
				}, "Turns on the three settings below. The main Performance switch must also be on. Turn this off to restore your game's original graphics.");
				NumberRow(state, "Render resolution", 0.58f, () => RenderPolicy.Scale(mod.scale.Value) * 100f, delegate(float value)
				{
					mod.scale.Value = RenderPolicy.Scale(value / 100f);
				}, 5f, "%", "Lower values draw fewer pixels. This can improve FPS, but the picture looks softer. 100% keeps full resolution.");
				NumberRow(state, "Shadow range", 0.49f, () => RenderPolicy.ShadowDistance(mod.shadows.Value), delegate(float value)
				{
					mod.shadows.Value = RenderPolicy.ShadowDistance(value);
				}, 10f, "m", "How far away real-time shadows are drawn. Lower values can improve FPS. 0 turns these shadows off; baked shadows may remain.");
				NumberRow(state, "Distant detail", 0.4f, () => RenderPolicy.Lod(mod.lod.Value) * 100f, delegate(float value)
				{
					mod.lod.Value = RenderPolicy.Lod(value / 100f);
				}, 5f, "%", "Lower values show simpler models sooner. This can help FPS, but details may change as you move. Only models with simpler versions are affected.");
				TextMeshProUGUI val3 = Button(state, transform, "Balanced preset", 0.04f, 0.49f, 0.29f, 0.37f, delegate
				{
					mod.BalancedPreset();
					((TMP_Text)state.Status).text = "Balanced preset applied. Save to keep it.";
					Refresh(state);
				});
				Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val3).rectTransform).parent).TryCast<RectTransform>(), "A starting point: 200 m draw distance, 85% resolution, 50 m shadows and 75% distant detail. Turns Performance and graphics tuning on.");
				TextMeshProUGUI val4 = Button(state, transform, "Restore graphics", 0.51f, 0.96f, 0.29f, 0.37f, delegate
				{
					mod.graphics.Value = false;
					mod.SettingsChanged();
					((TMP_Text)state.Status).text = "Graphics tuning off. Draw distance unchanged.";
					Refresh(state);
				});
				Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val4).rectTransform).parent).TryCast<RectTransform>(), "Turns off graphics tuning and restores the original values. Draw distance is separate; turn Performance off to restore everything.");
				state.DefaultHint = "Start with Balanced. Lower settings trade picture quality for FPS.";
			}
			else
			{
				BooleanRow(state, "Performance", 0.67f, () => mod.enabled.Value, delegate(bool value)
				{
					mod.enabled.Value = value;
				}, "The main switch for this mod. Off restores draw distance and graphics tuning. This only changes your graphics, not other players' games.");
				NumberRow(state, "Draw distance", 0.58f, () => RenderPolicy.Distance(mod.distance.Value), delegate(float value)
				{
					mod.distance.Value = RenderPolicy.Distance(value);
				}, 25f, "m", "How far away objects are drawn. A shorter distance can help FPS, but distant scenery may disappear. People, deals and saves keep working.");
				NumberRow(state, "View buffer", 0.49f, () => RenderPolicy.Padding(mod.padding.Value), delegate(float value)
				{
					mod.padding.Value = RenderPolicy.Padding(value);
				}, 5f, "°", "Keeps extra objects near the edge of your view eligible to draw. Your visible field of view does not change. Leave at 0 for best performance.");
				BooleanRow(state, "Diagnostic logs", 0.4f, () => mod.logging.Value, delegate(bool value)
				{
					mod.logging.Value = value;
				}, "Writes settings changes and errors to the MelonLoader log. Enable this when reporting a problem.");
				BooleanRow(state, "FPS reports", 0.31f, () => mod.statistics.Value, delegate(bool value)
				{
					mod.statistics.Value = value;
				}, "Writes average FPS and slow-frame times to the log every 30 seconds. Diagnostic logs must be on. Walk the same route with Performance on and off to compare.");
				TextMeshProUGUI val5 = Label(state, transform, "Comparison key", 0.06f, 0.61f, 0.22f, 0.295f, 20f);
				TextMeshProUGUI key = Button(state, transform, "", 0.65f, 0.94f, 0.22f, 0.295f, delegate
				{
					string[] array = new string[5] { "F6", "F7", "F8", "F9", "None" };
					int num2 = Array.FindIndex(array, (string value) => value.Equals(mod.toggle.Value, StringComparison.OrdinalIgnoreCase));
					mod.toggle.Value = array[(num2 + 1) % array.Length];
					mod.SettingsChanged();
					Refresh(state);
				});
				state.Refreshers.Add(delegate
				{
					((TMP_Text)key).text = ((mod.toggle.Value == "None") ? "Disabled" : mod.toggle.Value);
				});
				string text = "Press this key to switch Performance on or off without opening Settings. Click to choose another key.";
				Tip(state, ((TMP_Text)val5).rectTransform, text);
				Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)key).rectTransform).parent).TryCast<RectTransform>(), text);
				state.DefaultHint = "Changes apply right away. Hover over a setting to learn what it does.";
			}
			RectTransform val6 = Rect("Help", transform, 0.04f, 0.96f, 0.105f, 0.2f);
			Image obj2 = ((Component)val6).gameObject.AddComponent<Image>();
			((Graphic)obj2).color = new Color(1f, 1f, 1f, 0.035f);
			((Graphic)obj2).raycastTarget = false;
			state.Help = Label(state, (Transform)(object)val6, state.DefaultHint, 0.025f, 0.975f, 0.07f, 0.93f, 16f);
			((Graphic)state.Help).color = new Color(0.78f, 0.81f, 0.84f);
			state.Status = Label(state, transform, "Changes apply now. Save to keep them after restarting.", 0.04f, 0.96f, 0.065f, 0.1f, 13f);
			TextMeshProUGUI val7 = Button(state, transform, "Reset defaults", 0.04f, 0.49f, 0f, 0.06f, delegate
			{
				mod.ResetDefaults();
				((TMP_Text)state.Status).text = "Defaults applied. Save to keep them.";
				Refresh(state);
			});
			Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val7).rectTransform).parent).TryCast<RectTransform>(), "Resets this mod to its defaults. Graphics tuning is turned off. Your saved preferences change only when you click Save.");
			TextMeshProUGUI val8 = Button(state, transform, "Save settings", 0.51f, 0.96f, 0f, 0.06f, delegate
			{
				try
				{
					mod.SaveSettings();
					((TMP_Text)state.Status).text = "Settings saved.";
				}
				catch (Exception ex)
				{
					((TMP_Text)state.Status).text = "Could not save settings. Check the MelonLoader log.";
					mod.Log("Preference save failed: " + ex);
				}
			});
			Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val8).rectTransform).parent).TryCast<RectTransform>(), "Keeps your current Performance settings for the next time you start the game.");
			Refresh(state);
		}

		private static void Row(State state, string title, float y, string hint)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect("SettingRow", state.Panel.transform, 0.04f, 0.96f, y, y + 0.075f);
			((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = new Color(1f, 1f, 1f, 0.025f);
			Tip(state, val, hint);
			TextMeshProUGUI val2 = Label(state, state.Panel.transform, title, 0.06f, 0.53f, y, y + 0.075f, 20f);
			Tip(state, ((TMP_Text)val2).rectTransform, hint);
		}

		private static void BooleanRow(State state, string title, float y, Func<bool> read, Action<bool> write, string hint)
		{
			Row(state, title, y, hint);
			TextMeshProUGUI value = Button(state, state.Panel.transform, "", 0.65f, 0.94f, y + 0.007f, y + 0.068f, delegate
			{
				write(!read());
				Mod.Instance.SettingsChanged();
				Refresh(state);
			});
			Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)value).rectTransform).parent).TryCast<RectTransform>(), hint);
			state.Refreshers.Add(delegate
			{
				//IL_005c: 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)
				((TMP_Text)value).text = (read() ? "On" : "Off");
				((Graphic)value).color = (read() ? new Color(0.65f, 0.86f, 0.89f) : new Color(0.72f, 0.74f, 0.77f));
			});
		}

		private static void NumberRow(State state, string title, float y, Func<float> read, Action<float> write, float step, string unit, string hint)
		{
			Row(state, title, y, hint);
			TextMeshProUGUI value = Label(state, state.Panel.transform, "", 0.64f, 0.86f, y, y + 0.075f, 19f);
			((TMP_Text)value).alignment = (TextAlignmentOptions)514;
			TextMeshProUGUI val = Button(state, state.Panel.transform, "−", 0.56f, 0.64f, y + 0.007f, y + 0.068f, delegate
			{
				Change(-1f);
			});
			TextMeshProUGUI val2 = Button(state, state.Panel.transform, "+", 0.86f, 0.94f, y + 0.007f, y + 0.068f, delegate
			{
				Change(1f);
			});
			Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val).rectTransform).parent).TryCast<RectTransform>(), hint);
			Tip(state, ((Il2CppObjectBase)((Transform)((TMP_Text)val2).rectTransform).parent).TryCast<RectTransform>(), hint);
			Tip(state, ((TMP_Text)value).rectTransform, hint);
			state.Refreshers.Add(delegate
			{
				((TMP_Text)value).text = $"{read():F0}{((unit == "%" || unit == "°") ? "" : " ")}{unit}";
			});
			void Change(float direction)
			{
				write(read() + direction * step);
				Mod.Instance.SettingsChanged();
				Refresh(state);
			}
		}

		private static void Tip(State state, RectTransform? rect, string text)
		{
			if ((Object)(object)rect == (Object)null)
			{
				return;
			}
			Graphic component = ((Component)rect).GetComponent<Graphic>();
			if ((Object)(object)component != (Object)null)
			{
				component.raycastTarget = true;
			}
			EventTrigger trigger = ((Component)rect).gameObject.AddComponent<EventTrigger>();
			Bind((EventTriggerType)0, delegate
			{
				state.Hovered = rect;
				state.Hint = text;
				state.HoverStart = Time.unscaledTime;
			});
			Bind((EventTriggerType)1, delegate
			{
				if ((Object)(object)state.Hovered == (Object)(object)rect)
				{
					state.Hovered = null;
				}
			});
			void Bind(EventTriggerType type, Action action)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Expected O, but got Unknown
				Entry val = new Entry
				{
					eventID = type
				};
				((UnityEvent<BaseEventData>)(object)val.callback).AddListener(UnityAction<BaseEventData>.op_Implicit((Action<BaseEventData>)delegate
				{
					action();
				}));
				trigger.triggers.Add(val);
			}
		}

		private static void FitTabs(State state)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)state.Tab == (Object)null || (Object)(object)state.TabParent == (Object)null || state.OriginalTabs.Count == 0)
			{
				return;
			}
			RectTransform[] array = (from val3 in state.OriginalTabs.Select((TabRect saved) => saved.Rect).Append(((Component)state.Tab).GetComponent<RectTransform>())
				where (Object)(object)val3 != (Object)null
				select val3).ToArray();
			float left = state.Left;
			Rect rect = state.TabParent.rect;
			float num = Math.Max(left, ((Rect)(ref rect)).xMin + 4f);
			float right = state.Right;
			rect = state.TabParent.rect;
			float num2 = Math.Min(right, ((Rect)(ref rect)).xMax - 4f);
			if (num2 - num < 100f)
			{
				rect = state.TabParent.rect;
				num = ((Rect)(ref rect)).xMin + 4f;
				rect = state.TabParent.rect;
				num2 = ((Rect)(ref rect)).xMax - 4f;
			}
			if (num2 - num < 100f)
			{
				return;
			}
			float gap = 4f;
			float num3 = MenuLayout.TabWidth(num, num2, array.Length, gap);
			Vector2 val2 = default(Vector2);
			for (int num4 = 0; num4 < array.Length; num4++)
			{
				RectTransform val = array[num4];
				float y = ((Transform)val).localPosition.y;
				rect = val.rect;
				float height = ((Rect)(ref rect)).height;
				((Vector2)(ref val2))..ctor(0.5f, 0.5f);
				val.anchorMax = val2;
				val.anchorMin = val2;
				val.pivot = new Vector2(0.5f, 0.5f);
				val.sizeDelta = new Vector2(num3, height);
				float num5 = MenuLayout.TabCenter(num, num3, num4, gap);
				rect = state.TabParent.rect;
				float num6 = num5 - ((Rect)(ref rect)).center.x;
				rect = state.TabParent.rect;
				val.anchoredPosition = new Vector2(num6, y - ((Rect)(ref rect)).center.y);
				TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					((TMP_Text)componentInChildren).enableAutoSizing = true;
					((TMP_Text)componentInChildren).fontSizeMin = 10f;
					((TMP_Text)componentInChildren).fontSizeMax = 22f;
					RectTransform rectTransform = ((TMP_Text)componentInChildren).rectTransform;
					if ((Object)(object)rectTransform != (Object)(object)val)
					{
						rectTransform.anchorMin = Vector2.zero;
						rectTransform.anchorMax = Vector2.one;
						rectTransform.offsetMin = new Vector2(5f, 2f);
						rectTransform.offsetMax = new Vector2(-5f, -2f);
					}
				}
			}
		}

		private static void RestoreTabs(State state)
		{
			foreach (TabRect originalTab in state.OriginalTabs)
			{
				originalTab.Restore();
			}
			if ((Object)(object)state.Layout != (Object)null)
			{
				state.Layout.enabled = state.LayoutEnabled;
			}
		}

		private static RectTransform Rect(string name, Transform parent, float left, float right, float bottom, float top)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = new GameObject(name).AddComponent<RectTransform>();
			((Transform)obj).SetParent(parent, false);
			obj.anchorMin = new Vector2(left, bottom);
			obj.anchorMax = new Vector2(right, top);
			Vector2 offsetMin = (obj.offsetMax = Vector2.zero);
			obj.offsetMin = offsetMin;
			return obj;
		}

		private static TextMeshProUGUI Label(State state, Transform parent, string text, float left, float right, float bottom, float top, float size)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI obj = ((Component)Rect("Label", parent, left, right, bottom, top)).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).font = state.Font;
			((TMP_Text)obj).text = text;
			((Graphic)obj).color = Color.white;
			((TMP_Text)obj).fontSize = size;
			((TMP_Text)obj).enableAutoSizing = true;
			((TMP_Text)obj).fontSizeMin = 10f;
			((TMP_Text)obj).fontSizeMax = size;
			((TMP_Text)obj).alignment = (TextAlignmentOptions)4097;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static TextMeshProUGUI Button(State state, Transform parent, string text, float left, float right, float bottom, float top, Action action)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect("Control", parent, left, right, bottom, top);
			Image val2 = ((Component)val).gameObject.AddComponent<Image>();
			((Graphic)val2).color = new Color(0.18f, 0.19f, 0.21f, 0.95f);
			Button obj = ((Component)val).gameObject.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)val2;
			((UnityEvent)obj.onClick).AddListener(UnityAction.op_Implicit((Action)delegate
			{
				try
				{
					action();
				}
				catch (Exception ex)
				{
					Mod.Instance.Log("Settings control failed: " + ex);
				}
			}));
			TextMeshProUGUI obj2 = Label(state, (Transform)(object)val, text, 0.04f, 0.96f, 0.02f, 0.98f, 20f);
			((TMP_Text)obj2).alignment = (TextAlignmentOptions)514;
			return obj2;
		}

		private static void Refresh(State state)
		{
			foreach (Action refresher in state.Refreshers)
			{
				refresher();
			}
			if ((Object)(object)state.Help != (Object)null)
			{
				((TMP_Text)state.Help).text = (((Object)(object)state.Hovered != (Object)null && Time.unscaledTime - state.HoverStart >= 0.35f) ? state.Hint : state.DefaultHint);
			}
		}

		internal static void Cleanup()
		{
			foreach (State state in states.Values)
			{
				RestoreTabs(state);
				if ((Object)(object)state.Screen != (Object)null && state.Screen.Categories != null)
				{
					SettingsCategory[] array = ((IEnumerable<SettingsCategory>)state.Screen.Categories).Where((SettingsCategory category) => (Object)(object)category.Panel != (Object)(object)state.Panel).ToArray();
					state.Screen.Categories = new Il2CppReferenceArray<SettingsCategory>(array);
					if (array.Length != 0)
					{
						state.Screen.ShowCategory(0);
					}
				}
				if ((Object)(object)state.Tab != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)state.Tab).gameObject);
				}
				if ((Object)(object)state.Panel != (Object)null)
				{
					Object.Destroy((Object)(object)state.Panel);
				}
			}
			states.Clear();
		}
	}
	internal static class RenderPolicy
	{
		public static float Scale(float value)
		{
			if (!float.IsFinite(value))
			{
				return 1f;
			}
			return Math.Clamp(value, 0.5f, 1f);
		}

		public static float ShadowDistance(float value)
		{
			if (!float.IsFinite(value))
			{
				return 75f;
			}
			return Math.Clamp(value, 0f, 300f);
		}

		public static float Lod(float value)
		{
			if (!float.IsFinite(value))
			{
				return 1f;
			}
			return Math.Clamp(value, 0.25f, 1f);
		}

		public static float Distance(float value)
		{
			if (!float.IsFinite(value))
			{
				return 200f;
			}
			return Math.Clamp(value, 50f, 1000f);
		}

		public static float Padding(float value)
		{
			if (!float.IsFinite(value))
			{
				return 0f;
			}
			return Math.Clamp(value, 0f, 30f);
		}

		public static float CullDistance(float original, float distance)
		{
			if (!(original > 0f))
			{
				return Distance(distance);
			}
			return Math.Min(original, Distance(distance));
		}

		public static float PaddedFov(float fov, float padding)
		{
			return Math.Clamp(fov + Padding(padding), 1f, 170f);
		}

		public static float Restore(float current, float applied, float original)
		{
			if (current != applied)
			{
				return current;
			}
			return original;
		}

		public static float Percentile95(float[] samples, int count)
		{
			if (count == 0)
			{
				return 0f;
			}
			float[] array = new float[count];
			Array.Copy(samples, array, count);
			Array.Sort(array);
			return array[(int)Math.Ceiling((double)count * 0.95) - 1];
		}
	}
	internal sealed class OwnedFloat
	{
		private bool owned;

		private float original;

		private float applied;

		public float Apply(float current, float desired)
		{
			if (!owned || current != applied)
			{
				original = current;
			}
			applied = Math.Min(original, desired);
			owned = true;
			return applied;
		}

		public float Restore(float current)
		{
			float result = (owned ? RenderPolicy.Restore(current, applied, original) : current);
			owned = false;
			return result;
		}
	}
}