diff --git a/StardewModdingAPI/Helpers/CecilContext.cs b/StardewModdingAPI/Helpers/CecilContext.cs index ac76feff..b25a9ba6 100644 --- a/StardewModdingAPI/Helpers/CecilContext.cs +++ b/StardewModdingAPI/Helpers/CecilContext.cs @@ -100,7 +100,26 @@ namespace StardewModdingAPI.Helpers return methodDef; } - + + public ConstructorInfo GetSMAPITypeContructor(string type) + { + if (_assemblyDefinition == null) + throw new Exception("ERROR Assembly not properly read. Cannot parse"); + + if (ContextType != CecilContextType.SMAPI) + throw new Exception("GetSMAPIMethodReference can only be called on the SMAPI context"); + + ConstructorInfo methodInfo = null; + + var reflectionType = Assembly.GetExecutingAssembly().GetType(type); + if (reflectionType != null) + { + methodInfo = reflectionType.GetConstructor(Type.EmptyTypes); + } + + return methodInfo; + } + public MethodInfo GetSMAPIMethodReference(string type, string method) { if (_assemblyDefinition == null) @@ -111,16 +130,16 @@ namespace StardewModdingAPI.Helpers MethodInfo methodInfo = null; - var smapiAssembly = Assembly.GetExecutingAssembly().GetType(type); - if (smapiAssembly != null) + var reflectionType = Assembly.GetExecutingAssembly().GetType(type); + if (reflectionType != null) { - methodInfo = smapiAssembly.GetMethod(method); + methodInfo = reflectionType.GetMethod(method); } return methodInfo; } - public MethodReference ImportSMAPIMethodInStardew(MethodInfo method) + public MethodReference ImportSMAPIMethodInStardew(CecilContext destinationContext, MethodBase method) { if (_assemblyDefinition == null) throw new Exception("ERROR Assembly not properly read. Cannot parse"); @@ -131,7 +150,7 @@ namespace StardewModdingAPI.Helpers MethodReference reference = null; if (method != null) { - reference = _assemblyDefinition.MainModule.Import(method); + reference = destinationContext._assemblyDefinition.MainModule.Import(method); } return reference; } diff --git a/StardewModdingAPI/Helpers/CecilHelper.cs b/StardewModdingAPI/Helpers/CecilHelper.cs index 4e250cd9..ded40946 100644 --- a/StardewModdingAPI/Helpers/CecilHelper.cs +++ b/StardewModdingAPI/Helpers/CecilHelper.cs @@ -1,5 +1,6 @@ using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Collections.Generic; using System; using System.Collections.Generic; using System.Linq; @@ -15,18 +16,49 @@ namespace StardewModdingAPI.Helpers private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method) { + var callTarget = target; + if (method.HasParameters) + { + Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0); + ilProcessor.InsertBefore(target, loadObjInstruction); + callTarget = loadObjInstruction; + } Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method); - ilProcessor.InsertBefore(target, callEnterInstruction); + ilProcessor.InsertAfter(callTarget, callEnterInstruction); } private static void InjectMethod(ILProcessor ilProcessor, IEnumerable targets, MethodReference method) { - foreach(var target in targets) + foreach(var target in targets.ToList()) { InjectMethod(ilProcessor, target, method); } } + private static List GetMatchingInstructions(Collection instructions, OpCode opcode, object @object) + { + return instructions.Where(n => n.OpCode == opcode && n.Operand == @object).ToList(); + } + + public static void RedirectConstructor(CecilContext stardewContext, CecilContext smapiContext, + string typeToAlter, string methodToAlter, + string injecteeType, string injecteeMethod, + string injectedType, string injectedMethod) + { + var ilProcessor = stardewContext.GetMethodILProcessor(typeToAlter, methodToAlter); + var methodDefinition = stardewContext.GetMethodDefinition(injecteeType, injecteeMethod); + + var methodInfo = smapiContext.GetSMAPITypeContructor(injectedType); + var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo); + + var instructionsToAlter = GetMatchingInstructions(ilProcessor.Body.Instructions, OpCodes.Newobj, methodDefinition); + + var newInstruction = ilProcessor.Create(OpCodes.Newobj, reference); + foreach(var instruction in instructionsToAlter) + { + ilProcessor.Replace(instruction, newInstruction); + } + } // public void ReplaceInstruction(ILProcessor processor, OpCode opcode, string oldOperand, string newOperand) //{ @@ -38,7 +70,7 @@ namespace StardewModdingAPI.Helpers string injectedType, string injectedMethod) { var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod); - var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo); + var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo); var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod); InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference); } @@ -47,8 +79,8 @@ namespace StardewModdingAPI.Helpers string injectedType, string injectedMethod) { var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod); - var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo); - var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod); + var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo); + var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod); InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference); } } diff --git a/StardewModdingAPI/Inheritance/SGame.cs b/StardewModdingAPI/Inheritance/SGame.cs index 735cd58a..5a4ab169 100644 --- a/StardewModdingAPI/Inheritance/SGame.cs +++ b/StardewModdingAPI/Inheritance/SGame.cs @@ -179,42 +179,9 @@ namespace StardewModdingAPI.Inheritance public Farmer CurrentFarmer { get { return player; } } public SGame() - { - instance = this; - -#if DEBUG - SaveGame.serializer = new XmlSerializer(typeof (SaveGame), new Type[28] - { - typeof (Tool), - typeof (GameLocation), - typeof (Crow), - typeof (Duggy), - typeof (Bug), - typeof (BigSlime), - typeof (Fireball), - typeof (Ghost), - typeof (Child), - typeof (Pet), - typeof (Dog), - typeof (StardewValley.Characters.Cat), - typeof (Horse), - typeof (GreenSlime), - typeof (LavaCrab), - typeof (RockCrab), - typeof (ShadowGuy), - typeof (SkeletonMage), - typeof (SquidKid), - typeof (Grub), - typeof (Fly), - typeof (DustSpirit), - typeof (Quest), - typeof (MetalHead), - typeof (ShadowGirl), - typeof (Monster), - typeof (TerrainFeature), - typeof (SObject) - }); -#endif + { + instance = this; + graphics.GraphicsProfile = GraphicsProfile.HiDef; } protected override void Initialize() diff --git a/StardewModdingAPI/Manifest Resources/Microsoft.Xna.Framework.RuntimeProfile.txt b/StardewModdingAPI/Manifest Resources/Microsoft.Xna.Framework.RuntimeProfile.txt new file mode 100644 index 00000000..34e4189a --- /dev/null +++ b/StardewModdingAPI/Manifest Resources/Microsoft.Xna.Framework.RuntimeProfile.txt @@ -0,0 +1 @@ +HiDef \ No newline at end of file diff --git a/StardewModdingAPI/Program.cs b/StardewModdingAPI/Program.cs index 43e7a1f9..e8e1ec17 100644 --- a/StardewModdingAPI/Program.cs +++ b/StardewModdingAPI/Program.cs @@ -74,30 +74,19 @@ namespace StardewModdingAPI StardewContext = new CecilContext(CecilContextType.Stardew); 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"); + //StardewContext.ReplaceMethodInstruction(OpCodes.Newobj, "System.Void StardewValley.Game1::.ctor()"); + //CecilHelper.RedirectConstructor(StardewContext, SmapiContext, "StardewValley.Program", "Main", + // "StardewValley.Game1", ".ctor", "StardewModdingAPI.Inheritance.SGame", ".ctor"); + CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", ".ctor", "StardewModdingAPI.Program", "Test"); } - private static void WeaveOnEnterMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, Instruction target, MethodReference callback) + public static void Test(Game1 instance) { - // Instruction loadNameInstruction = ilProcessor.Create(OpCodes.Ldstr, name); - //ilProcessor.InsertBefore(target, loadNameInstruction); - Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, callback); - ilProcessor.InsertBefore(target, callEnterInstruction); + var inst = instance; + string test = Game1.samBandName; + var test2 = Game1.numberOfSelectedItems; } - - private static void WeaveOnExitMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, List targets, MethodReference callback) - { - // Instruction loadNameInstruction = ilProcessor.Create(OpCodes.Ldstr, name); - //ilProcessor.InsertBefore(target, loadNameInstruction); - foreach (var returnInstruction in targets) - { - Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, callback); - ilProcessor.InsertBefore(returnInstruction, callEnterInstruction); - } - } - + /// /// Set up the console properties /// @@ -120,6 +109,7 @@ namespace StardewModdingAPI _modPaths = new List(); _modContentPaths = new List(); + //TODO: Have an app.config and put the paths inside it so users can define locations to load mods from _modPaths.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods")); _modPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods")); @@ -145,10 +135,10 @@ namespace StardewModdingAPI private static void ConfigureSDV() { 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"); @@ -163,7 +153,7 @@ namespace StardewModdingAPI // Wait for the game to load up while (!ready) ; - + //SDV is running StardewModdingAPI.Log.Comment("SDV Loaded Into Memory"); @@ -181,12 +171,16 @@ namespace StardewModdingAPI //Events.MenuChanged += Events_MenuChanged; //Idk right now StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks..."); - StardewInvoke(() => - { - gamePtr.IsMouseVisible = false; - gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version; - StardewForm.Resize += Events.GraphicsEvents.InvokeResize; - }); + + StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } }); + //StardewInvoke(() => + //{ + // gamePtr.IsMouseVisible = false; + // gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version; + // StardewForm.Resize += Events.GraphicsEvents.InvokeResize; + //}); + + //var test = (Game1)StardewGameInfo.GetValue(StardewProgramType); } /// @@ -248,19 +242,19 @@ namespace StardewModdingAPI try { - gamePtr = new SGame(); - StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile..."); - Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; - LoadMods(); - - StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm(); - StardewForm.Closing += StardewForm_Closing; - + //gamePtr = new SGame(); + //StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile..."); + //Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef; + //LoadMods(); + + //StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm(); + //StardewForm.Closing += StardewForm_Closing; + ready = true; - StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } }); - //StardewGameInfo.SetValue(StardewProgramType, gamePtr); - gamePtr.Run(); + //Game1 g1 = gamePtr as Game1; + //StardewGameInfo.SetValue(StardewProgramType, g1); + //gamePtr.Run(); } catch (Exception ex) { diff --git a/StardewModdingAPI/StardewModdingAPI.csproj b/StardewModdingAPI/StardewModdingAPI.csproj index 42fd0c6b..929ba488 100644 --- a/StardewModdingAPI/StardewModdingAPI.csproj +++ b/StardewModdingAPI/StardewModdingAPI.csproj @@ -162,6 +162,9 @@ + + Always +