diff options
-rw-r--r-- | docs/release-notes.md | 3 | ||||
-rw-r--r-- | src/SMAPI.Installer/InteractiveInstaller.cs | 76 |
2 files changed, 49 insertions, 30 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index 8b81264a..271609e2 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -13,8 +13,9 @@ * Added update checks for Stardew64Installer if it patched the game. * Added smarter grouping for skipped mods, so it's easier to see root dependencies to update first. * Added error-handling to prevent a crash when the game can't update a map's seasonal tilesheets _(in Error Handler)_. - * On macOS, the `StardewModdingAPI.bin.osx` file is no longer overwritten if it's identical to avoid resetting file permissions (thanks to 007wayne!). + * Added installer option to enter a custom game path even if it detected a game folder. * `*.ico` files are now ignored when scanning for mods. + * Fixed `StardewModdingAPI.bin.osx` on macOS overwritten with an identical file on launch, which resets file permissions (thanks to 007wayne!). * Fixed error for non-English players after returning to title, reloading, and entering town with a completed movie theater. * Fixed `world_clear` console command not removing resource clumps outside the farm and secret woods. * Fixed inconsistent spelling/style for 'macOS'. diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index 6bfc6874..83ecd257 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -258,7 +258,6 @@ namespace StardewModdingApi.Installer ** collect details ****/ // get game path - this.PrintInfo("Where is your game folder?"); DirectoryInfo installDir = this.InteractivelyGetInstallPath(toolkit, context, gamePathArg); if (installDir == null) { @@ -712,49 +711,37 @@ namespace StardewModdingApi.Installer return dir; } - // use game folder which contains the installer, if any - { - DirectoryInfo curPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; - while (curPath?.Parent != null) // must be in a folder (not at the root) - { - if (context.LooksLikeGameFolder(curPath)) - return curPath; - - curPath = curPath.Parent; - } - } - - // use an installed path - DirectoryInfo[] defaultPaths = toolkit.GetGameFolders().ToArray(); + // let user choose detected path + DirectoryInfo[] defaultPaths = this.DetectGameFolders(toolkit, context).ToArray(); if (defaultPaths.Any()) { - // only one path - if (defaultPaths.Length == 1) - return defaultPaths.First(); - - // let user choose path + this.PrintInfo("Where do you want to add or remove SMAPI?"); Console.WriteLine(); - this.PrintInfo("Found multiple copies of the game:"); for (int i = 0; i < defaultPaths.Length; i++) this.PrintInfo($"[{i + 1}] {defaultPaths[i].FullName}"); + this.PrintInfo($"[{defaultPaths.Length + 1}] Enter a custom game path."); Console.WriteLine(); - string[] validOptions = Enumerable.Range(1, defaultPaths.Length).Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray(); - string choice = this.InteractivelyChoose("Where do you want to add/remove SMAPI? Type the number next to your choice, then press enter.", validOptions); + string[] validOptions = Enumerable.Range(1, defaultPaths.Length + 1).Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray(); + string choice = this.InteractivelyChoose("Type the number next to your choice, then press enter.", validOptions); int index = int.Parse(choice, CultureInfo.InvariantCulture) - 1; - return defaultPaths[index]; + + if (index < defaultPaths.Length) + return defaultPaths[index]; } + else + this.PrintInfo("Oops, couldn't find the game automatically."); - // ask user - this.PrintInfo("Oops, couldn't find the game automatically."); + // let user enter manual path while (true) { // get path from user + Console.WriteLine(); this.PrintInfo($"Type the file path to the game directory (the one containing '{context.ExecutableName}'), then press enter."); string path = Console.ReadLine()?.Trim(); if (string.IsNullOrWhiteSpace(path)) { - this.PrintInfo(" You must specify a directory path to continue."); + this.PrintWarning("You must specify a directory path to continue."); continue; } @@ -776,12 +763,12 @@ namespace StardewModdingApi.Installer // validate path if (!directory.Exists) { - this.PrintInfo(" That directory doesn't seem to exist."); + this.PrintWarning("That directory doesn't seem to exist."); continue; } if (!context.LooksLikeGameFolder(directory)) { - this.PrintInfo(" That directory doesn't contain a Stardew Valley executable."); + this.PrintWarning("That directory doesn't contain a Stardew Valley executable."); continue; } @@ -791,6 +778,37 @@ namespace StardewModdingApi.Installer } } + /// <summary>Get the possible game paths to update.</summary> + /// <param name="toolkit">The mod toolkit.</param> + /// <param name="context">The installer context.</param> + private IEnumerable<DirectoryInfo> DetectGameFolders(ModToolkit toolkit, InstallerContext context) + { + HashSet<string> foundPaths = new HashSet<string>(); + + // game folder which contains the installer, if any + { + DirectoryInfo curPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; + while (curPath?.Parent != null) // must be in a folder (not at the root) + { + if (context.LooksLikeGameFolder(curPath)) + { + foundPaths.Add(curPath.FullName); + yield return curPath; + break; + } + + curPath = curPath.Parent; + } + } + + // game paths detected by toolkit + foreach (DirectoryInfo dir in toolkit.GetGameFolders()) + { + if (foundPaths.Add(dir.FullName)) + yield return dir; + } + } + /// <summary>Interactively move mods out of the appdata directory.</summary> /// <param name="properModsDir">The directory which should contain all mods.</param> /// <param name="packagedModsDir">The installer directory containing packaged mods.</param> |