diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2020-05-20 19:38:08 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2020-05-20 19:38:08 -0400 |
commit | 310eb1fe9afdb820d5584a46200bf073fc00ccb7 (patch) | |
tree | 72c16c6d4f8447f45e8e6612cb25cd255d2b308c /src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs | |
parent | aa5cc2c9be8bdc79c6fa7b1b9c2581a05b88117d (diff) | |
parent | c5c30189e43f93c3f3c66207945187a974656c9e (diff) | |
download | SMAPI-310eb1fe9afdb820d5584a46200bf073fc00ccb7.tar.gz SMAPI-310eb1fe9afdb820d5584a46200bf073fc00ccb7.tar.bz2 SMAPI-310eb1fe9afdb820d5584a46200bf073fc00ccb7.zip |
Merge branch 'mod/harmony-2.0' into develop
# Conflicts:
# docs/release-notes.md
# src/SMAPI/Framework/ModLoading/AssemblyLoader.cs
Diffstat (limited to 'src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs')
-rw-r--r-- | src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs b/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs new file mode 100644 index 00000000..be98a666 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/Rewriters/Harmony1AssemblyRewriter.cs @@ -0,0 +1,123 @@ +using System; +using HarmonyLib; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.Framework.ModLoading.Framework; +using StardewModdingAPI.Framework.ModLoading.RewriteFacades; + +namespace StardewModdingAPI.Framework.ModLoading.Rewriters +{ + /// <summary>Rewrites Harmony 1.x assembly references to work with Harmony 2.x.</summary> + internal class Harmony1AssemblyRewriter : BaseInstructionHandler + { + /********* + ** Fields + *********/ + /// <summary>Whether any Harmony 1.x types were replaced.</summary> + private bool ReplacedTypes; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + public Harmony1AssemblyRewriter() + : base(defaultPhrase: "Harmony 1.x") { } + + /// <summary>Rewrite a type reference if needed.</summary> + /// <param name="module">The assembly module containing the instruction.</param> + /// <param name="type">The type definition to handle.</param> + /// <param name="replaceWith">Replaces the type reference with a new one.</param> + /// <returns>Returns whether the type was changed.</returns> + public override bool Handle(ModuleDefinition module, TypeReference type, Action<TypeReference> replaceWith) + { + // rewrite Harmony 1.x type to Harmony 2.0 type + if (type.Scope is AssemblyNameReference scope && scope.Name == "0Harmony" && scope.Version.Major == 1) + { + Type targetType = this.GetMappedType(type); + replaceWith(module.ImportReference(targetType)); + this.MarkRewritten(); + this.ReplacedTypes = true; + return true; + } + + return false; + } + + /// <summary>Rewrite a CIL instruction reference if needed.</summary> + /// <param name="module">The assembly module containing the instruction.</param> + /// <param name="cil">The CIL processor.</param> + /// <param name="instruction">The CIL instruction to handle.</param> + /// <param name="replaceWith">Replaces the CIL instruction with a new one.</param> + /// <returns>Returns whether the instruction was changed.</returns> + public override bool Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, Action<Instruction> replaceWith) + { + // rewrite Harmony 1.x methods to Harmony 2.0 + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + if (this.TryRewriteMethodsToFacade(module, methodRef)) + return true; + + // rewrite renamed fields + FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); + if (fieldRef != null) + { + if (fieldRef.DeclaringType.FullName == "HarmonyLib.HarmonyMethod" && fieldRef.Name == "prioritiy") + fieldRef.Name = nameof(HarmonyMethod.priority); + } + + return false; + } + + + /********* + ** Private methods + *********/ + /// <summary>Rewrite methods to use Harmony facades if needed.</summary> + /// <param name="module">The assembly module containing the method reference.</param> + /// <param name="methodRef">The method reference to map.</param> + private bool TryRewriteMethodsToFacade(ModuleDefinition module, MethodReference methodRef) + { + if (!this.ReplacedTypes) + return false; // not Harmony (or already using Harmony 2.0) + + // get facade type + Type toType; + switch (methodRef?.DeclaringType.FullName) + { + case "HarmonyLib.Harmony": + toType = typeof(HarmonyInstanceMethods); + break; + + case "HarmonyLib.AccessTools": + toType = typeof(AccessToolsMethods); + break; + + default: + return false; + } + + // map if there's a matching method + if (RewriteHelper.HasMatchingSignature(toType, methodRef)) + { + methodRef.DeclaringType = module.ImportReference(toType); + return true; + } + + return false; + } + + /// <summary>Get an equivalent Harmony 2.x type.</summary> + /// <param name="type">The Harmony 1.x method.</param> + private Type GetMappedType(TypeReference type) + { + // main Harmony object + if (type.FullName == "Harmony.HarmonyInstance") + return typeof(Harmony); + + // other objects + string fullName = type.FullName.Replace("Harmony.", "HarmonyLib."); + string targetName = typeof(Harmony).AssemblyQualifiedName.Replace(typeof(Harmony).FullName, fullName); + return Type.GetType(targetName, throwOnError: true); + } + } +} |