Decompiled source of ShadowsOfDoubt UI Scaler v1.0.1

BepInEx/plugins/ShadowsOfDoubt.UIScaler/ShadowsOfDoubt.UIScaler.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WindowCanvasScaler
{
	[BepInPlugin("khundian.sod.uiscaler", "Shadows of Doubt UI Scaler", "1.0.1")]
	public class Plugin : BasePlugin
	{
		private struct OriginalScaler
		{
			public bool existed;

			public ScaleMode uiScaleMode;

			public Vector2 referenceResolution;

			public float matchWidthOrHeight;

			public ScreenMatchMode screenMatchMode;
		}

		public class UiDriver : MonoBehaviour
		{
			private string _toast = "";

			private float _toastUntil;

			private GUIStyle? _toastStyle;

			public UiDriver(IntPtr handle)
				: base(handle)
			{
			}

			public UiDriver()
			{
			}

			private void OnEnable()
			{
				Instance?.DebugMsg("UiDriver.OnEnable");
				((MonoBehaviour)this).CancelInvoke("Tick");
				((MonoBehaviour)this).CancelInvoke("Heartbeat");
				((MonoBehaviour)this).InvokeRepeating("Tick", 0.25f, 0.25f);
				((MonoBehaviour)this).InvokeRepeating("Heartbeat", 1f, 3f);
			}

			private void OnDisable()
			{
				Instance?.DebugMsg("UiDriver.OnDisable");
				((MonoBehaviour)this).CancelInvoke("Tick");
				((MonoBehaviour)this).CancelInvoke("Heartbeat");
			}

			private void Update()
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0162: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				if (instance == null)
				{
					return;
				}
				if (Input.GetKeyDown(instance._keyToggle))
				{
					bool flag = !CfgEnabled.Value;
					CfgEnabled.Value = flag;
					instance._saveConfigAtMs = (double)Time.unscaledTime * 1000.0 + 500.0;
					if (!flag)
					{
						instance.RevertAll();
						ShowToast("UI Scaler: Disabled (reverted)");
					}
					else
					{
						instance._configuredCanvasIds.Clear();
						instance.SafeRescan();
						ShowToast("UI Scaler: Enabled");
					}
					instance._lastEnabledState = flag;
				}
				if (Input.GetKeyDown(instance._keyUp))
				{
					float num = Mathf.Clamp(Mathf.Round((CfgUiScale.Value + 0.05f) * 20f) / 20f, 0.1f, 5f);
					if (Math.Abs(num - CfgUiScale.Value) > 0.0001f)
					{
						CfgUiScale.Value = num;
						instance._saveConfigAtMs = (double)Time.unscaledTime * 1000.0 + 500.0;
						ShowToast($"UI Scale: {num:0.00}x");
						if (CfgEnabled.Value)
						{
							instance.RevertAll();
							instance.SafeRescan();
						}
					}
				}
				if (!Input.GetKeyDown(instance._keyDown))
				{
					return;
				}
				float num2 = Mathf.Clamp(Mathf.Round((CfgUiScale.Value - 0.05f) * 20f) / 20f, 0.1f, 5f);
				if (Math.Abs(num2 - CfgUiScale.Value) > 0.0001f)
				{
					CfgUiScale.Value = num2;
					instance._saveConfigAtMs = (double)Time.unscaledTime * 1000.0 + 500.0;
					ShowToast($"UI Scale: {num2:0.00}x");
					if (CfgEnabled.Value)
					{
						instance.RevertAll();
						instance.SafeRescan();
					}
				}
			}

			private void Heartbeat()
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				if (CfgLogTicks.Value && Instance != null)
				{
					Scene activeScene = SceneManager.GetActiveScene();
					Instance.Info($"[Tick] scene='{((Scene)(ref activeScene)).name}' buildIndex={((Scene)(ref activeScene)).buildIndex} configured={Instance._configuredCanvasIds.Count}");
				}
			}

			private void OnGUI()
			{
				//IL_006e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Expected O, but got Unknown
				if (!(Time.unscaledTime > _toastUntil))
				{
					if (_toastStyle == null)
					{
						_toastStyle = new GUIStyle(GUI.skin.label)
						{
							alignment = (TextAnchor)4,
							fontStyle = (FontStyle)1,
							fontSize = 18
						};
					}
					int num = Mathf.Min(Screen.width - 40, 800);
					Rect val = new Rect((float)(Screen.width - num) * 0.5f, 20f, (float)num, 32f);
					GUI.Box(val, GUIContent.none);
					GUI.Label(val, _toast, _toastStyle);
				}
			}

			private void Tick()
			{
				//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = Instance;
				if (instance == null)
				{
					return;
				}
				double num = (double)Time.unscaledTime * 1000.0;
				if (Volatile.Read(ref instance._cfgReloadRequested) == 1)
				{
					if (instance._cfgReloadAtMs <= 0.0)
					{
						instance._cfgReloadAtMs = num + 200.0;
					}
					else if (num >= instance._cfgReloadAtMs)
					{
						Interlocked.Exchange(ref instance._cfgReloadRequested, 0);
						instance._cfgReloadAtMs = 0.0;
						instance.DebugMsg("Config changed on disk; reloading...");
						try
						{
							((BasePlugin)instance).Config.Reload();
							instance.UpdateParsedHotkeys();
							instance.UpdateCachedNameTokens();
							bool value = CfgEnabled.Value;
							if (instance._lastEnabledState && !value)
							{
								instance.RevertAll();
								ShowToast("UI Scaler: Disabled (reverted)");
							}
							else if (!instance._lastEnabledState && value)
							{
								instance._configuredCanvasIds.Clear();
								instance.SafeRescan();
								ShowToast("UI Scaler: Enabled");
							}
							if (value)
							{
								instance.RevertAll();
								instance.SafeRescan();
								ShowToast($"UI Scaler: Reloaded ({CfgUiScale.Value:0.00}x)");
							}
							instance._lastEnabledState = value;
							instance.DebugMsg("Config reloaded and applied.");
						}
						catch (Exception ex)
						{
							instance.Info("Config reload failed: " + ex.Message);
						}
					}
				}
				if (instance._saveConfigAtMs > 0.0 && num >= instance._saveConfigAtMs)
				{
					instance._saveConfigAtMs = 0.0;
					try
					{
						((BasePlugin)instance).Config.Save();
					}
					catch
					{
					}
				}
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).buildIndex != instance._lastSceneIndex || ((Scene)(ref activeScene)).name != instance._lastSceneName)
				{
					instance.Info($"[SceneChange] '{instance._lastSceneName}' -> '{((Scene)(ref activeScene)).name}' ({instance._lastSceneIndex}->{((Scene)(ref activeScene)).buildIndex})");
					instance._lastSceneName = ((Scene)(ref activeScene)).name ?? string.Empty;
					instance._lastSceneIndex = ((Scene)(ref activeScene)).buildIndex;
					instance._configuredCanvasIds.Clear();
					if (CfgEnabled.Value)
					{
						instance.SafeRescan();
					}
				}
				if (CfgEnabled.Value && num >= instance._nextRescanAtMs)
				{
					float num2 = Mathf.Max(250f, (float)CfgRescanMS.Value);
					instance._nextRescanAtMs = num + (double)num2;
					instance.SafeRescan();
				}
			}

			private void ShowToast(string s)
			{
				_toast = s;
				_toastUntil = Time.unscaledTime + 1.5f;
			}
		}

		public const string PluginGuid = "khundian.sod.uiscaler";

		public const string PluginName = "Shadows of Doubt UI Scaler";

		public const string PluginVersion = "1.0.1";

		internal static Plugin Instance;

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<bool> CfgVerbose;

		internal static ConfigEntry<bool> CfgLogTicks;

		internal static ConfigEntry<int> CfgRefWidth;

		internal static ConfigEntry<int> CfgRefHeight;

		internal static ConfigEntry<float> CfgMatchWH;

		internal static ConfigEntry<float> CfgUiScale;

		internal static ConfigEntry<bool> CfgIncludeWorldSpace;

		internal static ConfigEntry<bool> CfgActiveSceneOnly;

		internal static ConfigEntry<string> CfgCanvasIncludeTokens;

		internal static ConfigEntry<string> CfgCanvasExcludeTokens;

		internal static ConfigEntry<int> CfgRescanMS;

		internal static ConfigEntry<string> CfgKeyToggle;

		internal static ConfigEntry<string> CfgKeyScaleUp;

		internal static ConfigEntry<string> CfgKeyScaleDown;

		private readonly HashSet<int> _configuredCanvasIds = new HashSet<int>();

		private readonly Dictionary<int, (Canvas canvas, OriginalScaler original)> _originalById = new Dictionary<int, (Canvas, OriginalScaler)>();

		private readonly List<int> _pruneKeys = new List<int>(64);

		private double _nextRescanAtMs;

		private string _lastSceneName = string.Empty;

		private int _lastSceneIndex = -1;

		private bool _lastEnabledState = true;

		private UiDriver _driver;

		private string[] _incTokens = Array.Empty<string>();

		private string[] _excTokens = Array.Empty<string>();

		private double _cfgReloadAtMs;

		private double _saveConfigAtMs;

		private KeyCode _keyToggle = (KeyCode)290;

		private KeyCode _keyUp = (KeyCode)292;

		private KeyCode _keyDown = (KeyCode)291;

		private FileSystemWatcher? _cfgWatcher;

		private int _cfgReloadRequested;

		public override void Load()
		{
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Expected O, but got Unknown
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			((BasePlugin)this).Config.GenerateSettingDescriptions = true;
			((BasePlugin)this).Config.SaveOnConfigSet = true;
			CfgEnabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. true = apply Canvas Scaler; false = revert all and pause.");
			CfgVerbose = ((BasePlugin)this).Config.Bind<bool>("Logging", "Verbose", false, "When true, prints detailed discovery/apply logs.");
			CfgLogTicks = ((BasePlugin)this).Config.Bind<bool>("Logging", "LogTicks", false, "When true, prints a 3s heartbeat with scene & configured counts.");
			CfgRefWidth = ((BasePlugin)this).Config.Bind<int>("Scaling", "ReferenceWidth", 1920, "Scaler Reference Resolution (width).");
			CfgRefHeight = ((BasePlugin)this).Config.Bind<int>("Scaling", "ReferenceHeight", 1080, "Scaler Reference Resolution (height).");
			CfgMatchWH = ((BasePlugin)this).Config.Bind<float>("Scaling", "MatchWidthOrHeight", 0.5f, "Match: 0=width, 1=height.");
			CfgUiScale = ((BasePlugin)this).Config.Bind<float>("Scaling", "UiScaleMultiplier", 1f, "Resolution multiplier (e.g., 1.25 = 125%).");
			CfgIncludeWorldSpace = ((BasePlugin)this).Config.Bind<bool>("Filter", "IncludeWorldSpaceCanvases", false, "Include RenderMode.WorldSpace canvases.");
			CfgActiveSceneOnly = ((BasePlugin)this).Config.Bind<bool>("Filter", "ActiveSceneOnly", true, "Affect only canvases in the active scene.");
			CfgCanvasIncludeTokens = ((BasePlugin)this).Config.Bind<string>("Filter", "CanvasIncludeTokens", "", "CSV/semicolon tokens; empty = include all.");
			CfgCanvasExcludeTokens = ((BasePlugin)this).Config.Bind<string>("Filter", "CanvasExcludeTokens", "", "CSV/semicolon tokens; empty = exclude none.");
			CfgRescanMS = ((BasePlugin)this).Config.Bind<int>("Engine", "RescanEveryMilliseconds", 2000, "Periodic rescan interval.");
			CfgKeyToggle = ((BasePlugin)this).Config.Bind<string>("Hotkeys", "Toggle", "F9", "Enable/disable plugin.");
			CfgKeyScaleUp = ((BasePlugin)this).Config.Bind<string>("Hotkeys", "ScaleUp", "F11", "Increase UiScale by +5%.");
			CfgKeyScaleDown = ((BasePlugin)this).Config.Bind<string>("Hotkeys", "ScaleDown", "F10", "Decrease UiScale by −5%.");
			_lastEnabledState = CfgEnabled.Value;
			UpdateParsedHotkeys();
			UpdateCachedNameTokens();
			ClassInjector.RegisterTypeInIl2Cpp<UiDriver>();
			GameObject val = new GameObject("ShadowsOfDoubt.UIScaler.Host");
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			_driver = val.AddComponent<UiDriver>();
			Info("Loaded.");
			DumpConfig();
			Scene activeScene = SceneManager.GetActiveScene();
			_lastSceneName = ((Scene)(ref activeScene)).name ?? string.Empty;
			_lastSceneIndex = ((Scene)(ref activeScene)).buildIndex;
			SafeRescan();
			WriteConfigHeaderIfMissing();
			StartConfigWatcher();
		}

		private void SafeRescan()
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Invalid comparison between Unknown and I4
			//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)
			Profiler.BeginSample("UIScaler.Rescan");
			try
			{
				int num = 0;
				_pruneKeys.Clear();
				foreach (KeyValuePair<int, (Canvas, OriginalScaler)> item in _originalById)
				{
					if ((Object)(object)item.Value.Item1 == (Object)null)
					{
						_pruneKeys.Add(item.Key);
					}
				}
				num = _pruneKeys.Count;
				for (int i = 0; i < _pruneKeys.Count; i++)
				{
					_originalById.Remove(_pruneKeys[i]);
				}
				_pruneKeys.Clear();
				Scene activeScene = SceneManager.GetActiveScene();
				Il2CppArrayBase<Canvas> val = Object.FindObjectsOfType<Canvas>(true);
				TryEnsureCapacity(_originalById, Math.Max(_originalById.Count, val.Length * 2));
				TryEnsureCapacity(_configuredCanvasIds, Math.Max(_configuredCanvasIds.Count, val.Length * 2));
				int num2 = 0;
				int num3 = 0;
				foreach (Canvas item2 in val)
				{
					if ((Object)(object)item2 == (Object)null || (!CfgIncludeWorldSpace.Value && (int)item2.renderMode == 2))
					{
						continue;
					}
					if (CfgActiveSceneOnly.Value)
					{
						Scene scene = ((Component)item2).gameObject.scene;
						if (((Scene)(ref scene)).handle != ((Scene)(ref activeScene)).handle)
						{
							continue;
						}
					}
					if (NameAllowed(((Object)item2).name))
					{
						num2++;
						if (ApplyScaler(item2))
						{
							num3++;
						}
					}
				}
				if (num3 > 0)
				{
					Canvas.ForceUpdateCanvases();
				}
				if (CfgVerbose.Value)
				{
					DebugMsg($"Rescan: seen={num2}, configuredNow={num3}, totalConfigured={_configuredCanvasIds.Count}, cleaned={num}");
				}
			}
			catch (Exception value)
			{
				Info($"Rescan error: {value}");
			}
			finally
			{
				Profiler.EndSample();
			}
		}

		private unsafe bool ApplyScaler(Canvas c)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057->IL0057: Incompatible stack types: Ref vs I4
			//IL_0050->IL0057: Incompatible stack types: I4 vs Ref
			//IL_0050->IL0057: Incompatible stack types: Ref vs I4
			//IL_00ba->IL00ba: Incompatible stack types: Ref vs I4
			//IL_00b3->IL00ba: Incompatible stack types: I4 vs Ref
			//IL_00b3->IL00ba: Incompatible stack types: Ref vs I4
			Profiler.BeginSample("UIScaler.Apply");
			try
			{
				int instanceID = ((Object)c).GetInstanceID();
				if (!_originalById.ContainsKey(instanceID))
				{
					CanvasScaler component = ((Component)c).GetComponent<CanvasScaler>();
					OriginalScaler originalScaler = default(OriginalScaler);
					originalScaler.existed = (Object)(object)component != (Object)null;
					ref OriginalScaler reference = ref originalScaler;
					int num;
					if ((Object)(object)component != (Object)null)
					{
						reference = ref *(OriginalScaler*)component.uiScaleMode;
						num = (int)(ref reference);
					}
					else
					{
						num = 0;
						reference = ref *(OriginalScaler*)num;
						num = (int)(ref reference);
					}
					Unsafe.Write(&((OriginalScaler*)num)->uiScaleMode, (ScaleMode)(ref reference));
					originalScaler.referenceResolution = (Vector2)(((Object)(object)component != (Object)null) ? component.referenceResolution : new Vector2(1920f, 1080f));
					originalScaler.matchWidthOrHeight = (((Object)(object)component != (Object)null) ? component.matchWidthOrHeight : 0.5f);
					ref OriginalScaler reference2 = ref originalScaler;
					int num2;
					if ((Object)(object)component != (Object)null)
					{
						reference2 = ref *(OriginalScaler*)component.screenMatchMode;
						num2 = (int)(ref reference2);
					}
					else
					{
						num2 = 0;
						reference2 = ref *(OriginalScaler*)num2;
						num2 = (int)(ref reference2);
					}
					Unsafe.Write(&((OriginalScaler*)num2)->screenMatchMode, (ScreenMatchMode)(ref reference2));
					OriginalScaler item = originalScaler;
					_originalById[instanceID] = (c, item);
				}
				if (_configuredCanvasIds.Contains(instanceID))
				{
					return false;
				}
				CanvasScaler val = ((Component)c).GetComponent<CanvasScaler>();
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)c).gameObject.AddComponent<CanvasScaler>();
				}
				val.uiScaleMode = (ScaleMode)1;
				float num3 = Mathf.Max(0.1f, Mathf.Min(5f, CfgUiScale.Value));
				Vector2 referenceResolution = default(Vector2);
				((Vector2)(ref referenceResolution))..ctor((float)Mathf.Max(1, Mathf.RoundToInt((float)CfgRefWidth.Value / num3)), (float)Mathf.Max(1, Mathf.RoundToInt((float)CfgRefHeight.Value / num3)));
				val.referenceResolution = referenceResolution;
				val.screenMatchMode = (ScreenMatchMode)0;
				val.matchWidthOrHeight = Mathf.Clamp01(CfgMatchWH.Value);
				_configuredCanvasIds.Add(instanceID);
				if (CfgVerbose.Value)
				{
					DebugMsg($"Configured '{((Object)c).name}' (mode={c.renderMode}, ref={val.referenceResolution}, match={val.matchWidthOrHeight:0.00})");
				}
				return true;
			}
			catch (Exception value)
			{
				Info($"ApplyScaler error on canvas '{((c != null) ? ((Object)c).name : null)}': {value}");
				return false;
			}
			finally
			{
				Profiler.EndSample();
			}
		}

		private void RevertAll()
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = 0;
			Profiler.BeginSample("UIScaler.RevertAll");
			try
			{
				_pruneKeys.Clear();
				foreach (KeyValuePair<int, (Canvas, OriginalScaler)> item in _originalById)
				{
					int key = item.Key;
					var (val, originalScaler) = item.Value;
					if ((Object)(object)val == (Object)null)
					{
						_pruneKeys.Add(key);
						continue;
					}
					CanvasScaler val2 = ((Component)val).GetComponent<CanvasScaler>();
					if (!originalScaler.existed && (Object)(object)val2 != (Object)null)
					{
						Object.Destroy((Object)(object)val2);
						num++;
					}
					else if (originalScaler.existed)
					{
						if ((Object)(object)val2 == (Object)null)
						{
							val2 = ((Component)val).gameObject.AddComponent<CanvasScaler>();
						}
						val2.uiScaleMode = originalScaler.uiScaleMode;
						val2.referenceResolution = originalScaler.referenceResolution;
						val2.matchWidthOrHeight = originalScaler.matchWidthOrHeight;
						val2.screenMatchMode = originalScaler.screenMatchMode;
						num++;
					}
					_configuredCanvasIds.Remove(key);
				}
				num2 = _pruneKeys.Count;
				for (int i = 0; i < _pruneKeys.Count; i++)
				{
					_originalById.Remove(_pruneKeys[i]);
				}
				_pruneKeys.Clear();
				Info($"Reverted {num} canvases; cleaned {num2} stale entries.");
			}
			finally
			{
				Profiler.EndSample();
			}
			Canvas.ForceUpdateCanvases();
		}

		private void WriteConfigHeaderIfMissing()
		{
			try
			{
				string path = Path.Combine(Paths.ConfigPath, "khundian.sod.uiscaler.cfg");
				if (File.Exists(path))
				{
					string text = File.ReadAllText(path);
					if (!text.StartsWith("# === Shadows of Doubt UI Scaler ===", StringComparison.Ordinal))
					{
						string text2 = string.Join(Environment.NewLine, "# === Shadows of Doubt UI Scaler ===", "# Version: 1.0.1", "#", "# Scales UI using Unity's Canvas Scaler (Scale With Screen Size).", "# [Hotkeys] use Unity KeyCode names (F9, F11, F10, BackQuote, etc.).", "# Toggle default is F9 (avoids Steam F12 screenshot).", "#", "");
						File.WriteAllText(path, text2 + text);
					}
				}
			}
			catch (Exception ex)
			{
				Info("Config header write skipped: " + ex.Message);
			}
		}

		private void StartConfigWatcher()
		{
			try
			{
				string configPath = Paths.ConfigPath;
				string filter = "khundian.sod.uiscaler.cfg";
				_cfgWatcher = new FileSystemWatcher(configPath, filter)
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
					IncludeSubdirectories = false,
					EnableRaisingEvents = true
				};
				_cfgWatcher.Changed += OnCfgChanged;
				_cfgWatcher.Created += OnCfgChanged;
				_cfgWatcher.Renamed += OnCfgRenamed;
				Info("Config watcher started.");
			}
			catch (Exception ex)
			{
				Info("Config watcher unavailable: " + ex.Message);
			}
		}

		private void OnCfgChanged(object sender, FileSystemEventArgs e)
		{
			Interlocked.Exchange(ref _cfgReloadRequested, 1);
		}

		private void OnCfgRenamed(object sender, RenamedEventArgs e)
		{
			OnCfgChanged(sender, e);
		}

		private static void TryEnsureCapacity<TVal>(Dictionary<int, TVal> dict, int capacity)
		{
			try
			{
				MethodInfo method = dict.GetType().GetMethod("EnsureCapacity", new Type[1] { typeof(int) });
				if (method != null)
				{
					method.Invoke(dict, new object[1] { capacity });
				}
			}
			catch
			{
			}
		}

		private static void TryEnsureCapacity(HashSet<int> set, int capacity)
		{
			try
			{
				MethodInfo method = set.GetType().GetMethod("EnsureCapacity", new Type[1] { typeof(int) });
				if (method != null)
				{
					method.Invoke(set, new object[1] { capacity });
				}
			}
			catch
			{
			}
		}

		private static string[] SplitTokens(string csv)
		{
			if (string.IsNullOrWhiteSpace(csv))
			{
				return Array.Empty<string>();
			}
			return (from s in csv.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim() into s
				where s.Length > 0
				select s).ToArray();
		}

		private bool NameAllowed(string name)
		{
			string name2 = name;
			string[] incTokens = _incTokens;
			string[] excTokens = _excTokens;
			if (incTokens.Length != 0 && !incTokens.Any((string tok) => name2.IndexOf(tok, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return false;
			}
			if (excTokens.Length != 0 && excTokens.Any((string tok) => name2.IndexOf(tok, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return false;
			}
			return true;
		}

		private void UpdateCachedNameTokens()
		{
			_incTokens = SplitTokens(CfgCanvasIncludeTokens.Value);
			_excTokens = SplitTokens(CfgCanvasExcludeTokens.Value);
		}

		private static KeyCode ParseKey(string s, KeyCode fallback)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(s))
			{
				return fallback;
			}
			try
			{
				return (KeyCode)Enum.Parse(typeof(KeyCode), s, ignoreCase: true);
			}
			catch
			{
				return fallback;
			}
		}

		private void UpdateParsedHotkeys()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			_keyToggle = ParseKey(CfgKeyToggle.Value, _keyToggle);
			_keyUp = ParseKey(CfgKeyScaleUp.Value, _keyUp);
			_keyDown = ParseKey(CfgKeyScaleDown.Value, _keyDown);
		}

		private void DumpConfig()
		{
			Info($"Enabled={CfgEnabled.Value} Scale={CfgUiScale.Value:0.00} Ref={CfgRefWidth.Value}x{CfgRefHeight.Value} Match={CfgMatchWH.Value:0.00} IncludeWS={CfgIncludeWorldSpace.Value} ActiveSceneOnly={CfgActiveSceneOnly.Value} RescanMS={CfgRescanMS.Value}");
		}

		internal void Info(string s)
		{
			((BasePlugin)this).Log.LogInfo((object)s);
		}

		internal void DebugMsg(string s)
		{
			if (CfgVerbose.Value)
			{
				((BasePlugin)this).Log.LogDebug((object)s);
			}
		}
	}
}