summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/release-notes.md3
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs76
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>