enable nullable annotations for most of the SMAPI toolkit (#837)
This commit is contained in:
parent
6b05296e71
commit
d706a25053
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Framework
|
namespace StardewModdingAPI.Toolkit.Framework
|
||||||
{
|
{
|
||||||
/// <summary>Contains the SMAPI installer's constants and assumptions.</summary>
|
/// <summary>Contains the SMAPI installer's constants and assumptions.</summary>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -41,7 +39,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
IEnumerable<string> paths = this
|
IEnumerable<string> paths = this
|
||||||
.GetCustomInstallPaths()
|
.GetCustomInstallPaths()
|
||||||
.Concat(this.GetDefaultInstallPaths())
|
.Concat(this.GetDefaultInstallPaths())
|
||||||
.Select(PathUtilities.NormalizePath)
|
.Select(path => PathUtilities.NormalizePath(path))
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// yield valid folders
|
// yield valid folders
|
||||||
|
@ -80,10 +78,12 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
return GameFolderType.NoGameFound;
|
return GameFolderType.NoGameFound;
|
||||||
|
|
||||||
// get assembly version
|
// get assembly version
|
||||||
Version version;
|
Version? version;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
version = AssemblyName.GetAssemblyName(executable.FullName).Version;
|
version = AssemblyName.GetAssemblyName(executable.FullName).Version;
|
||||||
|
if (version == null)
|
||||||
|
return GameFolderType.InvalidUnknown;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -123,7 +123,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
case Platform.Linux:
|
case Platform.Linux:
|
||||||
case Platform.Mac:
|
case Platform.Mac:
|
||||||
{
|
{
|
||||||
string home = Environment.GetEnvironmentVariable("HOME");
|
string home = Environment.GetEnvironmentVariable("HOME")!;
|
||||||
|
|
||||||
// Linux
|
// Linux
|
||||||
yield return $"{home}/GOG Games/Stardew Valley/game";
|
yield return $"{home}/GOG Games/Stardew Valley/game";
|
||||||
|
@ -148,13 +148,13 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
};
|
};
|
||||||
foreach (var pair in registryKeys)
|
foreach (var pair in registryKeys)
|
||||||
{
|
{
|
||||||
string path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value);
|
string? path = this.GetLocalMachineRegistryValue(pair.Key, pair.Value);
|
||||||
if (!string.IsNullOrWhiteSpace(path))
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
yield return path;
|
yield return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// via Steam library path
|
// via Steam library path
|
||||||
string steamPath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath");
|
string? steamPath = this.GetCurrentUserRegistryValue(@"Software\Valve\Steam", "SteamPath");
|
||||||
if (steamPath != null)
|
if (steamPath != null)
|
||||||
yield return Path.Combine(steamPath.Replace('/', '\\'), @"steamapps\common\Stardew Valley");
|
yield return Path.Combine(steamPath.Replace('/', '\\'), @"steamapps\common\Stardew Valley");
|
||||||
#endif
|
#endif
|
||||||
|
@ -188,7 +188,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
private IEnumerable<string> GetCustomInstallPaths()
|
private IEnumerable<string> GetCustomInstallPaths()
|
||||||
{
|
{
|
||||||
// get home path
|
// get home path
|
||||||
string homePath = Environment.GetEnvironmentVariable(this.Platform == Platform.Windows ? "USERPROFILE" : "HOME");
|
string homePath = Environment.GetEnvironmentVariable(this.Platform == Platform.Windows ? "USERPROFILE" : "HOME")!;
|
||||||
if (string.IsNullOrWhiteSpace(homePath))
|
if (string.IsNullOrWhiteSpace(homePath))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
}
|
}
|
||||||
|
|
||||||
// get install path
|
// get install path
|
||||||
XElement element = root.XPathSelectElement("//*[local-name() = 'GamePath']"); // can't use '//GamePath' due to the default namespace
|
XElement? element = root.XPathSelectElement("//*[local-name() = 'GamePath']"); // can't use '//GamePath' due to the default namespace
|
||||||
if (!string.IsNullOrWhiteSpace(element?.Value))
|
if (!string.IsNullOrWhiteSpace(element?.Value))
|
||||||
yield return element.Value.Trim();
|
yield return element.Value.Trim();
|
||||||
}
|
}
|
||||||
|
@ -219,27 +219,27 @@ namespace StardewModdingAPI.Toolkit.Framework.GameScanning
|
||||||
/// <summary>Get the value of a key in the Windows HKLM registry.</summary>
|
/// <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="key">The full path of the registry key relative to HKLM.</param>
|
||||||
/// <param name="name">The name of the value.</param>
|
/// <param name="name">The name of the value.</param>
|
||||||
private string GetLocalMachineRegistryValue(string key, string name)
|
private string? GetLocalMachineRegistryValue(string key, string name)
|
||||||
{
|
{
|
||||||
RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine;
|
RegistryKey localMachine = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64) : Registry.LocalMachine;
|
||||||
RegistryKey openKey = localMachine.OpenSubKey(key);
|
RegistryKey? openKey = localMachine.OpenSubKey(key);
|
||||||
if (openKey == null)
|
if (openKey == null)
|
||||||
return null;
|
return null;
|
||||||
using (openKey)
|
using (openKey)
|
||||||
return (string)openKey.GetValue(name);
|
return (string?)openKey.GetValue(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the value of a key in the Windows HKCU registry.</summary>
|
/// <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="key">The full path of the registry key relative to HKCU.</param>
|
||||||
/// <param name="name">The name of the value.</param>
|
/// <param name="name">The name of the value.</param>
|
||||||
private string GetCurrentUserRegistryValue(string key, string name)
|
private string? GetCurrentUserRegistryValue(string key, string name)
|
||||||
{
|
{
|
||||||
RegistryKey currentuser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser;
|
RegistryKey currentUser = Environment.Is64BitOperatingSystem ? RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64) : Registry.CurrentUser;
|
||||||
RegistryKey openKey = currentuser.OpenSubKey(key);
|
RegistryKey? openKey = currentUser.OpenSubKey(key);
|
||||||
if (openKey == null)
|
if (openKey == null)
|
||||||
return null;
|
return null;
|
||||||
using (openKey)
|
using (openKey)
|
||||||
return (string)openKey.GetValue(name);
|
return (string?)openKey.GetValue(name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
@ -59,11 +57,13 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
#if SMAPI_FOR_WINDOWS
|
#if SMAPI_FOR_WINDOWS
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
|
string? result = new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem")
|
||||||
.Get()
|
.Get()
|
||||||
.Cast<ManagementObject>()
|
.Cast<ManagementObject>()
|
||||||
.Select(entry => entry.GetPropertyValue("Caption").ToString())
|
.Select(entry => entry.GetPropertyValue("Caption").ToString())
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
return result ?? "Windows";
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
#endif
|
#endif
|
||||||
|
@ -137,7 +137,7 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
buffer = Marshal.AllocHGlobal(8192);
|
buffer = Marshal.AllocHGlobal(8192);
|
||||||
if (LowLevelEnvironmentUtility.uname(buffer) == 0)
|
if (LowLevelEnvironmentUtility.uname(buffer) == 0)
|
||||||
{
|
{
|
||||||
string os = Marshal.PtrToStringAnsi(buffer);
|
string? os = Marshal.PtrToStringAnsi(buffer);
|
||||||
return os == "Darwin";
|
return os == "Darwin";
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -24,13 +22,13 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
public ModType Type { get; }
|
public ModType Type { get; }
|
||||||
|
|
||||||
/// <summary>The mod manifest.</summary>
|
/// <summary>The mod manifest.</summary>
|
||||||
public Manifest Manifest { get; }
|
public Manifest? Manifest { get; }
|
||||||
|
|
||||||
/// <summary>The error which occurred parsing the manifest, if any.</summary>
|
/// <summary>The error which occurred parsing the manifest, if any.</summary>
|
||||||
public ModParseError ManifestParseError { get; set; }
|
public ModParseError ManifestParseError { get; set; }
|
||||||
|
|
||||||
/// <summary>A human-readable message for the <see cref="ManifestParseError"/>, if any.</summary>
|
/// <summary>A human-readable message for the <see cref="ManifestParseError"/>, if any.</summary>
|
||||||
public string ManifestParseErrorText { get; set; }
|
public string? ManifestParseErrorText { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
|
@ -51,7 +49,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
/// <param name="manifest">The mod manifest.</param>
|
/// <param name="manifest">The mod manifest.</param>
|
||||||
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
|
/// <param name="manifestParseError">The error which occurred parsing the manifest, if any.</param>
|
||||||
/// <param name="manifestParseErrorText">A human-readable message for the <paramref name="manifestParseError"/>, if any.</param>
|
/// <param name="manifestParseErrorText">A human-readable message for the <paramref name="manifestParseError"/>, if any.</param>
|
||||||
public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest manifest, ModParseError manifestParseError, string manifestParseErrorText)
|
public ModFolder(DirectoryInfo root, DirectoryInfo directory, ModType type, Manifest? manifest, ModParseError manifestParseError, string? manifestParseErrorText)
|
||||||
{
|
{
|
||||||
// save info
|
// save info
|
||||||
this.Directory = directory;
|
this.Directory = directory;
|
||||||
|
@ -61,9 +59,9 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
this.ManifestParseErrorText = manifestParseErrorText;
|
this.ManifestParseErrorText = manifestParseErrorText;
|
||||||
|
|
||||||
// set display name
|
// set display name
|
||||||
this.DisplayName = manifest?.Name;
|
this.DisplayName = !string.IsNullOrWhiteSpace(manifest?.Name)
|
||||||
if (string.IsNullOrWhiteSpace(this.DisplayName))
|
? manifest.Name
|
||||||
this.DisplayName = PathUtilities.GetRelativePath(root.FullName, directory.FullName);
|
: PathUtilities.GetRelativePath(root.FullName, directory.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the update keys for a mod.</summary>
|
/// <summary>Get the update keys for a mod.</summary>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -117,7 +115,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder)
|
public ModFolder ReadFolder(DirectoryInfo root, DirectoryInfo searchFolder)
|
||||||
{
|
{
|
||||||
// find manifest.json
|
// find manifest.json
|
||||||
FileInfo manifestFile = this.FindManifest(searchFolder);
|
FileInfo? manifestFile = this.FindManifest(searchFolder);
|
||||||
|
|
||||||
// set appropriate invalid-mod error
|
// set appropriate invalid-mod error
|
||||||
if (manifestFile == null)
|
if (manifestFile == null)
|
||||||
|
@ -147,13 +145,13 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
}
|
}
|
||||||
|
|
||||||
// read mod info
|
// read mod info
|
||||||
Manifest manifest = null;
|
Manifest? manifest = null;
|
||||||
ModParseError error = ModParseError.None;
|
ModParseError error = ModParseError.None;
|
||||||
string errorText = null;
|
string? errorText = null;
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!this.JsonHelper.ReadJsonFileIfExists<Manifest>(manifestFile.FullName, out manifest) || manifest == null)
|
if (!this.JsonHelper.ReadJsonFileIfExists<Manifest>(manifestFile.FullName, out manifest))
|
||||||
{
|
{
|
||||||
error = ModParseError.ManifestInvalid;
|
error = ModParseError.ManifestInvalid;
|
||||||
errorText = "its manifest is invalid.";
|
errorText = "its manifest is invalid.";
|
||||||
|
@ -186,7 +184,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
}
|
}
|
||||||
|
|
||||||
// build result
|
// build result
|
||||||
return new ModFolder(root, manifestFile.Directory, type, manifest, error, errorText);
|
return new ModFolder(root, manifestFile.Directory!, type, manifest, error, errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -249,7 +247,7 @@ namespace StardewModdingAPI.Toolkit.Framework.ModScanning
|
||||||
|
|
||||||
/// <summary>Find the manifest for a mod folder.</summary>
|
/// <summary>Find the manifest for a mod folder.</summary>
|
||||||
/// <param name="folder">The folder to search.</param>
|
/// <param name="folder">The folder to search.</param>
|
||||||
private FileInfo FindManifest(DirectoryInfo folder)
|
private FileInfo? FindManifest(DirectoryInfo folder)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#nullable disable
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Framework
|
namespace StardewModdingAPI.Toolkit.Framework
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
/// <param name="prereleaseTag">An optional prerelease tag.</param>
|
/// <param name="prereleaseTag">An optional prerelease tag.</param>
|
||||||
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
|
/// <param name="buildMetadata">Optional build metadata. This is ignored when determining version precedence.</param>
|
||||||
/// <returns>Returns whether the version was successfully parsed.</returns>
|
/// <returns>Returns whether the version was successfully parsed.</returns>
|
||||||
public static bool TryParse(string versionStr, bool allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string prereleaseTag, out string buildMetadata)
|
public static bool TryParse(string? versionStr, bool allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string? prereleaseTag, out string? buildMetadata)
|
||||||
{
|
{
|
||||||
// init
|
// init
|
||||||
major = 0;
|
major = 0;
|
||||||
|
@ -105,7 +105,7 @@ namespace StardewModdingAPI.Toolkit.Framework
|
||||||
/// <param name="raw">The raw characters to parse.</param>
|
/// <param name="raw">The raw characters to parse.</param>
|
||||||
/// <param name="index">The index of the next character to read.</param>
|
/// <param name="index">The index of the next character to read.</param>
|
||||||
/// <param name="tag">The parsed tag.</param>
|
/// <param name="tag">The parsed tag.</param>
|
||||||
private static bool TryParseTag(char[] raw, ref int index, out string tag)
|
private static bool TryParseTag(char[] raw, ref int index, [NotNullWhen(true)] out string? tag)
|
||||||
{
|
{
|
||||||
// read tag length
|
// read tag length
|
||||||
int length = 0;
|
int length = 0;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
{
|
{
|
||||||
|
@ -17,10 +16,11 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
public ModSiteKey Site { get; }
|
public ModSiteKey Site { get; }
|
||||||
|
|
||||||
/// <summary>The mod ID within the repository.</summary>
|
/// <summary>The mod ID within the repository.</summary>
|
||||||
public string ID { get; }
|
[MemberNotNullWhen(true, nameof(LooksValid))]
|
||||||
|
public string? ID { get; }
|
||||||
|
|
||||||
/// <summary>If specified, a substring in download names/descriptions to match.</summary>
|
/// <summary>If specified, a substring in download names/descriptions to match.</summary>
|
||||||
public string Subkey { get; }
|
public string? Subkey { get; }
|
||||||
|
|
||||||
/// <summary>Whether the update key seems to be valid.</summary>
|
/// <summary>Whether the update key seems to be valid.</summary>
|
||||||
public bool LooksValid { get; }
|
public bool LooksValid { get; }
|
||||||
|
@ -34,9 +34,9 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
/// <param name="site">The mod site containing the mod.</param>
|
/// <param name="site">The mod site containing the mod.</param>
|
||||||
/// <param name="id">The mod ID within the site.</param>
|
/// <param name="id">The mod ID within the site.</param>
|
||||||
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
||||||
public UpdateKey(string rawText, ModSiteKey site, string id, string subkey)
|
public UpdateKey(string? rawText, ModSiteKey site, string? id, string? subkey)
|
||||||
{
|
{
|
||||||
this.RawText = rawText?.Trim();
|
this.RawText = rawText?.Trim() ?? string.Empty;
|
||||||
this.Site = site;
|
this.Site = site;
|
||||||
this.ID = id?.Trim();
|
this.ID = id?.Trim();
|
||||||
this.Subkey = subkey?.Trim();
|
this.Subkey = subkey?.Trim();
|
||||||
|
@ -49,19 +49,19 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
/// <param name="site">The mod site containing the mod.</param>
|
/// <param name="site">The mod site containing the mod.</param>
|
||||||
/// <param name="id">The mod ID within the site.</param>
|
/// <param name="id">The mod ID within the site.</param>
|
||||||
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
||||||
public UpdateKey(ModSiteKey site, string id, string subkey)
|
public UpdateKey(ModSiteKey site, string? id, string? subkey)
|
||||||
: this(UpdateKey.GetString(site, id, subkey), site, id, subkey) { }
|
: this(UpdateKey.GetString(site, id, subkey), site, id, subkey) { }
|
||||||
|
|
||||||
/// <summary>Parse a raw update key.</summary>
|
/// <summary>Parse a raw update key.</summary>
|
||||||
/// <param name="raw">The raw update key to parse.</param>
|
/// <param name="raw">The raw update key to parse.</param>
|
||||||
public static UpdateKey Parse(string raw)
|
public static UpdateKey Parse(string? raw)
|
||||||
{
|
{
|
||||||
// extract site + ID
|
// extract site + ID
|
||||||
string rawSite;
|
string? rawSite;
|
||||||
string id;
|
string? id;
|
||||||
{
|
{
|
||||||
string[] parts = raw?.Trim().Split(':');
|
string[]? parts = raw?.Trim().Split(':');
|
||||||
if (parts == null || parts.Length != 2)
|
if (parts?.Length != 2)
|
||||||
return new UpdateKey(raw, ModSiteKey.Unknown, null, null);
|
return new UpdateKey(raw, ModSiteKey.Unknown, null, null);
|
||||||
|
|
||||||
rawSite = parts[0].Trim();
|
rawSite = parts[0].Trim();
|
||||||
|
@ -71,7 +71,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
id = null;
|
id = null;
|
||||||
|
|
||||||
// extract subkey
|
// extract subkey
|
||||||
string subkey = null;
|
string? subkey = null;
|
||||||
if (id != null)
|
if (id != null)
|
||||||
{
|
{
|
||||||
string[] parts = id.Split('@');
|
string[] parts = id.Split('@');
|
||||||
|
@ -111,7 +111,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
|
|
||||||
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
||||||
/// <param name="other">An object to compare with this object.</param>
|
/// <param name="other">An object to compare with this object.</param>
|
||||||
public bool Equals(UpdateKey other)
|
public bool Equals(UpdateKey? other)
|
||||||
{
|
{
|
||||||
if (!this.LooksValid)
|
if (!this.LooksValid)
|
||||||
{
|
{
|
||||||
|
@ -129,7 +129,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
|
|
||||||
/// <summary>Determines whether the specified object is equal to the current object.</summary>
|
/// <summary>Determines whether the specified object is equal to the current object.</summary>
|
||||||
/// <param name="obj">The object to compare with the current object.</param>
|
/// <param name="obj">The object to compare with the current object.</param>
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
return obj is UpdateKey other && this.Equals(other);
|
return obj is UpdateKey other && this.Equals(other);
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ namespace StardewModdingAPI.Toolkit.Framework.UpdateData
|
||||||
/// <param name="site">The mod site containing the mod.</param>
|
/// <param name="site">The mod site containing the mod.</param>
|
||||||
/// <param name="id">The mod ID within the repository.</param>
|
/// <param name="id">The mod ID within the repository.</param>
|
||||||
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
/// <param name="subkey">If specified, a substring in download names/descriptions to match.</param>
|
||||||
public static string GetString(ModSiteKey site, string id, string subkey = null)
|
public static string GetString(ModSiteKey site, string? id, string? subkey = null)
|
||||||
{
|
{
|
||||||
return $"{site}:{id}{subkey}".Trim();
|
return $"{site}:{id}{subkey}".Trim();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -24,7 +22,7 @@ namespace StardewModdingAPI.Toolkit
|
||||||
private readonly string UserAgent;
|
private readonly string UserAgent;
|
||||||
|
|
||||||
/// <summary>Maps vendor keys (like <c>Nexus</c>) to their mod URL template (where <c>{0}</c> is the mod ID). This doesn't affect update checks, which defer to the remote web API.</summary>
|
/// <summary>Maps vendor keys (like <c>Nexus</c>) to their mod URL template (where <c>{0}</c> is the mod ID). This doesn't affect update checks, which defer to the remote web API.</summary>
|
||||||
private readonly IDictionary<ModSiteKey, string> VendorModUrls = new Dictionary<ModSiteKey, string>()
|
private readonly Dictionary<ModSiteKey, string> VendorModUrls = new()
|
||||||
{
|
{
|
||||||
[ModSiteKey.Chucklefish] = "https://community.playstarbound.com/resources/{0}",
|
[ModSiteKey.Chucklefish] = "https://community.playstarbound.com/resources/{0}",
|
||||||
[ModSiteKey.GitHub] = "https://github.com/{0}/releases",
|
[ModSiteKey.GitHub] = "https://github.com/{0}/releases",
|
||||||
|
@ -45,7 +43,7 @@ namespace StardewModdingAPI.Toolkit
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
public ModToolkit()
|
public ModToolkit()
|
||||||
{
|
{
|
||||||
ISemanticVersion version = new SemanticVersion(this.GetType().Assembly.GetName().Version);
|
ISemanticVersion version = new SemanticVersion(this.GetType().Assembly.GetName().Version!);
|
||||||
this.UserAgent = $"SMAPI Mod Handler Toolkit/{version}";
|
this.UserAgent = $"SMAPI Mod Handler Toolkit/{version}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +57,7 @@ namespace StardewModdingAPI.Toolkit
|
||||||
/// <summary>Extract mod metadata from the wiki compatibility list.</summary>
|
/// <summary>Extract mod metadata from the wiki compatibility list.</summary>
|
||||||
public async Task<WikiModList> GetWikiCompatibilityListAsync()
|
public async Task<WikiModList> GetWikiCompatibilityListAsync()
|
||||||
{
|
{
|
||||||
var client = new WikiClient(this.UserAgent);
|
WikiClient client = new(this.UserAgent);
|
||||||
return await client.FetchModsAsync();
|
return await client.FetchModsAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,13 +87,13 @@ namespace StardewModdingAPI.Toolkit
|
||||||
|
|
||||||
/// <summary>Get an update URL for an update key (if valid).</summary>
|
/// <summary>Get an update URL for an update key (if valid).</summary>
|
||||||
/// <param name="updateKey">The update key.</param>
|
/// <param name="updateKey">The update key.</param>
|
||||||
public string GetUpdateUrl(string updateKey)
|
public string? GetUpdateUrl(string updateKey)
|
||||||
{
|
{
|
||||||
UpdateKey parsed = UpdateKey.Parse(updateKey);
|
UpdateKey parsed = UpdateKey.Parse(updateKey);
|
||||||
if (!parsed.LooksValid)
|
if (!parsed.LooksValid)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (this.VendorModUrls.TryGetValue(parsed.Site, out string urlTemplate))
|
if (this.VendorModUrls.TryGetValue(parsed.Site, out string? urlTemplate))
|
||||||
return string.Format(urlTemplate, parsed.ID);
|
return string.Format(urlTemplate, parsed.ID);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace StardewModdingAPI.Toolkit
|
||||||
{
|
{
|
||||||
if (version == null)
|
if (version == null)
|
||||||
throw new ArgumentNullException(nameof(version), "The input version string can't be null.");
|
throw new ArgumentNullException(nameof(version), "The input version string can't be null.");
|
||||||
if (!SemanticVersionReader.TryParse(version, allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string prereleaseTag, out string buildMetadata) || (!allowNonStandard && platformRelease != 0))
|
if (!SemanticVersionReader.TryParse(version, allowNonStandard, out int major, out int minor, out int patch, out int platformRelease, out string? prereleaseTag, out string? buildMetadata) || (!allowNonStandard && platformRelease != 0))
|
||||||
throw new FormatException($"The input '{version}' isn't a valid semantic version.");
|
throw new FormatException($"The input '{version}' isn't a valid semantic version.");
|
||||||
|
|
||||||
this.MajorVersion = major;
|
this.MajorVersion = major;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit
|
namespace StardewModdingAPI.Toolkit
|
||||||
|
@ -18,7 +16,7 @@ namespace StardewModdingAPI.Toolkit
|
||||||
** Public methods
|
** Public methods
|
||||||
*********/
|
*********/
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Compare(ISemanticVersion x, ISemanticVersion y)
|
public int Compare(ISemanticVersion? x, ISemanticVersion? y)
|
||||||
{
|
{
|
||||||
if (object.ReferenceEquals(x, y))
|
if (object.ReferenceEquals(x, y))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using StardewModdingAPI.Toolkit.Serialization.Models;
|
using StardewModdingAPI.Toolkit.Serialization.Models;
|
||||||
|
@ -35,7 +33,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="objectType">The object type.</param>
|
/// <param name="objectType">The object type.</param>
|
||||||
/// <param name="existingValue">The object being read.</param>
|
/// <param name="existingValue">The object being read.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
return serializer.Deserialize<ManifestContentPackFor>(reader);
|
return serializer.Deserialize<ManifestContentPackFor>(reader);
|
||||||
}
|
}
|
||||||
|
@ -44,7 +42,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="writer">The JSON writer.</param>
|
/// <param name="writer">The JSON writer.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("This converter does not write JSON.");
|
throw new InvalidOperationException("This converter does not write JSON.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
@ -37,13 +35,13 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="objectType">The object type.</param>
|
/// <param name="objectType">The object type.</param>
|
||||||
/// <param name="existingValue">The object being read.</param>
|
/// <param name="existingValue">The object being read.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
List<ManifestDependency> result = new List<ManifestDependency>();
|
List<ManifestDependency> result = new List<ManifestDependency>();
|
||||||
foreach (JObject obj in JArray.Load(reader).Children<JObject>())
|
foreach (JObject obj in JArray.Load(reader).Children<JObject>())
|
||||||
{
|
{
|
||||||
string uniqueID = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.UniqueID));
|
string uniqueID = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.UniqueID))!; // will be validated separately if null
|
||||||
string minVersion = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.MinimumVersion));
|
string? minVersion = obj.ValueIgnoreCase<string>(nameof(ManifestDependency.MinimumVersion));
|
||||||
bool required = obj.ValueIgnoreCase<bool?>(nameof(ManifestDependency.IsRequired)) ?? true;
|
bool required = obj.ValueIgnoreCase<bool?>(nameof(ManifestDependency.IsRequired)) ?? true;
|
||||||
result.Add(new ManifestDependency(uniqueID, minVersion, required));
|
result.Add(new ManifestDependency(uniqueID, minVersion, required));
|
||||||
}
|
}
|
||||||
|
@ -54,7 +52,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="writer">The JSON writer.</param>
|
/// <param name="writer">The JSON writer.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("This converter does not write JSON.");
|
throw new InvalidOperationException("This converter does not write JSON.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
@ -41,15 +39,17 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="objectType">The object type.</param>
|
/// <param name="objectType">The object type.</param>
|
||||||
/// <param name="existingValue">The object being read.</param>
|
/// <param name="existingValue">The object being read.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
string path = reader.Path;
|
string path = reader.Path;
|
||||||
switch (reader.TokenType)
|
switch (reader.TokenType)
|
||||||
{
|
{
|
||||||
case JsonToken.StartObject:
|
case JsonToken.StartObject:
|
||||||
return this.ReadObject(JObject.Load(reader));
|
return this.ReadObject(JObject.Load(reader));
|
||||||
|
|
||||||
case JsonToken.String:
|
case JsonToken.String:
|
||||||
return this.ReadString(JToken.Load(reader).Value<string>(), path);
|
return this.ReadString(JToken.Load(reader).Value<string>(), path);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path}).");
|
throw new SParseException($"Can't parse {nameof(ISemanticVersion)} from {reader.TokenType} node (path: {reader.Path}).");
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <param name="writer">The JSON writer.</param>
|
/// <param name="writer">The JSON writer.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteValue(value?.ToString());
|
writer.WriteValue(value?.ToString());
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
int major = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MajorVersion));
|
int major = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MajorVersion));
|
||||||
int minor = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MinorVersion));
|
int minor = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.MinorVersion));
|
||||||
int patch = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.PatchVersion));
|
int patch = obj.ValueIgnoreCase<int>(nameof(ISemanticVersion.PatchVersion));
|
||||||
string prereleaseTag = obj.ValueIgnoreCase<string>(nameof(ISemanticVersion.PrereleaseTag));
|
string? prereleaseTag = obj.ValueIgnoreCase<string>(nameof(ISemanticVersion.PrereleaseTag));
|
||||||
|
|
||||||
return new SemanticVersion(major, minor, patch, prereleaseTag: prereleaseTag);
|
return new SemanticVersion(major, minor, patch, prereleaseTag: prereleaseTag);
|
||||||
}
|
}
|
||||||
|
@ -83,11 +83,11 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
/// <summary>Read a JSON string.</summary>
|
/// <summary>Read a JSON string.</summary>
|
||||||
/// <param name="str">The JSON string value.</param>
|
/// <param name="str">The JSON string value.</param>
|
||||||
/// <param name="path">The path to the current JSON node.</param>
|
/// <param name="path">The path to the current JSON node.</param>
|
||||||
private ISemanticVersion ReadString(string str, string path)
|
private ISemanticVersion? ReadString(string str, string path)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(str))
|
if (string.IsNullOrWhiteSpace(str))
|
||||||
return null;
|
return null;
|
||||||
if (!SemanticVersion.TryParse(str, allowNonStandard: this.AllowNonStandard, out ISemanticVersion version))
|
if (!SemanticVersion.TryParse(str, allowNonStandard: this.AllowNonStandard, out ISemanticVersion? version))
|
||||||
throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path}).");
|
throw new SParseException($"Can't parse semantic version from invalid value '{str}', should be formatted like 1.2, 1.2.30, or 1.2.30-beta (path: {path}).");
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
@ -27,21 +25,12 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
return objectType == typeof(T) || Nullable.GetUnderlyingType(objectType) == typeof(T);
|
return objectType == typeof(T) || Nullable.GetUnderlyingType(objectType) == typeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Writes the JSON representation of the object.</summary>
|
|
||||||
/// <param name="writer">The JSON writer.</param>
|
|
||||||
/// <param name="value">The value.</param>
|
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("This converter does not write JSON.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Reads the JSON representation of the object.</summary>
|
/// <summary>Reads the JSON representation of the object.</summary>
|
||||||
/// <param name="reader">The JSON reader.</param>
|
/// <param name="reader">The JSON reader.</param>
|
||||||
/// <param name="objectType">The object type.</param>
|
/// <param name="objectType">The object type.</param>
|
||||||
/// <param name="existingValue">The object being read.</param>
|
/// <param name="existingValue">The object being read.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
string path = reader.Path;
|
string path = reader.Path;
|
||||||
switch (reader.TokenType)
|
switch (reader.TokenType)
|
||||||
|
@ -60,6 +49,15 @@ namespace StardewModdingAPI.Toolkit.Serialization.Converters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Writes the JSON representation of the object.</summary>
|
||||||
|
/// <param name="writer">The JSON writer.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("This converter does not write JSON.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********
|
/*********
|
||||||
** Protected methods
|
** Protected methods
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
@ -12,12 +10,12 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
/// <typeparam name="T">The value type.</typeparam>
|
/// <typeparam name="T">The value type.</typeparam>
|
||||||
/// <param name="obj">The JSON object to search.</param>
|
/// <param name="obj">The JSON object to search.</param>
|
||||||
/// <param name="fieldName">The field name.</param>
|
/// <param name="fieldName">The field name.</param>
|
||||||
public static T ValueIgnoreCase<T>(this JObject obj, string fieldName)
|
public static T? ValueIgnoreCase<T>(this JObject obj, string fieldName)
|
||||||
{
|
{
|
||||||
JToken token = obj.GetValue(fieldName, StringComparison.OrdinalIgnoreCase);
|
JToken? token = obj.GetValue(fieldName, StringComparison.OrdinalIgnoreCase);
|
||||||
return token != null
|
return token != null
|
||||||
? token.Value<T>()
|
? token.Value<T>()
|
||||||
: default(T);
|
: default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
@ -38,7 +37,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
/// <returns>Returns false if the file doesn't exist, else true.</returns>
|
/// <returns>Returns false if the file doesn't exist, else true.</returns>
|
||||||
/// <exception cref="ArgumentException">The given <paramref name="fullPath"/> is empty or invalid.</exception>
|
/// <exception cref="ArgumentException">The given <paramref name="fullPath"/> is empty or invalid.</exception>
|
||||||
/// <exception cref="JsonReaderException">The file contains invalid JSON.</exception>
|
/// <exception cref="JsonReaderException">The file contains invalid JSON.</exception>
|
||||||
public bool ReadJsonFileIfExists<TModel>(string fullPath, out TModel result)
|
public bool ReadJsonFileIfExists<TModel>(string fullPath, [NotNullWhen(true)] out TModel? result)
|
||||||
{
|
{
|
||||||
// validate
|
// validate
|
||||||
if (string.IsNullOrWhiteSpace(fullPath))
|
if (string.IsNullOrWhiteSpace(fullPath))
|
||||||
|
@ -52,7 +51,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is DirectoryNotFoundException or FileNotFoundException)
|
catch (Exception ex) when (ex is DirectoryNotFoundException or FileNotFoundException)
|
||||||
{
|
{
|
||||||
result = default(TModel);
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +59,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = this.Deserialize<TModel>(json);
|
result = this.Deserialize<TModel>(json);
|
||||||
return true;
|
return result != null;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -90,7 +89,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
|
throw new ArgumentException("The file path is empty or invalid.", nameof(fullPath));
|
||||||
|
|
||||||
// create directory if needed
|
// create directory if needed
|
||||||
string dir = Path.GetDirectoryName(fullPath);
|
string dir = Path.GetDirectoryName(fullPath)!;
|
||||||
if (dir == null)
|
if (dir == null)
|
||||||
throw new ArgumentException("The file path is invalid.", nameof(fullPath));
|
throw new ArgumentException("The file path is invalid.", nameof(fullPath));
|
||||||
if (!Directory.Exists(dir))
|
if (!Directory.Exists(dir))
|
||||||
|
@ -108,7 +107,8 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<TModel>(json, this.JsonSettings);
|
return JsonConvert.DeserializeObject<TModel>(json, this.JsonSettings)
|
||||||
|
?? throw new InvalidOperationException($"Couldn't deserialize model type '{typeof(TModel)}' from empty or null JSON.");
|
||||||
}
|
}
|
||||||
catch (JsonReaderException)
|
catch (JsonReaderException)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +117,8 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<TModel>(json.Replace('“', '"').Replace('”', '"'), this.JsonSettings);
|
return JsonConvert.DeserializeObject<TModel>(json.Replace('“', '"').Replace('”', '"'), this.JsonSettings)
|
||||||
|
?? throw new InvalidOperationException($"Couldn't deserialize model type '{typeof(TModel)}' from empty or null JSON.");
|
||||||
}
|
}
|
||||||
catch { /* rethrow original error */ }
|
catch { /* rethrow original error */ }
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
|
||||||
/// <param name="uniqueID">The unique mod ID to require.</param>
|
/// <param name="uniqueID">The unique mod ID to require.</param>
|
||||||
/// <param name="minimumVersion">The minimum required version (if any).</param>
|
/// <param name="minimumVersion">The minimum required version (if any).</param>
|
||||||
/// <param name="required">Whether the dependency must be installed to use the mod.</param>
|
/// <param name="required">Whether the dependency must be installed to use the mod.</param>
|
||||||
public ManifestDependency(string uniqueID, string minimumVersion, bool required = true)
|
public ManifestDependency(string uniqueID, string? minimumVersion, bool required = true)
|
||||||
: this(
|
: this(
|
||||||
uniqueID: uniqueID,
|
uniqueID: uniqueID,
|
||||||
minimumVersion: !string.IsNullOrWhiteSpace(minimumVersion)
|
minimumVersion: !string.IsNullOrWhiteSpace(minimumVersion)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Serialization
|
namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
|
@ -13,7 +11,7 @@ namespace StardewModdingAPI.Toolkit.Serialization
|
||||||
/// <summary>Construct an instance.</summary>
|
/// <summary>Construct an instance.</summary>
|
||||||
/// <param name="message">The error message.</param>
|
/// <param name="message">The error message.</param>
|
||||||
/// <param name="ex">The underlying exception, if any.</param>
|
/// <param name="ex">The underlying exception, if any.</param>
|
||||||
public SParseException(string message, Exception ex = null)
|
public SParseException(string message, Exception? ex = null)
|
||||||
: base(message, ex) { }
|
: base(message, ex) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using StardewModdingAPI.Toolkit.Framework;
|
using StardewModdingAPI.Toolkit.Framework;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Toolkit.Utilities
|
namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
|
@ -36,7 +33,6 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
|
|
||||||
/// <summary>Get the human-readable OS name and version.</summary>
|
/// <summary>Get the human-readable OS name and version.</summary>
|
||||||
/// <param name="platform">The current platform.</param>
|
/// <param name="platform">The current platform.</param>
|
||||||
[SuppressMessage("ReSharper", "EmptyGeneralCatchClause", Justification = "Error suppressed deliberately to fallback to default behaviour.")]
|
|
||||||
public static string GetFriendlyPlatformName(Platform platform)
|
public static string GetFriendlyPlatformName(Platform platform)
|
||||||
{
|
{
|
||||||
return LowLevelEnvironmentUtility.GetFriendlyPlatformName(platform.ToString());
|
return LowLevelEnvironmentUtility.GetFriendlyPlatformName(platform.ToString());
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -38,8 +37,11 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <param name="path">The path to split.</param>
|
/// <param name="path">The path to split.</param>
|
||||||
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
|
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string[] GetSegments(string path, int? limit = null)
|
public static string[] GetSegments(string? path, int? limit = null)
|
||||||
{
|
{
|
||||||
|
if (path == null)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
return limit.HasValue
|
return limit.HasValue
|
||||||
? path.Split(PathUtilities.PossiblePathSeparators, limit.Value, StringSplitOptions.RemoveEmptyEntries)
|
? path.Split(PathUtilities.PossiblePathSeparators, limit.Value, StringSplitOptions.RemoveEmptyEntries)
|
||||||
: path.Split(PathUtilities.PossiblePathSeparators, StringSplitOptions.RemoveEmptyEntries);
|
: path.Split(PathUtilities.PossiblePathSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
@ -47,8 +49,14 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
|
|
||||||
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
|
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
|
||||||
/// <param name="assetName">The asset name to normalize.</param>
|
/// <param name="assetName">The asset name to normalize.</param>
|
||||||
public static string NormalizeAssetName(string assetName)
|
[Pure]
|
||||||
|
[return: NotNullIfNotNull("assetName")]
|
||||||
|
public static string? NormalizeAssetName(string? assetName)
|
||||||
{
|
{
|
||||||
|
assetName = assetName?.Trim();
|
||||||
|
if (string.IsNullOrEmpty(assetName))
|
||||||
|
return assetName;
|
||||||
|
|
||||||
return string.Join(PathUtilities.PreferredAssetSeparator.ToString(), PathUtilities.GetSegments(assetName)); // based on MonoGame's ContentManager.Load<T> logic
|
return string.Join(PathUtilities.PreferredAssetSeparator.ToString(), PathUtilities.GetSegments(assetName)); // based on MonoGame's ContentManager.Load<T> logic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +64,8 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <param name="path">The file path to normalize.</param>
|
/// <param name="path">The file path to normalize.</param>
|
||||||
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
|
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string NormalizePath(string path)
|
[return: NotNullIfNotNull("path")]
|
||||||
|
public static string? NormalizePath(string? path)
|
||||||
{
|
{
|
||||||
path = path?.Trim();
|
path = path?.Trim();
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
|
@ -80,7 +89,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
}
|
}
|
||||||
|
|
||||||
// keep trailing separator
|
// keep trailing separator
|
||||||
if ((!hasRoot || segments.Any()) && PathUtilities.PossiblePathSeparators.Contains(path[path.Length - 1]))
|
if ((!hasRoot || segments.Any()) && PathUtilities.PossiblePathSeparators.Contains(path[^1]))
|
||||||
newPath += PathUtilities.PreferredPathSeparator;
|
newPath += PathUtilities.PreferredPathSeparator;
|
||||||
|
|
||||||
return newPath;
|
return newPath;
|
||||||
|
@ -98,7 +107,7 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
||||||
/// <param name="path">The path to check.</param>
|
/// <param name="path">The path to check.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static bool IsSafeRelativePath(string path)
|
public static bool IsSafeRelativePath(string? path)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
return true;
|
return true;
|
||||||
|
@ -111,9 +120,11 @@ namespace StardewModdingAPI.Toolkit.Utilities
|
||||||
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
|
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
|
||||||
/// <param name="str">The string to check.</param>
|
/// <param name="str">The string to check.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static bool IsSlug(string str)
|
public static bool IsSlug(string? str)
|
||||||
{
|
{
|
||||||
return !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
|
return
|
||||||
|
string.IsNullOrWhiteSpace(str)
|
||||||
|
|| !Regex.IsMatch(str, "[^a-z0-9_.-]", RegexOptions.IgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
@ -38,7 +36,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
/// <param name="objectType">The object type.</param>
|
/// <param name="objectType">The object type.</param>
|
||||||
/// <param name="existingValue">The object being read.</param>
|
/// <param name="existingValue">The object being read.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
string path = reader.Path;
|
string path = reader.Path;
|
||||||
|
|
||||||
|
@ -76,7 +74,7 @@ namespace StardewModdingAPI.Framework.Serialization
|
||||||
/// <param name="writer">The JSON writer.</param>
|
/// <param name="writer">The JSON writer.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
writer.WriteValue(value?.ToString());
|
writer.WriteValue(value?.ToString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#nullable disable
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
|
using ToolkitPathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
|
||||||
|
|
||||||
|
@ -22,14 +21,16 @@ namespace StardewModdingAPI.Utilities
|
||||||
/// <param name="path">The path to split.</param>
|
/// <param name="path">The path to split.</param>
|
||||||
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
|
/// <param name="limit">The number of segments to match. Any additional segments will be merged into the last returned part.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string[] GetSegments(string path, int? limit = null)
|
public static string[] GetSegments(string? path, int? limit = null)
|
||||||
{
|
{
|
||||||
return ToolkitPathUtilities.GetSegments(path, limit);
|
return ToolkitPathUtilities.GetSegments(path, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
|
/// <summary>Normalize an asset name to match how MonoGame's content APIs would normalize and cache it.</summary>
|
||||||
/// <param name="assetName">The asset name to normalize.</param>
|
/// <param name="assetName">The asset name to normalize.</param>
|
||||||
public static string NormalizeAssetName(string assetName)
|
[Pure]
|
||||||
|
[return: NotNullIfNotNull("assetName")]
|
||||||
|
public static string? NormalizeAssetName(string? assetName)
|
||||||
{
|
{
|
||||||
return ToolkitPathUtilities.NormalizeAssetName(assetName);
|
return ToolkitPathUtilities.NormalizeAssetName(assetName);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,8 @@ namespace StardewModdingAPI.Utilities
|
||||||
/// <param name="path">The file path to normalize.</param>
|
/// <param name="path">The file path to normalize.</param>
|
||||||
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
|
/// <remarks>This should only be used for file paths. For asset names, use <see cref="NormalizeAssetName"/> instead.</remarks>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static string NormalizePath(string path)
|
[return: NotNullIfNotNull("path")]
|
||||||
|
public static string? NormalizePath(string? path)
|
||||||
{
|
{
|
||||||
return ToolkitPathUtilities.NormalizePath(path);
|
return ToolkitPathUtilities.NormalizePath(path);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +48,7 @@ namespace StardewModdingAPI.Utilities
|
||||||
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
/// <summary>Get whether a path is relative and doesn't try to climb out of its containing folder (e.g. doesn't contain <c>../</c>).</summary>
|
||||||
/// <param name="path">The path to check.</param>
|
/// <param name="path">The path to check.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static bool IsSafeRelativePath(string path)
|
public static bool IsSafeRelativePath(string? path)
|
||||||
{
|
{
|
||||||
return ToolkitPathUtilities.IsSafeRelativePath(path);
|
return ToolkitPathUtilities.IsSafeRelativePath(path);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +56,7 @@ namespace StardewModdingAPI.Utilities
|
||||||
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
|
/// <summary>Get whether a string is a valid 'slug', containing only basic characters that are safe in all contexts (e.g. filenames, URLs, etc).</summary>
|
||||||
/// <param name="str">The string to check.</param>
|
/// <param name="str">The string to check.</param>
|
||||||
[Pure]
|
[Pure]
|
||||||
public static bool IsSlug(string str)
|
public static bool IsSlug(string? str)
|
||||||
{
|
{
|
||||||
return ToolkitPathUtilities.IsSlug(str);
|
return ToolkitPathUtilities.IsSlug(str);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue