Further work on getting MSIL Injection in. Next step is to overwrite the constructor for Game1 and redirect it to SGame

This commit is contained in:
ClxS 2016-03-09 22:11:57 +00:00
parent 608a5cb257
commit 792cfcd796
5 changed files with 63 additions and 28 deletions

View File

@ -48,13 +48,12 @@ namespace StardewModdingAPI.Helpers
public CecilContext(CecilContextType contextType) public CecilContext(CecilContextType contextType)
{ {
ContextType = contextType; ContextType = contextType;
if (ContextType == CecilContextType.SMAPI) if (ContextType == CecilContextType.SMAPI)
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location); _assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
else else
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath); _assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath);
} }
public ILProcessor GetMethodILProcessor(string type, string method) public ILProcessor GetMethodILProcessor(string type, string method)
{ {
if (_assemblyDefinition == null) if (_assemblyDefinition == null)
@ -64,7 +63,7 @@ namespace StardewModdingAPI.Helpers
throw new ArgumentNullException("Both type and method must be set"); throw new ArgumentNullException("Both type and method must be set");
Mono.Cecil.Cil.ILProcessor ilProcessor = null; Mono.Cecil.Cil.ILProcessor ilProcessor = null;
TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type); TypeDefinition typeDef = GetTypeDefinition(type);
if (typeDef != null) if (typeDef != null)
{ {
MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method); MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
@ -77,6 +76,31 @@ namespace StardewModdingAPI.Helpers
return ilProcessor; 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) public MethodInfo GetSMAPIMethodReference(string type, string method)
{ {
if (_assemblyDefinition == null) if (_assemblyDefinition == null)

View File

@ -11,7 +11,8 @@ namespace StardewModdingAPI.Helpers
{ {
public static class CecilHelper public static class CecilHelper
{ {
//System.Void StardewValley.Game1::.ctor()
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method) private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
{ {
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, 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, public static void InjectEntryMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
string injectedType, string injectedMethod) string injectedType, string injectedMethod)
{ {

View File

@ -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
{
}
}

View File

@ -72,7 +72,11 @@ namespace StardewModdingAPI
private static void ConfigureMethodInjection() private static void ConfigureMethodInjection()
{ {
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()")
var test = StardewContext.GetMethodILProcessor("StardewValley.Program", "Main");
StardewContext.GetTypeDefinition("StardewValley.Game1");
} }
private static void WeaveOnEnterMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, Instruction target, MethodReference callback) 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..."); 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");
// Change the game's version // Change the game's version
StardewModdingAPI.Log.Verbose("Injecting New SDV Version..."); StardewModdingAPI.Log.Verbose("Injecting New SDV Version...");
Game1.version += string.Format("-Z_MODDED | SMAPI {0}", Constants.VersionString); 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 = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm();
StardewForm.Closing += StardewForm_Closing; StardewForm.Closing += StardewForm_Closing;
ready = true; ready = true;
StardewGameInfo.SetValue(StardewProgramType, gamePtr); StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
gamePtr.Run(); //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
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -145,6 +145,7 @@
<Compile Include="Extensions.cs" /> <Compile Include="Extensions.cs" />
<Compile Include="Helpers\CecilContext.cs" /> <Compile Include="Helpers\CecilContext.cs" />
<Compile Include="Helpers\CecilHelper.cs" /> <Compile Include="Helpers\CecilHelper.cs" />
<Compile Include="Helpers\ReflectionHelper.cs" />
<Compile Include="Inheritance\ItemStackChange.cs" /> <Compile Include="Inheritance\ItemStackChange.cs" />
<Compile Include="Inheritance\Menus\SBobberBar.cs" /> <Compile Include="Inheritance\Menus\SBobberBar.cs" />
<Compile Include="Inheritance\Menus\SGameMenu.cs" /> <Compile Include="Inheritance\Menus\SGameMenu.cs" />