diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs
index 357e02b5..70761a2f 100644
--- a/src/SMAPI.ModBuildConfig/DeployModTask.cs
+++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs
@@ -9,6 +9,7 @@ using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Newtonsoft.Json;
using StardewModdingAPI.ModBuildConfig.Framework;
+using StardewModdingAPI.Toolkit.Framework;
using StardewModdingAPI.Toolkit.Serialization;
using StardewModdingAPI.Toolkit.Serialization.Models;
using StardewModdingAPI.Toolkit.Utilities;
@@ -91,7 +92,8 @@ namespace StardewModdingAPI.ModBuildConfig
try
{
new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest);
- } catch (JsonReaderException ex)
+ }
+ catch (JsonReaderException ex)
{
// log the inner exception, otherwise the message will be generic
Exception exToShow = ex.InnerException ?? ex;
@@ -100,7 +102,7 @@ namespace StardewModdingAPI.ModBuildConfig
}
// validate the manifest's fields
- if (!manifest.TryValidate(out string error))
+ if (!ManifestValidator.TryValidate(manifest, out string error))
{
this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}");
return false;
diff --git a/src/SMAPI.Toolkit/Framework/ManifestValidator.cs b/src/SMAPI.Toolkit/Framework/ManifestValidator.cs
new file mode 100644
index 00000000..62cfd8e9
--- /dev/null
+++ b/src/SMAPI.Toolkit/Framework/ManifestValidator.cs
@@ -0,0 +1,101 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using StardewModdingAPI.Toolkit.Utilities;
+
+namespace StardewModdingAPI.Toolkit.Framework
+{
+ /// Validates manifest fields.
+ public static class ManifestValidator
+ {
+ /// Try to validate a manifest's fields. Fails if any invalid field is found.
+ /// The manifest to validate.
+ /// The error message to display to the user.
+ /// Returns whether the manifest was validated successfully.
+ public static bool TryValidate(IManifest manifest, out string error)
+ {
+ // validate DLL / content pack fields
+ bool hasDll = !string.IsNullOrWhiteSpace(manifest.EntryDll);
+ bool isContentPack = manifest.ContentPackFor != null;
+
+ // validate field presence
+ if (!hasDll && !isContentPack)
+ {
+ error = $"manifest has no {nameof(IManifest.EntryDll)} or {nameof(IManifest.ContentPackFor)} field; must specify one.";
+ return false;
+ }
+ if (hasDll && isContentPack)
+ {
+ error = $"manifest sets both {nameof(IManifest.EntryDll)} and {nameof(IManifest.ContentPackFor)}, which are mutually exclusive.";
+ return false;
+ }
+
+ // validate DLL filename format
+ if (hasDll && manifest.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any())
+ {
+ error = $"manifest has invalid filename '{manifest.EntryDll}' for the EntryDLL field.";
+ return false;
+ }
+
+ // validate content pack ID
+ else if (isContentPack && string.IsNullOrWhiteSpace(manifest.ContentPackFor!.UniqueID))
+ {
+ error = $"manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field.";
+ return false;
+ }
+
+ // validate required fields
+ {
+ List missingFields = new List(3);
+
+ if (string.IsNullOrWhiteSpace(manifest.Name))
+ missingFields.Add(nameof(IManifest.Name));
+ if (manifest.Version == null || manifest.Version.ToString() == "0.0.0")
+ missingFields.Add(nameof(IManifest.Version));
+ if (string.IsNullOrWhiteSpace(manifest.UniqueID))
+ missingFields.Add(nameof(IManifest.UniqueID));
+
+ if (missingFields.Any())
+ {
+ error = $"manifest is missing required fields ({string.Join(", ", missingFields)}).";
+ return false;
+ }
+ }
+
+ // validate ID format
+ if (!PathUtilities.IsSlug(manifest.UniqueID))
+ {
+ error = "manifest specifies an invalid ID (IDs must only contain letters, numbers, underscores, periods, or hyphens).";
+ return false;
+ }
+
+ // validate dependencies
+ foreach (IManifestDependency? dependency in manifest.Dependencies)
+ {
+ // null dependency
+ if (dependency == null)
+ {
+ error = $"manifest has a null entry under {nameof(IManifest.Dependencies)}.";
+ return false;
+ }
+
+ // missing ID
+ if (string.IsNullOrWhiteSpace(dependency.UniqueID))
+ {
+ error = $"manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field.";
+ return false;
+ }
+
+ // invalid ID
+ if (!PathUtilities.IsSlug(dependency.UniqueID))
+ {
+ error = $"manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens).";
+ return false;
+ }
+ }
+
+ error = "";
+ return true;
+ }
+ }
+}
diff --git a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
index 1607cf3e..8a449f0a 100644
--- a/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
+++ b/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
using System.Text;
using Newtonsoft.Json;
using StardewModdingAPI.Toolkit.Serialization.Converters;
-using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Toolkit.Serialization.Models
{
@@ -106,95 +103,6 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models
this.UpdateKeys = updateKeys ?? Array.Empty();
}
- /// Try to validate a manifest's fields. Fails if any invalid field is found.
- /// The error message to display to the user.
- /// Returns whether the manifest was validated successfully.
- public bool TryValidate(out string error)
- {
- // validate DLL / content pack fields
- bool hasDll = !string.IsNullOrWhiteSpace(this.EntryDll);
- bool isContentPack = this.ContentPackFor != null;
-
- // validate field presence
- if (!hasDll && !isContentPack)
- {
- error = $"manifest has no {nameof(IManifest.EntryDll)} or {nameof(IManifest.ContentPackFor)} field; must specify one.";
- return false;
- }
- if (hasDll && isContentPack)
- {
- error = $"manifest sets both {nameof(IManifest.EntryDll)} and {nameof(IManifest.ContentPackFor)}, which are mutually exclusive.";
- return false;
- }
-
- // validate DLL filename format
- if (hasDll && this.EntryDll!.Intersect(Path.GetInvalidFileNameChars()).Any())
- {
- error = $"manifest has invalid filename '{this.EntryDll}' for the EntryDLL field.";
- return false;
- }
-
- // validate content pack ID
- else if (isContentPack && string.IsNullOrWhiteSpace(this.ContentPackFor!.UniqueID))
- {
- error = $"manifest declares {nameof(IManifest.ContentPackFor)} without its required {nameof(IManifestContentPackFor.UniqueID)} field.";
- return false;
- }
-
- // validate required fields
- {
- List missingFields = new List(3);
-
- if (string.IsNullOrWhiteSpace(this.Name))
- missingFields.Add(nameof(IManifest.Name));
- if (this.Version == null || this.Version.ToString() == "0.0.0")
- missingFields.Add(nameof(IManifest.Version));
- if (string.IsNullOrWhiteSpace(this.UniqueID))
- missingFields.Add(nameof(IManifest.UniqueID));
-
- if (missingFields.Any())
- {
- error = $"manifest is missing required fields ({string.Join(", ", missingFields)}).";
- return false;
- }
- }
-
- // validate ID format
- if (!PathUtilities.IsSlug(this.UniqueID))
- {
- error = "manifest specifies an invalid ID (IDs must only contain letters, numbers, underscores, periods, or hyphens).";
- return false;
- }
-
- // validate dependencies
- foreach (IManifestDependency? dependency in this.Dependencies)
- {
- // null dependency
- if (dependency == null)
- {
- error = $"manifest has a null entry under {nameof(IManifest.Dependencies)}.";
- return false;
- }
-
- // missing ID
- if (string.IsNullOrWhiteSpace(dependency.UniqueID))
- {
- error = $"manifest has a {nameof(IManifest.Dependencies)} entry with no {nameof(IManifestDependency.UniqueID)} field.";
- return false;
- }
-
- // invalid ID
- if (!PathUtilities.IsSlug(dependency.UniqueID))
- {
- error = $"manifest has a {nameof(IManifest.Dependencies)} entry with an invalid {nameof(IManifestDependency.UniqueID)} field (IDs must only contain letters, numbers, underscores, periods, or hyphens).";
- return false;
- }
- }
-
- error = "";
- return true;
- }
-
/// Override the update keys loaded from the mod info.
/// The new update keys to set.
internal void OverrideUpdateKeys(params string[] updateKeys)
diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs
index 352c22cc..0b4fe3e9 100644
--- a/src/SMAPI/Framework/ModLoading/ModResolver.cs
+++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using StardewModdingAPI.Toolkit;
+using StardewModdingAPI.Toolkit.Framework;
using StardewModdingAPI.Toolkit.Framework.ModData;
using StardewModdingAPI.Toolkit.Framework.ModScanning;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
@@ -138,7 +139,7 @@ namespace StardewModdingAPI.Framework.ModLoading
}
// validate manifest
- if (mod.Manifest is Manifest manifest && !manifest.TryValidate(out string manifestError))
+ if (!ManifestValidator.TryValidate(mod.Manifest, out string manifestError))
{
mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.InvalidManifest, $"its {manifestError}");
continue;