diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
index adb09556..2ee2d616 100644
--- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
+++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs
@@ -412,9 +412,8 @@ internal sealed class ModContentManager : BaseContentManager
imageSource = imageSource[(relativeMapFolder.Length + 1)..];
// validate tilesheet path
- string errorPrefix = $"{this.ModName} loaded map '{relativeMapPath}' with invalid tilesheet path '{imageSource}'.";
if (Path.IsPathRooted(imageSource))
- throw new SContentLoadException(ContentLoadErrorType.InvalidData, $"{errorPrefix} Tilesheet paths must not be an absolute path ({Path.GetPathRoot(imageSource)}).");
+ throw this.GetPathError(relativeMapFolder, imageSource, $"Tilesheet paths must not be an absolute path ({Path.GetPathRoot(imageSource)}).");
string[] imageSourcePathSegments = PathUtilities.GetSegments(imageSource);
if (imageSourcePathSegments.Contains(".."))
@@ -422,8 +421,8 @@ internal sealed class ModContentManager : BaseContentManager
int climbingCount = imageSourcePathSegments.Count(segment => segment.Equals("..", StringComparison.OrdinalIgnoreCase));
if (climbingCount > 1)
{
- string[] offendingSegments = imageSourcePathSegments.TakeWhile(seg => seg.Equals("..")).ToArray();
- throw new SContentLoadException(ContentLoadErrorType.InvalidData, $"{errorPrefix} Tilesheet paths must not climb more than one directory ({string.Join('/', offendingSegments)}/).");
+ var offendingSegments = imageSourcePathSegments.TakeWhile(seg => seg.Equals(".."));
+ throw this.GetPathError(relativeMapFolder, imageSource, $"Tilesheet paths must not climb more than one directory ({string.Join('/', offendingSegments)}/).");
}
}
@@ -431,7 +430,7 @@ internal sealed class ModContentManager : BaseContentManager
try
{
if (!this.TryGetTilesheetAssetName(relativeMapFolder, imageSource, out IAssetName? assetName, out string? error))
- throw new SContentLoadException(ContentLoadErrorType.InvalidData, $"{errorPrefix} {error}");
+ throw this.GetPathError(relativeMapFolder, imageSource, error);
if (assetName is not null)
{
@@ -446,7 +445,7 @@ internal sealed class ModContentManager : BaseContentManager
if (ex is SContentLoadException)
throw;
- throw new SContentLoadException(ContentLoadErrorType.InvalidData, $"{errorPrefix} The tilesheet couldn't be loaded.", ex);
+ throw this.GetPathError(relativeMapFolder, imageSource, "The tilesheet couldn't be loaded.", ex);
}
}
}
@@ -455,10 +454,10 @@ internal sealed class ModContentManager : BaseContentManager
/// The folder path containing the map, relative to the mod folder.
/// The tilesheet path to load.
/// The found asset name.
- /// A message indicating why the file couldn't be loaded.
+ /// A message indicating why the file couldn't be loaded, if applicable.
/// Returns whether the asset name was found.
/// See remarks on .
- private bool TryGetTilesheetAssetName(string modRelativeMapFolder, string relativePath, out IAssetName? assetName, out string? error)
+ private bool TryGetTilesheetAssetName(string modRelativeMapFolder, string relativePath, out IAssetName? assetName, [NotNullWhen(false)] out string? error)
{
error = null;
@@ -479,7 +478,8 @@ internal sealed class ModContentManager : BaseContentManager
}
// get relative to map file unless path has directory climbing
- if (!PathUtilities.GetSegments(relativePath).Contains("..")) {
+ if (!PathUtilities.GetSegments(relativePath).Contains(".."))
+ {
string localKey = Path.Combine(modRelativeMapFolder, relativePath);
if (this.GetModFile(localKey).Exists)
{
@@ -549,4 +549,14 @@ internal sealed class ModContentManager : BaseContentManager
return key;
}
+
+ /// Get an exception indicating that a map asset can't be loaded because one of its tilesheets has an invalid path.
+ /// The relative path to the folder which contains the map asset, relative to the mod folder.
+ /// The tilesheet's path to the image file.
+ /// The error message indicating why loading it failed. (Basic metadata like the image source is prepended automatically.)
+ /// The inner exception which caused the error, if applicable.
+ private SContentLoadException GetPathError(string relativeMapPath, string imageSource, string error, Exception? ex = null)
+ {
+ return new SContentLoadException(ContentLoadErrorType.InvalidData, $"{this.ModName} loaded map '{relativeMapPath}' with invalid tilesheet path '{imageSource}'. {error}", ex);
+ }
}