From 529e0dbb84548ac47d6b3ca9ac892b743171886e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Feb 2017 19:08:21 -0500 Subject: fix handling of localised XNB files (#173) --- .../Framework/ContentEventHelper.cs | 39 +++++++----------- src/StardewModdingAPI/Framework/SContentManager.cs | 48 ++++++++++++++-------- src/StardewModdingAPI/IContentEventHelper.cs | 14 ++++--- 3 files changed, 53 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/StardewModdingAPI/Framework/ContentEventHelper.cs b/src/StardewModdingAPI/Framework/ContentEventHelper.cs index d4a9bbb8..a58efe32 100644 --- a/src/StardewModdingAPI/Framework/ContentEventHelper.cs +++ b/src/StardewModdingAPI/Framework/ContentEventHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text.RegularExpressions; using Microsoft.Xna.Framework.Graphics; namespace StardewModdingAPI.Framework @@ -18,8 +17,11 @@ namespace StardewModdingAPI.Framework /********* ** Accessors *********/ - /// The normalised asset path being read. The format may change between platforms; see to compare with a known path. - public string Path { get; } + /// The content's locale code, if the content is localised. + public string Locale { get; } + + /// The normalised asset name being read. The format may change between platforms; see to compare with a known path. + public string AssetName { get; } /// The content data being read. public object Data { get; private set; } @@ -29,37 +31,24 @@ namespace StardewModdingAPI.Framework ** Public methods *********/ /// Construct an instance. - /// The file path being read. + /// The content's locale code, if the content is localised. + /// The normalised asset name being read. /// The content data being read. /// Normalises an asset key to match the cache key. - public ContentEventHelper(string path, object data, Func getNormalisedPath) + public ContentEventHelper(string locale, string assetName, object data, Func getNormalisedPath) { - this.Path = path; + this.Locale = locale; + this.AssetName = assetName; this.Data = data; this.GetNormalisedPath = getNormalisedPath; } - /// Get whether the asset path being loaded matches a given path after normalisation. - /// The expected asset path, relative to the game folder and without the .xnb extension (like 'Data\ObjectInformation'). - /// Whether to match a localised version of the asset file (like 'Data\ObjectInformation.ja-JP'). - public bool IsPath(string path, bool matchLocalisedVersion = true) + /// Get whether the asset name being loaded matches a given name after normalisation. + /// The expected asset path, relative to the game's content folder and without the .xnb extension or locale suffix (like 'Data\ObjectInformation'). + public bool IsAssetName(string path) { path = this.GetNormalisedPath(path); - - // equivalent - if (this.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase)) - return true; - - // localised version - if (matchLocalisedVersion) - { - return - this.Path.StartsWith($"{path}.", StringComparison.InvariantCultureIgnoreCase) // starts with given path - && Regex.IsMatch(this.Path.Substring(path.Length + 1), "^[a-z]+-[A-Z]+$"); // ends with locale (e.g. pt-BR) - } - - // no match - return false; + return this.AssetName.Equals(path, StringComparison.InvariantCultureIgnoreCase); } /// Get the data as a given type. diff --git a/src/StardewModdingAPI/Framework/SContentManager.cs b/src/StardewModdingAPI/Framework/SContentManager.cs index 45f42bf6..65c330db 100644 --- a/src/StardewModdingAPI/Framework/SContentManager.cs +++ b/src/StardewModdingAPI/Framework/SContentManager.cs @@ -31,7 +31,10 @@ namespace StardewModdingAPI.Framework private readonly IDictionary Cache; /// Applies platform-specific asset key normalisation so it's consistent with the underlying cache. - private readonly Func NormaliseKeyForPlatform; + private readonly Func NormaliseAssetNameForPlatform; + + /// The private method which generates the locale portion of an asset name. + private readonly IPrivateMethod GetKeyLocale; /********* @@ -57,19 +60,18 @@ namespace StardewModdingAPI.Framework this.Monitor = monitor; IReflectionHelper reflection = new ReflectionHelper(); - // get underlying asset cache - this.Cache = reflection - .GetPrivateField>(this, "loadedAssets") - .GetValue(); + // get underlying fields for interception + this.Cache = reflection.GetPrivateField>(this, "loadedAssets").GetValue(); + this.GetKeyLocale = reflection.GetPrivateMethod(this, "languageCode"); // get asset key normalisation logic if (Constants.TargetPlatform == Platform.Windows) { IPrivateMethod method = reflection.GetPrivateMethod(typeof(TitleContainer), "GetCleanPath"); - this.NormaliseKeyForPlatform = path => method.Invoke(path); + this.NormaliseAssetNameForPlatform = path => method.Invoke(path); } else - this.NormaliseKeyForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load logic + this.NormaliseAssetNameForPlatform = key => key.Replace('\\', '/'); // based on MonoGame's ContentManager.Load logic } /// Load an asset that has been processed by the content pipeline. @@ -77,8 +79,8 @@ namespace StardewModdingAPI.Framework /// The asset path relative to the loader root directory, not including the .xnb extension. public override T Load(string assetName) { - // normalise key so can override the cache value later - assetName = this.NormaliseKey(assetName); + // normalise asset name so can override the cache value later + assetName = this.NormaliseAssetName(assetName); // skip if no event handlers or already loaded if (!ContentEvents.HasAssetLoadingListeners() || this.Cache.ContainsKey(assetName)) @@ -86,7 +88,8 @@ namespace StardewModdingAPI.Framework // intercept load T data = base.Load(assetName); - IContentEventHelper helper = new ContentEventHelper(assetName, data, this.NormaliseKeyForPlatform); + string cacheLocale = this.GetCacheLocale(assetName, this.Cache); + IContentEventHelper helper = new ContentEventHelper(cacheLocale, assetName, data, this.NormaliseAssetName); ContentEvents.InvokeAssetLoading(this.Monitor, helper); this.Cache[assetName] = helper.Data; return (T)helper.Data; @@ -96,16 +99,27 @@ namespace StardewModdingAPI.Framework /********* ** Private methods *********/ - /// Normalise an asset key so it's consistent with the underlying cache. - /// The asset key. - private string NormaliseKey(string key) + /// Normalise an asset name so it's consistent with the underlying cache. + /// The asset key. + private string NormaliseAssetName(string assetName) { - // ensure key format is consistent - string[] parts = key.Split(SContentManager.PossiblePathSeparators, StringSplitOptions.RemoveEmptyEntries); - key = string.Join(SContentManager.PreferredPathSeparator, parts); + // ensure name format is consistent + string[] parts = assetName.Split(SContentManager.PossiblePathSeparators, StringSplitOptions.RemoveEmptyEntries); + assetName = string.Join(SContentManager.PreferredPathSeparator, parts); // apply platform normalisation logic - return this.NormaliseKeyForPlatform(key); + return this.NormaliseAssetNameForPlatform(assetName); + } + + /// Get the locale for which the asset name was saved, if any. + /// The normalised asset name. + /// The cache to search. + private string GetCacheLocale(string normalisedAssetName, IDictionary cache) + { + string locale = this.GetKeyLocale.Invoke(); + return this.Cache.ContainsKey($"{normalisedAssetName}.{this.GetKeyLocale.Invoke()}") + ? locale + : null; } } } diff --git a/src/StardewModdingAPI/IContentEventHelper.cs b/src/StardewModdingAPI/IContentEventHelper.cs index 98d074d9..be8290eb 100644 --- a/src/StardewModdingAPI/IContentEventHelper.cs +++ b/src/StardewModdingAPI/IContentEventHelper.cs @@ -8,8 +8,11 @@ namespace StardewModdingAPI /********* ** Accessors *********/ - /// The normalised asset path being read. The format may change between platforms; see to compare with a known path. - string Path { get; } + /// The content's locale code, if the content is localised. + string Locale { get; } + + /// The normalised asset name being read. The format may change between platforms; see to compare with a known path. + string AssetName { get; } /// The content data being read. object Data { get; } @@ -18,10 +21,9 @@ namespace StardewModdingAPI /********* ** Public methods *********/ - /// Get whether the asset path being loaded matches a given path after normalisation. - /// The expected asset path, relative to the game folder and without the .xnb extension (like 'Data\ObjectInformation'). - /// Whether to match a localised version of the asset file (like 'Data\ObjectInformation.ja-JP'). - bool IsPath(string path, bool matchLocalisedVersion = true); + /// Get whether the asset name being loaded matches a given name after normalisation. + /// The expected asset path, relative to the game's content folder and without the .xnb extension or locale suffix (like 'Data\ObjectInformation'). + bool IsAssetName(string path); /// Get the data as a given type. /// The expected data type. -- cgit