Decompiled source of Pause Menu Save v0.1.23

plugins/PauseMenuSave.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using PauseMenuSave.Logic;
using Sons.Gameplay.GameSetup;
using Sons.Gui;
using Sons.Multiplayer;
using Sons.Save;
using TMPro;
using TheForest.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization;
using UnityEngine.Localization.Components;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("PauseMenuSave")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.23.0")]
[assembly: AssemblyInformationalVersion("0.1.23+6366f4cb357066890da8b40938c52810a7bd1d8f")]
[assembly: AssemblyProduct("PauseMenuSave")]
[assembly: AssemblyTitle("PauseMenuSave")]
[assembly: AssemblyVersion("0.1.23.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;
		}
	}
}
internal static class SettingMove
{
	internal static void Move(ConfigFile config, string oldSection, string oldKey, ConfigEntryBase to)
	{
		bool saveOnConfigSet = config.SaveOnConfigSet;
		config.SaveOnConfigSet = false;
		try
		{
			ConfigEntry<string> val = config.Bind<string>(oldSection, oldKey, "\u0001", "Old place of a moved setting.");
			bool num = val.Value != "\u0001";
			if (num)
			{
				to.SetSerializedValue(((ConfigEntryBase)val).GetSerializedValue());
			}
			config.Remove(((ConfigEntryBase)val).Definition);
			if (num)
			{
				config.Save();
			}
		}
		finally
		{
			config.SaveOnConfigSet = saveOnConfigSet;
		}
	}
}
namespace PauseMenuSave
{
	public class AutoSaver : MonoBehaviour
	{
		[HarmonyPatch(typeof(SaveGameManager), "Save", new Type[]
		{
			typeof(string),
			typeof(string),
			typeof(bool)
		})]
		private static class GameSaveDone
		{
			private static void Postfix(string dir, bool savePlayerOnly)
			{
				//IL_004b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Expected O, but got Unknown
				if (!savePlayerOnly && uint.TryParse(Path.GetFileName(dir?.TrimEnd('/', '\\')), out var result) && result != 0 && result != _lastWrittenSlot)
				{
					_lastWrittenSlot = result;
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(70, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Game saved to slot ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(result);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": auto-save will save there (game's own slot id: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint>(GameSetupManager.GetSelectedSaveId());
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
					}
					logger.LogInfo(val);
				}
				if (_awaitingGame)
				{
					_gameReacted = true;
					Plugin.Trace("Game save finished, its after-save callbacks ran: the game confirms the save itself.");
				}
			}
		}

		internal static AutoSaver Instance;

		private const float CheckSeconds = 1f;

		private float _nextCheck;

		private float _nextSave = -1f;

		private static bool _awaitingGame;

		private static bool _gameReacted;

		private static uint _lastWrittenSlot;

		private float _confirmAt = -1f;

		public AutoSaver(IntPtr ptr)
			: base(ptr)
		{
		}

		private void Update()
		{
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.unscaledTime + 1f;
			if (_confirmAt >= 0f && Time.unscaledTime >= _confirmAt)
			{
				Confirm();
			}
			float num = Plugin.AutoSaveMinutes.Value;
			if ((Object)(object)LocalPlayer.Inventory == (Object)null)
			{
				_lastWrittenSlot = 0u;
			}
			if (!Plugin.AutoSaveEnabled.Value || (Object)(object)LocalPlayer.Inventory == (Object)null)
			{
				_nextSave = -1f;
			}
			else if (_nextSave < 0f)
			{
				_nextSave = Time.unscaledTime + num * 60f;
			}
			else
			{
				if (Time.unscaledTime < _nextSave)
				{
					return;
				}
				if (!LocalPlayer.IsInWorld)
				{
					Plugin.Trace($"Auto-save waiting: player view {LocalPlayer.CurrentView}.");
					return;
				}
				if (IsDown())
				{
					Plugin.Trace("Auto-save waiting: player not alive.");
					return;
				}
				_nextSave = Time.unscaledTime + num * 60f;
				if (PauseMenu.IsActive)
				{
					Plugin.Trace("Auto-save skipped: pause menu open, next one in " + num + " min.");
				}
				else if (BoltNetwork.isClient)
				{
					Plugin.Trace("Auto-save skipped: multiplayer guest, the host saves the world.");
				}
				else
				{
					Save();
				}
			}
		}

		internal static bool IsDown()
		{
			PlayerStats stats = LocalPlayer.Stats;
			if (!((Object)(object)LocalPlayer.Vitals != (Object)null) || LocalPlayer.Vitals.IsAlive())
			{
				if ((Object)(object)stats != (Object)null)
				{
					if (!stats.IsDead() && !stats.IsDying())
					{
						return stats.PlayerIsDowned;
					}
					return true;
				}
				return false;
			}
			return true;
		}

		private static string GameSlotByGuid()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = CoopLobbyManager.ActiveGuid();
				return $"game's lookup for world '{text}': slot {CoopSaveUtils.GetSaveIdForGuid(GameSetupManager.GetSaveGameType(), text)}";
			}
			catch (Exception ex)
			{
				return "game's lookup failed: " + ex.Message;
			}
		}

		private void Save()
		{
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				uint num = ((_lastWrittenSlot != 0) ? _lastWrittenSlot : GameSetupManager.GetSelectedSaveId());
				if (num == 0)
				{
					Show(Plugin.Text(Plugin.NoSlotMessage, "NoSlot"));
					ManualLogSource logger = Plugin.Logger;
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(48, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Auto-save skipped: game has no save slot yet (");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(GameSlotByGuid());
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
					}
					logger.LogWarning(val);
					return;
				}
				PauseMenu instance = PauseMenu._instance;
				SaveMenu val2 = (((Object)(object)instance != (Object)null) ? instance._saveMenu : null);
				string text = (((Object)(object)val2 != (Object)null) ? val2.GetCurrentGameSaveName() : null);
				if (string.IsNullOrEmpty(text))
				{
					throw new InvalidOperationException("current game save name not found");
				}
				_awaitingGame = true;
				_gameReacted = false;
				SaveGameManager.SaveToSlot((int)num, text);
				_confirmAt = Time.unscaledTime + 1f;
				ManualLogSource logger2 = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(25, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Auto-saved to slot ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<uint>(num);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" ('");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("').");
				}
				logger2.LogInfo(val3);
			}
			catch (Exception ex)
			{
				_awaitingGame = false;
				Show(Plugin.Text(Plugin.FailedMessage, "Failed"));
				ManualLogSource logger3 = Plugin.Logger;
				BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(18, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Auto-save failed: ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<Exception>(ex);
				}
				logger3.LogError(val4);
			}
		}

		private void Confirm()
		{
			_confirmAt = -1f;
			_awaitingGame = false;
			if (!_gameReacted)
			{
				Plugin.Trace("Game did not announce the auto-save: showing its own 'Game Saved' message.");
				Show(Plugin.Text(Plugin.SavedMessage, "Saved", "ScreenPrompts", "GAME_SAVED"));
			}
		}

		internal void Show(string text)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			float value = Plugin.MessageSeconds.Value;
			if (value <= 0f)
			{
				return;
			}
			HudGui instance = HudGui._instance;
			if (!PauseMenu.IsActive && (Object)(object)instance != (Object)null)
			{
				try
				{
					instance.DisplayGeneralMessage(text, value, (ItemUiData)null);
					return;
				}
				catch (Exception ex)
				{
					ManualLogSource logger = Plugin.Logger;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(38, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Game HUD message failed, using a box: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
			}
			MessageBox obj = ((Component)this).GetComponent<MessageBox>() ?? ((Component)this).gameObject.AddComponent<MessageBox>();
			obj.Message = text;
			obj.Until = Time.unscaledTime + value;
			((Behaviour)obj).enabled = true;
		}
	}
	public class MessageBox : MonoBehaviour
	{
		internal string Message;

		internal float Until;

		public MessageBox(IntPtr ptr)
			: base(ptr)
		{
		}

		private void OnGUI()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime > Until || string.IsNullOrEmpty(Message))
			{
				((Behaviour)this).enabled = false;
				return;
			}
			GUIContent val = new GUIContent(Message);
			Vector2 val2 = GUI.skin.box.CalcSize(val);
			GUI.Box(new Rect(((float)Screen.width - val2.x) / 2f, Plugin.MessageTop.Value, val2.x, val2.y), val);
		}
	}
	[BepInPlugin("aznan.sotf.pausemenusave", "PauseMenuSave", "0.1.23")]
	public class Plugin : BasePlugin
	{
		internal const string Version = "0.1.23";

		internal static ManualLogSource Logger;

		internal static ConfigEntry<bool> FullLogging;

		internal static ConfigEntry<string> ButtonText;

		internal static ConfigEntry<int> ButtonPosition;

		internal static ConfigEntry<bool> AutoSaveEnabled;

		internal static ConfigEntry<int> AutoSaveMinutes;

		internal static ConfigEntry<string> SavedMessage;

		internal static ConfigEntry<string> NoSlotMessage;

		internal static ConfigEntry<string> FailedMessage;

		internal static ConfigEntry<string> CharacterSavedMessage;

		internal static ConfigEntry<float> MessageSeconds;

		internal static ConfigEntry<float> MessageTop;

		private const string Automatic = "Empty = automatic, in the game's language.";

		private static string LocaleCode
		{
			get
			{
				Locale selectedLocale = LocalizationSettings.SelectedLocale;
				if (selectedLocale == null)
				{
					return null;
				}
				return selectedLocale.Identifier.Code;
			}
		}

		internal static void Trace(string message)
		{
			if (FullLogging.Value)
			{
				Logger.LogInfo((object)message);
			}
		}

		private static void Refuse(ConfigEntry<int> e, Func<int, bool> breaks, string why)
		{
			Check();
			e.SettingChanged += delegate
			{
				Check();
			};
			void Check()
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Expected O, but got Unknown
				if (breaks(e.Value))
				{
					ManualLogSource logger = Logger;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(28, 5, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((ConfigEntryBase)e).Definition.Section);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((ConfigEntryBase)e).Definition.Key);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" = ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(e.Value);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" refused (");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(why);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("); back to ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<object>(((ConfigEntryBase)e).DefaultValue);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
					}
					logger.LogWarning(val);
					e.Value = (int)((ConfigEntryBase)e).DefaultValue;
				}
			}
		}

		public override void Load()
		{
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Expected O, but got Unknown
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			Texts.SelfCheck();
			ButtonPosition = ((BasePlugin)this).Config.Bind<int>("General", "ButtonPosition", -1, "Place of the save button in the pause menu bar, counted from 0 at the left. -1 = right after the entry it is copied from. Advised: -1 to 20.");
			ConfigEntry<float> val = ((BasePlugin)this).Config.Bind<float>("Auto-save", "AutoSaveMinutes", 10f, "Old form of the setting, replaced below.");
			float value = val.Value;
			((BasePlugin)this).Config.Remove(((ConfigEntryBase)val).Definition);
			AutoSaveEnabled = ((BasePlugin)this).Config.Bind<bool>("Auto-save", "AutoSaveEnabled", value > 0f, "True = the game is saved automatically (single player or host).");
			AutoSaveMinutes = ((BasePlugin)this).Config.Bind<int>("Auto-save", "AutoSaveMinutes", 10, "Minutes between auto-saves. Advised: 1 to 60.");
			Refuse(AutoSaveMinutes, (int m) => m < 1, "less than a minute would save over your game every second; AutoSaveEnabled turns auto-save off");
			int num = Math.Max(1, (int)Math.Round(value));
			if (value > 0f && num != AutoSaveMinutes.Value)
			{
				AutoSaveMinutes.Value = num;
			}
			ButtonText = ((BasePlugin)this).Config.Bind<string>("Texts", "ButtonText", "", "Text of the save button added to the pause menu. Empty = automatic, in the game's language.");
			SavedMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "SavedMessage", "", "Message shown after an auto-save. Empty = automatic, in the game's language.");
			NoSlotMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "NoSlotMessage", "", "Message shown when a new game has no save slot yet. Empty = automatic, in the game's language.");
			FailedMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "FailedMessage", "", "Message shown when a save fails. Empty = automatic, in the game's language.");
			CharacterSavedMessage = ((BasePlugin)this).Config.Bind<string>("Texts", "CharacterSavedMessage", "", "Message shown when a multiplayer guest saves their character with the button. Empty = automatic, in the game's language.");
			MessageSeconds = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageSeconds", 3f, "How long messages stay on screen, in seconds (the game's own message in game, a box in the pause menu). 0 = no message. Advised: 0 to 30.");
			MessageTop = ((BasePlugin)this).Config.Bind<float>("Texts", "MessageTop", 40f, "Distance of the pause menu message box from the top of the screen, in pixels. Advised: 0 to 2000.");
			FullLogging = ((BasePlugin)this).Config.Bind<bool>("Debug", "FullLogging", false, "True = the BepInEx log gets every step useful to diagnose a problem (decisions, values read, object aimed at, failures with their cause). False = only loading, version and errors.");
			SettingMove.Move(((BasePlugin)this).Config, "General", "ButtonText", (ConfigEntryBase)(object)ButtonText);
			SettingMove.Move(((BasePlugin)this).Config, "Auto-save", "SavedMessage", (ConfigEntryBase)(object)SavedMessage);
			SettingMove.Move(((BasePlugin)this).Config, "Auto-save", "NoSlotMessage", (ConfigEntryBase)(object)NoSlotMessage);
			SettingMove.Move(((BasePlugin)this).Config, "Message", "FailedMessage", (ConfigEntryBase)(object)FailedMessage);
			SettingMove.Move(((BasePlugin)this).Config, "Message", "CharacterSavedMessage", (ConfigEntryBase)(object)CharacterSavedMessage);
			SettingMove.Move(((BasePlugin)this).Config, "Message", "MessageSeconds", (ConfigEntryBase)(object)MessageSeconds);
			SettingMove.Move(((BasePlugin)this).Config, "Message", "MessageTop", (ConfigEntryBase)(object)MessageTop);
			new Harmony("aznan.sotf.pausemenusave").PatchAll();
			ClassInjector.RegisterTypeInIl2Cpp<AutoSaver>();
			ClassInjector.RegisterTypeInIl2Cpp<MessageBox>();
			GameObject val2 = new GameObject("PauseMenuSave");
			Object.DontDestroyOnLoad((Object)val2);
			((Object)val2).hideFlags = (HideFlags)61;
			AutoSaver.Instance = val2.AddComponent<AutoSaver>();
			ManualLogSource logger = Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(21, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("PauseMenuSave ");
				((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>("0.1.23");
				((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" loaded");
			}
			logger.LogInfo(val3);
		}

		internal static string Text(ConfigEntry<string> setting, string modKey, string gameTable = null, string gameKey = null)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			if (!string.IsNullOrEmpty(setting.Value))
			{
				return setting.Value;
			}
			if (gameTable != null)
			{
				try
				{
					string localizedString = LocalizationSettings.StringDatabase.GetLocalizedString(TableReference.op_Implicit(gameTable), TableEntryReference.op_Implicit(gameKey), (Locale)null, (FallbackBehavior)0, (Il2CppReferenceArray<Object>)null);
					if (!string.IsNullOrEmpty(localizedString))
					{
						return localizedString;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger = Logger;
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(40, 3, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Game text ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(gameTable);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(gameKey);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" not found, using the mod's: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
			}
			return Texts.Get(LocaleCode, modKey);
		}
	}
}
namespace PauseMenuSave.Patches
{
	[HarmonyPatch(typeof(PauseMenu), "Open")]
	public static class SaveButtonInjector
	{
		private const string ButtonObjectName = "PauseMenuSave_Button";

		public static void Postfix(PauseMenu __instance)
		{
			Button val = null;
			foreach (Button componentsInChild in ((Component)__instance).GetComponentsInChildren<Button>(true))
			{
				if (((Object)componentsInChild).name == "PauseMenuSave_Button")
				{
					val = componentsInChild;
				}
			}
			Button val2 = val ?? AddButton(__instance);
			if ((Object)(object)val2 != (Object)null)
			{
				SetLabel(val2, Plugin.Text(Plugin.ButtonText, "Save", "Menus", "SAVE"));
			}
		}

		private static Button AddButton(PauseMenu __instance)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			List<Button> singlePlayerMenuButtons = __instance._singlePlayerMenuButtons;
			Button val = ((singlePlayerMenuButtons != null && singlePlayerMenuButtons.Count > 0) ? singlePlayerMenuButtons[0] : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Logger.LogError((object)"Pause menu has no button to copy; save button not added.");
				return null;
			}
			Button component = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent).GetComponent<Button>();
			((Object)component).name = "PauseMenuSave_Button";
			int value = Plugin.ButtonPosition.Value;
			((Component)component).transform.SetSiblingIndex((value >= 0) ? value : (((Component)val).transform.GetSiblingIndex() + 1));
			component.onClick = new ButtonClickedEvent();
			((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)delegate
			{
				OpenSaveScreen(__instance);
			}));
			foreach (LocalizeStringEvent componentsInChild in ((Component)component).GetComponentsInChildren<LocalizeStringEvent>(true))
			{
				Object.DestroyImmediate((Object)(object)componentsInChild);
			}
			foreach (LocalizedLinkText componentsInChild2 in ((Component)component).GetComponentsInChildren<LocalizedLinkText>(true))
			{
				Object.DestroyImmediate((Object)(object)componentsInChild2);
			}
			Plugin.Trace($"Save button added next to '{((Object)val).name}' under '{((Object)((Component)val).transform.parent).name}'.");
			return component;
		}

		private static void SetLabel(Button button, string text)
		{
			LinkTextGui component = ((Component)button).GetComponent<LinkTextGui>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetText(text);
			}
			TMP_Text componentInChildren = ((Component)button).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.text = text;
			}
		}

		private static void OpenSaveScreen(PauseMenu menu)
		{
			Plugin.Trace("Save button clicked.");
			if (BoltNetwork.isClient)
			{
				SaveGuestCharacter();
				return;
			}
			menu.SetSaveMenuActive(true);
			Plugin.Trace("Save screen opened from pause menu.");
		}

		private static void SaveGuestCharacter()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			bool flag = default(bool);
			try
			{
				CoopSaveUtils.SavePlayerData();
				AutoSaver.Instance?.Show(Plugin.Text(Plugin.CharacterSavedMessage, "CharacterSaved"));
				ManualLogSource logger = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(51, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Multiplayer guest: character saved (player down: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(AutoSaver.IsDown());
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(").");
				}
				logger.LogInfo(val);
			}
			catch (Exception ex)
			{
				AutoSaver.Instance?.Show(Plugin.Text(Plugin.FailedMessage, "Failed"));
				ManualLogSource logger2 = Plugin.Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(23, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Character save failed: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				logger2.LogError(val2);
			}
		}
	}
}
namespace PauseMenuSave.Logic
{
	public static class Texts
	{
		public const string Save = "Save";

		public const string Saved = "Saved";

		public const string NoSlot = "NoSlot";

		public const string Failed = "Failed";

		public const string CharacterSaved = "CharacterSaved";

		private static readonly Dictionary<string, Dictionary<string, string>> ByLanguage = new Dictionary<string, Dictionary<string, string>>
		{
			["en"] = new Dictionary<string, string>
			{
				["Save"] = "Save",
				["Saved"] = "Game saved",
				["NoSlot"] = "Auto-save skipped: save this game once first",
				["Failed"] = "Save failed (see BepInEx log)",
				["CharacterSaved"] = "Character saved"
			},
			["fr"] = new Dictionary<string, string>
			{
				["Save"] = "Sauvegarder",
				["Saved"] = "Partie sauvegardée",
				["NoSlot"] = "Sauvegarde auto ignorée : sauvegardez d'abord cette partie une fois",
				["Failed"] = "Échec de la sauvegarde (voir le journal BepInEx)",
				["CharacterSaved"] = "Personnage sauvegardé"
			}
		};

		public static string Get(string localeCode, string key)
		{
			string key2 = (localeCode ?? "en").Split('-')[0].ToLowerInvariant();
			if (!ByLanguage.TryGetValue(key2, out var value) || !value.TryGetValue(key, out var value2))
			{
				return ByLanguage["en"][key];
			}
			return value2;
		}

		public static void SelfCheck()
		{
			foreach (string key in ByLanguage["fr"].Keys)
			{
				if (!ByLanguage["en"].ContainsKey(key))
				{
					throw new InvalidOperationException("Texts: '" + key + "' missing in English.");
				}
			}
			if (Get("fr", "CharacterSaved") != "Personnage sauvegardé" || Get("ja", "Save") != "Save" || Get(null, "Save") != "Save")
			{
				throw new InvalidOperationException("Texts lookup is wrong.");
			}
		}
	}
}