Further test on injecting. Injecting a callback to the constructor and capturing the instance that way seems to work.
This commit is contained in:
parent
792cfcd796
commit
e1bfd54894
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
HiDef
|
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set up the console properties
|
||||
/// </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"));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue