Decompiled source of MoreOptimization v1.0.1

plugins/MoreOptimization.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")]
[assembly: AssemblyCompany("MoreOptimization")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("More Graphics Settings for Gamble With Your Friends")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("MoreOptimization")]
[assembly: AssemblyTitle("MoreOptimization")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MoreGraphicsSettings
{
	[BepInPlugin("com.ananas1k.gwyf.moreoptimization", "More Optimization", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		private const string PluginGuid = "com.ananas1k.gwyf.moreoptimization";

		private const string PluginName = "More Optimization";

		private const string PluginVersion = "1.0.1";

		private const float TextureLimitSceneDelayFallback = 1.75f;

		private const float TextureLimitEnforcementDurationFallback = 10f;

		private const float TextureLimitEnforcementInterval = 0.5f;

		private const float ContinuousQualityEnforcementInterval = 1f;

		private const int TextureCopyFixLogLimit = 5;

		public static ConfigEntry<int> ConfigGraphicsPreset;

		public static ConfigEntry<int> ConfigRenderScale;

		public static ConfigEntry<int> ConfigTextureResolution;

		public static ConfigEntry<int> ConfigShadows;

		public static ConfigEntry<int> ConfigVSync;

		public static ConfigEntry<int> ConfigAntiAliasing;

		public static ConfigEntry<int> ConfigTargetFPS;

		public static ConfigEntry<int> ConfigLodBias;

		public static ConfigEntry<int> ConfigAnisotropicFiltering;

		public static ConfigEntry<int> ConfigReflectionProbes;

		public static ConfigEntry<int> ConfigSoftParticles;

		public static ConfigEntry<int> ConfigBackgroundFpsLimit;

		public static ConfigEntry<int> ConfigLocalizationLanguage;

		private static ConfigEntry<float> ConfigTextureLimitDelay;

		private static ConfigEntry<float> ConfigTextureLimitEnforcementDuration;

		private static ConfigEntry<int> ConfigContinuousQualityEnforcement;

		private static ConfigEntry<int> ConfigRtlcTextureCopyFix;

		private static Plugin Instance;

		private Harmony harmony;

		private Coroutine textureLimitRoutine;

		private Coroutine continuousQualityRoutine;

		private static bool syncingSettingsUi;

		private static float nextTextureLimitApplyTime;

		private static bool gameHasFocus = true;

		private static bool gamePaused;

		private static bool useRussianText;

		private static bool textureCopyFallbackInProgress;

		private static int textureCopyFixLogCount;

		private static readonly float[] RenderScales = new float[5] { 0.5f, 0.6f, 0.75f, 0.9f, 1f };

		private static readonly int[] TargetFpsValues = new int[5] { -1, 30, 60, 120, 144 };

		private static readonly int[] QualityAntiAliasingLevels = new int[4] { 0, 2, 4, 8 };

		private static readonly int[] UrpMsaaSampleCounts = new int[4] { 1, 2, 4, 8 };

		private static readonly float[] LodBiasValues = new float[4] { 0.5f, 0.75f, 1f, 1.5f };

		private void Awake()
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			Instance = this;
			BindConfig();
			useRussianText = ShouldUseRussianText();
			if (ConfigGraphicsPreset.Value != 0)
			{
				ApplyPreset(ConfigGraphicsPreset.Value);
			}
			PrepareTextureCopyWindow();
			ApplyGraphicsSettings(applyTextureLimit: false);
			QueueDeferredTextureLimit();
			StartContinuousQualityEnforcer();
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
			harmony = new Harmony("com.ananas1k.gwyf.moreoptimization");
			int num = 0;
			num += (PatchMethod("GraphicsSettingsApplier", "ApplyAll", null, "PatchGraphicsSettingsApplier_ApplyAll") ? 1 : 0);
			num += (PatchMethod("SettingsLayoutRuntimeUI", "Awake", "PatchSettingsLayoutAwake", null) ? 1 : 0);
			num += (PatchMethod("SettingsLayout", "NotifyChanged", null, "PatchNotifyChanged") ? 1 : 0);
			num += (PatchMethod("Outline", "SmoothNormals", "PatchOutlineSmoothNormals", null) ? 1 : 0);
			num += PatchGraphicsCopyTextureMethods();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Active patches: {2}. URP, RTLC texture copy fix {3}, and {4} UI text enabled.", "More Optimization", "1.0.1", num, (ConfigRtlcTextureCopyFix.Value == 1) ? "enabled" : "disabled", useRussianText ? "Russian" : "English"));
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			if (continuousQualityRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(continuousQualityRoutine);
			}
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfig()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Expected O, but got Unknown
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Expected O, but got Unknown
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Expected O, but got Unknown
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Expected O, but got Unknown
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Expected O, but got Unknown
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Expected O, but got Unknown
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Expected O, but got Unknown
			ConfigGraphicsPreset = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "Preset", 0, new ConfigDescription("0=Custom, 1=Potato, 2=Low, 3=Medium, 4=High, 5=Ultra", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			ConfigRenderScale = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "RenderScale", 4, new ConfigDescription("0=0.5x, 1=0.6x, 2=0.75x, 3=0.9x, 4=1.0x", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ConfigTextureResolution = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "TextureResolution", 0, new ConfigDescription("0=Full, 1=Half, 2=Quarter, 3=Eighth", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			ConfigShadows = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "Shadows", 2, new ConfigDescription("0=Disable, 1=HardOnly, 2=All", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
			ConfigVSync = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "VSync", 0, new ConfigDescription("0=Off, 1=On", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigAntiAliasing = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "AntiAliasing", 0, new ConfigDescription("0=Off, 1=2x, 2=4x, 3=8x", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			ConfigTargetFPS = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "TargetFPS", 0, new ConfigDescription("0=Uncapped, 1=30, 2=60, 3=120, 4=144", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ConfigLodBias = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "LodBias", 2, new ConfigDescription("0=Low, 1=Balanced, 2=Default, 3=High", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			ConfigAnisotropicFiltering = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "AnisotropicFiltering", 1, new ConfigDescription("0=Disable, 1=Enable, 2=ForceEnable", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
			ConfigReflectionProbes = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "RealtimeReflectionProbes", 1, new ConfigDescription("0=Off, 1=On", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigSoftParticles = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "SoftParticles", 1, new ConfigDescription("0=Off, 1=On", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigBackgroundFpsLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Graphics", "BackgroundFPSLimit", 1, new ConfigDescription("0=Off, 1=10 FPS. Limits FPS while the game is minimized or unfocused.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigTextureLimitDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Compatibility", "TextureLimitSceneDelay", 1.75f, new ConfigDescription("Delay in seconds before applying Texture Resolution after a scene loads. Helps texture replacement mods avoid CopyTexture mipmap errors.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			ConfigTextureLimitEnforcementDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Compatibility", "TextureLimitEnforcementDuration", 10f, new ConfigDescription("How long Texture Resolution is re-applied after startup/scene/settings changes. Keeps Potato/Low texture quality from being reset by the game.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			ConfigContinuousQualityEnforcement = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "ContinuousQualityEnforcement", 1, new ConfigDescription("0=Off, 1=On. Re-applies More Optimization graphics settings while playing so level loads cannot reset Potato/Low texture quality or render scale.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigRtlcTextureCopyFix = ((BaseUnityPlugin)this).Config.Bind<int>("Compatibility", "RTLCTextureCopyFix", 1, new ConfigDescription("0=Off, 1=On. Falls back to Graphics.ConvertTexture when RTLC texture replacements hit a CopyTexture mipmap-count mismatch.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1), Array.Empty<object>()));
			ConfigLocalizationLanguage = ((BaseUnityPlugin)this).Config.Bind<int>("Localization", "Language", 0, new ConfigDescription("0=Auto, 1=English, 2=Russian. Auto uses Russian text when RTLC/XUnity Russian localization is detected.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
		}

		private static bool ShouldUseRussianText()
		{
			if (ConfigLocalizationLanguage != null)
			{
				if (ConfigLocalizationLanguage.Value == 1)
				{
					return false;
				}
				if (ConfigLocalizationLanguage.Value == 2)
				{
					return true;
				}
			}
			return IsRussianLocalizationDetected();
		}

		private static bool IsRussianLocalizationDetected()
		{
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					string text = pluginInfo.Key ?? string.Empty;
					string text2 = ((pluginInfo.Value != null && pluginInfo.Value.Metadata != null) ? (pluginInfo.Value.Metadata.Name ?? string.Empty) : string.Empty);
					string text3 = ((pluginInfo.Value != null && pluginInfo.Value.Metadata != null) ? (pluginInfo.Value.Metadata.GUID ?? string.Empty) : string.Empty);
					string text4 = (text + " " + text2 + " " + text3).ToLowerInvariant();
					if (text4.Contains("rtlc.gwyf") || text4.Contains("russian") || text4.Contains("рус"))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (Directory.Exists(Path.Combine(Paths.PluginPath, "RTLC-GWYF_Russian_Translation")))
				{
					return true;
				}
				if (File.Exists(Path.Combine(Paths.ConfigPath, "Translation", "ru", "Text", "Translate.txt")))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private bool PatchMethod(string typeName, string methodName, string prefixName, string postfixName)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Type type = AccessTools.TypeByName(typeName);
				if (type == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find type " + typeName + ". This patch will be skipped."));
					return false;
				}
				MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find method " + typeName + "." + methodName + ". This patch will be skipped."));
					return false;
				}
				HarmonyMethod val = ((prefixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), prefixName, (Type[])null));
				HarmonyMethod val2 = ((postfixName == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), postfixName, (Type[])null));
				harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch " + typeName + "." + methodName + ": " + ex.Message));
				return false;
			}
		}

		private int PatchGraphicsCopyTextureMethods()
		{
			return 0 + (PatchGraphicsCopyTextureMethod(new Type[2]
			{
				typeof(Texture),
				typeof(Texture)
			}, "PatchGraphicsCopyTexture") ? 1 : 0) + (PatchGraphicsCopyTextureMethod(new Type[4]
			{
				typeof(Texture),
				typeof(int),
				typeof(Texture),
				typeof(int)
			}, "PatchGraphicsCopyTextureElement") ? 1 : 0);
		}

		private bool PatchGraphicsCopyTextureMethod(Type[] parameterTypes, string prefixName)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Graphics), "CopyTexture", parameterTypes, (Type[])null);
				if (methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find UnityEngine.Graphics.CopyTexture overload. RTLC texture copy fix will be skipped for this overload.");
					return false;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), prefixName, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to patch UnityEngine.Graphics.CopyTexture: " + ex.Message));
				return false;
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			PrepareTextureCopyWindow();
			ApplyGraphicsSettings(applyTextureLimit: false);
			QueueDeferredTextureLimit();
		}

		private void OnActiveSceneChanged(Scene oldScene, Scene newScene)
		{
			PrepareTextureCopyWindow();
			ApplyGraphicsSettings(applyTextureLimit: false);
			QueueDeferredTextureLimit();
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			gameHasFocus = hasFocus;
			ApplyGraphicsSettings(applyTextureLimit: false);
		}

		private void OnApplicationPause(bool pauseStatus)
		{
			gamePaused = pauseStatus;
			ApplyGraphicsSettings(applyTextureLimit: false);
		}

		public static void ApplyPreset(int presetIndex)
		{
			presetIndex = ClampOption(presetIndex, 0, 5);
			switch (presetIndex)
			{
			case 1:
				ConfigRenderScale.Value = 0;
				ConfigTextureResolution.Value = 3;
				ConfigShadows.Value = 0;
				ConfigAntiAliasing.Value = 0;
				ConfigTargetFPS.Value = 1;
				ConfigLodBias.Value = 0;
				ConfigAnisotropicFiltering.Value = 0;
				ConfigReflectionProbes.Value = 0;
				ConfigSoftParticles.Value = 0;
				ConfigVSync.Value = 0;
				break;
			case 2:
				ConfigRenderScale.Value = 2;
				ConfigTextureResolution.Value = 2;
				ConfigShadows.Value = 0;
				ConfigAntiAliasing.Value = 0;
				ConfigTargetFPS.Value = 2;
				ConfigLodBias.Value = 1;
				ConfigAnisotropicFiltering.Value = 0;
				ConfigReflectionProbes.Value = 0;
				ConfigSoftParticles.Value = 0;
				ConfigVSync.Value = 0;
				break;
			case 3:
				ConfigRenderScale.Value = 4;
				ConfigTextureResolution.Value = 1;
				ConfigShadows.Value = 1;
				ConfigAntiAliasing.Value = 1;
				ConfigTargetFPS.Value = 2;
				ConfigLodBias.Value = 2;
				ConfigAnisotropicFiltering.Value = 1;
				ConfigReflectionProbes.Value = 0;
				ConfigSoftParticles.Value = 0;
				ConfigVSync.Value = 0;
				break;
			case 4:
				ConfigRenderScale.Value = 4;
				ConfigTextureResolution.Value = 0;
				ConfigShadows.Value = 2;
				ConfigAntiAliasing.Value = 2;
				ConfigTargetFPS.Value = 4;
				ConfigLodBias.Value = 2;
				ConfigAnisotropicFiltering.Value = 1;
				ConfigReflectionProbes.Value = 1;
				ConfigSoftParticles.Value = 1;
				ConfigVSync.Value = 0;
				break;
			case 5:
				ConfigRenderScale.Value = 4;
				ConfigTextureResolution.Value = 0;
				ConfigShadows.Value = 2;
				ConfigAntiAliasing.Value = 3;
				ConfigTargetFPS.Value = 0;
				ConfigLodBias.Value = 3;
				ConfigAnisotropicFiltering.Value = 2;
				ConfigReflectionProbes.Value = 1;
				ConfigSoftParticles.Value = 1;
				ConfigVSync.Value = 0;
				break;
			}
		}

		public static void ApplyGraphicsSettings()
		{
			ApplyGraphicsSettings(applyTextureLimit: true, forceTextureLimit: true);
		}

		private static void ApplyGraphicsSettings(bool applyTextureLimit, bool forceTextureLimit = false)
		{
			if (ConfigRenderScale != null)
			{
				if (applyTextureLimit)
				{
					ApplyTextureLimit(forceTextureLimit);
				}
				ApplyUnityQualitySettings();
				ApplyRenderPipelineSettings();
			}
		}

		private static void ApplyUnityQualitySettings()
		{
			if (IsBackgroundFpsCapActive())
			{
				QualitySettings.vSyncCount = 0;
				Application.targetFrameRate = 10;
			}
			else
			{
				QualitySettings.vSyncCount = ClampOption(ConfigVSync.Value, 0, 1);
				Application.targetFrameRate = TargetFpsValues[ClampOption(ConfigTargetFPS.Value, 0, TargetFpsValues.Length - 1)];
			}
			switch (ClampOption(ConfigShadows.Value, 0, 2))
			{
			case 0:
				QualitySettings.shadows = (ShadowQuality)0;
				QualitySettings.shadowCascades = 0;
				QualitySettings.shadowDistance = 0f;
				break;
			case 1:
				QualitySettings.shadows = (ShadowQuality)1;
				QualitySettings.shadowCascades = 1;
				QualitySettings.shadowDistance = 75f;
				break;
			default:
				QualitySettings.shadows = (ShadowQuality)2;
				QualitySettings.shadowCascades = 4;
				QualitySettings.shadowDistance = 150f;
				break;
			}
			QualitySettings.antiAliasing = QualityAntiAliasingLevels[ClampOption(ConfigAntiAliasing.Value, 0, QualityAntiAliasingLevels.Length - 1)];
			QualitySettings.lodBias = LodBiasValues[ClampOption(ConfigLodBias.Value, 0, LodBiasValues.Length - 1)];
			QualitySettings.anisotropicFiltering = (AnisotropicFiltering)ClampOption(ConfigAnisotropicFiltering.Value, 0, 2);
			QualitySettings.realtimeReflectionProbes = ConfigReflectionProbes.Value == 1;
			QualitySettings.softParticles = ConfigSoftParticles.Value == 1;
		}

		private static bool IsBackgroundFpsCapActive()
		{
			if (ConfigBackgroundFpsLimit != null && ConfigBackgroundFpsLimit.Value == 1)
			{
				if (gameHasFocus)
				{
					return gamePaused;
				}
				return true;
			}
			return false;
		}

		private static void ApplyRenderPipelineSettings()
		{
			RenderPipelineAsset val = GraphicsSettings.currentRenderPipeline ?? QualitySettings.renderPipeline;
			if (!((Object)(object)val == (Object)null))
			{
				Type type = ((object)val).GetType();
				float num = RenderScales[ClampOption(ConfigRenderScale.Value, 0, RenderScales.Length - 1)];
				TrySetProperty(type, val, "renderScale", num);
				switch (ClampOption(ConfigShadows.Value, 0, 2))
				{
				case 0:
					TrySetProperty(type, val, "shadowDistance", 0f);
					TrySetProperty(type, val, "shadowCascadeCount", 0);
					break;
				case 1:
					TrySetProperty(type, val, "shadowDistance", 75f);
					TrySetProperty(type, val, "shadowCascadeCount", 1);
					break;
				default:
					TrySetProperty(type, val, "shadowDistance", 150f);
					TrySetProperty(type, val, "shadowCascadeCount", 4);
					break;
				}
				int num2 = UrpMsaaSampleCounts[ClampOption(ConfigAntiAliasing.Value, 0, UrpMsaaSampleCounts.Length - 1)];
				TrySetProperty(type, val, "msaaSampleCount", num2);
			}
		}

		private static void ApplyTextureLimit(bool force = false)
		{
			if (ConfigTextureResolution != null && (force || !(Time.realtimeSinceStartup < nextTextureLimitApplyTime)))
			{
				QualitySettings.globalTextureMipmapLimit = ClampOption(ConfigTextureResolution.Value, 0, 3);
			}
		}

		private static void PrepareTextureCopyWindow()
		{
			if (ConfigTextureResolution != null)
			{
				float num = ((ConfigTextureLimitDelay == null) ? 1.75f : Mathf.Clamp(ConfigTextureLimitDelay.Value, 0f, 5f));
				nextTextureLimitApplyTime = Time.realtimeSinceStartup + num;
				if (ConfigTextureResolution.Value > 0)
				{
					QualitySettings.globalTextureMipmapLimit = 0;
				}
			}
		}

		private static void QueueDeferredTextureLimit()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				ApplyTextureLimit(force: true);
			}
			else
			{
				Instance.QueueDeferredTextureLimitInstance();
			}
		}

		private void QueueDeferredTextureLimitInstance()
		{
			if (textureLimitRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(textureLimitRoutine);
			}
			textureLimitRoutine = ((MonoBehaviour)this).StartCoroutine(ApplyTextureLimitAfterDelay());
		}

		private IEnumerator ApplyTextureLimitAfterDelay()
		{
			float num = ((ConfigTextureLimitDelay == null) ? 1.75f : Mathf.Clamp(ConfigTextureLimitDelay.Value, 0f, 5f));
			if (num > 0f)
			{
				yield return (object)new WaitForSecondsRealtime(num);
			}
			float duration = ((ConfigTextureLimitEnforcementDuration == null) ? 10f : Mathf.Clamp(ConfigTextureLimitEnforcementDuration.Value, 0f, 30f));
			float endTime = Time.realtimeSinceStartup + duration;
			while (true)
			{
				ApplyGraphicsSettings(applyTextureLimit: true, forceTextureLimit: true);
				if (duration <= 0f || Time.realtimeSinceStartup >= endTime)
				{
					break;
				}
				yield return (object)new WaitForSecondsRealtime(0.5f);
			}
			textureLimitRoutine = null;
		}

		private void StartContinuousQualityEnforcer()
		{
			if (continuousQualityRoutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(continuousQualityRoutine);
			}
			continuousQualityRoutine = ((MonoBehaviour)this).StartCoroutine(ContinuousQualityEnforcer());
		}

		private IEnumerator ContinuousQualityEnforcer()
		{
			while (true)
			{
				yield return (object)new WaitForSecondsRealtime(1f);
				if (ConfigContinuousQualityEnforcement != null && ConfigContinuousQualityEnforcement.Value == 1)
				{
					ApplyGraphicsSettings(applyTextureLimit: true);
				}
			}
		}

		private static void PatchGraphicsSettingsApplier_ApplyAll()
		{
			ApplyGraphicsSettings(applyTextureLimit: false);
			QueueDeferredTextureLimit();
		}

		private static bool PatchOutlineSmoothNormals(Mesh mesh, ref List<Vector3> __result)
		{
			if ((Object)(object)mesh != (Object)null)
			{
				return true;
			}
			__result = new List<Vector3>();
			return false;
		}

		private static bool PatchGraphicsCopyTexture(Texture src, Texture dst)
		{
			return !TryHandleMismatchedTextureCopy(src, dst, -1, -1);
		}

		private static bool PatchGraphicsCopyTextureElement(Texture src, int srcElement, Texture dst, int dstElement)
		{
			return !TryHandleMismatchedTextureCopy(src, dst, srcElement, dstElement);
		}

		private static bool TryHandleMismatchedTextureCopy(Texture src, Texture dst, int srcElement, int dstElement)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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)
			if (ConfigRtlcTextureCopyFix == null || ConfigRtlcTextureCopyFix.Value != 1)
			{
				return false;
			}
			if ((Object)(object)src == (Object)null || (Object)(object)dst == (Object)null || src.dimension != dst.dimension)
			{
				return false;
			}
			if (src.mipmapCount == dst.mipmapCount && src.width == dst.width && src.height == dst.height)
			{
				return false;
			}
			if (textureCopyFallbackInProgress)
			{
				return false;
			}
			textureCopyFallbackInProgress = true;
			try
			{
				if (src.graphicsFormat != dst.graphicsFormat)
				{
					Texture2D val = (Texture2D)(object)((src is Texture2D) ? src : null);
					if (val != null)
					{
						try
						{
							val.Compress(true);
						}
						catch
						{
						}
					}
				}
				if (src.graphicsFormat != dst.graphicsFormat)
				{
					return false;
				}
				int num = 0;
				int num2 = src.width;
				int num3 = src.height;
				while ((num2 > dst.width || num3 > dst.height) && num < src.mipmapCount - 1)
				{
					num2 = Mathf.Max(1, num2 / 2);
					num3 = Mathf.Max(1, num3 / 2);
					num++;
				}
				if (num2 == dst.width && num3 == dst.height)
				{
					int num4 = Mathf.Min(src.mipmapCount - num, dst.mipmapCount);
					for (int i = 0; i < num4; i++)
					{
						if (srcElement >= 0)
						{
							Graphics.CopyTexture(src, srcElement, num + i, dst, dstElement, i);
						}
						else
						{
							Graphics.CopyTexture(src, 0, num + i, dst, 0, i);
						}
					}
					LogTextureCopyFix(src, dst);
					return true;
				}
				return false;
			}
			catch (Exception ex)
			{
				if (textureCopyFixLogCount < 5)
				{
					LogWarning("RTLC texture copy fix failed for " + GetTextureName(src) + " -> " + GetTextureName(dst) + ": " + ex.Message);
				}
				textureCopyFixLogCount++;
				return false;
			}
			finally
			{
				textureCopyFallbackInProgress = false;
			}
		}

		private static void LogTextureCopyFix(Texture src, Texture dst)
		{
			if (textureCopyFixLogCount < 5)
			{
				LogInfo($"RTLC texture copy fix handled mipmap mismatch for {GetTextureName(src)} -> {GetTextureName(dst)} (src mips {src.mipmapCount}, dst mips {dst.mipmapCount}).");
			}
			textureCopyFixLogCount++;
		}

		private static string GetTextureName(Texture texture)
		{
			if ((Object)(object)texture == (Object)null)
			{
				return "<null>";
			}
			if (!string.IsNullOrEmpty(((Object)texture).name))
			{
				return ((Object)texture).name;
			}
			return ((object)texture).GetType().Name;
		}

		private static void PatchSettingsLayoutAwake(object __instance)
		{
			try
			{
				if (!(GetFieldValue(GetFieldValue(__instance, "layout"), "tabs") is IList tabs))
				{
					return;
				}
				object obj = FindGraphicsTab(tabs);
				if (obj == null || !(GetFieldValue(obj, "entries") is IList list) || ContainsModEntry(list, "ModGraphicsPreset"))
				{
					return;
				}
				List<ScriptableObject> list2 = new List<ScriptableObject>
				{
					CreateDropdown("ModGraphicsPreset", GetSettingLabel("ModGraphicsPreset"), GetSettingOptionsList("ModGraphicsPreset"), ConfigGraphicsPreset.Value),
					CreateDropdown("ModRenderScale", GetSettingLabel("ModRenderScale"), GetSettingOptionsList("ModRenderScale"), ConfigRenderScale.Value),
					CreateDropdown("ModTextureResolution", GetSettingLabel("ModTextureResolution"), GetSettingOptionsList("ModTextureResolution"), ConfigTextureResolution.Value),
					CreateDropdown("ModShadows", GetSettingLabel("ModShadows"), GetSettingOptionsList("ModShadows"), ConfigShadows.Value),
					CreateDropdown("ModAntiAliasing", GetSettingLabel("ModAntiAliasing"), GetSettingOptionsList("ModAntiAliasing"), ConfigAntiAliasing.Value),
					CreateDropdown("ModVSync", GetSettingLabel("ModVSync"), GetSettingOptionsList("ModVSync"), ConfigVSync.Value),
					CreateDropdown("ModTargetFPS", GetSettingLabel("ModTargetFPS"), GetSettingOptionsList("ModTargetFPS"), ConfigTargetFPS.Value),
					CreateDropdown("ModLodBias", GetSettingLabel("ModLodBias"), GetSettingOptionsList("ModLodBias"), ConfigLodBias.Value),
					CreateDropdown("ModAnisotropicFiltering", GetSettingLabel("ModAnisotropicFiltering"), GetSettingOptionsList("ModAnisotropicFiltering"), ConfigAnisotropicFiltering.Value),
					CreateDropdown("ModReflectionProbes", GetSettingLabel("ModReflectionProbes"), GetSettingOptionsList("ModReflectionProbes"), ConfigReflectionProbes.Value),
					CreateDropdown("ModSoftParticles", GetSettingLabel("ModSoftParticles"), GetSettingOptionsList("ModSoftParticles"), ConfigSoftParticles.Value),
					CreateDropdown("ModBackgroundFpsLimit", GetSettingLabel("ModBackgroundFpsLimit"), GetSettingOptionsList("ModBackgroundFpsLimit"), ConfigBackgroundFpsLimit.Value)
				};
				for (int num = list2.Count - 1; num >= 0; num--)
				{
					if ((Object)(object)list2[num] != (Object)null)
					{
						list.Insert(0, list2[num]);
					}
				}
			}
			catch (Exception ex)
			{
				LogWarning("Failed to extend the Graphics settings UI: " + ex.Message);
			}
		}

		private static object FindGraphicsTab(IList tabs)
		{
			foreach (object tab in tabs)
			{
				if (string.Equals(GetFieldValue(tab, "tabName") as string, "Graphics", StringComparison.OrdinalIgnoreCase))
				{
					return tab;
				}
			}
			return null;
		}

		private static bool ContainsModEntry(IList entries, string key)
		{
			foreach (object entry in entries)
			{
				ScriptableObject val = (ScriptableObject)((entry is ScriptableObject) ? entry : null);
				if ((Object)(object)val != (Object)null && ((Object)val).name == key)
				{
					return true;
				}
			}
			return false;
		}

		private static ScriptableObject CreateDropdown(string key, string label, List<string> options, int defaultIndex)
		{
			try
			{
				Type type = AccessTools.TypeByName("DropdownSettingItem");
				if (type == null)
				{
					LogWarning("Could not find DropdownSettingItem. More Optimization settings will not be shown in the menu.");
					return null;
				}
				ScriptableObject obj = ScriptableObject.CreateInstance(type);
				((Object)obj).name = key;
				TrySetField(obj, "key", key);
				TrySetField(obj, "label", label);
				TrySetField(obj, "options", options);
				TrySetField(obj, "index", defaultIndex);
				return obj;
			}
			catch (Exception ex)
			{
				LogWarning("Failed to create dropdown " + key + ": " + ex.Message);
				return null;
			}
		}

		private static void PatchNotifyChanged(object __instance)
		{
			try
			{
				if (syncingSettingsUi || !(GetFieldValue(__instance, "tabs") is IList tabs))
				{
					return;
				}
				Dictionary<string, ScriptableObject> dictionary = CollectModItems(tabs);
				if (dictionary.Count == 0)
				{
					return;
				}
				bool flag = false;
				bool flag2 = false;
				if (TryGetDropdownIndex(dictionary, "ModGraphicsPreset", out var index))
				{
					index = ClampOption(index, 0, 5);
					if (index != ConfigGraphicsPreset.Value)
					{
						ConfigGraphicsPreset.Value = index;
						flag = true;
						if (index != 0)
						{
							ApplyPreset(index);
							SyncDropdownsFromConfig(dictionary);
							RefreshVisibleSettingsUi(dictionary);
							flag2 = true;
						}
					}
				}
				if (!flag2)
				{
					int num = (int)(0u | (UpdateConfigFromDropdown(dictionary, "ModRenderScale", ConfigRenderScale, 0, 4) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModTextureResolution", ConfigTextureResolution, 0, 3) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModShadows", ConfigShadows, 0, 2) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModAntiAliasing", ConfigAntiAliasing, 0, 3) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModVSync", ConfigVSync, 0, 1) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModTargetFPS", ConfigTargetFPS, 0, 4) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModLodBias", ConfigLodBias, 0, 3) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModAnisotropicFiltering", ConfigAnisotropicFiltering, 0, 2) ? 1u : 0u) | (UpdateConfigFromDropdown(dictionary, "ModReflectionProbes", ConfigReflectionProbes, 0, 1) ? 1u : 0u)) | (UpdateConfigFromDropdown(dictionary, "ModSoftParticles", ConfigSoftParticles, 0, 1) ? 1 : 0);
					bool flag3 = UpdateConfigFromDropdown(dictionary, "ModBackgroundFpsLimit", ConfigBackgroundFpsLimit, 0, 1);
					if (num != 0)
					{
						ConfigGraphicsPreset.Value = 0;
						SetDropdownIndex(dictionary, "ModGraphicsPreset", 0);
						RefreshVisibleSettingsUi(dictionary);
						flag = true;
					}
					if (flag3)
					{
						flag = true;
					}
				}
				if (flag)
				{
					ApplyGraphicsSettings(applyTextureLimit: true, forceTextureLimit: true);
					QueueDeferredTextureLimit();
					SaveConfigFile();
				}
			}
			catch (Exception ex)
			{
				LogWarning("Failed to process Graphics settings change: " + ex.Message);
			}
		}

		private static Dictionary<string, ScriptableObject> CollectModItems(IList tabs)
		{
			Dictionary<string, ScriptableObject> dictionary = new Dictionary<string, ScriptableObject>();
			foreach (object tab in tabs)
			{
				if (!(GetFieldValue(tab, "entries") is IList list))
				{
					continue;
				}
				foreach (object item in list)
				{
					ScriptableObject val = (ScriptableObject)((item is ScriptableObject) ? item : null);
					if ((Object)(object)val != (Object)null && ((Object)val).name.StartsWith("Mod", StringComparison.Ordinal))
					{
						dictionary[((Object)val).name] = val;
					}
				}
			}
			return dictionary;
		}

		private static bool UpdateConfigFromDropdown(Dictionary<string, ScriptableObject> modItems, string key, ConfigEntry<int> config, int min, int max)
		{
			if (!TryGetDropdownIndex(modItems, key, out var index))
			{
				return false;
			}
			index = ClampOption(index, min, max);
			if (config.Value == index)
			{
				return false;
			}
			config.Value = index;
			return true;
		}

		private static void SyncDropdownsFromConfig(Dictionary<string, ScriptableObject> modItems)
		{
			SetDropdownIndex(modItems, "ModGraphicsPreset", ConfigGraphicsPreset.Value);
			SetDropdownIndex(modItems, "ModRenderScale", ConfigRenderScale.Value);
			SetDropdownIndex(modItems, "ModTextureResolution", ConfigTextureResolution.Value);
			SetDropdownIndex(modItems, "ModShadows", ConfigShadows.Value);
			SetDropdownIndex(modItems, "ModAntiAliasing", ConfigAntiAliasing.Value);
			SetDropdownIndex(modItems, "ModVSync", ConfigVSync.Value);
			SetDropdownIndex(modItems, "ModTargetFPS", ConfigTargetFPS.Value);
			SetDropdownIndex(modItems, "ModLodBias", ConfigLodBias.Value);
			SetDropdownIndex(modItems, "ModAnisotropicFiltering", ConfigAnisotropicFiltering.Value);
			SetDropdownIndex(modItems, "ModReflectionProbes", ConfigReflectionProbes.Value);
			SetDropdownIndex(modItems, "ModSoftParticles", ConfigSoftParticles.Value);
			SetDropdownIndex(modItems, "ModBackgroundFpsLimit", ConfigBackgroundFpsLimit.Value);
		}

		private static void RefreshVisibleSettingsUi(Dictionary<string, ScriptableObject> modItems)
		{
			if (syncingSettingsUi)
			{
				return;
			}
			syncingSettingsUi = true;
			try
			{
				foreach (KeyValuePair<string, ScriptableObject> modItem in modItems)
				{
					if (TryGetDropdownIndex(modItems, modItem.Key, out var index))
					{
						string settingLabel = GetSettingLabel(modItem.Key);
						string[] settingOptions = GetSettingOptions(modItem.Key);
						if (settingLabel != null && settingOptions != null)
						{
							RefreshLiveDropdown(settingLabel, settingOptions, index);
						}
					}
				}
			}
			finally
			{
				syncingSettingsUi = false;
			}
		}

		private static string GetSettingLabel(string key)
		{
			return key switch
			{
				"ModGraphicsPreset" => T("Quality Preset", "Пресет качества"), 
				"ModRenderScale" => T("Render Scale", "Масштаб рендера"), 
				"ModTextureResolution" => T("Texture Resolution", "Разрешение текстур"), 
				"ModShadows" => T("Shadows", "Тени"), 
				"ModAntiAliasing" => T("Anti-Aliasing", "Сглаживание"), 
				"ModVSync" => "VSync", 
				"ModTargetFPS" => T("Target FPS", "Лимит FPS"), 
				"ModLodBias" => T("LOD Bias", "Дальность LOD"), 
				"ModAnisotropicFiltering" => T("Anisotropic", "Анизотропия"), 
				"ModReflectionProbes" => T("Reflections", "Отражения"), 
				"ModSoftParticles" => T("Soft Particles", "Мягкие частицы"), 
				"ModBackgroundFpsLimit" => T("Background FPS", "FPS в фоне"), 
				_ => null, 
			};
		}

		private static List<string> GetSettingOptionsList(string key)
		{
			string[] settingOptions = GetSettingOptions(key);
			if (settingOptions != null)
			{
				return new List<string>(settingOptions);
			}
			return new List<string>();
		}

		private static string[] GetSettingOptions(string key)
		{
			switch (key)
			{
			case "ModGraphicsPreset":
				if (useRussianText)
				{
					return new string[6] { "Свои", "Картошка", "Низкие", "Средние", "Высокие", "Ультра" };
				}
				return new string[6] { "Custom", "Potato", "Low", "Medium", "High", "Ultra" };
			case "ModRenderScale":
				return new string[5] { "50%", "60%", "75%", "90%", "100%" };
			case "ModTextureResolution":
				if (useRussianText)
				{
					return new string[4] { "Полное", "Половина", "Четверть", "Восьмая" };
				}
				return new string[4] { "Full Res", "Half Res", "Quarter Res", "Eighth Res" };
			case "ModShadows":
				if (useRussianText)
				{
					return new string[3] { "Выкл", "Только жесткие", "Все" };
				}
				return new string[3] { "Off", "Hard Only", "All" };
			case "ModAntiAliasing":
				if (useRussianText)
				{
					return new string[4] { "Выкл", "2x", "4x", "8x" };
				}
				return new string[4] { "Off", "2x", "4x", "8x" };
			case "ModVSync":
				if (useRussianText)
				{
					return new string[2] { "Выкл", "Вкл" };
				}
				return new string[2] { "Off", "On" };
			case "ModTargetFPS":
				if (useRussianText)
				{
					return new string[5] { "Без лимита", "30", "60", "120", "144" };
				}
				return new string[5] { "Uncapped", "30", "60", "120", "144" };
			case "ModLodBias":
				if (useRussianText)
				{
					return new string[4] { "Низкая", "Баланс", "Обычная", "Высокая" };
				}
				return new string[4] { "Low", "Balanced", "Default", "High" };
			case "ModAnisotropicFiltering":
				if (useRussianText)
				{
					return new string[3] { "Выкл", "Вкл", "Принудительно" };
				}
				return new string[3] { "Off", "On", "Forced" };
			case "ModReflectionProbes":
				if (useRussianText)
				{
					return new string[2] { "Выкл", "Вкл" };
				}
				return new string[2] { "Off", "On" };
			case "ModSoftParticles":
				if (useRussianText)
				{
					return new string[2] { "Выкл", "Вкл" };
				}
				return new string[2] { "Off", "On" };
			case "ModBackgroundFpsLimit":
				if (useRussianText)
				{
					return new string[2] { "Выкл", "10 FPS" };
				}
				return new string[2] { "Off", "10 FPS" };
			default:
				return null;
			}
		}

		private static string T(string english, string russian)
		{
			if (!useRussianText)
			{
				return english;
			}
			return russian;
		}

		private static void RefreshLiveDropdown(string label, string[] expectedOptions, int index)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			Component[] array = Resources.FindObjectsOfTypeAll<Component>();
			foreach (Component val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)val.gameObject == (Object)null)
				{
					continue;
				}
				Scene scene = val.gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					string fullName = ((object)val).GetType().FullName;
					if (fullName != null && fullName.IndexOf("Dropdown", StringComparison.OrdinalIgnoreCase) >= 0 && DropdownOptionsMatch(val, expectedOptions) && (expectedOptions.Length > 2 || HierarchyContainsText(val.transform, label)))
					{
						SetLiveDropdownValue(val, index);
					}
				}
			}
		}

		private static bool DropdownOptionsMatch(object dropdown, string[] expectedOptions)
		{
			if (!(GetMemberValue(dropdown, "options") is IEnumerable enumerable))
			{
				return false;
			}
			List<string> list = new List<string>();
			foreach (object item2 in enumerable)
			{
				if (GetMemberValue(item2, "text") is string item)
				{
					list.Add(item);
				}
			}
			if (list.Count != expectedOptions.Length)
			{
				return false;
			}
			for (int i = 0; i < expectedOptions.Length; i++)
			{
				if (!string.Equals(NormalizeUiText(list[i]), NormalizeUiText(expectedOptions[i]), StringComparison.Ordinal))
				{
					return false;
				}
			}
			return true;
		}

		private static bool HierarchyContainsText(Transform transform, string expectedText)
		{
			string normalizedExpected = NormalizeUiText(expectedText);
			Transform val = transform;
			int num = 0;
			while ((Object)(object)val != (Object)null && num < 5)
			{
				if (TransformContainsText(val, normalizedExpected))
				{
					return true;
				}
				val = val.parent;
				num++;
			}
			return false;
		}

		private static bool TransformContainsText(Transform root, string normalizedExpected)
		{
			Component[] componentsInChildren = ((Component)root).GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && GetMemberValue(val, "text") is string value && string.Equals(NormalizeUiText(value), normalizedExpected, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private static void SetLiveDropdownValue(object dropdown, int index)
		{
			if (!TryInvokeMethod(dropdown, "SetValueWithoutNotify", new object[1] { index }))
			{
				TrySetProperty(dropdown, "value", index);
			}
			TryInvokeMethod(dropdown, "RefreshShownValue", new object[0]);
		}

		private static bool TryGetDropdownIndex(Dictionary<string, ScriptableObject> modItems, string key, out int index)
		{
			index = 0;
			if (!modItems.TryGetValue(key, out var value) || (Object)(object)value == (Object)null)
			{
				return false;
			}
			object fieldValue = GetFieldValue(value, "index");
			if (fieldValue is int)
			{
				index = (int)fieldValue;
				return true;
			}
			return false;
		}

		private static void SetDropdownIndex(Dictionary<string, ScriptableObject> modItems, string key, int index)
		{
			if (modItems.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				TrySetField(value, "index", index);
			}
		}

		private static object GetFieldValue(object target, string fieldName)
		{
			if (target == null)
			{
				return null;
			}
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(target.GetType(), fieldName);
				return (fieldInfo == null) ? null : fieldInfo.GetValue(target);
			}
			catch
			{
				return null;
			}
		}

		private static object GetMemberValue(object target, string memberName)
		{
			if (target == null)
			{
				return null;
			}
			try
			{
				Type type = target.GetType();
				PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property.GetValue(target, null);
				}
				FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				return (field == null) ? null : field.GetValue(target);
			}
			catch
			{
				return null;
			}
		}

		private static bool TrySetField(object target, string fieldName, object value)
		{
			if (target == null)
			{
				return false;
			}
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(target.GetType(), fieldName);
				if (fieldInfo == null)
				{
					return false;
				}
				fieldInfo.SetValue(target, value);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool TrySetProperty(object target, string propertyName, object value)
		{
			if (target == null)
			{
				return false;
			}
			return TrySetProperty(target.GetType(), target, propertyName, value);
		}

		private static bool TrySetProperty(Type type, object target, string propertyName, object value)
		{
			try
			{
				PropertyInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property == null)
				{
					return false;
				}
				property.SetValue(target, value, null);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool TryInvokeMethod(object target, string methodName, object[] args)
		{
			if (target == null)
			{
				return false;
			}
			try
			{
				Type[] array;
				if (args == null || args.Length == 0)
				{
					array = Type.EmptyTypes;
					args = new object[0];
				}
				else
				{
					array = new Type[args.Length];
					for (int i = 0; i < args.Length; i++)
					{
						array[i] = args[i].GetType();
					}
				}
				Type type = target.GetType();
				MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, array, null);
				if (methodInfo == null)
				{
					MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					for (int j = 0; j < methods.Length; j++)
					{
						if (methods[j].Name == methodName && methods[j].GetParameters().Length == args.Length)
						{
							methodInfo = methods[j];
							break;
						}
					}
				}
				if (methodInfo == null)
				{
					return false;
				}
				methodInfo.Invoke(target, args);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static string NormalizeUiText(string value)
		{
			if (value == null)
			{
				return string.Empty;
			}
			return value.Trim().Replace(" ", string.Empty).Replace("-", string.Empty)
				.Replace("_", string.Empty)
				.ToLowerInvariant();
		}

		private static int ClampOption(int value, int min, int max)
		{
			return Mathf.Clamp(value, min, max);
		}

		private static void SaveConfigFile()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return;
			}
			try
			{
				((BaseUnityPlugin)Instance).Config.Save();
			}
			catch (Exception ex)
			{
				LogWarning("Failed to save config: " + ex.Message);
			}
		}

		private static void LogWarning(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogWarning((object)message);
			}
		}

		private static void LogInfo(string message)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)message);
			}
		}
	}
}