Further test on injecting. Injecting a callback to the constructor and capturing the instance that way seems to work.

This commit is contained in:
ClxS 2016-03-10 20:13:57 +00:00
parent 792cfcd796
commit e1bfd54894
6 changed files with 104 additions and 88 deletions

View File

@ -101,6 +101,25 @@ 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;
}

View File

@ -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<Instruction> targets, MethodReference method)
{
foreach(var target in targets)
foreach(var target in targets.ToList())
{
InjectMethod(ilProcessor, target, method);
}
}
private static List<Instruction> GetMatchingInstructions(Collection<Instruction> 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,7 +79,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.Where(i => i.OpCode == OpCodes.Ret), reference);
}

View File

@ -181,40 +181,7 @@ namespace StardewModdingAPI.Inheritance
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
graphics.GraphicsProfile = GraphicsProfile.HiDef;
}
protected override void Initialize()

View File

@ -74,28 +74,17 @@ 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);
}
private static void WeaveOnExitMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, List<Instruction> 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);
}
var inst = instance;
string test = Game1.samBandName;
var test2 = Game1.numberOfSelectedItems;
}
/// <summary>
@ -120,6 +109,7 @@ namespace StardewModdingAPI
_modPaths = new List<string>();
_modContentPaths = new List<string>();
//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"));
@ -147,8 +137,8 @@ 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");
@ -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);
}
/// <summary>
@ -248,19 +242,19 @@ namespace StardewModdingAPI
try
{
gamePtr = new SGame();
StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile...");
Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
LoadMods();
//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;
//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)
{

View File

@ -162,6 +162,9 @@
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<Content Include="Manifest Resources\Microsoft.Xna.Framework.RuntimeProfile.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>