From 40a90147420614d7d593b478fcf93b9be542c5b0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 13:45:34 -0500 Subject: generalise CIL rewriters for reuse (#231) --- .../StardewModdingAPI.AssemblyRewriters.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 1e6caacc..01ca1d66 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -70,13 +70,12 @@ Properties\GlobalAssemblyInfo.cs - + - -- cgit From 74a56a7b3b3bde30fbcb711eaef977ad69601e03 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 14:02:43 -0500 Subject: further generalise CIL rewriters for reuse (#231) --- .../Framework/BaseMethodRewriter.cs | 89 ++++++++++++++++ .../Framework/RewriteHelper.cs | 41 ++++++++ .../Rewriters/BaseMethodRewriter.cs | 117 --------------------- .../Rewriters/SpriteBatchRewriter.cs | 3 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 3 +- 5 files changed, 134 insertions(+), 119 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/BaseMethodRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs new file mode 100644 index 00000000..c9729ee8 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs @@ -0,0 +1,89 @@ +using System; +using System.Linq; +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Base class for a method rewriter. + public abstract class BaseMethodRewriter : IInstructionRewriter + { + /********* + ** Public methods + *********/ + /// Get whether a CIL instruction should be rewritten. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool ShouldRewrite(Instruction instruction, bool platformChanged) + { + // ignore non-method-call instructions + if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) + return false; + + // check reference + MethodReference methodRef = (MethodReference)instruction.Operand; + return this.ShouldRewrite(methodRef, platformChanged); + } + + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) + { + MethodReference methodRef = (MethodReference)instruction.Operand; + this.Rewrite(module, cil, instruction, methodRef, assemblyMap); + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected abstract bool ShouldRewrite(MethodReference methodRef, bool platformChanged); + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which calls the method. + /// The method reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap); + + /// Get whether a method definition matches the signature expected by a method reference. + /// The method definition. + /// The method reference. + protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference) + { + // same name + if (definition.Name != reference.Name) + return false; + + // same arguments + ParameterInfo[] definitionParameters = definition.GetParameters(); + ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); + if (referenceParameters.Length != definitionParameters.Length) + return false; + for (int i = 0; i < referenceParameters.Length; i++) + { + if (!RewriteHelper.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) + return false; + } + return true; + } + + /// Get whether a type has a method whose signature matches the one expected by a method reference. + /// The type to check. + /// The method reference. + protected bool HasMatchingSignature(Type type, MethodReference reference) + { + return type + .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) + .Any(method => this.HasMatchingSignature(method, reference)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs new file mode 100644 index 00000000..0307053f --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs @@ -0,0 +1,41 @@ +using System; +using Mono.Cecil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Provides helper methods for field rewriters. + internal static class RewriteHelper + { + /********* + ** Public methods + *********/ + /// Get whether a type matches a type reference. + /// The defined type. + /// The type reference. + public static bool IsMatchingType(Type type, TypeReference reference) + { + // same namespace & name + if (type.Namespace != reference.Namespace || type.Name != reference.Name) + return false; + + // same generic parameters + if (type.IsGenericType) + { + if (!reference.IsGenericInstance) + return false; + + Type[] defGenerics = type.GetGenericArguments(); + TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); + if (defGenerics.Length != refGenerics.Length) + return false; + for (int i = 0; i < defGenerics.Length; i++) + { + if (!RewriteHelper.IsMatchingType(defGenerics[i], refGenerics[i])) + return false; + } + } + + return true; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/BaseMethodRewriter.cs deleted file mode 100644 index e44acaf9..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/BaseMethodRewriter.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters -{ - /// Base class for a method rewriter. - public abstract class BaseMethodRewriter : IInstructionRewriter - { - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction should be rewritten. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool ShouldRewrite(Instruction instruction, bool platformChanged) - { - // ignore non-method-call instructions - if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) - return false; - - // check reference - MethodReference methodRef = (MethodReference)instruction.Operand; - return this.ShouldRewrite(methodRef, platformChanged); - } - - /// Rewrite a CIL instruction for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction to rewrite. - /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) - { - MethodReference methodRef = (MethodReference)instruction.Operand; - this.Rewrite(module, cil, instruction, methodRef, assemblyMap); - } - - /********* - ** Protected methods - *********/ - /// Get whether the given method reference can be rewritten. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool ShouldRewrite(MethodReference methodRef, bool platformChanged); - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap); - - /// Get whether a method definition matches the signature expected by a method reference. - /// The method definition. - /// The method reference. - protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference) - { - // same name - if (definition.Name != reference.Name) - return false; - - // same arguments - ParameterInfo[] definitionParameters = definition.GetParameters(); - ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); - if (referenceParameters.Length != definitionParameters.Length) - return false; - for (int i = 0; i < referenceParameters.Length; i++) - { - if (!this.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) - return false; - } - return true; - } - - /// Get whether a type has a method whose signature matches the one expected by a method reference. - /// The type to check. - /// The method reference. - protected bool HasMatchingSignature(Type type, MethodReference reference) - { - return type - .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) - .Any(method => this.HasMatchingSignature(method, reference)); - } - - /// Get whether a type matches a type reference. - /// The defined type. - /// The type reference. - private bool IsMatchingType(Type type, TypeReference reference) - { - // same namespace & name - if (type.Namespace != reference.Namespace || type.Name != reference.Name) - return false; - - // same generic parameters - if (type.IsGenericType) - { - if (!reference.IsGenericInstance) - return false; - - Type[] defGenerics = type.GetGenericArguments(); - TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); - if (defGenerics.Length != refGenerics.Length) - return false; - for (int i = 0; i < defGenerics.Length; i++) - { - if (!this.IsMatchingType(defGenerics[i], refGenerics[i])) - return false; - } - } - - return true; - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs index f64bf768..819578e0 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Mono.Cecil; using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { @@ -13,7 +14,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /********* ** Protected methods *********/ - /// Get whether the given method reference can be rewritten. + /// Get whether a method reference should be rewritten. /// The method reference. /// Whether the mod was compiled on a different platform. protected override bool ShouldRewrite(MethodReference methodRef, bool platformChanged) diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 01ca1d66..0b549a86 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -74,7 +74,8 @@ - + + -- cgit From 5f9c03a8a93d51202e737d1d32c21e4a2e55ff08 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 14:28:44 -0500 Subject: add field rewriter for the `Game1.activeClickableMenu` change in SDV 1.2 (#231) --- .../Framework/BaseFieldRewriter.cs | 51 ++++++++++++++++++++++ .../Framework/BaseMethodRewriter.cs | 11 ++--- .../Rewriters/ActiveClickableMenuRewriter.cs | 40 +++++++++++++++++ .../Rewriters/SpriteBatchRewriter.cs | 3 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 + 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs new file mode 100644 index 00000000..7e01ca73 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs @@ -0,0 +1,51 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Base class for a field rewriter. + public abstract class BaseFieldRewriter : IInstructionRewriter + { + /********* + ** Public methods + *********/ + /// Get whether a CIL instruction should be rewritten. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool ShouldRewrite(Instruction instruction, bool platformChanged) + { + if (instruction.OpCode != OpCodes.Ldfld && instruction.OpCode != OpCodes.Ldsfld && instruction.OpCode != OpCodes.Stfld && instruction.OpCode != OpCodes.Stsfld) + return false; // not a field reference + return this.ShouldRewrite(instruction, (FieldReference)instruction.Operand, platformChanged); + } + + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) + { + FieldReference fieldRef = (FieldReference)instruction.Operand; + this.Rewrite(module, cil, instruction, fieldRef, assemblyMap); + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected abstract bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged); + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap); + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs index c9729ee8..e53e5c56 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs @@ -17,13 +17,9 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework /// Whether the mod was compiled on a different platform. public bool ShouldRewrite(Instruction instruction, bool platformChanged) { - // ignore non-method-call instructions if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) - return false; - - // check reference - MethodReference methodRef = (MethodReference)instruction.Operand; - return this.ShouldRewrite(methodRef, platformChanged); + return false; // not a method reference + return this.ShouldRewrite(instruction, (MethodReference)instruction.Operand, platformChanged); } /// Rewrite a CIL instruction for compatibility. @@ -42,9 +38,10 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework ** Protected methods *********/ /// Get whether a method reference should be rewritten. + /// The IL instruction. /// The method reference. /// Whether the mod was compiled on a different platform. - protected abstract bool ShouldRewrite(MethodReference methodRef, bool platformChanged); + protected abstract bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged); /// Rewrite a method for compatibility. /// The module being rewritten. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs new file mode 100644 index 00000000..b8aef019 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs @@ -0,0 +1,40 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites field references to . + /// Stardew Valley changed the field to a property, which broke many mods that reference it. + public class ActiveClickableMenuRewriter : BaseFieldRewriter + { + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == nameof(Game1.activeClickableMenu); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; + MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.activeClickableMenu)}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs index 819578e0..109a6a6e 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs @@ -15,9 +15,10 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters ** Protected methods *********/ /// Get whether a method reference should be rewritten. + /// The IL instruction. /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(MethodReference methodRef, bool platformChanged) + protected override bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged) { return platformChanged && methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 0b549a86..4433131b 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -74,8 +74,10 @@ + + -- cgit From 388ef0a01205387c5ae56f0cdbe8a86a1febc3de Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 22:56:42 -0500 Subject: reorganise rewriters (#231) --- .../Rewriters/ActiveClickableMenuRewriter.cs | 40 --------- .../Crossplatform/SpriteBatch_MethodRewriter.cs | 97 ++++++++++++++++++++++ .../Game1_ActiveClickableMenu_FieldRewriter.cs | 42 ++++++++++ .../Rewriters/SpriteBatchRewriter.cs | 96 --------------------- .../StardewModdingAPI.AssemblyRewriters.csproj | 4 +- src/StardewModdingAPI/Constants.cs | 10 ++- 6 files changed, 148 insertions(+), 141 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs deleted file mode 100644 index b8aef019..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/ActiveClickableMenuRewriter.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters -{ - /// Rewrites field references to . - /// Stardew Valley changed the field to a property, which broke many mods that reference it. - public class ActiveClickableMenuRewriter : BaseFieldRewriter - { - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == nameof(Game1.activeClickableMenu); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) - { - string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; - MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.activeClickableMenu)}")); - cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs new file mode 100644 index 00000000..252fe6d0 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs @@ -0,0 +1,97 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform +{ + /// Rewrites references to to fix inconsistent method signatures between MonoGame and XNA. + /// MonoGame has one SpriteBatch.Begin method with optional arguments, but XNA has multiple method overloads. Incompatible method references are rewritten to use , which redirects all method signatures to the proper compiled MonoGame/XNA method. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class SpriteBatch_MethodRewriter : BaseMethodRewriter + { + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return platformChanged + && methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName + && this.HasMatchingSignature(typeof(SpriteBatch_MethodRewriter.WrapperMethods), methodRef); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which calls the method. + /// The method reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) + { + methodRef.DeclaringType = module.Import(typeof(SpriteBatch_MethodRewriter.WrapperMethods)); + } + + + /********* + ** Wrapper methods + *********/ + /// Wraps methods that are incompatible when converting compiled code between MonoGame and XNA. + public class WrapperMethods : SpriteBatch + { + /********* + ** Public methods + *********/ + /// Construct an instance. + public WrapperMethods(GraphicsDevice graphicsDevice) : base(graphicsDevice) { } + + + /**** + ** MonoGame signatures + ****/ + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity); + } + + /**** + ** XNA signatures + ****/ + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin() + { + base.Begin(); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState) + { + base.Begin(sortMode, blendState); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix); + } + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs new file mode 100644 index 00000000..36634b28 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs @@ -0,0 +1,42 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 +{ + /// Rewrites field references to . + /// Stardew Valley changed the field to a property, which broke many mods that reference it. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class Game1_ActiveClickableMenu_FieldRewriter : BaseFieldRewriter + { + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == nameof(Game1.activeClickableMenu); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; + MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.activeClickableMenu)}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs deleted file mode 100644 index 109a6a6e..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SpriteBatchRewriter.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters -{ - /// Rewrites references to to fix inconsistent method signatures between MonoGame and XNA. - /// MonoGame has one SpriteBatch.Begin method with optional arguments, but XNA has multiple method overloads. Incompatible method references are rewritten to use , which redirects all method signatures to the proper compiled MonoGame/XNA method. - public class SpriteBatchRewriter : BaseMethodRewriter - { - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return platformChanged - && methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName - && this.HasMatchingSignature(typeof(SpriteBatchRewriter.WrapperMethods), methodRef); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) - { - methodRef.DeclaringType = module.Import(typeof(SpriteBatchRewriter.WrapperMethods)); - } - - - /********* - ** Wrapper methods - *********/ - /// Wraps methods that are incompatible when converting compiled code between MonoGame and XNA. - public class WrapperMethods : SpriteBatch - { - /********* - ** Public methods - *********/ - /// Construct an instance. - public WrapperMethods(GraphicsDevice graphicsDevice) : base(graphicsDevice) { } - - - /**** - ** MonoGame signatures - ****/ - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity); - } - - /**** - ** XNA signatures - ****/ - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin() - { - base.Begin(); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState) - { - base.Begin(sortMode, blendState); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix); - } - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 4433131b..a58dc176 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -77,8 +77,8 @@ - - + + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 57704def..cee33560 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -4,7 +4,8 @@ using System.IO; using System.Linq; using System.Reflection; using StardewModdingAPI.AssemblyRewriters; -using StardewModdingAPI.AssemblyRewriters.Rewriters; +using StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform; +using StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2; using StardewValley; namespace StardewModdingAPI @@ -125,8 +126,11 @@ namespace StardewModdingAPI { return new IInstructionRewriter[] { - new SpriteBatchRewriter(), - new ActiveClickableMenuRewriter() + // crossplatform + new SpriteBatch_MethodRewriter(), + + // Stardew Valley 1.2 + new Game1_ActiveClickableMenu_FieldRewriter() }; } -- cgit From 2b336faa1bcdf7c2abc1cffb72c1def70fa6e8a7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 23:03:36 -0500 Subject: add field rewriter for the `Game1.player` change in SDV 1.2 (#231) --- .../Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs | 42 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + src/StardewModdingAPI/Constants.cs | 3 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs new file mode 100644 index 00000000..fb87c19a --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs @@ -0,0 +1,42 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 +{ + /// Rewrites field references to . + /// Stardew Valley changed the field to a property, which broke many mods that reference it. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class Game1_Player_FieldRewriter : BaseFieldRewriter + { + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == nameof(Game1.player); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; + MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.player)}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index a58dc176..2383fb0b 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -77,6 +77,7 @@ + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index cee33560..74387ee3 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -130,7 +130,8 @@ namespace StardewModdingAPI new SpriteBatch_MethodRewriter(), // Stardew Valley 1.2 - new Game1_ActiveClickableMenu_FieldRewriter() + new Game1_ActiveClickableMenu_FieldRewriter(), + new Game1_Player_FieldRewriter() }; } -- cgit From 25a3d9773c7e91903ced41e4bafd164ae42a9644 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 9 Feb 2017 23:24:54 -0500 Subject: add field rewriter for the `Game1.gameMode` change in SDV 1.2 (#231) --- .../SDV1_2/Game1_GameMode_FieldRewriter.cs | 42 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + src/StardewModdingAPI/Constants.cs | 1 + 3 files changed, 44 insertions(+) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs new file mode 100644 index 00000000..e8cf3d13 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs @@ -0,0 +1,42 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 +{ + /// Rewrites field references to . + /// Stardew Valley changed the field to a property, which broke many mods that reference it. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class Game1_GameMode_FieldRewriter : BaseFieldRewriter + { + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == nameof(Game1.gameMode); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; + MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.gameMode)}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 2383fb0b..94501220 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -77,6 +77,7 @@ + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 74387ee3..c71401b1 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -131,6 +131,7 @@ namespace StardewModdingAPI // Stardew Valley 1.2 new Game1_ActiveClickableMenu_FieldRewriter(), + new Game1_GameMode_FieldRewriter(), new Game1_Player_FieldRewriter() }; } -- cgit From a6977878d59d12668dbccae635f9dfbb7d759547 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 10 Feb 2017 02:55:27 -0500 Subject: remove leftover references to Mono.Cecil.Rocks (#231) --- .../StardewModdingAPI.AssemblyRewriters.csproj | 4 ---- src/StardewModdingAPI/StardewModdingAPI.csproj | 1 - 2 files changed, 5 deletions(-) (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 94501220..d5394d67 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -60,10 +60,6 @@ ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll True - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll - True - diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index eca2713f..54cf0565 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -257,6 +257,5 @@ - \ No newline at end of file -- cgit From 3663f70603fae8ed34e1e0c7500adc2c899312a5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 01:01:52 -0500 Subject: split IInstructionFinder from IInstructionRewriter (#247) --- .../Framework/BaseFieldFinder.cs | 32 +++++++++++ .../Framework/BaseFieldRewriter.cs | 18 +----- .../Framework/BaseMethodFinder.cs | 67 ++++++++++++++++++++++ .../Framework/BaseMethodRewriter.cs | 53 +---------------- .../IInstructionFinder.cs | 13 +++++ .../IInstructionRewriter.cs | 7 +-- .../Crossplatform/SpriteBatch_MethodRewriter.cs | 2 +- .../Game1_ActiveClickableMenu_FieldRewriter.cs | 2 +- .../SDV1_2/Game1_GameMode_FieldRewriter.cs | 2 +- .../Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs | 2 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 3 + src/StardewModdingAPI/Framework/AssemblyLoader.cs | 4 +- 12 files changed, 124 insertions(+), 81 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs new file mode 100644 index 00000000..96e8b1c0 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs @@ -0,0 +1,32 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Base class for a field finder. + public abstract class BaseFieldFinder : IInstructionFinder + { + /********* + ** Public methods + *********/ + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool IsMatch(Instruction instruction, bool platformChanged) + { + if (instruction.OpCode != OpCodes.Ldfld && instruction.OpCode != OpCodes.Ldsfld && instruction.OpCode != OpCodes.Stfld && instruction.OpCode != OpCodes.Stsfld) + return false; // not a field reference + return this.IsMatch(instruction, (FieldReference)instruction.Operand, platformChanged); + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected abstract bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged); + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs index 7e01ca73..b2c25587 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs @@ -4,21 +4,11 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Framework { /// Base class for a field rewriter. - public abstract class BaseFieldRewriter : IInstructionRewriter + public abstract class BaseFieldRewriter : BaseFieldFinder, IInstructionRewriter { /********* ** Public methods *********/ - /// Get whether a CIL instruction should be rewritten. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool ShouldRewrite(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Ldfld && instruction.OpCode != OpCodes.Ldsfld && instruction.OpCode != OpCodes.Stfld && instruction.OpCode != OpCodes.Stsfld) - return false; // not a field reference - return this.ShouldRewrite(instruction, (FieldReference)instruction.Operand, platformChanged); - } - /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -34,12 +24,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged); - /// Rewrite a method for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs new file mode 100644 index 00000000..7526286a --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Base class for a method finder. + public abstract class BaseMethodFinder : IInstructionFinder + { + /********* + ** Public methods + *********/ + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool IsMatch(Instruction instruction, bool platformChanged) + { + if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) + return false; // not a method reference + return this.IsMatch(instruction, (MethodReference)instruction.Operand, platformChanged); + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected abstract bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged); + + /// Get whether a method definition matches the signature expected by a method reference. + /// The method definition. + /// The method reference. + protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference) + { + // same name + if (definition.Name != reference.Name) + return false; + + // same arguments + ParameterInfo[] definitionParameters = definition.GetParameters(); + ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); + if (referenceParameters.Length != definitionParameters.Length) + return false; + for (int i = 0; i < referenceParameters.Length; i++) + { + if (!RewriteHelper.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) + return false; + } + return true; + } + + /// Get whether a type has a method whose signature matches the one expected by a method reference. + /// The type to check. + /// The method reference. + protected bool HasMatchingSignature(Type type, MethodReference reference) + { + return type + .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) + .Any(method => this.HasMatchingSignature(method, reference)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs index e53e5c56..6af1a0e1 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs @@ -1,27 +1,14 @@ -using System; -using System.Linq; -using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Framework { /// Base class for a method rewriter. - public abstract class BaseMethodRewriter : IInstructionRewriter + public abstract class BaseMethodRewriter : BaseMethodFinder, IInstructionRewriter { /********* ** Public methods *********/ - /// Get whether a CIL instruction should be rewritten. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool ShouldRewrite(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) - return false; // not a method reference - return this.ShouldRewrite(instruction, (MethodReference)instruction.Operand, platformChanged); - } - /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -37,12 +24,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged); - /// Rewrite a method for compatibility. /// The module being rewritten. /// The CIL rewriter. @@ -50,37 +31,5 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework /// The method reference invoked by the . /// Metadata for mapping assemblies to the current platform. protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap); - - /// Get whether a method definition matches the signature expected by a method reference. - /// The method definition. - /// The method reference. - protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference) - { - // same name - if (definition.Name != reference.Name) - return false; - - // same arguments - ParameterInfo[] definitionParameters = definition.GetParameters(); - ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); - if (referenceParameters.Length != definitionParameters.Length) - return false; - for (int i = 0; i < referenceParameters.Length; i++) - { - if (!RewriteHelper.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) - return false; - } - return true; - } - - /// Get whether a type has a method whose signature matches the one expected by a method reference. - /// The type to check. - /// The method reference. - protected bool HasMatchingSignature(Type type, MethodReference reference) - { - return type - .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) - .Any(method => this.HasMatchingSignature(method, reference)); - } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs new file mode 100644 index 00000000..47a4247b --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs @@ -0,0 +1,13 @@ +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters +{ + /// Finds CIL instructions considered incompatible. + public interface IInstructionFinder + { + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + bool IsMatch(Instruction instruction, bool platformChanged); + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs index 5c24acb6..f99a0a5a 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs @@ -4,13 +4,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters { /// Rewrites a CIL instruction for compatibility. - public interface IInstructionRewriter + public interface IInstructionRewriter : IInstructionFinder { - /// Get whether a CIL instruction should be rewritten. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - bool ShouldRewrite(Instruction instruction, bool platformChanged); - /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs index 252fe6d0..a47b8410 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs @@ -19,7 +19,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform /// The IL instruction. /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, MethodReference methodRef, bool platformChanged) + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) { return platformChanged && methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs index 36634b28..66fe0369 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 /// The IL instruction. /// The field reference. /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs index e8cf3d13..9cb6e13d 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 /// The IL instruction. /// The field reference. /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs index fb87c19a..115e3d7d 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 /// The IL instruction. /// The field reference. /// Whether the mod was compiled on a different platform. - protected override bool ShouldRewrite(Instruction instruction, FieldReference fieldRef, bool platformChanged) + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index d5394d67..03ed6f3a 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -66,7 +66,10 @@ Properties\GlobalAssemblyInfo.cs + + + diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index a932a47c..eb5d1cf4 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -193,7 +193,7 @@ namespace StardewModdingAPI.Framework foreach (MethodDefinition method in this.GetMethods(module)) { // skip methods with no rewritable instructions - bool canRewrite = method.Body.Instructions.Any(op => rewriters.Any(rewriter => rewriter.ShouldRewrite(op, platformChanged))); + bool canRewrite = method.Body.Instructions.Any(op => rewriters.Any(rewriter => rewriter.IsMatch(op, platformChanged))); if (!canRewrite) continue; @@ -203,7 +203,7 @@ namespace StardewModdingAPI.Framework // rewrite instructions foreach (Instruction op in cil.Body.Instructions.ToArray()) { - IInstructionRewriter rewriter = rewriters.FirstOrDefault(p => p.ShouldRewrite(op, platformChanged)); + IInstructionRewriter rewriter = rewriters.FirstOrDefault(p => p.IsMatch(op, platformChanged)); rewriter?.Rewrite(module, cil, op, this.AssemblyMap); } -- cgit From 9fab0bf58f7c8b7d38f026bde4230e39049c056b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 01:32:30 -0500 Subject: reject mods which reference obsolete StardewModdingAPI.Extensions class (#247) --- .../Finders/SMAPI_Extensions_MethodFinder.cs | 31 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + src/StardewModdingAPI/Constants.cs | 3 +++ 3 files changed, 35 insertions(+) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs new file mode 100644 index 00000000..f359b595 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs @@ -0,0 +1,31 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Matches CIL instructions that reference the former StardewModdingAPI.Extensions class, which was removed in SMAPI 1.9. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class SMAPI_Extensions_MethodFinder : BaseMethodFinder + { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } = "obsolete StardewModdingAPI.Extensions API"; + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == "StardewModdingAPI.Extensions"; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 03ed6f3a..4d96d5d3 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -66,6 +66,7 @@ Properties\GlobalAssemblyInfo.cs + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index d8149766..f18e0792 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; using StardewModdingAPI.AssemblyRewriters; +using StardewModdingAPI.AssemblyRewriters.Finders; using StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform; using StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2; using StardewValley; @@ -140,6 +141,8 @@ namespace StardewModdingAPI { return new IInstructionFinder[] { + // APIs removed in SMAPI 1.9 + new SMAPI_Extensions_MethodFinder() }; } -- cgit From fae362723f1390cb7758bd151d50889cf6c999a9 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 03:23:20 -0400 Subject: reject mods which reference obsolete Game1.borderFont and Game1.smoothFont fields (#247) --- .../Finders/Game1_borderFont_FieldFinder.cs | 35 ++++++++++++++++++++++ .../Finders/Game1_smoothFont_FieldFinder.cs | 35 ++++++++++++++++++++++ .../Finders/SMAPI_Extensions_MethodFinder.cs | 2 +- .../Framework/BaseFieldFinder.cs | 7 +++++ .../Game1_ActiveClickableMenu_FieldRewriter.cs | 2 +- .../SDV1_2/Game1_GameMode_FieldRewriter.cs | 2 +- .../Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs | 2 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 ++ src/StardewModdingAPI/Constants.cs | 4 +++ 9 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs new file mode 100644 index 00000000..e25cc544 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference the former Game1.borderFont field, which was removed in Stardew Valley 1.2.3–1.2.6. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class Game1_borderFont_FieldFinder : BaseFieldFinder + { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } = $"obsolete {nameof(Game1)}.borderFont field"; + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + this.IsStaticField(instruction) + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == "borderFont"; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs new file mode 100644 index 00000000..b852f10d --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewValley; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference the former Game1.smoothFont field, which was removed in Stardew Valley 1.2.3–1.2.6. + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] + public class Game1_smoothFont_FieldFinder : BaseFieldFinder + { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } = $"obsolete {nameof(Game1)}.smoothFont field"; + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + this.IsStaticField(instruction) + && fieldRef.DeclaringType.FullName == typeof(Game1).FullName + && fieldRef.Name == "smoothFont"; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs index f359b595..4abcbc13 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs @@ -5,7 +5,7 @@ using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Matches CIL instructions that reference the former StardewModdingAPI.Extensions class, which was removed in SMAPI 1.9. + /// Finds CIL instructions that reference the former StardewModdingAPI.Extensions class, which was removed in SMAPI 1.9. [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] public class SMAPI_Extensions_MethodFinder : BaseMethodFinder { diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs index f2074f22..ac2facec 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs @@ -35,5 +35,12 @@ namespace StardewModdingAPI.AssemblyRewriters.Framework /// The field reference. /// Whether the mod was compiled on a different platform. protected abstract bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged); + + /// Whether an instruction is a static field reference. + /// The IL instruction. + protected bool IsStaticField(Instruction instruction) + { + return instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld; + } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs index bb49f16c..59a7c798 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return - (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + this.IsStaticField(instruction) && fieldRef.DeclaringType.FullName == typeof(Game1).FullName && fieldRef.Name == nameof(Game1.activeClickableMenu); } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs index 4d84d9ac..c3da6863 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return - (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + this.IsStaticField(instruction) && fieldRef.DeclaringType.FullName == typeof(Game1).FullName && fieldRef.Name == nameof(Game1.gameMode); } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs index f43f5d57..91eae416 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) { return - (instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld) // static field + this.IsStaticField(instruction) && fieldRef.DeclaringType.FullName == typeof(Game1).FullName && fieldRef.Name == nameof(Game1.player); } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 4d96d5d3..3f437ca0 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -66,6 +66,8 @@ Properties\GlobalAssemblyInfo.cs + + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index f18e0792..563cdd52 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -141,6 +141,10 @@ namespace StardewModdingAPI { return new IInstructionFinder[] { + // changes in Stardew Valley 1.2 + new Game1_borderFont_FieldFinder(), + new Game1_smoothFont_FieldFinder(), + // APIs removed in SMAPI 1.9 new SMAPI_Extensions_MethodFinder() }; -- cgit From a12bcf3b7845b7c4541ca7539a4810a81b87e3ce Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 17:48:53 -0400 Subject: reject mods which reference obsolete SGame class (#247) --- .../Finders/GenericTypeFinder.cs | 30 +++++++++++++ .../Framework/BaseTypeFinder.cs | 52 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 2 + src/StardewModdingAPI/Constants.cs | 3 +- 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs new file mode 100644 index 00000000..11ffa734 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs @@ -0,0 +1,30 @@ +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Base class for a type reference finder. + public class GenericTypeFinder : BaseTypeFinder + { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + /// The full type name to match. + public override string FullTypeName { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name to match. + /// A brief noun phrase indicating what the instruction finder matches. + public GenericTypeFinder(string fullTypeName, string nounPhrase) + { + this.FullTypeName = fullTypeName; + this.NounPhrase = nounPhrase; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs new file mode 100644 index 00000000..5a768794 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs @@ -0,0 +1,52 @@ +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Framework +{ + /// Base class for a type reference finder. + public abstract class BaseTypeFinder : IInstructionFinder + { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public abstract string NounPhrase { get; } + + /// The full type name to match. + public abstract string FullTypeName { get; } + + + /********* + ** Public methods + *********/ + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public virtual bool IsMatch(Instruction instruction, bool platformChanged) + { + string fullName = this.FullTypeName; + + // field reference + if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) + { + FieldReference field = (FieldReference)instruction.Operand; + return + field.DeclaringType.FullName == fullName // field on target class + || field.FieldType.FullName == fullName; // field value is target class + } + + // method reference + if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) + { + MethodReference method = (MethodReference)instruction.Operand; + return + method.DeclaringType.FullName == fullName // method on target class + || method.ReturnType.FullName == fullName // method returns target class + || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters + } + + return false; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 3f437ca0..d489eb34 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -69,8 +69,10 @@ + + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 563cdd52..1e1ca325 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -146,7 +146,8 @@ namespace StardewModdingAPI new Game1_smoothFont_FieldFinder(), // APIs removed in SMAPI 1.9 - new SMAPI_Extensions_MethodFinder() + new SMAPI_Extensions_MethodFinder(), + new GenericTypeFinder("StardewModdingAPI.Inheritance.SGame", "obsolete StardewModdingAPI.SGame class") }; } -- cgit From ccc57935de9b75e17b9f531df87e2ac4dac43dfc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 18:25:29 -0400 Subject: replace individual instruction finders with generic implementations (#247) --- .../Finders/Game1_borderFont_FieldFinder.cs | 35 ------------- .../Finders/Game1_smoothFont_FieldFinder.cs | 35 ------------- .../Finders/GenericFieldFinder.cs | 61 ++++++++++++++++++++++ .../Finders/GenericMethodFinder.cs | 54 +++++++++++++++++++ .../Finders/GenericTypeFinder.cs | 54 +++++++++++++++---- .../Finders/SMAPI_Extensions_MethodFinder.cs | 31 ----------- .../Framework/BaseTypeFinder.cs | 52 ------------------ .../StardewModdingAPI.AssemblyRewriters.csproj | 6 +-- src/StardewModdingAPI/Constants.cs | 8 +-- 9 files changed, 165 insertions(+), 171 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs deleted file mode 100644 index e25cc544..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_borderFont_FieldFinder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference the former Game1.borderFont field, which was removed in Stardew Valley 1.2.3–1.2.6. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class Game1_borderFont_FieldFinder : BaseFieldFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"obsolete {nameof(Game1)}.borderFont field"; - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == "borderFont"; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs deleted file mode 100644 index b852f10d..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/Game1_smoothFont_FieldFinder.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference the former Game1.smoothFont field, which was removed in Stardew Valley 1.2.3–1.2.6. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class Game1_smoothFont_FieldFinder : BaseFieldFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"obsolete {nameof(Game1)}.smoothFont field"; - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == "smoothFont"; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs new file mode 100644 index 00000000..056422a4 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs @@ -0,0 +1,61 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given field. + public sealed class GenericFieldFinder : BaseFieldFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The field name for which to find references. + private readonly string FieldName; + + /// Whether the field to match is static. + private readonly bool IsStatic; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The field name for which to find references. + /// Whether the field to match is static. + public GenericFieldFinder(string fullTypeName, string fieldName, bool isStatic) + { + this.FullTypeName = fullTypeName; + this.FieldName = fieldName; + this.IsStatic = isStatic; + this.NounPhrase = $"obsolete {fullTypeName}.{fieldName} field"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + this.IsStaticField(instruction) == this.IsStatic + && fieldRef.DeclaringType.FullName == this.FullTypeName + && fieldRef.Name == this.FieldName; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs new file mode 100644 index 00000000..f5443558 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs @@ -0,0 +1,54 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given method. + public sealed class GenericMethodFinder : BaseMethodFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The method name for which to find references. + private readonly string MethodName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The method name for which to find references. + public GenericMethodFinder(string fullTypeName, string methodName) + { + this.FullTypeName = fullTypeName; + this.MethodName = methodName; + this.NounPhrase = $"obsolete {fullTypeName}.{methodName} method"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == this.FullTypeName + && methodRef.Name == this.MethodName; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs index 11ffa734..1556cc3c 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs @@ -1,18 +1,24 @@ -using StardewModdingAPI.AssemblyRewriters.Framework; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Base class for a type reference finder. - public class GenericTypeFinder : BaseTypeFinder + /// Finds CIL instructions that reference a given type. + public sealed class GenericTypeFinder : IInstructionFinder { /********* ** Accessors *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + /// The full type name for which to find references. + private readonly string FullTypeName; + - /// The full type name to match. - public override string FullTypeName { get; } + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public string NounPhrase { get; } /********* @@ -20,11 +26,39 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders *********/ /// Construct an instance. /// The full type name to match. - /// A brief noun phrase indicating what the instruction finder matches. - public GenericTypeFinder(string fullTypeName, string nounPhrase) + public GenericTypeFinder(string fullTypeName) { this.FullTypeName = fullTypeName; - this.NounPhrase = nounPhrase; + this.NounPhrase = $"obsolete {fullTypeName} type"; + } + + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool IsMatch(Instruction instruction, bool platformChanged) + { + string fullName = this.FullTypeName; + + // field reference + if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) + { + FieldReference field = (FieldReference)instruction.Operand; + return + field.DeclaringType.FullName == fullName // field on target class + || field.FieldType.FullName == fullName; // field value is target class + } + + // method reference + if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) + { + MethodReference method = (MethodReference)instruction.Operand; + return + method.DeclaringType.FullName == fullName // method on target class + || method.ReturnType.FullName == fullName // method returns target class + || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters + } + + return false; } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs deleted file mode 100644 index 4abcbc13..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/SMAPI_Extensions_MethodFinder.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference the former StardewModdingAPI.Extensions class, which was removed in SMAPI 1.9. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class SMAPI_Extensions_MethodFinder : BaseMethodFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = "obsolete StardewModdingAPI.Extensions API"; - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return methodRef.DeclaringType.FullName == "StardewModdingAPI.Extensions"; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs deleted file mode 100644 index 5a768794..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseTypeFinder.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a type reference finder. - public abstract class BaseTypeFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public abstract string NounPhrase { get; } - - /// The full type name to match. - public abstract string FullTypeName { get; } - - - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public virtual bool IsMatch(Instruction instruction, bool platformChanged) - { - string fullName = this.FullTypeName; - - // field reference - if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) - { - FieldReference field = (FieldReference)instruction.Operand; - return - field.DeclaringType.FullName == fullName // field on target class - || field.FieldType.FullName == fullName; // field value is target class - } - - // method reference - if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) - { - MethodReference method = (MethodReference)instruction.Operand; - return - method.DeclaringType.FullName == fullName // method on target class - || method.ReturnType.FullName == fullName // method returns target class - || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters - } - - return false; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index d489eb34..5089318c 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -66,10 +66,8 @@ Properties\GlobalAssemblyInfo.cs - - - - + + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 1e1ca325..4c1c7a8e 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -142,12 +142,12 @@ namespace StardewModdingAPI return new IInstructionFinder[] { // changes in Stardew Valley 1.2 - new Game1_borderFont_FieldFinder(), - new Game1_smoothFont_FieldFinder(), + new GenericFieldFinder("Game1", "borderFont", isStatic: true), + new GenericFieldFinder("Game1", "smoothFont", isStatic: true), // APIs removed in SMAPI 1.9 - new SMAPI_Extensions_MethodFinder(), - new GenericTypeFinder("StardewModdingAPI.Inheritance.SGame", "obsolete StardewModdingAPI.SGame class") + new GenericTypeFinder("StardewModdingAPI.Extensions"), + new GenericTypeFinder("StardewModdingAPI.Inheritance.SGame") }; } -- cgit From ac19a1a85aac382271b01bfc801f7a293f8b0804 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 19:05:31 -0400 Subject: add incompatibility finders for events removed in SMAPI 1.9 (#247) --- .../Finders/GenericEventFinder.cs | 54 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + src/StardewModdingAPI/Constants.cs | 8 +++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs new file mode 100644 index 00000000..c2a981e5 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs @@ -0,0 +1,54 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given event. + public sealed class GenericEventFinder : BaseMethodFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The event name for which to find references. + private readonly string EventName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The event name for which to find references. + public GenericEventFinder(string fullTypeName, string eventName) + { + this.FullTypeName = fullTypeName; + this.EventName = eventName; + this.NounPhrase = $"obsolete {fullTypeName}.{eventName} event"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == this.FullTypeName + && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 5089318c..a3322e67 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -67,6 +67,7 @@ Properties\GlobalAssemblyInfo.cs + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index d86a065b..c0d3e3ae 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -154,7 +154,13 @@ namespace StardewModdingAPI new GenericTypeFinder("StardewModdingAPI.Inheritance.SObject"), new GenericTypeFinder("StardewModdingAPI.LogWriter"), new GenericTypeFinder("StardewModdingAPI.Manifest"), - new GenericTypeFinder("StardewModdingAPI.Version") + new GenericTypeFinder("StardewModdingAPI.Version"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawDebug"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawTick"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderHudEventNoCheck"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderGuiEventNoCheck"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck"), + new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck"), }; } -- cgit From da630efc1d5c95816493c2969736ae883e723757 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 14 Mar 2017 14:15:50 -0400 Subject: downgrade to .NET Framework 4.0 for better compatibility on Windows 7–8.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- release-notes.md | 1 + .../PlatformAssemblyMap.cs | 2 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 14 +++++------ .../packages.config | 2 +- .../StardewModdingAPI.Installer.csproj | 2 +- src/StardewModdingAPI/App.config | 2 +- .../Events/ContentEventHandler.cs | 8 ++++++ src/StardewModdingAPI/Events/ContentEvents.cs | 2 +- .../Framework/InternalExtensions.cs | 29 ++++++++++++++++++++-- .../Framework/Reflection/PrivateProperty.cs | 4 +-- src/StardewModdingAPI/Framework/UpdateHelper.cs | 7 +++--- src/StardewModdingAPI/Program.cs | 24 ++++++------------ src/StardewModdingAPI/StardewModdingAPI.csproj | 20 ++++++++------- src/StardewModdingAPI/packages.config | 4 +-- src/TrainerMod/TrainerMod.csproj | 4 +-- src/TrainerMod/packages.config | 2 +- 16 files changed, 77 insertions(+), 50 deletions(-) create mode 100644 src/StardewModdingAPI/Events/ContentEventHandler.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/release-notes.md b/release-notes.md index f91ef733..01d49dd7 100644 --- a/release-notes.md +++ b/release-notes.md @@ -20,6 +20,7 @@ For players: * Simplified error messages when a mod can't be loaded. * Simple nested mod folders are now recognised by SMAPI (e.g. `ModName-1.0\ModName\manifest.json`). * Improved TrainerMod command handling & feedback. +* Reduced minimum .NET Framework version to 4.0 for improved compatibility on Windows 7–8.1. * Fixed game's debug output being shown in the console for all users. * Fixed the game-outdated error not pausing before exit. * Fixed installer errors for some players when deleting files. diff --git a/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs b/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs index fce2b187..3ca90149 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI.AssemblyRewriters // cache assembly metadata this.Targets = targetAssemblies; this.TargetReferences = this.Targets.ToDictionary(assembly => assembly, assembly => AssemblyNameReference.Parse(assembly.FullName)); - this.TargetModules = this.Targets.ToDictionary(assembly => assembly, assembly => ModuleDefinition.ReadModule(assembly.Modules.Single().FullyQualifiedName)); + this.TargetModules = this.Targets.ToDictionary(assembly => assembly, assembly => ModuleDefinition.ReadModule(assembly.GetModules().Single().FullyQualifiedName)); } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index a3322e67..15bb15ac 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -9,7 +9,7 @@ Properties StardewModdingAPI.AssemblyRewriters StardewModdingAPI.AssemblyRewriters - v4.5 + v4.0 512 @@ -49,16 +49,16 @@ - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll - True + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.dll - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll - True + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Mdb.dll - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll - True + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Pdb.dll + + + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Rocks.dll diff --git a/src/StardewModdingAPI.AssemblyRewriters/packages.config b/src/StardewModdingAPI.AssemblyRewriters/packages.config index 88fbc79d..70ba1aed 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/packages.config +++ b/src/StardewModdingAPI.AssemblyRewriters/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj index e31a1452..366e1c6e 100644 --- a/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj +++ b/src/StardewModdingAPI.Installer/StardewModdingAPI.Installer.csproj @@ -9,7 +9,7 @@ Properties StardewModdingAPI.Installer StardewModdingAPI.Installer - v4.5 + v4.0 512 true diff --git a/src/StardewModdingAPI/App.config b/src/StardewModdingAPI/App.config index 27cdf0f7..314845f7 100644 --- a/src/StardewModdingAPI/App.config +++ b/src/StardewModdingAPI/App.config @@ -1,7 +1,7 @@ - + diff --git a/src/StardewModdingAPI/Events/ContentEventHandler.cs b/src/StardewModdingAPI/Events/ContentEventHandler.cs new file mode 100644 index 00000000..2a7e75d1 --- /dev/null +++ b/src/StardewModdingAPI/Events/ContentEventHandler.cs @@ -0,0 +1,8 @@ +namespace StardewModdingAPI.Events +{ + /// Represents a method that will handle a content event. + /// The source of the event. + /// The event arguments. + /// This deviates from in allowing T to be an interface instead of a concrete class. While .NET Framework 4.5 allows that, the current .NET Framework 4.0 targeted by SMAPI to improve compatibility does not. + public delegate void ContentEventHandler(object sender, IContentEventHelper e); +} diff --git a/src/StardewModdingAPI/Events/ContentEvents.cs b/src/StardewModdingAPI/Events/ContentEvents.cs index 9418673a..339e90fd 100644 --- a/src/StardewModdingAPI/Events/ContentEvents.cs +++ b/src/StardewModdingAPI/Events/ContentEvents.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.Events public static event EventHandler> AfterLocaleChanged; /// Raised when an XNB file is being read into the cache. Mods can change the data here before it's cached. - internal static event EventHandler AfterAssetLoaded; + internal static event ContentEventHandler AfterAssetLoaded; /********* diff --git a/src/StardewModdingAPI/Framework/InternalExtensions.cs b/src/StardewModdingAPI/Framework/InternalExtensions.cs index 4ca79518..46c76656 100644 --- a/src/StardewModdingAPI/Framework/InternalExtensions.cs +++ b/src/StardewModdingAPI/Framework/InternalExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework { @@ -59,12 +60,36 @@ namespace StardewModdingAPI.Framework /// The event handlers. /// The event sender. /// The event arguments. - public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, TEventArgs args) + public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, TEventArgs args) where TEventArgs : EventArgs { if (handlers == null) return; - foreach (EventHandler handler in Enumerable.Cast>(handlers)) + foreach (EventHandler handler in handlers.Cast>()) + { + try + { + handler.Invoke(sender, args); + } + catch (Exception ex) + { + monitor.Log($"A mod failed handling the {name} event:\n{ex.GetLogSummary()}", LogLevel.Error); + } + } + } + + /// Safely raise an event, and intercept any exceptions thrown by its handlers. + /// Encapsulates monitoring and logging. + /// The event name for error messages. + /// The event handlers. + /// The event sender. + /// The event arguments. + public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, IContentEventHelper args) + { + if (handlers == null) + return; + + foreach (ContentEventHandler handler in handlers.Cast()) { try { diff --git a/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs b/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs index 08204b7e..d89e8e44 100644 --- a/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs +++ b/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs @@ -60,7 +60,7 @@ namespace StardewModdingAPI.Framework.Reflection { try { - return (TValue)this.PropertyInfo.GetValue(this.Parent); + return (TValue)this.PropertyInfo.GetValue(this.Parent, null); } catch (InvalidCastException) { @@ -78,7 +78,7 @@ namespace StardewModdingAPI.Framework.Reflection { try { - this.PropertyInfo.SetValue(this.Parent, value); + this.PropertyInfo.SetValue(this.Parent, value, null); } catch (InvalidCastException) { diff --git a/src/StardewModdingAPI/Framework/UpdateHelper.cs b/src/StardewModdingAPI/Framework/UpdateHelper.cs index e01e55c8..342a08cf 100644 --- a/src/StardewModdingAPI/Framework/UpdateHelper.cs +++ b/src/StardewModdingAPI/Framework/UpdateHelper.cs @@ -1,7 +1,6 @@ using System.IO; using System.Net; using System.Reflection; -using System.Threading.Tasks; using Newtonsoft.Json; using StardewModdingAPI.Framework.Models; @@ -15,17 +14,17 @@ namespace StardewModdingAPI.Framework *********/ /// Get the latest release from a GitHub repository. /// The name of the repository from which to fetch releases (like "cjsu/SMAPI"). - public static async Task GetLatestVersionAsync(string repository) + public static GitRelease GetLatestVersion(string repository) { // build request // (avoid HttpClient for Mac compatibility) - HttpWebRequest request = WebRequest.CreateHttp($"https://api.github.com/repos/{repository}/releases/latest"); + HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"https://api.github.com/repos/{repository}/releases/latest"); AssemblyName assembly = typeof(UpdateHelper).Assembly.GetName(); request.UserAgent = $"{assembly.Name}/{assembly.Version}"; request.Accept = "application/vnd.github.v3+json"; // fetch data - using (WebResponse response = await request.GetResponseAsync()) + using (WebResponse response = request.GetResponse()) using (Stream responseStream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(responseStream)) { diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index db7a3df6..81e6518e 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -187,7 +187,7 @@ namespace StardewModdingAPI { try { - GitRelease release = UpdateHelper.GetLatestVersionAsync(Constants.GitHubRepository).Result; + GitRelease release = UpdateHelper.GetLatestVersion(Constants.GitHubRepository); ISemanticVersion latestVersion = new SemanticVersion(release.Tag); if (latestVersion.IsNewerThan(Constants.ApiVersion)) this.Monitor.Log($"You can update SMAPI from version {Constants.ApiVersion} to {latestVersion}", LogLevel.Alert); @@ -446,26 +446,18 @@ namespace StardewModdingAPI continue; } - // validate assembly + // initialise mod try { - if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) == 0) + // get mod entry type + Type modEntryType = modAssembly.GetExportedTypes().FirstOrDefault(x => x.BaseType == typeof(Mod)); + if(modEntryType == null) { - this.Monitor.Log($"{skippedPrefix} because its DLL has no 'Mod' subclass.", LogLevel.Error); + this.Monitor.Log($"{skippedPrefix} because its DLL has no {typeof(Mod).FullName} entry class.", LogLevel.Error); continue; } - } - catch (Exception ex) - { - this.Monitor.Log($"{skippedPrefix} because its DLL couldn't be loaded.\n{ex.GetLogSummary()}", LogLevel.Error); - continue; - } - - // initialise mod - try - { - // get implementation - TypeInfo modEntryType = modAssembly.DefinedTypes.First(x => x.BaseType == typeof(Mod)); + + // get mod class Mod mod = (Mod)modAssembly.CreateInstance(modEntryType.ToString()); if (mod == null) { diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index dceae74e..3a2bb756 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -9,7 +9,7 @@ Properties StardewModdingAPI StardewModdingAPI - v4.5 + v4.0 512 false @@ -60,7 +60,7 @@ $(SolutionDir)\..\bin\Debug\SMAPI $(SolutionDir)\..\bin\Debug\SMAPI\StardewModdingAPI.xml true - 6 + 7 x86 @@ -79,19 +79,22 @@ - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.dll True - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Mdb.dll True - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Pdb.dll True + + ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Rocks.dll + - ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.8.0.3\lib\net40\Newtonsoft.Json.dll True @@ -116,6 +119,7 @@ Properties\GlobalAssemblyInfo.cs + @@ -215,12 +219,10 @@ Designer - - Designer - Always + Always diff --git a/src/StardewModdingAPI/packages.config b/src/StardewModdingAPI/packages.config index e5fa3c3a..1dee2c2a 100644 --- a/src/StardewModdingAPI/packages.config +++ b/src/StardewModdingAPI/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj index a6303767..7845bd8c 100644 --- a/src/TrainerMod/TrainerMod.csproj +++ b/src/TrainerMod/TrainerMod.csproj @@ -9,7 +9,7 @@ Properties TrainerMod TrainerMod - v4.5 + v4.0 512 @@ -39,7 +39,7 @@ - ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.8.0.3\lib\net40\Newtonsoft.Json.dll True diff --git a/src/TrainerMod/packages.config b/src/TrainerMod/packages.config index 75e68e71..2c6c3f12 100644 --- a/src/TrainerMod/packages.config +++ b/src/TrainerMod/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file -- cgit From 307304a03edb82f3a1f5cfa6c47cb17687560ccb Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 14 Mar 2017 18:16:44 -0400 Subject: revert all projects except installer to .NET Framework 4.5 This caused obscure invalid-IL crashes when compiled through MonoDevelop on Linux. --- release-notes.md | 1 - .../PlatformAssemblyMap.cs | 2 +- .../StardewModdingAPI.AssemblyRewriters.csproj | 14 +++++------ .../packages.config | 2 +- src/StardewModdingAPI/App.config | 2 +- .../Events/ContentEventHandler.cs | 8 ------ src/StardewModdingAPI/Events/ContentEvents.cs | 2 +- .../Framework/InternalExtensions.cs | 29 ++-------------------- .../Framework/Reflection/PrivateProperty.cs | 4 +-- src/StardewModdingAPI/Framework/UpdateHelper.cs | 7 +++--- src/StardewModdingAPI/Program.cs | 24 ++++++++++++------ src/StardewModdingAPI/StardewModdingAPI.csproj | 20 +++++++-------- src/StardewModdingAPI/packages.config | 4 +-- src/TrainerMod/TrainerMod.csproj | 4 +-- src/TrainerMod/packages.config | 2 +- 15 files changed, 49 insertions(+), 76 deletions(-) delete mode 100644 src/StardewModdingAPI/Events/ContentEventHandler.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/release-notes.md b/release-notes.md index 01d49dd7..f91ef733 100644 --- a/release-notes.md +++ b/release-notes.md @@ -20,7 +20,6 @@ For players: * Simplified error messages when a mod can't be loaded. * Simple nested mod folders are now recognised by SMAPI (e.g. `ModName-1.0\ModName\manifest.json`). * Improved TrainerMod command handling & feedback. -* Reduced minimum .NET Framework version to 4.0 for improved compatibility on Windows 7–8.1. * Fixed game's debug output being shown in the console for all users. * Fixed the game-outdated error not pausing before exit. * Fixed installer errors for some players when deleting files. diff --git a/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs b/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs index 3ca90149..fce2b187 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/PlatformAssemblyMap.cs @@ -49,7 +49,7 @@ namespace StardewModdingAPI.AssemblyRewriters // cache assembly metadata this.Targets = targetAssemblies; this.TargetReferences = this.Targets.ToDictionary(assembly => assembly, assembly => AssemblyNameReference.Parse(assembly.FullName)); - this.TargetModules = this.Targets.ToDictionary(assembly => assembly, assembly => ModuleDefinition.ReadModule(assembly.GetModules().Single().FullyQualifiedName)); + this.TargetModules = this.Targets.ToDictionary(assembly => assembly, assembly => ModuleDefinition.ReadModule(assembly.Modules.Single().FullyQualifiedName)); } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 15bb15ac..a3322e67 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -9,7 +9,7 @@ Properties StardewModdingAPI.AssemblyRewriters StardewModdingAPI.AssemblyRewriters - v4.0 + v4.5 512 @@ -49,16 +49,16 @@ - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll + True - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Mdb.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll + True - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Pdb.dll - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Rocks.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll + True diff --git a/src/StardewModdingAPI.AssemblyRewriters/packages.config b/src/StardewModdingAPI.AssemblyRewriters/packages.config index 70ba1aed..88fbc79d 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/packages.config +++ b/src/StardewModdingAPI.AssemblyRewriters/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/StardewModdingAPI/App.config b/src/StardewModdingAPI/App.config index 314845f7..27cdf0f7 100644 --- a/src/StardewModdingAPI/App.config +++ b/src/StardewModdingAPI/App.config @@ -1,7 +1,7 @@ - + diff --git a/src/StardewModdingAPI/Events/ContentEventHandler.cs b/src/StardewModdingAPI/Events/ContentEventHandler.cs deleted file mode 100644 index 2a7e75d1..00000000 --- a/src/StardewModdingAPI/Events/ContentEventHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace StardewModdingAPI.Events -{ - /// Represents a method that will handle a content event. - /// The source of the event. - /// The event arguments. - /// This deviates from in allowing T to be an interface instead of a concrete class. While .NET Framework 4.5 allows that, the current .NET Framework 4.0 targeted by SMAPI to improve compatibility does not. - public delegate void ContentEventHandler(object sender, IContentEventHelper e); -} diff --git a/src/StardewModdingAPI/Events/ContentEvents.cs b/src/StardewModdingAPI/Events/ContentEvents.cs index 339e90fd..9418673a 100644 --- a/src/StardewModdingAPI/Events/ContentEvents.cs +++ b/src/StardewModdingAPI/Events/ContentEvents.cs @@ -28,7 +28,7 @@ namespace StardewModdingAPI.Events public static event EventHandler> AfterLocaleChanged; /// Raised when an XNB file is being read into the cache. Mods can change the data here before it's cached. - internal static event ContentEventHandler AfterAssetLoaded; + internal static event EventHandler AfterAssetLoaded; /********* diff --git a/src/StardewModdingAPI/Framework/InternalExtensions.cs b/src/StardewModdingAPI/Framework/InternalExtensions.cs index 46c76656..4ca79518 100644 --- a/src/StardewModdingAPI/Framework/InternalExtensions.cs +++ b/src/StardewModdingAPI/Framework/InternalExtensions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using StardewModdingAPI.Events; namespace StardewModdingAPI.Framework { @@ -60,36 +59,12 @@ namespace StardewModdingAPI.Framework /// The event handlers. /// The event sender. /// The event arguments. - public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, TEventArgs args) where TEventArgs : EventArgs + public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, TEventArgs args) { if (handlers == null) return; - foreach (EventHandler handler in handlers.Cast>()) - { - try - { - handler.Invoke(sender, args); - } - catch (Exception ex) - { - monitor.Log($"A mod failed handling the {name} event:\n{ex.GetLogSummary()}", LogLevel.Error); - } - } - } - - /// Safely raise an event, and intercept any exceptions thrown by its handlers. - /// Encapsulates monitoring and logging. - /// The event name for error messages. - /// The event handlers. - /// The event sender. - /// The event arguments. - public static void SafelyRaiseGenericEvent(this IMonitor monitor, string name, IEnumerable handlers, object sender, IContentEventHelper args) - { - if (handlers == null) - return; - - foreach (ContentEventHandler handler in handlers.Cast()) + foreach (EventHandler handler in Enumerable.Cast>(handlers)) { try { diff --git a/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs b/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs index d89e8e44..08204b7e 100644 --- a/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs +++ b/src/StardewModdingAPI/Framework/Reflection/PrivateProperty.cs @@ -60,7 +60,7 @@ namespace StardewModdingAPI.Framework.Reflection { try { - return (TValue)this.PropertyInfo.GetValue(this.Parent, null); + return (TValue)this.PropertyInfo.GetValue(this.Parent); } catch (InvalidCastException) { @@ -78,7 +78,7 @@ namespace StardewModdingAPI.Framework.Reflection { try { - this.PropertyInfo.SetValue(this.Parent, value, null); + this.PropertyInfo.SetValue(this.Parent, value); } catch (InvalidCastException) { diff --git a/src/StardewModdingAPI/Framework/UpdateHelper.cs b/src/StardewModdingAPI/Framework/UpdateHelper.cs index 342a08cf..e01e55c8 100644 --- a/src/StardewModdingAPI/Framework/UpdateHelper.cs +++ b/src/StardewModdingAPI/Framework/UpdateHelper.cs @@ -1,6 +1,7 @@ using System.IO; using System.Net; using System.Reflection; +using System.Threading.Tasks; using Newtonsoft.Json; using StardewModdingAPI.Framework.Models; @@ -14,17 +15,17 @@ namespace StardewModdingAPI.Framework *********/ /// Get the latest release from a GitHub repository. /// The name of the repository from which to fetch releases (like "cjsu/SMAPI"). - public static GitRelease GetLatestVersion(string repository) + public static async Task GetLatestVersionAsync(string repository) { // build request // (avoid HttpClient for Mac compatibility) - HttpWebRequest request = (HttpWebRequest)WebRequest.Create($"https://api.github.com/repos/{repository}/releases/latest"); + HttpWebRequest request = WebRequest.CreateHttp($"https://api.github.com/repos/{repository}/releases/latest"); AssemblyName assembly = typeof(UpdateHelper).Assembly.GetName(); request.UserAgent = $"{assembly.Name}/{assembly.Version}"; request.Accept = "application/vnd.github.v3+json"; // fetch data - using (WebResponse response = request.GetResponse()) + using (WebResponse response = await request.GetResponseAsync()) using (Stream responseStream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(responseStream)) { diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index 81e6518e..db7a3df6 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -187,7 +187,7 @@ namespace StardewModdingAPI { try { - GitRelease release = UpdateHelper.GetLatestVersion(Constants.GitHubRepository); + GitRelease release = UpdateHelper.GetLatestVersionAsync(Constants.GitHubRepository).Result; ISemanticVersion latestVersion = new SemanticVersion(release.Tag); if (latestVersion.IsNewerThan(Constants.ApiVersion)) this.Monitor.Log($"You can update SMAPI from version {Constants.ApiVersion} to {latestVersion}", LogLevel.Alert); @@ -446,18 +446,26 @@ namespace StardewModdingAPI continue; } - // initialise mod + // validate assembly try { - // get mod entry type - Type modEntryType = modAssembly.GetExportedTypes().FirstOrDefault(x => x.BaseType == typeof(Mod)); - if(modEntryType == null) + if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) == 0) { - this.Monitor.Log($"{skippedPrefix} because its DLL has no {typeof(Mod).FullName} entry class.", LogLevel.Error); + this.Monitor.Log($"{skippedPrefix} because its DLL has no 'Mod' subclass.", LogLevel.Error); continue; } - - // get mod class + } + catch (Exception ex) + { + this.Monitor.Log($"{skippedPrefix} because its DLL couldn't be loaded.\n{ex.GetLogSummary()}", LogLevel.Error); + continue; + } + + // initialise mod + try + { + // get implementation + TypeInfo modEntryType = modAssembly.DefinedTypes.First(x => x.BaseType == typeof(Mod)); Mod mod = (Mod)modAssembly.CreateInstance(modEntryType.ToString()); if (mod == null) { diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index dcb299a2..99666f08 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -9,7 +9,7 @@ Properties StardewModdingAPI StardewModdingAPI - v4.0 + v4.5 512 false @@ -77,22 +77,19 @@ - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll True - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Mdb.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll True - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Pdb.dll + ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll True - - ..\packages\Mono.Cecil.0.9.6.4\lib\net40\Mono.Cecil.Rocks.dll - - ..\packages\Newtonsoft.Json.8.0.3\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll True @@ -117,7 +114,6 @@ Properties\GlobalAssemblyInfo.cs - @@ -217,10 +213,12 @@ Designer + + Designer + Always - Always @@ -282,4 +280,4 @@ - \ No newline at end of file + diff --git a/src/StardewModdingAPI/packages.config b/src/StardewModdingAPI/packages.config index 1dee2c2a..e5fa3c3a 100644 --- a/src/StardewModdingAPI/packages.config +++ b/src/StardewModdingAPI/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/TrainerMod/TrainerMod.csproj b/src/TrainerMod/TrainerMod.csproj index c66f2ab8..0bd667d4 100644 --- a/src/TrainerMod/TrainerMod.csproj +++ b/src/TrainerMod/TrainerMod.csproj @@ -9,7 +9,7 @@ Properties TrainerMod TrainerMod - v4.0 + v4.5 512 @@ -37,7 +37,7 @@ - ..\packages\Newtonsoft.Json.8.0.3\lib\net40\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll True diff --git a/src/TrainerMod/packages.config b/src/TrainerMod/packages.config index 2c6c3f12..75e68e71 100644 --- a/src/TrainerMod/packages.config +++ b/src/TrainerMod/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file -- cgit From d724f54f32c4de113700fd2173c070d6a992f66e Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 12:55:06 -0400 Subject: replace SpriteBatch rewriter with a generic method mapper --- .../Crossplatform/SpriteBatch_MethodRewriter.cs | 104 --------------------- .../Rewriters/GenericMethodMapper.cs | 74 +++++++++++++++ .../Rewriters/Wrappers/SpriteBatchWrapper.cs | 59 ++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 3 +- src/StardewModdingAPI/Constants.cs | 6 +- 5 files changed, 139 insertions(+), 107 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/Wrappers/SpriteBatchWrapper.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs deleted file mode 100644 index 1459ff17..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Crossplatform/SpriteBatch_MethodRewriter.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform -{ - /// Rewrites references to to fix inconsistent method signatures between MonoGame and XNA. - /// MonoGame has one SpriteBatch.Begin method with optional arguments, but XNA has multiple method overloads. Incompatible method references are rewritten to use , which redirects all method signatures to the proper compiled MonoGame/XNA method. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class SpriteBatch_MethodRewriter : BaseMethodRewriter - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"{nameof(SpriteBatch)} methods"; - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return platformChanged - && methodRef.DeclaringType.FullName == typeof(SpriteBatch).FullName - && this.HasMatchingSignature(typeof(SpriteBatch_MethodRewriter.WrapperMethods), methodRef); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) - { - methodRef.DeclaringType = module.Import(typeof(SpriteBatch_MethodRewriter.WrapperMethods)); - } - - - /********* - ** Wrapper methods - *********/ - /// Wraps methods that are incompatible when converting compiled code between MonoGame and XNA. - public class WrapperMethods : SpriteBatch - { - /********* - ** Public methods - *********/ - /// Construct an instance. - public WrapperMethods(GraphicsDevice graphicsDevice) : base(graphicsDevice) { } - - - /**** - ** MonoGame signatures - ****/ - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity); - } - - /**** - ** XNA signatures - ****/ - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin() - { - base.Begin(); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState) - { - base.Begin(sortMode, blendState); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect); - } - - [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] - public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) - { - base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix); - } - } - } -} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs new file mode 100644 index 00000000..92ba3af1 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs @@ -0,0 +1,74 @@ +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites method references from one parent type to another if the signatures match. + public class GenericMethodMapper : BaseMethodRewriter + { + /********* + ** Properties + *********/ + /// The type whose methods to remap. + private readonly Type FromType; + + /// The type with methods to map to. + private readonly Type ToType; + + /// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on. + private readonly bool OnlyIfPlatformChanged; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The type whose methods to remap. + /// The type with methods to map to. + /// A brief noun phrase indicating what the instruction finder matches. + /// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on. + public GenericMethodMapper(Type fromType, Type toType, string nounPhrase, bool onlyIfPlatformChanged = false) + { + this.FromType = fromType; + this.ToType = toType; + this.NounPhrase = nounPhrase; + this.OnlyIfPlatformChanged = onlyIfPlatformChanged; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return + (!this.OnlyIfPlatformChanged || platformChanged) + && methodRef.DeclaringType.FullName == this.FromType.FullName + && this.HasMatchingSignature(this.ToType, methodRef); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which calls the method. + /// The method reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) + { + methodRef.DeclaringType = module.Import(this.ToType); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Wrappers/SpriteBatchWrapper.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Wrappers/SpriteBatchWrapper.cs new file mode 100644 index 00000000..ee68f1d5 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/Wrappers/SpriteBatchWrapper.cs @@ -0,0 +1,59 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers +{ + /// Wraps methods that are incompatible when converting compiled code between MonoGame and XNA. + public class SpriteBatchWrapper : SpriteBatch + { + /********* + ** Public methods + *********/ + /// Construct an instance. + public SpriteBatchWrapper(GraphicsDevice graphicsDevice) : base(graphicsDevice) { } + + + /**** + ** MonoGame signatures + ****/ + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Linux/Mac.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix? matrix) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, matrix ?? Matrix.Identity); + } + + /**** + ** XNA signatures + ****/ + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin() + { + base.Begin(); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState) + { + base.Begin(sortMode, blendState); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect); + } + + [SuppressMessage("ReSharper", "CS0109", Justification = "The 'new' modifier applies when compiled on Windows.")] + public new void Begin(SpriteSortMode sortMode, BlendState blendState, SamplerState samplerState, DepthStencilState depthStencilState, RasterizerState rasterizerState, Effect effect, Matrix transformMatrix) + { + base.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix); + } + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index a3322e67..8c10aea7 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -80,10 +80,11 @@ + + - diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 263d3352..a7e28213 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.AssemblyRewriters.Finders; -using StardewModdingAPI.AssemblyRewriters.Rewriters.Crossplatform; +using StardewModdingAPI.AssemblyRewriters.Rewriters; using StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2; +using StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers; using StardewValley; namespace StardewModdingAPI @@ -172,7 +174,7 @@ namespace StardewModdingAPI return new IInstructionRewriter[] { // crossplatform - new SpriteBatch_MethodRewriter(), + new GenericMethodMapper(typeof(SpriteBatch), typeof(SpriteBatchWrapper), $"{nameof(SpriteBatch)} methods", onlyIfPlatformChanged: true), // Stardew Valley 1.2 new Game1_ActiveClickableMenu_FieldRewriter(), -- cgit From 2e58f853d27cc200be5b34a1ebef5bbdb7703a49 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 13:40:37 -0400 Subject: replace field-to-property rewriters with a generic rewriter --- .../Rewriters/GenericFieldToPropertyRewriter.cs | 70 ++++++++++++++++++++++ .../Rewriters/GenericMethodMapper.cs | 8 +-- .../Game1_ActiveClickableMenu_FieldRewriter.cs | 49 --------------- .../SDV1_2/Game1_GameMode_FieldRewriter.cs | 49 --------------- .../Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs | 49 --------------- .../StardewModdingAPI.AssemblyRewriters.csproj | 6 +- src/StardewModdingAPI/Constants.cs | 9 ++- 7 files changed, 80 insertions(+), 160 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs new file mode 100644 index 00000000..f58bcfbb --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs @@ -0,0 +1,70 @@ +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites field references into property references. + public class GenericFieldToPropertyRewriter : BaseFieldRewriter + { + /********* + ** Properties + *********/ + /// The type whose field to which references should be rewritten. + private readonly Type Type; + + /// The field name to rewrite. + private readonly string FieldName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The type whose field to which references should be rewritten. + /// The field name to rewrite. + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public GenericFieldToPropertyRewriter(Type type, string fieldName, string nounPhrase = null) + { + this.Type = type; + this.FieldName = fieldName; + this.NounPhrase = nounPhrase ?? $"{type.Name}.{fieldName} field"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + fieldRef.DeclaringType.FullName == this.Type.FullName + && fieldRef.Name == this.FieldName; + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; + MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs index 92ba3af1..49e0aad7 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs @@ -34,13 +34,13 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// Construct an instance. /// The type whose methods to remap. /// The type with methods to map to. - /// A brief noun phrase indicating what the instruction finder matches. /// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on. - public GenericMethodMapper(Type fromType, Type toType, string nounPhrase, bool onlyIfPlatformChanged = false) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public GenericMethodMapper(Type fromType, Type toType, bool onlyIfPlatformChanged = false, string nounPhrase = null) { this.FromType = fromType; this.ToType = toType; - this.NounPhrase = nounPhrase; + this.NounPhrase = nounPhrase ?? $"{fromType.Name} methods"; this.OnlyIfPlatformChanged = onlyIfPlatformChanged; } @@ -71,4 +71,4 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters methodRef.DeclaringType = module.Import(this.ToType); } } -} \ No newline at end of file +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs deleted file mode 100644 index 59a7c798..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_ActiveClickableMenu_FieldRewriter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 -{ - /// Rewrites field references to . - /// Stardew Valley changed the field to a property, which broke many mods that reference it. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class Game1_ActiveClickableMenu_FieldRewriter : BaseFieldRewriter - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"{nameof(Game1)}.{nameof(Game1.activeClickableMenu)} field"; - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == nameof(Game1.activeClickableMenu); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) - { - string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; - MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.activeClickableMenu)}")); - cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs deleted file mode 100644 index c3da6863..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_GameMode_FieldRewriter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 -{ - /// Rewrites field references to . - /// Stardew Valley changed the field to a property, which broke many mods that reference it. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class Game1_GameMode_FieldRewriter : BaseFieldRewriter - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"{nameof(Game1)}.{nameof(Game1.gameMode)} field"; - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == nameof(Game1.gameMode); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) - { - string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; - MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.gameMode)}")); - cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs deleted file mode 100644 index 91eae416..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/SDV1_2/Game1_Player_FieldRewriter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; -using StardewValley; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2 -{ - /// Rewrites field references to . - /// Stardew Valley changed the field to a property, which broke many mods that reference it. - [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "This class is not meant to be used directly, and is deliberately named to make it easier to know what it changes at a glance.")] - public class Game1_Player_FieldRewriter : BaseFieldRewriter - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } = $"{nameof(Game1)}.{nameof(Game1.player)} field"; - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) - && fieldRef.DeclaringType.FullName == typeof(Game1).FullName - && fieldRef.Name == nameof(Game1.player); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) - { - string methodPrefix = instruction.OpCode == OpCodes.Ldsfld ? "get" : "set"; - MethodReference propertyRef = module.Import(typeof(Game1).GetMethod($"{methodPrefix}_{nameof(Game1.player)}")); - cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 8c10aea7..8416aad7 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -80,11 +80,9 @@ - + - - - + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index a7e28213..1965cae3 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -7,7 +7,6 @@ using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.AssemblyRewriters.Finders; using StardewModdingAPI.AssemblyRewriters.Rewriters; -using StardewModdingAPI.AssemblyRewriters.Rewriters.SDV1_2; using StardewModdingAPI.AssemblyRewriters.Rewriters.Wrappers; using StardewValley; @@ -174,12 +173,12 @@ namespace StardewModdingAPI return new IInstructionRewriter[] { // crossplatform - new GenericMethodMapper(typeof(SpriteBatch), typeof(SpriteBatchWrapper), $"{nameof(SpriteBatch)} methods", onlyIfPlatformChanged: true), + new GenericMethodMapper(typeof(SpriteBatch), typeof(SpriteBatchWrapper), onlyIfPlatformChanged: true), // Stardew Valley 1.2 - new Game1_ActiveClickableMenu_FieldRewriter(), - new Game1_GameMode_FieldRewriter(), - new Game1_Player_FieldRewriter() + new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), + new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), + new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)) }; } -- cgit From 4d48bdfe7c3806ec1995cd499ca9382ace2d8a53 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 13:50:01 -0400 Subject: drop 'generic' prefix for rewriters since they're all generic now --- .../Finders/EventFinder.cs | 54 ++++++++++++++++ .../Finders/FieldFinder.cs | 61 ++++++++++++++++++ .../Finders/GenericEventFinder.cs | 54 ---------------- .../Finders/GenericFieldFinder.cs | 61 ------------------ .../Finders/GenericMethodFinder.cs | 54 ---------------- .../Finders/GenericTypeFinder.cs | 64 ------------------- .../Finders/MethodFinder.cs | 54 ++++++++++++++++ .../Finders/TypeFinder.cs | 64 +++++++++++++++++++ .../Rewriters/FieldToPropertyRewriter.cs | 70 ++++++++++++++++++++ .../Rewriters/GenericFieldToPropertyRewriter.cs | 70 -------------------- .../Rewriters/GenericMethodMapper.cs | 74 ---------------------- .../Rewriters/MethodParentRewriter.cs | 74 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 20 +++--- src/StardewModdingAPI/Constants.cs | 46 +++++++------- 14 files changed, 410 insertions(+), 410 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs new file mode 100644 index 00000000..359ca63e --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -0,0 +1,54 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given event. + public sealed class EventFinder : BaseMethodFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The event name for which to find references. + private readonly string EventName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The event name for which to find references. + public EventFinder(string fullTypeName, string eventName) + { + this.FullTypeName = fullTypeName; + this.EventName = eventName; + this.NounPhrase = $"obsolete {fullTypeName}.{eventName} event"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == this.FullTypeName + && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs new file mode 100644 index 00000000..516641f2 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -0,0 +1,61 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given field. + public sealed class FieldFinder : BaseFieldFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The field name for which to find references. + private readonly string FieldName; + + /// Whether the field to match is static. + private readonly bool IsStatic; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The field name for which to find references. + /// Whether the field to match is static. + public FieldFinder(string fullTypeName, string fieldName, bool isStatic) + { + this.FullTypeName = fullTypeName; + this.FieldName = fieldName; + this.IsStatic = isStatic; + this.NounPhrase = $"obsolete {fullTypeName}.{fieldName} field"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + this.IsStaticField(instruction) == this.IsStatic + && fieldRef.DeclaringType.FullName == this.FullTypeName + && fieldRef.Name == this.FieldName; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs deleted file mode 100644 index c2a981e5..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericEventFinder.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference a given event. - public sealed class GenericEventFinder : BaseMethodFinder - { - /********* - ** Properties - *********/ - /// The full type name for which to find references. - private readonly string FullTypeName; - - /// The event name for which to find references. - private readonly string EventName; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The full type name for which to find references. - /// The event name for which to find references. - public GenericEventFinder(string fullTypeName, string eventName) - { - this.FullTypeName = fullTypeName; - this.EventName = eventName; - this.NounPhrase = $"obsolete {fullTypeName}.{eventName} event"; - } - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return methodRef.DeclaringType.FullName == this.FullTypeName - && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs deleted file mode 100644 index 056422a4..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericFieldFinder.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference a given field. - public sealed class GenericFieldFinder : BaseFieldFinder - { - /********* - ** Properties - *********/ - /// The full type name for which to find references. - private readonly string FullTypeName; - - /// The field name for which to find references. - private readonly string FieldName; - - /// Whether the field to match is static. - private readonly bool IsStatic; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The full type name for which to find references. - /// The field name for which to find references. - /// Whether the field to match is static. - public GenericFieldFinder(string fullTypeName, string fieldName, bool isStatic) - { - this.FullTypeName = fullTypeName; - this.FieldName = fieldName; - this.IsStatic = isStatic; - this.NounPhrase = $"obsolete {fullTypeName}.{fieldName} field"; - } - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - this.IsStaticField(instruction) == this.IsStatic - && fieldRef.DeclaringType.FullName == this.FullTypeName - && fieldRef.Name == this.FieldName; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs deleted file mode 100644 index f5443558..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericMethodFinder.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference a given method. - public sealed class GenericMethodFinder : BaseMethodFinder - { - /********* - ** Properties - *********/ - /// The full type name for which to find references. - private readonly string FullTypeName; - - /// The method name for which to find references. - private readonly string MethodName; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The full type name for which to find references. - /// The method name for which to find references. - public GenericMethodFinder(string fullTypeName, string methodName) - { - this.FullTypeName = fullTypeName; - this.MethodName = methodName; - this.NounPhrase = $"obsolete {fullTypeName}.{methodName} method"; - } - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return methodRef.DeclaringType.FullName == this.FullTypeName - && methodRef.Name == this.MethodName; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs deleted file mode 100644 index 1556cc3c..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/GenericTypeFinder.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Linq; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Finders -{ - /// Finds CIL instructions that reference a given type. - public sealed class GenericTypeFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// The full type name for which to find references. - private readonly string FullTypeName; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The full type name to match. - public GenericTypeFinder(string fullTypeName) - { - this.FullTypeName = fullTypeName; - this.NounPhrase = $"obsolete {fullTypeName} type"; - } - - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) - { - string fullName = this.FullTypeName; - - // field reference - if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) - { - FieldReference field = (FieldReference)instruction.Operand; - return - field.DeclaringType.FullName == fullName // field on target class - || field.FieldType.FullName == fullName; // field value is target class - } - - // method reference - if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) - { - MethodReference method = (MethodReference)instruction.Operand; - return - method.DeclaringType.FullName == fullName // method on target class - || method.ReturnType.FullName == fullName // method returns target class - || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters - } - - return false; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs new file mode 100644 index 00000000..6c210d68 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -0,0 +1,54 @@ +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given method. + public sealed class MethodFinder : BaseMethodFinder + { + /********* + ** Properties + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + /// The method name for which to find references. + private readonly string MethodName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name for which to find references. + /// The method name for which to find references. + public MethodFinder(string fullTypeName, string methodName) + { + this.FullTypeName = fullTypeName; + this.MethodName = methodName; + this.NounPhrase = $"obsolete {fullTypeName}.{methodName} method"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return methodRef.DeclaringType.FullName == this.FullTypeName + && methodRef.Name == this.MethodName; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs new file mode 100644 index 00000000..ba8e7102 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs @@ -0,0 +1,64 @@ +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters.Finders +{ + /// Finds CIL instructions that reference a given type. + public sealed class TypeFinder : IInstructionFinder + { + /********* + ** Accessors + *********/ + /// The full type name for which to find references. + private readonly string FullTypeName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name to match. + public TypeFinder(string fullTypeName) + { + this.FullTypeName = fullTypeName; + this.NounPhrase = $"obsolete {fullTypeName} type"; + } + + /// Get whether a CIL instruction matches. + /// The IL instruction. + /// Whether the mod was compiled on a different platform. + public bool IsMatch(Instruction instruction, bool platformChanged) + { + string fullName = this.FullTypeName; + + // field reference + if (instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld) + { + FieldReference field = (FieldReference)instruction.Operand; + return + field.DeclaringType.FullName == fullName // field on target class + || field.FieldType.FullName == fullName; // field value is target class + } + + // method reference + if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt) + { + MethodReference method = (MethodReference)instruction.Operand; + return + method.DeclaringType.FullName == fullName // method on target class + || method.ReturnType.FullName == fullName // method returns target class + || method.Parameters.Any(p => p.ParameterType.FullName == fullName); // method parameters + } + + return false; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs new file mode 100644 index 00000000..caf0a16c --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -0,0 +1,70 @@ +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites field references into property references. + public class FieldToPropertyRewriter : BaseFieldRewriter + { + /********* + ** Properties + *********/ + /// The type whose field to which references should be rewritten. + private readonly Type Type; + + /// The field name to rewrite. + private readonly string FieldName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The type whose field to which references should be rewritten. + /// The field name to rewrite. + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public FieldToPropertyRewriter(Type type, string fieldName, string nounPhrase = null) + { + this.Type = type; + this.FieldName = fieldName; + this.NounPhrase = nounPhrase ?? $"{type.Name}.{fieldName} field"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + fieldRef.DeclaringType.FullName == this.Type.FullName + && fieldRef.Name == this.FieldName; + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; + MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); + cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs deleted file mode 100644 index f58bcfbb..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericFieldToPropertyRewriter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters -{ - /// Rewrites field references into property references. - public class GenericFieldToPropertyRewriter : BaseFieldRewriter - { - /********* - ** Properties - *********/ - /// The type whose field to which references should be rewritten. - private readonly Type Type; - - /// The field name to rewrite. - private readonly string FieldName; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The type whose field to which references should be rewritten. - /// The field name to rewrite. - /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). - public GenericFieldToPropertyRewriter(Type type, string fieldName, string nounPhrase = null) - { - this.Type = type; - this.FieldName = fieldName; - this.NounPhrase = nounPhrase ?? $"{type.Name}.{fieldName} field"; - } - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - fieldRef.DeclaringType.FullName == this.Type.FullName - && fieldRef.Name == this.FieldName; - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) - { - string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; - MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); - cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs deleted file mode 100644 index 49e0aad7..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/GenericMethodMapper.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using Mono.Cecil; -using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; - -namespace StardewModdingAPI.AssemblyRewriters.Rewriters -{ - /// Rewrites method references from one parent type to another if the signatures match. - public class GenericMethodMapper : BaseMethodRewriter - { - /********* - ** Properties - *********/ - /// The type whose methods to remap. - private readonly Type FromType; - - /// The type with methods to map to. - private readonly Type ToType; - - /// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on. - private readonly bool OnlyIfPlatformChanged; - - - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// The type whose methods to remap. - /// 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. - /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). - public GenericMethodMapper(Type fromType, Type toType, bool onlyIfPlatformChanged = false, string nounPhrase = null) - { - this.FromType = fromType; - this.ToType = toType; - this.NounPhrase = nounPhrase ?? $"{fromType.Name} methods"; - this.OnlyIfPlatformChanged = onlyIfPlatformChanged; - } - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) - { - return - (!this.OnlyIfPlatformChanged || platformChanged) - && methodRef.DeclaringType.FullName == this.FromType.FullName - && this.HasMatchingSignature(this.ToType, methodRef); - } - - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) - { - methodRef.DeclaringType = module.Import(this.ToType); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs new file mode 100644 index 00000000..9c19f473 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -0,0 +1,74 @@ +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites method references from one parent type to another if the signatures match. + public class MethodParentRewriter : BaseMethodRewriter + { + /********* + ** Properties + *********/ + /// The type whose methods to remap. + private readonly Type FromType; + + /// The type with methods to map to. + private readonly Type ToType; + + /// Whether to only rewrite references if loading the assembly on a different platform than it was compiled on. + private readonly bool OnlyIfPlatformChanged; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The type whose methods to remap. + /// 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. + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public MethodParentRewriter(Type fromType, Type toType, bool onlyIfPlatformChanged = false, string nounPhrase = null) + { + this.FromType = fromType; + this.ToType = toType; + this.NounPhrase = nounPhrase ?? $"{fromType.Name} methods"; + this.OnlyIfPlatformChanged = onlyIfPlatformChanged; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a method reference should be rewritten. + /// The IL instruction. + /// The method reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + { + return + (!this.OnlyIfPlatformChanged || platformChanged) + && methodRef.DeclaringType.FullName == this.FromType.FullName + && this.HasMatchingSignature(this.ToType, methodRef); + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which calls the method. + /// The method reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) + { + methodRef.DeclaringType = module.Import(this.ToType); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 8416aad7..90e800d0 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -66,22 +66,22 @@ Properties\GlobalAssemblyInfo.cs - - - + + + + + - - + + + - - - - - + + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 1965cae3..5fa25014 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -143,27 +143,27 @@ namespace StardewModdingAPI return new IInstructionFinder[] { // changes in Stardew Valley 1.2 (that don't have rewriters) - new GenericFieldFinder("StardewValley.Game1", "borderFont", isStatic: true), - new GenericFieldFinder("StardewValley.Game1", "smoothFont", isStatic: true), - new GenericFieldFinder("StardewValley.Item", "set_Name", isStatic: false), + new FieldFinder("StardewValley.Game1", "borderFont", isStatic: true), + new FieldFinder("StardewValley.Game1", "smoothFont", isStatic: true), + new FieldFinder("StardewValley.Item", "set_Name", isStatic: false), // APIs removed in SMAPI 1.9 - new GenericTypeFinder("StardewModdingAPI.Advanced.ConfigFile"), - new GenericTypeFinder("StardewModdingAPI.Advanced.IConfigFile"), - new GenericTypeFinder("StardewModdingAPI.Entities.SPlayer"), - new GenericTypeFinder("StardewModdingAPI.Extensions"), - new GenericTypeFinder("StardewModdingAPI.Inheritance.ItemStackChange"), - new GenericTypeFinder("StardewModdingAPI.Inheritance.SGame"), - new GenericTypeFinder("StardewModdingAPI.Inheritance.SObject"), - new GenericTypeFinder("StardewModdingAPI.LogWriter"), - new GenericTypeFinder("StardewModdingAPI.Manifest"), - new GenericTypeFinder("StardewModdingAPI.Version"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawDebug"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawTick"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderHudEventNoCheck"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderGuiEventNoCheck"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck"), - new GenericEventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck") + new TypeFinder("StardewModdingAPI.Advanced.ConfigFile"), + new TypeFinder("StardewModdingAPI.Advanced.IConfigFile"), + new TypeFinder("StardewModdingAPI.Entities.SPlayer"), + new TypeFinder("StardewModdingAPI.Extensions"), + new TypeFinder("StardewModdingAPI.Inheritance.ItemStackChange"), + new TypeFinder("StardewModdingAPI.Inheritance.SGame"), + new TypeFinder("StardewModdingAPI.Inheritance.SObject"), + new TypeFinder("StardewModdingAPI.LogWriter"), + new TypeFinder("StardewModdingAPI.Manifest"), + new TypeFinder("StardewModdingAPI.Version"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawDebug"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "DrawTick"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderHudEventNoCheck"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderGuiEventNoCheck"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck"), + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck") }; } @@ -173,12 +173,12 @@ namespace StardewModdingAPI return new IInstructionRewriter[] { // crossplatform - new GenericMethodMapper(typeof(SpriteBatch), typeof(SpriteBatchWrapper), onlyIfPlatformChanged: true), + new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchWrapper), onlyIfPlatformChanged: true), // Stardew Valley 1.2 - new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), - new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), - new GenericFieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)) + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)) }; } -- cgit From 267e2469da290cbdb93bd58cea0272de403bbdab Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 14:06:36 -0400 Subject: rewrite removed font references for compatibility --- .../Rewriters/FieldReplaceRewriter.cs | 78 ++++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + src/StardewModdingAPI/Constants.cs | 6 +- .../StardewModdingAPI.config.json | 16 ----- 4 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs new file mode 100644 index 00000000..31f9a40f --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -0,0 +1,78 @@ +using System; +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Framework; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites references to one field with another. + public class FieldReplaceRewriter : BaseFieldRewriter + { + /********* + ** Properties + *********/ + /// The type whose field to which references should be rewritten. + private readonly Type Type; + + /// The field name to rewrite. + private readonly string FromFieldName; + + /// The new field name to reference. + private readonly string ToFieldName; + + + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the instruction finder matches. + public override string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The type whose field to which references should be rewritten. + /// The field name to rewrite. + /// The new field name to reference. + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName, string nounPhrase = null) + { + this.Type = type; + this.FromFieldName = fromFieldName; + this.ToFieldName = toFieldName; + this.NounPhrase = nounPhrase ?? $"{type.Name}.{fromFieldName} field"; + } + + + /********* + ** Protected methods + *********/ + /// Get whether a field reference should be rewritten. + /// The IL instruction. + /// The field reference. + /// Whether the mod was compiled on a different platform. + protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + { + return + fieldRef.DeclaringType.FullName == this.Type.FullName + && fieldRef.Name == this.FromFieldName; + } + + /// Rewrite a method for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction which references the field. + /// The field reference invoked by the . + /// Metadata for mapping assemblies to the current platform. + protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + { + FieldInfo field = this.Type.GetField(this.ToFieldName); + if(field == null) + throw new InvalidOperationException($"The {this.Type.FullName} class doesn't have a {this.ToFieldName} field."); + FieldReference newRef = module.Import(field); + cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 90e800d0..0136c39b 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -80,6 +80,7 @@ + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 5fa25014..9a7fba84 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -143,8 +143,6 @@ namespace StardewModdingAPI return new IInstructionFinder[] { // changes in Stardew Valley 1.2 (that don't have rewriters) - new FieldFinder("StardewValley.Game1", "borderFont", isStatic: true), - new FieldFinder("StardewValley.Game1", "smoothFont", isStatic: true), new FieldFinder("StardewValley.Item", "set_Name", isStatic: false), // APIs removed in SMAPI 1.9 @@ -178,7 +176,9 @@ namespace StardewModdingAPI // Stardew Valley 1.2 new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.activeClickableMenu)), new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.gameMode)), - new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)) + new FieldToPropertyRewriter(typeof(Game1), nameof(Game1.player)), + new FieldReplaceRewriter(typeof(Game1), "borderFont", nameof(Game1.smallFont)), + new FieldReplaceRewriter(typeof(Game1), "smoothFont", nameof(Game1.smallFont)) }; } diff --git a/src/StardewModdingAPI/StardewModdingAPI.config.json b/src/StardewModdingAPI/StardewModdingAPI.config.json index 64b4fd53..da6d3730 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.config.json +++ b/src/StardewModdingAPI/StardewModdingAPI.config.json @@ -93,22 +93,6 @@ This file contains advanced configuration for SMAPI. You generally shouldn't cha "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/211", "Notes": "Crashes with 'Method not found: Void StardewValley.Item.set_Name(System.String)'." }, - { - "Name": "CJB Cheats Menu", - "ID": "CJBCheatsMenu", - "UpperVersion": "1.13", - "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/4", - "Notes": "Uses removed Game1.borderFont." - }, - { - "Name": "CJB Item Spawner", - "ID": "CJBItemSpawner", - "UpperVersion": "1.6", - "Compatibility": "AssumeBroken", - "UpdateUrl": "http://www.nexusmods.com/stardewvalley/mods/93", - "Notes": "Uses removed Game1.borderFont." - }, { "Name": "Cooking Skill", "ID": "CookingSkill", -- cgit From 7b641d816466fe7d9229374c175f59ee32b8dc5c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 25 Mar 2017 15:17:26 -0400 Subject: simplify CIL rewriter hierarchy --- .../Finders/EventFinder.cs | 15 ++-- .../Finders/FieldFinder.cs | 23 +++--- .../Finders/MethodFinder.cs | 15 ++-- .../Framework/BaseFieldFinder.cs | 46 ----------- .../Framework/BaseFieldRewriter.cs | 35 -------- .../Framework/BaseMethodFinder.cs | 74 ----------------- .../Framework/BaseMethodRewriter.cs | 35 -------- .../Framework/RewriteHelper.cs | 41 ---------- .../RewriteHelper.cs | 94 ++++++++++++++++++++++ .../Rewriters/FieldReplaceRewriter.cs | 37 ++------- .../Rewriters/FieldToPropertyRewriter.cs | 31 ++----- .../Rewriters/MethodParentRewriter.cs | 24 +++--- .../StardewModdingAPI.AssemblyRewriters.csproj | 6 +- src/StardewModdingAPI/Constants.cs | 2 +- 14 files changed, 146 insertions(+), 332 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index 359ca63e..9d0184c6 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given event. - public sealed class EventFinder : BaseMethodFinder + public sealed class EventFinder : IInstructionFinder { /********* ** Properties @@ -21,7 +20,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -41,13 +40,15 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { - return methodRef.DeclaringType.FullName == this.FullTypeName + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + return + methodRef != null + && methodRef.DeclaringType.FullName == this.FullTypeName && (methodRef.Name == "add_" + this.EventName || methodRef.Name == "remove_" + this.EventName); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs index 516641f2..068119b8 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given field. - public sealed class FieldFinder : BaseFieldFinder + public class FieldFinder : IInstructionFinder { /********* ** Properties @@ -16,15 +15,12 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// The field name for which to find references. private readonly string FieldName; - /// Whether the field to match is static. - private readonly bool IsStatic; - /********* ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -33,27 +29,26 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Construct an instance. /// The full type name for which to find references. /// The field name for which to find references. - /// Whether the field to match is static. - public FieldFinder(string fullTypeName, string fieldName, bool isStatic) + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public FieldFinder(string fullTypeName, string fieldName, string nounPhrase = null) { this.FullTypeName = fullTypeName; this.FieldName = fieldName; - this.IsStatic = isStatic; - this.NounPhrase = $"obsolete {fullTypeName}.{fieldName} field"; + this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The field reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { + FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); return - this.IsStaticField(instruction) == this.IsStatic + fieldRef != null && fieldRef.DeclaringType.FullName == this.FullTypeName && fieldRef.Name == this.FieldName; } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index 6c210d68..bea549ee 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -1,11 +1,10 @@ using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Finders { /// Finds CIL instructions that reference a given method. - public sealed class MethodFinder : BaseMethodFinder + public class MethodFinder : IInstructionFinder { /********* ** Properties @@ -21,7 +20,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -41,13 +40,15 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { - return methodRef.DeclaringType.FullName == this.FullTypeName + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + return + methodRef != null + && methodRef.DeclaringType.FullName == this.FullTypeName && methodRef.Name == this.MethodName; } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs deleted file mode 100644 index ac2facec..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldFinder.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a field finder. - public abstract class BaseFieldFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public abstract string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Ldfld && instruction.OpCode != OpCodes.Ldsfld && instruction.OpCode != OpCodes.Stfld && instruction.OpCode != OpCodes.Stsfld) - return false; // not a field reference - return this.IsMatch(instruction, (FieldReference)instruction.Operand, platformChanged); - } - - - /********* - ** Protected methods - *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged); - - /// Whether an instruction is a static field reference. - /// The IL instruction. - protected bool IsStaticField(Instruction instruction) - { - return instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stsfld; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs deleted file mode 100644 index b2c25587..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseFieldRewriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a field rewriter. - public abstract class BaseFieldRewriter : BaseFieldFinder, IInstructionRewriter - { - /********* - ** Public methods - *********/ - /// Rewrite a CIL instruction for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction to rewrite. - /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) - { - FieldReference fieldRef = (FieldReference)instruction.Operand; - this.Rewrite(module, cil, instruction, fieldRef, assemblyMap); - } - - - /********* - ** Protected methods - *********/ - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs deleted file mode 100644 index bb71a9d7..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodFinder.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a method finder. - public abstract class BaseMethodFinder : IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public abstract string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) - { - if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt) - return false; // not a method reference - return this.IsMatch(instruction, (MethodReference)instruction.Operand, platformChanged); - } - - - /********* - ** Protected methods - *********/ - /// Get whether a method reference should be rewritten. - /// The IL instruction. - /// The method reference. - /// Whether the mod was compiled on a different platform. - protected abstract bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged); - - /// Get whether a method definition matches the signature expected by a method reference. - /// The method definition. - /// The method reference. - protected bool HasMatchingSignature(MethodInfo definition, MethodReference reference) - { - // same name - if (definition.Name != reference.Name) - return false; - - // same arguments - ParameterInfo[] definitionParameters = definition.GetParameters(); - ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); - if (referenceParameters.Length != definitionParameters.Length) - return false; - for (int i = 0; i < referenceParameters.Length; i++) - { - if (!RewriteHelper.IsMatchingType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) - return false; - } - return true; - } - - /// Get whether a type has a method whose signature matches the one expected by a method reference. - /// The type to check. - /// The method reference. - protected bool HasMatchingSignature(Type type, MethodReference reference) - { - return type - .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) - .Any(method => this.HasMatchingSignature(method, reference)); - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs deleted file mode 100644 index 6af1a0e1..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/BaseMethodRewriter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Base class for a method rewriter. - public abstract class BaseMethodRewriter : BaseMethodFinder, IInstructionRewriter - { - /********* - ** Public methods - *********/ - /// Rewrite a CIL instruction for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction to rewrite. - /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) - { - MethodReference methodRef = (MethodReference)instruction.Operand; - this.Rewrite(module, cil, instruction, methodRef, assemblyMap); - } - - - /********* - ** Protected methods - *********/ - /// Rewrite a method for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . - /// Metadata for mapping assemblies to the current platform. - protected abstract void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs b/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs deleted file mode 100644 index 0307053f..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/Framework/RewriteHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Mono.Cecil; - -namespace StardewModdingAPI.AssemblyRewriters.Framework -{ - /// Provides helper methods for field rewriters. - internal static class RewriteHelper - { - /********* - ** Public methods - *********/ - /// Get whether a type matches a type reference. - /// The defined type. - /// The type reference. - public static bool IsMatchingType(Type type, TypeReference reference) - { - // same namespace & name - if (type.Namespace != reference.Namespace || type.Name != reference.Name) - return false; - - // same generic parameters - if (type.IsGenericType) - { - if (!reference.IsGenericInstance) - return false; - - Type[] defGenerics = type.GetGenericArguments(); - TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); - if (defGenerics.Length != refGenerics.Length) - return false; - for (int i = 0; i < defGenerics.Length; i++) - { - if (!RewriteHelper.IsMatchingType(defGenerics[i], refGenerics[i])) - return false; - } - } - - return true; - } - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs b/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs new file mode 100644 index 00000000..cfb330dd --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/RewriteHelper.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using System.Reflection; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.AssemblyRewriters +{ + /// Provides helper methods for field rewriters. + internal static class RewriteHelper + { + /********* + ** Public methods + *********/ + /// Get the field reference from an instruction if it matches. + /// The IL instruction. + public static FieldReference AsFieldReference(Instruction instruction) + { + return instruction.OpCode == OpCodes.Ldfld || instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Stfld || instruction.OpCode == OpCodes.Stsfld + ? (FieldReference)instruction.Operand + : null; + } + + /// Get the method reference from an instruction if it matches. + /// The IL instruction. + public static MethodReference AsMethodReference(Instruction instruction) + { + return instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt + ? (MethodReference)instruction.Operand + : null; + } + + /// Get whether a type matches a type reference. + /// The defined type. + /// The type reference. + public static bool IsSameType(Type type, TypeReference reference) + { + // same namespace & name + if (type.Namespace != reference.Namespace || type.Name != reference.Name) + return false; + + // same generic parameters + if (type.IsGenericType) + { + if (!reference.IsGenericInstance) + return false; + + Type[] defGenerics = type.GetGenericArguments(); + TypeReference[] refGenerics = ((GenericInstanceType)reference).GenericArguments.ToArray(); + if (defGenerics.Length != refGenerics.Length) + return false; + for (int i = 0; i < defGenerics.Length; i++) + { + if (!RewriteHelper.IsSameType(defGenerics[i], refGenerics[i])) + return false; + } + } + + return true; + } + + /// Get whether a method definition matches the signature expected by a method reference. + /// The method definition. + /// The method reference. + public static bool HasMatchingSignature(MethodInfo definition, MethodReference reference) + { + // same name + if (definition.Name != reference.Name) + return false; + + // same arguments + ParameterInfo[] definitionParameters = definition.GetParameters(); + ParameterDefinition[] referenceParameters = reference.Parameters.ToArray(); + if (referenceParameters.Length != definitionParameters.Length) + return false; + for (int i = 0; i < referenceParameters.Length; i++) + { + if (!RewriteHelper.IsSameType(definitionParameters[i].ParameterType, referenceParameters[i].ParameterType)) + return false; + } + return true; + } + + /// Get whether a type has a method whose signature matches the one expected by a method reference. + /// The type to check. + /// The method reference. + public static bool HasMatchingSignature(Type type, MethodReference reference) + { + return type + .GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public) + .Any(method => RewriteHelper.HasMatchingSignature(method, reference)); + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index 31f9a40f..a715e07b 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -2,12 +2,12 @@ using System; using System.Reflection; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites references to one field with another. - public class FieldReplaceRewriter : BaseFieldRewriter + public class FieldReplaceRewriter : FieldFinder, IInstructionRewriter { /********* ** Properties @@ -15,20 +15,10 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The type whose field to which references should be rewritten. private readonly Type Type; - /// The field name to rewrite. - private readonly string FromFieldName; - /// The new field name to reference. private readonly string ToFieldName; - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - /********* ** Public methods *********/ @@ -38,38 +28,25 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The new field name to reference. /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName, string nounPhrase = null) + : base(type.FullName, fromFieldName, nounPhrase) { this.Type = type; - this.FromFieldName = fromFieldName; this.ToFieldName = toFieldName; - this.NounPhrase = nounPhrase ?? $"{type.Name}.{fromFieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - fieldRef.DeclaringType.FullName == this.Type.FullName - && fieldRef.Name == this.FromFieldName; - } - - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { FieldInfo field = this.Type.GetField(this.ToFieldName); - if(field == null) + if (field == null) throw new InvalidOperationException($"The {this.Type.FullName} class doesn't have a {this.ToFieldName} field."); FieldReference newRef = module.Import(field); cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index caf0a16c..62e24559 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -1,12 +1,12 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; +using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites field references into property references. - public class FieldToPropertyRewriter : BaseFieldRewriter + public class FieldToPropertyRewriter : FieldFinder, IInstructionRewriter { /********* ** Properties @@ -18,13 +18,6 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters private readonly string FieldName; - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } - - /********* ** Public methods *********/ @@ -33,34 +26,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The field name to rewrite. /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). public FieldToPropertyRewriter(Type type, string fieldName, string nounPhrase = null) + : base(type.FullName, fieldName, nounPhrase) { this.Type = type; this.FieldName = fieldName; - this.NounPhrase = nounPhrase ?? $"{type.Name}.{fieldName} field"; } /********* ** Protected methods *********/ - /// Get whether a field reference should be rewritten. - /// The IL instruction. - /// The field reference. - /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, FieldReference fieldRef, bool platformChanged) - { - return - fieldRef.DeclaringType.FullName == this.Type.FullName - && fieldRef.Name == this.FieldName; - } - - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which references the field. - /// The field reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, FieldReference fieldRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 9c19f473..9b895056 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -1,12 +1,11 @@ using System; using Mono.Cecil; using Mono.Cecil.Cil; -using StardewModdingAPI.AssemblyRewriters.Framework; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites method references from one parent type to another if the signatures match. - public class MethodParentRewriter : BaseMethodRewriter + public class MethodParentRewriter : IInstructionRewriter { /********* ** Properties @@ -25,7 +24,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters ** Accessors *********/ /// A brief noun phrase indicating what the instruction finder matches. - public override string NounPhrase { get; } + public string NounPhrase { get; } /********* @@ -48,26 +47,27 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /********* ** Protected methods *********/ - /// Get whether a method reference should be rewritten. + /// Get whether a CIL instruction matches. /// The IL instruction. - /// The method reference. /// Whether the mod was compiled on a different platform. - protected override bool IsMatch(Instruction instruction, MethodReference methodRef, bool platformChanged) + public bool IsMatch(Instruction instruction, bool platformChanged) { + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return - (!this.OnlyIfPlatformChanged || platformChanged) + methodRef != null + && (platformChanged || !this.OnlyIfPlatformChanged) && methodRef.DeclaringType.FullName == this.FromType.FullName - && this.HasMatchingSignature(this.ToType, methodRef); + && RewriteHelper.HasMatchingSignature(this.ToType, methodRef); } - /// Rewrite a method for compatibility. + /// Rewrite a CIL instruction for compatibility. /// The module being rewritten. /// The CIL rewriter. - /// The instruction which calls the method. - /// The method reference invoked by the . + /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - protected override void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, MethodReference methodRef, PlatformAssemblyMap assemblyMap) + public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) { + MethodReference methodRef = (MethodReference)instruction.Operand; methodRef.DeclaringType = module.Import(this.ToType); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 0136c39b..09fd79ed 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -70,11 +70,7 @@ - - - - - + diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 9a7fba84..52be6c05 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -143,7 +143,7 @@ namespace StardewModdingAPI return new IInstructionFinder[] { // changes in Stardew Valley 1.2 (that don't have rewriters) - new FieldFinder("StardewValley.Item", "set_Name", isStatic: false), + new FieldFinder("StardewValley.Item", "set_Name"), // APIs removed in SMAPI 1.9 new TypeFinder("StardewModdingAPI.Advanced.ConfigFile"), -- cgit From 85ed48809032fdbb8461ce4c34acfbe06f68652b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 26 Mar 2017 19:01:35 -0400 Subject: merge CIL finders & rewriters into one interface (#254) --- .../Finders/EventFinder.cs | 22 ++++++++++++-- .../Finders/FieldFinder.cs | 22 ++++++++++++-- .../Finders/MethodFinder.cs | 22 ++++++++++++-- .../Finders/TypeFinder.cs | 26 ++++++++++++++-- .../IInstructionFinder.cs | 23 -------------- .../IInstructionRewriter.cs | 16 ++++++++-- .../IncompatibleInstructionException.cs | 35 ++++++++++++++++++++++ .../Rewriters/FieldReplaceRewriter.cs | 28 +++++++++-------- .../Rewriters/FieldToPropertyRewriter.cs | 11 +++++-- .../Rewriters/MethodParentRewriter.cs | 35 ++++++++++++++-------- .../StardewModdingAPI.AssemblyRewriters.csproj | 2 +- src/StardewModdingAPI/Constants.cs | 23 +++++++------- src/StardewModdingAPI/Framework/AssemblyLoader.cs | 33 ++++++++++---------- .../Framework/IncompatibleInstructionException.cs | 27 ----------------- src/StardewModdingAPI/Program.cs | 1 + src/StardewModdingAPI/StardewModdingAPI.csproj | 1 - 16 files changed, 203 insertions(+), 124 deletions(-) delete mode 100644 src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs create mode 100644 src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs delete mode 100644 src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs index 848e54ff..bcceee32 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/EventFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given event. - public sealed class EventFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given event and throws an . + public class EventFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{eventName} event"; } + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs index 068119b8..cdfc3bd5 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/FieldFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given field. - public class FieldFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given field and throws an . + public class FieldFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field"; } + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs index d174bacd..2efcbb0f 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/MethodFinder.cs @@ -3,8 +3,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given method. - public class MethodFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given method and throws an . + public class MethodFinder : IInstructionRewriter { /********* ** Properties @@ -37,6 +37,22 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{methodName} method"; } + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + /********* ** Protected methods @@ -44,7 +60,7 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return diff --git a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs index 8f492d5f..96cbb229 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Finders/TypeFinder.cs @@ -4,8 +4,8 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters.Finders { - /// Finds CIL instructions that reference a given type. - public class TypeFinder : IInstructionFinder + /// Finds incompatible CIL instructions that reference a given type and throws an . + public class TypeFinder : IInstructionRewriter { /********* ** Accessors @@ -33,10 +33,30 @@ namespace StardewModdingAPI.AssemblyRewriters.Finders this.NounPhrase = nounPhrase ?? $"{fullTypeName} type"; } + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + throw new IncompatibleInstructionException(this.NounPhrase); + } + + + /********* + ** Protected methods + *********/ /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { string fullName = this.FullTypeName; diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs deleted file mode 100644 index cc3006b9..00000000 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionFinder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Mono.Cecil.Cil; - -namespace StardewModdingAPI.AssemblyRewriters -{ - /// Finds CIL instructions considered incompatible. - public interface IInstructionFinder - { - /********* - ** Accessors - *********/ - /// A brief noun phrase indicating what the instruction finder matches. - string NounPhrase { get; } - - - /********* - ** Methods - *********/ - /// Get whether a CIL instruction matches. - /// The IL instruction. - /// Whether the mod was compiled on a different platform. - bool IsMatch(Instruction instruction, bool platformChanged); - } -} diff --git a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs index b230f227..3a7b1365 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/IInstructionRewriter.cs @@ -3,9 +3,16 @@ using Mono.Cecil.Cil; namespace StardewModdingAPI.AssemblyRewriters { - /// Rewrites a CIL instruction for compatibility. - public interface IInstructionRewriter : IInstructionFinder + /// Rewrites CIL instructions for compatibility. + public interface IInstructionRewriter { + /********* + ** Accessors + *********/ + /// A brief noun phrase indicating what the rewriter matches. + string NounPhrase { get; } + + /********* ** Methods *********/ @@ -14,6 +21,9 @@ namespace StardewModdingAPI.AssemblyRewriters /// The CIL rewriter. /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap); + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged); } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs b/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs new file mode 100644 index 00000000..f7e6bd8f --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/IncompatibleInstructionException.cs @@ -0,0 +1,35 @@ +using System; + +namespace StardewModdingAPI.AssemblyRewriters +{ + /// An exception raised when an incompatible instruction is found while loading a mod assembly. + public class IncompatibleInstructionException : Exception + { + /********* + ** Accessors + *********/ + /// A brief noun phrase which describes the incompatible instruction that was found. + public string NounPhrase { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A brief noun phrase which describes the incompatible instruction that was found. + public IncompatibleInstructionException(string nounPhrase) + : base($"Found an incompatible CIL instruction ({nounPhrase}).") + { + this.NounPhrase = nounPhrase; + } + + /// Construct an instance. + /// A brief noun phrase which describes the incompatible instruction that was found. + /// A message which describes the error. + public IncompatibleInstructionException(string nounPhrase, string message) + : base(message) + { + this.NounPhrase = nounPhrase; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs index ffd22e7c..95663c49 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldReplaceRewriter.cs @@ -7,16 +7,13 @@ using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites references to one field with another. - public class FieldReplaceRewriter : FieldFinder, IInstructionRewriter + public class FieldReplaceRewriter : FieldFinder { /********* ** Properties *********/ - /// The type whose field to which references should be rewritten. - private readonly Type Type; - - /// The new field name to reference. - private readonly string ToFieldName; + /// The new field to reference. + private readonly FieldInfo ToField; /********* @@ -30,8 +27,9 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters public FieldReplaceRewriter(Type type, string fromFieldName, string toFieldName, string nounPhrase = null) : base(type.FullName, fromFieldName, nounPhrase) { - this.Type = type; - this.ToFieldName = toFieldName; + this.ToField = type.GetField(toFieldName); + if (this.ToField == null) + throw new InvalidOperationException($"The {type.FullName} class doesn't have a {toFieldName} field."); } /// Rewrite a CIL instruction for compatibility. @@ -39,13 +37,17 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The CIL rewriter. /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { - FieldInfo field = this.Type.GetField(this.ToFieldName); - if (field == null) - throw new InvalidOperationException($"The {this.Type.FullName} class doesn't have a {this.ToFieldName} field."); - FieldReference newRef = module.Import(field); + if (!this.IsMatch(instruction, platformChanged)) + return false; + + FieldReference newRef = module.Import(this.ToField); cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); + return true; } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs index f2f99cc1..a25f3fef 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/FieldToPropertyRewriter.cs @@ -6,7 +6,7 @@ using StardewModdingAPI.AssemblyRewriters.Finders; namespace StardewModdingAPI.AssemblyRewriters.Rewriters { /// Rewrites field references into property references. - public class FieldToPropertyRewriter : FieldFinder, IInstructionRewriter + public class FieldToPropertyRewriter : FieldFinder { /********* ** Properties @@ -37,11 +37,18 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters /// The CIL rewriter. /// The instruction to rewrite. /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) { + if (!this.IsMatch(instruction, platformChanged)) + return false; + string methodPrefix = instruction.OpCode == OpCodes.Ldsfld || instruction.OpCode == OpCodes.Ldfld ? "get" : "set"; MethodReference propertyRef = module.Import(this.Type.GetMethod($"{methodPrefix}_{this.FieldName}")); cil.Replace(instruction, cil.Create(OpCodes.Call, propertyRef)); + return true; } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs index 24d4dff9..3ec8c704 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/MethodParentRewriter.cs @@ -43,10 +43,32 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters this.OnlyIfPlatformChanged = onlyIfPlatformChanged; } + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction, platformChanged)) + return false; + + MethodReference methodRef = (MethodReference)instruction.Operand; + methodRef.DeclaringType = module.Import(this.ToType); + return true; + } + + + /********* + ** Protected methods + *********/ /// Get whether a CIL instruction matches. /// The IL instruction. /// Whether the mod was compiled on a different platform. - public bool IsMatch(Instruction instruction, bool platformChanged) + protected bool IsMatch(Instruction instruction, bool platformChanged) { MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); return @@ -55,16 +77,5 @@ namespace StardewModdingAPI.AssemblyRewriters.Rewriters && methodRef.DeclaringType.FullName == this.FromType.FullName && RewriteHelper.HasMatchingSignature(this.ToType, methodRef); } - - /// Rewrite a CIL instruction for compatibility. - /// The module being rewritten. - /// The CIL rewriter. - /// The instruction to rewrite. - /// Metadata for mapping assemblies to the current platform. - public void Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap) - { - MethodReference methodRef = (MethodReference)instruction.Operand; - methodRef.DeclaringType = module.Import(this.ToType); - } } } diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 09fd79ed..3c3acde3 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -70,8 +70,8 @@ + - diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 52be6c05..de0eab57 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -137,12 +137,15 @@ namespace StardewModdingAPI return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies); } - /// Get finders which match incompatible CIL instructions in mod assemblies. - internal static IEnumerable GetIncompatibilityFinders() + /// Get rewriters which detect or fix incompatible CIL instructions in mod assemblies. + internal static IEnumerable GetRewriters() { - return new IInstructionFinder[] + return new IInstructionRewriter[] { - // changes in Stardew Valley 1.2 (that don't have rewriters) + /**** + ** Finders throw an exception when incompatible code is found. + ****/ + // changes in Stardew Valley 1.2 (with no rewriters) new FieldFinder("StardewValley.Item", "set_Name"), // APIs removed in SMAPI 1.9 @@ -161,15 +164,11 @@ namespace StardewModdingAPI new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderHudEventNoCheck"), new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPostRenderGuiEventNoCheck"), new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderHudEventNoCheck"), - new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck") - }; - } + new EventFinder("StardewModdingAPI.Events.GraphicsEvents", "OnPreRenderGuiEventNoCheck"), - /// Get rewriters which fix incompatible CIL instructions in mod assemblies. - internal static IEnumerable GetRewriters() - { - return new IInstructionRewriter[] - { + /**** + ** Rewriters change CIL as needed to fix incompatible code + ****/ // crossplatform new MethodParentRewriter(typeof(SpriteBatch), typeof(SpriteBatchWrapper), onlyIfPlatformChanged: true), diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index aee0bbb3..5d00c525 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -193,33 +193,30 @@ namespace StardewModdingAPI.Framework this.ChangeTypeScope(type); } - // find incompatible instructions + // find (and optionally rewrite) incompatible instructions bool anyRewritten = false; - IInstructionFinder[] finders = Constants.GetIncompatibilityFinders().ToArray(); IInstructionRewriter[] rewriters = Constants.GetRewriters().ToArray(); foreach (MethodDefinition method in this.GetMethods(module)) { ILProcessor cil = method.Body.GetILProcessor(); foreach (Instruction instruction in cil.Body.Instructions.ToArray()) { - // throw exception if instruction is incompatible but can't be rewritten - IInstructionFinder finder = finders.FirstOrDefault(p => p.IsMatch(instruction, platformChanged)); - if (finder != null) - { - if (!assumeCompatible) - throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}."); - this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); - } - - // rewrite instruction if needed foreach (IInstructionRewriter rewriter in rewriters) { - if (!rewriter.IsMatch(instruction, platformChanged)) - continue; - - this.LogOnce(this.Monitor, loggedMessages, $"Rewriting {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); - rewriter.Rewrite(module, cil, instruction, this.AssemblyMap); - anyRewritten = true; + try + { + if (rewriter.Rewrite(module, cil, instruction, this.AssemblyMap, platformChanged)) + { + this.LogOnce(this.Monitor, loggedMessages, $"Rewrote {assembly.Name.Name} to fix {rewriter.NounPhrase}..."); + anyRewritten = true; + } + } + catch (IncompatibleInstructionException) + { + if (!assumeCompatible) + throw new IncompatibleInstructionException(rewriter.NounPhrase, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}."); + this.LogOnce(this.Monitor, loggedMessages, $"Found an incompatible CIL instruction ({rewriter.NounPhrase}) while loading assembly {assembly.Name.Name}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn); + } } } } diff --git a/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs b/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs deleted file mode 100644 index affe2cb3..00000000 --- a/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace StardewModdingAPI.Framework -{ - /// An exception raised when an incompatible instruction is found while loading a mod assembly. - internal class IncompatibleInstructionException : Exception - { - /********* - ** Accessors - *********/ - /// A brief noun phrase which describes the incompatible instruction that was found. - public string NounPhrase { get; } - - - /********* - ** Public methods - *********/ - /// Construct an instance. - /// A brief noun phrase which describes the incompatible instruction that was found. - /// A message which describes the error. - public IncompatibleInstructionException(string nounPhrase, string message) - : base(message) - { - this.NounPhrase = nounPhrase; - } - } -} diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs index ac646b1f..25605148 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -12,6 +12,7 @@ using System.Windows.Forms; #endif using Microsoft.Xna.Framework.Graphics; using Newtonsoft.Json; +using StardewModdingAPI.AssemblyRewriters; using StardewModdingAPI.Events; using StardewModdingAPI.Framework; using StardewModdingAPI.Framework.Logging; diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index 091b3d90..bcd0c390 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -150,7 +150,6 @@ - -- cgit From 5c253b7bae274c8509c90f8828f3f9f81653f250 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 26 Mar 2017 20:08:26 -0400 Subject: add type reference rewriter (#254) --- .../Rewriters/TypeReferenceRewriter.cs | 157 +++++++++++++++++++++ .../StardewModdingAPI.AssemblyRewriters.csproj | 1 + 2 files changed, 158 insertions(+) create mode 100644 src/StardewModdingAPI.AssemblyRewriters/Rewriters/TypeReferenceRewriter.cs (limited to 'src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj') diff --git a/src/StardewModdingAPI.AssemblyRewriters/Rewriters/TypeReferenceRewriter.cs b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/TypeReferenceRewriter.cs new file mode 100644 index 00000000..da6d9bc9 --- /dev/null +++ b/src/StardewModdingAPI.AssemblyRewriters/Rewriters/TypeReferenceRewriter.cs @@ -0,0 +1,157 @@ +using System; +using Mono.Cecil; +using Mono.Cecil.Cil; +using StardewModdingAPI.AssemblyRewriters.Finders; + +namespace StardewModdingAPI.AssemblyRewriters.Rewriters +{ + /// Rewrites all references to a type. + public class TypeReferenceRewriter : TypeFinder + { + /********* + ** Properties + *********/ + /// The full type name to which to find references. + private readonly string FromTypeName; + + /// The new type to reference. + private readonly Type ToType; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The full type name to which to find references. + /// The new type to reference. + /// A brief noun phrase indicating what the instruction finder matches (or null to generate one). + public TypeReferenceRewriter(string fromTypeFullName, Type toType, string nounPhrase = null) + : base(fromTypeFullName, nounPhrase) + { + this.FromTypeName = fromTypeFullName; + this.ToType = toType; + } + + /// Rewrite a method definition for compatibility. + /// The module being rewritten. + /// The method definition to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + bool rewritten = false; + + // return type + if (this.IsMatch(method.ReturnType)) + { + method.ReturnType = this.RewriteIfNeeded(module, method.ReturnType); + rewritten = true; + } + + // parameters + foreach (ParameterDefinition parameter in method.Parameters) + { + if (this.IsMatch(parameter.ParameterType)) + { + parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType); + rewritten = true; + } + } + + // generic parameters + for (int i = 0; i < method.GenericParameters.Count; i++) + { + var parameter = method.GenericParameters[i]; + if (this.IsMatch(parameter)) + { + TypeReference newType = this.RewriteIfNeeded(module, parameter); + if (newType != parameter) + method.GenericParameters[i] = new GenericParameter(parameter.Name, newType); + rewritten = true; + } + } + + // local variables + foreach (VariableDefinition variable in method.Body.Variables) + { + if (this.IsMatch(variable.VariableType)) + { + variable.VariableType = this.RewriteIfNeeded(module, variable.VariableType); + rewritten = true; + } + } + + return rewritten; + } + + /// Rewrite a CIL instruction for compatibility. + /// The module being rewritten. + /// The CIL rewriter. + /// The instruction to rewrite. + /// Metadata for mapping assemblies to the current platform. + /// Whether the mod was compiled on a different platform. + /// Returns whether the instruction was rewritten. + /// The CIL instruction is not compatible, and can't be rewritten. + public override bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged) + { + if (!this.IsMatch(instruction) && !instruction.ToString().Contains(this.FromTypeName)) + return false; + + // field reference + FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction); + if (fieldRef != null) + { + fieldRef.DeclaringType = this.RewriteIfNeeded(module, fieldRef.DeclaringType); + fieldRef.FieldType = this.RewriteIfNeeded(module, fieldRef.FieldType); + } + + // method reference + MethodReference methodRef = RewriteHelper.AsMethodReference(instruction); + if (methodRef != null) + { + methodRef.DeclaringType = this.RewriteIfNeeded(module, methodRef.DeclaringType); + methodRef.ReturnType = this.RewriteIfNeeded(module, methodRef.ReturnType); + foreach (var parameter in methodRef.Parameters) + parameter.ParameterType = this.RewriteIfNeeded(module, parameter.ParameterType); + } + + // type reference + if (instruction.Operand is TypeReference typeRef) + { + TypeReference newRef = this.RewriteIfNeeded(module, typeRef); + if (typeRef != newRef) + cil.Replace(instruction, cil.Create(instruction.OpCode, newRef)); + } + + return true; + } + + /********* + ** Private methods + *********/ + /// Get the adjusted type reference if it matches, else the same value. + /// The module being rewritten. + /// The type to replace if it matches. + private TypeReference RewriteIfNeeded(ModuleDefinition module, TypeReference type) + { + // root type + if (type.FullName == this.FromTypeName) + return module.Import(this.ToType); + + // generic arguments + if (type is GenericInstanceType genericType) + { + for (int i = 0; i < genericType.GenericArguments.Count; i++) + genericType.GenericArguments[i] = this.RewriteIfNeeded(module, genericType.GenericArguments[i]); + } + + // generic parameters (e.g. constraints) + for (int i = 0; i < type.GenericParameters.Count; i++) + type.GenericParameters[i] = new GenericParameter(this.RewriteIfNeeded(module, type.GenericParameters[i])); + + return type; + } + } +} diff --git a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj index 3c3acde3..775de9f2 100644 --- a/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj +++ b/src/StardewModdingAPI.AssemblyRewriters/StardewModdingAPI.AssemblyRewriters.csproj @@ -76,6 +76,7 @@ + -- cgit