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
|
@ -101,6 +101,25 @@ namespace StardewModdingAPI.Helpers
|
||||||
return methodDef;
|
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)
|
public MethodInfo GetSMAPIMethodReference(string type, string method)
|
||||||
{
|
{
|
||||||
if (_assemblyDefinition == null)
|
if (_assemblyDefinition == null)
|
||||||
|
@ -111,16 +130,16 @@ namespace StardewModdingAPI.Helpers
|
||||||
|
|
||||||
MethodInfo methodInfo = null;
|
MethodInfo methodInfo = null;
|
||||||
|
|
||||||
var smapiAssembly = Assembly.GetExecutingAssembly().GetType(type);
|
var reflectionType = Assembly.GetExecutingAssembly().GetType(type);
|
||||||
if (smapiAssembly != null)
|
if (reflectionType != null)
|
||||||
{
|
{
|
||||||
methodInfo = smapiAssembly.GetMethod(method);
|
methodInfo = reflectionType.GetMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodInfo;
|
return methodInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodReference ImportSMAPIMethodInStardew(MethodInfo method)
|
public MethodReference ImportSMAPIMethodInStardew(CecilContext destinationContext, MethodBase method)
|
||||||
{
|
{
|
||||||
if (_assemblyDefinition == null)
|
if (_assemblyDefinition == null)
|
||||||
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||||
|
@ -131,7 +150,7 @@ namespace StardewModdingAPI.Helpers
|
||||||
MethodReference reference = null;
|
MethodReference reference = null;
|
||||||
if (method != null)
|
if (method != null)
|
||||||
{
|
{
|
||||||
reference = _assemblyDefinition.MainModule.Import(method);
|
reference = destinationContext._assemblyDefinition.MainModule.Import(method);
|
||||||
}
|
}
|
||||||
return reference;
|
return reference;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -15,18 +16,49 @@ namespace StardewModdingAPI.Helpers
|
||||||
|
|
||||||
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
|
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);
|
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)
|
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);
|
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)
|
// public void ReplaceInstruction(ILProcessor processor, OpCode opcode, string oldOperand, string newOperand)
|
||||||
//{
|
//{
|
||||||
|
@ -38,7 +70,7 @@ namespace StardewModdingAPI.Helpers
|
||||||
string injectedType, string injectedMethod)
|
string injectedType, string injectedMethod)
|
||||||
{
|
{
|
||||||
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||||
var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo);
|
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
|
||||||
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||||
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference);
|
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +79,7 @@ namespace StardewModdingAPI.Helpers
|
||||||
string injectedType, string injectedMethod)
|
string injectedType, string injectedMethod)
|
||||||
{
|
{
|
||||||
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||||
var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo);
|
var reference = smapiContext.ImportSMAPIMethodInStardew(stardewContext, methodInfo);
|
||||||
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||||
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference);
|
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference);
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,40 +181,7 @@ namespace StardewModdingAPI.Inheritance
|
||||||
public SGame()
|
public SGame()
|
||||||
{
|
{
|
||||||
instance = this;
|
instance = this;
|
||||||
|
graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||||
#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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
HiDef
|
|
@ -74,28 +74,17 @@ namespace StardewModdingAPI
|
||||||
StardewContext = new CecilContext(CecilContextType.Stardew);
|
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||||
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||||
|
|
||||||
//StardewContext.ReplaceMethodInstruction(OpCodes.Newobj, "System.Void StardewValley.Game1::.ctor()")
|
//StardewContext.ReplaceMethodInstruction(OpCodes.Newobj, "System.Void StardewValley.Game1::.ctor()");
|
||||||
var test = StardewContext.GetMethodILProcessor("StardewValley.Program", "Main");
|
//CecilHelper.RedirectConstructor(StardewContext, SmapiContext, "StardewValley.Program", "Main",
|
||||||
StardewContext.GetTypeDefinition("StardewValley.Game1");
|
// "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);
|
var inst = instance;
|
||||||
//ilProcessor.InsertBefore(target, loadNameInstruction);
|
string test = Game1.samBandName;
|
||||||
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, callback);
|
var test2 = Game1.numberOfSelectedItems;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -120,6 +109,7 @@ namespace StardewModdingAPI
|
||||||
_modPaths = new List<string>();
|
_modPaths = new List<string>();
|
||||||
_modContentPaths = 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
|
//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(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "StardewValley", "Mods"));
|
||||||
_modPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods"));
|
_modPaths.Add(Path.Combine(Constants.ExecutionPath, "Mods"));
|
||||||
|
@ -147,8 +137,8 @@ namespace StardewModdingAPI
|
||||||
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
|
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
|
||||||
|
|
||||||
// Load in the assembly - ignores security
|
// Load in the assembly - ignores security
|
||||||
StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
//StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||||
//StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
||||||
StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true);
|
StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true);
|
||||||
StardewGameInfo = StardewProgramType.GetField("gamePtr");
|
StardewGameInfo = StardewProgramType.GetField("gamePtr");
|
||||||
|
|
||||||
|
@ -181,12 +171,16 @@ namespace StardewModdingAPI
|
||||||
//Events.MenuChanged += Events_MenuChanged; //Idk right now
|
//Events.MenuChanged += Events_MenuChanged; //Idk right now
|
||||||
|
|
||||||
StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks...");
|
StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks...");
|
||||||
StardewInvoke(() =>
|
|
||||||
{
|
StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
||||||
gamePtr.IsMouseVisible = false;
|
//StardewInvoke(() =>
|
||||||
gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version;
|
//{
|
||||||
StardewForm.Resize += Events.GraphicsEvents.InvokeResize;
|
// gamePtr.IsMouseVisible = false;
|
||||||
});
|
// gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version;
|
||||||
|
// StardewForm.Resize += Events.GraphicsEvents.InvokeResize;
|
||||||
|
//});
|
||||||
|
|
||||||
|
//var test = (Game1)StardewGameInfo.GetValue(StardewProgramType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -248,19 +242,19 @@ namespace StardewModdingAPI
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
gamePtr = new SGame();
|
//gamePtr = new SGame();
|
||||||
StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile...");
|
//StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile...");
|
||||||
Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
//Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||||
LoadMods();
|
//LoadMods();
|
||||||
|
|
||||||
StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm();
|
//StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm();
|
||||||
StardewForm.Closing += StardewForm_Closing;
|
//StardewForm.Closing += StardewForm_Closing;
|
||||||
|
|
||||||
ready = true;
|
ready = true;
|
||||||
|
|
||||||
StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
//Game1 g1 = gamePtr as Game1;
|
||||||
//StardewGameInfo.SetValue(StardewProgramType, gamePtr);
|
//StardewGameInfo.SetValue(StardewProgramType, g1);
|
||||||
gamePtr.Run();
|
//gamePtr.Run();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -162,6 +162,9 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
|
<Content Include="Manifest Resources\Microsoft.Xna.Framework.RuntimeProfile.txt">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Reference in New Issue