SMAPI/StardewModdingAPI/Helpers/CecilContext.cs

116 lines
3.9 KiB
C#

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;
}
}
}