summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'src/StardewModdingAPI/Framework')
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs4
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs83
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/FieldFinder.cs83
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/MethodFinder.cs83
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/PropertyFinder.cs83
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/TypeFinder.cs135
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/IInstructionRewriter.cs38
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/IncompatibleInstructionException.cs35
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs4
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs4
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Platform.cs12
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/PlatformAssemblyMap.cs55
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/RewriteHelper.cs94
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/FieldReplaceRewriter.cs53
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs54
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs93
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs157
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/Wrappers/SpriteBatchWrapper.cs59
-rw-r--r--src/StardewModdingAPI/Framework/SContentManager.cs2
19 files changed, 1123 insertions, 8 deletions
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
index 9c642bef..27a81038 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
@@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
-using StardewModdingAPI.AssemblyRewriters;
using StardewModdingAPI.Framework.Exceptions;
namespace StardewModdingAPI.Framework.ModLoading
@@ -270,8 +269,7 @@ namespace StardewModdingAPI.Framework.ModLoading
return;
// get assembly
- Assembly assembly;
- if (!this.TypeAssemblies.TryGetValue(type.FullName, out assembly))
+ if (!this.TypeAssemblies.TryGetValue(type.FullName, out Assembly assembly))
return;
// replace scope
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs
new file mode 100644
index 00000000..ce234e39
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs
@@ -0,0 +1,83 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference a given event and throws an <see cref="IncompatibleInstructionException"/>.</summary>
+ internal class EventFinder : IInstructionRewriter
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The full type name for which to find references.</summary>
+ private readonly string FullTypeName;
+
+ /// <summary>The event name for which to find references.</summary>
+ private readonly string EventName;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="fullTypeName">The full type name for which to find references.</param>
+ /// <param name="eventName">The event name for which to find references.</param>
+ /// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
+ public EventFinder(string fullTypeName, string eventName, string nounPhrase = null)
+ {
+ this.FullTypeName = fullTypeName;
+ this.EventName = eventName;
+ this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{eventName} event";
+ }
+
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ return false;
+ }
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(instruction))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="instruction">The IL instruction.</param>
+ protected bool IsMatch(Instruction instruction)
+ {
+ 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/Framework/ModLoading/Finders/FieldFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/FieldFinder.cs
new file mode 100644
index 00000000..2feaf2e6
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Finders/FieldFinder.cs
@@ -0,0 +1,83 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference a given field and throws an <see cref="IncompatibleInstructionException"/>.</summary>
+ internal class FieldFinder : IInstructionRewriter
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The full type name for which to find references.</summary>
+ private readonly string FullTypeName;
+
+ /// <summary>The field name for which to find references.</summary>
+ private readonly string FieldName;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="fullTypeName">The full type name for which to find references.</param>
+ /// <param name="fieldName">The field name for which to find references.</param>
+ /// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
+ public FieldFinder(string fullTypeName, string fieldName, string nounPhrase = null)
+ {
+ this.FullTypeName = fullTypeName;
+ this.FieldName = fieldName;
+ this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{fieldName} field";
+ }
+
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ return false;
+ }
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(instruction))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="instruction">The IL instruction.</param>
+ protected bool IsMatch(Instruction instruction)
+ {
+ FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ return
+ fieldRef != null
+ && fieldRef.DeclaringType.FullName == this.FullTypeName
+ && fieldRef.Name == this.FieldName;
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Finders/MethodFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/MethodFinder.cs
new file mode 100644
index 00000000..c3bb36e3
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Finders/MethodFinder.cs
@@ -0,0 +1,83 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference a given method and throws an <see cref="IncompatibleInstructionException"/>.</summary>
+ internal class MethodFinder : IInstructionRewriter
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The full type name for which to find references.</summary>
+ private readonly string FullTypeName;
+
+ /// <summary>The method name for which to find references.</summary>
+ private readonly string MethodName;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="fullTypeName">The full type name for which to find references.</param>
+ /// <param name="methodName">The method name for which to find references.</param>
+ /// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
+ public MethodFinder(string fullTypeName, string methodName, string nounPhrase = null)
+ {
+ this.FullTypeName = fullTypeName;
+ this.MethodName = methodName;
+ this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{methodName} method";
+ }
+
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ return false;
+ }
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(instruction))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="instruction">The IL instruction.</param>
+ protected bool IsMatch(Instruction instruction)
+ {
+ MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ return
+ methodRef != null
+ && methodRef.DeclaringType.FullName == this.FullTypeName
+ && methodRef.Name == this.MethodName;
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Finders/PropertyFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/PropertyFinder.cs
new file mode 100644
index 00000000..d1fed84b
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Finders/PropertyFinder.cs
@@ -0,0 +1,83 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference a given property and throws an <see cref="IncompatibleInstructionException"/>.</summary>
+ internal class PropertyFinder : IInstructionRewriter
+ {
+ /*********
+ ** Properties
+ *********/
+ /// <summary>The full type name for which to find references.</summary>
+ private readonly string FullTypeName;
+
+ /// <summary>The property name for which to find references.</summary>
+ private readonly string PropertyName;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="fullTypeName">The full type name for which to find references.</param>
+ /// <param name="propertyName">The property name for which to find references.</param>
+ /// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
+ public PropertyFinder(string fullTypeName, string propertyName, string nounPhrase = null)
+ {
+ this.FullTypeName = fullTypeName;
+ this.PropertyName = propertyName;
+ this.NounPhrase = nounPhrase ?? $"{fullTypeName}.{propertyName} property";
+ }
+
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ return false;
+ }
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(instruction))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="instruction">The IL instruction.</param>
+ protected bool IsMatch(Instruction instruction)
+ {
+ MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ return
+ methodRef != null
+ && methodRef.DeclaringType.FullName == this.FullTypeName
+ && (methodRef.Name == "get_" + this.PropertyName || methodRef.Name == "set_" + this.PropertyName);
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Finders/TypeFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/TypeFinder.cs
new file mode 100644
index 00000000..e67e6766
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Finders/TypeFinder.cs
@@ -0,0 +1,135 @@
+using System.Linq;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading.Finders
+{
+ /// <summary>Finds incompatible CIL instructions that reference a given type and throws an <see cref="IncompatibleInstructionException"/>.</summary>
+ internal class TypeFinder : IInstructionRewriter
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The full type name for which to find references.</summary>
+ private readonly string FullTypeName;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the instruction finder matches.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="fullTypeName">The full type name to match.</param>
+ /// <param name="nounPhrase">A brief noun phrase indicating what the instruction finder matches (or <c>null</c> to generate one).</param>
+ public TypeFinder(string fullTypeName, string nounPhrase = null)
+ {
+ this.FullTypeName = fullTypeName;
+ this.NounPhrase = nounPhrase ?? $"{fullTypeName} type";
+ }
+
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(method))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ public virtual bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
+ {
+ if (!this.IsMatch(instruction))
+ return false;
+
+ throw new IncompatibleInstructionException(this.NounPhrase);
+ }
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="method">The method deifnition.</param>
+ protected bool IsMatch(MethodDefinition method)
+ {
+ if (this.IsMatch(method.ReturnType))
+ return true;
+
+ foreach (VariableDefinition variable in method.Body.Variables)
+ {
+ if (this.IsMatch(variable.VariableType))
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>Get whether a CIL instruction matches.</summary>
+ /// <param name="instruction">The IL instruction.</param>
+ protected bool IsMatch(Instruction instruction)
+ {
+ // field reference
+ FieldReference fieldRef = RewriteHelper.AsFieldReference(instruction);
+ if (fieldRef != null)
+ {
+ return
+ this.IsMatch(fieldRef.DeclaringType) // field on target class
+ || this.IsMatch(fieldRef.FieldType); // field value is target class
+ }
+
+ // method reference
+ MethodReference methodRef = RewriteHelper.AsMethodReference(instruction);
+ if (methodRef != null)
+ {
+ return
+ this.IsMatch(methodRef.DeclaringType) // method on target class
+ || this.IsMatch(methodRef.ReturnType) // method returns target class
+ || methodRef.Parameters.Any(p => this.IsMatch(p.ParameterType)); // method parameters
+ }
+
+ return false;
+ }
+
+ /// <summary>Get whether a type reference matches the expected type.</summary>
+ /// <param name="type">The type to check.</param>
+ protected bool IsMatch(TypeReference type)
+ {
+ // root type
+ if (type.FullName == this.FullTypeName)
+ return true;
+
+ // generic arguments
+ if (type is GenericInstanceType genericType)
+ {
+ if (genericType.GenericArguments.Any(this.IsMatch))
+ return true;
+ }
+
+ // generic parameters (e.g. constraints)
+ if (type.GenericParameters.Any(this.IsMatch))
+ return true;
+
+ return false;
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/IInstructionRewriter.cs b/src/StardewModdingAPI/Framework/ModLoading/IInstructionRewriter.cs
new file mode 100644
index 00000000..9b35cdae
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/IInstructionRewriter.cs
@@ -0,0 +1,38 @@
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>Rewrites CIL instructions for compatibility.</summary>
+ internal interface IInstructionRewriter
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase indicating what the rewriter matches.</summary>
+ string NounPhrase { get; }
+
+
+ /*********
+ ** Methods
+ *********/
+ /// <summary>Rewrite a method definition for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="method">The method definition to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ bool Rewrite(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged);
+
+ /// <summary>Rewrite a CIL instruction for compatibility.</summary>
+ /// <param name="module">The module being rewritten.</param>
+ /// <param name="cil">The CIL rewriter.</param>
+ /// <param name="instruction">The instruction to rewrite.</param>
+ /// <param name="assemblyMap">Metadata for mapping assemblies to the current platform.</param>
+ /// <param name="platformChanged">Whether the mod was compiled on a different platform.</param>
+ /// <returns>Returns whether the instruction was rewritten.</returns>
+ /// <exception cref="IncompatibleInstructionException">The CIL instruction is not compatible, and can't be rewritten.</exception>
+ bool Rewrite(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged);
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/IncompatibleInstructionException.cs b/src/StardewModdingAPI/Framework/ModLoading/IncompatibleInstructionException.cs
new file mode 100644
index 00000000..17ec24b1
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/IncompatibleInstructionException.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>An exception raised when an incompatible instruction is found while loading a mod assembly.</summary>
+ internal class IncompatibleInstructionException : Exception
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A brief noun phrase which describes the incompatible instruction that was found.</summary>
+ public string NounPhrase { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="nounPhrase">A brief noun phrase which describes the incompatible instruction that was found.</param>
+ public IncompatibleInstructionException(string nounPhrase)
+ : base($"Found an incompatible CIL instruction ({nounPhrase}).")
+ {
+ this.NounPhrase = nounPhrase;
+ }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="nounPhrase">A brief noun phrase which describes the incompatible instruction that was found.</param>
+ /// <param name="message">A message which describes the error.</param>
+ public IncompatibleInstructionException(string nounPhrase, string message)
+ : base(message)
+ {
+ this.NounPhrase = nounPhrase;
+ }
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs b/src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs
index ab11272a..075e237a 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs
@@ -1,9 +1,9 @@
-using System;
+using System;
namespace StardewModdingAPI.Framework.ModLoading
{
/// <summary>An exception which indicates that something went seriously wrong while loading mods, and SMAPI should abort outright.</summary>
- public class InvalidModStateException : Exception
+ internal class InvalidModStateException : Exception
{
/// <summary>Construct an instance.</summary>
/// <param name="message">The error message.</param>
diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs b/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs
index 1b2b0b55..ab65f7b4 100644
--- a/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs
+++ b/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs
@@ -1,4 +1,4 @@
-namespace StardewModdingAPI.Framework.ModLoading
+namespace StardewModdingAPI.Framework.ModLoading
{
/// <summary>Indicates the status of a mod's metadata resolution.</summary>
internal enum ModMetadataStatus
@@ -9,4 +9,4 @@
/// <summary>The mod cannot be loaded.</summary>
Failed
}
-} \ No newline at end of file
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Platform.cs b/src/StardewModdingAPI/Framework/ModLoading/Platform.cs
new file mode 100644
index 00000000..45e881c4
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/Platform.cs
@@ -0,0 +1,12 @@
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>The game's platform version.</summary>
+ internal enum Platform
+ {
+ /// <summary>The Linux/Mac version of the game.</summary>
+ Mono,
+
+ /// <summary>The Windows version of the game.</summary>
+ Windows
+ }
+}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/PlatformAssemblyMap.cs b/src/StardewModdingAPI/Framework/ModLoading/PlatformAssemblyMap.cs
new file mode 100644
index 00000000..463f45e8
--- /dev/null
+++ b/src/StardewModdingAPI/Framework/ModLoading/PlatformAssemblyMap.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Mono.Cecil;
+
+namespace StardewModdingAPI.Framework.ModLoading
+{
+ /// <summary>Metadata for mapping assemblies to the current <see cref="Platform"/>.</summary>
+ internal class PlatformAssemblyMap
+ {
+ /*********
+ ** Accessors
+ *********/
+ /****
+ ** Data
+ ****/
+ /// <summary>The target game platform.</summary>
+ public readonly Platform TargetPlatform;
+
+ /// <summary>The short assembly names to remove as assembly reference, and replace with the <see cref="Targets"/>. These should be short names (like "Stardew Valley").</summary>
+ public readonly string[] RemoveNames;
+
+ /****
+ ** Metadata
+ ****/
+ /// <summary>The assemblies to target. Equivalent types should be rewritten to use these assemblies.</summary>
+ public readonly Assembly[] Targets;
+
+ /// <summary>An assembly => reference cache.</summary>
+ public readonly IDictionary<Assembly, AssemblyNameReference> TargetReferences;
+
+ /// <summary>An assembly => module cache.</summary>
+ public readonly IDictionary<Assembly, ModuleDefinition> TargetModules;
+