summaryrefslogtreecommitdiff
path: root/src/SMAPI.Installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Installer')
-rw-r--r--src/SMAPI.Installer/Framework/InstallerContext.cs4
-rw-r--r--src/SMAPI.Installer/InteractiveInstaller.cs101
-rw-r--r--src/SMAPI.Installer/Program.cs12
-rw-r--r--src/SMAPI.Installer/assets/unix-launcher.sh53
4 files changed, 106 insertions, 64 deletions
diff --git a/src/SMAPI.Installer/Framework/InstallerContext.cs b/src/SMAPI.Installer/Framework/InstallerContext.cs
index bb973230..a2c63dd8 100644
--- a/src/SMAPI.Installer/Framework/InstallerContext.cs
+++ b/src/SMAPI.Installer/Framework/InstallerContext.cs
@@ -12,7 +12,7 @@ namespace StardewModdingAPI.Installer.Framework
** Fields
*********/
/// <summary>The underlying toolkit game scanner.</summary>
- private readonly GameScanner GameScanner = new GameScanner();
+ private readonly GameScanner GameScanner = new();
/*********
@@ -44,7 +44,7 @@ namespace StardewModdingAPI.Installer.Framework
/// <summary>Get the installer's version number.</summary>
public ISemanticVersion GetInstallerVersion()
{
- var raw = this.GetType().Assembly.GetName().Version;
+ var raw = this.GetType().Assembly.GetName().Version!;
return new SemanticVersion(raw);
}
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs
index b3bba883..5138173a 100644
--- a/src/SMAPI.Installer/InteractiveInstaller.cs
+++ b/src/SMAPI.Installer/InteractiveInstaller.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -35,6 +36,7 @@ namespace StardewModdingApi.Installer
/// <summary>Get the absolute file or folder paths to remove when uninstalling SMAPI.</summary>
/// <param name="installDir">The folder for Stardew Valley and SMAPI.</param>
/// <param name="modsDir">The folder for SMAPI mods.</param>
+ [SuppressMessage("ReSharper", "StringLiteralTypo", Justification = "These are valid file names.")]
private IEnumerable<string> GetUninstallPaths(DirectoryInfo installDir, DirectoryInfo modsDir)
{
string GetInstallPath(string path) => Path.Combine(installDir.FullName, path);
@@ -126,7 +128,7 @@ namespace StardewModdingApi.Installer
/****
** Get basic info & set window title
****/
- ModToolkit toolkit = new ModToolkit();
+ ModToolkit toolkit = new();
var context = new InstallerContext();
Console.Title = $"SMAPI {context.GetInstallerVersion()} installer on {context.Platform} {context.PlatformName}";
Console.WriteLine();
@@ -164,7 +166,7 @@ namespace StardewModdingApi.Installer
}
// get game path from CLI
- string gamePathArg = null;
+ string? gamePathArg = null;
{
int pathIndex = Array.LastIndexOf(args, "--game-path") + 1;
if (pathIndex >= 1 && args.Length >= pathIndex)
@@ -189,8 +191,8 @@ namespace StardewModdingApi.Installer
** show theme selector
****/
// get theme writers
- var lightBackgroundWriter = new ColorfulConsoleWriter(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.LightBackground));
- var darkBackgroundWriter = new ColorfulConsoleWriter(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.DarkBackground));
+ ColorfulConsoleWriter lightBackgroundWriter = new(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.LightBackground));
+ ColorfulConsoleWriter darkBackgroundWriter = new(context.Platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.DarkBackground));
// print question
this.PrintPlain("Which text looks more readable?");
@@ -237,7 +239,7 @@ namespace StardewModdingApi.Installer
** collect details
****/
// get game path
- DirectoryInfo installDir = this.InteractivelyGetInstallPath(toolkit, context, gamePathArg);
+ DirectoryInfo? installDir = this.InteractivelyGetInstallPath(toolkit, context, gamePathArg);
if (installDir == null)
{
this.PrintError("Failed finding your game path.");
@@ -246,7 +248,7 @@ namespace StardewModdingApi.Installer
}
// get folders
- DirectoryInfo bundleDir = new DirectoryInfo(this.BundlePath);
+ DirectoryInfo bundleDir = new(this.BundlePath);
paths = new InstallerPaths(bundleDir, installDir);
}
@@ -354,8 +356,8 @@ namespace StardewModdingApi.Installer
// move global save data folder (changed in 3.2)
{
string dataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
- DirectoryInfo oldDir = new DirectoryInfo(Path.Combine(dataPath, "Saves", ".smapi"));
- DirectoryInfo newDir = new DirectoryInfo(Path.Combine(dataPath, ".smapi"));
+ DirectoryInfo oldDir = new(Path.Combine(dataPath, "Saves", ".smapi"));
+ DirectoryInfo newDir = new(Path.Combine(dataPath, ".smapi"));
if (oldDir.Exists)
{
@@ -428,7 +430,7 @@ namespace StardewModdingApi.Installer
}
// add or replace bundled mods
- DirectoryInfo bundledModsDir = new DirectoryInfo(Path.Combine(paths.BundlePath, "Mods"));
+ DirectoryInfo bundledModsDir = new(Path.Combine(paths.BundlePath, "Mods"));
if (bundledModsDir.Exists && bundledModsDir.EnumerateDirectories().Any())
{
this.PrintDebug("Adding bundled mods...");
@@ -449,8 +451,8 @@ namespace StardewModdingApi.Installer
}
// find target folder
- ModFolder targetMod = targetMods.FirstOrDefault(p => p.Manifest?.UniqueID?.Equals(sourceMod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase) == true);
- DirectoryInfo defaultTargetFolder = new DirectoryInfo(Path.Combine(paths.ModsPath, sourceMod.Directory.Name));
+ ModFolder? targetMod = targetMods.FirstOrDefault(p => p.Manifest?.UniqueID?.Equals(sourceMod.Manifest.UniqueID, StringComparison.OrdinalIgnoreCase) == true);
+ DirectoryInfo defaultTargetFolder = new(Path.Combine(paths.ModsPath, sourceMod.Directory.Name));
DirectoryInfo targetFolder = targetMod?.Directory ?? defaultTargetFolder;
this.PrintDebug(targetFolder.FullName == defaultTargetFolder.FullName
? $" adding {sourceMod.Manifest.Name}..."
@@ -532,27 +534,45 @@ namespace StardewModdingApi.Installer
/// <summary>Print a message without formatting.</summary>
/// <param name="text">The text to print.</param>
- private void PrintPlain(string text) => Console.WriteLine(text);
+ 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);
+ private void PrintDebug(string text)
+ {
+ this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Debug);
+ }
/// <summary>Print a debug message.</summary>
/// <param name="text">The text to print.</param>
- private void PrintInfo(string text) => this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Info);
+ private void PrintInfo(string text)
+ {
+ this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Info);
+ }
/// <summary>Print a warning message.</summary>
/// <param name="text">The text to print.</param>
- private void PrintWarning(string text) => this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Warn);
+ private void PrintWarning(string text)
+ {
+ this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Warn);
+ }
/// <summary>Print a warning message.</summary>
/// <param name="text">The text to print.</param>
- private void PrintError(string text) => this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Error);
+ private void PrintError(string text)
+ {
+ this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Error);
+ }
/// <summary>Print a success message.</summary>
/// <param name="text">The text to print.</param>
- private void PrintSuccess(string text) => this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Success);
+ private void PrintSuccess(string text)
+ {
+ this.ConsoleWriter.WriteLine(text, ConsoleLogLevel.Success);
+ }
/// <summary>Interactively delete a file or folder path, and block until deletion completes.</summary>
/// <param name="path">The file or folder path.</param>
@@ -562,7 +582,7 @@ namespace StardewModdingApi.Installer
{
try
{
- FileUtilities.ForceDelete(Directory.Exists(path) ? new DirectoryInfo(path) : (FileSystemInfo)new FileInfo(path));
+ FileUtilities.ForceDelete(Directory.Exists(path) ? new DirectoryInfo(path) : new FileInfo(path));
break;
}
catch (Exception ex)
@@ -578,7 +598,7 @@ namespace StardewModdingApi.Installer
/// <param name="source">The file or folder to copy.</param>
/// <param name="targetFolder">The folder to copy into.</param>
/// <param name="filter">A filter which matches directories and files to copy, or <c>null</c> to match all.</param>
- private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func<FileSystemInfo, bool> filter = null)
+ private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, Func<FileSystemInfo, bool>? filter = null)
{
if (filter != null && !filter(source))
return;
@@ -593,8 +613,8 @@ namespace StardewModdingApi.Installer
break;
case DirectoryInfo sourceDir:
- DirectoryInfo targetSubfolder = new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name));
- foreach (var entry in sourceDir.EnumerateFileSystemInfos())
+ DirectoryInfo targetSubfolder = new(Path.Combine(targetFolder.FullName, sourceDir.Name));
+ foreach (FileSystemInfo entry in sourceDir.EnumerateFileSystemInfos())
this.RecursiveCopy(entry, targetSubfolder, filter);
break;
@@ -608,7 +628,7 @@ namespace StardewModdingApi.Installer
/// <param name="message">The message to print.</param>
/// <param name="options">The allowed options (not case sensitive).</param>
/// <param name="indent">The indentation to prefix to output.</param>
- private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string> print = null)
+ private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string>? print = null)
{
print ??= this.PrintInfo;
@@ -616,8 +636,8 @@ namespace StardewModdingApi.Installer
{
print(indent + message);
Console.Write(indent);
- string input = Console.ReadLine()?.Trim().ToLowerInvariant();
- if (!options.Contains(input))
+ string? input = Console.ReadLine()?.Trim().ToLowerInvariant();
+ if (input == null || !options.Contains(input))
{
print($"{indent}That's not a valid option.");
continue;
@@ -630,7 +650,7 @@ namespace StardewModdingApi.Installer
/// <param name="toolkit">The mod toolkit.</param>
/// <param name="context">The installer context.</param>
/// <param name="specifiedPath">The path specified as a command-line argument (if any), which should override automatic path detection.</param>
- private DirectoryInfo InteractivelyGetInstallPath(ModToolkit toolkit, InstallerContext context, string specifiedPath)
+ private DirectoryInfo? InteractivelyGetInstallPath(ModToolkit toolkit, InstallerContext context, string? specifiedPath)
{
// use specified path
if (specifiedPath != null)
@@ -697,7 +717,7 @@ namespace StardewModdingApi.Installer
// get path from user
Console.WriteLine();
this.PrintInfo($"Type the file path to the game directory (the one containing '{Constants.GameDllName}'), then press enter.");
- string path = Console.ReadLine()?.Trim();
+ string? path = Console.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(path))
{
this.PrintWarning("You must specify a directory path to continue.");
@@ -710,14 +730,14 @@ namespace StardewModdingApi.Installer
: path.Replace("\\ ", " "); // in Linux/macOS, spaces in paths may be escaped if copied from the command line
if (path.StartsWith("~/"))
{
- string home = Environment.GetEnvironmentVariable("HOME") ?? Environment.GetEnvironmentVariable("USERPROFILE");
+ string home = Environment.GetEnvironmentVariable("HOME") ?? Environment.GetEnvironmentVariable("USERPROFILE")!;
path = Path.Combine(home, path.Substring(2));
}
// get directory
if (File.Exists(path))
- path = Path.GetDirectoryName(path);
- DirectoryInfo directory = new DirectoryInfo(path);
+ path = Path.GetDirectoryName(path)!;
+ DirectoryInfo directory = new(path);
// validate path
if (!directory.Exists)
@@ -763,7 +783,7 @@ namespace StardewModdingApi.Installer
// game folder which contains the installer, if any
{
- DirectoryInfo curPath = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
+ 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))
@@ -785,7 +805,7 @@ namespace StardewModdingApi.Installer
}
}
- /// <summary>Interactively move mods out of the appdata directory.</summary>
+ /// <summary>Interactively move mods out of the app data directory.</summary>
/// <param name="properModsDir">The directory which should contain all mods.</param>
/// <param name="packagedModsDir">The installer directory containing packaged mods.</param>
private void InteractivelyRemoveAppDataMods(DirectoryInfo properModsDir, DirectoryInfo packagedModsDir)
@@ -795,7 +815,7 @@ namespace StardewModdingApi.Installer
// get path
string appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley");
- DirectoryInfo modDir = new DirectoryInfo(Path.Combine(appDataPath, "Mods"));
+ DirectoryInfo modDir = new(Path.Combine(appDataPath, "Mods"));
// check if migration needed
if (!modDir.Exists)
@@ -808,7 +828,7 @@ namespace StardewModdingApi.Installer
{
// get type
bool isDir = entry is DirectoryInfo;
- if (!isDir && !(entry is FileInfo))
+ if (!isDir && entry is not FileInfo)
continue; // should never happen
// delete packaged mods (newer version bundled into SMAPI)
@@ -845,7 +865,7 @@ namespace StardewModdingApi.Installer
/// <summary>Move a filesystem entry to a new parent directory.</summary>
/// <param name="entry">The filesystem entry to move.</param>
/// <param name="newPath">The destination path.</param>
- /// <remarks>We can't use <see cref="FileInfo.MoveTo"/> or <see cref="DirectoryInfo.MoveTo"/>, because those don't work across partitions.</remarks>
+ /// <remarks>We can't use <see cref="FileInfo.MoveTo(string)"/> or <see cref="DirectoryInfo.MoveTo"/>, because those don't work across partitions.</remarks>
private void Move(FileSystemInfo entry, string newPath)
{
// file
@@ -872,15 +892,12 @@ namespace StardewModdingApi.Installer
/// <param name="entry">The file or folder info.</param>
private bool ShouldCopy(FileSystemInfo entry)
{
- switch (entry.Name)
+ return entry.Name switch
{
- case "mcs":
- return false; // ignore macOS symlink
- case "Mods":
- return false; // Mods folder handled separately
- default:
- return true;
- }
+ "mcs" => false, // ignore macOS symlink
+ "Mods" => false, // Mods folder handled separately
+ _ => true
+ };
}
}
}
diff --git a/src/SMAPI.Installer/Program.cs b/src/SMAPI.Installer/Program.cs
index 45cfea75..dc452a46 100644
--- a/src/SMAPI.Installer/Program.cs
+++ b/src/SMAPI.Installer/Program.cs
@@ -15,7 +15,7 @@ namespace StardewModdingApi.Installer
*********/
/// <summary>The absolute path of the installer folder.</summary>
[SuppressMessage("ReSharper", "AssignNullToNotNullAttribute", Justification = "The assembly location is never null in this context.")]
- private static readonly string InstallerPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ private static readonly string InstallerPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
/// <summary>The absolute path of the folder containing the unzipped installer files.</summary>
private static readonly string ExtractedBundlePath = Path.Combine(Path.GetTempPath(), $"SMAPI-installer-{Guid.NewGuid():N}");
@@ -31,7 +31,7 @@ namespace StardewModdingApi.Installer
public static void Main(string[] args)
{
// find install bundle
- FileInfo zipFile = new FileInfo(Path.Combine(Program.InstallerPath, "install.dat"));
+ FileInfo zipFile = new(Path.Combine(Program.InstallerPath, "install.dat"));
if (!zipFile.Exists)
{
Console.WriteLine($"Oops! Some of the installer files are missing; try re-downloading the installer. (Missing file: {zipFile.FullName})");
@@ -40,7 +40,7 @@ namespace StardewModdingApi.Installer
}
// unzip bundle into temp folder
- DirectoryInfo bundleDir = new DirectoryInfo(Program.ExtractedBundlePath);
+ DirectoryInfo bundleDir = new(Program.ExtractedBundlePath);
Console.WriteLine("Extracting install files...");
ZipFile.ExtractToDirectory(zipFile.FullName, bundleDir.FullName);
@@ -66,14 +66,14 @@ namespace StardewModdingApi.Installer
/// <summary>Method called when assembly resolution fails, which may return a manually resolved assembly.</summary>
/// <param name="sender">The event sender.</param>
/// <param name="e">The event arguments.</param>
- private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs e)
+ private static Assembly? CurrentDomain_AssemblyResolve(object? sender, ResolveEventArgs e)
{
try
{
- AssemblyName name = new AssemblyName(e.Name);
+ AssemblyName name = new(e.Name);
foreach (FileInfo dll in new DirectoryInfo(Program.InternalFilesPath).EnumerateFiles("*.dll"))
{
- if (name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.OrdinalIgnoreCase))
+ if (name.Name != null && name.Name.Equals(AssemblyName.GetAssemblyName(dll.FullName).Name, StringComparison.OrdinalIgnoreCase))
return Assembly.LoadFrom(dll.FullName);
}
return null;
diff --git a/src/SMAPI.Installer/assets/unix-launcher.sh b/src/SMAPI.Installer/assets/unix-launcher.sh
index 47937f95..ae9624e7 100644
--- a/src/SMAPI.Installer/assets/unix-launcher.sh
+++ b/src/SMAPI.Installer/assets/unix-launcher.sh
@@ -6,10 +6,41 @@
# move to script's directory
cd "$(dirname "$0")" || exit $?
-# change to true to skip opening a terminal
+# Whether to avoid opening a separate terminal window, and avoid logging anything to the console.
# This isn't recommended since you won't see errors, warnings, and update alerts.
SKIP_TERMINAL=false
+# Whether to avoid opening a separate terminal, but still send the usual log output to the console.
+USE_CURRENT_SHELL=false
+
+
+##########
+## Read environment variables
+##########
+if [ "$SMAPI_NO_TERMINAL" == "true" ]; then
+ SKIP_TERMINAL=true
+fi
+if [ "$SMAPI_USE_CURRENT_SHELL" == "true" ]; then
+ USE_CURRENT_SHELL=true
+fi
+
+
+##########
+## Read command-line arguments
+##########
+while [ "$#" -gt 0 ]; do
+ case "$1" in
+ --skip-terminal ) SKIP_TERMINAL=true; shift ;;
+ --use-current-shell ) USE_CURRENT_SHELL=true; shift ;;
+ -- ) shift; break ;;
+ * ) shift ;;
+ esac
+done
+
+if [ "$SKIP_TERMINAL" == "true" ]; then
+ USE_CURRENT_SHELL=true
+fi
+
##########
## Open terminal if needed
@@ -18,21 +49,13 @@ SKIP_TERMINAL=false
# Besides letting the player see errors/warnings/alerts in the console, this is also needed because
# Steam messes with the PATH.
if [ "$(uname)" == "Darwin" ]; then
- if [ ! -t 1 ]; then # https://stackoverflow.com/q/911168/262123
- # sanity check to make sure we don't have an infinite loop of opening windows
- for argument in "$@"; do
- if [ "$argument" == "--no-reopen-terminal" ]; then
- SKIP_TERMINAL=true
- break
- fi
- done
-
+ if [ ! -t 1 ]; then # not open in Terminal (https://stackoverflow.com/q/911168/262123)
# reopen in Terminal if needed
# https://stackoverflow.com/a/29511052/262123
- if [ "$SKIP_TERMINAL" == "false" ]; then
+ if [ "$USE_CURRENT_SHELL" == "false" ]; then
echo "Reopening in the Terminal app..."
echo '#!/bin/sh' > /tmp/open-smapi-terminal.sh
- echo "\"$0\" $@ --no-reopen-terminal" >> /tmp/open-smapi-terminal.sh
+ echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.sh
chmod +x /tmp/open-smapi-terminal.sh
cat /tmp/open-smapi-terminal.sh
open -W -a Terminal /tmp/open-smapi-terminal.sh
@@ -68,7 +91,7 @@ else
export LAUNCH_FILE
# run in terminal
- if [ "$SKIP_TERMINAL" == "false" ]; then
+ if [ "$USE_CURRENT_SHELL" == "false" ]; then
# select terminal (prefer xterm for best compatibility, then known supported terminals)
for terminal in xterm gnome-terminal kitty terminator xfce4-terminal konsole terminal termite alacritty mate-terminal x-terminal-emulator; do
if command -v "$terminal" 2>/dev/null; then
@@ -131,7 +154,9 @@ else
fi
# explicitly run without terminal
- else
+ elif [ "$SKIP_TERMINAL" == "true" ]; then
exec $LAUNCH_FILE --no-terminal "$@"
+ else
+ exec $LAUNCH_FILE "$@"
fi
fi