Fix save load/save error
This commit is contained in:
parent
bf0903b173
commit
917eaf48c4
|
@ -65,8 +65,11 @@ namespace StardewModdingAPI
|
||||||
internal static GameFramework GameFramework { get; } = GameFramework.MonoGame;
|
internal static GameFramework GameFramework { get; } = GameFramework.MonoGame;
|
||||||
|
|
||||||
/// <summary>The game's assembly name.</summary>
|
/// <summary>The game's assembly name.</summary>
|
||||||
|
#if SMAPI_FOR_MOBILE
|
||||||
|
internal static string GameAssemblyName { get; } = "StardewValley";
|
||||||
|
#else
|
||||||
internal static string GameAssemblyName { get; } = "Stardew Valley";
|
internal static string GameAssemblyName { get; } = "Stardew Valley";
|
||||||
|
#endif
|
||||||
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
/// <summary>The <see cref="Context.ScreenId"/> value which should appear in the SMAPI log, if any.</summary>
|
||||||
internal static int? LogScreenId { get; set; }
|
internal static int? LogScreenId { get; set; }
|
||||||
|
|
||||||
|
@ -139,11 +142,7 @@ namespace StardewModdingAPI
|
||||||
/// <summary>The directory path in which error logs should be stored.</summary>
|
/// <summary>The directory path in which error logs should be stored.</summary>
|
||||||
public static string LogDir { get; } = Path.Combine(Constants.DataPath, "ErrorLogs");
|
public static string LogDir { get; } = Path.Combine(Constants.DataPath, "ErrorLogs");
|
||||||
/// <summary>The directory path where all saves are stored.</summary>
|
/// <summary>The directory path where all saves are stored.</summary>
|
||||||
#if SMAPI_FOR_MOBILE
|
|
||||||
public static string SavesPath { get; } = Constants.DataPath;
|
|
||||||
#else
|
|
||||||
public static string SavesPath { get; } = Path.Combine(Constants.DataPath, "Saves");
|
public static string SavesPath { get; } = Path.Combine(Constants.DataPath, "Saves");
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>The name of the current save folder (if save info is available, regardless of whether the save file exists yet).</summary>
|
/// <summary>The name of the current save folder (if save info is available, regardless of whether the save file exists yet).</summary>
|
||||||
public static string? SaveFolderName => Constants.GetSaveFolderName();
|
public static string? SaveFolderName => Constants.GetSaveFolderName();
|
||||||
|
|
|
@ -178,9 +178,16 @@ namespace StardewModdingAPI.Framework.ModLoading
|
||||||
using MemoryStream outSymbolStream = new();
|
using MemoryStream outSymbolStream = new();
|
||||||
assembly.Definition.Write(outAssemblyStream, new WriterParameters { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider });
|
assembly.Definition.Write(outAssemblyStream, new WriterParameters { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider });
|
||||||
byte[] bytes = outAssemblyStream.ToArray();
|
byte[] bytes = outAssemblyStream.ToArray();
|
||||||
|
if (assembly.File.Name != "MoonSharp.Interpreter.dll" && assembly.File.Name != "PyTK.dll" )
|
||||||
|
{
|
||||||
lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
|
lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
lastAssembly = Assembly.Load(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (!oneAssembly)
|
if (!oneAssembly)
|
||||||
this.Monitor.Log($" Loading {assembly.File.Name}...");
|
this.Monitor.Log($" Loading {assembly.File.Name}...");
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades;
|
||||||
|
|
||||||
|
public static class EnumMethods
|
||||||
|
{
|
||||||
|
public static bool IsDefined<TEnum>(TEnum value) where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
Type enumType = typeof(TEnum);
|
||||||
|
return Enum.IsDefined(enumType, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
|
using HarmonyLib;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using MonoMod.Utils;
|
||||||
using StardewModdingAPI.Framework.ModLoading.Framework;
|
using StardewModdingAPI.Framework.ModLoading.Framework;
|
||||||
|
|
||||||
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
||||||
|
@ -31,7 +33,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
||||||
/// <param name="toType">The type with methods to map to.</param>
|
/// <param name="toType">The type with methods to map to.</param>
|
||||||
/// <param name="onlyIfPlatformChanged">Whether to only rewrite references if loading the assembly on a different platform than it was compiled on.</param>
|
/// <param name="onlyIfPlatformChanged">Whether to only rewrite references if loading the assembly on a different platform than it was compiled on.</param>
|
||||||
public MethodToAnotherStaticMethodRewriter(Type fromType, Predicate<MethodReference> fromMethodSelector, Type toType, string toMethod)
|
public MethodToAnotherStaticMethodRewriter(Type fromType, Predicate<MethodReference> fromMethodSelector, Type toType, string toMethod)
|
||||||
: base( $"{fromType.Name} methods")
|
: base($"{fromType.Name} methods")
|
||||||
{
|
{
|
||||||
this.FromType = fromType;
|
this.FromType = fromType;
|
||||||
this.FromMethodSelector = fromMethodSelector;
|
this.FromMethodSelector = fromMethodSelector;
|
||||||
|
@ -49,7 +51,14 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
|
||||||
if (!this.IsMatch(instruction))
|
if (!this.IsMatch(instruction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
instruction.Operand = module.ImportReference(this.ToType.GetMethod(this.ToMethod));
|
MethodReference newReference = module.ImportReference(this.ToType.GetMethod(this.ToMethod));
|
||||||
|
if (instruction.Operand is GenericInstanceMethod instructionOperand)
|
||||||
|
{
|
||||||
|
GenericInstanceMethod genericInstance = new(newReference);
|
||||||
|
genericInstance.GenericArguments.AddRange(instructionOperand.GenericArguments);
|
||||||
|
newReference = genericInstance;
|
||||||
|
}
|
||||||
|
instruction.Operand = newReference;
|
||||||
return this.MarkRewritten();
|
return this.MarkRewritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,7 @@ namespace StardewModdingAPI.Framework
|
||||||
new StringPatcher(this.Reflection),
|
new StringPatcher(this.Reflection),
|
||||||
new ThreadSilenceExitPatch(this.Monitor),
|
new ThreadSilenceExitPatch(this.Monitor),
|
||||||
new UIThreadPatch(this.Monitor),
|
new UIThreadPatch(this.Monitor),
|
||||||
|
new SaveGamePatch(this.Translator, this.Monitor),
|
||||||
#endif
|
#endif
|
||||||
new TitleMenuPatcher(this.OnLoadStageChanged)
|
new TitleMenuPatcher(this.OnLoadStageChanged)
|
||||||
);
|
);
|
||||||
|
|
|
@ -98,6 +98,8 @@ namespace StardewModdingAPI.Metadata
|
||||||
yield return new MethodToAnotherStaticMethodRewriter(typeof(ISoundBank), (method) => method.Name == nameof(SoundBankMethods.AddCue), typeof(SoundBankMethods), "AddCue");
|
yield return new MethodToAnotherStaticMethodRewriter(typeof(ISoundBank), (method) => method.Name == nameof(SoundBankMethods.AddCue), typeof(SoundBankMethods), "AddCue");
|
||||||
yield return new MethodToAnotherStaticMethodRewriter(typeof(ISoundBank), (method) => method.Name == nameof(SoundBankMethods.GetCueDefinition), typeof(SoundBankMethods), "GetCueDefinition");
|
yield return new MethodToAnotherStaticMethodRewriter(typeof(ISoundBank), (method) => method.Name == nameof(SoundBankMethods.GetCueDefinition), typeof(SoundBankMethods), "GetCueDefinition");
|
||||||
|
|
||||||
|
yield return new MethodToAnotherStaticMethodRewriter(typeof(Enum), (method) => method.Name == nameof(EnumMethods.IsDefined) && method.Parameters.Count == 1, typeof(EnumMethods), "IsDefined");
|
||||||
|
|
||||||
//Constructor Rewrites
|
//Constructor Rewrites
|
||||||
yield return new MethodParentRewriter(typeof(MapPage), typeof(MapPageMethods));
|
yield return new MethodParentRewriter(typeof(MapPage), typeof(MapPageMethods));
|
||||||
yield return new MethodParentRewriter(typeof(ItemGrabMenu), typeof(ItemGrabMenuMethods));
|
yield return new MethodParentRewriter(typeof(ItemGrabMenu), typeof(ItemGrabMenuMethods));
|
||||||
|
|
|
@ -3,10 +3,9 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Xml.Serialization;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
// using Microsoft.AppCenter.Crashes;
|
// using Microsoft.AppCenter.Crashes;
|
||||||
using Microsoft.Xna.Framework;
|
|
||||||
using StardewModdingAPI.Framework;
|
using StardewModdingAPI.Framework;
|
||||||
using StardewModdingAPI.Internal.Patching;
|
using StardewModdingAPI.Internal.Patching;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
@ -45,13 +44,25 @@ namespace StardewModdingAPI.Patches
|
||||||
/// <param name="harmony">The Harmony instance.</param>
|
/// <param name="harmony">The Harmony instance.</param>
|
||||||
public override void Apply(Harmony harmony, IMonitor monitor)
|
public override void Apply(Harmony harmony, IMonitor monitor)
|
||||||
{
|
{
|
||||||
|
// harmony.Patch(
|
||||||
|
// original: AccessTools.Method(typeof(SaveGame), "HandleLoadError"),
|
||||||
|
// prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.Prefix))
|
||||||
|
// );
|
||||||
|
// harmony.Patch(
|
||||||
|
// original: AccessTools.Method(typeof(SaveGameMenu), "update"),
|
||||||
|
// finalizer: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.SaveGameMenu_UpdateFinalizer))
|
||||||
|
// );
|
||||||
harmony.Patch(
|
harmony.Patch(
|
||||||
original: AccessTools.Method(typeof(SaveGame), "HandleLoadError"),
|
original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "GetValueFromXmlString"),
|
||||||
prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.Prefix))
|
prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationReaderInterpreter_PrefixGetValueFromXmlString))
|
||||||
);
|
);
|
||||||
harmony.Patch(
|
harmony.Patch(
|
||||||
original: AccessTools.Method(typeof(SaveGameMenu), "update"),
|
original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "AddListValue"),
|
||||||
finalizer: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.SaveGameMenu_UpdateFinalizer))
|
prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationReaderInterpreter_PrefixAddListValue))
|
||||||
|
);
|
||||||
|
harmony.Patch(
|
||||||
|
original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationWriterInterpreter"), "GetEnumXmlValue"),
|
||||||
|
prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationWriterInterpreter_PrefixGetEnumXmlValue))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,27 +103,20 @@ namespace StardewModdingAPI.Patches
|
||||||
else if (SaveGame.newerBackUpExists(fileName) != null)
|
else if (SaveGame.newerBackUpExists(fileName) != null)
|
||||||
SaveGame.Load(fileName, false, true);
|
SaveGame.Load(fileName, false, true);
|
||||||
else if (SaveGame.oldBackUpExists(fileName) != null)
|
else if (SaveGame.oldBackUpExists(fileName) != null)
|
||||||
{
|
|
||||||
SaveGame.Load(fileName, false, true);
|
SaveGame.Load(fileName, false, true);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (SaveGame.partialOldBackUpExists(fileName) == null)
|
if (SaveGame.partialOldBackUpExists(fileName) == null)
|
||||||
{
|
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IEnumerator<int> enumerator1 = SaveGame.getLoadEnumerator(fileName, false, true, true);
|
IEnumerator<int> enumerator1 = SaveGame.getLoadEnumerator(fileName, false, true, true);
|
||||||
while (enumerator1 != null)
|
while (enumerator1 != null)
|
||||||
{
|
|
||||||
if (!enumerator1.MoveNext())
|
if (!enumerator1.MoveNext())
|
||||||
enumerator1 = null;
|
enumerator1 = null;
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator<int> enumerator2 = SaveGame.Save();
|
IEnumerator<int> enumerator2 = SaveGame.Save();
|
||||||
while (enumerator2 != null)
|
while (enumerator2 != null)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!enumerator2.MoveNext())
|
if (!enumerator2.MoveNext())
|
||||||
|
@ -126,10 +130,8 @@ namespace StardewModdingAPI.Patches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
|
||||||
SAlertDialogUtil.AlertMessage(
|
SAlertDialogUtil.AlertMessage(
|
||||||
SaveGamePatch.Translator.Get("warn.save-broken"),
|
SaveGamePatch.Translator.Get("warn.save-broken"),
|
||||||
positive: SaveGamePatch.Translator.Get("btn.swap"),
|
positive: SaveGamePatch.Translator.Get("btn.swap"),
|
||||||
|
@ -138,33 +140,86 @@ namespace StardewModdingAPI.Patches
|
||||||
{
|
{
|
||||||
if (action == SAlertDialogUtil.ActionType.POSITIVE)
|
if (action == SAlertDialogUtil.ActionType.POSITIVE)
|
||||||
{
|
{
|
||||||
if (!SaveGame.swapForOldSave())
|
if (!SaveGame.swapForOldSave()) Game1.ExitToTitle();
|
||||||
{
|
|
||||||
Game1.ExitToTitle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
Game1.ExitToTitle();
|
Game1.ExitToTitle();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>The method to call instead of <see cref="StardewValley.Menus.SaveGameMenu.update"/>.</summary>
|
/// <summary>The method to call instead of <see cref="StardewValley.Menus.SaveGameMenu.update"/>.</summary>
|
||||||
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
|
/// <remarks>This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.</remarks>
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")]
|
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")]
|
||||||
private static Exception SaveGameMenu_UpdateFinalizer(SaveGameMenu __instance, Exception __exception)
|
private static Exception SaveGameMenu_UpdateFinalizer(SaveGameMenu __instance, Exception __exception)
|
||||||
{
|
{
|
||||||
if(__exception != null) {
|
if (__exception != null)
|
||||||
|
{
|
||||||
SaveGamePatch.Monitor.Log($"Failed during SaveGameMenu.update method :\n{__exception.InnerException ?? __exception}", LogLevel.Error);
|
SaveGamePatch.Monitor.Log($"Failed during SaveGameMenu.update method :\n{__exception.InnerException ?? __exception}", LogLevel.Error);
|
||||||
__instance.complete();
|
__instance.complete();
|
||||||
Game1.addHUDMessage(new HUDMessage("An error occurs during save the game.Check the error log for details.", HUDMessage.error_type));
|
Game1.addHUDMessage(new HUDMessage("An error occurs during save the game.Check the error log for details.", HUDMessage.error_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool XmlSerializationReaderInterpreter_PrefixGetValueFromXmlString(string value, ref object __result)
|
||||||
|
{
|
||||||
|
if (value?.Length > 0) return true;
|
||||||
|
__result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool XmlSerializationReaderInterpreter_PrefixAddListValue(object listType, ref object list, int index, object value, bool canCreateInstance)
|
||||||
|
{
|
||||||
|
Type type = (Type)AccessTools.Property(listType.GetType(), "Type").GetValue(listType);
|
||||||
|
if (type.IsArray) return true;
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
if (!canCreateInstance) throw new InvalidOperationException(string.Format("Could not serialize {0}. Default constructors are required for collections and enumerators.", type.FullName));
|
||||||
|
|
||||||
|
list = Activator.CreateInstance(type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type listItemType = (Type)AccessTools.Property(listType.GetType(), "ListItemType").GetValue(listType);
|
||||||
|
if (listItemType.IsEnum && value != null)
|
||||||
|
type.GetMethod("Add", new[] { typeof(string) }).Invoke(list, new[] { value.ToString() });
|
||||||
|
else
|
||||||
|
type.GetMethod("Add", new[] { listItemType }).Invoke(list, new[] { value });
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool XmlSerializationWriterInterpreter_PrefixGetEnumXmlValue(XmlTypeMapping typeMap, object ob, ref string __result)
|
||||||
|
{
|
||||||
|
if (ob == null)
|
||||||
|
{
|
||||||
|
__result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
object objectMap = AccessTools.Property(typeMap.GetType(), "ObjectMap").GetValue(typeMap);
|
||||||
|
if (ob is string enumString)
|
||||||
|
{
|
||||||
|
string[] enumNames = (string[])AccessTools.Property(objectMap.GetType(), "EnumNames").GetValue(objectMap);
|
||||||
|
string[] xmlNames = (string[])AccessTools.Property(objectMap.GetType(), "XmlNames").GetValue(objectMap);
|
||||||
|
for (int i = 0; i < enumNames.Length; i++)
|
||||||
|
if (enumString == enumNames[i])
|
||||||
|
{
|
||||||
|
__result = xmlNames[i];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
__result = (string)AccessTools.Method(objectMap.GetType(), "GetXmlName").Invoke(objectMap, new[] { typeMap.TypeFullName, ob });
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue