summaryrefslogtreecommitdiff
path: root/src/SMAPI.Toolkit
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-10-09 20:11:34 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-10-09 20:11:34 -0400
commit93a748996c1f728a1daafd2e69775c7eeb346b26 (patch)
tree2eeecc639014a6558e3d0f3ca41f65b429211412 /src/SMAPI.Toolkit
parente7d29a2f7dabde75fb1ad76af1975c9194b1b8bd (diff)
parentee77efcc976ef1a5ee64933a6174d2fac9c6d0f9 (diff)
downloadSMAPI-93a748996c1f728a1daafd2e69775c7eeb346b26.tar.gz
SMAPI-93a748996c1f728a1daafd2e69775c7eeb346b26.tar.bz2
SMAPI-93a748996c1f728a1daafd2e69775c7eeb346b26.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Toolkit')
-rw-r--r--src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs50
-rw-r--r--src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs4
-rw-r--r--src/SMAPI.Toolkit/SMAPI.Toolkit.csproj3
-rw-r--r--src/SMAPI.Toolkit/Serialization/Models/Manifest.cs53
4 files changed, 97 insertions, 13 deletions
diff --git a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
index 8e1538a5..66465ffe 100644
--- a/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
+++ b/src/SMAPI.Toolkit/Framework/GameScanning/GameScanner.cs
@@ -9,6 +9,7 @@ using StardewModdingAPI.Toolkit.Utilities;
using System.Reflection;
#if SMAPI_FOR_WINDOWS
using Microsoft.Win32;
+using VdfParser;
#endif
namespace StardewModdingAPI.Toolkit.Framework.GameScanning
@@ -23,6 +24,9 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
/// <summary>The current OS.</summary>
private readonly Platform Platform;
+ /// <summary>The Steam app ID for Stardew Valley.</summary>
+ private const string SteamAppId = "413150";
+
/*********
** Public methods
@@ -145,7 +149,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
#if SMAPI_FOR_WINDOWS
IDictionary<string, string> registryKeys = new Dictionary<string, string>
{
- [@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 413150"] = "InstallLocation", // Steam
+ [@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App " + GameScanner.SteamAppId] = "InstallLocation", // Steam
[@"SOFTWARE\WOW6432Node\GOG.com\Games\1453375253"] = "PATH", // GOG on 64-bit Windows
};
foreach (var pair in registryKeys)
@@ -158,7 +162,15 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
// via Steam library path
string? steamPath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath");
if (steamPath != null)
+ {
+ // conventional path
yield return Path.Combine(steamPath.Replace('/', '\\'), @"steamapps\common\Stardew Valley");
+
+ // from Steam's .vdf file
+ string? path = this.GetPathFromSteamLibrary(steamPath);
+ if (!string.IsNullOrWhiteSpace(path))
+ yield return path;
+ }
#endif
// default GOG/Steam paths
@@ -243,6 +255,42 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
using (openKey)
return (string?)openKey.GetValue(name);
}
+
+ /// <summary>Get the game directory path from alternative Steam library locations.</summary>
+ /// <param name="steamPath">The full path to the directory containing steam.exe.</param>
+ /// <returns>The game directory, if found.</returns>
+ private string? GetPathFromSteamLibrary(string? steamPath)
+ {
+ if (steamPath == null)
+ return null;
+
+ // get raw .vdf data
+ string libraryFoldersPath = Path.Combine(steamPath.Replace('/', '\\'), "steamapps\\libraryfolders.vdf");
+ using FileStream fileStream = File.OpenRead(libraryFoldersPath);
+ VdfDeserializer deserializer = new();
+ dynamic libraries = deserializer.Deserialize(fileStream);
+ if (libraries?.libraryfolders is null)
+ return null;
+
+ // get path from Stardew Valley app (if any)
+ foreach (dynamic pair in libraries.libraryfolders)
+ {
+ dynamic library = pair.Value;
+
+ foreach (dynamic app in library.apps)
+ {
+ string key = app.Key;
+ if (key == GameScanner.SteamAppId)
+ {
+ string path = library.path;
+
+ return Path.Combine(path.Replace("\\\\", "\\"), "steamapps", "common", "Stardew Valley");
+ }
+ }
+ }
+
+ return null;
+ }
#endif
}
}
diff --git a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
index a85ef109..5e9e3c35 100644
--- a/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
+++ b/src/SMAPI.Toolkit/Framework/ModScanning/ModScanner.cs
@@ -45,10 +45,14 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
".png",
".psd",
".tif",
+ ".xcf", // gimp files
// archives
".rar",
".zip",
+ ".7z",
+ ".tar",
+ ".tar.gz",
// backup files
".backup",
diff --git a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
index 7b79105f..10f1df70 100644
--- a/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
+++ b/src/SMAPI.Toolkit/SMAPI.Toolkit.csproj
@@ -11,9 +11,10 @@
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.43" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
- <PackageReference Include="Pathoschild.Http.FluentClient" Version="4.1.1" />
+ <PackageReference Include="Pathoschild.Http.FluentClient" Version="4.2.0" />
<PackageReference Include="System.Management" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
+ <PackageReference Include="VdfConverter" Version="1.0.3" Condition="'$(OS)' == 'Windows_NT'" Private="False" />
</ItemGroup>
<ItemGroup>
diff --git a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
index da3ad608..8a449f0a 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Text;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Converters;
@@ -90,13 +91,13 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
[JsonConstructor]
public Manifest(string uniqueId, string name, string author, string description, ISemanticVersion version, ISemanticVersion? minimumApiVersion, string? entryDll, IManifestContentPackFor? contentPackFor, IManifestDependency[]? dependencies, string[]? updateKeys)
{
- this.UniqueID = this.NormalizeWhitespace(uniqueId);
- this.Name = this.NormalizeWhitespace(name);
- this.Author = this.NormalizeWhitespace(author);
- this.Description = this.NormalizeWhitespace(description);
+ this.UniqueID = this.NormalizeField(uniqueId);
+ this.Name = this.NormalizeField(name, replaceSquareBrackets: true);
+ this.Author = this.NormalizeField(author);
+ this.Description = this.NormalizeField(description);
this.Version = version;
this.MinimumApiVersion = minimumApiVersion;
- this.EntryDll = this.NormalizeWhitespace(entryDll);
+ this.EntryDll = this.NormalizeField(entryDll);
this.ContentPackFor = contentPackFor;
this.Dependencies = dependencies ?? Array.Empty<IManifestDependency>();
this.UpdateKeys = updateKeys ?? Array.Empty<string>();
@@ -113,17 +114,47 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
/*********
** Private methods
*********/
- /// <summary>Normalize whitespace in a raw string.</summary>
+ /// <summary>Normalize a manifest field to strip newlines, trim whitespace, and optionally strip square brackets.</summary>
/// <param name="input">The input to strip.</param>
+ /// <param name="replaceSquareBrackets">Whether to replace square brackets with round ones. This is used in the mod name to avoid breaking the log format.</param>
#if NET5_0_OR_GREATER
[return: NotNullIfNotNull("input")]
#endif
- private string? NormalizeWhitespace(string? input)
+ private string? NormalizeField(string? input, bool replaceSquareBrackets = false)
{
- return input
- ?.Trim()
- .Replace("\r", "")
- .Replace("\n", "");
+ input = input?.Trim();
+
+ if (!string.IsNullOrEmpty(input))
+ {
+ StringBuilder? builder = null;
+
+ for (int i = 0; i < input.Length; i++)
+ {
+ switch (input[i])
+ {
+ case '\r':
+ case '\n':
+ builder ??= new StringBuilder(input);
+ builder[i] = ' ';
+ break;
+
+ case '[' when replaceSquareBrackets:
+ builder ??= new StringBuilder(input);
+ builder[i] = '(';
+ break;
+
+ case ']' when replaceSquareBrackets:
+ builder ??= new StringBuilder(input);
+ builder[i] = ')';
+ break;
+ }
+ }
+
+ if (builder != null)
+ input = builder.ToString();
+ }
+
+ return input;
}
}
}