Decompiled source of SaveKeeper v1.0.0

Zichen-SaveKeeper.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("Zichen-SaveKeeper")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Zichen-SaveKeeper")]
[assembly: AssemblyTitle("Zichen-SaveKeeper")]
[assembly: AssemblyVersion("1.0.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
[BepInPlugin("zichen.savekeeper", "A.SaveKeeper", "1.0.0")]
public sealed class ZichenSaveKeeperPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(MenuPageSaves), "OnDeleteGame")]
	private static class MenuPageSavesOnDeleteGamePatch
	{
		private static bool Prefix()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEnabled())
			{
				return true;
			}
			if (allowPlayerDelete != null && allowPlayerDelete.Value)
			{
				playerMenuDeleteInProgress = true;
				return true;
			}
			MenuManager.instance.PagePopUp("SaveKeeper", Color.red, "当前配置禁止手动删除存档。", "OK", false);
			LogInfo("Blocked manual save deletion by config.");
			return false;
		}

		private static void Postfix()
		{
			playerMenuDeleteInProgress = false;
		}
	}

	[HarmonyPatch(typeof(StatsManager), "SaveFileDelete")]
	private static class StatsManagerSaveFileDeletePatch
	{
		private static bool Prefix(string saveFileName)
		{
			if (!IsEnabled())
			{
				return true;
			}
			if (playerMenuDeleteInProgress)
			{
				playerMenuDeleteInProgress = false;
				LogInfo("Allowed manual save deletion: " + saveFileName);
				return true;
			}
			if (blockGameDelete == null || !blockGameDelete.Value)
			{
				LogInfo("Allowed game save deletion by config: " + saveFileName);
				return true;
			}
			LogInfo("Blocked game save deletion: " + saveFileName);
			return false;
		}
	}

	[HarmonyPatch(typeof(StatsManager), "SaveGame")]
	private static class StatsManagerSaveGamePatch
	{
		private static bool Prefix(string fileName)
		{
			if (!ShouldBlockDeathOverwrite())
			{
				return true;
			}
			if (IsArenaNow())
			{
				LogInfo("Blocked SaveGame in arena/death result flow: " + fileName);
				return false;
			}
			if (playerDeathSaveBlocked && !SemiFunc.IsMultiplayer())
			{
				LogInfo("Blocked SaveGame after singleplayer death: " + fileName);
				return false;
			}
			if (multiplayerDeathSaveBlocked && SemiFunc.IsMultiplayer())
			{
				LogInfo("Blocked SaveGame after multiplayer team death: " + fileName);
				return false;
			}
			return true;
		}

		private static void Postfix(string fileName, bool __runOriginal)
		{
			if (IsEnabled() && __runOriginal)
			{
				CleanupOldBackups(fileName);
			}
		}
	}

	[HarmonyPatch(typeof(StatsManager), "SaveFileSave")]
	private static class StatsManagerSaveFileSavePatch
	{
		private static void Prefix(StatsManager __instance)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEnabled() || savePublicRooms == null || !savePublicRooms.Value || (Object)(object)__instance == (Object)null || (Object)(object)GameManager.instance == (Object)null || GameManagerLobbyTypeField == null)
			{
				return;
			}
			object value = GameManagerLobbyTypeField.GetValue(GameManager.instance);
			if (value is LobbyTypes)
			{
				LobbyTypes val = (LobbyTypes)value;
				if ((int)val != 0 && (__instance.savedLobbyTypes == null || !__instance.savedLobbyTypes.Contains(val)))
				{
					LogInfo("Saving non-private room progress.");
					__instance.SaveGame(GetCurrentSaveFileName());
				}
			}
		}
	}

	[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeath")]
	private static class PlayerAvatarPlayerDeathPatch
	{
		private static void Prefix()
		{
			if (!ShouldBlockDeathOverwrite() || SemiFunc.IsMultiplayer() || IsShopNow())
			{
				return;
			}
			playerDeathSaveBlocked = true;
			LogInfo("Singleplayer death detected. Save overwrite is temporarily blocked.");
			ZichenSaveKeeperPlugin instance = Instance;
			if (instance != null)
			{
				((MonoBehaviour)instance).StartCoroutine(ResetPlayerDeathBlockLater());
			}
			if (restoreSaveAfterSingleplayerDeath != null && restoreSaveAfterSingleplayerDeath.Value)
			{
				string currentSaveFileName = GetCurrentSaveFileName();
				if (!string.IsNullOrWhiteSpace(currentSaveFileName))
				{
					ZichenSaveKeeperPlugin instance2 = Instance;
					if (instance2 != null)
					{
						((MonoBehaviour)instance2).StartCoroutine(RestoreSingleplayerSaveAfterDeathLater(currentSaveFileName));
					}
				}
			}
			if (autoReloadSingleplayer == null || !autoReloadSingleplayer.Value)
			{
				return;
			}
			string currentSaveFileName2 = GetCurrentSaveFileName();
			if (!string.IsNullOrWhiteSpace(currentSaveFileName2))
			{
				ZichenSaveKeeperPlugin instance3 = Instance;
				if (instance3 != null)
				{
					((MonoBehaviour)instance3).StartCoroutine(ReloadSingleplayerLater(currentSaveFileName2));
				}
			}
		}
	}

	[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
	private static class PlayerAvatarPlayerDeathRpcPatch
	{
		private static void Postfix()
		{
			if (ShouldBlockDeathOverwrite() && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && AreAllPlayersDead())
			{
				multiplayerDeathSaveBlocked = true;
				LogInfo("All players are dead. Multiplayer save overwrite is temporarily blocked.");
				ZichenSaveKeeperPlugin instance = Instance;
				if (instance != null)
				{
					((MonoBehaviour)instance).StartCoroutine(ResetMultiplayerDeathBlockLater());
				}
			}
		}
	}

	[HarmonyPatch(typeof(PlayerAvatar), "Revive")]
	private static class PlayerAvatarRevivePatch
	{
		private static void Prefix()
		{
			playerDeathSaveBlocked = false;
			multiplayerDeathSaveBlocked = false;
		}
	}

	[HarmonyPatch(typeof(GameDirector), "Update")]
	private static class GameDirectorUpdatePatch
	{
		private static void Postfix()
		{
			if (!IsEnabled() || restoreBackupAfterArenaReturn == null || !restoreBackupAfterArenaReturn.Value || !SemiFunc.IsMultiplayer() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (IsArenaNow())
			{
				multiplayerArenaSeen = true;
			}
			else if (multiplayerArenaSeen && !multiplayerArenaRestoreRunning && IsLobbyOrLobbyMenuNow())
			{
				LogInfo("Returned from multiplayer arena. Restoring latest backup soon.");
				ZichenSaveKeeperPlugin instance = Instance;
				if (instance != null)
				{
					((MonoBehaviour)instance).StartCoroutine(RestoreBackupAfterArenaReturnLater());
				}
			}
		}
	}

	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	private static class RunManagerChangeLevelPatch
	{
		private static void Prefix(RunManager __instance, bool _levelFailed)
		{
			originalArenaLevelsDuringForcedRace = null;
			if (!IsEnabled() || deathArenaMode == null || deathArenaMode.Value == "官方随机" || (Object)(object)__instance == (Object)null || !_levelFailed || (Object)(object)__instance.levelCurrent == (Object)null || (Object)(object)__instance.levelCurrent == (Object)(object)__instance.levelLobby || IsLevelShop(__instance.levelCurrent) || IsLevelArena(__instance.levelCurrent))
			{
				return;
			}
			string value = deathArenaMode.Value;
			Level val = ((value == "皇冠竞技场") ? FindCrownArenaLevel(__instance.levelArena) : FindArenaRaceLevel(__instance.levelArena));
			if ((Object)(object)val == (Object)null)
			{
				if (!missingRaceArenaLogged)
				{
					missingRaceArenaLogged = true;
					LogWarning("Could not find requested death arena mode '" + value + "'. Keeping the game's original arena selection.");
				}
			}
			else
			{
				originalArenaLevelsDuringForcedRace = new List<Level>(__instance.levelArena);
				__instance.levelArena = new List<Level> { val };
				LogInfo("Forcing death arena mode '" + value + "' to level: " + ((Object)val).name);
			}
		}

		private static void Postfix(RunManager __instance)
		{
			if ((Object)(object)__instance != (Object)null && originalArenaLevelsDuringForcedRace != null)
			{
				__instance.levelArena = originalArenaLevelsDuringForcedRace;
				originalArenaLevelsDuringForcedRace = null;
			}
		}
	}

	[HarmonyPatch(typeof(StatsManager), "LoadGame")]
	private static class StatsManagerLoadGamePatch
	{
		private static void Postfix()
		{
			if (!manualRestoreRunning)
			{
				playerDeathSaveBlocked = false;
				multiplayerDeathSaveBlocked = false;
			}
		}
	}

	[HarmonyPatch(typeof(RunManager), "LeaveToMainMenu")]
	private static class RunManagerLeaveToMainMenuPatch
	{
		private static void Prefix()
		{
			playerDeathSaveBlocked = false;
			multiplayerDeathSaveBlocked = false;
			playerMenuDeleteInProgress = false;
			multiplayerArenaSeen = false;
			multiplayerArenaRestoreRunning = false;
			manualRestoreRunning = false;
		}
	}

	[CompilerGenerated]
	private sealed class <ManualRestoreLatestProgressCoroutine>d__58 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string saveFileName;

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

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

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

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

		private bool MoveNext()
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				manualRestoreRunning = true;
				LogInfo("Manual latest progress reload started: " + saveFileName);
				if (SemiFunc.IsMultiplayer())
				{
					if (!PhotonNetwork.IsMasterClient)
					{
						LogWarning("Manual restore failed because only the host can reload a multiplayer save.");
						ShowPopup("只有主机可以手动恢复多人存档进度。", Color.yellow);
						manualRestoreRunning = false;
						return false;
					}
					multiplayerDeathSaveBlocked = true;
				}
				else
				{
					playerDeathSaveBlocked = true;
				}
				RestoreBestSaveAfterArenaReturn(saveFileName);
				<>2__current = (object)new WaitForSeconds(0.65f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				try
				{
					SemiFunc.MenuActionSingleplayerGame(saveFileName, (List<string>)null);
				}
				catch (Exception ex)
				{
					LogError("Manual restore failed to trigger game reload: " + ex.Message);
					TryLoadGameInMemory(saveFileName);
					ShowPopup("已恢复存档数据,但触发重载失败。", Color.yellow);
					playerDeathSaveBlocked = false;
					multiplayerDeathSaveBlocked = false;
					manualRestoreRunning = false;
					return false;
				}
				<>2__current = (object)new WaitForSeconds(1f);
				<>1__state = 2;
				return true;
			case 2:
				<>1__state = -1;
				TryLoadGameInMemory(saveFileName);
				playerDeathSaveBlocked = false;
				multiplayerDeathSaveBlocked = false;
				manualRestoreRunning = false;
				LogInfo("Manual latest progress reload completed: " + saveFileName);
				ShowPopup("已重新加载当前存档的最新进度。", Color.green);
				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();
		}
	}

	[CompilerGenerated]
	private sealed class <ReloadSingleplayerLater>d__65 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string saveFileName;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(4f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				if (!IsEnabled() || string.IsNullOrWhiteSpace(saveFileName) || SemiFunc.IsMultiplayer())
				{
					playerDeathSaveBlocked = false;
					return false;
				}
				if (restoreLatestBackupBeforeReload != null && restoreLatestBackupBeforeReload.Value)
				{
					RestoreLatestBackup(saveFileName);
				}
				LogInfo("Reloading save after singleplayer death: " + saveFileName);
				SemiFunc.MenuActionSingleplayerGame(saveFileName, (List<string>)null);
				<>2__current = (object)new WaitForSeconds(1f);
				<>1__state = 2;
				return true;
			case 2:
				<>1__state = -1;
				TryLoadGameInMemory(saveFileName);
				playerDeathSaveBlocked = false;
				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();
		}
	}

	[CompilerGenerated]
	private sealed class <ResetMultiplayerDeathBlockLater>d__64 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

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

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

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

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

		private bool MoveNext()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(12f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				multiplayerDeathSaveBlocked = false;
				LogInfo("Multiplayer death save block expired.");
				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();
		}
	}

	[CompilerGenerated]
	private sealed class <ResetPlayerDeathBlockLater>d__63 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

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

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

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

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

		private bool MoveNext()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(12f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				playerDeathSaveBlocked = false;
				LogInfo("Singleplayer death save block expired.");
				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();
		}
	}

	[CompilerGenerated]
	private sealed class <RestoreBackupAfterArenaReturnLater>d__67 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

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

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

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

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

		private bool MoveNext()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				multiplayerArenaRestoreRunning = true;
				<>2__current = (object)new WaitForSeconds(3f);
				<>1__state = 1;
				return true;
			case 1:
			{
				<>1__state = -1;
				string currentSaveFileName = GetCurrentSaveFileName();
				if (string.IsNullOrWhiteSpace(currentSaveFileName))
				{
					LogWarning("Could not restore backup after arena return because current save is empty.");
				}
				else
				{
					RestoreBestSaveAfterArenaReturn(currentSaveFileName);
					TryLoadGameInMemory(currentSaveFileName);
					LogInfo("Checked save recovery after returning from multiplayer arena: " + currentSaveFileName);
				}
				multiplayerArenaSeen = false;
				multiplayerArenaRestoreRunning = false;
				multiplayerDeathSaveBlocked = false;
				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();
		}
	}

	[CompilerGenerated]
	private sealed class <RestoreSingleplayerSaveAfterDeathLater>d__66 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string saveFileName;

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

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

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

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

		private bool MoveNext()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = (object)new WaitForSeconds(3f);
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				if (!IsEnabled() || SemiFunc.IsMultiplayer() || string.IsNullOrWhiteSpace(saveFileName))
				{
					return false;
				}
				RestoreBestSaveAfterArenaReturn(saveFileName);
				TryLoadGameInMemory(saveFileName);
				playerDeathSaveBlocked = false;
				LogInfo("Checked singleplayer save recovery after death: " + saveFileName);
				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 = "zichen.savekeeper";

	public const string PluginName = "A.SaveKeeper";

	public const string PluginVersion = "1.0.0";

	private const string InfoSection = "模组信息";

	private const string SaveSection = "A.存档管家";

	private const int DeathSaveBlockSeconds = 12;

	private const string DeathArenaModeRace = "赛车比赛";

	private const string DeathArenaModeCrown = "皇冠竞技场";

	private const string DeathArenaModeOfficial = "官方随机";

	private static readonly FieldInfo StatsManagerCurrentSaveField = typeof(StatsManager).GetField("saveFileCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PlayerAvatarDeadSetField = typeof(PlayerAvatar).GetField("deadSet", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo GameManagerLobbyTypeField = typeof(GameManager).GetField("lobbyType", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly Regex BackupNumberRegex = new Regex("_BACKUP(\\d+)", RegexOptions.IgnoreCase);

	private static ConfigEntry<bool> featureEnabled;

	private static ConfigEntry<bool> allowPlayerDelete;

	private static ConfigEntry<bool> blockGameDelete;

	private static ConfigEntry<bool> blockDeathOverwrite;

	private static ConfigEntry<bool> savePublicRooms;

	private static ConfigEntry<bool> autoReloadSingleplayer;

	private static ConfigEntry<bool> restoreLatestBackupBeforeReload;

	private static ConfigEntry<bool> restoreBackupAfterArenaReturn;

	private static ConfigEntry<bool> restoreSaveAfterSingleplayerDeath;

	private static ConfigEntry<KeyboardShortcut> manualRestoreShortcut;

	private static ConfigEntry<int> maxBackupCount;

	private static ConfigEntry<bool> showConflictWarning;

	private static ConfigEntry<bool> verboseLogging;

	private static ConfigEntry<string> deathArenaMode;

	private Harmony harmony;

	private ConfigEntry<string> moduleNameInfo;

	private ConfigEntry<string> moduleVersionInfo;

	private ConfigEntry<string> contactInfo;

	private static bool playerDeathSaveBlocked;

	private static bool multiplayerDeathSaveBlocked;

	private static bool playerMenuDeleteInProgress;

	private static bool multiplayerArenaSeen;

	private static bool multiplayerArenaRestoreRunning;

	private static List<Level> originalArenaLevelsDuringForcedRace;

	private static bool missingRaceArenaLogged;

	private static bool noSaveDeleteConflictDetected;

	private static bool conflictPopupShown;

	private static bool manualRestoreRunning;

	public static ZichenSaveKeeperPlugin Instance { get; private set; }

	private void Awake()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		Instance = this;
		BindConfig();
		harmony = new Harmony("zichen.savekeeper.patch");
		harmony.PatchAll(typeof(ZichenSaveKeeperPlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"zichen-savekeeper loaded.");
	}

	private void OnDestroy()
	{
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
		harmony = null;
		if (Instance == this)
		{
			Instance = null;
		}
	}

	private void BindConfig()
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Expected O, but got Unknown
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Expected O, but got Unknown
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Expected O, but got Unknown
		//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0406: Expected O, but got Unknown
		moduleNameInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "模组名称", "存档管家", new ConfigDescription("当前模组的中文名称。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 1000,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		moduleNameInfo.Value = "存档管家";
		moduleVersionInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "模组版本号", "1.0.0", new ConfigDescription("当前模组版本号。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 990,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		moduleVersionInfo.Value = "1.0.0";
		contactInfo = ((BaseUnityPlugin)this).Config.Bind<string>("模组信息", "REPO交流QQ群", "824639225", new ConfigDescription("REPO游戏交流、BUG反馈、优化建议、功能请求请加QQ群。此处仅用于显示,不影响功能。", (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 980,
				CustomDrawer = DrawInfo,
				ReadOnly = true
			}
		}));
		contactInfo.Value = "824639225";
		featureEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "启用", true, ConfigDescriptionWithOrder("开启存档保护。关闭后,本模组不会拦截删档或保存。", 900));
		allowPlayerDelete = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "允许玩家手动删除存档", true, ConfigDescriptionWithOrder("开启后,玩家在存档菜单里主动删除存档会被放行。关闭后,玩家手动删除也会被阻止。", 890));
		blockGameDelete = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "阻止游戏自动删除存档", true, ConfigDescriptionWithOrder("开启后,游戏流程触发的自动删档会被阻止。建议保持开启。", 880));
		blockDeathOverwrite = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "阻止死亡覆盖存档", true, ConfigDescriptionWithOrder("开启后,玩家死亡、全员死亡或进入竞技场结算时,会阻止游戏把失败后的状态写进当前存档。", 870));
		savePublicRooms = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "公开房间也保存存档", true, ConfigDescriptionWithOrder("开启后,公开匹配房间会像私人房间一样在正常过关、回车、进商店等流程保存进度。死亡和竞技场危险保存仍会被保护逻辑拦截。", 865));
		autoReloadSingleplayer = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "单人死亡后自动读档", false, ConfigDescriptionWithOrder("实验功能。开启后,单人模式死亡数秒后会自动重新载入当前存档。多人模式先不自动读档。", 860));
		restoreLatestBackupBeforeReload = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "自动读档前恢复最新备份", true, ConfigDescriptionWithOrder("实验功能。单人自动读档前,把当前存档目录里编号最大的备份文件复制回主存档文件。", 850));
		restoreBackupAfterArenaReturn = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "死亡比赛后校验最新进度", true, ConfigDescriptionWithOrder("开启后,多人全灭进入死亡比赛再回到房间时,会比较主存档和最新备份,保留进度更高的一份,避免回退关卡。", 840));
		restoreSaveAfterSingleplayerDeath = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "单人死亡后恢复最新存档", true, ConfigDescriptionWithOrder("开启后,单人死亡流程结束后会重新读取当前主存档/备份中进度更新的一份,避免重新从第一关开始。", 835));
		manualRestoreShortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("A.存档管家", "手动恢复最新进度快捷键", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), ConfigDescriptionWithOrder("按下后,会对当前存档执行主存档/最新备份进度比较,并重新读取进度更高的一份。用于出现回退时手动救回进度。", 832));
		maxBackupCount = ((BaseUnityPlugin)this).Config.Bind<int>("A.存档管家", "最多保留备份数量", 20, ConfigDescriptionWithOrder("每次保存后,自动清理当前存档目录中过旧的 _BACKUP 文件。设为 0 表示不自动清理。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), 831));
		showConflictWarning = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "提示NoSaveDelete冲突", true, ConfigDescriptionWithOrder("开启后,如果检测到原 No Save Delete 模组同时安装,会在日志和游戏内弹窗提示。建议不要同时启用两个存档保护模组。", 829));
		verboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("A.存档管家", "详细日志", false, ConfigDescriptionWithOrder("开启后输出保存、恢复、备份清理等详细日志。发布和日常游玩建议关闭。", 828));
		deathArenaMode = ((BaseUnityPlugin)this).Config.Bind<string>("A.存档管家", "死亡后进入比赛类型", "赛车比赛", new ConfigDescription("选择全灭后进入哪种比赛。赛车比赛:强制进入赛车;皇冠竞技场:强制进入普通皇冠竞技场;官方随机:完全使用游戏原版随机逻辑。", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "赛车比赛", "皇冠竞技场", "官方随机" }), new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = 830
			}
		}));
		DetectNoSaveDeleteConflict();
	}

	private void Update()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		ShowConflictWarningOnce();
		if (IsEnabled() && manualRestoreShortcut != null)
		{
			KeyboardShortcut value = manualRestoreShortcut.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && !((Object)(object)StatsManager.instance == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				ManualRestoreLatestProgress();
			}
		}
	}

	private void DrawInfo(ConfigEntryBase entry)
	{
		GUILayout.Label(entry.BoxedValue?.ToString() ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
	}

	private static ConfigDescription ConfigDescriptionWithOrder(string description, int order)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		return new ConfigDescription(description, (AcceptableValueBase)null, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = order
			}
		});
	}

	private static ConfigDescription ConfigDescriptionWithOrder(string description, AcceptableValueBase acceptableValues, int order)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		return new ConfigDescription(description, acceptableValues, new object[1]
		{
			new ConfigurationManagerAttributes
			{
				Order = order
			}
		});
	}

	private static bool IsEnabled()
	{
		if (featureEnabled != null)
		{
			return featureEnabled.Value;
		}
		return false;
	}

	private static bool ShouldBlockDeathOverwrite()
	{
		if (IsEnabled() && blockDeathOverwrite != null)
		{
			return blockDeathOverwrite.Value;
		}
		return false;
	}

	private static void LogInfo(string message)
	{
		if (verboseLogging != null && verboseLogging.Value)
		{
			ZichenSaveKeeperPlugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)message);
			}
		}
	}

	private static void LogWarning(string message)
	{
		ZichenSaveKeeperPlugin instance = Instance;
		if (instance != null)
		{
			((BaseUnityPlugin)instance).Logger.LogWarning((object)message);
		}
	}

	private static void LogError(string message)
	{
		ZichenSaveKeeperPlugin instance = Instance;
		if (instance != null)
		{
			((BaseUnityPlugin)instance).Logger.LogError((object)message);
		}
	}

	private static void ManualRestoreLatestProgress()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		if (manualRestoreRunning)
		{
			LogInfo("Manual restore ignored because another restore is already running.");
			return;
		}
		string currentSaveFileName = GetCurrentSaveFileName();
		if (string.IsNullOrWhiteSpace(currentSaveFileName))
		{
			LogWarning("Manual restore failed because current save is empty.");
			ShowPopup("当前没有可恢复的存档。", Color.yellow);
			return;
		}
		ZichenSaveKeeperPlugin instance = Instance;
		if (instance != null)
		{
			((MonoBehaviour)instance).StartCoroutine(ManualRestoreLatestProgressCoroutine(currentSaveFileName));
		}
	}

	[IteratorStateMachine(typeof(<ManualRestoreLatestProgressCoroutine>d__58))]
	private static IEnumerator ManualRestoreLatestProgressCoroutine(string saveFileName)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ManualRestoreLatestProgressCoroutine>d__58(0)
		{
			saveFileName = saveFileName
		};
	}

	private static void ShowPopup(string message, Color color)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)MenuManager.instance != (Object)null)
			{
				MenuManager.instance.PagePopUp("SaveKeeper", color, message, "OK", false);
			}
		}
		catch
		{
		}
	}

	private static void DetectNoSaveDeleteConflict()
	{
		try
		{
			noSaveDeleteConflictDetected = false;
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				string text = pluginInfo.Key ?? string.Empty;
				PluginInfo value = pluginInfo.Value;
				object obj;
				if (value == null)
				{
					obj = null;
				}
				else
				{
					BepInPlugin metadata = value.Metadata;
					obj = ((metadata != null) ? metadata.Name : null);
				}
				if (obj == null)
				{
					obj = string.Empty;
				}
				string text2 = (string)obj;
				if (LooksLikeNoSaveDelete(text) || LooksLikeNoSaveDelete(text2))
				{
					noSaveDeleteConflictDetected = true;
					LogWarning("Detected possible No Save Delete conflict from loaded plugin: " + text + " / " + text2);
					return;
				}
			}
			string pluginPath = Paths.PluginPath;
			if (!Directory.Exists(pluginPath))
			{
				return;
			}
			foreach (string item in Directory.EnumerateFileSystemEntries(pluginPath, "*", SearchOption.TopDirectoryOnly))
			{
				if (LooksLikeNoSaveDelete(Path.GetFileName(item) ?? string.Empty))
				{
					noSaveDeleteConflictDetected = true;
					LogWarning("Detected possible No Save Delete conflict in plugin folder: " + item);
					break;
				}
			}
		}
		catch (Exception ex)
		{
			LogWarning("Failed to scan No Save Delete conflict: " + ex.Message);
		}
	}

	private static bool LooksLikeNoSaveDelete(string value)
	{
		if (string.IsNullOrWhiteSpace(value))
		{
			return false;
		}
		string text = value.Replace("_", string.Empty).Replace("-", string.Empty).Replace(" ", string.Empty);
		if (text.IndexOf("NoSaveDelete", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text.IndexOf("PxntxrezStudioNoSaveDelete", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static void ShowConflictWarningOnce()
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		if (!conflictPopupShown && noSaveDeleteConflictDetected && showConflictWarning != null && showConflictWarning.Value && !((Object)(object)MenuManager.instance == (Object)null))
		{
			conflictPopupShown = true;
			ShowPopup("检测到 No Save Delete 可能同时安装。建议禁用原模组,避免两个存档保护模组互相抢保存逻辑。", Color.yellow);
		}
	}

	[IteratorStateMachine(typeof(<ResetPlayerDeathBlockLater>d__63))]
	private static IEnumerator ResetPlayerDeathBlockLater()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ResetPlayerDeathBlockLater>d__63(0);
	}

	[IteratorStateMachine(typeof(<ResetMultiplayerDeathBlockLater>d__64))]
	private static IEnumerator ResetMultiplayerDeathBlockLater()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ResetMultiplayerDeathBlockLater>d__64(0);
	}

	[IteratorStateMachine(typeof(<ReloadSingleplayerLater>d__65))]
	private static IEnumerator ReloadSingleplayerLater(string saveFileName)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <ReloadSingleplayerLater>d__65(0)
		{
			saveFileName = saveFileName
		};
	}

	[IteratorStateMachine(typeof(<RestoreSingleplayerSaveAfterDeathLater>d__66))]
	private static IEnumerator RestoreSingleplayerSaveAfterDeathLater(string saveFileName)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <RestoreSingleplayerSaveAfterDeathLater>d__66(0)
		{
			saveFileName = saveFileName
		};
	}

	[IteratorStateMachine(typeof(<RestoreBackupAfterArenaReturnLater>d__67))]
	private static IEnumerator RestoreBackupAfterArenaReturnLater()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <RestoreBackupAfterArenaReturnLater>d__67(0);
	}

	private static string GetCurrentSaveFileName()
	{
		if ((Object)(object)StatsManager.instance == (Object)null || StatsManagerCurrentSaveField == null)
		{
			return null;
		}
		return StatsManagerCurrentSaveField.GetValue(StatsManager.instance) as string;
	}

	private static bool AreAllPlayersDead()
	{
		if (PlayerAvatarDeadSetField == null)
		{
			return false;
		}
		List<PlayerAvatar> list = SemiFunc.PlayerGetList();
		if (list == null || list.Count == 0)
		{
			return false;
		}
		foreach (PlayerAvatar item in list)
		{
			if (!((Object)(object)item == (Object)null))
			{
				object value = PlayerAvatarDeadSetField.GetValue(item);
				if (!(value is bool) || !(bool)value)
				{
					return false;
				}
			}
		}
		return true;
	}

	private static bool IsArenaNow()
	{
		try
		{
			return SemiFunc.RunIsArena();
		}
		catch
		{
			return false;
		}
	}

	private static bool IsShopNow()
	{
		try
		{
			return SemiFunc.RunIsShop();
		}
		catch
		{
			return false;
		}
	}

	private static bool IsLevelArena(Level level)
	{
		try
		{
			return SemiFunc.IsLevelArena(level);
		}
		catch
		{
			return false;
		}
	}

	private static bool IsLevelShop(Level level)
	{
		try
		{
			return SemiFunc.IsLevelShop(level);
		}
		catch
		{
			return false;
		}
	}

	private static Level FindArenaRaceLevel(List<Level> arenaLevels)
	{
		if (arenaLevels == null || arenaLevels.Count == 0)
		{
			return null;
		}
		foreach (Level arenaLevel in arenaLevels)
		{
			if (IsRaceLevelName(arenaLevel))
			{
				return arenaLevel;
			}
		}
		if (arenaLevels.Count > 1)
		{
			return arenaLevels[1];
		}
		return null;
	}

	private static Level FindCrownArenaLevel(List<Level> arenaLevels)
	{
		if (arenaLevels == null || arenaLevels.Count == 0)
		{
			return null;
		}
		foreach (Level arenaLevel in arenaLevels)
		{
			if (!IsRaceLevelName(arenaLevel))
			{
				return arenaLevel;
			}
		}
		return arenaLevels[0];
	}

	private static bool IsRaceLevelName(Level level)
	{
		if ((Object)(object)level == (Object)null)
		{
			return false;
		}
		string text = ((Object)level).name ?? string.Empty;
		string text2 = level.NarrativeName ?? string.Empty;
		if (text.IndexOf("race", StringComparison.OrdinalIgnoreCase) < 0 && text2.IndexOf("race", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("racing", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return text2.IndexOf("racing", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return true;
	}

	private static bool IsLobbyOrLobbyMenuNow()
	{
		try
		{
			return SemiFunc.RunIsLobby() || SemiFunc.RunIsLobbyMenu();
		}
		catch
		{
			return false;
		}
	}

	private static void TryLoadGameInMemory(string saveFileName)
	{
		try
		{
			MethodInfo method = typeof(StatsManager).GetMethod("LoadGame", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method == null || (Object)(object)StatsManager.instance == (Object)null)
			{
				LogWarning("StatsManager.LoadGame was not found.");
				return;
			}
			method.Invoke(StatsManager.instance, new object[2] { saveFileName, null });
		}
		catch (Exception ex)
		{
			LogError("Failed to reload save in memory: " + ex.Message);
		}
	}

	private static void RestoreLatestBackup(string saveFileName)
	{
		try
		{
			string text = Path.Combine(Application.persistentDataPath, "saves", saveFileName);
			if (!Directory.Exists(text))
			{
				LogWarning("Save directory was not found: " + text);
				return;
			}
			string text2 = FindLatestBackup(text, saveFileName);
			if (string.IsNullOrEmpty(text2))
			{
				LogWarning("No backup file found for save: " + saveFileName);
				return;
			}
			string destFileName = Path.Combine(text, saveFileName + ".es3");
			File.Copy(text2, destFileName, overwrite: true);
			LogInfo("Restored latest backup: " + text2);
		}
		catch (Exception ex)
		{
			LogError("Failed to restore latest backup: " + ex.Message);
		}
	}

	private static void RestoreBestSaveAfterArenaReturn(string saveFileName)
	{
		try
		{
			string text = Path.Combine(Application.persistentDataPath, "saves", saveFileName);
			string text2 = Path.Combine(text, saveFileName + ".es3");
			if (!Directory.Exists(text))
			{
				LogWarning("Save directory was not found: " + text);
				return;
			}
			string text3 = FindLatestBackup(text, saveFileName);
			if (string.IsNullOrEmpty(text3))
			{
				LogWarning("No backup file found after arena return for save: " + saveFileName);
				return;
			}
			if (!File.Exists(text2))
			{
				File.Copy(text3, text2, overwrite: true);
				LogInfo("Main save was missing. Restored latest backup: " + text3);
				return;
			}
			int num = ReadSaveRunLevel(saveFileName, saveFileName);
			int num2 = ReadSaveRunLevel(saveFileName, Path.GetFileNameWithoutExtension(text3));
			if (num2 > num)
			{
				File.Copy(text3, text2, overwrite: true);
				LogInfo("Backup has newer progress. Restored backup level " + num2 + " over main level " + num + ".");
			}
			else
			{
				LogInfo("Keeping main save after arena return. Main level=" + num + ", backup level=" + num2 + ".");
			}
		}
		catch (Exception ex)
		{
			LogError("Failed to choose best save after arena return: " + ex.Message);
		}
	}

	private static int ReadSaveRunLevel(string folderName, string fileName)
	{
		try
		{
			StatsManager instance = StatsManager.instance;
			if (int.TryParse((instance != null) ? instance.SaveFileGetRunLevel(folderName, fileName) : null, out var result))
			{
				return result;
			}
		}
		catch (Exception ex)
		{
			LogWarning("Failed to read save level from " + fileName + ": " + ex.Message);
		}
		return -1;
	}

	private static string FindLatestBackup(string saveDirectory, string saveFileName)
	{
		return (from path in Directory.GetFiles(saveDirectory, saveFileName + "_BACKUP*.es3")
			select new
			{
				Path = path,
				Number = ExtractBackupNumber(path, BackupNumberRegex)
			} into item
			where item.Number >= 0
			orderby item.Number descending
			select item.Path).FirstOrDefault();
	}

	private static void CleanupOldBackups(string saveFileName)
	{
		try
		{
			if (maxBackupCount == null || maxBackupCount.Value <= 0 || string.IsNullOrWhiteSpace(saveFileName))
			{
				return;
			}
			string path2 = Path.Combine(Application.persistentDataPath, "saves", saveFileName);
			if (!Directory.Exists(path2))
			{
				return;
			}
			List<BackupFileInfo> list = (from path in Directory.GetFiles(path2, saveFileName + "_BACKUP*.es3")
				select new BackupFileInfo
				{
					Path = path,
					Number = ExtractBackupNumber(path, BackupNumberRegex)
				} into item
				where item.Number >= 0
				orderby item.Number descending
				select item).ToList();
			if (list.Count <= maxBackupCount.Value)
			{
				return;
			}
			foreach (BackupFileInfo item in list.Skip(maxBackupCount.Value))
			{
				File.Delete(item.Path);
				LogInfo("Deleted old backup: " + item.Path);
			}
		}
		catch (Exception ex)
		{
			LogError("Failed to clean old backups: " + ex.Message);
		}
	}

	private static int ExtractBackupNumber(string filePath, Regex regex)
	{
		Match match = regex.Match(Path.GetFileNameWithoutExtension(filePath));
		if (match.Success && int.TryParse(match.Groups[1].Value, out var result))
		{
			return result;
		}
		return -1;
	}
}
internal sealed class ConfigurationManagerAttributes
{
	public bool? ShowRangeAsPercent;

	public Action<ConfigEntryBase> CustomDrawer;

	public bool? Browsable;

	public string Category;

	public object DefaultValue;

	public bool? HideDefaultButton;

	public bool? HideSettingName;

	public string Description;

	public string DispName;

	public int? Order;

	public bool? ReadOnly;

	public bool? IsAdvanced;
}
internal sealed class BackupFileInfo
{
	public string Path;

	public int Number;
}