From 6d2d90b7681e4e274e92742e98905ec4000486ca Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 12 Mar 2017 01:31:15 -0500 Subject: [PATCH] add logic to detect incompatible mod instructions & reject mod load (#247) --- src/StardewModdingAPI/Constants.cs | 8 ++++++ .../Framework/AssemblyLoader.cs | 18 +++++++++++++ .../IncompatibleInstructionException.cs | 27 +++++++++++++++++++ src/StardewModdingAPI/Program.cs | 5 ++++ .../StardewModdingAPI.csproj | 1 + 5 files changed, 59 insertions(+) create mode 100644 src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs diff --git a/src/StardewModdingAPI/Constants.cs b/src/StardewModdingAPI/Constants.cs index 66bf5842..d8149766 100644 --- a/src/StardewModdingAPI/Constants.cs +++ b/src/StardewModdingAPI/Constants.cs @@ -135,6 +135,14 @@ namespace StardewModdingAPI return new PlatformAssemblyMap(targetPlatform, removeAssemblyReferences, targetAssemblies); } + /// Get finders which match incompatible CIL instructions in mod assemblies. + internal static IEnumerable GetIncompatibilityFinders() + { + return new IInstructionFinder[] + { + }; + } + /// Get rewriters which fix incompatible CIL instructions in mod assemblies. internal static IEnumerable GetRewriters() { diff --git a/src/StardewModdingAPI/Framework/AssemblyLoader.cs b/src/StardewModdingAPI/Framework/AssemblyLoader.cs index 8af67772..dfe0d03f 100644 --- a/src/StardewModdingAPI/Framework/AssemblyLoader.cs +++ b/src/StardewModdingAPI/Framework/AssemblyLoader.cs @@ -55,6 +55,7 @@ namespace StardewModdingAPI.Framework /// Preprocess and load an assembly. /// The assembly file path. /// Returns the rewrite metadata for the preprocessed assembly. + /// An incompatible CIL instruction was found while rewriting the assembly. public Assembly Load(string assemblyPath) { // get referenced local assemblies @@ -159,6 +160,7 @@ namespace StardewModdingAPI.Framework /// Rewrite the types referenced by an assembly. /// The assembly to rewrite. /// Returns whether the assembly was modified. + /// An incompatible CIL instruction was found while rewriting the assembly. private bool RewriteAssembly(AssemblyDefinition assembly) { ModuleDefinition module = assembly.MainModule; @@ -189,6 +191,22 @@ namespace StardewModdingAPI.Framework this.ChangeTypeScope(type); } + // throw exception if assembly contains incompatible instructions can't be rewritten + { + IInstructionFinder[] finders = Constants.GetIncompatibilityFinders().ToArray(); + foreach (MethodDefinition method in this.GetMethods(module)) + { + foreach (Instruction instruction in method.Body.Instructions) + { + foreach (IInstructionFinder finder in finders) + { + if (finder.IsMatch(instruction, platformChanged)) + throw new IncompatibleInstructionException(finder.NounPhrase, $"Found an incompatible CIL instruction ({finder.NounPhrase}) while loading assembly {assembly.Name.Name}."); + } + } + } + } + // rewrite incompatible instructions bool anyRewritten = false; IInstructionRewriter[] rewriters = Constants.GetRewriters().ToArray(); diff --git a/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs b/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs new file mode 100644 index 00000000..affe2cb3 --- /dev/null +++ b/src/StardewModdingAPI/Framework/IncompatibleInstructionException.cs @@ -0,0 +1,27 @@ +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 bf3c86fb..cb8cc2e5 100644 --- a/src/StardewModdingAPI/Program.cs +++ b/src/StardewModdingAPI/Program.cs @@ -453,6 +453,11 @@ namespace StardewModdingAPI { modAssembly = modAssemblyLoader.Load(assemblyPath); } + catch (IncompatibleInstructionException ex) + { + this.Monitor.Log($"{skippedPrefix} because it's not compatible with the latest version of the game (detected {ex.NounPhrase}). Please check for a newer version of the mod (you have v{manifest.Version}).", LogLevel.Error); + continue; + } catch (Exception ex) { this.Monitor.Log($"{skippedPrefix} because its DLL '{manifest.EntryDll}' couldn't be loaded.\n{ex.GetLogSummary()}", LogLevel.Error); diff --git a/src/StardewModdingAPI/StardewModdingAPI.csproj b/src/StardewModdingAPI/StardewModdingAPI.csproj index ab808948..92726ca0 100644 --- a/src/StardewModdingAPI/StardewModdingAPI.csproj +++ b/src/StardewModdingAPI/StardewModdingAPI.csproj @@ -154,6 +154,7 @@ +