diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-04-01 22:59:50 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-04-01 22:59:50 -0400 |
commit | bac1f34f65412419656eea15ea81081f8a29867e (patch) | |
tree | 7721b47edcf67e33b2995ecea6b870fc60a270a1 /src/SMAPI | |
parent | 6ad8ca932eb697a76dd9df43d6ae93c7ca4b2af5 (diff) | |
download | SMAPI-bac1f34f65412419656eea15ea81081f8a29867e.tar.gz SMAPI-bac1f34f65412419656eea15ea81081f8a29867e.tar.bz2 SMAPI-bac1f34f65412419656eea15ea81081f8a29867e.zip |
fix local file path asset name parsing locale codes in rare cases (#766)
Mod file paths can't be localized through the content pipeline. Normally the locale would be ignored anyway due to the file extension, but it'd be incorrectly parsed if the file name ended with a locale and no file extension (like "assets/example.fr-FR").
Diffstat (limited to 'src/SMAPI')
6 files changed, 30 insertions, 24 deletions
diff --git a/src/SMAPI/Framework/ContentCoordinator.cs b/src/SMAPI/Framework/ContentCoordinator.cs index 108257bf..8483d45d 100644 --- a/src/SMAPI/Framework/ContentCoordinator.cs +++ b/src/SMAPI/Framework/ContentCoordinator.cs @@ -156,7 +156,7 @@ namespace StardewModdingAPI.Framework ); this.ContentManagers.Add(contentManagerForAssetPropagation); this.VanillaContentManager = new LocalizedContentManager(serviceProvider, rootDirectory); - this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, this.ParseAssetName); + this.CoreAssets = new CoreAssetPropagator(this.MainContentManager, contentManagerForAssetPropagation, this.Monitor, reflection, aggressiveMemoryOptimizations, name => this.ParseAssetName(name, allowLocales: true)); this.LocaleCodes = new Lazy<Dictionary<string, LocalizedContentManager.LanguageCode>>(() => this.GetLocaleCodes(customLanguages: Enumerable.Empty<ModLanguage>())); } @@ -269,11 +269,17 @@ namespace StardewModdingAPI.Framework /// <summary>Parse a raw asset name.</summary> /// <param name="rawName">The raw asset name to parse.</param> + /// <param name="allowLocales">Whether to parse locales in the <paramref name="rawName"/>. If this is false, any locale codes in the name are treated as if they were part of the base name (e.g. for mod files).</param> /// <exception cref="ArgumentException">The <paramref name="rawName"/> is null or empty.</exception> - public AssetName ParseAssetName(string rawName) + public AssetName ParseAssetName(string rawName, bool allowLocales) { return !string.IsNullOrWhiteSpace(rawName) - ? AssetName.Parse(rawName, parseLocale: locale => this.LocaleCodes.Value.TryGetValue(locale, out LocalizedContentManager.LanguageCode langCode) ? langCode : null) + ? AssetName.Parse( + rawName: rawName, + parseLocale: allowLocales + ? locale => this.LocaleCodes.Value.TryGetValue(locale, out LocalizedContentManager.LanguageCode langCode) ? langCode : null + : _ => null + ) : throw new ArgumentException("The asset name can't be null or empty.", nameof(rawName)); } @@ -303,7 +309,7 @@ namespace StardewModdingAPI.Framework if (parts.Length != 3) // managed key prefix, mod id, relative path return false; contentManagerID = Path.Combine(parts[0], parts[1]); - relativePath = this.ParseAssetName(parts[2]); + relativePath = this.ParseAssetName(parts[2], allowLocales: false); return true; } @@ -357,7 +363,7 @@ namespace StardewModdingAPI.Framework string locale = this.GetLocale(); return this.InvalidateCache((_, rawName, type) => { - IAssetName assetName = this.ParseAssetName(rawName); + IAssetName assetName = this.ParseAssetName(rawName, allowLocales: true); IAssetInfo info = new AssetInfo(locale, assetName, type, this.MainContentManager.AssertAndNormalizeAssetName); return predicate(info); }, dispose); @@ -378,7 +384,7 @@ namespace StardewModdingAPI.Framework { foreach ((string key, object asset) in contentManager.InvalidateCache((key, type) => predicate(contentManager, key, type), dispose)) { - AssetName assetName = this.ParseAssetName(key); + AssetName assetName = this.ParseAssetName(key, allowLocales: true); if (!invalidatedAssets.ContainsKey(assetName)) invalidatedAssets[assetName] = asset.GetType(); } @@ -394,7 +400,7 @@ namespace StardewModdingAPI.Framework continue; // get map path - AssetName mapPath = this.ParseAssetName(this.MainContentManager.AssertAndNormalizeAssetName(location.mapPath.Value)); + AssetName mapPath = this.ParseAssetName(this.MainContentManager.AssertAndNormalizeAssetName(location.mapPath.Value), allowLocales: true); if (!invalidatedAssets.ContainsKey(mapPath) && predicate(this.MainContentManager, mapPath.Name, typeof(Map))) invalidatedAssets[mapPath] = typeof(Map); } diff --git a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs index 2d921cc3..31199b3a 100644 --- a/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/BaseContentManager.cs @@ -119,7 +119,7 @@ namespace StardewModdingAPI.Framework.ContentManagers public sealed override T Load<T>(string assetName, LanguageCode language) { assetName = this.PrenormalizeRawAssetName(assetName); - IAssetName parsedName = this.Coordinator.ParseAssetName(assetName); + IAssetName parsedName = this.Coordinator.ParseAssetName(assetName, allowLocales: this.TryLocalizeKeys); return this.LoadLocalized<T>(parsedName, language, useCache: true); } @@ -161,7 +161,7 @@ namespace StardewModdingAPI.Framework.ContentManagers // use cached key string rawName = LocalizedContentManager.localizedAssetNames[assetName.Name]; if (assetName.Name != rawName) - assetName = this.Coordinator.ParseAssetName(rawName); + assetName = this.Coordinator.ParseAssetName(rawName, allowLocales: this.TryLocalizeKeys); return this.LoadExact<T>(assetName, useCache: useCache); } @@ -213,7 +213,7 @@ namespace StardewModdingAPI.Framework.ContentManagers IDictionary<string, object> removeAssets = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); this.Cache.Remove((key, asset) => { - string baseAssetName = this.Coordinator.ParseAssetName(key).BaseName; + string baseAssetName = this.Coordinator.ParseAssetName(key, allowLocales: this.TryLocalizeKeys).BaseName; // check if asset should be removed bool remove = removeAssets.ContainsKey(baseAssetName); diff --git a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs index 63b40d66..9ed989da 100644 --- a/src/SMAPI/Framework/ContentManagers/ModContentManager.cs +++ b/src/SMAPI/Framework/ContentManagers/ModContentManager.cs @@ -111,7 +111,7 @@ namespace StardewModdingAPI.Framework.ContentManagers { // the underlying content manager adds a .xnb extension implicitly, so // we need to strip it here to avoid trying to load a '.xnb.xnb' file. - IAssetName loadName = this.Coordinator.ParseAssetName(assetName.Name[..^".xnb".Length]); + IAssetName loadName = this.Coordinator.ParseAssetName(assetName.Name[..^".xnb".Length], allowLocales: false); // load asset asset = this.RawLoad<T>(loadName, useCache: false); @@ -201,7 +201,7 @@ namespace StardewModdingAPI.Framework.ContentManagers string relativePath = PathUtilities.GetRelativePath(this.RootDirectory, file.FullName); string internalKey = Path.Combine(this.Name, relativePath); - return this.Coordinator.ParseAssetName(internalKey); + return this.Coordinator.ParseAssetName(internalKey, allowLocales: false); } @@ -343,7 +343,7 @@ namespace StardewModdingAPI.Framework.ContentManagers } // get from game assets - IAssetName contentKey = this.Coordinator.ParseAssetName(this.GetContentKeyForTilesheetImageSource(relativePath)); + IAssetName contentKey = this.Coordinator.ParseAssetName(this.GetContentKeyForTilesheetImageSource(relativePath), allowLocales: false); try { this.GameContentManager.LoadLocalized<Texture2D>(contentKey, this.GameContentManager.Language, useCache: true); // no need to bypass cache here, since we're not storing the asset diff --git a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs index b0064532..ae914c46 100644 --- a/src/SMAPI/Framework/ModHelpers/ContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ContentHelper.cs @@ -107,7 +107,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <inheritdoc /> public T Load<T>(string key, ContentSource source = ContentSource.ModFolder) { - IAssetName assetName = this.ContentCore.ParseAssetName(key); + IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: source == ContentSource.GameContent); try { @@ -157,21 +157,21 @@ namespace StardewModdingAPI.Framework.ModHelpers public bool InvalidateCache(string key) { string actualKey = this.GetActualAssetKey(key, ContentSource.GameContent); - this.Monitor.Log($"Requested cache invalidation for '{actualKey}'.", LogLevel.Trace); + this.Monitor.Log($"Requested cache invalidation for '{actualKey}'."); return this.ContentCore.InvalidateCache(asset => asset.Name.IsEquivalentTo(actualKey)).Any(); } /// <inheritdoc /> public bool InvalidateCache<T>() { - this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible.", LogLevel.Trace); - return this.ContentCore.InvalidateCache((contentManager, key, type) => typeof(T).IsAssignableFrom(type)).Any(); + this.Monitor.Log($"Requested cache invalidation for all assets of type {typeof(T)}. This is an expensive operation and should be avoided if possible."); + return this.ContentCore.InvalidateCache((_, _, type) => typeof(T).IsAssignableFrom(type)).Any(); } /// <inheritdoc /> public bool InvalidateCache(Func<IAssetInfo, bool> predicate) { - this.Monitor.Log("Requested cache invalidation for all assets matching a predicate.", LogLevel.Trace); + this.Monitor.Log("Requested cache invalidation for all assets matching a predicate."); return this.ContentCore.InvalidateCache(predicate).Any(); } @@ -183,7 +183,7 @@ namespace StardewModdingAPI.Framework.ModHelpers assetName ??= $"temp/{Guid.NewGuid():N}"; - return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName), data, this.NormalizeAssetName); + return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName, allowLocales: true/* no way to know if it's a game or mod asset here*/), data, this.NormalizeAssetName); } diff --git a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs index 42a4de20..0eb385d4 100644 --- a/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/GameContentHelper.cs @@ -58,13 +58,13 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <inheritdoc /> public IAssetName ParseAssetName(string rawName) { - return this.ContentCore.ParseAssetName(rawName); + return this.ContentCore.ParseAssetName(rawName, allowLocales: true); } /// <inheritdoc /> public T Load<T>(string key) { - IAssetName assetName = this.ContentCore.ParseAssetName(key); + IAssetName assetName = this.ContentCore.ParseAssetName(key, allowLocales: true); return this.Load<T>(assetName); } @@ -117,7 +117,7 @@ namespace StardewModdingAPI.Framework.ModHelpers assetName ??= $"temp/{Guid.NewGuid():N}"; - return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName), data, key => this.ParseAssetName(key).Name); + return new AssetDataForObject(this.CurrentLocale, this.ContentCore.ParseAssetName(assetName, allowLocales: true), data, key => this.ParseAssetName(key).Name); } /// <summary>Get the underlying game content manager.</summary> diff --git a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs index 45899dd7..2379583c 100644 --- a/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs +++ b/src/SMAPI/Framework/ModHelpers/ModContentHelper.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Framework.ModHelpers /// <inheritdoc /> public T Load<T>(string relativePath) { - IAssetName assetName = this.ContentCore.ParseAssetName(relativePath); + IAssetName assetName = this.ContentCore.ParseAssetName(relativePath, allowLocales: false); try { @@ -69,7 +69,7 @@ namespace StardewModdingAPI.Framework.ModHelpers relativePath ??= $"temp/{Guid.NewGuid():N}"; - return new AssetDataForObject(this.ContentCore.GetLocale(), this.ContentCore.ParseAssetName(relativePath), data, key => this.ContentCore.ParseAssetName(key).Name); + return new AssetDataForObject(this.ContentCore.GetLocale(), this.ContentCore.ParseAssetName(relativePath, allowLocales: false), data, key => this.ContentCore.ParseAssetName(key, allowLocales: false).Name); } } } |