diff options
-rw-r--r-- | docs/release-notes.md | 4 | ||||
-rw-r--r-- | src/SMAPI/Constants.cs | 31 | ||||
-rw-r--r-- | src/SMAPI/Framework/SCore.cs | 8 | ||||
-rw-r--r-- | src/SMAPI/Patches/SaveGamePatcher.cs | 55 |
4 files changed, 73 insertions, 25 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index 36f07129..eeff3347 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,6 +1,10 @@ ← [README](README.md) # Release notes +## Upcoming release +* For mod authors: + * Fixed save constants not set correctly in edge cases where the folder name doesn't match the save ID. + ## 3.12.0 01 August 2021 for Stardew Valley 1.5.4 or later. See [release highlights](https://www.patreon.com/posts/54388616). diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 3877e17a..bfaee70e 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Reflection; using StardewModdingAPI.Enums; using StardewModdingAPI.Framework; @@ -165,6 +164,9 @@ namespace StardewModdingAPI /// <summary>The language code for non-translated mod assets.</summary> internal static LocalizedContentManager.LanguageCode DefaultLanguage { get; } = LocalizedContentManager.LanguageCode.en; + /// <summary>The name of the last save file loaded by the game.</summary> + internal static string LastRawSaveFileName { get; set; } + /********* ** Internal methods @@ -341,30 +343,9 @@ namespace StardewModdingAPI if (Context.LoadStage == LoadStage.None) return null; - // get basic info - string rawSaveName = Game1.GetSaveGameName(set_value: false); - ulong saveID = Context.LoadStage == LoadStage.SaveParsed - ? SaveGame.loaded.uniqueIDForThisGame - : Game1.uniqueIDForThisGame; - - // get best match (accounting for rare case where folder name isn't sanitized) - DirectoryInfo folder = null; - foreach (string saveName in new[] { rawSaveName, new string(rawSaveName.Where(char.IsLetterOrDigit).ToArray()) }) - { - try - { - folder = new DirectoryInfo(Path.Combine(Constants.SavesPath, $"{saveName}_{saveID}")); - if (folder.Exists) - return folder; - } - catch (ArgumentException) - { - // ignore invalid path - } - } - - // if save doesn't exist yet, return the default one we expect to be created - return folder; + // get save + string rawSaveName = Constants.LastRawSaveFileName; + return new DirectoryInfo(Path.Combine(Constants.SavesPath, rawSaveName)); } } } diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index a34b3eff..b826789d 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -257,6 +257,7 @@ namespace StardewModdingAPI.Framework MiniMonoModHotfix.Apply(); HarmonyPatcher.Apply("SMAPI", this.Monitor, new Game1Patcher(this.Reflection, this.OnLoadStageChanged), + new SaveGamePatcher(this.OnSaveFileReading), new TitleMenuPatcher(this.OnLoadStageChanged) ); @@ -1101,6 +1102,13 @@ namespace StardewModdingAPI.Framework this.EventManager.ReturnedToTitle.RaiseEmpty(); } + /// <summary>Raised before the game begins reading a save file.</summary> + /// <param name="fileName">The save folder name.</param> + internal void OnSaveFileReading(string fileName) + { + Constants.LastRawSaveFileName = fileName; + } + /// <summary>Apply fixes to the save after it's loaded.</summary> private void ApplySaveFixes() { diff --git a/src/SMAPI/Patches/SaveGamePatcher.cs b/src/SMAPI/Patches/SaveGamePatcher.cs new file mode 100644 index 00000000..969c514e --- /dev/null +++ b/src/SMAPI/Patches/SaveGamePatcher.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using HarmonyLib; +using StardewModdingAPI.Internal.Patching; +using StardewValley; +using StardewValley.Menus; + +namespace StardewModdingAPI.Patches +{ + /// <summary>Harmony patches for <see cref="SaveGame"/> which track the last loaded save ID.</summary> + /// <remarks>Patch methods must be static for Harmony to work correctly. See the Harmony documentation before renaming patch arguments.</remarks> + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony and methods are named for clarity.")] + [SuppressMessage("ReSharper", "IdentifierTypo", Justification = "Argument names are defined by Harmony and methods are named for clarity.")] + internal class SaveGamePatcher : BasePatcher + { + /********* + ** Fields + *********/ + /// <summary>A callback to invoke when a save file is being loaded.</summary> + private static Action<string> OnSaveFileReading; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="onSaveFileReading">A callback to invoke when a save file is being loaded.</param> + public SaveGamePatcher(Action<string> onSaveFileReading) + { + SaveGamePatcher.OnSaveFileReading = onSaveFileReading; + } + + /// <inheritdoc /> + public override void Apply(Harmony harmony, IMonitor monitor) + { + harmony.Patch( + original: this.RequireMethod<SaveGame>(nameof(SaveGame.getLoadEnumerator)), + prefix: this.GetHarmonyMethod(nameof(SaveGamePatcher.Before_GetLoadEnumerator)) + ); + } + + + /********* + ** Private methods + *********/ + /// <summary>The method to call before <see cref="TitleMenu.createdNewCharacter"/>.</summary> + /// <returns>Returns whether to execute the original method.</returns> + /// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks> + private static bool Before_GetLoadEnumerator(string file) + { + SaveGamePatcher.OnSaveFileReading(file); + return true; + } + } +} |