Encapsulated Cecil functionality. Removed references to stardew injector from program
This commit is contained in:
parent
c8e09331d2
commit
8201b96034
|
@ -20,6 +20,11 @@ namespace StardewModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
public static string ExecutionPath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execution path to execute the code.
|
||||||
|
/// </summary>
|
||||||
|
public static string StardewExePath => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Stardew Valley.exe";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Title for the API console
|
/// Title for the API console
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Helpers
|
||||||
|
{
|
||||||
|
public enum CecilContextType
|
||||||
|
{
|
||||||
|
SMAPI,
|
||||||
|
Stardew
|
||||||
|
}
|
||||||
|
public class CecilContext
|
||||||
|
{
|
||||||
|
public CecilContextType ContextType { get; private set;}
|
||||||
|
|
||||||
|
private AssemblyDefinition _assemblyDefinition { get; set; }
|
||||||
|
private bool _isMemoryStreamDirty { get; set; }
|
||||||
|
|
||||||
|
private MemoryStream _modifiedAssembly;
|
||||||
|
private MemoryStream ModifiedAssembly
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(_modifiedAssembly == null)
|
||||||
|
{
|
||||||
|
_modifiedAssembly = new MemoryStream();
|
||||||
|
_assemblyDefinition.Write(_modifiedAssembly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(_isMemoryStreamDirty)
|
||||||
|
{
|
||||||
|
_modifiedAssembly.Dispose();
|
||||||
|
_modifiedAssembly = new MemoryStream();
|
||||||
|
_assemblyDefinition.Write(_modifiedAssembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _modifiedAssembly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CecilContext(CecilContextType contextType)
|
||||||
|
{
|
||||||
|
ContextType = contextType;
|
||||||
|
|
||||||
|
if (ContextType == CecilContextType.SMAPI)
|
||||||
|
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
|
||||||
|
else
|
||||||
|
_assemblyDefinition = AssemblyDefinition.ReadAssembly(Constants.StardewExePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILProcessor GetMethodILProcessor(string type, string method)
|
||||||
|
{
|
||||||
|
if (_assemblyDefinition == null)
|
||||||
|
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(method))
|
||||||
|
throw new ArgumentNullException("Both type and method must be set");
|
||||||
|
|
||||||
|
Mono.Cecil.Cil.ILProcessor ilProcessor = null;
|
||||||
|
TypeDefinition typeDef = _assemblyDefinition.MainModule.Types.FirstOrDefault(n => n.FullName == type);
|
||||||
|
if (typeDef != null)
|
||||||
|
{
|
||||||
|
MethodDefinition methodDef = typeDef.Methods.FirstOrDefault(m => m.Name == method);
|
||||||
|
if (methodDef != null)
|
||||||
|
{
|
||||||
|
ilProcessor = methodDef.Body.GetILProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ilProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo GetSMAPIMethodReference(string type, string method)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
MethodInfo methodInfo = null;
|
||||||
|
|
||||||
|
var smapiAssembly = Assembly.GetExecutingAssembly().GetType(type);
|
||||||
|
if (smapiAssembly != null)
|
||||||
|
{
|
||||||
|
methodInfo = smapiAssembly.GetMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodReference ImportSMAPIMethodInStardew(MethodInfo method)
|
||||||
|
{
|
||||||
|
if (_assemblyDefinition == null)
|
||||||
|
throw new Exception("ERROR Assembly not properly read. Cannot parse");
|
||||||
|
|
||||||
|
if (ContextType != CecilContextType.SMAPI)
|
||||||
|
throw new Exception("ImportSmapiMethodInStardew can only be called on the Stardew context");
|
||||||
|
|
||||||
|
MethodReference reference = null;
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
reference = _assemblyDefinition.MainModule.Import(method);
|
||||||
|
}
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using Mono.Cecil;
|
||||||
|
using Mono.Cecil.Cil;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace StardewModdingAPI.Helpers
|
||||||
|
{
|
||||||
|
public static class CecilHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
|
||||||
|
{
|
||||||
|
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
|
||||||
|
ilProcessor.InsertBefore(target, callEnterInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InjectMethod(ILProcessor ilProcessor, IEnumerable<Instruction> targets, MethodReference method)
|
||||||
|
{
|
||||||
|
foreach(var target in targets)
|
||||||
|
{
|
||||||
|
InjectMethod(ilProcessor, target, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InjectEntryMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
|
||||||
|
string injectedType, string injectedMethod)
|
||||||
|
{
|
||||||
|
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||||
|
var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo);
|
||||||
|
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||||
|
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.First(), reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InjectExitMethod(CecilContext stardewContext, CecilContext smapiContext, string injecteeType, string injecteeMethod,
|
||||||
|
string injectedType, string injectedMethod)
|
||||||
|
{
|
||||||
|
var methodInfo = smapiContext.GetSMAPIMethodReference(injectedType, injectedMethod);
|
||||||
|
var reference = stardewContext.ImportSMAPIMethodInStardew(methodInfo);
|
||||||
|
var ilProcessor = stardewContext.GetMethodILProcessor(injecteeType, injecteeMethod);
|
||||||
|
InjectMethod(ilProcessor, ilProcessor.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret), reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
using StardewModdingAPI.Events;
|
using StardewModdingAPI.Events;
|
||||||
|
using StardewModdingAPI.Helpers;
|
||||||
using StardewModdingAPI.Inheritance;
|
using StardewModdingAPI.Inheritance;
|
||||||
using StardewModdingAPI.Inheritance.Menus;
|
using StardewModdingAPI.Inheritance.Menus;
|
||||||
using StardewValley;
|
using StardewValley;
|
||||||
|
@ -36,8 +37,8 @@ namespace StardewModdingAPI
|
||||||
public static Thread gameThread;
|
public static Thread gameThread;
|
||||||
public static Thread consoleInputThread;
|
public static Thread consoleInputThread;
|
||||||
|
|
||||||
public static bool StardewInjectorLoaded { get; private set; }
|
public static CecilContext StardewContext;
|
||||||
public static Mod StardewInjectorMod { get; private set; }
|
public static CecilContext SmapiContext;
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -52,7 +53,6 @@ namespace StardewModdingAPI
|
||||||
ConfigureUI();
|
ConfigureUI();
|
||||||
ConfigurePaths();
|
ConfigurePaths();
|
||||||
ConfigureMethodInjection();
|
ConfigureMethodInjection();
|
||||||
ConfigureInjector();
|
|
||||||
//ConfigureSDV();
|
//ConfigureSDV();
|
||||||
|
|
||||||
//GameRunInvoker();
|
//GameRunInvoker();
|
||||||
|
@ -72,23 +72,8 @@ namespace StardewModdingAPI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void ConfigureMethodInjection()
|
private static void ConfigureMethodInjection()
|
||||||
{
|
{
|
||||||
AssemblyDefinition stardewAssembly = AssemblyDefinition.ReadAssembly(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||||
TypeDefinition type = stardewAssembly.MainModule.Types.FirstOrDefault(n => n.FullName == "StardewValley.FarmAnimal");
|
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||||
MethodDefinition foundMethod = type.Methods.FirstOrDefault(m => m.Name == "eatGrass");
|
|
||||||
Mono.Cecil.Cil.ILProcessor ilProcessor = foundMethod.Body.GetILProcessor();
|
|
||||||
|
|
||||||
var smapiAssembly = Assembly.GetExecutingAssembly().GetType("StardewModdingAPI.Events.FarmAnimal");
|
|
||||||
var eventMethod = smapiAssembly.GetMethod("eatGrass_OnEnter");
|
|
||||||
|
|
||||||
AssemblyDefinition fstardewAssembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
|
|
||||||
MethodReference ffoundMethod = stardewAssembly.MainModule.Import(eventMethod);
|
|
||||||
|
|
||||||
WeaveOnEnterMethod(ilProcessor, foundMethod.Body.Instructions.First(), ffoundMethod);
|
|
||||||
WeaveOnExitMethod(ilProcessor, foundMethod.Body.Instructions.Where(i => i.OpCode == OpCodes.Ret).ToList(), ffoundMethod);
|
|
||||||
|
|
||||||
MemoryStream stream = new MemoryStream();
|
|
||||||
stardewAssembly.Write(stream);
|
|
||||||
Assembly.Load(stream.GetBuffer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
@ -151,49 +136,6 @@ namespace StardewModdingAPI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load the injector.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This will load the injector before anything else if it sees it
|
|
||||||
/// It doesn't matter though
|
|
||||||
/// I'll leave it as a feature in case anyone in the community wants to tinker with it
|
|
||||||
/// All you need is a DLL that inherits from mod and is called StardewInjector.dll with an Entry() method
|
|
||||||
/// </remarks>
|
|
||||||
private static void ConfigureInjector()
|
|
||||||
{
|
|
||||||
foreach (string ModPath in _modPaths)
|
|
||||||
{
|
|
||||||
foreach (String s in Directory.GetFiles(ModPath, "StardewInjector.dll"))
|
|
||||||
{
|
|
||||||
StardewModdingAPI.Log.Success(ConsoleColor.Green, "Found Stardew Injector DLL: " + s);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Assembly mod = Assembly.UnsafeLoadFrom(s); //to combat internet-downloaded DLLs
|
|
||||||
|
|
||||||
if (mod.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) > 0)
|
|
||||||
{
|
|
||||||
StardewModdingAPI.Log.Success("Loading Injector DLL...");
|
|
||||||
TypeInfo tar = mod.DefinedTypes.First(x => x.BaseType == typeof(Mod));
|
|
||||||
Mod m = (Mod)mod.CreateInstance(tar.ToString());
|
|
||||||
Console.WriteLine("LOADED: {0} by {1} - Version {2} | Description: {3}", m.Name, m.Authour, m.Version, m.Description);
|
|
||||||
m.Entry(false);
|
|
||||||
StardewInjectorLoaded = true;
|
|
||||||
StardewInjectorMod = m;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StardewModdingAPI.Log.Error("Invalid Mod DLL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
StardewModdingAPI.Log.Error("Failed to load mod '{0}'. Exception details:\n" + ex, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Load Stardev Valley and control features
|
/// Load Stardev Valley and control features
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
|
<Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
@ -121,7 +121,7 @@
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -143,6 +143,8 @@
|
||||||
<Compile Include="Events\Player.cs" />
|
<Compile Include="Events\Player.cs" />
|
||||||
<Compile Include="Events\Time.cs" />
|
<Compile Include="Events\Time.cs" />
|
||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
|
<Compile Include="Helpers\CecilContext.cs" />
|
||||||
|
<Compile Include="Helpers\CecilHelper.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" />
|
||||||
|
@ -177,9 +179,7 @@
|
||||||
<Install>false</Install>
|
<Install>false</Install>
|
||||||
</BootstrapperPackage>
|
</BootstrapperPackage>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup />
|
||||||
<Folder Include="Cecil\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>
|
<PostBuildEvent>
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Stardew Valley">
|
<Reference Include="Stardew Valley">
|
||||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="xTile">
|
<Reference Include="xTile">
|
||||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Reference in New Issue