Decompiled source of SaveMaster v1.1.3

BepInEx\plugins\MechGaming-SaveMaster\SaveMaster.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MechGaming")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.3.0")]
[assembly: AssemblyInformationalVersion("1.1.3")]
[assembly: AssemblyProduct("SaveMaster")]
[assembly: AssemblyTitle("SaveMaster")]
[assembly: AssemblyVersion("1.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MechGaming.REPO.SaveMaster
{
	internal static class HotkeyInput
	{
		private const int KeyDownMask = 32768;

		private const float TextInputScanInterval = 0.15f;

		private static readonly Dictionary<KeyCode, bool> PreviousStates = new Dictionary<KeyCode, bool>();

		private static readonly Type? UnityInputFieldType = Type.GetType("UnityEngine.UI.InputField, UnityEngine.UI");

		private static readonly Type? TmpInputFieldType = Type.GetType("TMPro.TMP_InputField, Unity.TextMeshPro");

		private static int _currentProcessId;

		private static float _nextTextInputScanTime;

		private static bool _cachedNoTextInputActive = true;

		private static int CurrentProcessId
		{
			get
			{
				if (_currentProcessId == 0)
				{
					_currentProcessId = Process.GetCurrentProcess().Id;
				}
				return _currentProcessId;
			}
		}

		internal static bool GetKeyDown(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			bool flag = GetUnityKeyDown(key) || GetWindowsKey(key);
			bool value;
			bool flag2 = PreviousStates.TryGetValue(key, out value) && value;
			PreviousStates[key] = flag;
			if (flag)
			{
				return !flag2;
			}
			return false;
		}

		internal static bool NoTextInputActive(Func<bool> gameCheck)
		{
			try
			{
				if (gameCheck())
				{
					_cachedNoTextInputActive = true;
					return true;
				}
			}
			catch
			{
			}
			if (Time.unscaledTime < _nextTextInputScanTime)
			{
				return _cachedNoTextInputActive;
			}
			_nextTextInputScanTime = Time.unscaledTime + 0.15f;
			_cachedNoTextInputActive = !AnyFocusedTextInput();
			return _cachedNoTextInputActive;
		}

		private static bool GetUnityKeyDown(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return Input.GetKeyDown(key);
			}
			catch
			{
				return false;
			}
		}

		private static bool GetWindowsKey(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			int num = ToVirtualKey(key);
			if (num != 0 && IsForegroundProcess())
			{
				return IsVirtualKeyDown(num);
			}
			return false;
		}

		private static bool IsVirtualKeyDown(int virtualKey)
		{
			try
			{
				return (GetAsyncKeyState(virtualKey) & 0x8000) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsForegroundProcess()
		{
			try
			{
				IntPtr foregroundWindow = GetForegroundWindow();
				if (foregroundWindow == IntPtr.Zero)
				{
					return false;
				}
				GetWindowThreadProcessId(foregroundWindow, out var processId);
				return processId == CurrentProcessId;
			}
			catch
			{
				return false;
			}
		}

		private static int ToVirtualKey(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_0012: Expected I4, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected I4, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected I4, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Invalid comparison between Unknown and I4
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Invalid comparison between Unknown and I4
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected I4, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Invalid comparison between Unknown and I4
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected I4, but got Unknown
			if ((int)key >= 97 && (int)key <= 122)
			{
				return 65 + (key - 97);
			}
			if ((int)key >= 48 && (int)key <= 57)
			{
				return 48 + (key - 48);
			}
			if ((int)key >= 282 && (int)key <= 678)
			{
				return 112 + (key - 282);
			}
			if ((int)key <= 27)
			{
				if ((int)key <= 9)
				{
					if ((int)key == 8)
					{
						return 8;
					}
					if ((int)key == 9)
					{
						return 9;
					}
				}
				else
				{
					if ((int)key == 13)
					{
						return 13;
					}
					if ((int)key == 27)
					{
						return 27;
					}
				}
			}
			else if ((int)key <= 127)
			{
				if ((int)key == 32)
				{
					return 32;
				}
				if ((int)key == 127)
				{
					return 46;
				}
			}
			else
			{
				switch (key - 273)
				{
				case 7:
					return 33;
				case 8:
					return 34;
				case 6:
					return 35;
				case 5:
					return 36;
				case 3:
					return 37;
				case 0:
					return 38;
				case 2:
					return 39;
				case 1:
					return 40;
				case 4:
					return 45;
				}
				switch (key - 303)
				{
				case 1:
					return 160;
				case 0:
					return 161;
				case 3:
					return 162;
				case 2:
					return 163;
				case 5:
					return 164;
				case 4:
					return 165;
				}
				switch (key - 323)
				{
				case 0:
					return 1;
				case 1:
					return 2;
				case 2:
					return 4;
				}
			}
			return 0;
		}

		private static bool AnyFocusedTextInput()
		{
			if (!TypeHasFocusedInput(UnityInputFieldType))
			{
				return TypeHasFocusedInput(TmpInputFieldType);
			}
			return true;
		}

		private static bool TypeHasFocusedInput(Type? inputType)
		{
			if (inputType == null)
			{
				return false;
			}
			try
			{
				Object[] array = Resources.FindObjectsOfTypeAll(inputType);
				for (int i = 0; i < array.Length; i++)
				{
					if (IsFocusedInput(array[i]))
					{
						return true;
					}
				}
			}
			catch
			{
				return false;
			}
			return false;
		}

		private static bool IsFocusedInput(Object input)
		{
			Component val = (Component)(object)((input is Component) ? input : null);
			if (val == null || !val.gameObject.activeInHierarchy)
			{
				return false;
			}
			Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null);
			if (val2 != null && !val2.enabled)
			{
				return false;
			}
			PropertyInfo property = ((object)input).GetType().GetProperty("isFocused", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.PropertyType == typeof(bool))
			{
				try
				{
					return (bool)property.GetValue(input, null);
				}
				catch
				{
					return false;
				}
			}
			return false;
		}

		[DllImport("user32.dll")]
		private static extern short GetAsyncKeyState(int virtualKey);

		[DllImport("user32.dll")]
		private static extern IntPtr GetForegroundWindow();

		[DllImport("user32.dll")]
		private static extern uint GetWindowThreadProcessId(IntPtr windowHandle, out uint processId);
	}
	[BepInPlugin("MechGaming.REPO.SaveMaster", "SaveMaster", "1.1.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[CompilerGenerated]
		private sealed class <LoadSaveRoutine>d__65 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Plugin <>4__this;

			public SaveEntry entry;

			public RestorePoint restorePoint;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LoadSaveRoutine>d__65(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Expected O, but got Unknown
				int num = <>1__state;
				Plugin plugin = <>4__this;
				switch (num)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					if (plugin._operationRunning)
					{
						plugin.PublishStatus("Save operation already running.");
						return false;
					}
					if (!plugin.CanManageSaves())
					{
						plugin.PublishStatus("Save loading is host/singleplayer only.");
						return false;
					}
					plugin._operationRunning = true;
					plugin.PublishStatus("Loading " + entry.FileName + "...");
					if (!plugin.PrepareRestorePoint(entry, restorePoint))
					{
						plugin._operationRunning = false;
						return false;
					}
					List<string> backups = GetBackups(entry.FileName);
					ClearRunFailureFlags();
					if (IsMultiplayer())
					{
						SemiFunc.MenuActionHostGame(entry.FileName, backups);
					}
					else
					{
						SemiFunc.MenuActionSingleplayerGame(entry.FileName, backups);
					}
					<>2__current = (object)new WaitForSecondsRealtime(1.2f);
					<>1__state = 1;
					return true;
				}
				case 1:
					<>1__state = -1;
					plugin.TryReloadInMemory(entry.FileName);
					plugin._operationRunning = false;
					plugin.SetMenuOpen(open: false);
					plugin.PublishStatus("Loaded " + entry.FileName + ".");
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		public const string PluginGuid = "MechGaming.REPO.SaveMaster";

		public const string PluginName = "SaveMaster";

		public const string PluginVersion = "1.1.3";

		private const int WindowWidth = 840;

		private const int WindowHeight = 590;

		private const float RefreshCooldown = 0.5f;

		private static readonly FieldInfo SaveFileCurrentField = AccessTools.Field(typeof(StatsManager), "saveFileCurrent");

		private static readonly FieldInfo TeamNameField = AccessTools.Field(typeof(StatsManager), "teamName");

		private static readonly FieldInfo TeamNameChangedField = AccessTools.Field(typeof(StatsManager), "teamNameChanged");

		private static readonly FieldInfo Es3PasswordField = AccessTools.Field(typeof(StatsManager), "totallyNormalString");

		private static readonly FieldInfo MenuPageSavesMaxSaveFilesField = AccessTools.Field(typeof(MenuPageSaves), "maxSaveFiles");

		private static readonly FieldInfo RunManagerAllPlayersDeadField = AccessTools.Field(typeof(RunManager), "allPlayersDead");

		private static readonly FieldInfo RunManagerLevelFailedField = AccessTools.Field(typeof(RunManager), "levelFailed");

		private static readonly FieldInfo RunManagerGameOverField = AccessTools.Field(typeof(RunManager), "gameOver");

		private readonly List<SaveEntry> _saveEntries = new List<SaveEntry>();

		private Harmony? _harmony;

		private ConfigEntry<KeyCode> _menuKey;

		private ConfigEntry<KeyCode> _quickLoadKey;

		private ConfigEntry<bool> _enableQuickLoad;

		private ConfigEntry<bool> _autoLoadCheckpointOnDeath;

		private ConfigEntry<bool> _preventGameDeletes;

		private ConfigEntry<bool> _allowManagerDeletes;

		private Rect _windowRect = new Rect(40f, 40f, 840f, 590f);

		private Vector2 _scrollPosition;

		private bool _menuOpen;

		private bool _loadingSaves;

		private bool _operationRunning;

		private bool _storedCursorVisible;

		private CursorLockMode _storedCursorLockMode;

		private float _lastRefreshTime = -0.5f;

		private float _statusMessageUntil;

		private string _statusMessage = string.Empty;

		private DialogMode _dialogMode;

		private SaveEntry? _dialogSave;

		private RestorePoint _dialogRestorePoint;

		private string _newSaveName = string.Empty;

		private string _renameSaveName = string.Empty;

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

		internal static bool ManagedDeleteInProgress { get; private set; }

		private static string SavesRoot => Path.Combine(Application.persistentDataPath, "saves");

		private void Awake()
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			Instance = this;
			_menuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ManagerKey", (KeyCode)288, "Key used to open the SaveMaster manager.");
			_quickLoadKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "QuickLoadKey", (KeyCode)290, "Key used to load the current save without opening the manager.");
			_enableQuickLoad = ((BaseUnityPlugin)this).Config.Bind<bool>("Controls", "EnableQuickLoad", true, "Allow the quick-load key to reload the current save.");
			_autoLoadCheckpointOnDeath = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "AutoLoadCheckpointOnDeath", true, "Automatically load the current checkpoint instead of advancing the failure flow when all players die.");
			_preventGameDeletes = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "PreventGameDeletes", true, "Block R.E.P.O. from deleting saves after death or through vanilla flows.");
			_allowManagerDeletes = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "AllowManagerDeletes", true, "Allow saves to be deleted from the SaveMaster manager after confirmation.");
			_harmony = new Harmony("MechGaming.REPO.SaveMaster");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"SaveMaster 1.1.3 loaded.");
		}

		private void Update()
		{
			//IL_0006: 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)
			if (HotkeyInput.GetKeyDown(_menuKey.Value))
			{
				SetMenuOpen(!_menuOpen);
			}
			else if (_menuOpen)
			{
				if (HotkeyInput.GetKeyDown((KeyCode)27))
				{
					if (_dialogMode != 0)
					{
						CloseDialog();
					}
					else
					{
						SetMenuOpen(open: false);
					}
				}
			}
			else if (_enableQuickLoad.Value && HotkeyInput.GetKeyDown(_quickLoadKey.Value) && NoTextInputActive())
			{
				QuickLoadCurrentSave();
			}
		}

		private void LateUpdate()
		{
			if (_menuOpen)
			{
				Cursor.visible = true;
				Cursor.lockState = (CursorLockMode)0;
			}
		}

		private void OnDestroy()
		{
			SetMenuOpen(open: false);
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		private void OnGUI()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			DrawStatusMessage();
			if (_menuOpen)
			{
				_windowRect = ClampWindow(GUI.Window(((Object)this).GetInstanceID(), _windowRect, new WindowFunction(DrawManagerWindow), "SaveMaster"));
			}
			DrawDialog();
		}

		private void SetMenuOpen(bool open)
		{
			//IL_0079: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			if (_menuOpen != open)
			{
				_menuOpen = open;
				CloseDialog();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("SaveMaster manager " + (open ? "opened" : "closed") + "."));
				if (open)
				{
					_storedCursorVisible = Cursor.visible;
					_storedCursorLockMode = Cursor.lockState;
					Cursor.visible = true;
					Cursor.lockState = (CursorLockMode)0;
					RefreshSaves(force: true);
				}
				else
				{
					Cursor.visible = _storedCursorVisible;
					Cursor.lockState = _storedCursorLockMode;
				}
			}
		}

		private void DrawManagerWindow(int windowId)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			EnsureStyles();
			GUILayout.Space(6f);
			DrawCurrentSaveHeader();
			if (!CanManageSaves())
			{
				DrawWarning("Save operations are host/singleplayer only.");
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("New Checkpoint Save", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
			{
				_newSaveName = BuildDefaultTeamName();
				_dialogMode = DialogMode.NewSave;
			}
			if (GUILayout.Button("Refresh", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }))
			{
				RefreshSaves(force: true);
			}
			GUILayout.FlexibleSpace();
			if (GUILayout.Button($"Close [{_menuKey.Value} / ESC]", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(190f),
				GUILayout.Height(30f)
			}))
			{
				SetMenuOpen(open: false);
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(8f);
			if (_loadingSaves)
			{
				GUILayout.Label("Loading saves...", SaveMasterStyles.CenterLabel, Array.Empty<GUILayoutOption>());
			}
			else if (_saveEntries.Count == 0)
			{
				GUILayout.Label("No save files found.", SaveMasterStyles.CenterLabel, Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label($"Available Saves ({_saveEntries.Count})", SaveMasterStyles.Subtitle, Array.Empty<GUILayoutOption>());
				_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(445f) });
				foreach (SaveEntry saveEntry in _saveEntries)
				{
					DrawSaveEntry(saveEntry);
				}
				GUILayout.EndScrollView();
			}
			if (!string.IsNullOrWhiteSpace(_statusMessage) && Time.unscaledTime < _statusMessageUntil)
			{
				GUILayout.Space(4f);
				GUILayout.Label(_statusMessage, SaveMasterStyles.StatusLabel, Array.Empty<GUILayoutOption>());
			}
			GUI.DragWindow(new Rect(0f, 0f, 840f, 24f));
		}

		private void DrawCurrentSaveHeader()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			string currentSaveDisplayName = GetCurrentSaveDisplayName();
			string text = (string.IsNullOrWhiteSpace(currentSaveDisplayName) ? "Current Save: none" : ("Current Save: " + currentSaveDisplayName));
			Rect rect = GUILayoutUtility.GetRect(818f, 36f);
			Color color = GUI.color;
			GUI.color = new Color(0.05f, 0.32f, 0.08f, 0.86f);
			GUI.Box(rect, GUIContent.none);
			GUI.color = color;
			GUI.Label(new Rect(((Rect)(ref rect)).x + 12f, ((Rect)(ref rect)).y + 8f, ((Rect)(ref rect)).width - 24f, 22f), text, SaveMasterStyles.HeaderLabel);
		}

		private void DrawWarning(string message)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = GUILayoutUtility.GetRect(818f, 30f);
			Color color = GUI.color;
			GUI.color = new Color(0.42f, 0.18f, 0.02f, 0.86f);
			GUI.Box(rect, GUIContent.none);
			GUI.color = color;
			GUI.Label(new Rect(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 6f, ((Rect)(ref rect)).width - 20f, 20f), message, SaveMasterStyles.HeaderLabel);
		}

		private void DrawSaveEntry(SaveEntry entry)
		{
			SaveEntry entry2 = entry;
			GUILayout.BeginVertical((GUIStyle)(string.Equals(entry2.FileName, GetCurrentSaveName(), StringComparison.Ordinal) ? ((object)SaveMasterStyles.CurrentSaveBox) : ((object)GUI.skin.box)), Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(545f) });
			GUILayout.Label(entry2.DisplayName, SaveMasterStyles.SaveTitle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(entry2.FileName + "  |  " + entry2.DateText, SaveMasterStyles.SmallLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Space(4f);
			GUILayout.Label("Level: " + entry2.LevelText + "     Money: " + entry2.CurrencyText + "     Time: " + FormatTime(entry2.TimePlayedSeconds), SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Label($"Total Haul: {entry2.TotalHaulText}     Backups: {entry2.BackupCount}", SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Players: " + entry2.PlayerNamesText, SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			if (!entry2.IsValid)
			{
				GUILayout.Label("Main save file is missing or unreadable; backups may still exist.", SaveMasterStyles.WarningLabel, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndVertical();
			GUILayout.FlexibleSpace();
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(245f) });
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawEntryButton("Load", delegate
			{
				ConfirmLoad(entry2, RestorePoint.Current);
			}, null, entry2.IsValid);
			DrawEntryButton("Prev", delegate
			{
				ConfirmLoad(entry2, RestorePoint.LatestBackup);
			}, null, entry2.BackupCount > 0);
			DrawEntryButton("Start", delegate
			{
				ConfirmLoad(entry2, RestorePoint.OldestBackup);
			}, null, entry2.BackupCount > 0);
			GUILayout.EndHorizontal();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			DrawEntryButton("Clone", delegate
			{
				CloneExistingSave(entry2);
			});
			DrawEntryButton("Rename", delegate
			{
				_dialogSave = entry2;
				_renameSaveName = entry2.DisplayName;
				_dialogMode = DialogMode.Rename;
			});
			DrawEntryButton("Delete", delegate
			{
				_dialogSave = entry2;
				_dialogMode = DialogMode.Delete;
			}, SaveMasterStyles.DeleteButton);
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
		}

		private static void DrawEntryButton(string label, Action onClick, GUIStyle? style = null, bool enabled = true)
		{
			bool enabled2 = GUI.enabled;
			GUI.enabled = enabled2 && enabled;
			if (GUILayout.Button(label, style ?? GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }))
			{
				onClick();
			}
			GUI.enabled = enabled2;
		}

		private void DrawDialog()
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (_menuOpen && _dialogMode != 0)
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((float)Screen.width - 470f) / 2f, ((float)Screen.height - 230f) / 2f, 470f, 230f);
				string text = _dialogMode switch
				{
					DialogMode.NewSave => "New Save", 
					DialogMode.Rename => "Rename Save", 
					DialogMode.Delete => "Delete Save", 
					DialogMode.Load => "Load Save", 
					_ => "SaveMaster", 
				};
				GUI.Window(((Object)this).GetInstanceID() + 7141, val, new WindowFunction(DrawDialogWindow), text);
			}
		}

		private void DrawDialogWindow(int windowId)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			EnsureStyles();
			GUILayout.Space(12f);
			switch (_dialogMode)
			{
			case DialogMode.NewSave:
				DrawNewSaveDialog();
				break;
			case DialogMode.Rename:
				DrawRenameDialog();
				break;
			case DialogMode.Delete:
				DrawDeleteDialog();
				break;
			case DialogMode.Load:
				DrawLoadDialog();
				break;
			}
			GUI.DragWindow(new Rect(0f, 0f, 470f, 24f));
		}

		private void DrawNewSaveDialog()
		{
			GUILayout.Label("Create a new save from the current checkpoint.", SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("Save name", SaveMasterStyles.SmallLabel, Array.Empty<GUILayoutOption>());
			GUI.SetNextControlName("SaveMasterNewSaveName");
			_newSaveName = GUILayout.TextField(_newSaveName, 40, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) });
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Create", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				CreateSnapshotFromCurrent(_newSaveName);
				CloseDialog();
			}
			if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				CloseDialog();
			}
			GUILayout.EndHorizontal();
			GUI.FocusControl("SaveMasterNewSaveName");
		}

		private void DrawRenameDialog()
		{
			if (_dialogSave == null)
			{
				CloseDialog();
				return;
			}
			GUILayout.Label("Renaming: " + _dialogSave.FileName, SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label("New display name", SaveMasterStyles.SmallLabel, Array.Empty<GUILayoutOption>());
			GUI.SetNextControlName("SaveMasterRenameName");
			_renameSaveName = GUILayout.TextField(_renameSaveName, 40, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) });
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Rename", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				RenameSave(_dialogSave.FileName, _renameSaveName);
				CloseDialog();
			}
			if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				CloseDialog();
			}
			GUILayout.EndHorizontal();
			GUI.FocusControl("SaveMasterRenameName");
		}

		private void DrawDeleteDialog()
		{
			if (_dialogSave == null)
			{
				CloseDialog();
				return;
			}
			GUILayout.Label("Delete this save permanently?", SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label(_dialogSave.FileName, SaveMasterStyles.SaveTitle, Array.Empty<GUILayoutOption>());
			GUILayout.Label("This removes the whole save folder, including backups.", SaveMasterStyles.WarningLabel, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Delete Forever", SaveMasterStyles.DeleteButton, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				DeleteSave(_dialogSave.FileName);
				CloseDialog();
			}
			if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				CloseDialog();
			}
			GUILayout.EndHorizontal();
		}

		private void DrawLoadDialog()
		{
			if (_dialogSave == null)
			{
				CloseDialog();
				return;
			}
			GUILayout.Label("Load this save now?", SaveMasterStyles.RowLabel, Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.Label(_dialogSave.FileName, SaveMasterStyles.SaveTitle, Array.Empty<GUILayoutOption>());
			GUILayout.Label(GetRestorePointDescription(_dialogRestorePoint), SaveMasterStyles.WarningLabel, Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Load", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				((MonoBehaviour)this).StartCoroutine(LoadSaveRoutine(_dialogSave, _dialogRestorePoint));
				CloseDialog();
			}
			if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(36f) }))
			{
				CloseDialog();
			}
			GUILayout.EndHorizontal();
		}

		private void ConfirmLoad(SaveEntry entry, RestorePoint restorePoint)
		{
			_dialogSave = entry;
			_dialogRestorePoint = restorePoint;
			_dialogMode = DialogMode.Load;
		}

		private void CloseDialog()
		{
			_dialogMode = DialogMode.None;
			_dialogSave = null;
			_dialogRestorePoint = RestorePoint.Current;
		}

		private void QuickLoadCurrentSave()
		{
			if (_operationRunning)
			{
				PublishStatus("Save operation already running.");
				return;
			}
			string currentSave = GetCurrentSaveName();
			if (string.IsNullOrWhiteSpace(currentSave))
			{
				PublishStatus("No current save to load.");
				return;
			}
			SaveEntry saveEntry = _saveEntries.FirstOrDefault((SaveEntry save) => save.FileName == currentSave) ?? BuildEntryForSave(currentSave);
			if (!saveEntry.IsValid)
			{
				PublishStatus("Current save is not valid: " + currentSave);
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(LoadSaveRoutine(saveEntry, RestorePoint.Current));
			}
		}

		[IteratorStateMachine(typeof(<LoadSaveRoutine>d__65))]
		private IEnumerator LoadSaveRoutine(SaveEntry entry, RestorePoint restorePoint)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <LoadSaveRoutine>d__65(0)
			{
				<>4__this = this,
				entry = entry,
				restorePoint = restorePoint
			};
		}

		private bool PrepareRestorePoint(SaveEntry entry, RestorePoint restorePoint)
		{
			object obj;
			switch (restorePoint)
			{
			case RestorePoint.Current:
				return true;
			default:
				obj = entry.Backups.LastOrDefault() ?? string.Empty;
				break;
			case RestorePoint.LatestBackup:
				obj = entry.Backups.FirstOrDefault() ?? string.Empty;
				break;
			}
			string text = (string)obj;
			if (string.IsNullOrWhiteSpace(text))
			{
				PublishStatus("No backup exists for that restore point.");
				return false;
			}
			try
			{
				RestoreBackupToMain(entry.FileName, text);
				PublishStatus("Restored " + text + " before loading.");
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not restore backup {text}: {ex}");
				PublishStatus("Could not restore " + text + ": " + ex.Message);
				return false;
			}
		}

		private void RefreshSaves(bool force = false)
		{
			if (!_loadingSaves && !((Object)(object)StatsManager.instance == (Object)null) && (force || !(Time.unscaledTime - _lastRefreshTime < 0.5f)))
			{
				_lastRefreshTime = Time.unscaledTime;
				RefreshSavesAsync();
			}
		}

		private async void RefreshSavesAsync()
		{
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			_loadingSaves = true;
			try
			{
				List<SaveFolder> obj = await instance.SaveFileGetAllAsync();
				_saveEntries.Clear();
				foreach (SaveFolder item in obj)
				{
					_saveEntries.Add(BuildEntry(item));
				}
				((BaseUnityPlugin)this).Logger.LogDebug((object)$"Loaded {_saveEntries.Count} save entries.");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not refresh saves: {ex}");
				PublishStatus("Could not refresh saves: " + ex.Message);
			}
			finally
			{
				_loadingSaves = false;
			}
		}

		private SaveEntry BuildEntry(SaveFolder folder)
		{
			SaveEntry saveEntry = new SaveEntry(folder.name)
			{
				Backups = ((folder.backups != null) ? new List<string>(folder.backups) : new List<string>()),
				IsValid = folder.isValid
			};
			FillEntryDetails(saveEntry);
			return saveEntry;
		}

		private SaveEntry BuildEntryForSave(string fileName)
		{
			SaveEntry saveEntry = new SaveEntry(fileName)
			{
				IsValid = File.Exists(GetMainSavePath(fileName)),
				Backups = GetBackups(fileName)
			};
			FillEntryDetails(saveEntry);
			return saveEntry;
		}

		private void FillEntryDetails(SaveEntry entry)
		{
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				entry.DisplayName = FirstNonEmpty(instance.SaveFileGetTeamName(entry.FileName, (string)null), entry.FileName);
				entry.DateText = FirstNonEmpty(instance.SaveFileGetDateAndTime(entry.FileName, (string)null), "Unknown date");
				entry.LevelText = FirstNonEmpty(instance.SaveFileGetRunLevel(entry.FileName, (string)null), "0");
				entry.CurrencyText = FormatMoney(instance.SaveFileGetRunCurrency(entry.FileName, (string)null));
				entry.TotalHaulText = FormatMoney(instance.SaveFileGetTotalHaul(entry.FileName, (string)null));
				entry.TimePlayedSeconds = instance.SaveFileGetTimePlayed(entry.FileName, (string)null);
				entry.PlayerNames = instance.SaveFileGetPlayerNames(entry.FileName, (string)null) ?? new List<string>();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Could not read save details for " + entry.FileName + ": " + ex.Message));
				entry.DisplayName = entry.FileName + " (unreadable)";
				entry.DateText = "Unknown date";
				entry.LevelText = "?";
				entry.CurrencyText = "?";
				entry.TotalHaulText = "?";
				entry.PlayerNames = new List<string>();
			}
		}

		private void CreateSnapshotFromCurrent(string displayName)
		{
			if (!CanManageSaves())
			{
				PublishStatus("Save creation is host/singleplayer only.");
				return;
			}
			if (_operationRunning)
			{
				PublishStatus("Save operation already running.");
				return;
			}
			string displayName2 = SanitizeDisplayName(displayName);
			string text = CreateUniqueSaveFileName();
			string currentSaveName = GetCurrentSaveName();
			try
			{
				if (!string.IsNullOrWhiteSpace(currentSaveName) && Directory.Exists(GetSaveDirectory(currentSaveName)))
				{
					CopySaveDirectory(currentSaveName, text);
				}
				else
				{
					StatsManager.instance.SaveGame(text);
				}
				RenameStoredSave(text, displayName2, updateAllBackups: true);
				RefreshSaves(force: true);
				PublishStatus("Created save " + text + ".");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not create save snapshot: {ex}");
				PublishStatus("Could not create save: " + ex.Message);
			}
		}

		private void CloneExistingSave(SaveEntry entry)
		{
			if (!CanManageSaves())
			{
				PublishStatus("Save cloning is host/singleplayer only.");
				return;
			}
			string text = CreateUniqueSaveFileName();
			try
			{
				CopySaveDirectory(entry.FileName, text);
				RenameStoredSave(text, entry.DisplayName + " Copy", updateAllBackups: true);
				RefreshSaves(force: true);
				PublishStatus("Cloned " + entry.FileName + ".");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not clone save {entry.FileName}: {ex}");
				PublishStatus("Could not clone save: " + ex.Message);
			}
		}

		private void RenameSave(string fileName, string displayName)
		{
			if (!CanManageSaves())
			{
				PublishStatus("Save renaming is host/singleplayer only.");
				return;
			}
			string text = SanitizeDisplayName(displayName);
			try
			{
				RenameStoredSave(fileName, text, updateAllBackups: true);
				if (string.Equals(GetCurrentSaveName(), fileName, StringComparison.Ordinal))
				{
					TeamNameField?.SetValue(StatsManager.instance, text);
					TeamNameChangedField?.SetValue(StatsManager.instance, !IsDefaultTeamName(text));
				}
				RefreshSaves(force: true);
				PublishStatus("Renamed " + fileName + ".");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not rename save {fileName}: {ex}");
				PublishStatus("Could not rename save: " + ex.Message);
			}
		}

		private void DeleteSave(string fileName)
		{
			if (!CanManageSaves())
			{
				PublishStatus("Save deletion is host/singleplayer only.");
				return;
			}
			if (!_allowManagerDeletes.Value)
			{
				PublishStatus("Manager deletes are disabled in config.");
				return;
			}
			try
			{
				ManagedDeleteInProgress = true;
				StatsManager.instance.SaveFileDelete(fileName);
				RefreshSaves(force: true);
				PublishStatus("Deleted " + fileName + ".");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not delete save {fileName}: {ex}");
				PublishStatus("Could not delete save: " + ex.Message);
			}
			finally
			{
				ManagedDeleteInProgress = false;
			}
		}

		internal bool ShouldBlockVanillaDelete(string saveFileName)
		{
			if (!_preventGameDeletes.Value || ManagedDeleteInProgress)
			{
				return false;
			}
			PublishStatus("Blocked game delete: " + saveFileName);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Blocked vanilla save deletion for " + saveFileName + "."));
			RefreshSaves(force: true);
			return true;
		}

		internal bool TryAutoLoadCheckpointOnLevelFailure(ChangeLevelType changeLevelType)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (!_autoLoadCheckpointOnDeath.Value || _operationRunning)
			{
				return false;
			}
			if ((int)changeLevelType != 0 || !CanManageSaves())
			{
				return false;
			}
			if (SafeGameCheck((Func<bool>)SemiFunc.RunIsTutorial) || SafeGameCheck((Func<bool>)SemiFunc.RunIsArena) || SafeGameCheck((Func<bool>)SemiFunc.MenuLevel))
			{
				return false;
			}
			string currentSaveName = GetCurrentSaveName();
			if (string.IsNullOrWhiteSpace(currentSaveName))
			{
				return false;
			}
			SaveEntry saveEntry = BuildEntryForSave(currentSaveName);
			RestorePoint restorePoint = ((!saveEntry.IsValid) ? RestorePoint.LatestBackup : RestorePoint.Current);
			if (!saveEntry.IsValid && saveEntry.BackupCount == 0)
			{
				return false;
			}
			PublishStatus("Death detected; loading checkpoint " + currentSaveName + ".");
			((MonoBehaviour)this).StartCoroutine(LoadSaveRoutine(saveEntry, restorePoint));
			return true;
		}

		internal void LogInfo(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		internal static void EnsureVanillaSaveMenuCanCreateSaves(MenuPageSaves? menuPageSaves)
		{
			if ((Object)(object)menuPageSaves == (Object)null)
			{
				return;
			}
			try
			{
				MenuPageSavesMaxSaveFilesField?.SetValue(menuPageSaves, 0);
				Plugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogDebug((object)"Disabled vanilla save slot cap for the save menu.");
				}
			}
			catch (Exception ex)
			{
				Plugin? instance2 = Instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogDebug((object)("Could not adjust save slot cap: " + ex.Message));
				}
			}
		}

		private static void CopySaveDirectory(string sourceSaveName, string destinationSaveName)
		{
			string saveDirectory = GetSaveDirectory(sourceSaveName);
			string saveDirectory2 = GetSaveDirectory(destinationSaveName);
			if (!Directory.Exists(saveDirectory))
			{
				throw new DirectoryNotFoundException(saveDirectory);
			}
			if (Directory.Exists(saveDirectory2))
			{
				throw new IOException("Destination save already exists: " + destinationSaveName);
			}
			Directory.CreateDirectory(saveDirectory2);
			string[] directories = Directory.GetDirectories(saveDirectory, "*", SearchOption.AllDirectories);
			for (int i = 0; i < directories.Length; i++)
			{
				string path = directories[i].Substring(saveDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				Directory.CreateDirectory(Path.Combine(saveDirectory2, path));
			}
			directories = Directory.GetFiles(saveDirectory, "*", SearchOption.AllDirectories);
			foreach (string text in directories)
			{
				string path2 = text.Substring(saveDirectory.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				string path3 = Path.GetDirectoryName(path2) ?? string.Empty;
				string path4 = RenameSaveFile(Path.GetFileName(path2), sourceSaveName, destinationSaveName);
				string destFileName = Path.Combine(saveDirectory2, path3, path4);
				File.Copy(text, destFileName, overwrite: false);
			}
		}

		private static string RenameSaveFile(string fileName, string sourceSaveName, string destinationSaveName)
		{
			if (!fileName.EndsWith(".es3", StringComparison.OrdinalIgnoreCase) || !fileName.StartsWith(sourceSaveName, StringComparison.OrdinalIgnoreCase))
			{
				return fileName;
			}
			return destinationSaveName + fileName.Substring(sourceSaveName.Length);
		}

		private static void RestoreBackupToMain(string saveFileName, string backupName)
		{
			string mainSavePath = GetMainSavePath(saveFileName);
			string text = Path.Combine(GetSaveDirectory(saveFileName), backupName + ".es3");
			if (!File.Exists(text))
			{
				throw new FileNotFoundException("Backup file not found.", text);
			}
			if (File.Exists(mainSavePath))
			{
				string destFileName = Path.Combine(GetSaveDirectory(saveFileName), $"{saveFileName}_SAVEMASTER_{DateTime.Now:yyyyMMdd_HHmmss}.es3");
				File.Copy(mainSavePath, destFileName, overwrite: false);
			}
			File.Copy(text, mainSavePath, overwrite: true);
		}

		private static void RenameStoredSave(string saveFileName, string displayName, bool updateAllBackups)
		{
			string saveDirectory = GetSaveDirectory(saveFileName);
			if (!Directory.Exists(saveDirectory))
			{
				throw new DirectoryNotFoundException(saveDirectory);
			}
			List<string> list = new List<string> { GetMainSavePath(saveFileName) };
			if (updateAllBackups)
			{
				list.AddRange(Directory.GetFiles(saveDirectory, saveFileName + "_BACKUP*.es3", SearchOption.TopDirectoryOnly));
			}
			foreach (string item in list.Distinct())
			{
				if (File.Exists(item))
				{
					WriteTeamName(item, displayName);
				}
			}
		}

		private static void WriteTeamName(string savePath, string displayName)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			string savePassword = GetSavePassword();
			ES3Settings val = new ES3Settings(savePath, (EncryptionType)1, savePassword, (ES3Settings)null);
			ES3.Save<string>("teamName", displayName, val);
			ES3.Save<bool>("teamNameChanged", !IsDefaultTeamName(displayName), val);
		}

		private static List<string> GetBackups(string saveFileName)
		{
			try
			{
				return ((Object)(object)StatsManager.instance != (Object)null) ? StatsManager.instance.SaveFileGetBackups(saveFileName, SavesRoot) : new List<string>();
			}
			catch
			{
				return new List<string>();
			}
		}

		private void TryReloadInMemory(string saveFileName)
		{
			try
			{
				StatsManager.instance.LoadGame(saveFileName, (List<string>)null);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Could not reload save in memory: " + ex.Message));
			}
		}

		private static void ClearRunFailureFlags()
		{
			RunManager instance = RunManager.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				SetFieldValue(RunManagerAllPlayersDeadField, instance, value: false);
				SetFieldValue(RunManagerLevelFailedField, instance, value: false);
				SetFieldValue(RunManagerGameOverField, instance, value: false);
			}
		}

		private static string CreateUniqueSaveFileName(string suffixToken = "")
		{
			string text = $"REPO_SAVE_{DateTime.Now:yyyy_MM_dd_HH_mm_ss}{suffixToken}";
			string text2 = text;
			int num = 2;
			while (Directory.Exists(GetSaveDirectory(text2)))
			{
				text2 = $"{text}_{num}";
				num++;
			}
			return text2;
		}

		private string BuildDefaultTeamName()
		{
			string current = GetCurrentSaveName();
			SaveEntry saveEntry = _saveEntries.FirstOrDefault((SaveEntry save) => save.FileName == current);
			string currentSaveDisplayName = GetCurrentSaveDisplayName();
			string arg = ((saveEntry != null) ? saveEntry.DisplayName : FirstNonEmpty(currentSaveDisplayName, "SaveMaster"));
			return $"{arg} {DateTime.Now:HH:mm}";
		}

		private string GetCurrentSaveDisplayName()
		{
			string currentSave = GetCurrentSaveName();
			if (string.IsNullOrWhiteSpace(currentSave))
			{
				return string.Empty;
			}
			SaveEntry saveEntry = _saveEntries.FirstOrDefault((SaveEntry save) => string.Equals(save.FileName, currentSave, StringComparison.Ordinal));
			if (saveEntry != null && !string.IsNullOrWhiteSpace(saveEntry.DisplayName))
			{
				return saveEntry.DisplayName;
			}
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return currentSave;
			}
			try
			{
				return FirstNonEmpty(instance.SaveFileGetTeamName(currentSave, (string)null), currentSave);
			}
			catch
			{
				return currentSave;
			}
		}

		private static string SanitizeDisplayName(string displayName)
		{
			string text = (displayName ?? string.Empty).Trim();
			if (string.IsNullOrWhiteSpace(text))
			{
				return "R.E.P.O.";
			}
			if (text.Length > 40)
			{
				return text.Substring(0, 40);
			}
			return text;
		}

		private static string GetCurrentSaveName()
		{
			try
			{
				return (SaveFileCurrentField?.GetValue(StatsManager.instance) as string) ?? string.Empty;
			}
			catch
			{
				return string.Empty;
			}
		}

		private static string GetSavePassword()
		{
			try
			{
				return (Es3PasswordField?.GetValue(StatsManager.instance) as string) ?? string.Empty;
			}
			catch
			{
				return string.Empty;
			}
		}

		private static string GetSaveDirectory(string saveFileName)
		{
			return Path.Combine(SavesRoot, saveFileName);
		}

		private static string GetMainSavePath(string saveFileName)
		{
			return Path.Combine(GetSaveDirectory(saveFileName), saveFileName + ".es3");
		}

		private static string FirstNonEmpty(string? value, string fallback)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			return value;
		}

		private static string FormatMoney(string rawValue)
		{
			if (!int.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return FirstNonEmpty(rawValue, "0");
			}
			if (result >= 1000000)
			{
				return $"{(float)result / 1000000f:0.#}m";
			}
			if (result >= 1000)
			{
				return $"{(float)result / 1000f:0.#}k";
			}
			return result.ToString(CultureInfo.InvariantCulture);
		}

		private static string FormatTime(float seconds)
		{
			int num = Mathf.Max(0, Mathf.RoundToInt(seconds / 60f));
			int num2 = num / 60;
			int num3 = num % 60;
			if (num2 <= 0)
			{
				return $"{num3}m";
			}
			return $"{num2}h {num3}m";
		}

		private static string GetRestorePointDescription(RestorePoint restorePoint)
		{
			return restorePoint switch
			{
				RestorePoint.Current => "Loads the latest saved state.", 
				RestorePoint.LatestBackup => "Restores the newest backup first, then loads it. SaveMaster keeps the current file as a safety copy.", 
				RestorePoint.OldestBackup => "Restores the oldest backup first, useful for returning to the start of a saved map. SaveMaster keeps the current file as a safety copy.", 
				_ => string.Empty, 
			};
		}

		private bool CanManageSaves()
		{
			if ((Object)(object)StatsManager.instance != (Object)null)
			{
				return SafeGameCheck((Func<bool>)SemiFunc.IsMasterClientOrSingleplayer);
			}
			return false;
		}

		private static bool IsMultiplayer()
		{
			return SafeGameCheck((Func<bool>)SemiFunc.IsMultiplayer);
		}

		private static bool NoTextInputActive()
		{
			return HotkeyInput.NoTextInputActive((Func<bool>)SemiFunc.NoTextInputsActive);
		}

		private static bool SafeGameCheck(Func<bool> check)
		{
			try
			{
				return check();
			}
			catch
			{
				return false;
			}
		}

		private static T GetFieldValue<T>(FieldInfo? field, object? instance, T fallback)
		{
			if (field == null || instance == null)
			{
				return fallback;
			}
			try
			{
				return (T)((field.GetValue(instance) is T val) ? ((object)val) : ((object)fallback));
			}
			catch
			{
				return fallback;
			}
		}

		private static void SetFieldValue<T>(FieldInfo? field, object? instance, T value)
		{
			if (field == null || instance == null)
			{
				return;
			}
			try
			{
				field.SetValue(instance, value);
			}
			catch
			{
			}
		}

		private static bool IsDefaultTeamName(string displayName)
		{
			return string.Equals(displayName, "R.E.P.O.", StringComparison.Ordinal);
		}

		private void PublishStatus(string message)
		{
			_statusMessage = message;
			_statusMessageUntil = Time.unscaledTime + 4f;
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		private void DrawStatusMessage()
		{
			//IL_0057: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.unscaledTime >= _statusMessageUntil) && !string.IsNullOrWhiteSpace(_statusMessage))
			{
				Rect val = new Rect(Mathf.Max(12f, ((float)Screen.width - 560f) / 2f), 42f, Mathf.Min(560f, (float)Screen.width - 24f), 42f);
				Color color = GUI.color;
				GUI.color = new Color(0f, 0f, 0f, 0.72f);
				GUI.Box(val, GUIContent.none);
				GUI.color = color;
				GUI.Label(val, _statusMessage, SaveMasterStyles.ToastLabel);
			}
		}

		private static Rect ClampWindow(Rect rect)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			((Rect)(ref rect)).x = Mathf.Clamp(((Rect)(ref rect)).x, 8f, Mathf.Max(8f, (float)Screen.width - ((Rect)(ref rect)).width - 8f));
			((Rect)(ref rect)).y = Mathf.Clamp(((Rect)(ref rect)).y, 8f, Mathf.Max(8f, (float)Screen.height - ((Rect)(ref rect)).height - 8f));
			return rect;
		}

		private static void EnsureStyles()
		{
			SaveMasterStyles.Ensure();
		}
	}
	internal sealed class SaveEntry
	{
		internal string FileName { get; }

		internal string DisplayName { get; set; }

		internal string DateText { get; set; }

		internal string LevelText { get; set; }

		internal string CurrencyText { get; set; }

		internal string TotalHaulText { get; set; }

		internal float TimePlayedSeconds { get; set; }

		internal bool IsValid { get; set; }

		internal List<string> Backups { get; set; }

		internal List<string> PlayerNames { get; set; }

		internal int BackupCount => Backups.Count;

		internal string PlayerNamesText
		{
			get
			{
				if (PlayerNames.Count <= 0)
				{
					return "Unknown";
				}
				return string.Join(", ", PlayerNames);
			}
		}

		internal SaveEntry(string fileName)
		{
			FileName = fileName;
			DisplayName = fileName;
			DateText = "Unknown date";
			LevelText = "?";
			CurrencyText = "?";
			TotalHaulText = "?";
			Backups = new List<string>();
			PlayerNames = new List<string>();
		}
	}
	internal enum DialogMode
	{
		None,
		NewSave,
		Rename,
		Delete,
		Load
	}
	internal enum RestorePoint
	{
		Current,
		LatestBackup,
		OldestBackup
	}
	internal static class SaveMasterStyles
	{
		internal static GUIStyle HeaderLabel;

		internal static GUIStyle Subtitle;

		internal static GUIStyle SaveTitle;

		internal static GUIStyle RowLabel;

		internal static GUIStyle SmallLabel;

		internal static GUIStyle CenterLabel;

		internal static GUIStyle StatusLabel;

		internal static GUIStyle WarningLabel;

		internal static GUIStyle ToastLabel;

		internal static GUIStyle DeleteButton;

		internal static GUIStyle CurrentSaveBox;

		private static bool _initialized;

		internal static void Ensure()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0069: Expected O, but got Unknown
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_00be: 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)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Expected O, but got Unknown
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Expected O, but got Unknown
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Expected O, but got Unknown
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Expected O, but got Unknown
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			if (!_initialized)
			{
				HeaderLabel = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)3
				};
				HeaderLabel.normal.textColor = Color.white;
				Subtitle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14,
					fontStyle = (FontStyle)1
				};
				Subtitle.normal.textColor = new Color(0.94f, 0.96f, 1f, 1f);
				SaveTitle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
				SaveTitle.normal.textColor = Color.white;
				RowLabel = new GUIStyle(GUI.skin.label)
				{
					fontSize = 13,
					wordWrap = true
				};
				RowLabel.normal.textColor = new Color(0.9f, 0.92f, 0.94f, 1f);
				SmallLabel = new GUIStyle(GUI.skin.label)
				{
					fontSize = 11,
					wordWrap = true
				};
				SmallLabel.normal.textColor = new Color(0.72f, 0.76f, 0.8f, 1f);
				CenterLabel = new GUIStyle(RowLabel)
				{
					alignment = (TextAnchor)4
				};
				StatusLabel = new GUIStyle(RowLabel)
				{
					fontStyle = (FontStyle)1
				};
				StatusLabel.normal.textColor = new Color(0.7f, 1f, 0.7f, 1f);
				WarningLabel = new GUIStyle(RowLabel)
				{
					fontStyle = (FontStyle)1
				};
				WarningLabel.normal.textColor = new Color(1f, 0.82f, 0.35f, 1f);
				ToastLabel = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					wordWrap = true
				};
				ToastLabel.normal.textColor = new Color(0.84f, 1f, 0.82f, 1f);
				DeleteButton = new GUIStyle(GUI.skin.button);
				DeleteButton.normal.textColor = Color.red;
				DeleteButton.hover.textColor = new Color(1f, 0.35f, 0.35f, 1f);
				DeleteButton.active.textColor = Color.red;
				CurrentSaveBox = new GUIStyle(GUI.skin.box);
				CurrentSaveBox.normal.textColor = Color.white;
				_initialized = true;
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager), "SaveFileDelete")]
	internal static class StatsManagerSaveFileDeletePatch
	{
		private static bool Prefix(string saveFileName)
		{
			return !(Plugin.Instance?.ShouldBlockVanillaDelete(saveFileName) ?? false);
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	internal static class RunManagerChangeLevelPatch
	{
		private static bool Prefix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!_levelFailed || _completedLevel)
			{
				return true;
			}
			return !(Plugin.Instance?.TryAutoLoadCheckpointOnLevelFailure(_changeLevelType) ?? false);
		}
	}
	[HarmonyPatch(typeof(MenuPageSaves), "Start")]
	internal static class MenuPageSavesStartPatch
	{
		private static void Postfix(MenuPageSaves __instance)
		{
			Plugin.EnsureVanillaSaveMenuCanCreateSaves(__instance);
		}
	}
	[HarmonyPatch(typeof(MenuPageSaves), "OnNewGame")]
	internal static class MenuPageSavesOnNewGamePatch
	{
		private static void Prefix(MenuPageSaves __instance)
		{
			Plugin.EnsureVanillaSaveMenuCanCreateSaves(__instance);
		}
	}
}