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>
|
||||
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>
|
||||
/// Title for the API console
|
||||
/// </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.Cil;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.Helpers;
|
||||
using StardewModdingAPI.Inheritance;
|
||||
using StardewModdingAPI.Inheritance.Menus;
|
||||
using StardewValley;
|
||||
|
@ -34,10 +35,10 @@ namespace StardewModdingAPI
|
|||
public static Form StardewForm;
|
||||
|
||||
public static Thread gameThread;
|
||||
public static Thread consoleInputThread;
|
||||
|
||||
public static bool StardewInjectorLoaded { get; private set; }
|
||||
public static Mod StardewInjectorMod { get; private set; }
|
||||
public static Thread consoleInputThread;
|
||||
|
||||
public static CecilContext StardewContext;
|
||||
public static CecilContext SmapiContext;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -52,7 +53,6 @@ namespace StardewModdingAPI
|
|||
ConfigureUI();
|
||||
ConfigurePaths();
|
||||
ConfigureMethodInjection();
|
||||
ConfigureInjector();
|
||||
//ConfigureSDV();
|
||||
|
||||
//GameRunInvoker();
|
||||
|
@ -72,23 +72,8 @@ namespace StardewModdingAPI
|
|||
/// </summary>
|
||||
private static void ConfigureMethodInjection()
|
||||
{
|
||||
AssemblyDefinition stardewAssembly = AssemblyDefinition.ReadAssembly(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||
TypeDefinition type = stardewAssembly.MainModule.Types.FirstOrDefault(n => n.FullName == "StardewValley.FarmAnimal");
|
||||
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());
|
||||
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||
}
|
||||
|
||||
private static void WeaveOnEnterMethod(Mono.Cecil.Cil.ILProcessor ilProcessor, Instruction target, MethodReference callback)
|
||||
|
@ -150,50 +135,7 @@ namespace StardewModdingAPI
|
|||
throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Load Stardev Valley and control features
|
||||
/// </summary>
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
</Reference>
|
||||
<Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
|
||||
<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 Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -121,7 +121,7 @@
|
|||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -143,6 +143,8 @@
|
|||
<Compile Include="Events\Player.cs" />
|
||||
<Compile Include="Events\Time.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="Helpers\CecilContext.cs" />
|
||||
<Compile Include="Helpers\CecilHelper.cs" />
|
||||
<Compile Include="Inheritance\ItemStackChange.cs" />
|
||||
<Compile Include="Inheritance\Menus\SBobberBar.cs" />
|
||||
<Compile Include="Inheritance\Menus\SGameMenu.cs" />
|
||||
|
@ -177,9 +179,7 @@
|
|||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Cecil\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
<Private>False</Private>
|
||||
</Reference>
|
||||
<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 Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -64,7 +64,7 @@
|
|||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xTile">
|
||||
<HintPath>D:\Games\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue