diff --git a/ModAssistant/Localisation/en-DEBUG.xaml b/ModAssistant/Localisation/en-DEBUG.xaml index 44e62dde..0e749032 100644 --- a/ModAssistant/Localisation/en-DEBUG.xaml +++ b/ModAssistant/Localisation/en-DEBUG.xaml @@ -60,6 +60,12 @@ Mods:UninstallButton Mods:LoadFailed Mods:CheckingInstalledMods + Mods:GameUpdatedPrompt:Title + Mods:GameUpdatedPrompt:OkCancel + Mods:FailedToSelect:Title + Mods:FailedToSelect:Body1 + Mods:FailedToSelect:Body2 + Mods:CheckingPreviousMods Mods:LoadingMods Mods:FinishedLoadingMods Mods:NoMods diff --git a/ModAssistant/Localisation/en.xaml b/ModAssistant/Localisation/en.xaml index 60898af1..f789b278 100644 --- a/ModAssistant/Localisation/en.xaml +++ b/ModAssistant/Localisation/en.xaml @@ -91,6 +91,12 @@ Uninstall Could not load mods list Checking installed mods + Reselect mods? + The game has updated since you last modded, would you like to automatically re-select the mods you had installed previously? + Failed to select {0} mods + The following mods could not be found and weren't selected: + These mods might not be updated for the current game version. + Checking previously installed mods Loading Mods Finished loading mods No mods available for this version of Beat Saber diff --git a/ModAssistant/Pages/Mods.xaml.cs b/ModAssistant/Pages/Mods.xaml.cs index 29d6c96d..bea7fddd 100644 --- a/ModAssistant/Pages/Mods.xaml.cs +++ b/ModAssistant/Pages/Mods.xaml.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -30,6 +31,7 @@ public sealed partial class Mods : Page public static List InstalledMods = new List(); public static List ManifestsToMatch = new List(); public List CategoryNames = new List(); + public static List MissingOldMods = new List(); public CollectionView view; public bool PendingChanges; @@ -124,6 +126,31 @@ public async Task LoadMods() DescriptionColumn.Width = 800; } + string lastModdedVersion = CheckPreviousInstallDirs(); + if (lastModdedVersion != null && + !DirectoryContainsMods(Path.Combine(App.BeatSaberInstallDirectory, "Plugins")) && + !DirectoryContainsMods(Path.Combine(App.BeatSaberInstallDirectory, "IPA/Pending/Plugins"))) + { + string body = (string)FindResource("Mods:GameUpdatedPrompt:OkCancel"); + string title = (string)FindResource("Mods:GameUpdatedPrompt:Title"); + + var reinstallMods = System.Windows.Forms.MessageBox.Show(body, title, MessageBoxButtons.OKCancel) == DialogResult.OK; + if (reinstallMods) { + MainWindow.Instance.MainText = $"{FindResource("Mods:CheckingPreviousMods")}..."; + await Task.Run(async () => await SelectPreviousMods(lastModdedVersion)); + InstalledColumn.Width = double.NaN; + UninstallColumn.Width = 70; + DescriptionColumn.Width = 750; + if (MissingOldMods.Count > 0) { + string notSelectedTitle = string.Format((string)FindResource("Mods:FailedToSelect:Title"), MissingOldMods.Count); + string notSelectedBody1 = (string)FindResource("Mods:FailedToSelect:Body1"); + string notSelectedBody2 = (string)FindResource("Mods:FailedToSelect:Body2"); + + System.Windows.Forms.MessageBox.Show($"{notSelectedBody1}\n{string.Join(",\n", MissingOldMods)}\n\n{notSelectedBody2}", notSelectedTitle, MessageBoxButtons.OK); + } + } + } + MainWindow.Instance.MainText = $"{FindResource("Mods:LoadingMods")}..."; await Task.Run(async () => await PopulateModsList()); @@ -190,6 +217,15 @@ public async Task CheckInstalledMods() CheckInstallDir("Libs"); } + public async Task SelectPreviousMods(string previousModsDirectory) + { + if(AllModsList == null) await GetAllMods(); + + CheckInstallDir(previousModsDirectory, true); + CheckInstallDir("Libs"); + //CheckPreviousInstallDirs(); + } + public async Task GetAllMods() { var resp = await HttpClient.GetAsync(Utils.Constants.BeatModsAPIUrl + "mod"); @@ -206,7 +242,52 @@ public async Task GetAllMods() } } - private void CheckInstallDir(string directory) + private SemVersion SemverForPluginFolder(string directory) + { + string versionString = directory.Substring(directory.LastIndexOf("Old") + 3, directory.Length - directory.LastIndexOf("Plugins")).Trim(); + SemVersion semver; + if (!SemVersion.TryParse(versionString, out semver)) return null; + return semver; + } + + private int CompareInstallDirs(string a, string b) + { + SemVersion semverA = SemverForPluginFolder(a); + SemVersion semverB = SemverForPluginFolder(b); + if (semverA == null) return 1; + else if(semverB == null) return 0; + + return semverB.CompareTo(semverA); + } + + private bool DirectoryContainsMods(string directory) + { + if (Directory.Exists(directory)) + { + foreach (string file in Directory.GetFileSystemEntries(Path.Combine(App.BeatSaberInstallDirectory, directory))) + { + string fileExtension = Path.GetExtension(file); + + if (File.Exists(file) && (fileExtension == ".dll" || fileExtension == ".manifest")) return true; + } + } + return false; + } + + private string CheckPreviousInstallDirs() + { + if (!Directory.Exists(App.BeatSaberInstallDirectory)) return null; + + string[] directories = Directory.GetDirectories(App.BeatSaberInstallDirectory, "Old*Plugins"); + if (directories.Length == 0) return null; + Array.Sort(directories, CompareInstallDirs); + + foreach(string directory in directories) if (DirectoryContainsMods(directory)) return directory; + + return null; + } + + private void CheckInstallDir(string directory, bool setFailedMods = false) { if (!Directory.Exists(Path.Combine(App.BeatSaberInstallDirectory, directory))) { @@ -241,6 +322,12 @@ private void CheckInstallDir(string directory) AddDetectedMod(mod); } } + else if (setFailedMods) + { + string fileName = Path.GetFileNameWithoutExtension(file); + if (!MissingOldMods.Contains(fileName)) MissingOldMods.Add(fileName); + // maybe hook into the manifest to get the actual name. too lazy for now. + } } } } @@ -274,7 +361,7 @@ private void AddDetectedMod(Mod mod) if (!InstalledMods.Contains(mod)) { InstalledMods.Add(mod); - if (App.SelectInstalledMods && !DefaultMods.Contains(mod.name)) + if (!DefaultMods.Contains(mod.name)) { DefaultMods.Add(mod.name); }