diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/SMAPI.Installer/InteractiveInstaller.cs | 5 | ||||
-rw-r--r-- | src/SMAPI/Constants.cs | 3 | ||||
-rw-r--r-- | src/SMAPI/Framework/Patching/GameLocationPatch.cs | 60 | ||||
-rw-r--r-- | src/SMAPI/Framework/Patching/GamePatcher.cs | 45 | ||||
-rw-r--r-- | src/SMAPI/Framework/Patching/IHarmonyPatch.cs | 15 | ||||
-rw-r--r-- | src/SMAPI/Program.cs | 17 | ||||
-rw-r--r-- | src/SMAPI/StardewModdingAPI.csproj | 7 | ||||
-rw-r--r-- | src/lib/0Harmony.dll | bin | 0 -> 99840 bytes | |||
-rw-r--r-- | src/lib/0Harmony.pdb | bin | 0 -> 323072 bytes |
9 files changed, 143 insertions, 9 deletions
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index 30cb10b4..f9239604 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -87,11 +87,16 @@ namespace StardewModdingApi.Installer string GetInstallPath(string path) => Path.Combine(installDir.FullName, path); // common + yield return GetInstallPath("0Harmony.dll"); + yield return GetInstallPath("0Harmony.pdb"); yield return GetInstallPath("Mono.Cecil.dll"); yield return GetInstallPath("Newtonsoft.Json.dll"); yield return GetInstallPath("StardewModdingAPI.exe"); yield return GetInstallPath("StardewModdingAPI.config.json"); yield return GetInstallPath("StardewModdingAPI.metadata.json"); + yield return GetInstallPath("StardewModdingAPI.Toolkit.dll"); + yield return GetInstallPath("StardewModdingAPI.Toolkit.pdb"); + yield return GetInstallPath("StardewModdingAPI.xml"); yield return GetInstallPath("System.ValueTuple.dll"); yield return GetInstallPath("steam_appid.txt"); diff --git a/src/SMAPI/Constants.cs b/src/SMAPI/Constants.cs index 31744ccd..532112ff 100644 --- a/src/SMAPI/Constants.cs +++ b/src/SMAPI/Constants.cs @@ -106,6 +106,9 @@ namespace StardewModdingAPI /// <summary>The target game platform.</summary> internal static Platform Platform { get; } = EnvironmentUtility.DetectPlatform(); + /// <summary>The game's assembly name.</summary> + internal static string GameAssemblyName => Constants.Platform == Platform.Windows ? "Stardew Valley" : "StardewValley"; + /********* ** Internal methods diff --git a/src/SMAPI/Framework/Patching/GameLocationPatch.cs b/src/SMAPI/Framework/Patching/GameLocationPatch.cs new file mode 100644 index 00000000..c0216b8f --- /dev/null +++ b/src/SMAPI/Framework/Patching/GameLocationPatch.cs @@ -0,0 +1,60 @@ +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using Harmony; +using StardewValley; +using xTile.Tiles; + +namespace StardewModdingAPI.Framework.Patching +{ + /// <summary>A Harmony patch for the <see cref="GameLocation.updateSeasonalTileSheets"/> method.</summary> + internal class GameLocationPatch : IHarmonyPatch + { + /********* + ** Accessors + *********/ + /// <summary>A unique name for this patch.</summary> + public string Name => $"{nameof(GameLocation)}.{nameof(GameLocation.updateSeasonalTileSheets)}"; + + + /********* + ** Public methods + *********/ + /// <summary>Apply the Harmony patch.</summary> + /// <param name="harmony">The Harmony instance.</param> + public void Apply(HarmonyInstance harmony) + { + MethodInfo method = AccessTools.Method(typeof(GameLocation), nameof(GameLocation.updateSeasonalTileSheets)); + MethodInfo prefix = AccessTools.Method(this.GetType(), nameof(GameLocationPatch.Prefix)); + + harmony.Patch(method, new HarmonyMethod(prefix), null); + } + + + /********* + ** Private methods + *********/ + /// <summary>An implementation of <see cref="GameLocation.updateSeasonalTileSheets"/> which correctly handles custom map tilesheets.</summary> + /// <param name="__instance">The location instance being patched.</param> + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Argument name is defined by Harmony.")] + private static bool Prefix(GameLocation __instance) + { + if (!__instance.IsOutdoors || __instance.Name.Equals("Desert")) + return false; + foreach (TileSheet tilesheet in __instance.Map.TileSheets) + { + string imageSource = tilesheet.ImageSource; + string imageFile = Path.GetFileName(imageSource); + if (imageFile.StartsWith("spring_") || imageFile.StartsWith("summer_") || imageFile.StartsWith("fall_") || imageFile.StartsWith("winter_")) + { + string imageDir = Path.GetDirectoryName(imageSource); + if (string.IsNullOrWhiteSpace(imageDir)) + imageDir = "Maps"; + tilesheet.ImageSource = Path.Combine(imageDir, Game1.currentSeason + "_" + imageFile.Split('_')[1]); + } + } + + return false; + } + } +} diff --git a/src/SMAPI/Framework/Patching/GamePatcher.cs b/src/SMAPI/Framework/Patching/GamePatcher.cs new file mode 100644 index 00000000..71ca8e55 --- /dev/null +++ b/src/SMAPI/Framework/Patching/GamePatcher.cs @@ -0,0 +1,45 @@ +using System; +using Harmony; + +namespace StardewModdingAPI.Framework.Patching +{ + /// <summary>Encapsulates applying Harmony patches to the game.</summary> + internal class GamePatcher + { + /********* + ** Properties + *********/ + /// <summary>Encapsulates monitoring and logging.</summary> + private readonly IMonitor Monitor; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="monitor">Encapsulates monitoring and logging.</param> + public GamePatcher(IMonitor monitor) + { + this.Monitor = monitor; + } + + /// <summary>Apply all loaded patches to the game.</summary> + /// <param name="patches">The patches to apply.</param> + public void Apply(params IHarmonyPatch[] patches) + { + HarmonyInstance harmony = HarmonyInstance.Create("io.smapi"); + foreach (IHarmonyPatch patch in patches) + { + try + { + patch.Apply(harmony); + } + catch (Exception ex) + { + this.Monitor.Log($"Couldn't apply runtime patch '{patch.Name}' to the game. Some SMAPI features may not work correctly. See log file for details.", LogLevel.Error); + this.Monitor.Log(ex.GetLogSummary(), LogLevel.Trace); + } + } + } + } +} diff --git a/src/SMAPI/Framework/Patching/IHarmonyPatch.cs b/src/SMAPI/Framework/Patching/IHarmonyPatch.cs new file mode 100644 index 00000000..cb42f40e --- /dev/null +++ b/src/SMAPI/Framework/Patching/IHarmonyPatch.cs @@ -0,0 +1,15 @@ +using Harmony; + +namespace StardewModdingAPI.Framework.Patching +{ + /// <summary>A Harmony patch to apply.</summary> + internal interface IHarmonyPatch + { + /// <summary>A unique name for this patch.</summary> + string Name { get; } + + /// <summary>Apply the Harmony patch.</summary> + /// <param name="harmony">The Harmony instance.</param> + void Apply(HarmonyInstance harmony); + } +} diff --git a/src/SMAPI/Program.cs b/src/SMAPI/Program.cs index 28523722..4ede45d5 100644 --- a/src/SMAPI/Program.cs +++ b/src/SMAPI/Program.cs @@ -24,6 +24,7 @@ using StardewModdingAPI.Framework.ModData; using StardewModdingAPI.Framework.Models; using StardewModdingAPI.Framework.ModHelpers; using StardewModdingAPI.Framework.ModLoading; +using StardewModdingAPI.Framework.Patching; using StardewModdingAPI.Framework.Reflection; using StardewModdingAPI.Framework.Serialisation; using StardewModdingAPI.Internal; @@ -32,7 +33,7 @@ using StardewModdingAPI.Toolkit.Serialisation; using StardewModdingAPI.Toolkit.Serialisation.Converters; using StardewModdingAPI.Toolkit.Utilities; using StardewValley; -using Keys = System.Windows.Forms.Keys; +using Keys = Microsoft.Xna.Framework.Input.Keys; using Monitor = StardewModdingAPI.Framework.Monitor; using SObject = StardewValley.Object; using ThreadState = System.Threading.ThreadState; @@ -141,6 +142,11 @@ namespace StardewModdingAPI }; this.EventManager = new EventManager(this.Monitor, this.ModRegistry); + // apply game patches + new GamePatcher(this.Monitor).Apply( + new GameLocationPatch() + ); + // init JSON parser JsonConverter[] converters = { new StringEnumConverter<Buttons>(), @@ -348,14 +354,7 @@ namespace StardewModdingAPI Console.ResetColor(); Program.PressAnyKeyToExit(showMessage: true); } - - // get game assembly name - const string gameAssemblyName = -#if SMAPI_FOR_WINDOWS - "Stardew Valley"; -#else - "StardewValley"; -#endif + string gameAssemblyName = Constants.GameAssemblyName; // game not present if (Type.GetType($"StardewValley.Game1, {gameAssemblyName}", throwOnError: false) == null) diff --git a/src/SMAPI/StardewModdingAPI.csproj b/src/SMAPI/StardewModdingAPI.csproj index 7eb5f95a..2df3e63f 100644 --- a/src/SMAPI/StardewModdingAPI.csproj +++ b/src/SMAPI/StardewModdingAPI.csproj @@ -53,6 +53,10 @@ <ApplicationIcon>icon.ico</ApplicationIcon> </PropertyGroup> <ItemGroup> + <Reference Include="0Harmony, Version=1.0.9.1, Culture=neutral, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\lib\0Harmony.dll</HintPath> + </Reference> <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath> <Private>True</Private> @@ -103,6 +107,8 @@ <Compile Include="Framework\ContentManagers\GameContentManager.cs" /> <Compile Include="Framework\ContentManagers\IContentManager.cs" /> <Compile Include="Framework\ContentManagers\ModContentManager.cs" /> + <Compile Include="Framework\Patching\GamePatcher.cs" /> + <Compile Include="Framework\Patching\IHarmonyPatch.cs" /> <Compile Include="Framework\Serialisation\ColorConverter.cs" /> <Compile Include="Framework\Serialisation\PointConverter.cs" /> <Compile Include="Framework\Serialisation\RectangleConverter.cs" /> @@ -279,6 +285,7 @@ <Compile Include="Framework\Monitor.cs" /> <Compile Include="Metadata\InstructionMetadata.cs" /> <Compile Include="Mod.cs" /> + <Compile Include="Framework\Patching\GameLocationPatch.cs" /> <Compile Include="PatchMode.cs" /> <Compile Include="GamePlatform.cs" /> <Compile Include="Program.cs" /> diff --git a/src/lib/0Harmony.dll b/src/lib/0Harmony.dll Binary files differnew file mode 100644 index 00000000..63619429 --- /dev/null +++ b/src/lib/0Harmony.dll diff --git a/src/lib/0Harmony.pdb b/src/lib/0Harmony.pdb Binary files differnew file mode 100644 index 00000000..d7a4c67c --- /dev/null +++ b/src/lib/0Harmony.pdb |