summaryrefslogtreecommitdiff
path: root/src/SMAPI.Installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Installer')
-rw-r--r--src/SMAPI.Installer/Framework/InstallerPaths.cs61
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs463
-rw-r--r--src/SMAPI.Installer/StardewModdingAPI.Installer.csproj1
3 files changed, 362 insertions, 163 deletions
diff --git a/src/SMAPI.Installer/Framework/InstallerPaths.cs b/src/SMAPI.Installer/Framework/InstallerPaths.cs
new file mode 100644
index 00000000..d212876a
--- /dev/null
+++ b/src/SMAPI.Installer/Framework/InstallerPaths.cs
@@ -0,0 +1,61 @@
+using System.IO;
+
+namespace StardewModdingAPI.Installer.Framework
+{
+ /// <summary>Manages paths for the SMAPI installer.</summary>
+ internal class InstallerPaths
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The directory containing the installer files for the current platform.</summary>
+ public DirectoryInfo PackageDir { get; }
+
+ /// <summary>The directory containing the installed game.</summary>
+ public DirectoryInfo GameDir { get; }
+
+ /// <summary>The directory into which to install mods.</summary>
+ public DirectoryInfo ModsDir { get; }
+
+ /// <summary>The full path to the directory containing the installer files for the current platform.</summary>
+ public string PackagePath => this.PackageDir.FullName;
+
+ /// <summary>The full path to the directory containing the installed game.</summary>
+ public string GamePath => this.GameDir.FullName;
+
+ /// <summary>The full path to the directory into which to install mods.</summary>
+ public string ModsPath => this.ModsDir.FullName;
+
+ /// <summary>The full path to the installed SMAPI executable file.</summary>
+ public string ExecutablePath { get; }
+
+ /// <summary>The full path to the vanilla game launcher on Linux/Mac.</summary>
+ public string UnixLauncherPath { get; }
+
+ /// <summary>The full path to the installed SMAPI launcher on Linux/Mac before it's renamed.</summary>
+ public string UnixSmapiLauncherPath { get; }
+
+ /// <summary>The full path to the vanilla game launcher on Linux/Mac after SMAPI is installed.</summary>
+ public string UnixBackupLauncherPath { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="packageDir">The directory path containing the installer files for the current platform.</param>
+ /// <param name="gameDir">The directory path for the installed game.</param>
+ /// <param name="gameExecutableName">The name of the game's executable file for the current platform.</param>
+ public InstallerPaths(DirectoryInfo packageDir, DirectoryInfo gameDir, string gameExecutableName)
+ {
+ this.PackageDir = packageDir;
+ this.GameDir = gameDir;
+ this.ModsDir = new DirectoryInfo(Path.Combine(gameDir.FullName, "Mods"));
+
+ this.ExecutablePath = Path.Combine(gameDir.FullName, gameExecutableName);
+ this.UnixLauncherPath = Path.Combine(gameDir.FullName, "StardewValley");
+ this.UnixSmapiLauncherPath = Path.Combine(gameDir.FullName, "StardewModdingAPI");
+ this.UnixBackupLauncherPath = Path.Combine(gameDir.FullName, "StardewValley-original");
+ }
+ }
+}
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs
index f39486e1..0aac1da2 100644
--- a/src/SMAPI.Installer/InteractiveInstaller.cs
+++ b/src/SMAPI.Installer/InteractiveInstaller.cs
@@ -7,6 +7,7 @@ using System.Reflection;
using System.Threading;
using Microsoft.Win32;
using StardewModdingApi.Installer.Enums;
+using StardewModdingAPI.Installer.Framework;
using StardewModdingAPI.Internal;
using StardewModdingAPI.Internal.ConsoleWriting;
@@ -135,7 +136,7 @@ namespace StardewModdingApi.Installer
}
/// <summary>Handles writing color-coded text to the console.</summary>
- private readonly ColorfulConsoleWriter ConsoleWriter;
+ private ColorfulConsoleWriter ConsoleWriter;
/*********
@@ -168,6 +169,9 @@ namespace StardewModdingApi.Installer
/// </remarks>
public void Run(string[] args)
{
+ /*********
+ ** Step 1: initial setup
+ *********/
/****
** Get platform & set window title
****/
@@ -175,6 +179,9 @@ namespace StardewModdingApi.Installer
Console.Title = $"SMAPI {this.GetDisplayVersion(this.GetType().Assembly.GetName().Version)} installer on {platform} {EnvironmentUtility.GetFriendlyPlatformName(platform)}";
Console.WriteLine();
+ /****
+ ** Check if correct installer
+ ****/
#if SMAPI_FOR_WINDOWS
if (platform == Platform.Linux || platform == Platform.Mac)
{
@@ -182,78 +189,17 @@ namespace StardewModdingApi.Installer
Console.ReadLine();
return;
}
-#endif
-
- /****
- ** read command-line arguments
- ****/
- // get action from CLI
- bool installArg = args.Contains("--install");
- bool uninstallArg = args.Contains("--uninstall");
- if (installArg && uninstallArg)
- {
- this.PrintError("You can't specify both --install and --uninstall command-line flags.");
- Console.ReadLine();
- return;
- }
-
- // get game path from CLI
- string gamePathArg = null;
- {
- int pathIndex = Array.LastIndexOf(args, "--game-path") + 1;
- if (pathIndex >= 1 && args.Length >= pathIndex)
- gamePathArg = args[pathIndex];
- }
-
- /****
- ** collect details
- ****/
- // get game path
- DirectoryInfo installDir = this.InteractivelyGetInstallPath(platform, gamePathArg);
- if (installDir == null)
- {
- this.PrintError("Failed finding your game path.");
- Console.ReadLine();
- return;
- }
-
- // get folders
- DirectoryInfo packageDir = platform.IsMono()
- ? new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) // installer runs from internal folder on Mono
- : new DirectoryInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "internal", "Windows"));
- DirectoryInfo modsDir = new DirectoryInfo(Path.Combine(installDir.FullName, "Mods"));
- var paths = new
- {
- executable = Path.Combine(installDir.FullName, EnvironmentUtility.GetExecutableName(platform)),
- unixSmapiLauncher = Path.Combine(installDir.FullName, "StardewModdingAPI"),
- unixLauncher = Path.Combine(installDir.FullName, "StardewValley"),
- unixLauncherBackup = Path.Combine(installDir.FullName, "StardewValley-original")
- };
-
- // show output
- this.PrintInfo($"Your game folder: {installDir}.");
-
- /****
- ** validate assumptions
- ****/
- if (!packageDir.Exists)
- {
- this.PrintError(platform == Platform.Windows && packageDir.FullName.Contains(Path.GetTempPath()) && packageDir.FullName.Contains(".zip")
- ? "The installer is missing some files. It looks like you're running the installer from inside the downloaded zip; make sure you unzip the downloaded file first, then run the installer from the unzipped folder."
- : $"The 'internal/{packageDir.Name}' package folder is missing (should be at {packageDir})."
- );
- Console.ReadLine();
- return;
- }
- if (!File.Exists(paths.executable))
+#else
+ if (platform == Platform.Windows)
{
- this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
+ this.PrintError($"This is the installer for Linux/Mac. Run the 'install on Windows.exe' file instead.");
Console.ReadLine();
return;
}
+#endif
/****
- ** validate Windows dependencies
+ ** Check Windows dependencies
****/
if (platform == Platform.Windows)
{
@@ -276,159 +222,324 @@ namespace StardewModdingApi.Installer
}
}
- Console.WriteLine();
-
/****
- ** ask user what to do
+ ** read command-line arguments
****/
- ScriptAction action;
+ // get action from CLI
+ bool installArg = args.Contains("--install");
+ bool uninstallArg = args.Contains("--uninstall");
+ if (installArg && uninstallArg)
+ {
+ this.PrintError("You can't specify both --install and --uninstall command-line flags.");
+ Console.ReadLine();
+ return;
+ }
- if (installArg)
- action = ScriptAction.Install;
- else if (uninstallArg)
- action = ScriptAction.Uninstall;
- else
+ // get game path from CLI
+ string gamePathArg = null;
+ {
+ int pathIndex = Array.LastIndexOf(args, "--game-path") + 1;
+ if (pathIndex >= 1 && args.Length >= pathIndex)
+ gamePathArg = args[pathIndex];
+ }
+
+
+ /*********
+ ** Step 2: choose a theme (can't auto-detect on Linux/Mac)
+ *********/
+ MonitorColorScheme scheme = MonitorColorScheme.AutoDetect;
+ if (platform == Platform.Linux || platform == Platform.Mac)
{
- this.PrintInfo("You can....");
- this.PrintInfo("[1] Install SMAPI.");
- this.PrintInfo("[2] Uninstall SMAPI.");
+ /****
+ ** print header
+ ****/
+ this.PrintPlain("Hi there! I'll help you install or remove SMAPI. Just a few questions first.");
+ this.PrintPlain("----------------------------------------------------------------------------");
+ Console.WriteLine();
+
+ /****
+ ** show theme selector
+ ****/
+ // get theme writers
+ var lightBackgroundWriter = new ColorfulConsoleWriter(EnvironmentUtility.DetectPlatform(), MonitorColorScheme.LightBackground);
+ var darkDarkgroundWriter = new ColorfulConsoleWriter(EnvironmentUtility.DetectPlatform(), MonitorColorScheme.DarkBackground);
+
+ // print question
+ this.PrintPlain("Which text looks more readable?");
+ Console.WriteLine();
+ Console.Write(" [1] ");
+ lightBackgroundWriter.WriteLine("Dark text on light background", ConsoleLogLevel.Info);
+ Console.Write(" [2] ");
+ darkDarkgroundWriter.WriteLine("Light text on dark background", ConsoleLogLevel.Info);
Console.WriteLine();
- string choice = this.InteractivelyChoose("What do you want to do? Type 1 or 2, then press enter.", "1", "2");
+ // handle choice
+ string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" });
switch (choice)
{
case "1":
- action = ScriptAction.Install;
+ scheme = MonitorColorScheme.LightBackground;
+ this.ConsoleWriter = lightBackgroundWriter;
break;
case "2":
- action = ScriptAction.Uninstall;
+ scheme = MonitorColorScheme.DarkBackground;
+ this.ConsoleWriter = darkDarkgroundWriter;
break;
default:
throw new InvalidOperationException($"Unexpected action key '{choice}'.");
}
- Console.WriteLine();
}
+ Console.Clear();
- /****
- ** Always uninstall old files
- ****/
- // restore game launcher
- if (platform.IsMono() && File.Exists(paths.unixLauncherBackup))
- {
- this.PrintDebug("Removing SMAPI launcher...");
- this.InteractivelyDelete(paths.unixLauncher);
- File.Move(paths.unixLauncherBackup, paths.unixLauncher);
- }
- // remove old files
- string[] removePaths = this.GetUninstallPaths(installDir, modsDir)
- .Where(path => Directory.Exists(path) || File.Exists(path))
- .ToArray();
- if (removePaths.Any())
+ /*********
+ ** Step 3: find game folder
+ *********/
+ InstallerPaths paths;
{
- this.PrintDebug(action == ScriptAction.Install ? "Removing previous SMAPI files..." : "Removing SMAPI files...");
- foreach (string path in removePaths)
- this.InteractivelyDelete(path);
+ /****
+ ** print header
+ ****/
+ this.PrintInfo("Hi there! I'll help you install or remove SMAPI. Just a few questions first.");
+ this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
+ this.PrintDebug("----------------------------------------------------------------------------");
+ Console.WriteLine();
+
+ /****
+ ** collect details
+ ****/
+ // get game path
+ this.PrintInfo("Where is your game folder?");
+ DirectoryInfo installDir = this.InteractivelyGetInstallPath(platform, gamePathArg);
+ if (installDir == null)
+ {
+ this.PrintError("Failed finding your game path.");
+ Console.ReadLine();
+ return;
+ }
+
+ // get folders
+ DirectoryInfo packageDir = platform.IsMono()
+ ? new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)) // installer runs from internal folder on Mono
+ : new DirectoryInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "internal", "Windows"));
+ paths = new InstallerPaths(packageDir, installDir, EnvironmentUtility.GetExecutableName(platform));
}
+ Console.Clear();
- /****
- ** Install new files
- ****/
- if (action == ScriptAction.Install)
+
+ /*********
+ ** Step 4: validate assumptions
+ *********/
{
- // copy SMAPI files to game dir
- this.PrintDebug("Adding SMAPI files...");
- foreach (FileInfo sourceFile in packageDir.EnumerateFiles().Where(this.ShouldCopyFile))
+ if (!paths.PackageDir.Exists)
{
- if (sourceFile.Name == this.InstallerFileName)
- continue;
+ this.PrintError(platform == Platform.Windows && paths.PackagePath.Contains(Path.GetTempPath()) && paths.PackagePath.Contains(".zip")
+ ? "The installer is missing some files. It looks like you're running the installer from inside the downloaded zip; make sure you unzip the downloaded file first, then run the installer from the unzipped folder."
+ : $"The 'internal/{paths.PackageDir.Name}' package folder is missing (should be at {paths.PackagePath})."
+ );
+ Console.ReadLine();
+ return;
+ }
- string targetPath = Path.Combine(installDir.FullName, sourceFile.Name);
- this.InteractivelyDelete(targetPath);
- sourceFile.CopyTo(targetPath);
+ if (!File.Exists(paths.ExecutablePath))
+ {
+ this.PrintError("The detected game install path doesn't contain a Stardew Valley executable.");
+ Console.ReadLine();
+ return;
}
+ }
+
+
+ /*********
+ ** Step 5: ask what to do
+ *********/
+ ScriptAction action;
+ {
+ /****
+ ** print header
+ ****/
+ this.PrintInfo("Hi there! I'll help you install or remove SMAPI. Just one question first.");
+ this.PrintDebug($"Game path: {paths.GamePath}");
+ this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
+ this.PrintDebug("----------------------------------------------------------------------------");
+ Console.WriteLine();
- // replace mod launcher (if possible)
- if (platform.IsMono())
+ /****
+ ** ask what to do
+ ****/
+ if (installArg)
+ action = ScriptAction.Install;
+ else if (uninstallArg)
+ action = ScriptAction.Uninstall;
+ else
{
- this.PrintDebug("Safely replacing game launcher...");
- if (File.Exists(paths.unixLauncher))
+ this.PrintInfo("What do you want to do?");
+ Console.WriteLine();
+ this.PrintInfo("[1] Install SMAPI.");
+ this.PrintInfo("[2] Uninstall SMAPI.");
+ Console.WriteLine();
+
+ string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" });
+ switch (choice)
{
- if (!File.Exists(paths.unixLauncherBackup))
- File.Move(paths.unixLauncher, paths.unixLauncherBackup);
- else
- this.InteractivelyDelete(paths.unixLauncher);
+ case "1":
+ action = ScriptAction.Install;
+ break;
+ case "2":
+ action = ScriptAction.Uninstall;
+ break;
+ default:
+ throw new InvalidOperationException($"Unexpected action key '{choice}'.");
}
+ }
+ }
+ Console.Clear();
+
- File.Move(paths.unixSmapiLauncher, paths.unixLauncher);
+ /*********
+ ** Step 6: apply
+ *********/
+ {
+ /****
+ ** print header
+ ****/
+ this.PrintInfo($"That's all I need! I'll {action.ToString().ToLower()} SMAPI now.");
+ this.PrintDebug($"Game path: {paths.GamePath}");
+ this.PrintDebug($"Color scheme: {this.GetDisplayText(scheme)}");
+ this.PrintDebug("----------------------------------------------------------------------------");
+ Console.WriteLine();
+
+ /****
+ ** Always uninstall old files
+ ****/
+ // restore game launcher
+ if (platform.IsMono() && File.Exists(paths.UnixBackupLauncherPath))
+ {
+ this.PrintDebug("Removing SMAPI launcher...");
+ this.InteractivelyDelete(paths.UnixLauncherPath);
+ File.Move(paths.UnixBackupLauncherPath, paths.UnixLauncherPath);
}
- // create mods directory (if needed)
- if (!modsDir.Exists)
+ // remove old files
+ string[] removePaths = this.GetUninstallPaths(paths.GameDir, paths.ModsDir)
+ .Where(path => Directory.Exists(path) || File.Exists(path))
+ .ToArray();
+ if (removePaths.Any())
{
- this.PrintDebug("Creating mods directory...");
- modsDir.Create();
+ this.PrintDebug(action == ScriptAction.Install ? "Removing previous SMAPI files..." : "Removing SMAPI files...");
+ foreach (string path in removePaths)
+ this.InteractivelyDelete(path);
}
- // add or replace bundled mods
- modsDir.Create();
- DirectoryInfo packagedModsDir = new DirectoryInfo(Path.Combine(packageDir.FullName, "Mods"));
- if (packagedModsDir.Exists && packagedModsDir.EnumerateDirectories().Any())
+ /****
+ ** Install new files
+ ****/
+ if (action == ScriptAction.Install)
{
- this.PrintDebug("Adding bundled mods...");
+ // copy SMAPI files to game dir
+ this.PrintDebug("Adding SMAPI files...");
+ foreach (FileInfo sourceFile in paths.PackageDir.EnumerateFiles().Where(this.ShouldCopyFile))
+ {
+ if (sourceFile.Name == this.InstallerFileName)
+ continue;
+
+ string targetPath = Path.Combine(paths.GameDir.FullName, sourceFile.Name);
+ this.InteractivelyDelete(targetPath);
+ sourceFile.CopyTo(targetPath);
+ }
- // special case: rename Omegasis' SaveBackup mod
+ // replace mod launcher (if possible)
+ if (platform.IsMono())
{
- DirectoryInfo oldFolder = new DirectoryInfo(Path.Combine(modsDir.FullName, "SaveBackup"));
- DirectoryInfo newFolder = new DirectoryInfo(Path.Combine(modsDir.FullName, "AdvancedSaveBackup"));
- FileInfo manifest = new FileInfo(Path.Combine(oldFolder.FullName, "manifest.json"));
- if (manifest.Exists && !newFolder.Exists && File.ReadLines(manifest.FullName).Any(p => p.IndexOf("Omegasis", StringComparison.InvariantCultureIgnoreCase) != -1))
+ this.PrintDebug("Safely replacing game launcher...");
+ if (File.Exists(paths.UnixLauncherPath))
{
- this.PrintDebug($" moving {oldFolder.Name} to {newFolder.Name}...");
- this.Move(oldFolder, newFolder.FullName);
+ if (!File.Exists(paths.UnixBackupLauncherPath))
+ File.Move(paths.UnixLauncherPath, paths.UnixBackupLauncherPath);
+ else
+ this.InteractivelyDelete(paths.UnixLauncherPath);
}
+
+ File.Move(paths.UnixSmapiLauncherPath, paths.UnixLauncherPath);
}
- // add bundled mods
- foreach (DirectoryInfo sourceDir in packagedModsDir.EnumerateDirectories())
+ // create mods directory (if needed)
+ if (!paths.ModsDir.Exists)
{
- this.PrintDebug($" adding {sourceDir.Name}...");
+ this.PrintDebug("Creating mods directory...");
+ paths.ModsDir.Create();
+ }
+
+ // add or replace bundled mods
+ DirectoryInfo packagedModsDir = new DirectoryInfo(Path.Combine(paths.PackageDir.FullName, "Mods"));
+ if (packagedModsDir.Exists && packagedModsDir.EnumerateDirectories().Any())
+ {
+ this.PrintDebug("Adding bundled mods...");
- // init/clear target dir
- DirectoryInfo targetDir = new DirectoryInfo(Path.Combine(modsDir.FullName, sourceDir.Name));
- if (targetDir.Exists)
+ // special case: rename Omegasis' SaveBackup mod
{
- this.ProtectBundledFiles.TryGetValue(targetDir.Name, out HashSet<string> protectedFiles);
- foreach (FileSystemInfo entry in targetDir.EnumerateFileSystemInfos())
+ DirectoryInfo oldFolder = new DirectoryInfo(Path.Combine(paths.ModsDir.FullName, "SaveBackup"));
+ DirectoryInfo newFolder = new DirectoryInfo(Path.Combine(paths.ModsDir.FullName, "AdvancedSaveBackup"));
+ FileInfo manifest = new FileInfo(Path.Combine(oldFolder.FullName, "manifest.json"));
+ if (manifest.Exists && !newFolder.Exists && File.ReadLines(manifest.FullName).Any(p => p.IndexOf("Omegasis", StringComparison.InvariantCultureIgnoreCase) != -1))
{
- if (protectedFiles == null || !protectedFiles.Contains(entry.Name))
- this.InteractivelyDelete(entry.FullName);
+ this.PrintDebug($" moving {oldFolder.Name} to {newFolder.Name}...");
+ this.Move(oldFolder, newFolder.FullName);
}
}
- else
- targetDir.Create();
- // copy files
- foreach (FileInfo sourceFile in sourceDir.EnumerateFiles().Where(this.ShouldCopyFile))
- sourceFile.CopyTo(Path.Combine(targetDir.FullName, sourceFile.Name));
+ // add bundled mods
+ foreach (DirectoryInfo sourceDir in packagedModsDir.EnumerateDirectories())
+ {
+ this.PrintDebug($" adding {sourceDir.Name}...");
+
+ // init/clear target dir
+ DirectoryInfo targetDir = new DirectoryInfo(Path.Combine(paths.ModsDir.FullName, sourceDir.Name));
+ if (targetDir.Exists)
+ {
+ this.ProtectBundledFiles.TryGetValue(targetDir.Name, out HashSet<string> protectedFiles);
+ foreach (FileSystemInfo entry in targetDir.EnumerateFileSystemInfos())
+ {
+ if (protectedFiles == null || !protectedFiles.Contains(entry.Name))
+ this.InteractivelyDelete(entry.FullName);
+ }
+ }
+ else
+ targetDir.Create();
+
+ // copy files
+ foreach (FileInfo sourceFile in sourceDir.EnumerateFiles().Where(this.ShouldCopyFile))
+ sourceFile.CopyTo(Path.Combine(targetDir.FullName, sourceFile.Name));
+ }
+
+ // set SMAPI's color scheme if defined
+ if (scheme != MonitorColorScheme.AutoDetect)
+ {
+ string configPath = Path.Combine(paths.GamePath, "StardewModdingAPI.config.json");
+ string text = File
+ .ReadAllText(configPath)
+ .Replace(@"""ColorScheme"": ""AutoDetect""", $@"""ColorScheme"": ""{scheme}""");
+ File.WriteAllText(configPath, text);
+ }
}
- }
- // remove obsolete appdata mods
- this.InteractivelyRemoveAppDataMods(modsDir, packagedModsDir);
+ // remove obsolete appdata mods
+ this.InteractivelyRemoveAppDataMods(paths.ModsDir, packagedModsDir);
+ }
}
Console.WriteLine();
Console.WriteLine();
- /****
- ** final instructions
- ****/
+
+ /*********
+ ** Step 7: final instructions
+ *********/
if (platform == Platform.Windows)
{
if (action == ScriptAction.Install)
{
this.PrintSuccess("SMAPI is installed! If you use Steam, set your launch options to enable achievements (see smapi.io/install):");
- this.PrintSuccess($" \"{Path.Combine(installDir.FullName, "StardewModdingAPI.exe")}\" %command%");
+ this.PrintSuccess($" \"{Path.Combine(paths.GamePath, "StardewModdingAPI.exe")}\" %command%");
Console.WriteLine();
this.PrintSuccess("If you don't use Steam, launch StardewModdingAPI.exe in your game folder to play with mods.");
}
@@ -460,6 +571,23 @@ namespace StardewModdingApi.Installer
return str;
}
+ /// <summary>Get the display text for a color scheme.</summary>
+ /// <param name="scheme">The color scheme.</param>
+ private string GetDisplayText(MonitorColorScheme scheme)
+ {
+ switch (scheme)
+ {
+ case MonitorColorScheme.AutoDetect:
+ return "auto-detect";
+ case MonitorColorScheme.DarkBackground:
+ return "light text on dark background";
+ case MonitorColorScheme.LightBackground:
+ return "dark text on light background";
+ default:
+ return scheme.ToString();
+ }
+ }
+
/// <summary>Get the value of a key in the Windows HKLM registry.</summary>
/// <param name="key">The full path of the registry key relative to HKLM.</param>
/// <param name="name">The name of the value.</param>
@@ -486,6 +614,10 @@ namespace StardewModdingApi.Installer
return (string)openKey.GetValue(name);
}
+ /// <summary>Print a message without formatting.</summary>
+ /// <param name="text">The text to print.</param>
+ private void PrintPlain(string text) => Console.WriteLine(text);
+
/// <summary>Print a debug message.</summary>
/// <param name="text">The text to print.</param>
private void PrintDebug(string text) => this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Debug);
@@ -594,17 +726,22 @@ namespace StardewModdingApi.Installer
}
/// <summary>Interactively ask the user to choose a value.</summary>
+ /// <param name="print">A callback which prints a message to the console.</param>
/// <param name="message">The message to print.</param>
/// <param name="options">The allowed options (not case sensitive).</param>
- private string InteractivelyChoose(string message, params string[] options)
+ /// <param name="indent">The indentation to prefix to output.</param>
+ private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string> print = null)
{
+ print = print ?? this.PrintInfo;
+
while (true)
{
- this.PrintInfo(message);
+ print(indent + message);
+ Console.Write(indent);
string input = Console.ReadLine()?.Trim().ToLowerInvariant();
if (!options.Contains(input))
{
- this.PrintInfo("That's not a valid option.");
+ print($"{indent}That's not a valid option.");
continue;
}
return input;
diff --git a/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj b/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
index 2ad7e82a..e82c6093 100644
--- a/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
+++ b/src/SMAPI.Installer/StardewModdingAPI.Installer.csproj
@@ -40,6 +40,7 @@
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Enums\ScriptAction.cs" />
+ <Compile Include="Framework\InstallerPaths.cs" />
<Compile Include="InteractiveInstaller.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />