diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-06-17 18:51:12 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-09-14 18:05:18 -0400 |
commit | a7544e5afb4ba7f12d248503106cc8d407c2bad1 (patch) | |
tree | 986ee649e5144de93ea03749ea6b041437c899a7 /src | |
parent | 46a0dd6236a39175e85c11eac44a710db1c07847 (diff) | |
download | SMAPI-a7544e5afb4ba7f12d248503106cc8d407c2bad1.tar.gz SMAPI-a7544e5afb4ba7f12d248503106cc8d407c2bad1.tar.bz2 SMAPI-a7544e5afb4ba7f12d248503106cc8d407c2bad1.zip |
move game detection into toolkit for reuse
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI.Installer/InteractiveInstaller.cs | 106 | ||||
-rw-r--r-- | src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs | 129 | ||||
-rw-r--r-- | src/SMAPI.Toolkit/ModToolkit.cs | 9 | ||||
-rw-r--r-- | src/SMAPI.Toolkit/SMAPI.Toolkit.csproj | 1 |
4 files changed, 144 insertions, 101 deletions
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index c8d36b01..41400617 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.Win32; using StardewModdingApi.Installer.Enums; using StardewModdingAPI.Installer.Framework; -using StardewModdingAPI.Internal; using StardewModdingAPI.Internal.ConsoleWriting; using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework.ModScanning; @@ -37,64 +36,7 @@ namespace StardewModdingApi.Installer "SMAPI.ConsoleCommands" }; - /// <summary>The default file paths where Stardew Valley can be installed.</summary> - /// <param name="platform">The target platform.</param> - /// <remarks>Derived from the crossplatform mod config: https://github.com/Pathoschild/Stardew.ModBuildConfig. </remarks> - private IEnumerable<string> GetDefaultInstallPaths(Platform platform) - { - switch (platform) - { - case Platform.Linux: - case Platform.Mac: - { - string home = Environment.GetEnvironmentVariable("HOME"); - // Linux - yield return $"{home}/GOG Games/Stardew Valley/game"; - yield return Directory.Exists($"{home}/.steam/steam/steamapps/common/Stardew Valley") - ? $"{home}/.steam/steam/steamapps/common/Stardew Valley" - : $"{home}/.local/share/Steam/steamapps/common/Stardew Valley"; - - // Mac - yield return "/Applications/Stardew Valley.app/Contents/MacOS"; - yield return $"{home}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS"; - } - break; - - case Platform.Windows: - { - // Windows - foreach (string programFiles in new[] { @"C:\Program Files", @"C:\Program Files (x86)" }) - { - yield return $@"{programFiles}\GalaxyClient\Games\Stardew Valley"; - yield return $@"{programFiles}\GOG Galaxy\Games\Stardew Valley"; - yield return $@"{programFiles}\Steam\steamapps\common\Stardew Valley"; - } - - // Windows registry - IDictionary<string, string> registryKeys = new Dictionary<string, string> - { - [@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150"] = "InstallLocation", // Steam - [@"SOFTWARE\WOW6432Node\GOG.com\Games\1453375253"] = "PATH", // GOG on 64-bit Windows - }; - foreach (var pair in registryKeys) - { - string path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value); - if (!string.IsNullOrWhiteSpace(path)) - yield return path; - } - - // via Steam library path - string steampath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath"); - if (steampath != null) - yield return Path.Combine(steampath.Replace('/', '\\'), @"steamapps\common\Stardew Valley"); - } - break; - - default: - throw new InvalidOperationException($"Unknown platform '{platform}'."); - } - } /// <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> @@ -186,8 +128,9 @@ namespace StardewModdingApi.Installer ** Step 1: initial setup *********/ /**** - ** Get platform & set window title + ** Get basic info & set window title ****/ + ModToolkit toolkit = new ModToolkit(); Platform platform = EnvironmentUtility.DetectPlatform(); Console.Title = $"SMAPI {this.GetDisplayVersion(this.GetType().Assembly.GetName().Version)} installer on {platform} {EnvironmentUtility.GetFriendlyPlatformName(platform)}"; Console.WriteLine(); @@ -323,7 +266,7 @@ namespace StardewModdingApi.Installer ****/ // get game path this.PrintInfo("Where is your game folder?"); - DirectoryInfo installDir = this.InteractivelyGetInstallPath(platform, gamePathArg); + DirectoryInfo installDir = this.InteractivelyGetInstallPath(platform, toolkit, gamePathArg); if (installDir == null) { this.PrintError("Failed finding your game path."); @@ -489,7 +432,6 @@ namespace StardewModdingApi.Installer { this.PrintDebug("Adding bundled mods..."); - ModToolkit toolkit = new ModToolkit(); ModFolder[] targetMods = toolkit.GetModFolders(paths.ModsPath).ToArray(); foreach (ModFolder sourceMod in toolkit.GetModFolders(bundledModsDir.FullName)) { @@ -597,32 +539,6 @@ namespace StardewModdingApi.Installer } } - /// <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> - private string GetLocalMachineRegistryValue(string key, string name) - { - RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine; - RegistryKey openKey = localMachine.OpenSubKey(key); - if (openKey == null) - return null; - using (openKey) - return (string)openKey.GetValue(name); - } - - /// <summary>Get the value of a key in the Windows HKCU registry.</summary> - /// <param name="key">The full path of the registry key relative to HKCU.</param> - /// <param name="name">The name of the value.</param> - private string GetCurrentUserRegistryValue(string key, string name) - { - RegistryKey currentuser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser; - RegistryKey openKey = currentuser.OpenSubKey(key); - if (openKey == null) - return null; - using (openKey) - 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); @@ -788,8 +704,9 @@ namespace StardewModdingApi.Installer /// <summary>Interactively locate the game install path to update.</summary> /// <param name="platform">The current platform.</param> + /// <param name="toolkit">The mod toolkit.</param> /// <param name="specifiedPath">The path specified as a command-line argument (if any), which should override automatic path detection.</param> - private DirectoryInfo InteractivelyGetInstallPath(Platform platform, string specifiedPath) + private DirectoryInfo InteractivelyGetInstallPath(Platform platform, ModToolkit toolkit, string specifiedPath) { // get executable name string executableFilename = EnvironmentUtility.GetExecutableName(platform); @@ -812,18 +729,7 @@ namespace StardewModdingApi.Installer } // get installed paths - DirectoryInfo[] defaultPaths = - ( - from path in this.GetDefaultInstallPaths(platform).Distinct(StringComparer.InvariantCultureIgnoreCase) - let dir = new DirectoryInfo(path) - where dir.Exists && dir.EnumerateFiles(executableFilename).Any() - select dir - ) - .GroupBy(p => p.FullName, StringComparer.InvariantCultureIgnoreCase) // ignore duplicate paths - .Select(p => p.First()) - .ToArray(); - - // choose where to install + DirectoryInfo[] defaultPaths = toolkit.GetGameFolders().ToArray(); if (defaultPaths.Any()) { // only one path diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs new file mode 100644 index 00000000..1755a7db --- /dev/null +++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using StardewModdingAPI.Toolkit.Utilities; +#if SMAPI_FOR_WINDOWS +using Microsoft.Win32; +#endif + +namespace StardewModdingAPI.Toolkit.Framework.GameScanning +{ + /// <summary>Finds installed game folders.</summary> + public class GameScanner + { + /********* + ** Public methods + *********/ + /// <summary>Find all valid Stardew Valley install folders.</summary> + /// <remarks>This checks default game locations, and on Windows checks the Windows registry for GOG/Steam install data. A folder is considered 'valid' if it contains the Stardew Valley executable for the current OS.</remarks> + public IEnumerable<DirectoryInfo> Scan() + { + // get unique candidate paths + Platform platform = EnvironmentUtility.DetectPlatform(); + string executableFilename = EnvironmentUtility.GetExecutableName(platform); + IEnumerable<string> paths = this.GetDefaultInstallPaths(platform).Select(PathUtilities.NormalisePathSeparators).Distinct(StringComparer.InvariantCultureIgnoreCase); + + // get valid folders + foreach (string path in paths) + { + DirectoryInfo folder = new DirectoryInfo(path); + if (folder.Exists && folder.EnumerateFiles(executableFilename).Any()) + yield return folder; + } + } + + + /********* + ** Private methods + *********/ + /// <summary>The default file paths where Stardew Valley can be installed.</summary> + /// <param name="platform">The target platform.</param> + /// <remarks>Derived from the crossplatform mod config: https://github.com/Pathoschild/Stardew.ModBuildConfig. </remarks> + private IEnumerable<string> GetDefaultInstallPaths(Platform platform) + { + switch (platform) + { + case Platform.Linux: + case Platform.Mac: + { + string home = Environment.GetEnvironmentVariable("HOME"); + + // Linux + yield return $"{home}/GOG Games/Stardew Valley/game"; + yield return Directory.Exists($"{home}/.steam/steam/steamapps/common/Stardew Valley") + ? $"{home}/.steam/steam/steamapps/common/Stardew Valley" + : $"{home}/.local/share/Steam/steamapps/common/Stardew Valley"; + + // Mac + yield return "/Applications/Stardew Valley.app/Contents/MacOS"; + yield return $"{home}/Library/Application Support/Steam/steamapps/common/Stardew Valley/Contents/MacOS"; + } + break; + + case Platform.Windows: + { + // Windows + foreach (string programFiles in new[] { @"C:\Program Files", @"C:\Program Files (x86)" }) + { + yield return $@"{programFiles}\GalaxyClient\Games\Stardew Valley"; + yield return $@"{programFiles}\GOG Galaxy\Games\Stardew Valley"; + yield return $@"{programFiles}\Steam\steamapps\common\Stardew Valley"; + } + + // Windows registry +#if SMAPI_FOR_WINDOWS + IDictionary<string, string> registryKeys = new Dictionary<string, string> + { + [@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150"] = "InstallLocation", // Steam + [@"SOFTWARE\WOW6432Node\GOG.com\Games\1453375253"] = "PATH", // GOG on 64-bit Windows + }; + foreach (var pair in registryKeys) + { + string path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value); + if (!string.IsNullOrWhiteSpace(path)) + yield return path; + } + + // via Steam library path + string steampath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath"); + if (steampath != null) + yield return Path.Combine(steampath.Replace('/', '\\'), @"steamapps\common\Stardew Valley"); +#endif + } + break; + + default: + throw new InvalidOperationException($"Unknown platform '{platform}'."); + } + } + +#if SMAPI_FOR_WINDOWS + /// <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> + private string GetLocalMachineRegistryValue(string key, string name) + { + RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine; + RegistryKey openKey = localMachine.OpenSubKey(key); + if (openKey == null) + return null; + using (openKey) + return (string)openKey.GetValue(name); + } + + /// <summary>Get the value of a key in the Windows HKCU registry.</summary> + /// <param name="key">The full path of the registry key relative to HKCU.</param> + /// <param name="name">The name of the value.</param> + private string GetCurrentUserRegistryValue(string key, string name) + { + RegistryKey currentuser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser; + RegistryKey openKey = currentuser.OpenSubKey(key); + if (openKey == null) + return null; + using (openKey) + return (string)openKey.GetValue(name); + } +#endif + } +} diff --git a/src/SMAPI.Toolkit/ModToolkit.cs b/src/SMAPI.Toolkit/ModToolkit.cs index f1da62a2..4b026b7a 100644 --- a/src/SMAPI.Toolkit/ModToolkit.cs +++ b/src/SMAPI.Toolkit/ModToolkit.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; +using StardewModdingAPI.Toolkit.Framework.GameScanning; using StardewModdingAPI.Toolkit.Framework.ModData; using StardewModdingAPI.Toolkit.Framework.ModScanning; using StardewModdingAPI.Toolkit.Serialisation; @@ -46,6 +47,13 @@ namespace StardewModdingAPI.Toolkit this.UserAgent = $"SMAPI Mod Handler Toolkit/{version}"; } + /// <summary>Find valid Stardew Valley install folders.</summary> + /// <remarks>This checks default game locations, and on Windows checks the Windows registry for GOG/Steam install data. A folder is considered 'valid' if it contains the Stardew Valley executable for the current OS.</remarks> + public IEnumerable<DirectoryInfo> GetGameFolders() + { + return new GameScanner().Scan(); + } + /// <summary>Extract mod metadata from the wiki compatibility list.</summary> public async Task<WikiModList> GetWikiCompatibilityListAsync() { @@ -72,7 +80,6 @@ namespace StardewModdingAPI.Toolkit /// <summary>Extract information about all mods in the given folder.</summary> /// <param name="rootPath">The root folder containing mods. Only the <paramref name="modPath"/> will be searched, but this field allows it to be treated as a potential mod folder of its own.</param> /// <param name="modPath">The mod path to search.</param> - // /// <param name="tryConsolidateMod">If the folder contains multiple XNB mods, treat them as subfolders of a single mod. This is useful when reading a single mod archive, as opposed to a mods folder.</param> public IEnumerable<ModFolder> GetModFolders(string rootPath, string modPath) { return new ModScanner(this.JsonHelper).GetModFolders(rootPath, modPath); diff --git a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj index 2be84316..53bbe1ac 100644 --- a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj +++ b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj @@ -19,6 +19,7 @@ <PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Pathoschild.Http.FluentClient" Version="3.2.0" /> <PackageReference Include="System.Management" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT'" /> + <PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" Condition="'$(OS)' == 'Windows_NT' AND '$(TargetFramework)' == 'netstandard2.0'" /> </ItemGroup> <ItemGroup> |