From 792cfcd796df9938a59622897a683d5907c20046 Mon Sep 17 00:00:00 2001 From: ClxS Date: Wed, 9 Mar 2016 22:11:57 +0000 Subject: [PATCH] Further work on getting MSIL Injection in. Next step is to overwrite the constructor for Game1 and redirect it to SGame --- StardewModdingAPI/Helpers/CecilContext.cs | 30 +++++++++++++-- StardewModdingAPI/Helpers/CecilHelper.cs | 10 ++++- StardewModdingAPI/Helpers/ReflectionHelper.cs | 13 +++++++ StardewModdingAPI/Program.cs | 37 +++++++------------ StardewModdingAPI/StardewModdingAPI.csproj | 1 + 5 files changed, 63 insertions(+), 28 deletions(-) create mode 100644 StardewModdingAPI/Helpers/ReflectionHelper.cs diff --git a/StardewModdingAPI/Helpers/CecilContext.cs b/StardewModdingAPI/Helpers/CecilContext.cs index 39e08270..ac76feff 100644 --- a/StardewModdingAPI/Helpers/CecilContext.cs +++ b/StardewModdingAPI/Helpers/CecilContext.cs @@ -48,13 +48,12 @@ namespace StardewModdingAPI.Helpers public CecilContext(CecilContextType contextType) { ContextType = contextType; - if (ContextType == CecilContextType.SMAPI) _assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location); else _assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath); } - + public ILProcessor GetMethodILProcessor(string type, string method) { if (_assemblyDefinition == null) @@ -64,7 +63,7 @@ namespace StardewModdingAPI.Helpers throw new ArgumentNullException("Both type and method must be set"); Mono.Cecil.Cil.ILProcessor ilProcessor = null; - TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type); + TypeDefinition typeDef = GetTypeDefinition(type); if (typeDef != null) { MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method); @@ -77,6 +76,31 @@ namespace StardewModdingAPI.Helpers return ilProcessor; } + public TypeDefinition GetTypeDefinition(string type) + { + if (_assemblyDefinition == null) + throw new Exception("ERROR Assembly not properly read. Cannot parse"); + + if (string.IsNullOrWhiteSpace(type)) + throw new ArgumentNullException("Both type and method must be set"); + + TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type); + return typeDef; + } + + public MethodDefinition GetMethodDefinition(string type, string method) + { + MethodDefinition methodDef = null; + TypeDefinition typeDef = GetTypeDefinition(type); + + if (typeDef != null) + { + methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method); + } + + return methodDef; + } + public MethodInfo GetSMAPIMethodReference(string type, string method) { if (_assemblyDefinition == null) diff --git a/StardewModdingAPI/Helpers/CecilHelper.cs b/StardewModdingAPI/Helpers/CecilHelper.cs index 4beb6c4b..4e250cd9 100644 --- a/StardewModdingAPI/Helpers/CecilHelper.cs +++ b/StardewModdingAPI/Helpers/CecilHelper.cs @@ -11,7 +11,8 @@ namespace StardewModdingAPI.Helpers { public static class CecilHelper { - + //System.Void StardewValley.Game1::.ctor() + private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method) { Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method); @@ -26,6 +27,13 @@ namespace StardewModdingAPI.Helpers } } + + // public void ReplaceInstruction(ILProcessor processor, OpCode opcode, string oldOperand, string newOperand) + //{ + //var instructions = processor.Body.Instructions.Where(i => i.OpCode == opcode && i.Operand == oldOperand); + // processor.Create() + //} + public static void InjectEntryMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod, string injectedType, string injectedMethod) { diff --git a/StardewModdingAPI/Helpers/ReflectionHelper.cs b/StardewModdingAPI/Helpers/ReflectionHelper.cs new file mode 100644 index 00000000..72c484b9 --- /dev/null +++ b/StardewModdingAPI/Helpers/ReflectionHelper.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StardewModdingAPI.Helpers +{ + public static class ReflectionHelper + { + + } +} diff --git a/StardewModdingAPI/Program.cs b/StardewModdingAPI/Program.cs index 7a75e2b7..43e7a1f9 100644 --- a/StardewModdingAPI/Program.cs +++ b/StardewModdingAPI/Program.cs @@ -72,7 +72,11 @@ namespace StardewModdingAPI private static void ConfigureMethodInjection() { StardewContext = new CecilContext(CecilContextType.Stardew); - //SmapiContext = new CecilContext(CecilContextType.SMAPI); + SmapiContext = new CecilContext(CecilContextType.SMAPI); + + //StardewContext.ReplaceMethodInstruction(OpCodes.Newobj, "System.Void StardewValley.Game1::.ctor()") + var test = StardewContext.GetMethodILProcessor("StardewValley.Program", "Main"); + StardewContext.GetTypeDefinition("StardewValley.Game1"); } private static void WeaveOnEnterMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, Instruction target, MethodReference callback) @@ -143,11 +147,11 @@ namespace StardewModdingAPI StardewModdingAPI.Log.Info("Initializing SDV Assembly..."); // Load in the assembly - ignores security - //StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe"); - StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer()); + StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe"); + //StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer()); StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true); StardewGameInfo = StardewProgramType.GetField("gamePtr"); - + // Change the game's version StardewModdingAPI.Log.Verbose("Injecting New SDV Version..."); Game1.version += string.Format("-Z_MODDED | SMAPI {0}", Constants.VersionString); @@ -252,26 +256,11 @@ namespace StardewModdingAPI StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm(); StardewForm.Closing += StardewForm_Closing; - ready = true; - - StardewGameInfo.SetValue(StardewProgramType, gamePtr); - gamePtr.Run(); - - #region deprecated - if (false) - { - //Nope, I can't get it to work. I depend on Game1 being an SGame, and can't cast a parent to a child - //I'm leaving this here in case the community is interested - //StardewInjectorMod.Entry(true); - Type gt = StardewAssembly.GetType("StardewValley.Game1", true); - gamePtr = (SGame)Activator.CreateInstance(gt); - - ready = true; - - StardewGameInfo.SetValue(StardewProgramType, gamePtr); - gamePtr.Run(); - } - #endregion + ready = true; + + StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } }); + //StardewGameInfo.SetValue(StardewProgramType, gamePtr); + gamePtr.Run(); } catch (Exception ex) { diff --git a/StardewModdingAPI/StardewModdingAPI.csproj b/StardewModdingAPI/StardewModdingAPI.csproj index 04a5d596..42fd0c6b 100644 --- a/StardewModdingAPI/StardewModdingAPI.csproj +++ b/StardewModdingAPI/StardewModdingAPI.csproj @@ -145,6 +145,7 @@ +