diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs
index 557c6832..c3bdbff5 100644
--- a/src/SMAPI/Constants.cs
+++ b/src/SMAPI/Constants.cs
@@ -65,8 +65,11 @@ namespace StardewModdingAPI
internal static GameFramework GameFramework { get; } = GameFramework.MonoGame;
/// The game's assembly name.
+#if SMAPI_FOR_MOBILE
+ internal static string GameAssemblyName { get; } = "StardewValley";
+#else
internal static string GameAssemblyName { get; } = "Stardew Valley";
-
+#endif
/// The value which should appear in the SMAPI log, if any.
internal static int? LogScreenId { get; set; }
@@ -139,11 +142,7 @@ namespace StardewModdingAPI
/// The directory path in which error logs should be stored.
public static string LogDir { get; } = Path.Combine(Constants.DataPath, "ErrorLogs");
/// The directory path where all saves are stored.
-#if SMAPI_FOR_MOBILE
- public static string SavesPath { get; } = Constants.DataPath;
-#else
public static string SavesPath { get; } = Path.Combine(Constants.DataPath, "Saves");
-#endif
/// The name of the current save folder (if save info is available, regardless of whether the save file exists yet).
public static string? SaveFolderName => Constants.GetSaveFolderName();
diff --git a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
index f0ad5600..51200573 100644
--- a/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -178,7 +178,14 @@ namespace StardewModdingAPI.Framework.ModLoading
using MemoryStream outSymbolStream = new();
assembly.Definition.Write(outAssemblyStream, new WriterParameters { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider });
byte[] bytes = outAssemblyStream.ToArray();
- lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
+ if (assembly.File.Name != "MoonSharp.Interpreter.dll" && assembly.File.Name != "PyTK.dll" )
+ {
+ lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
+ }
+ else
+ {
+ lastAssembly = Assembly.Load(bytes);
+ }
}
else
{
diff --git a/src/SMAPI/Framework/ModLoading/RewriteFacades/EnumMethods.cs b/src/SMAPI/Framework/ModLoading/RewriteFacades/EnumMethods.cs
new file mode 100644
index 00000000..cd03229b
--- /dev/null
+++ b/src/SMAPI/Framework/ModLoading/RewriteFacades/EnumMethods.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace StardewModdingAPI.Framework.ModLoading.RewriteFacades;
+
+public static class EnumMethods
+{
+ public static bool IsDefined(TEnum value) where TEnum : struct, Enum
+ {
+ Type enumType = typeof(TEnum);
+ return Enum.IsDefined(enumType, value);
+ }
+}
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/MethodToAnotherStaticMethodRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/MethodToAnotherStaticMethodRewriter.cs
index 66fa094b..cc78b5c0 100644
--- a/src/SMAPI/Framework/ModLoading/Rewriters/MethodToAnotherStaticMethodRewriter.cs
+++ b/src/SMAPI/Framework/ModLoading/Rewriters/MethodToAnotherStaticMethodRewriter.cs
@@ -1,6 +1,8 @@
using System;
+using HarmonyLib;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using MonoMod.Utils;
using StardewModdingAPI.Framework.ModLoading.Framework;
namespace StardewModdingAPI.Framework.ModLoading.Rewriters
@@ -31,7 +33,7 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
/// The type with methods to map to.
/// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on.
public MethodToAnotherStaticMethodRewriter(Type fromType, Predicate fromMethodSelector, Type toType, string toMethod)
- : base( $"{fromType.Name} methods")
+ : base($"{fromType.Name} methods")
{
this.FromType = fromType;
this.FromMethodSelector = fromMethodSelector;
@@ -49,7 +51,14 @@ namespace StardewModdingAPI.Framework.ModLoading.Rewriters
if (!this.IsMatch(instruction))
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();
}
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 4e559a47..e0c08a09 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -293,6 +293,7 @@ namespace StardewModdingAPI.Framework
new StringPatcher(this.Reflection),
new ThreadSilenceExitPatch(this.Monitor),
new UIThreadPatch(this.Monitor),
+ new SaveGamePatch(this.Translator, this.Monitor),
#endif
new TitleMenuPatcher(this.OnLoadStageChanged)
);
diff --git a/src/SMAPI/Metadata/InstructionMetadata.cs b/src/SMAPI/Metadata/InstructionMetadata.cs
index aaabf6d1..b545a421 100644
--- a/src/SMAPI/Metadata/InstructionMetadata.cs
+++ b/src/SMAPI/Metadata/InstructionMetadata.cs
@@ -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.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
yield return new MethodParentRewriter(typeof(MapPage), typeof(MapPageMethods));
yield return new MethodParentRewriter(typeof(ItemGrabMenu), typeof(ItemGrabMenuMethods));
diff --git a/src/SMAPI/Patches/SaveGamePatch.cs b/src/SMAPI/Patches/SaveGamePatch.cs
index 77a2b74a..1afb6cbd 100644
--- a/src/SMAPI/Patches/SaveGamePatch.cs
+++ b/src/SMAPI/Patches/SaveGamePatch.cs
@@ -3,10 +3,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using System.Reflection;
+using System.Xml.Serialization;
using HarmonyLib;
// using Microsoft.AppCenter.Crashes;
-using Microsoft.Xna.Framework;
using StardewModdingAPI.Framework;
using StardewModdingAPI.Internal.Patching;
using StardewValley;
@@ -45,13 +44,25 @@ namespace StardewModdingAPI.Patches
/// The Harmony instance.
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(
- original: AccessTools.Method(typeof(SaveGame), "HandleLoadError"),
- prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.Prefix))
+ original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "GetValueFromXmlString"),
+ prefix: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.XmlSerializationReaderInterpreter_PrefixGetValueFromXmlString))
);
harmony.Patch(
- original: AccessTools.Method(typeof(SaveGameMenu), "update"),
- finalizer: new HarmonyMethod(this.GetType(), nameof(SaveGamePatch.SaveGameMenu_UpdateFinalizer))
+ original: AccessTools.Method(typeof(XmlSerializer).Assembly.GetType("System.Xml.Serialization.XmlSerializationReaderInterpreter"), "AddListValue"),
+ 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)
SaveGame.Load(fileName, false, true);
else if (SaveGame.oldBackUpExists(fileName) != null)
- {
SaveGame.Load(fileName, false, true);
- }
else
{
if (SaveGame.partialOldBackUpExists(fileName) == null)
- {
failed = true;
- }
else
{
IEnumerator enumerator1 = SaveGame.getLoadEnumerator(fileName, false, true, true);
while (enumerator1 != null)
- {
if (!enumerator1.MoveNext())
enumerator1 = null;
- }
IEnumerator enumerator2 = SaveGame.Save();
while (enumerator2 != null)
- {
try
{
if (!enumerator2.MoveNext())
@@ -124,12 +128,10 @@ namespace StardewModdingAPI.Patches
// Crashes.TrackError(ex, null, errorAttachmentLogArray);
failed = true;
}
- }
}
}
if (failed)
- {
SAlertDialogUtil.AlertMessage(
SaveGamePatch.Translator.Get("warn.save-broken"),
positive: SaveGamePatch.Translator.Get("btn.swap"),
@@ -138,33 +140,86 @@ namespace StardewModdingAPI.Patches
{
if (action == SAlertDialogUtil.ActionType.POSITIVE)
{
- if (!SaveGame.swapForOldSave())
- {
- Game1.ExitToTitle();
- }
+ if (!SaveGame.swapForOldSave()) Game1.ExitToTitle();
}
else
- {
Game1.ExitToTitle();
- }
}
);
- }
return false;
}
+
/// The method to call instead of .
/// This method must be static for Harmony to work correctly. See the Harmony documentation before renaming arguments.
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument names are defined by Harmony.")]
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);
__instance.complete();
Game1.addHUDMessage(new HUDMessage("An error occurs during save the game.Check the error log for details.", HUDMessage.error_type));
}
+
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