using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("MoreSaveSlots")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("raddude")] [assembly: AssemblyProduct("MoreSaveSlots")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(true)] [assembly: Guid("071994df-c696-4ece-ad36-48d4036214b5")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace MoreSaveSlots; internal class Configs { internal static ConfigEntry numPages; internal static void InitializeConfigs() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown ConfigFile config = ((BaseUnityPlugin)MSS_Plugin.Instance).Config; numPages = config.Bind("Settings", "Number of save slot pages
(must restart game)", 3, new ConfigDescription("The number of pages of save slots. Each page has 6 slots.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), Array.Empty())); } } internal static class Extensions { public static T GetPrivateField(this object obj, string field) { return (T)Traverse.Create(obj).Field(field).GetValue(); } public static void SetPrivateField(this object obj, string field, object value) { Traverse.Create(obj).Field(field).SetValue(value); } public static void SetPrivateField(string field, object value) { Traverse.Create(typeof(T)).Field(field).SetValue(value); } public static object InvokePrivateMethod(this object obj, string method, params object[] parameters) { return AccessTools.Method(obj.GetType(), method, (Type[])null, (Type[])null).Invoke(obj, parameters); } } [BepInPlugin("com.raddude.moresaveslots", "MoreSaveSlots", "1.0.0")] public class MSS_Plugin : BaseUnityPlugin { public const string PLUGIN_GUID = "com.raddude.moresaveslots"; public const string PLUGIN_NAME = "MoreSaveSlots"; public const string PLUGIN_VERSION = "1.0.0"; private static ManualLogSource _logger; internal static MSS_Plugin Instance { get; private set; } internal static void LogDebug(string message) { _logger.LogDebug((object)message); } internal static void LogInfo(string message) { _logger.LogInfo((object)message); } internal static void LogWarning(string message) { _logger.LogWarning((object)message); } internal static void LogError(string message) { _logger.LogError((object)message); } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; _logger = ((BaseUnityPlugin)this).Logger; Configs.InitializeConfigs(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.raddude.moresaveslots"); } } internal class Paginator { internal const int SLOTS_PER_PAGE = 6; private static int _currentPage = 0; private static readonly List _allSlotRoots = new List(); private static BackupSavesListUI _backUpSavesListUI; public static void AddMoreSaveSlots(BackupSavesListUI backupSavesListUI, StartMenuButton[] existingButtons) { _backUpSavesListUI = backupSavesListUI; List list = (from b in existingButtons where (int)b.GetPrivateField("type") == 8 orderby b.saveSlot select b).ToList(); foreach (StartMenuButton item in list) { _allSlotRoots.Add(((Component)((Component)item).transform.parent).gameObject); } for (int i = 6; i < 6 * Configs.numPages.Value; i++) { int index = i % 6; GameObject val = _allSlotRoots[index]; GameObject val2 = Object.Instantiate(val, val.transform.parent); ((Object)val2).name = $"SaveSlot_{i}"; StartMenuButton componentInChildren = val2.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.saveSlot = i; RefreshSlotText(componentInChildren); } val2.SetActive(false); _allSlotRoots.Add(val2); } } private static void RefreshSlotText(StartMenuButton btn) { if (SaveSlots.slotsActive != null && btn.saveSlot < SaveSlots.slotsActive.Length && SaveSlots.slotsActive[btn.saveSlot]) { DateTime lastWriteTime = File.GetLastWriteTime(SaveSlots.GetSlotSavePath(btn.saveSlot)); btn.SetButtonText(lastWriteTime.ToShortTimeString() + "\n" + lastWriteTime.ToShortDateString()); } else { btn.SetButtonText("(empty)"); } } public static bool ButtonClick(StartMenuButtonType button) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 if ((int)button == 13) { _backUpSavesListUI.HideList(); _currentPage = Mathf.Max(0, _currentPage - 1); SaveSlotsUI.UpdatePageNumText(_currentPage); ShowPage(_currentPage); return false; } if ((int)button == 14) { _backUpSavesListUI.HideList(); _currentPage = Mathf.Min(Configs.numPages.Value - 1, _currentPage + 1); SaveSlotsUI.UpdatePageNumText(_currentPage); ShowPage(_currentPage); return false; } return true; } private static void ShowPage(int page) { foreach (GameObject allSlotRoot in _allSlotRoots) { allSlotRoot.SetActive(false); } _currentPage = page; int num = page * 6; int num2 = Mathf.Min(num + 6, 6 * Configs.numPages.Value); for (int i = num; i < num2; i++) { _allSlotRoots[i].SetActive(true); } } } internal class SaveMenuPatches { [HarmonyPatch(typeof(SaveSlots), "Awake")] private class SaveSlotsPatches { public static bool Prefix() { SaveSlots.slotsActive = new bool[6 * Configs.numPages.Value]; for (int i = 0; i < SaveSlots.slotsActive.Length; i++) { SaveSlots.currentSlot = i; if (File.Exists(SaveSlots.GetCurrentSavePath())) { SaveSlots.slotsActive[i] = true; SaveSlots.activeSlotsCount++; } } MSS_Plugin.LogDebug("SaveSlots: activeSlotsCount is " + SaveSlots.activeSlotsCount); return false; } } [HarmonyPatch(typeof(StartMenu))] private class StartMenuPatches { [HarmonyBefore(new string[] { "com.nandbrew.nandtweaks" })] [HarmonyPostfix] [HarmonyPatch("Start")] public static void InitializeSaveSlotsUI(StartMenu __instance, GameObject ___saveSlotUI) { SaveSlotsUI.InitializeUI(___saveSlotUI); Paginator.AddMoreSaveSlots(__instance.backupSavesUI, ___saveSlotUI.GetComponentsInChildren(true)); } [HarmonyPrefix] [HarmonyPatch("ButtonClick", new Type[] { typeof(StartMenuButtonType) })] public static bool ButtonClickPatch(StartMenuButtonType button) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Paginator.ButtonClick(button); } } [HarmonyPatch(typeof(BackupSavesListUI))] private class BackupSavesListUIPatches { [HarmonyPrefix] [HarmonyPatch("ShowList")] public static bool ShowList(StartMenuButton parentSlotButton, BackupSavesListUI __instance, ref int ___showingListFor) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (___showingListFor == parentSlotButton.saveSlot) { return false; } ___showingListFor = parentSlotButton.saveSlot; bool active = false; ((Component)__instance).transform.position = ((Component)parentSlotButton).transform.parent.position; if (parentSlotButton.saveSlot % 6 <= 2) { ((Component)__instance).transform.Translate(Vector3.up * __instance.topRowOffset); } StartMenuButton[] buttons = __instance.buttons; StartMenuButton[] array = buttons; foreach (StartMenuButton val in array) { string backupPath = SaveSlots.GetBackupPath(parentSlotButton.saveSlot, val.saveSlot); if (File.Exists(backupPath)) { active = true; DateTime lastWriteTime = File.GetLastWriteTime(backupPath); string buttonText = lastWriteTime.ToShortTimeString() + "\n" + lastWriteTime.ToShortDateString(); val.SetButtonText(buttonText); ((Component)((Component)val).transform.parent).gameObject.SetActive(true); } else { ((Component)((Component)val).transform.parent).gameObject.SetActive(false); } } ((Component)__instance.list).gameObject.SetActive(active); return false; } [HarmonyPrefix] [HarmonyPatch("Update")] public static bool Update(BackupSavesListUI __instance, int ___showingListFor, Vector3 ___backButtonInitialPos, Vector3 ___backButtonAltPos) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance.currentLookedAtSlot == (Object)null)) { __instance.ShowList(__instance.currentLookedAtSlot); } Vector3 val; if (___showingListFor % 6 == 4 && ((Component)__instance.list).gameObject.activeInHierarchy) { ((Component)SaveSlotsUI.PageNumTextMesh).gameObject.SetActive(false); val = ___backButtonAltPos; } else { ((Component)SaveSlotsUI.PageNumTextMesh).gameObject.SetActive(true); val = ___backButtonInitialPos; } __instance.backButton.parent.localPosition = Vector3.Lerp(__instance.backButton.parent.localPosition, val, Time.deltaTime * 8f); if (___showingListFor % 6 == 1 && ((Component)__instance.list).gameObject.activeInHierarchy) { __instance.chooseSlotText.SetActive(false); } else { __instance.chooseSlotText.SetActive(true); } return false; } } } internal class SaveSlotsUI { internal const StartMenuButtonType PREVIOUS_BUTTON = 13; internal const StartMenuButtonType NEXT_BUTTON = 14; internal static TextMesh PageNumTextMesh { get; private set; } internal static void InitializeUI(GameObject saveSlotUI) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0148: 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_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) saveSlotUI.transform.GetChild(12).localPosition = new Vector3(1.157f, 1.12f, 0.033f); Transform val = Object.Instantiate(saveSlotUI.transform.GetChild(10)); ((Object)val).name = "previous button"; val.SetParent(saveSlotUI.transform); val.localPosition = new Vector3(-1.45f, 0.5f, 0f); val.localEulerAngles = new Vector3(0f, 180f, 0f); val.localScale = new Vector3(1f, 1f, 1f); ((Component)val.GetChild(1)).GetComponent().text = "Previous"; ((Component)val.GetChild(2)).GetComponent().text = "←"; ((Component)val.GetChild(0)).GetComponent().SetPrivateField("type", (object)(StartMenuButtonType)13); Transform val2 = Object.Instantiate(saveSlotUI.transform.GetChild(10)); ((Object)val2).name = "next button"; val2.SetParent(saveSlotUI.transform); val2.localPosition = new Vector3(1.45f, 0.5f, 0f); val2.localEulerAngles = new Vector3(0f, 180f, 0f); val2.localScale = new Vector3(1f, 1f, 1f); ((Component)val2.GetChild(1)).GetComponent().text = "Next"; ((Component)val2.GetChild(2)).GetComponent().text = "→"; ((Component)val2.GetChild(0)).GetComponent().SetPrivateField("type", (object)(StartMenuButtonType)14); Transform val3 = Object.Instantiate(saveSlotUI.transform.GetChild(3)); ((Object)val3).name = "page num text"; val3.SetParent(saveSlotUI.transform); val3.localPosition = new Vector3(0f, 0.5f, 0f); val3.localEulerAngles = new Vector3(0f, 0f, 0f); val3.localScale = new Vector3(0.0165f, 0.0177f, 0.0177f); PageNumTextMesh = ((Component)val3).GetComponent(); PageNumTextMesh.text = "page 1"; } internal static void UpdatePageNumText(int pageNum) { PageNumTextMesh.text = $"page {pageNum + 1}"; } }