Decompiled source of GK2Plus v0.1.0

BepInEx\plugins\GK2Plus\GK2Plus.dll

Decompiled 19 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GK2Plus.Core;
using GK2Plus.Features.Cheats;
using GK2Plus.Features.General;
using GK2Plus.Framework;
using GK2Plus.Framework.Crafting;
using GK2Plus.Framework.Diagnostics;
using GK2Plus.Framework.Events;
using GK2Plus.Framework.Farming;
using GK2Plus.Framework.Inventory;
using GK2Plus.Framework.Localization;
using GK2Plus.Framework.Quests;
using GK2Plus.Framework.Saves;
using GK2Plus.Framework.UI;
using GK2Plus.Framework.World;
using GK2Plus.Framework.Zombies;
using HarmonyLib;
using LazyBearTechnology;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("duhhbzz")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+76b76c9815df8dfd14cd1e3d11c5df32a3ded7e0")]
[assembly: AssemblyProduct("GK2Plus")]
[assembly: AssemblyTitle("GK2Plus")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GK2Plus
{
	[BepInPlugin("com.duhhbzz.gk2plus", "GK2+", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private FeatureRegistry _featureRegistry;

		private CompatibilityManager _compatibilityManager;

		private GK2Services _services;

		internal static ConfigEntry<bool> MasterEnabled { get; private set; }

		private void Awake()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			((BaseUnityPlugin)this).Logger.LogInfo((object)"----------------------------------------");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GK2+ v0.1.0");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Graveyard Keeper Plus");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"----------------------------------------");
			MasterEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for GK2+ gameplay features.");
			_harmony = new Harmony("com.duhhbzz.gk2plus");
			_compatibilityManager = new CompatibilityManager(((BaseUnityPlugin)this).Logger);
			_compatibilityManager.Scan();
			_services = new GK2Services(((BaseUnityPlugin)this).Logger);
			_services.Initialize();
			FrameworkDiagnostics.LogReady(((BaseUnityPlugin)this).Logger);
			((MonoBehaviour)this).StartCoroutine(MainMenuBadgeController.Run(((BaseUnityPlugin)this).Logger));
			_featureRegistry = new FeatureRegistry();
			RegisterFeatures(_featureRegistry);
			if (MasterEnabled.Value)
			{
				_featureRegistry.InitializeAll(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, _harmony);
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"GK2+ master switch is disabled. Gameplay features will not initialize.");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GK2+ v0.1.0 loaded successfully.");
		}

		private void RegisterFeatures(FeatureRegistry registry)
		{
			registry.Register(new ManualSaveFeature(_services.Saves));
			registry.Register(new BasicCheatsFeature(_services.Saves, _services.UI));
		}

		private void OnDestroy()
		{
			_services?.Shutdown();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace GK2Plus.Framework
{
	internal abstract class GK2ServiceBase : IGK2Service
	{
		protected ManualLogSource Logger { get; }

		public abstract string Name { get; }

		protected GK2ServiceBase(ManualLogSource logger)
		{
			Logger = logger;
		}

		public virtual void Initialize()
		{
			Logger.LogDebug((object)("Initializing framework service: " + Name));
		}

		public virtual void Shutdown()
		{
			Logger.LogDebug((object)("Shutting down framework service: " + Name));
		}
	}
	internal sealed class GK2Services
	{
		private readonly ManualLogSource _logger;

		private readonly List<IGK2Service> _services;

		public GK2EventService Events { get; }

		public GK2SaveService Saves { get; }

		public GK2WorldService World { get; }

		public GK2InventoryService Inventory { get; }

		public GK2CraftingService Crafting { get; }

		public GK2FarmingService Farming { get; }

		public GK2ZombieService Zombies { get; }

		public GK2QuestService Quests { get; }

		public GK2LocalizationService Localization { get; }

		public GK2UIService UI { get; }

		public GK2Services(ManualLogSource logger)
		{
			_logger = logger ?? throw new ArgumentNullException("logger");
			Events = new GK2EventService(logger);
			Saves = new GK2SaveService(logger);
			World = new GK2WorldService(logger);
			Inventory = new GK2InventoryService(logger);
			Crafting = new GK2CraftingService(logger);
			Farming = new GK2FarmingService(logger);
			Zombies = new GK2ZombieService(logger);
			Quests = new GK2QuestService(logger);
			Localization = new GK2LocalizationService(logger);
			UI = new GK2UIService(logger);
			_services = new List<IGK2Service> { Events, Saves, World, Inventory, Crafting, Farming, Zombies, Quests, Localization, UI };
		}

		public void Initialize()
		{
			_logger.LogInfo((object)$"Initializing {_services.Count} GK2+ framework service(s)...");
			foreach (IGK2Service service in _services)
			{
				service.Initialize();
			}
		}

		public void Shutdown()
		{
			for (int num = _services.Count - 1; num >= 0; num--)
			{
				try
				{
					_services[num].Shutdown();
				}
				catch (Exception arg)
				{
					_logger.LogError((object)$"Failed to shut down framework service '{_services[num].Name}': {arg}");
				}
			}
		}
	}
	internal interface IGK2Service
	{
		string Name { get; }

		void Initialize();

		void Shutdown();
	}
}
namespace GK2Plus.Framework.Zombies
{
	internal sealed class GK2ZombieService : GK2ServiceBase
	{
		public override string Name => "Zombies";

		public GK2ZombieService(ManualLogSource logger)
			: base(logger)
		{
		}
	}
}
namespace GK2Plus.Framework.World
{
	internal sealed class GK2WorldService : GK2ServiceBase
	{
		public override string Name => "World";

		public GK2WorldService(ManualLogSource logger)
			: base(logger)
		{
		}
	}
}
namespace GK2Plus.Framework.UI
{
	internal sealed class GK2MenuAction
	{
		public string Id { get; }

		public string Tab { get; }

		public string Label { get; }

		public Action Execute { get; }

		public Func<bool> IsAvailable { get; }

		public GK2MenuAction(string id, string tab, string label, Action execute, Func<bool> isAvailable = null)
		{
			Id = id ?? throw new ArgumentNullException("id");
			Tab = tab ?? throw new ArgumentNullException("tab");
			Label = label ?? throw new ArgumentNullException("label");
			Execute = execute ?? throw new ArgumentNullException("execute");
			IsAvailable = isAvailable;
		}

		public bool CanExecute()
		{
			if (IsAvailable != null)
			{
				return IsAvailable();
			}
			return true;
		}
	}
	internal sealed class GK2UIService : GK2ServiceBase
	{
		private readonly List<GK2MenuAction> _menuActions = new List<GK2MenuAction>();

		private readonly Dictionary<string, Func<string>> _tabNotices = new Dictionary<string, Func<string>>();

		private ModMenuController _modMenuController;

		public override string Name => "UI";

		public GK2UIService(ManualLogSource logger)
			: base(logger)
		{
		}

		public override void Initialize()
		{
			base.Initialize();
			_modMenuController = ModMenuController.Create(base.Logger);
			foreach (GK2MenuAction menuAction in _menuActions)
			{
				_modMenuController.RegisterMenuAction(menuAction);
			}
			foreach (KeyValuePair<string, Func<string>> tabNotice in _tabNotices)
			{
				_modMenuController.RegisterTabNotice(tabNotice.Key, tabNotice.Value);
			}
			base.Logger.LogInfo((object)"GK2+ UI service owns the persistent mod-menu controller.");
		}

		public void RegisterMenuAction(GK2MenuAction action)
		{
			if (action != null)
			{
				_menuActions.Add(action);
				if ((Object)(object)_modMenuController != (Object)null)
				{
					_modMenuController.RegisterMenuAction(action);
				}
			}
		}

		public void RegisterTabNotice(string tab, Func<string> noticeProvider)
		{
			if (!string.IsNullOrWhiteSpace(tab) && noticeProvider != null)
			{
				_tabNotices[tab] = noticeProvider;
				if ((Object)(object)_modMenuController != (Object)null)
				{
					_modMenuController.RegisterTabNotice(tab, noticeProvider);
				}
			}
		}

		public void RefreshMenu()
		{
			_modMenuController?.RefreshActiveTab();
		}

		public void HideMenu()
		{
			_modMenuController?.HideMenu();
		}

		public void ShowMenu()
		{
			_modMenuController?.ShowMenu();
		}

		public override void Shutdown()
		{
			if ((Object)(object)_modMenuController != (Object)null)
			{
				_modMenuController.ShutdownController();
			}
			_modMenuController = null;
			_menuActions.Clear();
			_tabNotices.Clear();
			base.Shutdown();
		}
	}
	internal static class MainMenuBadgeController
	{
		private const string BadgeObjectName = "GK2PlusMainMenuBadge";

		private static bool _created;

		public static IEnumerator Run(ManualLogSource logger)
		{
			if (_created)
			{
				yield break;
			}
			Component mainMenu = null;
			for (int frame = 0; frame < 7200; frame++)
			{
				if (!((Object)(object)mainMenu == (Object)null))
				{
					break;
				}
				mainMenu = FindActiveMainMenuWindow();
				if ((Object)(object)mainMenu == (Object)null)
				{
					yield return null;
				}
			}
			if ((Object)(object)mainMenu == (Object)null)
			{
				logger.LogWarning((object)"GK2+ main-menu badge could not find an active UIMainMenuWindow.");
				yield break;
			}
			for (int frame = 0; frame < 5; frame++)
			{
				yield return null;
			}
			try
			{
				CreateBadge(mainMenu.transform, logger);
				_created = true;
				logger.LogInfo((object)"GK2+ main-menu badge created.");
			}
			catch (Exception arg)
			{
				logger.LogError((object)$"Failed to create GK2+ main-menu badge: {arg}");
			}
		}

		private static Component FindActiveMainMenuWindow()
		{
			MonoBehaviour[] array = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
			foreach (MonoBehaviour val in array)
			{
				if (!((Object)(object)val == (Object)null) && ((object)val).GetType().Name == "UIMainMenuWindow" && ((Component)val).gameObject.activeInHierarchy)
				{
					return (Component)(object)val;
				}
			}
			return null;
		}

		private static void CreateBadge(Transform mainMenuRoot, ManualLogSource logger)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: 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_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_0346: Unknown result type (might be due to invalid IL or missing references)
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			Transform val = mainMenuRoot.Find("GK2PlusMainMenuBadge");
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
			Sprite val2 = FindSprite("titlescreen-menu-bg");
			Sprite val3 = FindSprite("comm-frame_1-border");
			Sprite val4 = FindSprite("widget_perks-text_decor-drk_1");
			Sprite val5 = FindSprite("wskull");
			if ((Object)(object)val3 == (Object)null)
			{
				throw new InvalidOperationException("Native frame sprite was not found.");
			}
			GameObject val6 = FindBodyTextTemplate(mainMenuRoot);
			GameObject val7 = FindTitleButtonTemplate(mainMenuRoot);
			if ((Object)(object)val6 == (Object)null)
			{
				throw new InvalidOperationException("Could not find a native body text template.");
			}
			if ((Object)(object)val7 == (Object)null)
			{
				throw new InvalidOperationException("Could not find the native New Game button background/title template.");
			}
			GameObject val8 = new GameObject("GK2PlusMainMenuBadge", new Type[1] { typeof(RectTransform) });
			val8.transform.SetParent(mainMenuRoot, false);
			RectTransform val9 = (RectTransform)val8.transform;
			val9.anchorMin = new Vector2(1f, 1f);
			val9.anchorMax = new Vector2(1f, 1f);
			val9.pivot = new Vector2(1f, 1f);
			val9.anchoredPosition = new Vector2(-14f, -12f);
			val9.sizeDelta = new Vector2(176f, 84f);
			if ((Object)(object)val2 != (Object)null)
			{
				CreateStretchImage(val8.transform, "Background", val2, (Type)1, new Vector2(-4f, -4f), new Vector2(4f, 4f));
			}
			CreateStretchImage(val8.transform, "Frame", val3, (Type)1, Vector2.zero, Vector2.zero);
			GameObject obj = CreateNativeButtonTitle(val7, val8.transform, "GK2+ v0.1.0", new Vector2(0f, -15f));
			if ((Object)(object)val5 != (Object)null)
			{
				CreateFixedImage(val8.transform, "SkullTop", val5, (Type)0, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, -4f), new Vector2(14f, 11f));
			}
			CreateLabel(val6, val8.transform, "HotkeyText", "Press F2 for Mod Menu", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -39f), new Vector2(152f, 16f), 9f, new Color(0.93f, 0.78f, 0.5f, 1f));
			if ((Object)(object)val4 != (Object)null)
			{
				CreateFixedImage(val8.transform, "Divider", val4, (Type)1, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -55f), new Vector2(124f, 6f));
			}
			CreateLabel(val6, val8.transform, "StatusText", "Status: Loaded", new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -63f), new Vector2(146f, 14f), 8.5f, new Color(0.87f, 0.8f, 0.62f, 1f));
			obj.transform.SetAsLastSibling();
			if ((Object)(object)val5 != (Object)null)
			{
				Transform val10 = val8.transform.Find("SkullTop");
				if ((Object)(object)val10 != (Object)null)
				{
					val10.SetAsLastSibling();
				}
			}
			logger.LogDebug((object)"GK2+ badge now uses a cloned native main-menu button as the title bar.");
		}

		private static GameObject FindTitleButtonTemplate(Transform root)
		{
			Transform val = root.Find("Bg/Vertical Group/NewGame/Content/Back");
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).gameObject;
		}

		private static GameObject FindBodyTextTemplate(Transform root)
		{
			Transform val = root.Find("Bg/Vertical Group/ButtonTipsStr");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).gameObject;
			}
			Transform val2 = root.Find("Bg/Vertical Group/NewGame/Content/Back/Label");
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return ((Component)val2).gameObject;
		}

		private static GameObject CreateNativeButtonTitle(GameObject template, Transform parent, string text, Vector2 anchoredPosition)
		{
			//IL_002c: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, parent, false);
			((Object)val).name = "TitleButton";
			val.SetActive(true);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 1f);
			component.anchorMax = new Vector2(0.5f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = anchoredPosition;
			component.sizeDelta = new Vector2(142f, 26f);
			((Transform)component).localScale = Vector3.one;
			Component[] components = val.GetComponents<Component>();
			foreach (Component val2 in components)
			{
				if (!((Object)(object)val2 == (Object)null) && ((object)val2).GetType().Name == "HorizontalLayoutGroup")
				{
					Object.Destroy((Object)(object)val2);
				}
			}
			Transform obj = val.transform.Find("Label");
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("Cloned native button did not contain its Label child.");
			}
			GameObject gameObject = ((Component)obj).gameObject;
			components = gameObject.GetComponents<Component>();
			foreach (Component val3 in components)
			{
				if (!((Object)(object)val3 == (Object)null))
				{
					string name = ((object)val3).GetType().Name;
					if (name == "LocalizedLabel" || name == "LocalizedVerticalOffset")
					{
						Object.Destroy((Object)(object)val3);
					}
				}
			}
			RectTransform component2 = gameObject.GetComponent<RectTransform>();
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.pivot = new Vector2(0.5f, 0.5f);
			component2.anchoredPosition = Vector2.zero;
			component2.offsetMin = new Vector2(8f, 1f);
			component2.offsetMax = new Vector2(-8f, -1f);
			Component obj2 = FindTmp(gameObject);
			if ((Object)(object)obj2 == (Object)null)
			{
				throw new InvalidOperationException("Native title button label has no TextMeshProUGUI.");
			}
			SetProperty(obj2, "text", text);
			TrySetEnumProperty(obj2, "alignment", "Center");
			Image component3 = val.GetComponent<Image>();
			if ((Object)(object)component3 != (Object)null)
			{
				((Graphic)component3).raycastTarget = false;
			}
			return val;
		}

		private static Sprite FindSprite(string name)
		{
			return ((IEnumerable<Sprite>)Resources.FindObjectsOfTypeAll<Sprite>()).FirstOrDefault((Func<Sprite, bool>)((Sprite sprite) => (Object)(object)sprite != (Object)null && string.Equals(((Object)sprite).name, name, StringComparison.Ordinal)));
		}

		private static GameObject CreateStretchImage(Transform parent, string name, Sprite sprite, Type type, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_002e: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.offsetMin = offsetMin;
			val2.offsetMax = offsetMax;
			Image component = val.GetComponent<Image>();
			component.sprite = sprite;
			component.type = type;
			component.preserveAspect = false;
			((Graphic)component).raycastTarget = false;
			return val;
		}

		private static GameObject CreateFixedImage(Transform parent, string name, Sprite sprite, Type type, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size)
		{
			//IL_002e: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_0094: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform val2 = (RectTransform)val.transform;
			val2.anchorMin = anchorMin;
			val2.anchorMax = anchorMax;
			val2.pivot = pivot;
			val2.anchoredPosition = anchoredPosition;
			val2.sizeDelta = size;
			Image component = val.GetComponent<Image>();
			component.sprite = sprite;
			component.type = type;
			component.preserveAspect = true;
			((Graphic)component).raycastTarget = false;
			return val;
		}

		private static GameObject CreateLabel(GameObject template, Transform parent, string name, string text, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size, float fontSize, Color color)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, parent, false);
			((Object)val).name = name;
			val.SetActive(true);
			Component[] components = val.GetComponents<Component>();
			foreach (Component val2 in components)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					switch (((object)val2).GetType().Name)
					{
					case "LazyButtonTipsStr":
					case "LocalizedLabel":
					case "LocalizedVerticalOffset":
						Object.Destroy((Object)(object)val2);
						break;
					}
				}
			}
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = anchorMin;
			component.anchorMax = anchorMax;
			component.pivot = pivot;
			component.anchoredPosition = anchoredPosition;
			component.sizeDelta = size;
			Component obj = FindTmp(val);
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("Cloned native label has no TextMeshProUGUI.");
			}
			SetProperty(obj, "text", text);
			SetProperty(obj, "fontSize", fontSize);
			SetProperty(obj, "color", color);
			TrySetEnumProperty(obj, "fontStyle", "Normal");
			TrySetEnumProperty(obj, "fontWeight", "Regular");
			TrySetEnumProperty(obj, "alignment", "Center");
			return val;
		}

		private static Component FindTmp(GameObject obj)
		{
			return ((IEnumerable<Component>)obj.GetComponents<Component>()).FirstOrDefault((Func<Component, bool>)((Component component) => (Object)(object)component != (Object)null && ((object)component).GetType().Name == "TextMeshProUGUI"));
		}

		private static void TrySetEnumProperty(Component component, string propertyName, string enumValue)
		{
			PropertyInfo property = ((object)component).GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null || !property.CanWrite || !property.PropertyType.IsEnum)
			{
				return;
			}
			try
			{
				object value = Enum.Parse(property.PropertyType, enumValue);
				property.SetValue(component, value, null);
			}
			catch
			{
			}
		}

		private static void SetProperty(Component component, string propertyName, object value)
		{
			if (!((Object)(object)component == (Object)null))
			{
				PropertyInfo property = ((object)component).GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite)
				{
					property.SetValue(component, value, null);
				}
			}
		}
	}
	internal sealed class ModMenuController : MonoBehaviour
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__25_0;

			public static UnityAction <>9__25_1;

			public static UnityAction <>9__25_2;

			public static Func<GK2MenuAction, string> <>9__26_0;

			public static Func<Component, bool> <>9__47_0;

			internal void <BuildMenu>b__25_0()
			{
				Application.OpenURL("https://github.com/duhhbzz/GK2Plus");
			}

			internal void <BuildMenu>b__25_1()
			{
				Application.OpenURL("");
			}

			internal void <BuildMenu>b__25_2()
			{
				Application.OpenURL("https://github.com/duhhbzz/GK2Plus/issues/new");
			}

			internal string <BuildRegisteredActionButtons>b__26_0(GK2MenuAction action)
			{
				return action.Tab;
			}

			internal bool <FindTmp>b__47_0(Component component)
			{
				if ((Object)(object)component != (Object)null)
				{
					return ((object)component).GetType().Name == "TextMeshProUGUI";
				}
				return false;
			}
		}

		private const string RootObjectName = "GK2PlusModMenuRoot";

		private static readonly string[] Tabs = new string[7] { "General", "Inventory", "Crafting", "Farming", "Zombies", "Cheats", "More" };

		private ManualLogSource _logger;

		private GameObject _menuRoot;

		private GameObject _pageTitle;

		private GameObject _pageText;

		private GameObject _githubButton;

		private GameObject _nexusButton;

		private GameObject _bugButton;

		private GameObject _contentRoot;

		private GameObject _menuButtonLabelTemplate;

		private Sprite _menuButtonSprite;

		private readonly Dictionary<string, GameObject> _tabButtons = new Dictionary<string, GameObject>();

		private readonly List<GK2MenuAction> _registeredMenuActions = new List<GK2MenuAction>();

		private readonly Dictionary<GK2MenuAction, GameObject> _registeredActionButtons = new Dictionary<GK2MenuAction, GameObject>();

		private readonly Dictionary<string, Func<string>> _tabNotices = new Dictionary<string, Func<string>>(StringComparer.OrdinalIgnoreCase);

		private string _activeTab = "General";

		private bool _built;

		public static ModMenuController Create(ManualLogSource logger)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			GameObject val = GameObject.Find("GK2PlusModMenuController");
			if ((Object)(object)val != (Object)null)
			{
				ModMenuController component = val.GetComponent<ModMenuController>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			val = new GameObject("GK2PlusModMenuController");
			Object.DontDestroyOnLoad((Object)(object)val);
			ModMenuController modMenuController = val.AddComponent<ModMenuController>();
			modMenuController._logger = logger;
			return modMenuController;
		}

		public void RegisterMenuAction(GK2MenuAction action)
		{
			if (action == null)
			{
				return;
			}
			if (_registeredMenuActions.Any((GK2MenuAction existing) => string.Equals(existing.Id, action.Id, StringComparison.OrdinalIgnoreCase)))
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)("GK2+ ignored duplicate menu action id '" + action.Id + "'."));
				}
				return;
			}
			_registeredMenuActions.Add(action);
			if (_built && (Object)(object)_contentRoot != (Object)null && (Object)(object)_menuButtonLabelTemplate != (Object)null && (Object)(object)_menuButtonSprite != (Object)null)
			{
				BuildRegisteredActionButtons();
				SetActiveTab(_activeTab);
			}
		}

		public void RegisterTabNotice(string tab, Func<string> noticeProvider)
		{
			if (!string.IsNullOrWhiteSpace(tab) && noticeProvider != null)
			{
				_tabNotices[tab] = noticeProvider;
				if (_built && string.Equals(_activeTab, tab, StringComparison.OrdinalIgnoreCase))
				{
					SetText(_pageText, GetPlaceholderText(_activeTab));
				}
			}
		}

		private void Start()
		{
			((MonoBehaviour)this).StartCoroutine(BootstrapWhenReady());
		}

		private IEnumerator BootstrapWhenReady()
		{
			for (int frame = 0; frame < 7200; frame++)
			{
				if (TryGetReadyContext(out var _, out var uiRoot, out var bodyTemplate, out var buttonLabelTemplate))
				{
					try
					{
						BuildMenu((Transform)(object)uiRoot, bodyTemplate, buttonLabelTemplate);
						_built = true;
						ManualLogSource logger = _logger;
						if (logger != null)
						{
							logger.LogInfo((object)"GK2+ mod menu shell ready under persistent GUIElements.Root. Press F2 to toggle.");
						}
						yield break;
					}
					catch (Exception arg)
					{
						CleanupPartialMenu();
						ManualLogSource logger2 = _logger;
						if (logger2 != null)
						{
							logger2.LogError((object)$"GK2+ mod menu shell build failed once: {arg}");
						}
						yield break;
					}
				}
				yield return null;
			}
			ManualLogSource logger3 = _logger;
			if (logger3 != null)
			{
				logger3.LogWarning((object)"GK2+ mod menu shell timed out waiting for native UI.");
			}
		}

		private void Update()
		{
			if (!_built || (Object)(object)_menuRoot == (Object)null)
			{
				return;
			}
			if (Input.GetKeyDown((KeyCode)283))
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogInfo((object)"GK2+ F2 detected; toggling mod menu.");
				}
				ToggleMenu();
			}
			else if (_menuRoot.activeSelf && Input.GetKeyDown((KeyCode)27))
			{
				HideMenu();
			}
		}

		private bool TryGetReadyContext(out Component mainMenu, out RectTransform uiRoot, out GameObject bodyTemplate, out GameObject buttonLabelTemplate)
		{
			mainMenu = null;
			uiRoot = null;
			bodyTemplate = null;
			buttonLabelTemplate = null;
			MonoBehaviour[] array = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
			foreach (MonoBehaviour val in array)
			{
				if (!((Object)(object)val == (Object)null) && !(((object)val).GetType().Name != "UIMainMenuWindow") && ((Component)val).gameObject.activeInHierarchy)
				{
					mainMenu = (Component)(object)val;
					break;
				}
			}
			if ((Object)(object)mainMenu == (Object)null)
			{
				return false;
			}
			GUIElements instance = GUIElements.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.Root == (Object)null || !((Component)instance).gameObject.activeInHierarchy)
			{
				return false;
			}
			uiRoot = instance.Root;
			Transform transform = mainMenu.transform;
			Transform val2 = transform.Find("Bg/Vertical Group/ButtonTipsStr");
			Transform val3 = transform.Find("Bg/Vertical Group/NewGame/Content/Back/Label");
			if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
			{
				return false;
			}
			if ((Object)(object)FindTmp(((Component)val2).gameObject) == (Object)null || (Object)(object)FindTmp(((Component)val3).gameObject) == (Object)null)
			{
				return false;
			}
			bodyTemplate = ((Component)val2).gameObject;
			buttonLabelTemplate = ((Component)val3).gameObject;
			return true;
		}

		private void BuildMenu(Transform uiRoot, GameObject bodyTemplate, GameObject buttonLabelTemplate)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00b5: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Expected O, but got Unknown
			//IL_035a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0406: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0440: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_049a: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0508: Unknown result type (might be due to invalid IL or missing references)
			//IL_0517: Unknown result type (might be due to invalid IL or missing references)
			//IL_0526: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_0567: Unknown result type (might be due to invalid IL or missing references)
			//IL_0576: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_0594: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05da: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0607: Unknown result type (might be due to invalid IL or missing references)
			//IL_069f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d2: Expected O, but got Unknown
			//IL_077b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0782: Expected O, but got Unknown
			//IL_07a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_07bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_07d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_07fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0821: Unknown result type (might be due to invalid IL or missing references)
			//IL_0861: Unknown result type (might be due to invalid IL or missing references)
			//IL_0870: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_08b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_08c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_08cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_08de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0946: Unknown result type (might be due to invalid IL or missing references)
			//IL_0955: Unknown result type (might be due to invalid IL or missing references)
			//IL_071c: Unknown result type (might be due to invalid IL or missing references)
			//IL_072b: Unknown result type (might be due to invalid IL or missing references)
			//IL_073a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0749: Unknown result type (might be due to invalid IL or missing references)
			//IL_0758: Unknown result type (might be due to invalid IL or missing references)
			//IL_0988: Unknown result type (might be due to invalid IL or missing references)
			//IL_098d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0993: Expected O, but got Unknown
			//IL_09c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_09d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a45: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a54: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a87: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a8c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a92: Expected O, but got Unknown
			//IL_0a1a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a1f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a25: Expected O, but got Unknown
			//IL_0b16: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b25: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b5b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b66: Unknown result type (might be due to invalid IL or missing references)
			//IL_0abf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ace: Unknown result type (might be due to invalid IL or missing references)
			//IL_0add: Unknown result type (might be due to invalid IL or missing references)
			//IL_0aec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0afb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c8c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c99: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cc6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cd0: Expected O, but got Unknown
			//IL_0d0d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d12: Unknown result type (might be due to invalid IL or missing references)
			Transform val = uiRoot.Find("GK2PlusModMenuRoot");
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
			Sprite val2 = FindSprite("comm-frame_1-border");
			Sprite val3 = FindSprite("titlescreen-menu-bg");
			Sprite val4 = FindSprite("widget_perks-text_decor-drk_1");
			Sprite val5 = FindSprite("comm-btn-simple_red-active");
			if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val5 == (Object)null)
			{
				throw new InvalidOperationException("Required native GK2 window/button sprites are not loaded.");
			}
			GameObject val6 = new GameObject("GK2PlusModMenuRoot", new Type[1] { typeof(RectTransform) });
			val6.transform.SetParent(uiRoot, false);
			val6.transform.SetAsLastSibling();
			RectTransform component = val6.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			_menuRoot = val6;
			Canvas val7 = val6.AddComponent<Canvas>();
			val7.overrideSorting = true;
			val7.sortingOrder = 30000;
			if ((Object)(object)val6.GetComponent<GraphicRaycaster>() == (Object)null)
			{
				val6.AddComponent<GraphicRaycaster>();
			}
			GameObject obj = CreateImage(val6.transform, "Dimmer", null, (Type)0, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
			Image component2 = obj.GetComponent<Image>();
			((Graphic)component2).color = new Color(0.03f, 0.01f, 0.03f, 0.18f);
			((Graphic)component2).raycastTarget = true;
			obj.transform.SetAsFirstSibling();
			GameObject val8 = new GameObject("Window", new Type[1] { typeof(RectTransform) });
			val8.transform.SetParent(val6.transform, false);
			val8.transform.SetAsLastSibling();
			RectTransform component3 = val8.GetComponent<RectTransform>();
			component3.anchorMin = new Vector2(0.5f, 0.5f);
			component3.anchorMax = new Vector2(0.5f, 0.5f);
			component3.pivot = new Vector2(0.5f, 0.5f);
			component3.anchoredPosition = Vector2.zero;
			component3.sizeDelta = new Vector2(440f, 300f);
			((Graphic)CreateImage(val8.transform, "SolidBacking", null, (Type)0, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero).GetComponent<Image>()).color = new Color(0.24f, 0.05f, 0.13f, 0.99f);
			((Graphic)CreateStretchImage(val8.transform, "Background", val3, (Type)1, new Vector2(-3f, -3f), new Vector2(3f, 3f)).GetComponent<Image>()).color = new Color(1f, 1f, 1f, 0.98f);
			CreateStretchImage(val8.transform, "Frame", val2, (Type)1, Vector2.zero, Vector2.zero);
			Vector4 val9 = default(Vector4);
			((Vector4)(ref val9))..ctor(9f, 7f, 9f, 7f);
			GameObject val10 = new GameObject("ContentSafeArea", new Type[1] { typeof(RectTransform) });
			val10.transform.SetParent(val8.transform, false);
			RectTransform component4 = val10.GetComponent<RectTransform>();
			component4.anchorMin = Vector2.zero;
			component4.anchorMax = Vector2.one;
			component4.pivot = new Vector2(0.5f, 0.5f);
			component4.offsetMin = new Vector2(val9.x, val9.y);
			component4.offsetMax = new Vector2(0f - val9.z, 0f - val9.w);
			((Transform)component4).localScale = Vector3.one;
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				string text = $"spriteBorder={val2.border}, ";
				string text2 = $"visualInsetsUi(L,B,R,T)={val9}, ";
				Rect rect = component4.rect;
				logger.LogInfo((object)("GK2+ frame safe area: " + text + text2 + $"safeSize={((Rect)(ref rect)).size}"));
			}
			CreateNativeTitleText(buttonLabelTemplate, val8.transform, "GK2+ Mod Menu", new Vector2(0f, -16f), new Vector2(210f, 20f), 0.72f);
			CreateBodyText(bodyTemplate, val8.transform, "VersionText", "v0.1.0", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(18f, -17f), new Vector2(64f, 14f), 8f, "Left");
			CreateBodyText(bodyTemplate, val8.transform, "HeaderF2Hint", "F2 Toggle", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-94f, -17f), new Vector2(58f, 14f), 8f, "Center");
			CreateBodyText(bodyTemplate, val8.transform, "HeaderEscHint", "ESC Close", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-18f, -17f), new Vector2(62f, 14f), 8f, "Center");
			if ((Object)(object)val4 != (Object)null)
			{
				CreateFixedImage(val8.transform, "HeaderDivider", val4, (Type)1, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -35f), new Vector2(390f, 5f));
			}
			float num = 54f;
			float num2 = 18f;
			float num3 = 2f;
			float num4 = (0f - ((float)Tabs.Length * num + (float)(Tabs.Length - 1) * num3)) / 2f + num / 2f;
			for (int i = 0; i < Tabs.Length; i++)
			{
				string tab = Tabs[i];
				float num5 = num4 + (float)i * (num + num3);
				GameObject val11 = CreateActionButton(buttonLabelTemplate, val8.transform, val5, tab, new Vector2(num5, -52f), new Vector2(num, num2));
				((UnityEvent)val11.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					SetActiveTab(tab);
				});
				_tabButtons[tab] = val11;
			}
			if ((Object)(object)val4 != (Object)null)
			{
				CreateFixedImage(val8.transform, "TabDivider", val4, (Type)1, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -69f), new Vector2(390f, 5f));
			}
			GameObject val12 = new GameObject("Content", new Type[1] { typeof(RectTransform) });
			val12.transform.SetParent(val8.transform, false);
			RectTransform component5 = val12.GetComponent<RectTransform>();
			component5.anchorMin = new Vector2(0.5f, 1f);
			component5.anchorMax = new Vector2(0.5f, 1f);
			component5.pivot = new Vector2(0.5f, 1f);
			component5.anchoredPosition = new Vector2(0f, -77f);
			component5.sizeDelta = new Vector2(392f, 176f);
			Image obj2 = val12.AddComponent<Image>();
			((Graphic)obj2).color = new Color(0.12f, 0.02f, 0.07f, 0.86f);
			((Graphic)obj2).raycastTarget = false;
			_contentRoot = val12;
			_menuButtonLabelTemplate = buttonLabelTemplate;
			_menuButtonSprite = val5;
			_pageTitle = CreateNativeTitleText(buttonLabelTemplate, val12.transform, "General", new Vector2(0f, -13f), new Vector2(190f, 20f), 0.62f);
			_pageText = CreateBodyText(bodyTemplate, val12.transform, "PageText", "", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), new Vector2(350f, 102f), 10f, "Center");
			Component component6 = FindTmp(_pageText);
			SetProperty(component6, "characterSpacing", 2.45f);
			SetProperty(component6, "wordSpacing", 1.25f);
			_githubButton = CreateActionButton(buttonLabelTemplate, val12.transform, val5, "GitHub", new Vector2(-92f, -142f), new Vector2(78f, 20f));
			ButtonClickedEvent onClick = _githubButton.GetComponent<Button>().onClick;
			object obj3 = <>c.<>9__25_0;
			if (obj3 == null)
			{
				UnityAction val13 = delegate
				{
					Application.OpenURL("https://github.com/duhhbzz/GK2Plus");
				};
				<>c.<>9__25_0 = val13;
				obj3 = (object)val13;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj3);
			_nexusButton = CreateActionButton(buttonLabelTemplate, val12.transform, val5, ProjectLinks.HasNexusUrl ? "Nexus Mods" : "Nexus Soon", new Vector2(0f, -142f), new Vector2(92f, 20f));
			Button component7 = _nexusButton.GetComponent<Button>();
			((Selectable)component7).interactable = ProjectLinks.HasNexusUrl;
			if (ProjectLinks.HasNexusUrl)
			{
				ButtonClickedEvent onClick2 = component7.onClick;
				object obj4 = <>c.<>9__25_1;
				if (obj4 == null)
				{
					UnityAction val14 = delegate
					{
						Application.OpenURL("");
					};
					<>c.<>9__25_1 = val14;
					obj4 = (object)val14;
				}
				((UnityEvent)onClick2).AddListener((UnityAction)obj4);
			}
			_bugButton = CreateActionButton(buttonLabelTemplate, val12.transform, val5, "Report Bug", new Vector2(100f, -142f), new Vector2(92f, 20f));
			ButtonClickedEvent onClick3 = _bugButton.GetComponent<Button>().onClick;
			object obj5 = <>c.<>9__25_2;
			if (obj5 == null)
			{
				UnityAction val15 = delegate
				{
					Application.OpenURL("https://github.com/duhhbzz/GK2Plus/issues/new");
				};
				<>c.<>9__25_2 = val15;
				obj5 = (object)val15;
			}
			((UnityEvent)onClick3).AddListener((UnityAction)obj5);
			BuildRegisteredActionButtons();
			if ((Object)(object)val4 != (Object)null)
			{
				CreateFixedImage(val8.transform, "FooterDivider", val4, (Type)1, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 36f), new Vector2(390f, 5f));
			}
			GameObject val16 = CreateActionButton(buttonLabelTemplate, val10.transform, val5, "Close", Vector2.zero, new Vector2(72f, 20f));
			RectTransform component8 = val16.GetComponent<RectTransform>();
			component8.SetInsetAndSizeFromParentEdge((Edge)1, 4f, 72f);
			component8.SetInsetAndSizeFromParentEdge((Edge)3, 4f, 20f);
			((Transform)component8).localScale = Vector3.one;
			((Transform)component8).localRotation = Quaternion.identity;
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Vector3[] array2 = (Vector3[])(object)new Vector3[4];
			Vector3[] array3 = (Vector3[])(object)new Vector3[4];
			component3.GetWorldCorners(array);
			component4.GetWorldCorners(array2);
			component8.GetWorldCorners(array3);
			float num6 = array2[2].x - array3[2].x;
			float num7 = array3[0].y - array2[0].y;
			float num8 = array[2].x - array3[2].x;
			float num9 = array3[0].y - array[0].y;
			ManualLogSource logger2 = _logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)("GK2+ Close safe dock: " + $"safeRightGapPx={num6:0.##}, " + $"safeBottomGapPx={num7:0.##}, " + $"totalRightGapPx={num8:0.##}, " + $"totalBottomGapPx={num9:0.##}, " + $"buttonBL={array3[0]}, buttonTR={array3[2]}"));
			}
			((UnityEvent)val16.GetComponent<Button>().onClick).AddListener(new UnityAction(HideMenu));
			SetActiveTab("General");
			_menuRoot.SetActive(false);
			ManualLogSource logger3 = _logger;
			if (logger3 != null)
			{
				string name = ((Object)uiRoot).name;
				Scene scene = ((Component)uiRoot).gameObject.scene;
				logger3.LogInfo((object)("GK2+ mod menu host attached to '" + name + "' " + $"(scene='{((Scene)(ref scene)).name}', sortingOrder={val7.sortingOrder})."));
			}
		}

		private void BuildRegisteredActionButtons()
		{
			//IL_0199: 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_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Expected O, but got Unknown
			foreach (GameObject value in _registeredActionButtons.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			_registeredActionButtons.Clear();
			if ((Object)(object)_contentRoot == (Object)null || (Object)(object)_menuButtonLabelTemplate == (Object)null || (Object)(object)_menuButtonSprite == (Object)null)
			{
				return;
			}
			foreach (IGrouping<string, GK2MenuAction> item in from gK2MenuAction in _registeredMenuActions
				group gK2MenuAction by gK2MenuAction.Tab)
			{
				List<GK2MenuAction> list = item.ToList();
				int count = list.Count;
				if (count == 0)
				{
					continue;
				}
				for (int num = 0; num < count; num++)
				{
					GK2MenuAction action = list[num];
					int num2 = num / 4;
					int num3 = num % 4;
					int num4 = num2 * 4;
					int num5 = Mathf.Min(4, count - num4);
					float num6 = Mathf.Min(108f, (350f - (float)(num5 - 1) * 6f) / (float)num5);
					float num7 = (0f - ((float)num5 * num6 + (float)(num5 - 1) * 6f)) / 2f + num6 / 2f + (float)num3 * (num6 + 6f);
					float num8 = -92f - (float)num2 * 27f;
					GameObject val = CreateActionButton(_menuButtonLabelTemplate, _contentRoot.transform, _menuButtonSprite, action.Label, new Vector2(num7, num8), new Vector2(num6, 20f));
					((UnityEvent)val.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
					{
						if (!action.CanExecute())
						{
							ManualLogSource logger = _logger;
							if (logger != null)
							{
								logger.LogWarning((object)("GK2+ menu action '" + action.Id + "' is currently unavailable."));
							}
							RefreshRegisteredActionButtons();
						}
						else
						{
							try
							{
								action.Execute();
							}
							catch (Exception arg)
							{
								ManualLogSource logger2 = _logger;
								if (logger2 != null)
								{
									logger2.LogError((object)$"GK2+ menu action '{action.Id}' failed: {arg}");
								}
							}
							RefreshRegisteredActionButtons();
						}
					});
					_registeredActionButtons[action] = val;
				}
			}
			RefreshRegisteredActionButtons();
		}

		private void RefreshRegisteredActionButtons()
		{
			foreach (KeyValuePair<GK2MenuAction, GameObject> registeredActionButton in _registeredActionButtons)
			{
				GK2MenuAction key = registeredActionButton.Key;
				GameObject value = registeredActionButton.Value;
				if ((Object)(object)value == (Object)null)
				{
					continue;
				}
				bool flag = string.Equals(key.Tab, _activeTab, StringComparison.OrdinalIgnoreCase);
				value.SetActive(flag);
				if (flag)
				{
					Button component = value.GetComponent<Button>();
					if ((Object)(object)component != (Object)null)
					{
						((Selectable)component).interactable = key.CanExecute();
					}
				}
			}
		}

		private GameObject CreateActionButton(GameObject textTemplate, Transform parent, Sprite sprite, string text, Vector2 anchoredPosition, Vector2 size)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00a6: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: 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_0168: 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)
			GameObject val = new GameObject(text + "Button", new Type[4]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image),
				typeof(Button)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 1f);
			component.anchorMax = new Vector2(0.5f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = anchoredPosition;
			component.sizeDelta = size;
			Image component2 = val.GetComponent<Image>();
			component2.sprite = sprite;
			component2.type = (Type)1;
			((Graphic)component2).raycastTarget = true;
			Button component3 = val.GetComponent<Button>();
			((Selectable)component3).targetGraphic = (Graphic)(object)component2;
			((Selectable)component3).transition = (Transition)1;
			Navigation navigation = ((Selectable)component3).navigation;
			((Navigation)(ref navigation)).mode = (Mode)3;
			((Selectable)component3).navigation = navigation;
			ColorBlock colors = ((Selectable)component3).colors;
			((ColorBlock)(ref colors)).normalColor = Color.white;
			((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 1f, 1f, 0.92f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.82f, 0.82f, 0.82f, 0.92f);
			((ColorBlock)(ref colors)).selectedColor = Color.white;
			((ColorBlock)(ref colors)).disabledColor = new Color(0.58f, 0.58f, 0.58f, 0.6f);
			((ColorBlock)(ref colors)).fadeDuration = 0.05f;
			((Selectable)component3).colors = colors;
			CreateNativeButtonLabel(textTemplate, val.transform, text, 0.55f);
			return val;
		}

		private GameObject CreateNativeButtonLabel(GameObject template, Transform parent, string text, float scale)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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)
			//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_00e1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, parent, false);
			((Object)val).name = "Label";
			val.SetActive(true);
			StripLocalization(val);
			RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 0.5f);
			component.anchorMax = new Vector2(0.5f, 0.5f);
			component.pivot = new Vector2(0.5f, 0.5f);
			component.anchoredPosition = Vector2.zero;
			((Transform)component).localRotation = Quaternion.identity;
			((Transform)component).localScale = new Vector3(scale, scale, 1f);
			if ((Object)(object)val2 != (Object)null)
			{
				Rect rect = val2.rect;
				float num = Mathf.Max(1f, ((Rect)(ref rect)).width / scale);
				rect = val2.rect;
				float num2 = Mathf.Max(1f, ((Rect)(ref rect)).height / scale);
				component.sizeDelta = new Vector2(num, num2);
			}
			Component obj = FindTmp(val);
			if ((Object)(object)obj == (Object)null)
			{
				throw new InvalidOperationException("Native button label template has no TextMeshProUGUI.");
			}
			SetProperty(obj, "text", text);
			SetProperty(obj, "enableAutoSizing", false);
			SetProperty(obj, "characterSpacing", 0.75f);
			SetProperty(obj, "wordSpacing", 0.3f);
			TrySetEnumProperty(obj, "alignment", "Center");
			return val;
		}

		private void SetActiveTab(string tabName)
		{
			//IL_005e: 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)
			_activeTab = tabName;
			foreach (KeyValuePair<string, GameObject> tabButton in _tabButtons)
			{
				Image component = tabButton.Value.GetComponent<Image>();
				if (!((Object)(object)component == (Object)null))
				{
					((Graphic)component).color = (Color)((tabButton.Key == tabName) ? Color.white : new Color(0.72f, 0.72f, 0.78f, 0.88f));
				}
			}
			SetText(_pageTitle, tabName);
			SetText(_pageText, GetPlaceholderText(tabName));
			LayoutPageTextForTab(tabName);
			bool active = tabName == "More";
			if ((Object)(object)_githubButton != (Object)null)
			{
				_githubButton.SetActive(active);
			}
			if ((Object)(object)_nexusButton != (Object)null)
			{
				_nexusButton.SetActive(active);
			}
			if ((Object)(object)_bugButton != (Object)null)
			{
				_bugButton.SetActive(active);
			}
			RefreshRegisteredActionButtons();
		}

		private void LayoutPageTextForTab(string tab)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_pageText == (Object)null))
			{
				RectTransform component = _pageText.GetComponent<RectTransform>();
				if (!((Object)(object)component == (Object)null))
				{
					bool flag = string.Equals(tab, "Cheats", StringComparison.OrdinalIgnoreCase);
					component.anchoredPosition = (flag ? new Vector2(0f, -40f) : new Vector2(0f, -40f));
					component.sizeDelta = (flag ? new Vector2(350f, 42f) : new Vector2(350f, 102f));
				}
			}
		}

		private string GetPlaceholderText(string tab)
		{
			string text = (ProjectLinks.HasNexusUrl ? "Follow development on GitHub or visit the GK2+ page on Nexus Mods." : "Follow development on GitHub. The Nexus Mods page is coming soon.");
			switch (tab)
			{
			case "General":
				return "Coming Soon\n\nGK2+ is still under active development.\nThis page will contain global mod settings, UI options, and hotkeys.\n\n" + text;
			case "Inventory":
				return "Coming Soon\n\nPlanned: unified storage, shared chest resources, search/filtering,\nand stack-size quality-of-life options.\n\n" + text;
			case "Crafting":
				return "Coming Soon\n\nPlanned: recipe pinning, resource-pull behavior, and crafting QoL.\n\n" + text;
			case "Farming":
				return "Coming Soon\n\nPlanned: continuous planting, seed-selection behavior, and farming QoL.\n\n" + text;
			case "Zombies":
				return "Coming Soon\n\nPlanned: a central zombie manager, stats overview, and equipment tools.\n\n" + text;
			case "Cheats":
			{
				if (_tabNotices.TryGetValue("Cheats", out var value))
				{
					string text2 = value();
					if (!string.IsNullOrWhiteSpace(text2))
					{
						return text2;
					}
				}
				return "Money cheats use a save-safety checkpoint.\nHealth and stamina refills use native player systems.";
			}
			case "More":
				return "GK2+ Project Links\n\nGitHub: source, development progress, and releases.\n" + (ProjectLinks.HasNexusUrl ? "Nexus Mods: public mod page and downloads.\n" : "Nexus Mods: public mod page coming soon.\n") + "Report Bug: opens a new GitHub issue for GK2+.\n\nQuest/map tools, compatibility, diagnostics, and About will live here.";
			default:
				return tab;
			}
		}

		public void RefreshActiveTab()
		{
			if (_built && (Object)(object)_pageTitle != (Object)null && (Object)(object)_pageText != (Object)null)
			{
				SetActiveTab(_activeTab);
			}
		}

		public void ToggleMenu()
		{
			if (!_built || (Object)(object)_menuRoot == (Object)null)
			{
				ManualLogSource logger = _logger;
				if (logger != null)
				{
					logger.LogWarning((object)"GK2+ F2 toggle ignored because the menu shell is not ready.");
				}
				return;
			}
			bool flag = !_menuRoot.activeSelf;
			_menuRoot.SetActive(flag);
			if (flag)
			{
				_menuRoot.transform.SetAsLastSibling();
				SetActiveTab(_activeTab);
			}
			ManualLogSource logger2 = _logger;
			if (logger2 != null)
			{
				string[] obj = new string[7]
				{
					"GK2+ mod menu ",
					flag ? "shown" : "hidden",
					" in ",
					DetectContext(),
					" context; parent='",
					null,
					null
				};
				Transform parent = _menuRoot.transform.parent;
				obj[5] = ((parent != null) ? ((Object)parent).name : null) ?? "<none>";
				obj[6] = "'.";
				logger2.LogInfo((object)string.Concat(obj));
			}
		}

		public void ShowMenu()
		{
			if (_built && !((Object)(object)_menuRoot == (Object)null))
			{
				_menuRoot.SetActive(true);
				_menuRoot.transform.SetAsLastSibling();
				SetActiveTab(_activeTab);
			}
		}

		public void HideMenu()
		{
			if ((Object)(object)_menuRoot != (Object)null)
			{
				_menuRoot.SetActive(false);
			}
		}

		public void ShutdownController()
		{
			HideMenu();
			CleanupPartialMenu();
			if ((Object)(object)this != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}

		private static string DetectContext()
		{
			MonoBehaviour[] array = Resources.FindObjectsOfTypeAll<MonoBehaviour>();
			foreach (MonoBehaviour val in array)
			{
				if ((Object)(object)val != (Object)null && ((object)val).GetType().Name == "UIMainMenuWindow" && ((Component)val).gameObject.activeInHierarchy)
				{
					return "MainMenu";
				}
			}
			return "Gameplay";
		}

		private void CleanupPartialMenu()
		{
			if ((Object)(object)_menuRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_menuRoot);
				_menuRoot = null;
			}
			_tabButtons.Clear();
			_registeredActionButtons.Clear();
			_tabNotices.Clear();
			_pageTitle = null;
			_pageText = null;
			_githubButton = null;
			_nexusButton = null;
			_bugButton = null;
			_contentRoot = null;
			_menuButtonLabelTemplate = null;
			_menuButtonSprite = null;
			_built = false;
		}

		private GameObject CreateNativeTitleText(GameObject template, Transform parent, string text, Vector2 anchoredPosition, Vector2 size, float scale)
		{
			//IL_0033: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			GameObject val = Object.Instantiate<GameObject>(template, parent, false);
			((Object)val).name = "NativeTitle";
			val.SetActive(true);
			StripLocalization(val);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.5f, 1f);
			component.anchorMax = new Vector2(0.5f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = anchoredPosition;
			component.sizeDelta = size;
			((Transform)component).localScale = new Vector3(scale, scale, 1f);
			Component component2 = FindTmp(val);
			SetProperty(component2, "text", text);
			TrySetEnumProperty(component2, "alignment", "Center");
			return val;
		}

		private GameObject CreateBodyText(GameObject template, Transform parent, string name, string text, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size, float fontSize, string alignment)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(template, parent, false);
			((Object)val).name = name;
			val.SetActive(true);
			StripLocalization(val);
			Component[] components = val.GetComponents<Component>();
			foreach (Component val2 in components)
			{
				if ((Object)(object)val2 != (Object)null && ((object)val2).GetType().Name == "LazyButtonTipsStr")
				{
					Object.Destroy((Object)(object)val2);
				}
			}
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = anchorMin;
			component.anchorMax = anchorMax;
			component.pivot = pivot;
			((Transform)component).localScale = Vector3.one;
			((Transform)component).localRotation = Quaternion.identity;
			if (anchorMin == Vector2.zero && anchorMax == Vector2.one && size == Vector2.zero)
			{
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
			}
			else
			{
				component.anchoredPosition = anchoredPosition;
				component.sizeDelta = size;
			}
			Component component2 = FindTmp(val);
			SetProperty(component2, "text", text);
			SetProperty(component2, "enableAutoSizing", false);
			SetProperty(component2, "fontSize", fontSize);
			SetProperty(component2, "fontSizeMin", fontSize);
			SetProperty(component2, "fontSizeMax", fontSize);
			SetProperty(component2, "characterSpacing", 1.45f);
			SetProperty(component2, "wordSpacing", 0.75f);
			SetProperty(component2, "lineSpacing", 2f);
			SetProperty(component2, "paragraphSpacing", 2f);
			SetProperty(component2, "margin", Vector4.zero);
			SetProperty(component2, "color", (object)new Color(1f, 0.84f, 0.48f, 1f));
			TrySetEnumProperty(component2, "fontStyle", "Normal");
			TrySetEnumProperty(component2, "fontWeight", "Regular");
			TrySetEnumProperty(component2, "alignment", alignment);
			return val;
		}

		private GameObject CreateImage(Transform parent, string name, Sprite sprite, Type type, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size)
		{
			//IL_002e: 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_0040: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[3]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image)
			});
			val.transform.SetParent(parent, false);
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = anchorMin;
			component.anchorMax = anchorMax;
			component.pivot = pivot;
			if (anchorMin == Vector2.zero && anchorMax == Vector2.one && size == Vector2.zero)
			{
				component.offsetMin = Vector2.zero;
				component.offsetMax = Vector2.zero;
			}
			else
			{
				component.anchoredPosition = anchoredPosition;
				component.sizeDelta = size;
			}
			Image component2 = val.GetComponent<Image>();
			component2.sprite = sprite;
			component2.type = type;
			((Graphic)component2).raycastTarget = false;
			return val;
		}

		private GameObject CreateStretchImage(Transform parent, string name, Sprite sprite, Type type, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = CreateImage(parent, name, sprite, type, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
			RectTransform component = obj.GetComponent<RectTransform>();
			component.offsetMin = offsetMin;
			component.offsetMax = offsetMax;
			return obj;
		}

		private GameObject CreateFixedImage(Transform parent, string name, Sprite sprite, Type type, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return CreateImage(parent, name, sprite, type, anchorMin, anchorMax, pivot, anchoredPosition, size);
		}

		private void StripLocalization(GameObject obj)
		{
			Component[] components = obj.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string name = ((object)val).GetType().Name;
					if (name == "LocalizedLabel" || name == "LocalizedVerticalOffset")
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
		}

		private void SetText(GameObject obj, string text)
		{
			Component val = FindTmp(obj);
			if ((Object)(object)val != (Object)null)
			{
				SetProperty(val, "text", text);
			}
		}

		private static Component FindTmp(GameObject obj)
		{
			if ((Object)(object)obj == (Object)null)
			{
				return null;
			}
			return ((IEnumerable<Component>)obj.GetComponents<Component>()).FirstOrDefault((Func<Component, bool>)((Component component) => (Object)(object)component != (Object)null && ((object)component).GetType().Name == "TextMeshProUGUI"));
		}

		private static Sprite FindSprite(string name)
		{
			return ((IEnumerable<Sprite>)Resources.FindObjectsOfTypeAll<Sprite>()).FirstOrDefault((Func<Sprite, bool>)((Sprite sprite) => (Object)(object)sprite != (Object)null && string.Equals(((Object)sprite).name, name, StringComparison.Ordinal)));
		}

		private static void TrySetEnumProperty(Component component, string propertyName, string enumValue)
		{
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			PropertyInfo property = ((object)component).GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null || !property.CanWrite || !property.PropertyType.IsEnum)
			{
				return;
			}
			try
			{
				object value = Enum.Parse(property.PropertyType, enumValue);
				property.SetValue(component, value, null);
			}
			catch
			{
			}
		}

		private static void SetProperty(Component component, string propertyName, object value)
		{
			if (!((Object)(object)component == (Object)null))
			{
				PropertyInfo property = ((object)component).GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite)
				{
					property.SetValue(component, value, null);
				}
			}
		}
	}
}
namespace GK2Plus.Framework.Saves
{
	internal sealed class GK2SaveService : GK2ServiceBase
	{
		private const string SaveDataExtension = ".dat";

		private const string SaveInfoExtension = ".info";

		private const string CheatTaintExtension = ".gk2plus-cheat-taint";

		private const string CheatTaintBackupFileName = "GK2Plus-CheatTaint.txt";

		private const int MaxBackupsPerSlot = 5;

		private bool _initialized;

		private bool _saveLoading;

		private bool _saveWriting;

		private long _saveGeneration;

		private long _checkpointGeneration = -1L;

		private string _checkpointSlotName;

		private string _checkpointBackupDirectory;

		public override string Name => "Saves";

		public string BackupRootPath => Path.Combine(Paths.ConfigPath, "GK2Plus", "SaveBackups");

		public bool IsSaveOperationInProgress
		{
			get
			{
				if (!_saveLoading)
				{
					return _saveWriting;
				}
				return true;
			}
		}

		public int BackupRetentionPerSlot => 5;

		public bool IsActiveSaveCheatTainted
		{
			get
			{
				try
				{
					MainGame instance = MainGame.Instance;
					SaveSlotData val = ((instance != null) ? instance.SaveSlotData : null);
					return val != null && !string.IsNullOrWhiteSpace(val.slotName) && IsSlotCheatTainted(val.slotName);
				}
				catch
				{
					return false;
				}
			}
		}

		public bool HasLoadedSave
		{
			get
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Invalid comparison between Unknown and I4
				try
				{
					return (Object)(object)MainGame.Instance != (Object)null && (int)MainGame.Instance.gameState == 1 && MainGame.Instance.GameSave != null && MainGame.Instance.SaveSlotData != null && !string.IsNullOrWhiteSpace(MainGame.Instance.SaveSlotData.slotName);
				}
				catch
				{
					return false;
				}
			}
		}

		public GK2SaveService(ManualLogSource logger)
			: base(logger)
		{
		}

		public override void Initialize()
		{
			base.Initialize();
			if (!_initialized)
			{
				SaveSystem.OnSaveLoadingStarted += OnSaveLoadingStarted;
				SaveSystem.OnSaveLoadingEnded += OnSaveLoadingEnded;
				SaveSystem.OnSaveWriteStarted += OnSaveWriteStarted;
				SaveSystem.OnSaveWriteStartedInstant += OnSaveWriteStartedInstant;
				SaveSystem.OnSaveWriteEnded += OnSaveWriteEnded;
				_initialized = true;
				base.Logger.LogInfo((object)("GK2+ save safety ready. Persistent mutations use on-demand " + $"checkpoints with a {5}-backup per-slot retention cap."));
			}
		}

		public override void Shutdown()
		{
			if (_initialized)
			{
				SaveSystem.OnSaveLoadingStarted -= OnSaveLoadingStarted;
				SaveSystem.OnSaveLoadingEnded -= OnSaveLoadingEnded;
				SaveSystem.OnSaveWriteStarted -= OnSaveWriteStarted;
				SaveSystem.OnSaveWriteStartedInstant -= OnSaveWriteStartedInstant;
				SaveSystem.OnSaveWriteEnded -= OnSaveWriteEnded;
				_initialized = false;
			}
			_saveLoading = false;
			_saveWriting = false;
			ClearCheckpoint();
			base.Shutdown();
		}

		public bool TryPrepareMutation(string operationName, SaveMutationRisk risk, out SaveMutationContext context)
		{
			context = null;
			if (string.IsNullOrWhiteSpace(operationName))
			{
				base.Logger.LogError((object)"GK2+ save safety rejected an unnamed mutation.");
				return false;
			}
			if (!TryGetActiveSlot(out var slotData, out var error))
			{
				base.Logger.LogWarning((object)("GK2+ save safety blocked '" + operationName + "': " + error));
				return false;
			}
			if (IsSaveOperationInProgress)
			{
				base.Logger.LogWarning((object)("GK2+ save safety blocked '" + operationName + "' because GK2 is currently loading or writing a save."));
				return false;
			}
			string text = null;
			if (risk >= SaveMutationRisk.Moderate)
			{
				if (!TryGetOrCreateCheckpoint(operationName, slotData, out var result))
				{
					base.Logger.LogError((object)("GK2+ save safety blocked '" + operationName + "' because the pre-mutation checkpoint failed: " + result.Error));
					return false;
				}
				text = result.BackupDirectory;
			}
			context = new SaveMutationContext(operationName, risk, slotData.slotName, DateTime.UtcNow, text);
			base.Logger.LogInfo((object)("GK2+ mutation prepared: operation='" + operationName + "', " + $"risk={risk}, slot='{slotData.slotName}', " + "backup=" + (text ?? "<not required>") + "."));
			return true;
		}

		public bool TryRunProtectedMutation(string operationName, SaveMutationRisk risk, Action mutation, Func<bool> validator = null)
		{
			if (mutation == null)
			{
				throw new ArgumentNullException("mutation");
			}
			if (!TryPrepareMutation(operationName, risk, out var context))
			{
				return false;
			}
			try
			{
				mutation();
				if (validator != null && !validator())
				{
					base.Logger.LogError((object)("GK2+ mutation validation failed: operation='" + operationName + "', " + string.Format("risk={0}, backup={1}.", risk, context.BackupDirectory ?? "<none>")));
					return false;
				}
				base.Logger.LogInfo((object)("GK2+ mutation completed: operation='" + operationName + "', " + $"risk={risk}, slot='{context.SlotName}'."));
				return true;
			}
			catch (Exception arg)
			{
				base.Logger.LogError((object)("GK2+ mutation failed: operation='" + operationName + "', " + string.Format("risk={0}, backup={1}, ", risk, context.BackupDirectory ?? "<none>") + $"exception={arg}"));
				return false;
			}
		}

		public bool TryManualSave(out string error)
		{
			error = null;
			if (!TryGetActiveSlot(out var slotData, out error))
			{
				return false;
			}
			if (IsSaveOperationInProgress)
			{
				error = "GK2 is already loading or writing a save.";
				return false;
			}
			MainGame instance = MainGame.Instance;
			GameSave val = ((instance != null) ? instance.GameSave : null);
			if (val == null)
			{
				error = "The active GameSave is unavailable.";
				return false;
			}
			bool callbackReceived = false;
			bool success = false;
			try
			{
				base.Logger.LogInfo((object)("GK2+ manual save requested for slot '" + slotData.slotName + "'."));
				SaveSystem.Save(slotData, val, (Action)delegate
				{
					callbackReceived = true;
					success = true;
				}, (Action)delegate
				{
					callbackReceived = true;
					success = false;
				}, true, (Action<SaveSlotData, GameSave>)null);
				if (!callbackReceived)
				{
					error = "GK2 SaveSystem returned without a completion callback.";
					base.Logger.LogError((object)("GK2+ manual save failed: " + error));
					return false;
				}
				if (!success)
				{
					error = "GK2 SaveSystem reported that the save failed.";
					base.Logger.LogError((object)("GK2+ manual save failed: " + error));
					return false;
				}
				base.Logger.LogInfo((object)("GK2+ manual save completed for slot '" + slotData.slotName + "'."));
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				base.Logger.LogError((object)$"GK2+ manual save threw an unexpected exception: {ex}");
				return false;
			}
		}

		public bool TryGetLastSaveDateTime(out DateTime savedAt)
		{
			savedAt = default(DateTime);
			if (!TryGetActiveSlot(out var slotData, out var _))
			{
				return false;
			}
			try
			{
				savedAt = slotData.GetSaveDateTime();
				return savedAt != default(DateTime);
			}
			catch
			{
				savedAt = default(DateTime);
				return false;
			}
		}

		public bool TryMarkActiveSaveCheatTainted(string cheatId, out string error)
		{
			error = null;
			if (!TryGetActiveSlot(out var slotData, out error))
			{
				return false;
			}
			if (IsSaveOperationInProgress)
			{
				error = "GK2 is currently loading or writing a save.";
				return false;
			}
			try
			{
				string cheatTaintPath = GetCheatTaintPath(slotData.slotName);
				if (!File.Exists(cheatTaintPath))
				{
					WriteCheatTaintMarker(cheatTaintPath, slotData.slotName, cheatId);
				}
				if (!TryMarkExistingBackupsCheatTainted(slotData.slotName, cheatTaintPath, out error))
				{
					base.Logger.LogError((object)("GK2+ cheat taint was written for slot '" + slotData.slotName + "', but one or more existing backups could not be marked: " + error));
					return false;
				}
				base.Logger.LogWarning((object)("GK2+ cheat taint active for slot '" + slotData.slotName + "'. Platform achievements will be blocked while this save is loaded."));
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				base.Logger.LogError((object)$"GK2+ failed to mark the active save as cheat-tainted: {ex}");
				return false;
			}
		}

		public bool IsSlotCheatTainted(string slotName)
		{
			if (string.IsNullOrWhiteSpace(slotName))
			{
				return false;
			}
			try
			{
				return File.Exists(GetCheatTaintPath(slotName));
			}
			catch
			{
				return false;
			}
		}

		public bool TryCreateBackup(string reason, out SaveBackupResult result)
		{
			result = null;
			if (!TryGetActiveSlot(out var slotData, out var error))
			{
				result = new SaveBackupResult(success: false, null, null, error);
				return false;
			}
			if (IsSaveOperationInProgress)
			{
				error = "GK2 is currently loading or writing a save.";
				result = new SaveBackupResult(success: false, slotData.slotName, null, error);
				return false;
			}
			return TryCreateBackupCore(reason, slotData, out result);
		}

		private bool TryGetOrCreateCheckpoint(string reason, SaveSlotData slotData, out SaveBackupResult result)
		{
			if (_checkpointGeneration == _saveGeneration && string.Equals(_checkpointSlotName, slotData.slotName, StringComparison.Ordinal) && !string.IsNullOrEmpty(_checkpointBackupDirectory) && Directory.Exists(_checkpointBackupDirectory))
			{
				result = new SaveBackupResult(success: true, slotData.slotName, _checkpointBackupDirectory, null);
				base.Logger.LogDebug((object)("GK2+ save safety reused checkpoint for slot " + $"'{slotData.slotName}' generation {_saveGeneration}."));
				return true;
			}
			if (!TryCreateBackupCore(reason, slotData, out result))
			{
				return false;
			}
			_checkpointGeneration = _saveGeneration;
			_checkpointSlotName = slotData.slotName;
			_checkpointBackupDirectory = result.BackupDirectory;
			return true;
		}

		private bool TryCreateBackupCore(string reason, SaveSlotData slotData, out SaveBackupResult result)
		{
			result = null;
			string saveFolder = SaveSystem.SaveFolder;
			string text = Path.Combine(saveFolder, slotData.slotName + ".dat");
			string text2 = Path.Combine(saveFolder, slotData.slotName + ".info");
			if (!File.Exists(text) || !File.Exists(text2))
			{
				string text3 = string.Join(", ", new string[2]
				{
					(!File.Exists(text)) ? Path.GetFileName(text) : null,
					(!File.Exists(text2)) ? Path.GetFileName(text2) : null
				}.Where((string value) => value != null));
				string error = "Required active-slot file(s) are missing: " + text3 + ". Save folder: " + saveFolder;
				result = new SaveBackupResult(success: false, slotData.slotName, null, error);
				return false;
			}
			string path = SanitizePathPart(slotData.slotName, 64);
			string text4 = SanitizePathPart(string.IsNullOrWhiteSpace(reason) ? "manual" : reason, 48);
			string text5 = DateTime.UtcNow.ToString("yyyyMMdd-HHmmssfff");
			string text6 = Path.Combine(BackupRootPath, path);
			string text7 = Path.Combine(text6, text5 + "_" + text4);
			string text8 = text7 + ".tmp";
			try
			{
				Directory.CreateDirectory(text6);
				if (Directory.Exists(text8))
				{
					Directory.Delete(text8, recursive: true);
				}
				Directory.CreateDirectory(text8);
				CopyStableFile(text, Path.Combine(text8, Path.GetFileName(text)));
				CopyStableFile(text2, Path.Combine(text8, Path.GetFileName(text2)));
				bool flag = IsSlotCheatTainted(slotData.slotName);
				if (flag)
				{
					CopyStableFile(GetCheatTaintPath(slotData.slotName), Path.Combine(text8, "GK2Plus-CheatTaint.txt"));
				}
				File.WriteAllText(Path.Combine(text8, "GK2Plus-Backup.txt"), BuildManifest(slotData.slotName, reason, saveFolder, text, text2, flag), Encoding.UTF8);
				Directory.Move(text8, text7);
				PruneOldBackups(text6, text7);
				result = new SaveBackupResult(success: true, slotData.slotName, text7, null);
				base.Logger.LogWarning((object)("GK2+ save backup created for slot '" + slotData.slotName + "': " + text7));
				return true;
			}
			catch (Exception ex)
			{
				TryDeleteDirectory(text8);
				result = new SaveBackupResult(success: false, slotData.slotName, null, ex.Message);
				base.Logger.LogError((object)$"GK2+ failed to back up slot '{slotData.slotName}': {ex}");
				return false;
			}
		}

		private static void PruneOldBackups(string slotBackupRoot, string protectedDirectory)
		{
			foreach (DirectoryInfo item in (from directory in new DirectoryInfo(slotBackupRoot).GetDirectories()
				where !directory.Name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase)
				select directory).OrderByDescending<DirectoryInfo, string>((DirectoryInfo directory) => directory.Name, StringComparer.Ordinal).ToArray().Skip(5))
			{
				if (!string.Equals(item.FullName, protectedDirectory, StringComparison.OrdinalIgnoreCase))
				{
					TryDeleteDirectory(item.FullName);
				}
			}
		}

		private bool TryGetActiveSlot(out SaveSlotData slotData, out string error)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			slotData = null;
			error = null;
			try
			{
				MainGame instance = MainGame.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					error = "MainGame is not initialized.";
					return false;
				}
				if ((int)instance.gameState != 1)
				{
					error = "No gameplay save is currently loaded.";
					return false;
				}
				if (instance.GameSave == null)
				{
					error = "The active GameSave is unavailable.";
					return false;
				}
				slotData = instance.SaveSlotData;
				if (slotData == null || string.IsNullOrWhiteSpace(slotData.slotName))
				{
					error = "The active save slot is unavailable.";
					slotData = null;
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				error = "Failed to resolve the active save slot: " + ex.Message;
				slotData = null;
				return false;
			}
		}

		private static void CopyStableFile(string sourcePath, string destinationPath)
		{
			long length;
			using (FileStream fileStream = new FileStream(sourcePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
			{
				length = fileStream.Length;
				using FileStream fileStream2 = new FileStream(destinationPath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
				fileStream.CopyTo(fileStream2);
				fileStream2.Flush();
			}
			FileInfo fileInfo = new FileInfo(destinationPath);
			if (!fileInfo.Exists || fileInfo.Length != length)
			{
				throw new IOException("Backup verification failed for '" + Path.GetFileName(sourcePath) + "'. " + $"Expected {length} bytes, copied " + $"{(fileInfo.Exists ? fileInfo.Length : 0)} bytes.");
			}
		}

		private static string BuildManifest(string slotName, string reason, string saveFolder, string dataSource, string infoSource, bool cheatTainted)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("GK2+ Save Safety Backup");
			stringBuilder.AppendLine("=======================");
			stringBuilder.AppendLine("GK2PlusVersion=0.1.0");
			stringBuilder.AppendLine("CreatedUtc=" + DateTime.UtcNow.ToString("O"));
			stringBuilder.AppendLine("SlotName=" + slotName);
			stringBuilder.AppendLine("Reason=" + (reason ?? string.Empty));
			stringBuilder.AppendLine("SourceSaveFolder=" + saveFolder);
			stringBuilder.AppendLine("DataFile=" + Path.GetFileName(dataSource));
			stringBuilder.AppendLine("InfoFile=" + Path.GetFileName(infoSource));
			stringBuilder.AppendLine("CheatTainted=" + cheatTainted);
			return stringBuilder.ToString();
		}

		private string GetCheatTaintPath(string slotName)
		{
			return Path.Combine(SaveSystem.SaveFolder, slotName + ".gk2plus-cheat-taint");
		}

		private void WriteCheatTaintMarker(string destinationPath, string slotName, string cheatId)
		{
			string text = destinationPath + ".tmp";
			try
			{
				string contents = "GK2+ Cheat Taint\n================\nFormatVersion=1\nGK2PlusVersion=0.1.0\nSlotName=" + slotName + "\nFirstCheatId=" + (cheatId ?? string.Empty) + "\nFirstCheatUtc=" + DateTime.UtcNow.ToString("O") + "\nAchievementsDisabled=True\n";
				File.WriteAllText(text, contents, Encoding.UTF8);
				if (File.Exists(destinationPath))
				{
					File.Delete(text);
				}
				else
				{
					File.Move(text, destinationPath);
				}
			}
			finally
			{
				if (File.Exists(text))
				{
					try
					{
						File.Delete(text);
					}
					catch
					{
					}
				}
			}
		}

		private bool TryMarkExistingBackupsCheatTainted(string slotName, string activeMarkerPath, out string error)
		{
			error = null;
			string path = SanitizePathPart(slotName, 64);
			string path2 = Path.Combine(BackupRootPath, path);
			if (!Directory.Exists(path2))
			{
				return true;
			}
			try
			{
				DirectoryInfo[] directories = new DirectoryInfo(path2).GetDirectories();
				foreach (DirectoryInfo directoryInfo in directories)
				{
					if (!directoryInfo.Name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase))
					{
						string text = Path.Combine(directoryInfo.FullName, "GK2Plus-CheatTaint.txt");
						if (!File.Exists(text))
						{
							CopyStableFile(activeMarkerPath, text);
						}
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				return false;
			}
		}

		private static string SanitizePathPart(string value, int maxLength)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return "unnamed";
			}
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (invalidFileNameChars.Contains(c) || char.IsControl(c) || c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar)
				{
					stringBuilder.Append('_');
				}
				else if (char.IsWhiteSpace(c))
				{
					stringBuilder.Append('-');
				}
				else
				{
					stringBuilder.Append(c);
				}
				if (stringBuilder.Length >= maxLength)
				{
					break;
				}
			}
			string text = stringBuilder.ToString().Trim(' ', '.', '-', '_');
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "unnamed";
		}

		private static void TryDeleteDirectory(string path)
		{
			if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
			{
				return;
			}
			try
			{
				Directory.Delete(path, recursive: true);
			}
			catch
			{
			}
		}

		private void AdvanceSaveGeneration(string reason)
		{
			_saveGeneration++;
			ClearCheckpoint();
			base.Logger.LogDebug((object)("GK2+ save checkpoint invalidated after " + reason + "; " + $"generation={_saveGeneration}."));
		}

		private void ClearCheckpoint()
		{
			_checkpointGeneration = -1L;
			_checkpointSlotName = null;
			_checkpointBackupDirectory = null;
		}

		private void OnSaveLoadingStarted()
		{
			_saveLoading = true;
			ClearCheckpoint();