summaryrefslogtreecommitdiff
path: root/src/StardewModdingAPI/Framework/ModLoading
diff options
context:
space:
mode:
Diffstat (limited to 'src/StardewModdingAPI/Framework/ModLoading')
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs61
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs15
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs333
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs36
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs82
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/FieldFinder.cs82
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/MethodFinder.cs82
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/PropertyFinder.cs82
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Finders/TypeFinder.cs133
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/IInstructionHandler.cs34
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/IncompatibleInstructionException.cs35
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/InstructionHandleResult.cs24
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/InvalidModStateException.cs14
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModDependencyStatus.cs18
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModMetadata.cs68
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs12
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs366
-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.cs50
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/FieldToPropertyRewriter.cs51
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/MethodParentRewriter.cs88
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/TypeReferenceRewriter.cs154
-rw-r--r--src/StardewModdingAPI/Framework/ModLoading/Rewriters/VirtualEntryCallRemover.cs90
25 files changed, 0 insertions, 2071 deletions
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
deleted file mode 100644
index 4378798c..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyDefinitionResolver.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System.Collections.Generic;
-using Mono.Cecil;
-
-namespace StardewModdingAPI.Framework.ModLoading
-{
- /// <summary>A minimal assembly definition resolver which resolves references to known assemblies.</summary>
- internal class AssemblyDefinitionResolver : DefaultAssemblyResolver
- {
- /*********
- ** Properties
- *********/
- /// <summary>The known assemblies.</summary>
- private readonly IDictionary<string, AssemblyDefinition> Loaded = new Dictionary<string, AssemblyDefinition>();
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Add known assemblies to the resolver.</summary>
- /// <param name="assemblies">The known assemblies.</param>
- public void Add(params AssemblyDefinition[] assemblies)
- {
- foreach (AssemblyDefinition assembly in assemblies)
- {
- this.Loaded[assembly.Name.Name] = assembly;
- this.Loaded[assembly.Name.FullName] = assembly;
- }
- }
-
- /// <summary>Resolve an assembly reference.</summary>
- /// <param name="name">The assembly name.</param>
- public override AssemblyDefinition Resolve(AssemblyNameReference name) => this.ResolveName(name.Name) ?? base.Resolve(name);
-
- /// <summary>Resolve an assembly reference.</summary>
- /// <param name="name">The assembly name.</param>
- /// <param name="parameters">The assembly reader parameters.</param>
- public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) => this.ResolveName(name.Name) ?? base.Resolve(name, parameters);
-
- /// <summary>Resolve an assembly reference.</summary>
- /// <param name="fullName">The assembly full name (including version, etc).</param>
- public override AssemblyDefinition Resolve(string fullName) => this.ResolveName(fullName) ?? base.Resolve(fullName);
-
- /// <summary>Resolve an assembly reference.</summary>
- /// <param name="fullName">The assembly full name (including version, etc).</param>
- /// <param name="parameters">The assembly reader parameters.</param>
- public override AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) => this.ResolveName(fullName) ?? base.Resolve(fullName, parameters);
-
-
- /*********
- ** Private methods
- *********/
- /// <summary>Resolve a known assembly definition based on its short or full name.</summary>
- /// <param name="name">The assembly's short or full name.</param>
- private AssemblyDefinition ResolveName(string name)
- {
- return this.Loaded.ContainsKey(name)
- ? this.Loaded[name]
- : null;
- }
- }
-}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs
deleted file mode 100644
index 11be19fc..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoadStatus.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace StardewModdingAPI.Framework.ModLoading
-{
- /// <summary>Indicates the result of an assembly load.</summary>
- internal enum AssemblyLoadStatus
- {
- /// <summary>The assembly was loaded successfully.</summary>
- Okay = 1,
-
- /// <summary>The assembly could not be loaded.</summary>
- Failed = 2,
-
- /// <summary>The assembly is already loaded.</summary>
- AlreadyLoaded = 3
- }
-}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
deleted file mode 100644
index 1e3c4a05..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyLoader.cs
+++ /dev/null
@@ -1,333 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using Mono.Cecil;
-using Mono.Cecil.Cil;
-using StardewModdingAPI.Framework.Exceptions;
-using StardewModdingAPI.Metadata;
-
-namespace StardewModdingAPI.Framework.ModLoading
-{
- /// <summary>Preprocesses and loads mod assemblies.</summary>
- internal class AssemblyLoader
- {
- /*********
- ** Properties
- *********/
- /// <summary>Metadata for mapping assemblies to the current platform.</summary>
- private readonly PlatformAssemblyMap AssemblyMap;
-
- /// <summary>A type => assembly lookup for types which should be rewritten.</summary>
- private readonly IDictionary<string, Assembly> TypeAssemblies;
-
- /// <summary>Encapsulates monitoring and logging.</summary>
- private readonly IMonitor Monitor;
-
- /// <summary>Whether to enable developer mode logging.</summary>
- private readonly bool IsDeveloperMode;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="targetPlatform">The current game platform.</param>
- /// <param name="monitor">Encapsulates monitoring and logging.</param>
- /// <param name="isDeveloperMode">Whether to enable developer mode logging.</param>
- public AssemblyLoader(Platform targetPlatform, IMonitor monitor, bool isDeveloperMode)
- {
- this.Monitor = monitor;
- this.IsDeveloperMode = isDeveloperMode;
- this.AssemblyMap = Constants.GetAssemblyMap(targetPlatform);
-
- // generate type => assembly lookup for types which should be rewritten
- this.TypeAssemblies = new Dictionary<string, Assembly>();
- foreach (Assembly assembly in this.AssemblyMap.Targets)
- {
- ModuleDefinition module = this.AssemblyMap.TargetModules[assembly];
- foreach (TypeDefinition type in module.GetTypes())
- {
- if (!type.IsPublic)
- continue; // no need to rewrite
- if (type.Namespace.Contains("<"))
- continue; // ignore assembly metadata
- this.TypeAssemblies[type.FullName] = assembly;
- }
- }
- }
-
- /// <summary>Preprocess and load an assembly.</summary>
- /// <param name="mod">The mod for which the assembly is being loaded.</param>
- /// <param name="assemblyPath">The assembly file path.</param>
- /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
- /// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns>
- /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
- public Assembly Load(IModMetadata mod, string assemblyPath, bool assumeCompatible)
- {
- // get referenced local assemblies
- AssemblyParseResult[] assemblies;
- {
- AssemblyDefinitionResolver resolver = new AssemblyDefinitionResolver();
- HashSet<string> visitedAssemblyNames = new HashSet<string>(AppDomain.CurrentDomain.GetAssemblies().Select(p => p.GetName().Name)); // don't try loading assemblies that are already loaded
- assemblies = this.GetReferencedLocalAssemblies(new FileInfo(assemblyPath), visitedAssemblyNames, resolver).ToArray();
- }
-
- // validate load
- if (!assemblies.Any() || assemblies[0].Status == AssemblyLoadStatus.Failed)
- {
- throw new SAssemblyLoadFailedException(!File.Exists(assemblyPath)
- ? $"Could not load '{assemblyPath}' because it doesn't exist."
- : $"Could not load '{assemblyPath}'."
- );
- }
- if (assemblies.Last().Status == AssemblyLoadStatus.AlreadyLoaded) // mod assembly is last in dependency order
- throw new SAssemblyLoadFailedException($"Could not load '{assemblyPath}' because it was already loaded. Do you have two copies of this mod?");
-
- // rewrite & load assemblies in leaf-to-root order
- bool oneAssembly = assemblies.Length == 1;
- Assembly lastAssembly = null;
- HashSet<string> loggedMessages = new HashSet<string>();
- foreach (AssemblyParseResult assembly in assemblies)
- {
- if (assembly.Status == AssemblyLoadStatus.AlreadyLoaded)
- continue;
-
- bool changed = this.RewriteAssembly(mod, assembly.Definition, assumeCompatible, loggedMessages, logPrefix: " ");
- if (changed)
- {
- if (!oneAssembly)
- this.Monitor.Log($" Loading {assembly.File.Name} (rewritten in memory)...", LogLevel.Trace);
- using (MemoryStream outStream = new MemoryStream())
- {
- assembly.Definition.Write(outStream);
- byte[] bytes = outStream.ToArray();
- lastAssembly = Assembly.Load(bytes);
- }
- }
- else
- {
- if (!oneAssembly)
- this.Monitor.Log($" Loading {assembly.File.Name}...", LogLevel.Trace);
- lastAssembly = Assembly.UnsafeLoadFrom(assembly.File.FullName);
- }
- }
-
- // last assembly loaded is the root
- return lastAssembly;
- }
-
- /// <summary>Resolve an assembly by its name.</summary>
- /// <param name="name">The assembly name.</param>
- /// <remarks>
- /// This implementation returns the first loaded assembly which matches the short form of
- /// the assembly name, to resolve assembly resolution issues when rewriting
- /// assemblies (especially with Mono). Since this is meant to be called on <see cref="AppDomain.AssemblyResolve"/>,
- /// the implicit assumption is that loading the exact assembly failed.
- /// </remarks>
- public Assembly ResolveAssembly(string name)
- {
- string shortName = name.Split(new[] { ',' }, 2).First(); // get simple name (without version and culture)
- return AppDomain.CurrentDomain
- .GetAssemblies()
- .FirstOrDefault(p => p.GetName().Name == shortName);
- }
-
-
- /*********
- ** Private methods
- *********/
- /****
- ** Assembly parsing
- ****/
- /// <summary>Get a list of referenced local assemblies starting from the mod assembly, ordered from leaf to root.</summary>
- /// <param name="file">The assembly file to load.</param>
- /// <param name="visitedAssemblyNames">The assembly names that should be skipped.</param>
- /// <param name="assemblyResolver">A resolver which resolves references to known assemblies.</param>
- /// <returns>Returns the rewrite metadata for the preprocessed assembly.</returns>
- private IEnumerable<AssemblyParseResult> GetReferencedLocalAssemblies(FileInfo file, HashSet<string> visitedAssemblyNames, IAssemblyResolver assemblyResolver)
- {
- // validate
- if (file.Directory == null)
- throw new InvalidOperationException($"Could not get directory from file path '{file.FullName}'.");
- if (!file.Exists)
- yield break; // not a local assembly
-
- // read assembly
- byte[] assemblyBytes = File.ReadAllBytes(file.FullName);
- AssemblyDefinition assembly;
- using (Stream readStream = new MemoryStream(assemblyBytes))
- assembly = AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Deferred) { AssemblyResolver = assemblyResolver });
-
- // skip if already visited
- if (visitedAssemblyNames.Contains(assembly.Name.Name))
- yield return new AssemblyParseResult(file, null, AssemblyLoadStatus.AlreadyLoaded);
- visitedAssemblyNames.Add(assembly.Name.Name);
-
- // yield referenced assemblies
- foreach (AssemblyNameReference dependency in assembly.MainModule.AssemblyReferences)
- {
- FileInfo dependencyFile = new FileInfo(Path.Combine(file.Directory.FullName, $"{dependency.Name}.dll"));
- foreach (AssemblyParseResult result in this.GetReferencedLocalAssemblies(dependencyFile, visitedAssemblyNames, assemblyResolver))
- yield return result;
- }
-
- // yield assembly
- yield return new AssemblyParseResult(file, assembly, AssemblyLoadStatus.Okay);
- }
-
- /****
- ** Assembly rewriting
- ****/
- /// <summary>Rewrite the types referenced by an assembly.</summary>
- /// <param name="mod">The mod for which the assembly is being loaded.</param>
- /// <param name="assembly">The assembly to rewrite.</param>
- /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
- /// <param name="loggedMessages">The messages that have already been logged for this mod.</param>
- /// <param name="logPrefix">A string to prefix to log messages.</param>
- /// <returns>Returns whether the assembly was modified.</returns>
- /// <exception cref="IncompatibleInstructionException">An incompatible CIL instruction was found while rewriting the assembly.</exception>
- private bool RewriteAssembly(IModMetadata mod, AssemblyDefinition assembly, bool assumeCompatible, HashSet<string> loggedMessages, string logPrefix)
- {
- ModuleDefinition module = assembly.MainModule;
- string filename = $"{assembly.Name.Name}.dll";
-
- // swap assembly references if needed (e.g. XNA => MonoGame)
- bool platformChanged = false;
- for (int i = 0; i < module.AssemblyReferences.Count; i++)
- {
- // remove old assembly reference
- if (this.AssemblyMap.RemoveNames.Any(name => module.AssemblyReferences[i].Name == name))
- {
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewriting {filename} for OS...");
- platformChanged = true;
- module.AssemblyReferences.RemoveAt(i);
- i--;
- }
- }
- if (platformChanged)
- {
- // add target assembly references
- foreach (AssemblyNameReference target in this.AssemblyMap.TargetReferences.Values)
- module.AssemblyReferences.Add(target);
-
- // rewrite type scopes to use target assemblies
- IEnumerable<TypeReference> typeReferences = module.GetTypeReferences().OrderBy(p => p.FullName);
- foreach (TypeReference type in typeReferences)
- this.ChangeTypeScope(type);
- }
-
- // find (and optionally rewrite) incompatible instructions
- bool anyRewritten = false;
- IInstructionHandler[] handlers = new InstructionMetadata().GetHandlers().ToArray();
- foreach (MethodDefinition method in this.GetMethods(module))
- {
- // check method definition
- foreach (IInstructionHandler handler in handlers)
- {
- InstructionHandleResult result = handler.Handle(module, method, this.AssemblyMap, platformChanged);
- this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
- if (result == InstructionHandleResult.Rewritten)
- anyRewritten = true;
- }
-
- // check CIL instructions
- ILProcessor cil = method.Body.GetILProcessor();
- foreach (Instruction instruction in cil.Body.Instructions.ToArray())
- {
- foreach (IInstructionHandler handler in handlers)
- {
- InstructionHandleResult result = handler.Handle(module, cil, instruction, this.AssemblyMap, platformChanged);
- this.ProcessInstructionHandleResult(mod, handler, result, loggedMessages, logPrefix, assumeCompatible, filename);
- if (result == InstructionHandleResult.Rewritten)
- anyRewritten = true;
- }
- }
- }
-
- return platformChanged || anyRewritten;
- }
-
- /// <summary>Process the result from an instruction handler.</summary>
- /// <param name="mod">The mod being analysed.</param>
- /// <param name="handler">The instruction handler.</param>
- /// <param name="result">The result returned by the handler.</param>
- /// <param name="loggedMessages">The messages already logged for the current mod.</param>
- /// <param name="assumeCompatible">Assume the mod is compatible, even if incompatible code is detected.</param>
- /// <param name="logPrefix">A string to prefix to log messages.</param>
- /// <param name="filename">The assembly filename for log messages.</param>
- private void ProcessInstructionHandleResult(IModMetadata mod, IInstructionHandler handler, InstructionHandleResult result, HashSet<string> loggedMessages, string logPrefix, bool assumeCompatible, string filename)
- {
- switch (result)
- {
- case InstructionHandleResult.Rewritten:
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Rewrote {filename} to fix {handler.NounPhrase}...");
- break;
-
- case InstructionHandleResult.NotCompatible:
- if (!assumeCompatible)
- throw new IncompatibleInstructionException(handler.NounPhrase, $"Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}.");
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Found an incompatible CIL instruction ({handler.NounPhrase}) while loading assembly {filename}, but SMAPI is configured to allow it anyway. The mod may crash or behave unexpectedly.", LogLevel.Warn);
- break;
-
- case InstructionHandleResult.DetectedGamePatch:
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected game patcher ({handler.NounPhrase}) in assembly {filename}.");
- this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} patches the game, which may impact game stability. If you encounter problems, try removing this mod first.", LogLevel.Warn);
- break;
-
- case InstructionHandleResult.DetectedSaveSerialiser:
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected possible save serialiser change ({handler.NounPhrase}) in assembly {filename}.");
- this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} seems to change the save serialiser. It may change your saves in such a way that they won't work without this mod in the future.", LogLevel.Warn);
- break;
-
- case InstructionHandleResult.DetectedDynamic:
- this.Monitor.LogOnce(loggedMessages, $"{logPrefix}Detected 'dynamic' keyword ({handler.NounPhrase}) in assembly {filename}.");
- this.Monitor.LogOnce(loggedMessages, $"{mod.DisplayName} uses the 'dynamic' keyword, which isn't compatible with Stardew Valley on Linux or Mac.",
-#if SMAPI_FOR_WINDOWS
- this.IsDeveloperMode ? LogLevel.Warn : LogLevel.Debug
-#else
- LogLevel.Warn
-#endif
- );
- break;
-
- case InstructionHandleResult.None:
- break;
-
- default:
- throw new NotSupportedException($"Unrecognised instruction handler result '{result}'.");
- }
- }
-
- /// <summary>Get the correct reference to use for compatibility with the current platform.</summary>
- /// <param name="type">The type reference to rewrite.</param>
- private void ChangeTypeScope(TypeReference type)
- {
- // check skip conditions
- if (type == null || type.FullName.StartsWith("System."))
- return;
-
- // get assembly
- if (!this.TypeAssemblies.TryGetValue(type.FullName, out Assembly assembly))
- return;
-
- // replace scope
- AssemblyNameReference assemblyRef = this.AssemblyMap.TargetReferences[assembly];
- type.Scope = assemblyRef;
- }
-
- /// <summary>Get all methods in a module.</summary>
- /// <param name="module">The module to search.</param>
- private IEnumerable<MethodDefinition> GetMethods(ModuleDefinition module)
- {
- return (
- from type in module.GetTypes()
- where type.HasMethods
- from method in type.Methods
- where method.HasBody
- select method
- );
- }
- }
-}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs b/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs
deleted file mode 100644
index b56a776c..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/AssemblyParseResult.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.IO;
-using Mono.Cecil;
-
-namespace StardewModdingAPI.Framework.ModLoading
-{
- /// <summary>Metadata about a parsed assembly definition.</summary>
- internal class AssemblyParseResult
- {
- /*********
- ** Accessors
- *********/
- /// <summary>The original assembly file.</summary>
- public readonly FileInfo File;
-
- /// <summary>The assembly definition.</summary>
- public readonly AssemblyDefinition Definition;
-
- /// <summary>The result of the assembly load.</summary>
- public AssemblyLoadStatus Status;
-
-
- /*********
- ** Public methods
- *********/
- /// <summary>Construct an instance.</summary>
- /// <param name="file">The original assembly file.</param>
- /// <param name="assembly">The assembly definition.</param>
- /// <param name="status">The result of the assembly load.</param>
- public AssemblyParseResult(FileInfo file, AssemblyDefinition assembly, AssemblyLoadStatus status)
- {
- this.File = file;
- this.Definition = assembly;
- this.Status = status;
- }
- }
-}
diff --git a/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs b/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs
deleted file mode 100644
index e4beb7a9..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/Finders/EventFinder.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using Mono.Cecil;
-using Mono.Cecil.Cil;
-
-namespace StardewModdingAPI.Framework.ModLoading.Finders
-{
- /// <summary>Finds incompatible CIL instructions that reference a given event.</summary>
- internal class EventFinder : IInstructionHandler
- {
- /*********
- ** 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;
-
- /// <summary>The result to return for matching instructions.</summary>
- private readonly InstructionHandleResult Result;
-
-
- /*********
- ** 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="result">The result to return for matching instructions.</param>
- public EventFinder(string fullTypeName, string eventName, InstructionHandleResult result)
- {
- this.FullTypeName = fullTypeName;
- this.EventName = eventName;
- this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{eventName} event";
- }
-
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</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>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</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>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
- }
-
-
- /*********
- ** 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
deleted file mode 100644
index 00805815..00000000
--- a/src/StardewModdingAPI/Framework/ModLoading/Finders/FieldFinder.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using Mono.Cecil;
-using Mono.Cecil.Cil;
-
-namespace StardewModdingAPI.Framework.ModLoading.Finders
-{
- /// <summary>Finds incompatible CIL instructions that reference a given field.</summary>
- internal class FieldFinder : IInstructionHandler
- {
- /*********
- ** 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;
-
- /// <summary>The result to return for matching instructions.</summary>
- private readonly InstructionHandleResult Result;
-
-
- /*********
- ** 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="result">The result to return for matching instructions.</param>
- public FieldFinder(string fullTypeName, string fieldName, InstructionHandleResult result)
- {
- this.FullTypeName = fullTypeName;
- this.FieldName = fieldName;
- this.Result = result;
- this.NounPhrase = $"{fullTypeName}.{fieldName} field";
- }
-
- /// <summary>Perform the predefined logic for a method if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="method">The method definition containing the instruction.</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>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, MethodDefinition method, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return InstructionHandleResult.None;
- }
-
- /// <summary>Perform the predefined logic for an instruction if applicable.</summary>
- /// <param name="module">The assembly module containing the instruction.</param>
- /// <param name="cil">The CIL processor.</param>
- /// <param name="instruction">The instruction to handle.</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>
- public virtual InstructionHandleResult Handle(ModuleDefinition module, ILProcessor cil, Instruction instruction, PlatformAssemblyMap assemblyMap, bool platformChanged)
- {
- return this.IsMatch(instruction)
- ? this.Result
- : InstructionHandleResult.None;
- }
-
-
- /*********
- ** 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/ModLoa