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 PreviousStates = new Dictionary(); 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 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 d__65 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Plugin <>4__this; public SaveEntry entry; public RestorePoint restorePoint; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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 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 _saveEntries = new List(); private Harmony? _harmony; private ConfigEntry _menuKey; private ConfigEntry _quickLoadKey; private ConfigEntry _enableQuickLoad; private ConfigEntry _autoLoadCheckpointOnDeath; private ConfigEntry _preventGameDeletes; private ConfigEntry _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("Controls", "ManagerKey", (KeyCode)288, "Key used to open the SaveMaster manager."); _quickLoadKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "QuickLoadKey", (KeyCode)290, "Key used to load the current save without opening the manager."); _enableQuickLoad = ((BaseUnityPlugin)this).Config.Bind("Controls", "EnableQuickLoad", true, "Allow the quick-load key to reload the current save."); _autoLoadCheckpointOnDeath = ((BaseUnityPlugin)this).Config.Bind("Safety", "AutoLoadCheckpointOnDeath", true, "Automatically load the current checkpoint instead of advancing the failure flow when all players die."); _preventGameDeletes = ((BaseUnityPlugin)this).Config.Bind("Safety", "PreventGameDeletes", true, "Block R.E.P.O. from deleting saves after death or through vanilla flows."); _allowManagerDeletes = ((BaseUnityPlugin)this).Config.Bind("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()); 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()); } else if (_saveEntries.Count == 0) { GUILayout.Label("No save files found.", SaveMasterStyles.CenterLabel, Array.Empty()); } else { GUILayout.Label($"Available Saves ({_saveEntries.Count})", SaveMasterStyles.Subtitle, Array.Empty()); _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()); } 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()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(545f) }); GUILayout.Label(entry2.DisplayName, SaveMasterStyles.SaveTitle, Array.Empty()); GUILayout.Label(entry2.FileName + " | " + entry2.DateText, SaveMasterStyles.SmallLabel, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Level: " + entry2.LevelText + " Money: " + entry2.CurrencyText + " Time: " + FormatTime(entry2.TimePlayedSeconds), SaveMasterStyles.RowLabel, Array.Empty()); GUILayout.Label($"Total Haul: {entry2.TotalHaulText} Backups: {entry2.BackupCount}", SaveMasterStyles.RowLabel, Array.Empty()); GUILayout.Label("Players: " + entry2.PlayerNamesText, SaveMasterStyles.RowLabel, Array.Empty()); if (!entry2.IsValid) { GUILayout.Label("Main save file is missing or unreadable; backups may still exist.", SaveMasterStyles.WarningLabel, Array.Empty()); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(245f) }); GUILayout.BeginHorizontal(Array.Empty()); 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()); 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()); GUILayout.Space(8f); GUILayout.Label("Save name", SaveMasterStyles.SmallLabel, Array.Empty()); GUI.SetNextControlName("SaveMasterNewSaveName"); _newSaveName = GUILayout.TextField(_newSaveName, 40, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.Space(8f); GUILayout.Label("New display name", SaveMasterStyles.SmallLabel, Array.Empty()); GUI.SetNextControlName("SaveMasterRenameName"); _renameSaveName = GUILayout.TextField(_renameSaveName, 40, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.Space(8f); GUILayout.Label(_dialogSave.FileName, SaveMasterStyles.SaveTitle, Array.Empty()); GUILayout.Label("This removes the whole save folder, including backups.", SaveMasterStyles.WarningLabel, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); 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()); GUILayout.Space(8f); GUILayout.Label(_dialogSave.FileName, SaveMasterStyles.SaveTitle, Array.Empty()); GUILayout.Label(GetRestorePointDescription(_dialogRestorePoint), SaveMasterStyles.WarningLabel, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); 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(d__65))] private IEnumerator LoadSaveRoutine(SaveEntry entry, RestorePoint restorePoint) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new 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 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(folder.backups) : new List()), 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(); } 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(); } } 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)SemiFunc.RunIsTutorial) || SafeGameCheck((Func)SemiFunc.RunIsArena) || SafeGameCheck((Func)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 list = new List { 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("teamName", displayName, val); ES3.Save("teamNameChanged", !IsDefaultTeamName(displayName), val); } private static List GetBackups(string saveFileName) { try { return ((Object)(object)StatsManager.instance != (Object)null) ? StatsManager.instance.SaveFileGetBackups(saveFileName, SavesRoot) : new List(); } catch { return new List(); } } private void TryReloadInMemory(string saveFileName) { try { StatsManager.instance.LoadGame(saveFileName, (List)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)SemiFunc.IsMasterClientOrSingleplayer); } return false; } private static bool IsMultiplayer() { return SafeGameCheck((Func)SemiFunc.IsMultiplayer); } private static bool NoTextInputActive() { return HotkeyInput.NoTextInputActive((Func)SemiFunc.NoTextInputsActive); } private static bool SafeGameCheck(Func check) { try { return check(); } catch { return false; } } private static T GetFieldValue(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(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 Backups { get; set; } internal List 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(); PlayerNames = new List(); } } 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); } } }