Decompiled source of ReidrheimrCustomMainMenu v1.0.1

CustomMainMenu.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Valheim.SettingsGui;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CustomMainMenu")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Custom main menu logo, music, loading art, Discord link, optional loading tips, and optional skip of the 1.0 startup intro.")]
[assembly: AssemblyFileVersion("1.5.0.0")]
[assembly: AssemblyInformationalVersion("1.5.0")]
[assembly: AssemblyProduct("CustomMainMenu")]
[assembly: AssemblyTitle("CustomMainMenu")]
[assembly: AssemblyVersion("1.5.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CustomMainMenu
{
	internal static class ConfigLayout
	{
		internal static readonly string[] SectionOrder = new string[6] { "General", "Startup", "Main Menu", "Loading Screen", "Changelog", "Discord Panel" };

		internal static bool IsOurs(ConfigFile config)
		{
			CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				return config == ((BaseUnityPlugin)instance).Config;
			}
			return false;
		}

		internal static bool TryWrite(ConfigFile config)
		{
			try
			{
				byte[] array = Build(config);
				string configFilePath = config.ConfigFilePath;
				if (File.Exists(configFilePath) && BytesEqual(File.ReadAllBytes(configFilePath), array))
				{
					return true;
				}
				string directoryName = Path.GetDirectoryName(configFilePath);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				File.WriteAllBytes(configFilePath, array);
				return true;
			}
			catch (Exception ex)
			{
				CustomMainMenuPlugin.LogWarn("Config could not be written in section order: " + ex.Message);
				return false;
			}
		}

		private static byte[] Build(ConfigFile config)
		{
			Dictionary<string, List<ConfigEntryBase>> dictionary = new Dictionary<string, List<ConfigEntryBase>>(StringComparer.Ordinal);
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				if (!dictionary.TryGetValue(item.Key.Section, out var value))
				{
					value = new List<ConfigEntryBase>();
					dictionary[item.Key.Section] = value;
				}
				value.Add(item.Value);
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (StreamWriter streamWriter = new StreamWriter(memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)))
			{
				streamWriter.NewLine = "\r\n";
				streamWriter.WriteLine("## Settings file was created by plugin Custom Main Menu v1.5.0");
				streamWriter.WriteLine("## Plugin GUID: rdmods.custommainmenu");
				streamWriter.WriteLine();
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				for (int i = 0; i < SectionOrder.Length; i++)
				{
					string text = SectionOrder[i];
					if (dictionary.TryGetValue(text, out var value2))
					{
						WriteSection(streamWriter, text, value2);
						hashSet.Add(text);
					}
				}
				foreach (KeyValuePair<string, List<ConfigEntryBase>> item2 in dictionary)
				{
					if (!hashSet.Contains(item2.Key))
					{
						WriteSection(streamWriter, item2.Key, item2.Value);
					}
				}
			}
			return memoryStream.ToArray();
		}

		private static void WriteSection(StreamWriter writer, string section, List<ConfigEntryBase> entries)
		{
			writer.WriteLine("[" + section + "]");
			writer.WriteLine();
			for (int i = 0; i < entries.Count; i++)
			{
				ConfigEntryBase val = entries[i];
				val.WriteDescription(writer);
				writer.WriteLine(val.Definition.Key + " = " + val.GetSerializedValue());
				writer.WriteLine();
			}
		}

		private static bool BytesEqual(byte[] a, byte[] b)
		{
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}
	}
	[BepInPlugin("rdmods.custommainmenu", "Custom Main Menu", "1.5.0")]
	public sealed class CustomMainMenuPlugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnDiscordJoinClicked;
		}

		public const string ModGuid = "rdmods.custommainmenu";

		public const string ModName = "Custom Main Menu";

		public const string ModVersion = "1.5.0";

		public const string AssetFolderName = "CustomMainMenu";

		public const string LoadingArtFolderName = "LoadingArt";

		public const string PluginDefaultsFolderName = "defaults";

		public const string HowToCustomizeFileName = "README-HOW-TO-CUSTOMIZE.txt";

		public const int LoadingArtMaxSlot = 12;

		public const string DiscordTitleText = "Need help? Join Discord!";

		public const string DiscordButtonText = "Click here to join Discord!";

		internal static volatile bool MainMenuActive;

		private static ConfigEntry<bool>? _enableLogging;

		private Harmony? _harmony;

		private bool _inactive;

		private Coroutine? _atmosphereRoutine;

		private Coroutine? _discordLogoRoutine;

		private static GameObject? _discordPanel;

		private static GameObject? _discordJoinButton;

		private static Image? _discordLogoImage;

		private static Sprite? _ownedMenuLogoSprite;

		private static Sprite? _ownedDiscordLogoSprite;

		private static TMP_FontAsset? _cachedDiscordFont;

		private static string? _pluginDefaultsRoot;

		private static string? _loadingArtSourceFolder;

		private static bool _loadingArtSourceResolved;

		private static readonly string[] ImageExtensions = new string[3] { ".png", ".jpg", ".jpeg" };

		private static readonly List<Button> ButtonScratch = new List<Button>();

		internal static CustomMainMenuPlugin? Instance { get; private set; }

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

		internal static ManualLogSource? ModLog { get; private set; }

		internal static bool IsActive
		{
			get
			{
				CustomMainMenuPlugin instance = Instance;
				if ((Object)(object)instance != (Object)null && !instance._inactive && ModEnabled != null)
				{
					return ModEnabled.Value;
				}
				return false;
			}
		}

		internal static GameObject? DiscordPanelRoot => _discordPanel;

		internal static bool IsVerboseLogging
		{
			get
			{
				if (_enableLogging != null)
				{
					return _enableLogging.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			Instance = this;
			ModLog = ((BaseUnityPlugin)this).Logger;
			if ((int)SystemInfo.graphicsDeviceType == 4)
			{
				_inactive = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Main Menu is client-only and stays inactive on headless instances.");
				return;
			}
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn this mod on or off. Takes effect after restarting the game.");
			_enableLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableLogging", false, "Write extra log lines (menu lifecycle, loading art, Discord panel). Off by default.");
			SkipStartupIntro.BindConfig(((BaseUnityPlugin)this).Config);
			MenuAmbientMute.BindConfig(((BaseUnityPlugin)this).Config);
			LoadingTips.BindConfig(((BaseUnityPlugin)this).Config);
			LogoPlacement.BindConfig(((BaseUnityPlugin)this).Config);
			MenuChangelog.BindConfig(((BaseUnityPlugin)this).Config);
			DiscordPanelVisibility.BindConfig(((BaseUnityPlugin)this).Config);
			ConfigLayout.TryWrite(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			ModEnabled.SettingChanged += OnEnabledChanged;
			if (!ModEnabled.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom Main Menu is disabled in config.");
				return;
			}
			EnsureAssetDirectories();
			CachePluginDefaultsRoot();
			TryInstallHowToCustomize();
			ResolveLoadingArtSourceFolderOnce();
			MenuChangelog.Initialize();
			_harmony = new Harmony("rdmods.custommainmenu");
			ApplyHarmonyPatchesSafely(_harmony);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Custom Main Menu 1.5.0 loaded. Asset root: " + GetAssetRootPath()));
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading art folder: " + GetResolvedLoadingArtFolder()));
			((BaseUnityPlugin)this).Logger.LogInfo((object)SkipStartupIntro.DescribeConfig());
			((BaseUnityPlugin)this).Logger.LogInfo((object)LoadingTips.DescribeConfig());
			((BaseUnityPlugin)this).Logger.LogInfo((object)LogoPlacement.DescribeConfig());
		}

		private void OnDestroy()
		{
			if (_inactive)
			{
				Instance = null;
				return;
			}
			if (ModEnabled != null)
			{
				ModEnabled.SettingChanged -= OnEnabledChanged;
			}
			SkipStartupIntro.ResetSession();
			MenuAmbientMute.Shutdown();
			LogoPlacement.ResetMenuSession();
			LoadingScreens.StopAllWatchers("plugin destroyed");
			ReleaseMenu();
			MenuMusic.StopImmediate();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			Instance = null;
		}

		private void OnEnabledChanged(object sender, EventArgs e)
		{
			if (ModEnabled != null && !ModEnabled.Value)
			{
				LoadingScreens.StopAllWatchers("mod disabled");
				ReleaseMenu();
				MenuMusic.StopImmediate();
			}
		}

		private void ReleaseMenu()
		{
			StopRoutine(ref _atmosphereRoutine);
			StopRoutine(ref _discordLogoRoutine);
			DestroyDiscordPanel();
			ReleaseOwnedMenuLogoSprite();
			MainMenuActive = false;
			WorldMusicMute.Release();
			MenuAmbientMute.Restore();
			MenuChangelog.StopWatching();
		}

		private void StopRoutine(ref Coroutine? routine)
		{
			if (routine != null)
			{
				((MonoBehaviour)this).StopCoroutine(routine);
				routine = null;
			}
		}

		internal static void LogInfo(string message)
		{
			if (IsVerboseLogging)
			{
				ManualLogSource? modLog = ModLog;
				if (modLog != null)
				{
					modLog.LogInfo((object)message);
				}
			}
		}

		internal static void LogWarn(string message)
		{
			ManualLogSource? modLog = ModLog;
			if (modLog != null)
			{
				modLog.LogWarning((object)message);
			}
		}

		private void ApplyHarmonyPatchesSafely(Harmony harmony)
		{
			int num = 0;
			int num2 = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(CustomMainMenuPlugin).Assembly);
			foreach (Type type in typesFromAssembly)
			{
				num++;
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					num2++;
					((BaseUnityPlugin)this).Logger.LogError((object)("Harmony patch failed for " + type.FullName + ": " + ex.Message));
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} Harmony apply finished (types={1}, failures={2}).", "Custom Main Menu", num, num2));
		}

		internal static string GetAssetRootPath()
		{
			return Path.Combine(Paths.ConfigPath, "CustomMainMenu");
		}

		internal static string GetLoadingArtFolder()
		{
			return Path.Combine(GetAssetRootPath(), "LoadingArt");
		}

		internal static string GetPluginDefaultsRoot()
		{
			if (_pluginDefaultsRoot != null)
			{
				return _pluginDefaultsRoot;
			}
			CachePluginDefaultsRoot();
			return _pluginDefaultsRoot ?? string.Empty;
		}

		internal static string GetResolvedLoadingArtFolder()
		{
			if (!_loadingArtSourceResolved)
			{
				ResolveLoadingArtSourceFolderOnce();
			}
			return _loadingArtSourceFolder ?? GetLoadingArtFolder();
		}

		internal static string GetLogoPath()
		{
			return ResolveUserOrDefaultImage("logo");
		}

		internal static string GetDiscordLogoPath()
		{
			return ResolveUserOrDefaultImage("discordlogo");
		}

		private static string ResolveImagePath(string folder, string baseName)
		{
			for (int i = 0; i < ImageExtensions.Length; i++)
			{
				string text = Path.Combine(folder, baseName + ImageExtensions[i]);
				if (File.Exists(text))
				{
					return text;
				}
			}
			return Path.Combine(folder, baseName + ImageExtensions[0]);
		}

		private static string ResolveUserOrDefaultImage(string baseName)
		{
			string text = ResolveImagePath(GetAssetRootPath(), baseName);
			if (File.Exists(text))
			{
				return text;
			}
			string pluginDefaultsRoot = GetPluginDefaultsRoot();
			if (string.IsNullOrEmpty(pluginDefaultsRoot))
			{
				return text;
			}
			string text2 = ResolveImagePath(pluginDefaultsRoot, baseName);
			if (!File.Exists(text2))
			{
				return text;
			}
			return text2;
		}

		private static string ResolveUserOrDefaultExact(string fileName)
		{
			string text = Path.Combine(GetAssetRootPath(), fileName);
			if (File.Exists(text))
			{
				return text;
			}
			string pluginDefaultsRoot = GetPluginDefaultsRoot();
			if (string.IsNullOrEmpty(pluginDefaultsRoot))
			{
				return text;
			}
			string text2 = Path.Combine(pluginDefaultsRoot, fileName);
			if (!File.Exists(text2))
			{
				return text;
			}
			return text2;
		}

		internal static string GetMusicPath()
		{
			return ResolveUserOrDefaultExact("mainmenu.ogg");
		}

		internal static string GetChangelogPath()
		{
			return ResolveUserOrDefaultExact("changelog.txt");
		}

		internal static string GetCustomTipsPath()
		{
			return ResolveUserOrDefaultExact("customtips.txt");
		}

		internal static string? GetRandomLoadingArtPath()
		{
			string resolvedLoadingArtFolder = GetResolvedLoadingArtFolder();
			if (!Directory.Exists(resolvedLoadingArtFolder))
			{
				return null;
			}
			string result = null;
			int num = 0;
			for (int i = 1; i <= 12; i++)
			{
				string text = ResolveImagePath(resolvedLoadingArtFolder, "art" + i);
				if (File.Exists(text))
				{
					num++;
					if (num == 1 || Random.Range(0, num) == 0)
					{
						result = text;
					}
				}
			}
			return result;
		}

		private void EnsureAssetDirectories()
		{
			try
			{
				Directory.CreateDirectory(GetAssetRootPath());
				Directory.CreateDirectory(GetLoadingArtFolder());
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not create asset folders: " + ex.Message));
			}
		}

		private static void TryInstallHowToCustomize()
		{
			try
			{
				string pluginDefaultsRoot = GetPluginDefaultsRoot();
				if (string.IsNullOrEmpty(pluginDefaultsRoot))
				{
					return;
				}
				string text = Path.Combine(pluginDefaultsRoot, "README-HOW-TO-CUSTOMIZE.txt");
				if (File.Exists(text))
				{
					string assetRootPath = GetAssetRootPath();
					Directory.CreateDirectory(assetRootPath);
					string text2 = Path.Combine(assetRootPath, "README-HOW-TO-CUSTOMIZE.txt");
					if (!File.Exists(text2) || !FilesAreIdentical(text, text2))
					{
						File.Copy(text, text2, overwrite: true);
					}
				}
			}
			catch (Exception ex)
			{
				LogWarn("How-to customize file could not be copied: " + ex.Message);
			}
		}

		private static bool FilesAreIdentical(string left, string right)
		{
			byte[] array = File.ReadAllBytes(left);
			byte[] array2 = File.ReadAllBytes(right);
			if (array.Length != array2.Length)
			{
				return false;
			}
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] != array2[i])
				{
					return false;
				}
			}
			return true;
		}

		private static void CachePluginDefaultsRoot()
		{
			try
			{
				CustomMainMenuPlugin instance = Instance;
				string text = (((Object)(object)instance != (Object)null) ? ((BaseUnityPlugin)instance).Info.Location : null);
				if (string.IsNullOrEmpty(text))
				{
					_pluginDefaultsRoot = string.Empty;
					return;
				}
				string directoryName = Path.GetDirectoryName(text);
				_pluginDefaultsRoot = (string.IsNullOrEmpty(directoryName) ? string.Empty : Path.Combine(directoryName, "defaults"));
			}
			catch
			{
				_pluginDefaultsRoot = string.Empty;
			}
		}

		private static void ResolveLoadingArtSourceFolderOnce()
		{
			if (!_loadingArtSourceResolved)
			{
				_loadingArtSourceResolved = true;
				string loadingArtFolder = GetLoadingArtFolder();
				if (DirectoryHasLoadingImages(loadingArtFolder))
				{
					_loadingArtSourceFolder = loadingArtFolder;
					return;
				}
				string pluginDefaultsRoot = GetPluginDefaultsRoot();
				string text = (string.IsNullOrEmpty(pluginDefaultsRoot) ? string.Empty : Path.Combine(pluginDefaultsRoot, "LoadingArt"));
				_loadingArtSourceFolder = (DirectoryHasLoadingImages(text) ? text : loadingArtFolder);
			}
		}

		private static bool DirectoryHasLoadingImages(string dir)
		{
			if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir))
			{
				return false;
			}
			try
			{
				for (int i = 0; i < ImageExtensions.Length; i++)
				{
					using IEnumerator<string> enumerator = Directory.EnumerateFiles(dir, "*" + ImageExtensions[i]).GetEnumerator();
					if (enumerator.MoveNext())
					{
						_ = enumerator.Current;
						return true;
					}
				}
			}
			catch
			{
				return false;
			}
			return false;
		}

		internal static void OnMainMenuOpened(FejdStartup fejd)
		{
			MainMenuActive = true;
			DiscordPanelVisibility.ResetSessionState();
			MenuMusic.RefreshTrackState();
			WorldMusicMute.MuteForMenu();
			MenuAmbientMute.Apply();
			CustomMainMenuPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.StopRoutine(ref instance._atmosphereRoutine);
				instance._atmosphereRoutine = ((MonoBehaviour)instance).StartCoroutine(MenuAtmosphere.ApplyWhenReady());
			}
			EnsureDiscordPanel(fejd, "main menu opened");
		}

		internal static void OnLogoutToMenu()
		{
			MenuMusic.RefreshTrackState();
			WorldMusicMute.MuteForMenu();
		}

		internal static void OnWorldLoadCommitted()
		{
			LoadingScreens.StopAllWatchers("world load committed");
			LogoPlacement.ResetMenuSession();
			CustomMainMenuPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.ReleaseMenu();
			}
			LogInfo("Menu released (world load).");
		}

		internal static void PresentBrandedMenu(FejdStartup fejd, string reason)
		{
			LogInfo("Main menu presented (" + reason + ").");
			OnMainMenuOpened(fejd);
			MenuChangelog.OnMenuPresented();
			ApplyMenuChrome(fejd);
			MenuChangelog.ApplyOnMenuOpen(fejd);
			MenuMusic.Start();
		}

		internal static void HideTitleLogo(FejdStartup fejd)
		{
			if (!((Object)(object)fejd == (Object)null))
			{
				Transform val = FindLogoRoot(fejd);
				if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(false);
				}
			}
		}

		internal static void ApplyMenuChrome(FejdStartup fejd)
		{
			if (!((Object)(object)fejd == (Object)null))
			{
				HideSerializedClutter(fejd);
				if (!ApplyCustomLogo(fejd) && !((Object)(object)Instance == (Object)null))
				{
					((MonoBehaviour)Instance).StartCoroutine(RetryLogoNextFrame(fejd));
				}
			}
		}

		private static IEnumerator RetryLogoNextFrame(FejdStartup fejd)
		{
			yield return null;
			if (MainMenuActive && (Object)(object)fejd != (Object)null && !CinematicsManager.IsStartedPlaying())
			{
				ApplyCustomLogo(fejd);
			}
		}

		private static void HideSerializedClutter(FejdStartup fejd)
		{
			HideGo(fejd.m_moddedText, "m_moddedText");
			HideGo(fejd.m_merchStoreButtonParent, "m_merchStoreButtonParent");
			if (!MenuChangelog.ShouldShowPanel())
			{
				HideGo(fejd.m_changeLog, "m_changeLog");
				HideGo(fejd.m_showChangelogButton, "m_showChangelogButton");
				HideGo(fejd.m_changeLogNotice, "m_changeLogNotice");
			}
		}

		private static void HideGo(GameObject? go, string label)
		{
			if (!((Object)(object)go == (Object)null) && go.activeSelf)
			{
				go.SetActive(false);
				if (IsVerboseLogging)
				{
					LogInfo("Hidden " + label + " (" + ((Object)go).name + ").");
				}
			}
		}

		private static bool NameHas(string? name, string token)
		{
			if (name != null)
			{
				return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static Transform? FindLogoRoot(FejdStartup fejd)
		{
			if ((Object)(object)fejd.m_mainMenu != (Object)null)
			{
				Transform val = FindDeepNorthLogoRoot(fejd.m_mainMenu.transform);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return FindDeepNorthLogoRoot(((Component)fejd).transform);
		}

		private static Transform? FindDeepNorthLogoRoot(Transform root)
		{
			string name = ((Object)root).name;
			if (NameHas(name, "logo") && NameHas(name, "deepnorth"))
			{
				return root;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val = FindDeepNorthLogoRoot(root.GetChild(i));
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static bool ApplyCustomLogo(FejdStartup fejd)
		{
			Transform val = FindLogoRoot(fejd);
			if ((Object)(object)val != (Object)null)
			{
				HideLogoVfxChildren(val);
				if (!((Component)val).gameObject.activeSelf)
				{
					((Component)val).gameObject.SetActive(true);
				}
			}
			string logoPath = GetLogoPath();
			if (!File.Exists(logoPath))
			{
				LogoPlacement.ApplyMenuOffset(((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null);
				return true;
			}
			Image best = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Image>() : null);
			int hi = (((Object)(object)best != (Object)null) ? ScoreLogo(best) : int.MinValue);
			if ((Object)(object)best == (Object)null || hi < 50)
			{
				FindBestLogoImage(((Component)fejd).transform, ref best, ref hi);
			}
			bool flag = (Object)(object)best != (Object)null && LogoLooksNamed(best);
			if ((Object)(object)best == (Object)null || (hi < 50 && !(hi >= 25 && flag)))
			{
				if (IsVerboseLogging)
				{
					LogWarn($"Logo not applied (best score {hi}).");
				}
				return (Object)(object)best != (Object)null;
			}
			Sprite val2 = LoadSpriteFromDisk(logoPath);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			ApplyLogo(best, val2);
			if (!((Component)best).gameObject.activeSelf)
			{
				((Component)best).gameObject.SetActive(true);
			}
			HideLogoVfxChildren(((Component)best).transform);
			ReplaceOwnedSprite(ref _ownedMenuLogoSprite, val2);
			LogoPlacement.ApplyMenuOffset(best);
			if (IsVerboseLogging)
			{
				LogInfo($"Logo applied (score {hi}, name={((Object)((Component)best).gameObject).name}).");
			}
			return true;
		}

		private static void FindBestLogoImage(Transform root, ref Image? best, ref int hi)
		{
			Image component = ((Component)root).GetComponent<Image>();
			if ((Object)(object)component != (Object)null)
			{
				string name = ((Object)root).name;
				string name2 = (((Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name : string.Empty);
				if (NameHas(name, "logo") || NameHas(name, "valheim") || NameHas(name, "title") || NameHas(name2, "logo") || NameHas(name2, "valheim"))
				{
					int num = ScoreLogo(component);
					if (num > hi)
					{
						hi = num;
						best = component;
					}
				}
			}
			for (int i = 0; i < root.childCount; i++)
			{
				FindBestLogoImage(root.GetChild(i), ref best, ref hi);
			}
		}

		private static bool LogoLooksNamed(Image img)
		{
			string name = ((Object)((Component)img).gameObject).name;
			string name2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name : string.Empty);
			if (!NameHas(name, "logo") && !NameHas(name, "valheim") && !NameHas(name, "title") && !NameHas(name2, "logo"))
			{
				return NameHas(name2, "valheim");
			}
			return true;
		}

		private static int ScoreLogo(Image img)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			string name = ((Object)((Component)img).gameObject).name;
			string name2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name : string.Empty);
			int num = 0;
			if (NameHas(name, "logo"))
			{
				num += 100;
			}
			if (NameHas(name, "deepnorth"))
			{
				num += 40;
			}
			if (NameHas(name, "valheim") || NameHas(name2, "valheim"))
			{
				num += 80;
			}
			if (NameHas(name, "title"))
			{
				num += 40;
			}
			if (NameHas(name2, "logo"))
			{
				num += 50;
			}
			if (((Component)img).gameObject.activeInHierarchy)
			{
				num += 15;
			}
			if (((Behaviour)img).enabled)
			{
				num += 10;
			}
			RectTransform rectTransform = ((Graphic)img).rectTransform;
			if ((Object)(object)rectTransform != (Object)null)
			{
				Rect rect = rectTransform.rect;
				float width = ((Rect)(ref rect)).width;
				rect = rectTransform.rect;
				if (Mathf.Abs(width * ((Rect)(ref rect)).height) > 10000f)
				{
					num += 10;
				}
			}
			return num;
		}

		internal static Sprite? LoadSpriteFromDisk(string path)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte[] data = File.ReadAllBytes(path);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!TextureDecode.LoadImage(val, data, markNonReadable: false))
				{
					Object.Destroy((Object)(object)val);
					return null;
				}
				val.Apply(true, false);
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			}
			catch (Exception ex)
			{
				LogWarn("logo load: " + ex.Message);
				return null;
			}
		}

		internal static void DestroyOwnedSprite(Sprite? sprite)
		{
			if (!((Object)(object)sprite == (Object)null))
			{
				Texture2D texture = sprite.texture;
				Object.Destroy((Object)(object)sprite);
				if ((Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
			}
		}

		private static void ReplaceOwnedSprite(ref Sprite? owned, Sprite? next)
		{
			if (owned == next)
			{
				owned = next;
				return;
			}
			Sprite? sprite = owned;
			owned = next;
			DestroyOwnedSprite(sprite);
		}

		private static void ReleaseOwnedMenuLogoSprite()
		{
			DestroyOwnedSprite(_ownedMenuLogoSprite);
			_ownedMenuLogoSprite = null;
		}

		private static void ReleaseOwnedDiscordLogoSprite()
		{
			DestroyOwnedSprite(_ownedDiscordLogoSprite);
			_ownedDiscordLogoSprite = null;
		}

		internal static void HideLogoVfxChildren(Transform logoRoot)
		{
			for (int i = 0; i < logoRoot.childCount; i++)
			{
				HideLogoVfxWalk(logoRoot.GetChild(i));
			}
		}

		private static void HideLogoVfxWalk(Transform child)
		{
			string name = ((Object)child).name;
			if ((NameHas(name, "snow") || NameHas(name, "ember") || NameHas(name, "particle")) && ((Component)child).gameObject.activeSelf)
			{
				((Component)child).gameObject.SetActive(false);
				if (IsVerboseLogging)
				{
					LogInfo("Deep North logo VFX hidden (" + ((Object)child).name + ").");
				}
			}
			for (int i = 0; i < child.childCount; i++)
			{
				HideLogoVfxWalk(child.GetChild(i));
			}
		}

		internal static void ApplyLogo(Image target, Sprite sp)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_005d: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			target.sprite = sp;
			target.overrideSprite = sp;
			((Behaviour)target).enabled = true;
			target.preserveAspect = true;
			target.type = (Type)0;
			((Graphic)target).material = null;
			((Graphic)target).color = Color.white;
			if ((Object)(object)((Graphic)target).rectTransform != (Object)null)
			{
				RectTransform rectTransform = ((Graphic)target).rectTransform;
				Rect rect = sp.rect;
				float width = ((Rect)(ref rect)).width;
				rect = sp.rect;
				rectTransform.sizeDelta = new Vector2(width, ((Rect)(ref rect)).height);
				((Transform)((Graphic)target).rectTransform).localScale = Vector3.one;
			}
		}

		private static bool IsUsableTmpFont(TMP_FontAsset? font)
		{
			if ((Object)(object)font == (Object)null)
			{
				return false;
			}
			return !NameHas(((Object)font).name, "LiberationSans");
		}

		private static void ApplyMenuFont(TextMeshProUGUI target, TextMeshProUGUI? template)
		{
			//IL_004e: 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)
			TMP_FontAsset val = null;
			if ((Object)(object)template != (Object)null && IsUsableTmpFont(((TMP_Text)template).font))
			{
				val = ((TMP_Text)template).font;
				((TMP_Text)target).fontSharedMaterial = ((TMP_Text)template).fontSharedMaterial;
				((TMP_Text)target).fontSize = Mathf.Clamp(((TMP_Text)template).fontSize - 1f, 16f, 28f);
				((Graphic)target).color = ((Graphic)template).color;
			}
			if ((Object)(object)val == (Object)null)
			{
				val = FindAnyUsableTmpFont();
				((TMP_Text)target).fontSize = 22f;
				if ((Object)(object)template == (Object)null || !IsUsableTmpFont(((TMP_Text)template).font))
				{
					((Graphic)target).color = new Color(0.93f, 0.79f, 0.42f);
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				((TMP_Text)target).font = val;
			}
			else
			{
				LogWarn("Discord title: no usable TMP font found.");
			}
		}

		private static TMP_FontAsset? FindAnyUsableTmpFont()
		{
			if (IsUsableTmpFont(_cachedDiscordFont))
			{
				return _cachedDiscordFont;
			}
			TMP_FontAsset val = null;
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance != (Object)null)
			{
				TextMeshProUGUI[] componentsInChildren = ((Component)instance).GetComponentsInChildren<TextMeshProUGUI>(true);
				foreach (TextMeshProUGUI val2 in componentsInChildren)
				{
					if ((Object)(object)val2 != (Object)null && IsUsableTmpFont(((TMP_Text)val2).font))
					{
						val = ((TMP_Text)val2).font;
						break;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
				val = (IsUsableTmpFont(defaultFontAsset) ? defaultFontAsset : null);
			}
			_cachedDiscordFont = val;
			return val;
		}

		internal static void EnsureDiscordPanel(FejdStartup fejd, string reason)
		{
			if (!((Object)(object)fejd == (Object)null))
			{
				if ((Object)(object)_discordPanel == (Object)null)
				{
					CreateDiscordPanel(fejd);
				}
				else
				{
					DiscordPanelVisibility.Refresh(fejd, reason);
				}
			}
		}

		internal static void DestroyDiscordPanel()
		{
			CustomMainMenuPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.StopRoutine(ref instance._discordLogoRoutine);
			}
			if ((Object)(object)_discordPanel != (Object)null)
			{
				Object.Destroy((Object)(object)_discordPanel);
			}
			_discordPanel = null;
			_discordJoinButton = null;
			_discordLogoImage = null;
			ReleaseOwnedDiscordLogoSprite();
			DiscordPanelVisibility.ResetSessionState();
		}

		private static void EnsureParentedToStartMenuCanvas(FejdStartup fejd)
		{
			if ((Object)(object)_discordPanel == (Object)null)
			{
				return;
			}
			Canvas val = DiscordPanelVisibility.ResolveStartMenuCanvas(fejd);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)_discordPanel.transform.parent == (Object)(object)((Component)val).transform))
			{
				_discordPanel.transform.SetParent(((Component)val).transform, false);
				ApplyDiscordPanelAnchors(_discordPanel.GetComponent<RectTransform>());
				if (IsVerboseLogging)
				{
					LogInfo("Discord panel reparented to start-menu canvas (" + ((Object)val).name + ").");
				}
			}
		}

		private static void ApplyDiscordPanelAnchors(RectTransform? rootRt)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rootRt == (Object)null))
			{
				rootRt.anchorMin = new Vector2(1f, 0f);
				rootRt.anchorMax = new Vector2(1f, 0f);
				rootRt.pivot = new Vector2(1f, 0f);
				rootRt.anchoredPosition = new Vector2(-36f, 105f);
				rootRt.sizeDelta = new Vector2(328f, 360f);
				((Transform)rootRt).localScale = Vector3.one;
			}
		}

		internal static Button? FindStartMenuButton(FejdStartup startup)
		{
			if ((Object)(object)startup == (Object)null)
			{
				return null;
			}
			GameObject val = (((Object)(object)startup.m_menuList != (Object)null) ? startup.m_menuList : startup.m_mainMenu);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			val.GetComponentsInChildren<Button>(true, ButtonScratch);
			Button val2 = null;
			Button val3 = null;
			for (int i = 0; i < ButtonScratch.Count; i++)
			{
				Button val4 = ButtonScratch[i];
				if (!((Object)(object)val4 == (Object)null) && !IsOurDiscordUi(((Component)val4).gameObject))
				{
					if (val2 == null)
					{
						val2 = val4;
					}
					if (NameHas(((Object)((Component)val4).gameObject).name, "start"))
					{
						val3 = val4;
						break;
					}
				}
			}
			ButtonScratch.Clear();
			return val3 ?? val2;
		}

		private static bool IsOurDiscordUi(GameObject go)
		{
			Transform val = go.transform;
			while ((Object)(object)val != (Object)null)
			{
				if (((Object)val).name == "CustomMainMenuDiscordPanel")
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		internal static void SetButtonText(GameObject go, string txt)
		{
			TextMeshProUGUI[] componentsInChildren = go.GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI obj in componentsInChildren)
			{
				((TMP_Text)obj).richText = false;
				((TMP_Text)obj).text = txt;
			}
			Text[] componentsInChildren2 = go.GetComponentsInChildren<Text>(true);
			foreach (Text obj2 in componentsInChildren2)
			{
				obj2.supportRichText = false;
				obj2.text = txt;
			}
		}

		private static void NormalizeRectForLayoutGroup(RectTransform rt)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_004b: 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)
			((Transform)rt).localScale = Vector3.one;
			rt.anchorMin = new Vector2(0f, 0f);
			rt.anchorMax = new Vector2(1f, 1f);
			rt.pivot = new Vector2(0.5f, 0.5f);
			rt.anchoredPosition = Vector2.zero;
			rt.offsetMin = Vector2.zero;
			rt.offsetMax = Vector2.zero;
		}

		internal static void CreateDiscordPanel(FejdStartup? fejd = null)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: 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_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: 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)
			try
			{
				if (fejd == null)
				{
					fejd = FejdStartup.instance;
				}
				if ((Object)(object)_discordPanel != (Object)null)
				{
					if ((Object)(object)fejd != (Object)null)
					{
						EnsureParentedToStartMenuCanvas(fejd);
						EnsureDiscordJoinButton(fejd, null);
						DiscordPanelVisibility.Refresh(fejd, "Discord panel reuse");
					}
				}
				else
				{
					if ((Object)(object)fejd == (Object)null || (Object)(object)Instance == (Object)null)
					{
						return;
					}
					Canvas val = DiscordPanelVisibility.ResolveStartMenuCanvas(fejd);
					if ((Object)(object)val == (Object)null)
					{
						LogInfo("Discord panel deferred: start-menu canvas not ready.");
						return;
					}
					_discordPanel = new GameObject("CustomMainMenuDiscordPanel");
					_discordPanel.transform.SetParent(((Component)val).transform, false);
					ApplyDiscordPanelAnchors(_discordPanel.AddComponent<RectTransform>());
					VerticalLayoutGroup obj = _discordPanel.AddComponent<VerticalLayoutGroup>();
					((LayoutGroup)obj).childAlignment = (TextAnchor)4;
					((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
					((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false;
					((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
					((HorizontalOrVerticalLayoutGroup)obj).spacing = 16f;
					_discordPanel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
					Button val2 = FindStartMenuButton(fejd);
					TextMeshProUGUI template = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponentInChildren<TextMeshProUGUI>(true) : null);
					GameObject val3 = new GameObject("DiscordTitle", new Type[1] { typeof(RectTransform) });
					val3.SetActive(false);
					val3.transform.SetParent(_discordPanel.transform, false);
					NormalizeRectForLayoutGroup(val3.GetComponent<RectTransform>());
					LayoutElement obj2 = val3.AddComponent<LayoutElement>();
					obj2.flexibleWidth = 1f;
					obj2.preferredWidth = 300f;
					obj2.preferredHeight = 90f;
					TextMeshProUGUI obj3 = val3.AddComponent<TextMeshProUGUI>();
					((TMP_Text)obj3).richText = false;
					((TMP_Text)obj3).text = "Need help? Join Discord!";
					((TMP_Text)obj3).horizontalAlignment = (HorizontalAlignmentOptions)2;
					((TMP_Text)obj3).verticalAlignment = (VerticalAlignmentOptions)512;
					((TMP_Text)obj3).textWrappingMode = (TextWrappingModes)1;
					((TMP_Text)obj3).overflowMode = (TextOverflowModes)0;
					((TMP_Text)obj3).margin = Vector4.zero;
					((TMP_Text)obj3).enableAutoSizing = false;
					((Graphic)obj3).raycastTarget = false;
					ApplyMenuFont(obj3, template);
					val3.SetActive(true);
					GameObject val4 = new GameObject("DiscordLogo");
					val4.transform.SetParent(_discordPanel.transform, false);
					LayoutElement obj4 = val4.AddComponent<LayoutElement>();
					obj4.preferredHeight = 150f;
					obj4.preferredWidth = 300f;
					_discordLogoImage = val4.AddComponent<Image>();
					_discordLogoImage.type = (Type)0;
					((Graphic)_discordLogoImage).color = Color.white;
					((Graphic)_discordLogoImage).material = null;
					_discordLogoImage.preserveAspect = true;
					((Graphic)_discordLogoImage).raycastTarget = false;
					GameObject val5 = new GameObject("DiscordGlyph");
					val5.transform.SetParent(val4.transform, false);
					RectTransform obj5 = val5.AddComponent<RectTransform>();
					obj5.anchorMin = Vector2.zero;
					obj5.anchorMax = Vector2.one;
					Vector2 offsetMin = (obj5.offsetMax = Vector2.zero);
					obj5.offsetMin = offsetMin;
					Text obj6 = val5.AddComponent<Text>();
					obj6.text = "DIS";
					obj6.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
					obj6.fontSize = 24;
					obj6.fontStyle = (FontStyle)1;
					obj6.alignment = (TextAnchor)4;
					((Graphic)obj6).color = Color.white;
					EnsureDiscordJoinButton(fejd, val2);
					Instance._discordLogoRoutine = ((MonoBehaviour)Instance).StartCoroutine(LoadDiscordLogoDelayed());
					DiscordPanelVisibility.Refresh(fejd, "Discord panel created");
					if (IsVerboseLogging)
					{
						LogInfo("Discord panel ready (canvas=" + ((Object)val).name + ").");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? modLog = ModLog;
				if (modLog != null)
				{
					modLog.LogError((object)("Discord panel failed: " + ex));
				}
			}
		}

		private static void EnsureDiscordJoinButton(FejdStartup fejd, Button? startButton, float discordContentWidth = 300f)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			if ((Object)(object)_discordPanel == (Object)null)
			{
				return;
			}
			if ((Object)(object)_discordJoinButton != (Object)null)
			{
				_discordJoinButton.SetActive(true);
				return;
			}
			Button val = (((Object)(object)startButton != (Object)null) ? startButton : FindStartMenuButton(fejd));
			if ((Object)(object)val != (Object)null)
			{
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, _discordPanel.transform, false);
				val2.SetActive(true);
				LayoutElement obj = val2.GetComponent<LayoutElement>() ?? val2.AddComponent<LayoutElement>();
				obj.preferredWidth = discordContentWidth;
				obj.preferredHeight = 46f;
				Button component = val2.GetComponent<Button>();
				if ((Object)(object)component != (Object)null)
				{
					component.onClick = new ButtonClickedEvent();
					ButtonClickedEvent onClick = component.onClick;
					object obj2 = <>O.<0>__OnDiscordJoinClicked;
					if (obj2 == null)
					{
						UnityAction val3 = OnDiscordJoinClicked;
						<>O.<0>__OnDiscordJoinClicked = val3;
						obj2 = (object)val3;
					}
					((UnityEvent)onClick).AddListener((UnityAction)obj2);
					SetButtonText(val2, "Click here to join Discord!");
				}
				_discordJoinButton = val2;
			}
			else
			{
				_discordJoinButton = CreateFallbackDiscordButton(_discordPanel.transform, discordContentWidth);
			}
		}

		private static GameObject CreateFallbackDiscordButton(Transform parent, float width)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			GameObject val = new GameObject("DiscordJoinButton", new Type[5]
			{
				typeof(RectTransform),
				typeof(CanvasRenderer),
				typeof(Image),
				typeof(Button),
				typeof(LayoutElement)
			});
			val.transform.SetParent(parent, false);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.preferredWidth = width;
			component.preferredHeight = 46f;
			((Graphic)val.GetComponent<Image>()).color = new Color(0.18f, 0.14f, 0.08f, 0.92f);
			ButtonClickedEvent onClick = val.GetComponent<Button>().onClick;
			object obj = <>O.<0>__OnDiscordJoinClicked;
			if (obj == null)
			{
				UnityAction val2 = OnDiscordJoinClicked;
				<>O.<0>__OnDiscordJoinClicked = val2;
				obj = (object)val2;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			GameObject val3 = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val3.transform.SetParent(val.transform, false);
			NormalizeRectForLayoutGroup(val3.GetComponent<RectTransform>());
			TextMeshProUGUI obj2 = val3.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj2).richText = false;
			((TMP_Text)obj2).text = "Click here to join Discord!";
			((TMP_Text)obj2).horizontalAlignment = (HorizontalAlignmentOptions)2;
			((TMP_Text)obj2).verticalAlignment = (VerticalAlignmentOptions)512;
			((TMP_Text)obj2).enableAutoSizing = false;
			((Graphic)obj2).raycastTarget = false;
			ApplyMenuFont(obj2, null);
			return val;
		}

		private static void OnDiscordJoinClicked()
		{
			if (!DiscordPanelVisibility.TryOpenInviteUrl())
			{
				LogWarn("Discord invite URL is missing or invalid in config.");
			}
		}

		private static IEnumerator LoadDiscordLogoDelayed()
		{
			yield return null;
			CustomMainMenuPlugin instance = Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance._discordLogoRoutine = null;
			}
			if ((Object)(object)_discordLogoImage == (Object)null)
			{
				yield break;
			}
			string discordLogoPath = GetDiscordLogoPath();
			if (!File.Exists(discordLogoPath))
			{
				yield break;
			}
			Sprite val = LoadSpriteFromDisk(discordLogoPath);
			if ((Object)(object)val == (Object)null)
			{
				yield break;
			}
			if ((Object)(object)_discordLogoImage == (Object)null)
			{
				DestroyOwnedSprite(val);
				yield break;
			}
			_discordLogoImage.sprite = val;
			_discordLogoImage.overrideSprite = val;
			_discordLogoImage.type = (Type)0;
			((Graphic)_discordLogoImage).color = Color.white;
			((Graphic)_discordLogoImage).material = null;
			ReplaceOwnedSprite(ref _ownedDiscordLogoSprite, val);
			foreach (Transform item in ((Component)_discordLogoImage).transform)
			{
				Transform val2 = item;
				if (((Object)val2).name == "DiscordGlyph")
				{
					((Component)val2).gameObject.SetActive(false);
				}
			}
		}
	}
	internal static class DiscordPanelVisibility
	{
		private static ConfigEntry<bool>? _visibleOnlyOnStartMenu;

		private static ConfigEntry<string>? _discordInviteUrl;

		private static bool? _lastAppliedVisible;

		internal static void BindConfig(ConfigFile config)
		{
			_visibleOnlyOnStartMenu = config.Bind<bool>("Discord Panel", "VisibleOnlyOnStartMenu", true, "When true, the Discord panel is visible only on the main Start Game screen. Hidden on character/world select and during load.");
			_discordInviteUrl = config.Bind<string>("Discord Panel", "DiscordInviteUrl", "https://discord.gg/yX9tv3FXY6", "Discord invite opened by the panel button (http or https). Leave empty to disable the link.");
		}

		internal static bool TryOpenInviteUrl()
		{
			string text = (_discordInviteUrl?.Value ?? string.Empty).Trim();
			if (!IsValidInviteUrl(text))
			{
				return false;
			}
			Application.OpenURL(text);
			CustomMainMenuPlugin.LogInfo("Discord opened.");
			return true;
		}

		private static bool IsValidInviteUrl(string url)
		{
			if (string.IsNullOrWhiteSpace(url))
			{
				return false;
			}
			if (!Uri.TryCreate(url, UriKind.Absolute, out Uri result))
			{
				return false;
			}
			if (!(result.Scheme == Uri.UriSchemeHttp))
			{
				return result.Scheme == Uri.UriSchemeHttps;
			}
			return true;
		}

		internal static bool IsGatingEnabled()
		{
			if (_visibleOnlyOnStartMenu != null)
			{
				return _visibleOnlyOnStartMenu.Value;
			}
			return true;
		}

		internal static void Refresh(FejdStartup? fejd, string reason)
		{
			Evaluate(fejd, null, changedActive: false, reason);
		}

		internal static void Evaluate(FejdStartup? fejd, RelayKind? changed, bool changedActive, string reason)
		{
			GameObject discordPanelRoot = CustomMainMenuPlugin.DiscordPanelRoot;
			if (!((Object)(object)discordPanelRoot == (Object)null))
			{
				ApplyVisible(discordPanelRoot, ComputeShouldBeVisible(fejd, changed, changedActive), reason);
			}
		}

		private static bool ComputeShouldBeVisible(FejdStartup? fejd, RelayKind? changed, bool changedActive)
		{
			if (!CustomMainMenuPlugin.MainMenuActive)
			{
				return false;
			}
			if (IsOverlayBlockingStartMenu(fejd, changed, changedActive))
			{
				return false;
			}
			if (!IsGatingEnabled())
			{
				return true;
			}
			if ((Object)(object)fejd == (Object)null || (Object)(object)fejd.m_mainMenu == (Object)null)
			{
				return false;
			}
			if (changed != RelayKind.Main)
			{
				return fejd.m_mainMenu.activeInHierarchy;
			}
			return changedActive;
		}

		private static bool IsOverlayBlockingStartMenu(FejdStartup? fejd, RelayKind? changed, bool changedActive)
		{
			if (CinematicsManager.IsStartedPlaying())
			{
				return true;
			}
			if ((Object)(object)fejd == (Object)null)
			{
				return false;
			}
			GameObject cinematicsMenuList = fejd.m_cinematicsMenuList;
			if ((Object)(object)cinematicsMenuList != (Object)null && ((changed == RelayKind.Cinematics) ? changedActive : cinematicsMenuList.activeInHierarchy))
			{
				return true;
			}
			EulaWindow eulaWindow = fejd.m_eulaWindow;
			if ((Object)(object)eulaWindow != (Object)null && ((changed == RelayKind.Eula) ? changedActive : eulaWindow.IsWindowActive()))
			{
				return true;
			}
			return false;
		}

		internal static Canvas? ResolveStartMenuCanvas(FejdStartup fejd)
		{
			if ((Object)(object)fejd == (Object)null)
			{
				return null;
			}
			if ((Object)(object)fejd.m_mainMenu != (Object)null)
			{
				Canvas componentInParent = fejd.m_mainMenu.GetComponentInParent<Canvas>(true);
				if ((Object)(object)componentInParent != (Object)null)
				{
					return componentInParent;
				}
			}
			Canvas componentInParent2 = ((Component)fejd).GetComponentInParent<Canvas>(true);
			if ((Object)(object)componentInParent2 != (Object)null)
			{
				return componentInParent2;
			}
			return ((Component)fejd).GetComponentInChildren<Canvas>(true);
		}

		private static void ApplyVisible(GameObject panel, bool visible, string reason)
		{
			if (_lastAppliedVisible != visible || panel.activeSelf != visible)
			{
				panel.SetActive(visible);
				_lastAppliedVisible = visible;
				if (CustomMainMenuPlugin.IsVerboseLogging)
				{
					CustomMainMenuPlugin.LogInfo(string.Format("Discord panel {0} ({1}); gating={2}.", visible ? "shown" : "hidden", reason, IsGatingEnabled()));
				}
			}
		}

		internal static void ResetSessionState()
		{
			_lastAppliedVisible = null;
		}
	}
	internal static class LoadingScreens
	{
		private const float MaxLoadWaitSeconds = 2400f;

		private const float LoadingFadeOutSeconds = 0.6f;

		private static Coroutine? _fadeCoroutine;

		private static Coroutine? _timeoutCoroutine;

		private static bool _sessionArmed;

		private static bool _spawnHookAttached;

		private static string? _artPath;

		private static Image? _artImage;

		private static Sprite? _artSprite;

		private static Sprite? _artOriginalSprite;

		private static Color _artOriginalColor;

		private static bool _chromeApplied;

		private static GameObject? _hiddenTopBar;

		private static GameObject? _hiddenLogoGo;

		private static Image? _logoImage;

		private static Sprite? _logoOriginalSprite;

		private static Sprite? _logoOriginalOverride;

		private static Color _logoOriginalColor;

		private static bool _logoOriginalEnabled;

		private static bool _logoOriginalPreserveAspect;

		private static Vector2 _logoOriginalSizeDelta;

		private static Vector2 _logoOriginalAnchoredPos;

		private static Image? _logoOffsetImage;

		private static bool _logoPosCaptured;

		private static Sprite? _customLoadingLogoSprite;

		private static readonly List<Image> _disabledBackdrops = new List<Image>(4);

		private static readonly Vector3[] _childCorners = (Vector3[])(object)new Vector3[4];

		private static readonly Vector3[] _parentCorners = (Vector3[])(object)new Vector3[4];

		internal static void ResetForMainMenu()
		{
			StopAllWatchers("main menu reset");
		}

		internal static void StopAllWatchers(string reason)
		{
			if ((_sessionArmed || (Object)(object)_artImage != (Object)null) && CustomMainMenuPlugin.IsVerboseLogging)
			{
				LogLoading("Loading art session ended (" + reason + ").");
			}
			StopCoroutineRef(ref _fadeCoroutine);
			StopCoroutineRef(ref _timeoutCoroutine);
			DetachSpawnHook();
			RestoreLoadingChrome();
			LoadingTips.ResetSession();
			ClearArt();
			DestroyArtSprite();
			_sessionArmed = false;
			_artPath = null;
			_chromeApplied = false;
		}

		internal static void TryStartLoadingScreenWatcher(string trigger)
		{
			CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance;
			if (!((Object)(object)instance == (Object)null) && CustomMainMenuPlugin.IsActive && !CustomMainMenuPlugin.MainMenuActive && !_sessionArmed && !((Object)(object)_artImage != (Object)null))
			{
				_artPath = CustomMainMenuPlugin.GetRandomLoadingArtPath();
				bool flag = !string.IsNullOrEmpty(_artPath) && File.Exists(_artPath);
				LoadingTips.ReloadFromDisk();
				bool flag2 = LoadingTips.HasCustomTips();
				bool flag3 = LoadingTips.HideTopLetterboxEnabled();
				bool flag4 = LoadingTips.HideTipBackdropEnabled();
				bool flag5 = LoadingTips.UseCustomLoadingLogoEnabled();
				_sessionArmed = true;
				AttachSpawnHook();
				_timeoutCoroutine = ((MonoBehaviour)instance).StartCoroutine(TimeoutGuardCoroutine());
				if (!flag)
				{
					_artPath = null;
				}
				if (CustomMainMenuPlugin.IsVerboseLogging)
				{
					LogLoading(flag ? ("Loading art armed (" + trigger + "): " + _artPath) : $"Loading overlay armed ({trigger}): tips={flag2}, hideTopLetterbox={flag3}, hideTipBackdrop={flag4}, customLogo={flag5}.");
				}
				TryApplyToHud(Hud.instance, trigger);
			}
		}

		internal static void OnHudAwake(Hud hud)
		{
			if (_sessionArmed)
			{
				TryApplyToHud(hud, "Hud.Awake");
			}
		}

		private static void TryApplyToHud(Hud? hud, string trigger)
		{
			if ((Object)(object)hud == (Object)null)
			{
				return;
			}
			LoadingTips.CaptureVanillaIfNeeded(hud);
			TryApplyLoadingChrome(hud, trigger);
			if (!((Object)(object)_artImage != (Object)null) && !string.IsNullOrEmpty(_artPath) && !((Object)(object)hud.m_loadingImage == (Object)null))
			{
				ApplyArt(_artPath, hud.m_loadingImage);
				if (CustomMainMenuPlugin.IsVerboseLogging && (Object)(object)_artImage != (Object)null)
				{
					LogLoading("Loading art applied via Hud.m_loadingImage (" + trigger + ").");
				}
			}
		}

		private static void ApplyArt(string path, Image image)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = null;
			try
			{
				val = SpriteFromFile(path);
				if (!((Object)(object)val == (Object)null))
				{
					_artOriginalSprite = image.sprite;
					_artOriginalColor = ((Graphic)image).color;
					ApplyLoadingImage(image, val);
					_artImage = image;
					_artSprite = val;
				}
			}
			catch (Exception ex)
			{
				CustomMainMenuPlugin.LogWarn("Hud loading art failed: " + ex.Message);
				CustomMainMenuPlugin.DestroyOwnedSprite(val);
				_artImage = null;
				_artSprite = null;
			}
		}

		private static void TryApplyLoadingChrome(Hud hud, string trigger)
		{
			if (_chromeApplied || (Object)(object)hud.m_loadingScreen == (Object)null)
			{
				return;
			}
			bool flag = LoadingTips.HideTopLetterboxEnabled();
			bool flag2 = LoadingTips.HideTipBackdropEnabled();
			bool flag3 = LoadingTips.ShouldShowCustomLoadingLogo();
			bool flag4 = LoadingTips.UseCustomLoadingLogoEnabled();
			if (!flag && !flag2 && !flag4)
			{
				return;
			}
			Transform transform = ((Component)hud.m_loadingScreen).transform;
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val == (Object)null)
			{
				_chromeApplied = true;
				return;
			}
			Transform tip = (((Object)(object)hud.m_loadingTip != (Object)null) ? hud.m_loadingTip.transform : null);
			Transform art = (((Object)(object)hud.m_loadingImage != (Object)null) ? ((Component)hud.m_loadingImage).transform : null);
			Transform indicator = (((Object)(object)hud.m_loadingIndicator != (Object)null) ? ((Component)hud.m_loadingIndicator).transform : null);
			Image[] componentsInChildren = ((Component)transform).GetComponentsInChildren<Image>(true);
			GameObject val2 = null;
			float num = float.MinValue;
			Image val3 = null;
			int num2 = int.MinValue;
			foreach (Image val4 in componentsInChildren)
			{
				if ((Object)(object)val4 == (Object)null || (Object)(object)((Component)val4).gameObject == (Object)null)
				{
					continue;
				}
				Transform transform2 = ((Component)val4).transform;
				if (IsArtOrIndicator(transform2, transform, art, indicator))
				{
					continue;
				}
				RectTransform rectTransform = ((Graphic)val4).rectTransform;
				float normalizedCenterY = GetNormalizedCenterY(rectTransform, val);
				if (LooksLikeLetterboxBar(rectTransform, val))
				{
					if (normalizedCenterY >= 0.62f && !IsTipAncestor(transform2, transform, tip) && normalizedCenterY > num)
					{
						num = normalizedCenterY;
						val2 = ((Component)val4).gameObject;
					}
					continue;
				}
				int num3 = ScoreLoadingTitleLogo(val4, normalizedCenterY);
				if (num3 > num2)
				{
					num2 = num3;
					val3 = val4;
				}
			}
			if (flag)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					val2.SetActive(false);
					_hiddenTopBar = val2;
					if (CustomMainMenuPlugin.IsVerboseLogging)
					{
						LogLoading("Top loading letterbox hidden (" + trigger + "): " + GetHierarchyPath(val2.transform) + ".");
					}
				}
				else
				{
					LogLoading("Top loading letterbox not found (" + trigger + "); vanilla top bar left as-is.");
				}
			}
			if ((Object)(object)val3 != (Object)null && num2 >= 40)
			{
				if (flag3)
				{
					TryApplyCustomLoadingLogo(val3, trigger);
				}
				else if (flag)
				{
					((Component)val3).gameObject.SetActive(false);
					_hiddenLogoGo = ((Component)val3).gameObject;
					if (CustomMainMenuPlugin.IsVerboseLogging)
					{
						LogLoading("Vanilla loading logo hidden (" + trigger + "): " + GetHierarchyPath(((Component)val3).transform) + ".");
					}
				}
				else
				{
					ApplyLoadingLogoOffset(val3);
				}
			}
			else if (flag || flag3)
			{
				LogLoading("Loading title logo not found (" + trigger + ").");
			}
			if (flag2)
			{
				HideTipBackdrops(componentsInChildren, transform, val, art, indicator, val2, val3, trigger);
			}
			_chromeApplied = true;
		}

		private static void TryApplyCustomLoadingLogo(Image logo, string trigger)
		{
			//IL_007b: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			string logoPath = CustomMainMenuPlugin.GetLogoPath();
			if (string.IsNullOrEmpty(logoPath) || !File.Exists(logoPath))
			{
				LogLoading("Custom loading logo skipped (" + trigger + "): logo file missing.");
				ApplyLoadingLogoOffset(logo);
				return;
			}
			Sprite val = CustomMainMenuPlugin.LoadSpriteFromDisk(logoPath);
			if ((Object)(object)val == (Object)null)
			{
				LogLoading("Custom loading logo skipped (" + trigger + "): decode failed.");
				ApplyLoadingLogoOffset(logo);
				return;
			}
			_logoImage = logo;
			_logoOriginalSprite = logo.sprite;
			_logoOriginalOverride = logo.overrideSprite;
			_logoOriginalColor = ((Graphic)logo).color;
			_logoOriginalEnabled = ((Behaviour)logo).enabled;
			_logoOriginalPreserveAspect = logo.preserveAspect;
			_logoOriginalSizeDelta = (((Object)(object)((Graphic)logo).rectTransform != (Object)null) ? ((Graphic)logo).rectTransform.sizeDelta : Vector2.zero);
			_customLoadingLogoSprite = val;
			CustomMainMenuPlugin.ApplyLogo(logo, val);
			if (!((Component)logo).gameObject.activeSelf)
			{
				((Component)logo).gameObject.SetActive(true);
			}
			CustomMainMenuPlugin.HideLogoVfxChildren(((Component)logo).transform);
			ApplyLoadingLogoOffset(logo);
			if (CustomMainMenuPlugin.IsVerboseLogging)
			{
				LogLoading("Custom loading logo applied (" + trigger + "): " + GetHierarchyPath(((Component)logo).transform) + ".");
			}
		}

		private static void ApplyLoadingLogoOffset(Image logo)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)logo == (Object)null) && !((Object)(object)((Graphic)logo).rectTransform == (Object)null))
			{
				if (!_logoPosCaptured)
				{
					_logoOriginalAnchoredPos = ((Graphic)logo).rectTransform.anchoredPosition;
					_logoOffsetImage = logo;
					_logoPosCaptured = true;
				}
				LogoPlacement.ApplyLoadingOffset(((Graphic)logo).rectTransform, _logoOriginalAnchoredPos);
			}
		}

		private static void HideTipBackdrops(Image[] images, Transform root, RectTransform rootRt, Transform? art, Transform? indicator, GameObject? topBar, Image? logo, string trigger)
		{
			int num = 0;
			foreach (Image val in images)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null || !((Behaviour)val).enabled)
				{
					continue;
				}
				Transform transform = ((Component)val).transform;
				if (IsArtOrIndicator(transform, root, art, indicator) || ((Object)(object)topBar != (Object)null && (Object)(object)transform == (Object)(object)topBar.transform) || ((Object)(object)logo != (Object)null && (Object)(object)transform == (Object)(object)((Component)logo).transform))
				{
					continue;
				}
				RectTransform rectTransform = ((Graphic)val).rectTransform;
				if (LooksLikeLetterboxBar(rectTransform, rootRt) && !(GetNormalizedCenterY(rectTransform, rootRt) > 0.45f))
				{
					((Behaviour)val).enabled = false;
					((Graphic)val).raycastTarget = false;
					_disabledBackdrops.Add(val);
					num++;
					if (CustomMainMenuPlugin.IsVerboseLogging)
					{
						LogLoading("Loading tip backdrop hidden (" + trigger + "): " + GetHierarchyPath(transform) + ".");
					}
				}
			}
			if (num == 0)
			{
				LogLoading("Loading tip backdrop not found (" + trigger + "); vanilla bottom bar left as-is.");
			}
		}

		private static void RestoreLoadingChrome()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_hiddenTopBar))
			{
				_hiddenTopBar.SetActive(true);
			}
			_hiddenTopBar = null;
			if (Object.op_Implicit((Object)(object)_logoImage))
			{
				_logoImage.sprite = _logoOriginalSprite;
				_logoImage.overrideSprite = _logoOriginalOverride;
				((Graphic)_logoImage).color = _logoOriginalColor;
				((Behaviour)_logoImage).enabled = _logoOriginalEnabled;
				_logoImage.preserveAspect = _logoOriginalPreserveAspect;
				if ((Object)(object)((Graphic)_logoImage).rectTransform != (Object)null)
				{
					((Graphic)_logoImage).rectTransform.sizeDelta = _logoOriginalSizeDelta;
				}
			}
			_logoImage = null;
			if (Object.op_Implicit((Object)(object)_logoOffsetImage) && (Object)(object)((Graphic)_logoOffsetImage).rectTransform != (Object)null)
			{
				((Graphic)_logoOffsetImage).rectTransform.anchoredPosition = _logoOriginalAnchoredPos;
			}
			_logoOffsetImage = null;
			_logoPosCaptured = false;
			if (Object.op_Implicit((Object)(object)_hiddenLogoGo))
			{
				_hiddenLogoGo.SetActive(true);
			}
			_hiddenLogoGo = null;
			CustomMainMenuPlugin.DestroyOwnedSprite(_customLoadingLogoSprite);
			_customLoadingLogoSprite = null;
			_logoOriginalSprite = null;
			_logoOriginalOverride = null;
			for (int i = 0; i < _disabledBackdrops.Count; i++)
			{
				Image val = _disabledBackdrops[i];
				if ((Object)(object)val != (Object)null)
				{
					((Behaviour)val).enabled = true;
				}
			}
			_disabledBackdrops.Clear();
			_chromeApplied = false;
		}

		private static bool IsArtOrIndicator(Transform t, Transform root, Transform? art, Transform? indicator)
		{
			if ((Object)(object)t == (Object)(object)root)
			{
				return true;
			}
			if ((Object)(object)art != (Object)null && ((Object)(object)t == (Object)(object)art || t.IsChildOf(art)))
			{
				return true;
			}
			if ((Object)(object)indicator != (Object)null)
			{
				if (!((Object)(object)t == (Object)(object)indicator))
				{
					return t.IsChildOf(indicator);
				}
				return true;
			}
			return false;
		}

		private static bool IsTipAncestor(Transform t, Transform root, Transform? tip)
		{
			if ((Object)(object)tip == (Object)null)
			{
				return false;
			}
			Transform val = tip;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root)
			{
				if ((Object)(object)val == (Object)(object)t)
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static int ScoreLoadingTitleLogo(Image img, float normalizedY)
		{
			if (normalizedY < 0.45f)
			{
				return int.MinValue;
			}
			string text = (((Object)((Component)img).gameObject).name ?? string.Empty).ToLowerInvariant();
			string text2 = (((Object)(object)img.sprite != (Object)null) ? ((Object)img.sprite).name.ToLowerInvariant() : string.Empty);
			if (text.Contains("tip") || text.Contains("progress") || text.Contains("spin") || text.Contains("bar") || text.Contains("letter") || text2.Contains("tip"))
			{
				return int.MinValue;
			}
			int num = 0;
			if (text.Contains("logo") || text2.Contains("logo"))
			{
				num += 100;
			}
			if (text.Contains("valheim") || text2.Contains("valheim"))
			{
				num += 80;
			}
			if (text.Contains("title"))
			{
				num += 40;
			}
			if (num == 0)
			{
				return int.MinValue;
			}
			if (normalizedY >= 0.62f)
			{
				num += 20;
			}
			return num;
		}

		private static bool LooksLikeLetterboxBar(RectTransform rt, RectTransform root)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (IsFullscreenLike(rt))
			{
				return false;
			}
			Rect rect = rt.rect;
			float num = Mathf.Abs(((Rect)(ref rect)).width);
			rect = rt.rect;
			float num2 = Mathf.Abs(((Rect)(ref rect)).height);
			rect = root.rect;
			float num3 = Mathf.Abs(((Rect)(ref rect)).width);
			rect = root.rect;
			float num4 = Mathf.Abs(((Rect)(ref rect)).height);
			if (num3 < 8f || num4 < 8f)
			{
				return false;
			}
			if (num < num3 * 0.78f)
			{
				return false;
			}
			float num5 = num2 / num4;
			if (num5 >= 0.04f)
			{
				return num5 <= 0.34f;
			}
			return false;
		}

		private static float GetNormalizedCenterY(RectTransform rt, RectTransform root)
		{
			rt.GetWorldCorners(_childCorners);
			root.GetWorldCorners(_parentCorners);
			float y = _parentCorners[0].y;
			float num = _parentCorners[1].y - y;
			if (Mathf.Abs(num) < 0.001f)
			{
				return 0f;
			}
			return ((_childCorners[0].y + _childCorners[1].y) * 0.5f - y) / num;
		}

		private static void AttachSpawnHook()
		{
			if (_spawnHookAttached)
			{
				return;
			}
			try
			{
				Game.m_playerInitialSpawn += OnPlayerInitialSpawn;
				_spawnHookAttached = true;
			}
			catch (Exception ex)
			{
				CustomMainMenuPlugin.LogWarn("Could not subscribe to Game.m_playerInitialSpawn: " + ex.Message);
			}
		}

		private static void DetachSpawnHook()
		{
			if (_spawnHookAttached)
			{
				try
				{
					Game.m_playerInitialSpawn -= OnPlayerInitialSpawn;
				}
				catch (Exception ex)
				{
					CustomMainMenuPlugin.LogWarn("Could not unsubscribe from Game.m_playerInitialSpawn: " + ex.Message);
				}
				_spawnHookAttached = false;
			}
		}

		private static void OnPlayerInitialSpawn()
		{
			DetachSpawnHook();
			StopCoroutineRef(ref _timeoutCoroutine);
			MenuMusic.CommitForWorldEntry("initial spawn");
			_sessionArmed = false;
			RestoreLoadingChrome();
			LoadingTips.ResetSession();
			FadeOutAndClearArt();
		}

		private static IEnumerator TimeoutGuardCoroutine()
		{
			yield return (object)new WaitForSecondsRealtime(2400f);
			_timeoutCoroutine = null;
			LogLoading("Loading art timeout reached; restoring vanilla loading screen.");
			MenuMusic.CommitForWorldEntry("load timeout");
			StopAllWatchers("timeout");
		}

		private static void StopCoroutineRef(ref Coroutine? coroutine)
		{
			if (coroutine != null)
			{
				CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					((MonoBehaviour)instance).StopCoroutine(coroutine);
				}
				coroutine = null;
			}
		}

		private static void DestroyArtSprite()
		{
			CustomMainMenuPlugin.DestroyOwnedSprite(_artSprite);
			_artSprite = null;
		}

		private static void LogLoading(string message)
		{
			CustomMainMenuPlugin.LogInfo(message);
		}

		private static Sprite? SpriteFromFile(string absolutePath)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				byte[] data = File.ReadAllBytes(absolutePath);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				if (!TextureDecode.LoadImage(val, data, markNonReadable: true))
				{
					Object.Destroy((Object)(object)val);
					return null;
				}
				return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
			}
			catch
			{
				return null;
			}
		}

		private static void FadeOutAndClearArt()
		{
			CustomMainMenuPlugin instance = CustomMainMenuPlugin.Instance;
			if ((Object)(object)_artImage == (Object)null || (Object)(object)instance == (Object)null)
			{
				ClearArt();
				DestroyArtSprite();
			}
			else
			{
				StopCoroutineRef(ref _fadeCoroutine);
				_fadeCoroutine = ((MonoBehaviour)instance).StartCoroutine(LoadingFadeCoroutine(_artImage));
			}
		}

		private static IEnumerator LoadingFadeCoroutine(Image image)
		{
			float elapsed = 0f;
			float startAlpha = ((Graphic)image).canvasRenderer.GetAlpha();
			while (elapsed < 0.6f && (Object)(object)image != (Object)null)
			{
				elapsed += Time.unscaledDeltaTime;
				float num = Mathf.Clamp01(elapsed / 0.6f);
				((Graphic)image).color = new Color(1f, 1f, 1f, Mathf.Lerp(startAlpha, 0f, num));
				((Graphic)image).SetAllDirty();
				yield return null;
			}
			ClearArt();
			DestroyArtSprite();
			_fadeCoroutine = null;
		}

		private static void ClearArt()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Image artImage = _artImage;
			_artImage = null;
			if ((Object)(object)artImage != (Object)null)
			{
				artImage.sprite = _artOriginalSprite;
				artImage.overrideSprite = _artOriginalSprite;
				((Graphic)artImage).color = _artOriginalColor;
				((Graphic)artImage).SetAllDirty();
			}
			_artOriginalSprite = null;
		}

		private static void ApplyLoadingImage(Image target, Sprite customSprite)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			target.sprite = customSprite;
			target.overrideSprite = customSprite;
			((Behaviour)target).enabled = true;
			target.preserveAspect = false;
			target.type = (Type)0;
			((Graphic)target).material = null;
			((Graphic)target).color = Color.white;
			StretchToFullscreen(((Graphic)target).rectTransform);
			((Graphic)target).SetAllDirty();
		}

		private static bool Approximately(float a, float b)
		{
			return Mathf.Abs(a - b) <= 0.001f;
		}

		internal static bool IsFullscreenLike(RectTransform rt)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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)
			//IL_003a: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rt == (Object)null)
			{
				return false;
			}
			bool num = Approximately(rt.anchorMin.x, 0f) && Approximately(rt.anchorMin.y, 0f) && Approximately(rt.anchorMax.x, 1f) && Approximately(rt.anchorMax.y, 1f);
			bool flag = Approximately(rt.offsetMin.x, 0f) && Approximately(rt.offsetMin.y, 0f) && Approximately(rt.offsetMax.x, 0f) && Approximately(rt.offsetMax.y, 0f);
			return num && flag;
		}

		internal static string GetHierarchyPath(Transform transform)
		{
			if ((Object)(object)transform == (Object)null)
			{
				return "<null>";
			}
			List<string> list = new List<string>();
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				list.Insert(0, ((Object)val).name);
				val = val.parent;
			}
			return string.Join("/", list.ToArray());
		}

		internal static void StretchToFullscreen(RectTransform rt)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rt == (Object)null))
			{
				rt.anchorMin = Vector2.zero;
				rt.anchorMax = Vector2.one;
				rt.pivot = new Vector2(0.5f, 0.5f);
				rt.offsetMin = Vector2.zero;
				rt.offsetMax = Vector2.zero;
				rt.anchoredPosition = Vector2.zero;
				((Transform)rt).localScale = Vector3.one;
				((Transform)rt).localRotation = Quaternion.identity;
			}
		}
	}
	internal static class LoadingTips
	{
		internal const string ContentFileName = "customtips.txt";

		internal const int MaxTips = 20;

		private static ConfigEntry<bool>? _useCustomTips;

		private static ConfigEntry<bool>? _hideTopLetterbox;

		private static ConfigEntry<bool>? _hideTipBackdrop;

		private static ConfigEntry<bool>? _useCustomLoadingLogo;

		private static ConfigEntry<bool>? _showCustomLogoWhenLetterboxHidden;

		private static readonly List<string> CachedTips = new List<string>(20);

		private static List<string>? _vanillaTips;

		private static int _vanillaFromHudId;

		internal static void BindConfig(ConfigFile config)
		{
			_useCustomTips = config.Bind<bool>("Loading Screen", "UseCustomTips", false, "Use tips from CustomMainMenu/customtips.txt on the world-load screen. Off keeps Valheim's own tips. One tip per line, up to " + 20 + ". Blank lines are skipped. Missing or empty file keeps vanilla tips.");
			_hideTopLetterbox = config.Bind<bool>("Loading Screen", "HideTopLetterbox", false, "Hide the top black letterbox and Valheim's fiery loading logo. The bottom tip bar stays. Restored when you are in-world so sleep/teleport overlays stay vanilla. Use ShowCustomLogoWhenLetterboxHidden if you still want your menu logo on that screen.");
			_hideTipBackdrop = config.Bind<bool>("Loading Screen", "HideTipBackdrop", false, "Hide the black bar behind loading-screen tips. Tip text, click-to-cycle, and the spinner stay. Restored in-world.");
			_useCustomLoadingLogo = config.Bind<bool>("Loading Screen", "UseCustomLoadingLogo", false, "Replace Valheim's fiery loading-screen logo with the same logo.png / logo.jpg used on the start menu. Off keeps the vanilla title. Missing file leaves vanilla.");
			_showCustomLogoWhenLetterboxHidden = config.Bind<bool>("Loading Screen", "ShowCustomLogoWhenLetterboxHidden", false, "If HideTopLetterbox is on, still show the custom loading logo. Does nothing unless UseCustomLoadingLogo is on. Never keeps the vanilla fiery logo.");
		}

		internal static bool UseCustomTipsEnabled()
		{
			if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _useCustomTips != null)
			{
				return _useCustomTips.Value;
			}
			return false;
		}

		internal static bool HideTopLetterboxEnabled()
		{
			if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _hideTopLetterbox != null)
			{
				return _hideTopLetterbox.Value;
			}
			return false;
		}

		internal static bool HideTipBackdropEnabled()
		{
			if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _hideTipBackdrop != null)
			{
				return _hideTipBackdrop.Value;
			}
			return false;
		}

		internal static bool UseCustomLoadingLogoEnabled()
		{
			if (CustomMainMenuPlugin.ModEnabled != null && CustomMainMenuPlugin.ModEnabled.Value && _useCustomLoadingLogo != null)
			{
				return _useCustomLoadingLogo.Value;
			}
			return false;
		}

		internal static bool ShowCustomLogoWhenLetterboxHiddenEnabled()
		{
			if (UseCustomLoadingLogoEnabled() && _showCustomLogoWhenLetterboxHidden != null)
			{
				return _showCustomLogoWhenLetterboxHidden.Value;
			}
			return false;
		}

		internal static bool ShouldShowCustomLoadingLogo()
		{
			if (!UseCustomLoadingLogoEnabled())
			{
				return false;
			}
			if (HideTopLetterboxEnabled())
			{
				return ShowCustomLogoWhenLetterboxHiddenEnabled();
			}
			return true;
		}

		internal static bool HasCustomTips()
		{
			if (UseCustomTipsEnabled())
			{
				return CachedTips.Count > 0;
			}
			return false;
		}

		internal static string GetContentFilePath()
		{
			return CustomMainMenuPlugin.GetCustomTipsPath();
		}

		internal static void ReloadFromDisk()
		{
			CachedTips.Clear();
			if (!UseCustomTipsEnabled())
			{
				return;
			}
			try
			{
				string contentFilePath = GetContentFilePath();
				if (!File.Exists(contentFilePath))
				{
					CustomMainMenuPlugin.LogInfo("Custom loading tips enabled but customtips.txt was not found; vanilla tips stay.");
					return;
				}
				bool flag = false;
				string[] array = File.ReadAllLines(contentFilePath, Encoding.UTF8);
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0)
					{
						if (CachedTips.Count >= 20)
						{
							flag = true;
							break;
						}
						CachedTips.Add(text);
					}
				}
				if (CachedTips.Count == 0)
				{
					CustomMainMenuPlugin.LogInfo("Custom loading tips file is empty; vanilla tips stay.");
				}
				else
				{
					CustomMainMenuPlugin.LogInfo("Custom loading tips loaded: " + CachedTips.Count + (flag ? (" (extra lines beyond " + 20 + " ignored).") : "."));
				}
			}
			catch (Exception ex)
			{
				CachedTips.Clear();
				CustomMainMenuPlugin.LogWarn("Custom loading tips could not be read: " + ex.Message);
			}
		}

		internal static void CaptureVanillaIfNeeded(Hud? hud)
		{
			if (UseCustomTipsEnabled() && !((Object)(object)hud == (Object)null) && hud.m_loadingTips != null)
			{
				int instanceID = ((Object)hud).GetInstanceID();
				if (_vanillaTips == null || _vanillaFromHudId != instanceID)
				{
					_vanillaTips = new List<string>(hud.m_loadingTips);
					_vanillaFromHudId = instanceID;
				}
			}
		}

		internal static void ApplyToShuffle(Hud? hud)
		{
			if (!((Object)(object)hud == (Object)null) && hud.m_loadingTips != null && HasCustomTips())
			{
				CaptureVanillaIfNeeded(hud);
				hud.m_loadingTips.Clear();
				hud.m_loadingTips.AddRange(CachedTips);
			}
		}

		internal static void RestoreVanilla(Hud? hud)
		{
			if (_vanillaTips != null && !((Object)(object)hud == (Object)null) && hud.m_loadingTips != null && ((Object)hud).GetInstanceID() == _vanillaFromHudId)
			{
				hud.m_loadingTips.Clear();
				hud.m_loadingTips.AddRange(_vanillaTips);
			}
		}

		internal static void ResetSession()
		{
			RestoreVanilla(Hud.instance);
		}

		internal static string DescribeConfig()
		{
			return "UseCustomTips=" + UseCustomTipsEnabled() + ", HideTopLetterbox=" + HideTopLetterboxEnabled() + ", HideTipBackdrop=" + HideTipBackdropEnabled() + ", UseCustomLoadingLogo=" + UseCustomLoadingLogoEnabled() + ", ShowCustomLogoWhenLetterboxHidden=" + ShowCustomLogoWhenLetterboxHiddenEnabled() + ".";
		}
	}
	internal static class LogoPlacement
	{
		private static ConfigEntry<float>? _menuOffsetY;

		private static ConfigEntry<float>? _loadingOffsetY;

		private static Image? _menuLogo;

		private static Vector2 _menuBaseline;

		private static int _menuLogoId;

		internal static float MenuOffsetY => _menuOffsetY?.Value ?? 0f;

		internal static float LoadingOffsetY => _loadingOffsetY?.Value ?? 0f;

		internal static void BindConfig(ConfigFile config)
		{
			if (_menuOffsetY != null)
			{
				_menuOffsetY.SettingChanged -= OnMenuOffsetChanged;
			}
			_menuOffsetY = config.Bind<float>("Main Menu", "LogoOffsetY", 0f, "Vertical offset for the start-menu logo, in UI units. 0 is the current default placement. Negative moves the logo down, positive moves it up.");
			_loadingOffsetY = config.Bind<float>("Loading Screen", "LoadingLogoOffsetY", 0f, "Vertical offset for the world-load logo, in UI units. 0 is the current default placement. Negative moves the logo down, positive moves it up. Only applies when that logo is shown.");
			_menuOffsetY.SettingChanged += OnMenuOffsetChanged;
		}

		internal static void ApplyMenuOffset(Image? logo)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)logo == (Object)null) && !((Object)(object)((Graphic)logo).rectTransform == (Object)null))
			{
				int instanceID = ((Object)logo).GetInstanceID();
				if (_menuLogoId != instanceID)
				{
					_menuBaseline = ((Graphic)logo).rectTransform.anchoredPosition;
					_menuLogoId = instanceID;
				}
				_menuLogo = logo;
				SetY(((Graphic)logo).rectTransform, _menuBaseline, MenuOffsetY);
			}
		}

		internal static void ApplyLoadingOffset(RectTransform? rt, Vector2 baseline)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SetY(rt, baseline, LoadingOffsetY);
		}

		internal static void ResetMenuSession()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			_menuLogo = null;
			_menuLogoId = 0;
			_menuBaseline = Vector2.zero;
		}

		internal static string DescribeConfig()
		{
			return "LogoOffsetY=" + MenuOffsetY + ", LoadingLogoOffsetY=" + LoadingOffsetY + ".";
		}

		private static void OnMenuOffsetChanged(object sender, EventArgs e)
		{
			if (CustomMainMenuPlugin.MainMenuActive)
			{
				Image menuLogo = _menuLogo;
				if (!((Object)(object)menuLogo == (Object)null))
				{
					ApplyMenuOffset(menuLogo);
				}
			}
		}

		private static void SetY(RectTransform? rt, Vector2 baseline, float offsetY)
		{
			//IL_000a: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)rt == (Object)null))
			{
				Vector2 anchoredPosition = baseline;
				anchoredPosition.y += offsetY;
				rt.anchoredPosition = anchoredPosition;
			}
		}
	}
	internal static class MenuAmbientMute
	{
		private struct MutedSource
		{
			public AudioSource Source;

			public float Volume;

			public bool Mute;
		}

		private static ConfigEntry<bool>? _muteAmbient;

		private static readonly List<MutedSource> _muted = new List<MutedSource>();

		private static readonly HashSet<int> _mutedIds = new HashSet<int>();

		private static readonly List<AudioSource> _scratch = new List<AudioSource>();

		internal static void BindConfig(ConfigFile config)
		{
			if (_muteAmbient != null)
			{
				_muteAmbient.SettingChanged -= OnMuteSettingChanged;
			}
			_muteAmbient = config.Bind<bool>("Main Menu", "MuteAmbient", true, "Mute ocean, wind, and looping biome ambient on the start menu. Button clicks still follow the Effects slider. Off restores vanilla menu ambient.");
			TryMigrateMuteAmbientFromRetiredSection(config);
			_muteAmbient.SettingChanged += OnMuteSettingChanged;
		}

		private static void TryMigrateMuteAmbientFromRetiredSection(ConfigFile config)
		{
			if (_muteAmbient != null)
			{
				string configFilePath = config.ConfigFilePath;
				if (!string.IsNullOrEmpty(configFilePath) && File.Exists(configFilePath) && !TryReadIniBool(configFilePath, "Main Menu", "MuteAmbient", out var _) && TryReadIniBool(configFilePath, "Menu Scenery", "MuteAmbient", out var value2))
				{
					_muteAmbient.Value = value2;
				}
			}
		}

		private static bool TryReadIniBool(string path, string section, string key, out bool value)
		{
			value = false;
			string[] array;
			try
			{
				array = File.ReadAllLines(path);
			}
			catch
			{
				return false;
			}
			bool flag = false;
			string b = "[" + section + "]";
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text[0] == '#' || text[0] == ';')
				{
					continue;
				}
				if (text[0] == '[')
				{
					flag = string.Equals(text, b, StringComparison.Ordinal);
				}
				else
				{
					if (!flag || !text.StartsWith(key, StringComparison.Ordinal))
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num > 0 && string.Equals(text.Substring(0, num).Trim(), key, StringComparison.Ordinal))
					{
						string text2 = text.Substring(num + 1).Trim();
						int num2 = text2.IndexOfAny(new char[4] { ' ', '\t', '#', ';' });
						if (num2 > 0)
						{
							text2 = text2.Substring(0, num2);
						}
						return bool.TryParse(text2, out value);
					}
				}
			}
			return false;
		}

		internal static void Shutdown()
		{
			if (_muteAmbient != null)
			{
				_muteAmbient.SettingChanged -= OnMuteSettingChanged;
			}
			Restore();
		}

		private static bool ShouldMute()
		{
			if (CustomMainMenuPlugin.IsActive && CustomMainMenuPlugin.MainMenuActive)
			{
				if (_muteAmbient != null)
				{
					return _muteAmbient.Value;
				}
				return true;
			}
			return false;
		}

		internal static void Apply()
		{
			if (!ShouldMute())
			{
				Restore();
			}
			else
			{
				MuteStartSceneAmbient();
			}
		}

		internal static void Restore()
		{
			for (int i = 0; i < _muted.Count; i++)
			{
				MutedSource mutedSource = _muted[i];
				if (!((Object)(object)mutedSource.Source == (Object)null))
				{
					mutedSource.Source.volume = mutedSource.Volume;
					mutedSource.Source.mute = mutedSource.Mute;
				}
			}
			_muted.Clear();
			_mutedIds.Clear();
		}

		private static void OnMuteSettingChanged(object sender, EventArgs e)
		{
			Apply();
		}

		private static void MuteStartSceneAmbient()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				FejdStartup instance = FejdStartup.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				AudioMan instance2 = AudioMan.instance;
				AudioMixerGroup gui = (((Object)(object)instance2 != (Object)null) ? instance2.m_guiMixer : null);
				AudioMixerGroup ambient = (((Object)(object)instance2 != (Object)null) ? instance2.m_ambientMixer : null);
				AudioMixerGroup music = (((Object)(object)MusicMan.instance != (Object)null) ? MusicMan.instance.m_musicMixer : null);
				int num = 0;
				Scene scene = ((Component)instance).gameObject.scene;
				GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
				for (int i = 0; i < rootGameObjects.Length; i++)
				{
					rootGameObjects[i].GetComponentsInChildren<AudioSource>(true, _scratch);
					for (int j = 0; j < _scratch.Count; j++)
					{
						AudioSource src = _scratch[j];
						if (ShouldMuteSceneSource(src, gui, ambient, music) && RememberAndSilence(src))
						{
							num++;
						}
					}
				}
				_scratch.Clear();
				if (num > 0 && CustomMainMenuPlugin.IsVerboseLogging)
				{
					CustomMainMenuPlugin.LogInfo($"Menu ambient muted ({num} scene source(s)).");
				}
			}
			catch (Exception ex)
			{
				_scratch.Clear();
				CustomMainMenuPlugin.LogInfo("Menu ambient scene mute skipped: " + ex.Message);
			}
		}

		private static bool ShouldMuteSceneSource(AudioSource? src, AudioMixerGroup? gui, AudioMixerGroup? ambient, AudioMixerGroup? music)
		{
			if ((Object)(object)src == (Object)null)
			{
				return false;
			}
			AudioMixerGroup outputAudioMixerGroup = src.outputAudioMixerGroup;
			if (IsGuiOrMusicGroup(outputAudioMixerGroup, gui, music))
			{
				return false;
			}
			if ((Object)(object)ambient != (Object)null && (Object)(object)outputAudioMixerGroup == (Object)(object)ambient)
			{
				return true;
			}
			if ((Object)(object)outputAudioMixerGroup != (Object)null && GroupNameContains(outputAudioMixerGroup, "Ambient"))
			{
				return true;
			}
			if (!src.loop)
			{
				if (src.isPlaying)
				{
					return src.spatialBlend > 0.05f;
				}
				return false;
			}
			return true;
		}

		private static bool IsGuiOrMusicGroup(AudioMixerGroup? group, AudioMixerGroup? gui, AudioMixerGroup? music)
		{
			if ((Object)(object)group == (Object)null)
			{
				return false;
			}
			if ((Object)(object)gui != (Object)null && (Object)(object)group == (Object)(object)gui)
			{
				return true;
			}
			if ((Object)(object)music != (Object)null && (Object)(object)group == (Object)(object)music)
			{
				return true;
			}
			if (!GroupNameContains(group, "GUI"))
			{
				return GroupNameContains(group, "Music");
			}
			return true;
		}

		private static bool GroupNameContains(AudioMixerGroup group, string token)
		{
			string name = ((Object)group).name;
			if (!string.IsNullOrEmpty(name))
			{
				return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static bool RememberAndSilence(AudioSource? src)
		{
			if ((Object)(object)src == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)src).GetInstanceID();
			if (!_mutedIds.Add(instanceID))
			{
				src.volume = 0f;
				src.mute = true;
				return false;
			}
			_muted.Add(new MutedSource
			{
				Source = src,
				Volume = src.volume,
				Mute = src.mute
			});
			src.volume = 0f;
			src.mute = true;
			return true;
		}
	}
	internal static class MenuAtmosphere
	{
		private const float NightTimeFraction = 0.875f;

		private const float SunAngle = 45f;

		private static readonly Color FogColor = new Color(0.047f, 0.165f, 0.196f, 1f);

		private static readonly Color AmbientLight = new Color(0.191f, 0.269f, 0.36f, 1f);

		private static readonly Color SunColor = new Color(0.337f, 0.729f, 0.714f, 1f);

		private const float FogDensity = 0.08f;

		private const float SunIntensity = 0.05f;

		private const float ValheimLitSunIntensity = 0.7f;

		private static bool _applied;

		internal static IEnumerator ApplyWhenReady()
		{
			_applied = false;
			for (int i = 0; i < 8; i++)
			{
				if (!CustomMainMenuPlugin.MainMenuActive)
				{
					break;
				}
				if (!CustomMainMenuPlugin.IsActive)
				{
					break;
				}
				if (TryApply())
				{
					yield break;
				}
				yield return null;
			}
			if (CustomMainMenuPlugin.MainMenuActive && !_applied)
			{
				CustomMainMenuPlugin.LogInfo("Menu atmosphere: no directional light on the start scene; night look was not applied.");
			}
		}

		private static bool TryApply()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_00a5: 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_00af: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			Light val = ResolveSun();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			RenderSettings.fog = true;
			RenderSettings.fogColor = FogColor;
			RenderSettings.fogDensity = 0.08f;
			RenderSettings.ambientMode = (AmbientMode)3;
			RenderSettings.ambientLight = AmbientLight;
			Quaternion val2 = Quaternion.Euler(-45f, 0f, 0f) * Quaternion.Euler(0f, -90f, 0f) * Quaternion.Euler(225f, 0f, 0f);
			((Component)val).transform.rotation = val2;
			val.intensity = 0.05f;
			val.color = SunColor;
			Vector3 val3 = -(val2 * Vector3.forward);
			Shader.SetGlobalVector("_SunDir", Vector4.op_Implicit(val3));
			Shader.SetGlobalVector("_SkyboxSunDir", Vector4.op_Implicit(val3));
			Shader.SetGlobalColor("_SunFogColor", FogColor);
			Shader.SetGlobalColor("_SunColor", SunColor * 0.7f);
			Shader.SetGlobalColor("_AmbientColor", AmbientLight);
			Shader.SetGlobalFloat("_Wet", 0f);
			_applied = true;
			if (CustomMainMenuPlugin.IsVerboseLogging)
			{
				CustomMainMenuPlugin.LogInfo($"Menu atmosphere applied (night, fog density {0.08f:0.###}, fraction {0.875f:0.###}).");
			}
			return true;
		}

		private static Light? ResolveSun()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_005f: 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_003b: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Invalid comparison between Unknown and I4
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Invalid comparison between Unknown and I4
			//IL_00bb: 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)
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			Scene scene = ((Component)instance).gameObject.scene;
			Light sun = RenderSettings.sun;
			if ((Object)(object)sun != (Object)null && (int)sun.type == 1 && ((Component)sun).gameObject.scene == scene)
			{
				return sun;
			}
			GameObject val = GameObject.Find("Directional Light");
			if ((Object)(object)val != (Object)null && val.scene == scene)
			{
				Light component = val.GetComponent<Light>();
				if ((Object)(object)component != (Object)null && (int)component.type == 1)
				{
					return component;
				}
			}
			Light[] array = Object.FindObjectsByType<Light>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (Light val2 in array)
			{
				if ((Object)(object)val2 != (Object)null && (int)val2.type == 1 && ((Component)val2).gameObject.scene == scene)
				{
					return val2;
				}
			}
			return null;
		}
	}
	internal static class MenuChangelog
	{
		internal const string ContentFileName = "changelog.txt";

		private static ConfigEntry<bool>? _enabled;

		private static ConfigEntry<bool>? _useCustomText;

		private static ConfigEntry<bool>? _overrideVanillaText;

		private static ConfigEntry<bool>? _autoOpen;

		private static ConfigEntry<string>? _titleText;

		private static string _customText = string.Empty;

		private static FileSystemWatcher? _watcher;

		private static ChangeLog? _changeLog;

		private static TMP_Text? _titleLabel;

		private static Text? _titleTextLabel;

		private static string _vanillaTitle = string.Empty;

		pr