diff --git a/docs/release-notes.md b/docs/release-notes.md index 04874729..4875d1cd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -12,6 +12,7 @@ * Fixed update alert shown for a prerelease version on GitHub if it's not marked as prerelease. * For mod authors: + * SMAPI now treats square brackets in the manifest `Name` field as round brackets, to avoid breaking tools which parse log files. * Updated to [FluentHttpClient](https://github.com/Pathoschild/FluentHttpClient#readme) 4.2.0 (see [changes](https://github.com/Pathoschild/FluentHttpClient/blob/develop/RELEASE-NOTES.md#420)). ## 3.16.2 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(); this.UpdateKeys = updateKeys ?? Array.Empty(); @@ -113,17 +114,47 @@ namespace StardewModdingAPI.Toolkit.Serialization.Models /********* ** Private methods *********/ - /// Normalize whitespace in a raw string. + /// Normalize a manifest field to strip newlines, trim whitespace, and optionally strip square brackets. /// The input to strip. + /// Whether to replace square brackets with round ones. This is used in the mod name to avoid breaking the log format. #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; } } }