Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Automatic Backups v1.3.0
AutomaticBackups_IL2Cpp.dll
Decompiled 3 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using AutomaticBackups; using HarmonyLib; using Il2CppFishNet; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.UI.MainMenu; using Il2CppScheduleOne.UI.Settings; using Il2CppSystem.IO; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "AutomaticBackups", "1.3.0-beta", "coderTrevor", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("AutomaticBackups_IL2Cpp")] [assembly: AssemblyConfiguration("IL2CPP")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+708d6547b1197fe84da47c41d4cfe3ad111b9073")] [assembly: AssemblyProduct("AutomaticBackups_IL2Cpp")] [assembly: AssemblyTitle("AutomaticBackups_IL2Cpp")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } } namespace AutomaticBackups { [RegisterTypeInIl2Cpp] public class AutoSaveToggle : SettingsToggle { public GameObject saveTimeSlider = null; public void Start() { bool value = Core.enableAutoSave.Value; Toggle component = ((Component)this).gameObject.GetComponent<Toggle>(); component.isOn = value; saveTimeSlider.SetActive(value); } public override void OnValueChanged(bool value) { saveTimeSlider.SetActive(value); Core.enableAutoSave.Value = value; } } [RegisterTypeInIl2Cpp] public class AutoSaveTimeSlider : SettingsSlider { protected virtual void OnEnable() { ((SettingsSlider)this).slider.minValue = Core.autoSaveSliderMinTime.Value; ((SettingsSlider)this).slider.maxValue = Core.autoSaveSliderMaxTime.Value; ((SettingsSlider)this).slider.value = Core.autoSaveTime.Value; } public override void OnValueChanged(float value) { ((SettingsSlider)this).timeOnValueChange = Time.time; if (((SettingsSlider)this).DisplayValue) { ((TMP_Text)((SettingsSlider)this).valueLabel).text = ((SettingsSlider)this).GetDisplayValue(value); ((Behaviour)((SettingsSlider)this).valueLabel).enabled = true; } Core.autoSaveTime.Value = (int)value; } } [RegisterTypeInIl2Cpp] public class RetentionAmountSlider : SettingsSlider { protected int multiplier; protected virtual void OnEnable() { multiplier = Core.retentionSliderMultiplier.Value; int num = DivideAndFloor(Core.retentionSliderMinFiles.Value, multiplier); int num2 = DivideAndFloor(Core.retentionSliderMaxFiles.Value, multiplier); int num3 = DivideAndFloor(Core.autoDeleteRetentionCount.Value, multiplier); ((SettingsSlider)this).slider.minValue = num; ((SettingsSlider)this).slider.maxValue = num2; ((SettingsSlider)this).slider.value = num3; } private int DivideAndFloor(int numerator, int denominator) { float num = (float)numerator / (float)denominator; return (int)Mathf.Floor(num); } public override string GetDisplayValue(float value) { return ((int)Mathf.Round(value * (float)multiplier)).ToString(); } public override void OnValueChanged(float value) { ((SettingsSlider)this).timeOnValueChange = Time.time; if (((SettingsSlider)this).DisplayValue) { ((TMP_Text)((SettingsSlider)this).valueLabel).text = ((SettingsSlider)this).GetDisplayValue(value); ((Behaviour)((SettingsSlider)this).valueLabel).enabled = true; } Core.autoDeleteRetentionCount.Value = (int)value * multiplier; } } [HarmonyPatch(typeof(MainMenuScreen), "Awake")] internal static class MainMenuScreenPatch { public static void Prefix(MainMenuScreen __instance) { if (!(((Object)((Component)__instance).gameObject).name != "RestoreBackup")) { __instance.OpenOnStart = true; __instance.PreviousScreen = (MainMenuScreen)(object)ContinueMenuMod.continueScreen; __instance.Group = ((Component)__instance).gameObject.GetComponent<CanvasGroup>(); } } } [RegisterTypeInIl2Cpp] public class ContinueMenuMod : MonoBehaviour { protected Transform continueMenu; protected Transform loadMenu = null; protected MainMenuScreen restoreBackupScreen = null; public static ContinueScreen continueScreen; public ImportScreen importScreen; private void Awake() { Init(); } private void Init() { GameObject gameObject = ((Component)this).gameObject; continueMenu = gameObject.transform.Find("Continue"); continueScreen = ((Component)continueMenu).GetComponent<ContinueScreen>(); Transform val = continueMenu.Find("Container"); for (int i = 0; i < val.childCount; i++) { AddButtonToSlot(val.GetChild(i), i); } importScreen = ((Component)gameObject.transform.Find("ImportScreen")).GetComponent<ImportScreen>(); } private void AddButtonToSlot(Transform slot, int slotNumber) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) Transform val = slot.Find("Container"); Transform val2 = val.Find("Info"); Transform val3 = val2.Find("Export"); Transform val4 = Object.Instantiate<Transform>(val3, val2); ((Object)val4).name = "Restore"; Button component = ((Component)val4).GetComponent<Button>(); ((UnityEventBase)component.onClick).RemoveAllListeners(); ((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)delegate { LoadClicked(slotNumber); })); val4.localPosition = new Vector3(-215f, 20f, 0f); Transform child = val4.GetChild(0); TextMeshProUGUI component2 = ((Component)child).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).text = "Restore"; Object.DestroyImmediate((Object)(object)((Component)val4).GetComponent<SaveExportButton>()); } public void LoadClicked(int slot) { if (!Object.op_Implicit((Object)(object)loadMenu)) { CreateLoadMenu(); } PopulateLoadMenu(slot); restoreBackupScreen.Open(true); } private void CreateLoadMenu() { loadMenu = Object.Instantiate<GameObject>(Core.backupRestorePanelPrefab).transform; Transform val = continueMenu.Find("Background"); Transform val2 = Object.Instantiate<Transform>(val, loadMenu); ((Object)val2).name = "Background"; Transform val3 = continueMenu.Find("Title"); Transform val4 = Object.Instantiate<Transform>(val3, loadMenu); ((Object)val4).name = "Title"; TextMeshProUGUI component = ((Component)val4).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = "Restore Backup"; loadMenu.SetParent(((Component)this).transform, false); ((Object)loadMenu).name = "RestoreBackup"; ((Component)loadMenu).gameObject.SetActive(false); restoreBackupScreen = ((Component)loadMenu).gameObject.AddComponent<MainMenuScreen>(); Transform val5 = loadMenu.Find("Scroll View"); val5.SetAsLastSibling(); } private void PopulateLoadMenu(int slot) { Transform val = loadMenu.Find("Scroll View").Find("Viewport").Find("Content"); SettingsMenuMod.DestroyAllChildren(val); string backupsPath = GetBackupsPath(slot); if (!Directory.Exists(backupsPath)) { Core.Log("No backups folder found at " + backupsPath); return; } DirectoryInfo directoryInfo = new DirectoryInfo(backupsPath); IOrderedEnumerable<FileInfo> orderedEnumerable = from f in directoryInfo.GetFiles("*.zip") orderby Math.Min(f.CreationTimeUtc.Ticks, f.LastWriteTimeUtc.Ticks) select f; foreach (FileInfo file in orderedEnumerable) { Transform val2 = Object.Instantiate<Transform>(Core.backupRestoreButtonPrefab.transform, val); TimeSpan fileAge = DateTime.Now - file.CreationTime; if (file.LastWriteTimeUtc.Ticks < file.CreationTimeUtc.Ticks) { fileAge = DateTime.Now - file.LastWriteTime; } UpdateButtonText(val2, file.Name, fileAge); Button component = ((Component)val2.GetChild(0)).GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)delegate { BackupSelected(file.FullName, slot); })); } } private string GetBackupsPath(int slot) { string backupFolderPath = Singleton<SaveManager>.Instance.BackupFolderPath; return Path.Combine(backupFolderPath, $"SaveGame_{slot + 1}"); } private void UpdateButtonText(Transform buttonParent, string fileName, TimeSpan fileAge) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) Transform child = buttonParent.GetChild(0).GetChild(0); TextMeshProUGUI val = ((Component)child).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val).fontSize = 16f; ((Graphic)val).color = Color.white; ((TMP_Text)val).alignment = (TextAlignmentOptions)513; ((TMP_Text)val).text = " " + fileName; Transform child2 = buttonParent.GetChild(0).GetChild(1); Transform child3 = child2.GetChild(0); TextMeshProUGUI val2 = ((Component)child3).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).fontSize = 14f; ((Graphic)val2).color = Color.white; ((TMP_Text)val2).alignment = (TextAlignmentOptions)513; ((TMP_Text)val2).text = "Created"; Transform child4 = child2.GetChild(1); TextMeshProUGUI val3 = ((Component)child4).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).fontSize = 14f; ((Graphic)val3).color = Color.gray; ((TMP_Text)val3).alignment = (TextAlignmentOptions)513; ((TMP_Text)val3).text = " " + TimeLabel(fileAge); } private string TimeLabel(TimeSpan fileAge) { int num = Mathf.RoundToInt((float)fileAge.TotalHours); int num2 = num / 24; if (num2 == 0) { return num switch { 0 => "In the last hour", 1 => "One hour ago", _ => $"{num} hours ago", }; } if (num2 == 1) { return "Yesterday"; } if (num2 > 365) { return "More than a year ago"; } return num2 + " days ago"; } private void BackupSelected(string fileName, int slot) { if (!string.IsNullOrEmpty(fileName)) { string tempImportPath = SaveImportButton.TempImportPath; if (Directory.Exists(tempImportPath)) { Directory.Delete(tempImportPath, recursive: true); } SaveImportButton.UnzipSaveFile(fileName, tempImportPath); importScreen.Initialize(slot, (MainMenuScreen)(object)((Component)continueMenu).gameObject.GetComponent<ContinueScreen>()); restoreBackupScreen.Close(false); ((MainMenuScreen)importScreen).Open(true); } } } [RegisterTypeInIl2Cpp] public class SettingsMenuMod : MonoBehaviour { private void Awake() { Init(); } private void Init() { GameObject gameObject = ((Component)this).gameObject; Transform settings = gameObject.transform.Find("Settings"); Button modSettingsButton = CreateBackupsButton(settings); GameObject modSettingsPanel = CreateBackupsPanel(settings); AddSettingsCategory(settings, modSettingsButton, modSettingsPanel); } private Button CreateBackupsButton(Transform settings) { Transform val = settings.Find("Buttons"); Transform val2 = val.Find("Controls"); Transform val3 = Object.Instantiate<Transform>(val2, val); ((Object)val3).name = "Backups"; TextMeshProUGUI component = ((Component)((Component)val3).transform.Find("Name")).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = "Backups"; return ((Component)val3).GetComponent<Button>(); } private GameObject CreateBackupsPanel(Transform settings) { Transform val = settings.Find("Content"); Transform val2 = val.Find("Controls"); Transform val3 = Object.Instantiate<Transform>(val2, val); ((Object)val3).name = "Backups"; Transform val4 = SetupRetentionSlider(val3); Transform val5 = SetupDeleteOldestToggle(val3); val5.SetParent((Transform)null); val4.SetParent((Transform)null); DestroyAllChildren(val3); val5.SetParent(val3); val4.SetParent(val3); AddAutoSaveControls(val3, val5, val4); return ((Component)val3).gameObject; } private Transform SetupDeleteOldestToggle(Transform settingsPanel) { Transform val = settingsPanel.Find("InvertY"); ((Object)val).name = "DeleteOldSaves"; HorizontalLayoutGroup val2 = ((Component)val).gameObject.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val2).spacing = 25f; SetLabelText(val, "Limit Backups"); Transform val3 = val.Find("Toggle"); val3.SetAsFirstSibling(); Toggle component = ((Component)val3).GetComponent<Toggle>(); component.isOn = Core.enableAutoDelete.Value; Object.DestroyImmediate((Object)(object)((Component)val3).GetComponent<InvertYToggle>()); ((Component)val3).gameObject.AddComponent<DeleteOldestToggle>(); return val; } private Transform SetupRetentionSlider(Transform settingsPanel) { Transform val = settingsPanel.Find("Sensitivity"); ((Object)val).name = "RetainedCount"; SetLabelText(val, "Max Files Per Save Slot"); Transform val2 = val.Find("Slider"); ((UnityEventBase)((Component)val2).GetComponent<Slider>().onValueChanged).RemoveAllListeners(); Object.DestroyImmediate((Object)(object)((Component)val2).GetComponent<SensitivitySlider>()); ((Component)val2).gameObject.AddComponent<RetentionAmountSlider>(); return val; } private void SetLabelText(Transform parent, string text) { Transform val = parent.Find("Label"); ((Component)val).GetComponent<TMP_Text>().text = text; } public static void DestroyAllChildren(Transform parent) { for (int num = parent.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)parent.GetChild(num)).gameObject); } } private void AddAutoSaveControls(Transform modSettingsPanel, Transform deleteOldestToggle, Transform retentionSlider) { Transform val = Object.Instantiate<Transform>(deleteOldestToggle, modSettingsPanel); Transform val2 = Object.Instantiate<Transform>(retentionSlider, modSettingsPanel); ((Object)val).name = "Enable AutoSave"; ((Object)val2).name = "AutoSave Time"; Object.DestroyImmediate((Object)(object)((Component)val.Find("Toggle")).GetComponent<DeleteOldestToggle>()); Object.DestroyImmediate((Object)(object)((Component)val2.Find("Slider")).GetComponent<RetentionAmountSlider>()); SetLabelText(val, "Enable AutoSave"); SetLabelText(val2, "Minutes Before Saving"); AutoSaveToggle autoSaveToggle = ((Component)val.Find("Toggle")).gameObject.AddComponent<AutoSaveToggle>(); autoSaveToggle.saveTimeSlider = ((Component)val2).gameObject; ((Component)val2.Find("Slider")).gameObject.AddComponent<AutoSaveTimeSlider>(); } private void AddSettingsCategory(Transform settings, Button modSettingsButton, GameObject modSettingsPanel) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown SettingsScreen component = ((Component)settings).GetComponent<SettingsScreen>(); EnlargeCategories(component); SettingsCategory val = new SettingsCategory(); val.Button = modSettingsButton; val.Panel = modSettingsPanel; int num = ((Il2CppArrayBase<SettingsCategory>)(object)component.Categories).Length - 1; ((Il2CppArrayBase<SettingsCategory>)(object)component.Categories)[num] = val; } private void EnlargeCategories(SettingsScreen settingsScreen) { Il2CppReferenceArray<SettingsCategory> categories = settingsScreen.Categories; SettingsCategory[] array = (SettingsCategory[])(object)new SettingsCategory[((Il2CppArrayBase<SettingsCategory>)(object)categories).Length + 1]; for (int i = 0; i < ((Il2CppArrayBase<SettingsCategory>)(object)categories).Length; i++) { array[i] = ((Il2CppArrayBase<SettingsCategory>)(object)categories)[i]; } settingsScreen.Categories = Il2CppReferenceArray<SettingsCategory>.op_Implicit(array); } } [RegisterTypeInIl2Cpp] public class DeleteOldestToggle : SettingsToggle { protected GameObject retainedCountSlider = null; public void Start() { retainedCountSlider = GameObject.Find("RetainedCount"); retainedCountSlider.SetActive(Core.enableAutoDelete.Value); } public override void OnValueChanged(bool value) { retainedCountSlider.SetActive(value); Core.enableAutoDelete.Value = value; } } public class Core : MelonMod { [HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })] private static class SavePatch { public static void Postfix(string saveFolderPath) { string backupPath = GetBackupPath(saveFolderPath); string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string text2 = (autoSaving ? "auto_" : ""); string text3 = Path.Combine(backupPath, text2 + text + ".zip"); SaveExportButton.ZipSaveFolder(saveFolderPath, text3); logger.Msg("Exported\n" + saveFolderPath + " to\n" + text3); AddNewBackupFile(text3); ResetAutoSaveTimer(); } } public static Instance logger; public static MelonPreferences_Entry<bool> enableAutoDelete; public static MelonPreferences_Entry<int> autoDeleteRetentionCount; public const int DEFAULT_RETENTION_COUNT = 125; private MelonPreferences_Category autoDeleteCategory; public static MelonPreferences_Entry<bool> enableAutoSave; public static MelonPreferences_Entry<int> autoSaveTime; public const int DEFAULT_SAVE_TIME = 10; private MelonPreferences_Category autoSaveCategory; public const int RETENTION_SLIDER_MULTIPLIER = 5; public const int RETENTION_SLIDER_MIN_FILES = 25; public const int RETENTION_SLIDER_MAX_FILES = 250; public static MelonPreferences_Entry<int> retentionSliderMultiplier; public static MelonPreferences_Entry<int> retentionSliderMinFiles; public static MelonPreferences_Entry<int> retentionSliderMaxFiles; public const int AUTOSAVE_TIME_MIN_MINUTES = 1; public const int AUTOSAVE_TIME_MAX_MINUTES = 60; public static MelonPreferences_Entry<int> autoSaveSliderMinTime; public static MelonPreferences_Entry<int> autoSaveSliderMaxTime; protected static DirectoryInfo backupDirInfo; protected static Queue<FileInfo> orderedBackups; protected static float timeBeforeAutoSave = 600f; protected static bool autoSaving = false; protected static bool inMainScene = false; public Il2CppAssetBundle assetBundle = null; public static GameObject scrollViewPrefab = null; public static GameObject backupRestorePanelPrefab = null; public static GameObject backupRestoreButtonPrefab = null; public override void OnInitializeMelon() { //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown logger = ((MelonBase)this).LoggerInstance; autoDeleteCategory = MelonPreferences.CreateCategory("AutoDeleteCategory"); enableAutoDelete = autoDeleteCategory.CreateEntry<bool>("EnableAutoDelete", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMultiplier = autoDeleteCategory.CreateEntry<int>("RetentionSliderMultiplier", 5, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMinFiles = autoDeleteCategory.CreateEntry<int>("RetentionSliderMinFiles", 25, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMaxFiles = autoDeleteCategory.CreateEntry<int>("RetentionSliderMaxFiles", 250, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoDeleteRetentionCount = autoDeleteCategory.CreateEntry<int>("AutoDeleteRetentionCount", 125, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveCategory = MelonPreferences.CreateCategory("AutoSaveCategory"); enableAutoSave = autoSaveCategory.CreateEntry<bool>("EnableAutoSave", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveSliderMinTime = autoSaveCategory.CreateEntry<int>("timeSliderMinMinutes", 1, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveSliderMaxTime = autoSaveCategory.CreateEntry<int>("timeSliderMaxMinutes", 60, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveTime = autoSaveCategory.CreateEntry<int>("autoSaveTime", 10, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); Stream manifestResourceStream = ((MelonBase)this).MelonAssembly.Assembly.GetManifestResourceStream("AutomaticBackups.scrollview"); if (manifestResourceStream == null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load scrollview asset bundle!"); return; } byte[] array; using (MemoryStream memoryStream = new MemoryStream()) { manifestResourceStream.CopyTo(memoryStream); array = memoryStream.ToArray(); } Stream val = (Stream)new MemoryStream(Il2CppStructArray<byte>.op_Implicit(array)); assetBundle = Il2CppAssetBundleManager.LoadFromStream(val); if (assetBundle == null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load asset bundle"); return; } manifestResourceStream.Close(); val.Close(); string[] array2 = Il2CppArrayBase<string>.op_Implicit((Il2CppArrayBase<string>)(object)assetBundle.GetAllAssetNames()); try { scrollViewPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/scroll view.prefab"); if ((Object)(object)scrollViewPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/scroll view.prefab"); } backupRestorePanelPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/backuprestore.prefab"); if ((Object)(object)backupRestorePanelPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/backuprestore.prefab"); } backupRestoreButtonPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/backup restore button.prefab"); if ((Object)(object)backupRestoreButtonPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/backup restore button.prefab"); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Msg("Failed to load " + ex.Message); } logger.Msg("Automatic Backups Initialized."); } public static void Log(string msg) { logger.Msg(msg); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { inMainScene = false; if (sceneName == "Menu") { GameObject val = GameObject.Find("MainMenu"); if (Object.op_Implicit((Object)(object)val.GetComponent<SettingsMenuMod>())) { Log("Backups menu has already been created."); return; } val.AddComponent<SettingsMenuMod>(); val.AddComponent<ContinueMenuMod>(); } else if (sceneName == "Main") { ScanBackupsFolder(); ResetAutoSaveTimer(); inMainScene = true; } } protected void ScanBackupsFolder() { string loadedGameFolderPath = Singleton<LoadManager>.Instance.LoadedGameFolderPath; string backupPath = GetBackupPath(loadedGameFolderPath); Log("Backups will be saved to\n" + backupPath); backupDirInfo = new DirectoryInfo(backupPath); IOrderedEnumerable<FileInfo> collection = from f in backupDirInfo.GetFiles("*.zip") orderby Math.Min(f.CreationTimeUtc.Ticks, f.LastWriteTimeUtc.Ticks) select f; orderedBackups = new Queue<FileInfo>(collection); if (orderedBackups.Count > 0) { long num = orderedBackups.Sum((FileInfo f) => f.Length); double value = (double)num / 1048576.0; Log($"{orderedBackups.Count} backup files found, using {value:F1} MB of disk space"); } DeleteExpiredBackups(); } protected static void DeleteExpiredBackups() { if (enableAutoDelete.Value && orderedBackups.Count > autoDeleteRetentionCount.Value) { Log("Number of backup files exceeds user preferences"); while (orderedBackups.Count > autoDeleteRetentionCount.Value) { FileInfo fileInfo = orderedBackups.Dequeue(); Log(fileInfo.Name + " from " + fileInfo.CreationTime.ToShortDateString() + " will be deleted"); fileInfo.Delete(); } } } public static void AddNewBackupFile(string path) { orderedBackups.Enqueue(new FileInfo(path)); DeleteExpiredBackups(); } private static string GetBackupPath(string saveFolderPath) { saveFolderPath = saveFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); string fileName = Path.GetFileName(saveFolderPath); string path = Directory.GetParent(saveFolderPath)?.FullName ?? throw new InvalidOperationException("No parent directory for '" + saveFolderPath + "'."); string text = Path.Combine(path, "Backups", fileName); Directory.CreateDirectory(text); return text; } public override void OnUpdate() { ((MelonBase)this).OnUpdate(); if (enableAutoSave.Value && InstanceFinder.IsHost && inMainScene) { timeBeforeAutoSave -= Time.deltaTime; if (!(timeBeforeAutoSave > 0f)) { Log("Autosaving..."); autoSaving = true; Singleton<SaveManager>.Instance.Save(); } } } private static void ResetAutoSaveTimer() { timeBeforeAutoSave = 60f * (float)autoSaveTime.Value; autoSaving = false; } } }
AutomaticBackups_Mono.dll
Decompiled 3 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using AutomaticBackups; using FishNet; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using ScheduleOne.DevUtilities; using ScheduleOne.Persistence; using ScheduleOne.UI.MainMenu; using ScheduleOne.UI.Settings; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "AutomaticBackups", "1.3.0-beta", "coderTrevor", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("AutomaticBackups_Mono")] [assembly: AssemblyConfiguration("MONO")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+708d6547b1197fe84da47c41d4cfe3ad111b9073")] [assembly: AssemblyProduct("AutomaticBackups_Mono")] [assembly: AssemblyTitle("AutomaticBackups_Mono")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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; } } } namespace AutomaticBackups { [RegisterTypeInIl2Cpp] public class AutoSaveToggle : SettingsToggle { public GameObject saveTimeSlider = null; public void Start() { bool value = Core.enableAutoSave.Value; Toggle component = ((Component)this).gameObject.GetComponent<Toggle>(); component.isOn = value; saveTimeSlider.SetActive(value); } protected override void OnValueChanged(bool value) { saveTimeSlider.SetActive(value); Core.enableAutoSave.Value = value; } } [RegisterTypeInIl2Cpp] public class AutoSaveTimeSlider : SettingsSlider { protected virtual void OnEnable() { base.slider.minValue = Core.autoSaveSliderMinTime.Value; base.slider.maxValue = Core.autoSaveSliderMaxTime.Value; base.slider.value = Core.autoSaveTime.Value; } protected override void OnValueChanged(float value) { base.timeOnValueChange = Time.time; if (base.DisplayValue) { ((TMP_Text)base.valueLabel).text = ((SettingsSlider)this).GetDisplayValue(value); ((Behaviour)base.valueLabel).enabled = true; } Core.autoSaveTime.Value = (int)value; } } [RegisterTypeInIl2Cpp] public class RetentionAmountSlider : SettingsSlider { protected int multiplier; protected virtual void OnEnable() { multiplier = Core.retentionSliderMultiplier.Value; int num = DivideAndFloor(Core.retentionSliderMinFiles.Value, multiplier); int num2 = DivideAndFloor(Core.retentionSliderMaxFiles.Value, multiplier); int num3 = DivideAndFloor(Core.autoDeleteRetentionCount.Value, multiplier); base.slider.minValue = num; base.slider.maxValue = num2; base.slider.value = num3; } private int DivideAndFloor(int numerator, int denominator) { float num = (float)numerator / (float)denominator; return (int)Mathf.Floor(num); } protected override string GetDisplayValue(float value) { return ((int)Mathf.Round(value * (float)multiplier)).ToString(); } protected override void OnValueChanged(float value) { base.timeOnValueChange = Time.time; if (base.DisplayValue) { ((TMP_Text)base.valueLabel).text = ((SettingsSlider)this).GetDisplayValue(value); ((Behaviour)base.valueLabel).enabled = true; } Core.autoDeleteRetentionCount.Value = (int)value * multiplier; } } [HarmonyPatch(typeof(MainMenuScreen), "Awake")] internal static class MainMenuScreenPatch { public static void Prefix(MainMenuScreen __instance) { if (!(((Object)((Component)__instance).gameObject).name != "RestoreBackup")) { __instance.OpenOnStart = true; __instance.PreviousScreen = (MainMenuScreen)(object)ContinueMenuMod.continueScreen; __instance.Group = ((Component)__instance).gameObject.GetComponent<CanvasGroup>(); } } } [RegisterTypeInIl2Cpp] public class ContinueMenuMod : MonoBehaviour { protected Transform continueMenu; protected Transform loadMenu = null; protected MainMenuScreen restoreBackupScreen = null; public static ContinueScreen continueScreen; public ImportScreen importScreen; private void Awake() { Init(); } private void Init() { GameObject gameObject = ((Component)this).gameObject; continueMenu = gameObject.transform.Find("Continue"); continueScreen = ((Component)continueMenu).GetComponent<ContinueScreen>(); Transform val = continueMenu.Find("Container"); for (int i = 0; i < val.childCount; i++) { AddButtonToSlot(val.GetChild(i), i); } importScreen = ((Component)gameObject.transform.Find("ImportScreen")).GetComponent<ImportScreen>(); } private void AddButtonToSlot(Transform slot, int slotNumber) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) Transform val = slot.Find("Container"); Transform val2 = val.Find("Info"); Transform val3 = val2.Find("Export"); Transform val4 = Object.Instantiate<Transform>(val3, val2); ((Object)val4).name = "Restore"; Button component = ((Component)val4).GetComponent<Button>(); ((UnityEventBase)component.onClick).RemoveAllListeners(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { LoadClicked(slotNumber); }); val4.localPosition = new Vector3(-215f, 20f, 0f); Transform child = val4.GetChild(0); TextMeshProUGUI component2 = ((Component)child).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component2).text = "Restore"; Object.DestroyImmediate((Object)(object)((Component)val4).GetComponent<SaveExportButton>()); } public void LoadClicked(int slot) { if (!Object.op_Implicit((Object)(object)loadMenu)) { CreateLoadMenu(); } PopulateLoadMenu(slot); restoreBackupScreen.Open(true); } private void CreateLoadMenu() { loadMenu = Object.Instantiate<GameObject>(Core.backupRestorePanelPrefab).transform; Transform val = continueMenu.Find("Background"); Transform val2 = Object.Instantiate<Transform>(val, loadMenu); ((Object)val2).name = "Background"; Transform val3 = continueMenu.Find("Title"); Transform val4 = Object.Instantiate<Transform>(val3, loadMenu); ((Object)val4).name = "Title"; TextMeshProUGUI component = ((Component)val4).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = "Restore Backup"; loadMenu.SetParent(((Component)this).transform, false); ((Object)loadMenu).name = "RestoreBackup"; ((Component)loadMenu).gameObject.SetActive(false); restoreBackupScreen = ((Component)loadMenu).gameObject.AddComponent<MainMenuScreen>(); Transform val5 = loadMenu.Find("Scroll View"); val5.SetAsLastSibling(); } private void PopulateLoadMenu(int slot) { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown Transform val = loadMenu.Find("Scroll View").Find("Viewport").Find("Content"); SettingsMenuMod.DestroyAllChildren(val); string backupsPath = GetBackupsPath(slot); if (!Directory.Exists(backupsPath)) { Core.Log("No backups folder found at " + backupsPath); return; } DirectoryInfo directoryInfo = new DirectoryInfo(backupsPath); IOrderedEnumerable<FileInfo> orderedEnumerable = from f in directoryInfo.GetFiles("*.zip") orderby Math.Min(f.CreationTimeUtc.Ticks, f.LastWriteTimeUtc.Ticks) select f; foreach (FileInfo file in orderedEnumerable) { Transform val2 = Object.Instantiate<Transform>(Core.backupRestoreButtonPrefab.transform, val); TimeSpan fileAge = DateTime.Now - file.CreationTime; if (file.LastWriteTimeUtc.Ticks < file.CreationTimeUtc.Ticks) { fileAge = DateTime.Now - file.LastWriteTime; } UpdateButtonText(val2, file.Name, fileAge); Button component = ((Component)val2.GetChild(0)).GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { BackupSelected(file.FullName, slot); }); } } private string GetBackupsPath(int slot) { string backupFolderPath = Singleton<SaveManager>.Instance.BackupFolderPath; return Path.Combine(backupFolderPath, $"SaveGame_{slot + 1}"); } private void UpdateButtonText(Transform buttonParent, string fileName, TimeSpan fileAge) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) Transform child = buttonParent.GetChild(0).GetChild(0); TextMeshProUGUI val = ((Component)child).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val).fontSize = 16f; ((Graphic)val).color = Color.white; ((TMP_Text)val).alignment = (TextAlignmentOptions)513; ((TMP_Text)val).text = " " + fileName; Transform child2 = buttonParent.GetChild(0).GetChild(1); Transform child3 = child2.GetChild(0); TextMeshProUGUI val2 = ((Component)child3).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).fontSize = 14f; ((Graphic)val2).color = Color.white; ((TMP_Text)val2).alignment = (TextAlignmentOptions)513; ((TMP_Text)val2).text = "Created"; Transform child4 = child2.GetChild(1); TextMeshProUGUI val3 = ((Component)child4).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val3).fontSize = 14f; ((Graphic)val3).color = Color.gray; ((TMP_Text)val3).alignment = (TextAlignmentOptions)513; ((TMP_Text)val3).text = " " + TimeLabel(fileAge); } private string TimeLabel(TimeSpan fileAge) { int num = Mathf.RoundToInt((float)fileAge.TotalHours); int num2 = num / 24; if (num2 == 0) { return num switch { 0 => "In the last hour", 1 => "One hour ago", _ => $"{num} hours ago", }; } if (num2 == 1) { return "Yesterday"; } if (num2 > 365) { return "More than a year ago"; } return num2 + " days ago"; } private void BackupSelected(string fileName, int slot) { if (!string.IsNullOrEmpty(fileName)) { string tempImportPath = SaveImportButton.TempImportPath; if (Directory.Exists(tempImportPath)) { Directory.Delete(tempImportPath, recursive: true); } SaveImportButton.UnzipSaveFile(fileName, tempImportPath); importScreen.Initialize(slot, (MainMenuScreen)(object)((Component)continueMenu).gameObject.GetComponent<ContinueScreen>()); restoreBackupScreen.Close(false); ((MainMenuScreen)importScreen).Open(true); } } } [RegisterTypeInIl2Cpp] public class SettingsMenuMod : MonoBehaviour { private void Awake() { Init(); } private void Init() { GameObject gameObject = ((Component)this).gameObject; Transform settings = gameObject.transform.Find("Settings"); Button modSettingsButton = CreateBackupsButton(settings); GameObject modSettingsPanel = CreateBackupsPanel(settings); AddSettingsCategory(settings, modSettingsButton, modSettingsPanel); } private Button CreateBackupsButton(Transform settings) { Transform val = settings.Find("Buttons"); Transform val2 = val.Find("Controls"); Transform val3 = Object.Instantiate<Transform>(val2, val); ((Object)val3).name = "Backups"; TextMeshProUGUI component = ((Component)((Component)val3).transform.Find("Name")).GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = "Backups"; return ((Component)val3).GetComponent<Button>(); } private GameObject CreateBackupsPanel(Transform settings) { Transform val = settings.Find("Content"); Transform val2 = val.Find("Controls"); Transform val3 = Object.Instantiate<Transform>(val2, val); ((Object)val3).name = "Backups"; Transform val4 = SetupRetentionSlider(val3); Transform val5 = SetupDeleteOldestToggle(val3); val5.SetParent((Transform)null); val4.SetParent((Transform)null); DestroyAllChildren(val3); val5.SetParent(val3); val4.SetParent(val3); AddAutoSaveControls(val3, val5, val4); return ((Component)val3).gameObject; } private Transform SetupDeleteOldestToggle(Transform settingsPanel) { Transform val = settingsPanel.Find("InvertY"); ((Object)val).name = "DeleteOldSaves"; HorizontalLayoutGroup val2 = ((Component)val).gameObject.AddComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val2).spacing = 25f; SetLabelText(val, "Limit Backups"); Transform val3 = val.Find("Toggle"); val3.SetAsFirstSibling(); Toggle component = ((Component)val3).GetComponent<Toggle>(); component.isOn = Core.enableAutoDelete.Value; Object.DestroyImmediate((Object)(object)((Component)val3).GetComponent<InvertYToggle>()); ((Component)val3).gameObject.AddComponent<DeleteOldestToggle>(); return val; } private Transform SetupRetentionSlider(Transform settingsPanel) { Transform val = settingsPanel.Find("Sensitivity"); ((Object)val).name = "RetainedCount"; SetLabelText(val, "Max Files Per Save Slot"); Transform val2 = val.Find("Slider"); ((UnityEventBase)((Component)val2).GetComponent<Slider>().onValueChanged).RemoveAllListeners(); Object.DestroyImmediate((Object)(object)((Component)val2).GetComponent<SensitivitySlider>()); ((Component)val2).gameObject.AddComponent<RetentionAmountSlider>(); return val; } private void SetLabelText(Transform parent, string text) { Transform val = parent.Find("Label"); ((Component)val).GetComponent<TMP_Text>().text = text; } public static void DestroyAllChildren(Transform parent) { for (int num = parent.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)parent.GetChild(num)).gameObject); } } private void AddAutoSaveControls(Transform modSettingsPanel, Transform deleteOldestToggle, Transform retentionSlider) { Transform val = Object.Instantiate<Transform>(deleteOldestToggle, modSettingsPanel); Transform val2 = Object.Instantiate<Transform>(retentionSlider, modSettingsPanel); ((Object)val).name = "Enable AutoSave"; ((Object)val2).name = "AutoSave Time"; Object.DestroyImmediate((Object)(object)((Component)val.Find("Toggle")).GetComponent<DeleteOldestToggle>()); Object.DestroyImmediate((Object)(object)((Component)val2.Find("Slider")).GetComponent<RetentionAmountSlider>()); SetLabelText(val, "Enable AutoSave"); SetLabelText(val2, "Minutes Before Saving"); AutoSaveToggle autoSaveToggle = ((Component)val.Find("Toggle")).gameObject.AddComponent<AutoSaveToggle>(); autoSaveToggle.saveTimeSlider = ((Component)val2).gameObject; ((Component)val2.Find("Slider")).gameObject.AddComponent<AutoSaveTimeSlider>(); } private void AddSettingsCategory(Transform settings, Button modSettingsButton, GameObject modSettingsPanel) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown SettingsScreen component = ((Component)settings).GetComponent<SettingsScreen>(); EnlargeCategories(component); SettingsCategory val = new SettingsCategory(); val.Button = modSettingsButton; val.Panel = modSettingsPanel; int num = component.Categories.Length - 1; component.Categories[num] = val; } private void EnlargeCategories(SettingsScreen settingsScreen) { SettingsCategory[] categories = settingsScreen.Categories; SettingsCategory[] array = (SettingsCategory[])(object)new SettingsCategory[categories.Length + 1]; for (int i = 0; i < categories.Length; i++) { array[i] = categories[i]; } settingsScreen.Categories = array; } } [RegisterTypeInIl2Cpp] public class DeleteOldestToggle : SettingsToggle { protected GameObject retainedCountSlider = null; public void Start() { retainedCountSlider = GameObject.Find("RetainedCount"); retainedCountSlider.SetActive(Core.enableAutoDelete.Value); } protected override void OnValueChanged(bool value) { retainedCountSlider.SetActive(value); Core.enableAutoDelete.Value = value; } } public class Core : MelonMod { [HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })] private static class SavePatch { public static void Postfix(string saveFolderPath) { string backupPath = GetBackupPath(saveFolderPath); string text = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); string text2 = (autoSaving ? "auto_" : ""); string text3 = Path.Combine(backupPath, text2 + text + ".zip"); SaveExportButton.ZipSaveFolder(saveFolderPath, text3); logger.Msg("Exported\n" + saveFolderPath + " to\n" + text3); AddNewBackupFile(text3); ResetAutoSaveTimer(); } } public static Instance logger; public static MelonPreferences_Entry<bool> enableAutoDelete; public static MelonPreferences_Entry<int> autoDeleteRetentionCount; public const int DEFAULT_RETENTION_COUNT = 125; private MelonPreferences_Category autoDeleteCategory; public static MelonPreferences_Entry<bool> enableAutoSave; public static MelonPreferences_Entry<int> autoSaveTime; public const int DEFAULT_SAVE_TIME = 10; private MelonPreferences_Category autoSaveCategory; public const int RETENTION_SLIDER_MULTIPLIER = 5; public const int RETENTION_SLIDER_MIN_FILES = 25; public const int RETENTION_SLIDER_MAX_FILES = 250; public static MelonPreferences_Entry<int> retentionSliderMultiplier; public static MelonPreferences_Entry<int> retentionSliderMinFiles; public static MelonPreferences_Entry<int> retentionSliderMaxFiles; public const int AUTOSAVE_TIME_MIN_MINUTES = 1; public const int AUTOSAVE_TIME_MAX_MINUTES = 60; public static MelonPreferences_Entry<int> autoSaveSliderMinTime; public static MelonPreferences_Entry<int> autoSaveSliderMaxTime; protected static DirectoryInfo backupDirInfo; protected static Queue<FileInfo> orderedBackups; protected static float timeBeforeAutoSave = 600f; protected static bool autoSaving = false; protected static bool inMainScene = false; public AssetBundle assetBundle = null; public static GameObject scrollViewPrefab = null; public static GameObject backupRestorePanelPrefab = null; public static GameObject backupRestoreButtonPrefab = null; public override void OnInitializeMelon() { logger = ((MelonBase)this).LoggerInstance; autoDeleteCategory = MelonPreferences.CreateCategory("AutoDeleteCategory"); enableAutoDelete = autoDeleteCategory.CreateEntry<bool>("EnableAutoDelete", false, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMultiplier = autoDeleteCategory.CreateEntry<int>("RetentionSliderMultiplier", 5, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMinFiles = autoDeleteCategory.CreateEntry<int>("RetentionSliderMinFiles", 25, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); retentionSliderMaxFiles = autoDeleteCategory.CreateEntry<int>("RetentionSliderMaxFiles", 250, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoDeleteRetentionCount = autoDeleteCategory.CreateEntry<int>("AutoDeleteRetentionCount", 125, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveCategory = MelonPreferences.CreateCategory("AutoSaveCategory"); enableAutoSave = autoSaveCategory.CreateEntry<bool>("EnableAutoSave", true, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveSliderMinTime = autoSaveCategory.CreateEntry<int>("timeSliderMinMinutes", 1, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveSliderMaxTime = autoSaveCategory.CreateEntry<int>("timeSliderMaxMinutes", 60, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); autoSaveTime = autoSaveCategory.CreateEntry<int>("autoSaveTime", 10, (string)null, (string)null, false, false, (ValueValidator)null, (string)null); Stream manifestResourceStream = ((MelonBase)this).MelonAssembly.Assembly.GetManifestResourceStream("AutomaticBackups.scrollview"); if (manifestResourceStream == null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load scrollview asset bundle!"); return; } assetBundle = AssetBundle.LoadFromStream(manifestResourceStream); if ((Object)(object)assetBundle == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load asset bundle"); return; } manifestResourceStream.Close(); string[] allAssetNames = assetBundle.GetAllAssetNames(); try { scrollViewPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/scroll view.prefab"); if ((Object)(object)scrollViewPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/scroll view.prefab"); } backupRestorePanelPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/backuprestore.prefab"); if ((Object)(object)backupRestorePanelPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/backuprestore.prefab"); } backupRestoreButtonPrefab = assetBundle.LoadAsset<GameObject>("assets/assets/trevorsassets/modmenu/prefabs/backup restore button.prefab"); if ((Object)(object)backupRestoreButtonPrefab == (Object)null) { ((MelonBase)this).LoggerInstance.Msg("Failed to load assets/assets/trevorsassets/modmenu/prefabs/backup restore button.prefab"); } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Msg("Failed to load " + ex.Message); } logger.Msg("Automatic Backups Initialized."); } public static void Log(string msg) { logger.Msg(msg); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { inMainScene = false; if (sceneName == "Menu") { GameObject val = GameObject.Find("MainMenu"); if (Object.op_Implicit((Object)(object)val.GetComponent<SettingsMenuMod>())) { Log("Backups menu has already been created."); return; } val.AddComponent<SettingsMenuMod>(); val.AddComponent<ContinueMenuMod>(); } else if (sceneName == "Main") { ScanBackupsFolder(); ResetAutoSaveTimer(); inMainScene = true; } } protected void ScanBackupsFolder() { string loadedGameFolderPath = Singleton<LoadManager>.Instance.LoadedGameFolderPath; string backupPath = GetBackupPath(loadedGameFolderPath); Log("Backups will be saved to\n" + backupPath); backupDirInfo = new DirectoryInfo(backupPath); IOrderedEnumerable<FileInfo> collection = from f in backupDirInfo.GetFiles("*.zip") orderby Math.Min(f.CreationTimeUtc.Ticks, f.LastWriteTimeUtc.Ticks) select f; orderedBackups = new Queue<FileInfo>(collection); if (orderedBackups.Count > 0) { long num = orderedBackups.Sum((FileInfo f) => f.Length); double num2 = (double)num / 1048576.0; Log($"{orderedBackups.Count} backup files found, using {num2:F1} MB of disk space"); } DeleteExpiredBackups(); } protected static void DeleteExpiredBackups() { if (enableAutoDelete.Value && orderedBackups.Count > autoDeleteRetentionCount.Value) { Log("Number of backup files exceeds user preferences"); while (orderedBackups.Count > autoDeleteRetentionCount.Value) { FileInfo fileInfo = orderedBackups.Dequeue(); Log(fileInfo.Name + " from " + fileInfo.CreationTime.ToShortDateString() + " will be deleted"); fileInfo.Delete(); } } } public static void AddNewBackupFile(string path) { orderedBackups.Enqueue(new FileInfo(path)); DeleteExpiredBackups(); } private static string GetBackupPath(string saveFolderPath) { saveFolderPath = saveFolderPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); string fileName = Path.GetFileName(saveFolderPath); string path = Directory.GetParent(saveFolderPath)?.FullName ?? throw new InvalidOperationException("No parent directory for '" + saveFolderPath + "'."); string text = Path.Combine(path, "Backups", fileName); Directory.CreateDirectory(text); return text; } public override void OnUpdate() { ((MelonBase)this).OnUpdate(); if (enableAutoSave.Value && InstanceFinder.IsHost && inMainScene) { timeBeforeAutoSave -= Time.deltaTime; if (!(timeBeforeAutoSave > 0f)) { Log("Autosaving..."); autoSaving = true; Singleton<SaveManager>.Instance.Save(); } } } private static void ResetAutoSaveTimer() { timeBeforeAutoSave = 60f * (float)autoSaveTime.Value; autoSaving = false; } } }