BizHawk/BizHawk.Emulation.Cores/ExternalCores/ExternalCore.cs

254 lines
7.1 KiB
C#

using System;
using System.Security;
using System.Threading;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
namespace BizHawk
{
/// <summary>
/// universal interface to a shared library
/// </summary>
public interface ILibAccessor : IDisposable
{
IntPtr GetProcAddress(string name);
bool IsOpen { get; }
}
/// <summary>
/// universal access to an external emulator core
/// </summary>
public interface IExternalCoreAccessor : IDisposable
{
IntPtr Signal(string type, IntPtr obj, string param, IntPtr value);
bool IsOpen { get; }
void RegisterCore(ExternalCore core, bool register);
}
public class CoreAccessor : IExternalCoreAccessor
{
ILibAccessor mLibAccessor;
public CoreAccessor(ILibAccessor accessor)
{
mLibAccessor = accessor;
if (accessor.IsOpen)
{
mSignal = (SignalCallbackDelegate)Marshal.GetDelegateForFunctionPointer(accessor.GetProcAddress("Core_signal"), typeof(SignalCallbackDelegate));
IsOpen = true;
}
}
public void RegisterCore(ExternalCore core, bool register)
{
if (core is StaticCoreCommon) return;
//defer initialization until the core is needed, to avoid pointless costs for cores the user isnt using
if (!IsInitialized)
{
IsInitialized = true;
scc = new StaticCoreCommon(this);
scc.RegisterClientSignal(new SignalCallbackDelegate(ClientSignal));
scc.Initialize();
}
if (register)
{
mCoreRegistry[core.ManagedOpaque] = core;
}
else
{
mCoreRegistry.Remove(core.ManagedOpaque);
}
}
StaticCoreCommon scc;
Dictionary<IntPtr, ExternalCore> mCoreRegistry = new Dictionary<IntPtr, ExternalCore>();
public void Dispose()
{
if (mLibAccessor == null) return;
scc.Dispose();
mLibAccessor.Dispose();
mLibAccessor = null;
IsOpen = false;
}
public bool IsOpen { get; private set; }
bool IsInitialized;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr SignalCallbackDelegate(string type, IntPtr obj, string param, IntPtr value);
SignalCallbackDelegate mSignal;
//external cores call into the client from here
public IntPtr ClientSignal(string type, IntPtr obj, string param, IntPtr value)
{
//static calls
if (obj == IntPtr.Zero)
{
return scc.ClientSignal(type, obj, param, value);
}
else
{
return mCoreRegistry[obj].ClientSignal(type, obj, param, value);
}
}
public IntPtr Signal(string type, IntPtr obj, string param, IntPtr value)
{
if (!IsOpen) throw new InvalidOperationException("core accessor is open");
return mSignal(type, obj, param, value);
}
}
//todo - make abstract
public class ExternalCore : IDisposable
{
//rename to managed and unmanaged
public IntPtr ManagedOpaque;
public IntPtr UnmanagedOpaque;
static int _ManagedOpaque_Counter = 1;
private static object oLock = new object();
protected IExternalCoreAccessor mAccessor;
public ExternalCore(IExternalCoreAccessor accessor)
{
mAccessor = accessor;
lock (oLock)
{
ManagedOpaque = new IntPtr(_ManagedOpaque_Counter);
_ManagedOpaque_Counter++;
}
mAccessor.RegisterCore(this, true);
}
public virtual void Dispose()
{
mAccessor.RegisterCore(this, false);
//universal delete mechanism?
//probably not.
}
/// <summary>
/// cores call into the client from here. this system is not fully baked yet, though
/// </summary>
public virtual IntPtr ClientSignal(string type, IntPtr obj, string param, IntPtr value)
{
//if (type == "FACTORY")
//{
// return new DiscInterface(mAccessor).UnmanagedOpaque;
//}
//else return IntPtr.Zero;
return IntPtr.Zero;
}
/// <summary>
/// merely emits an integer of the current system int size to an ILGenerator
/// </summary>
static void EmitIntPtr(ILGenerator gen, IntPtr val)
{
if (IntPtr.Size == 4) gen.Emit(OpCodes.Ldc_I4, val.ToInt32());
else gen.Emit(OpCodes.Ldc_I8, val.ToInt64());
}
/// <summary>
/// retrieves a function pointer from the core and returns it as the specified delegate type
/// </summary>
protected void QueryCoreCall<T>(out T del, string name)
{
del = QueryCoreCall<T>(name);
}
/// <summary>
/// retrieves a function pointer from the core and returns it as the specified delegate type
/// </summary>
protected T QueryCoreCall<T>(string name)
{
MethodInfo mi = typeof(T).GetMethod("Invoke");
ParameterInfo[] pis = mi.GetParameters();
Type[] unmanagedParamTypes = new Type[pis.Length + 1];
Type[] managedParamTypes = new Type[pis.Length];
unmanagedParamTypes[0] = typeof(int);
for (int i = 0; i < pis.Length; i++)
{
unmanagedParamTypes[i + 1] = pis[i].ParameterType;
managedParamTypes[i] = pis[i].ParameterType;
}
IntPtr fptr = mAccessor.Signal("QUERY_FUNCTION", IntPtr.Zero, name, IntPtr.Zero);
if (fptr == IntPtr.Zero)
throw new InvalidOperationException("external core was missing requested function: " + name);
DynamicMethod dm = new DynamicMethod("", mi.ReturnType, managedParamTypes, GetType().Module);
ILGenerator gen = dm.GetILGenerator();
EmitIntPtr(gen, UnmanagedOpaque);
for (int i = 0; i < pis.Length; i++) gen.Emit(OpCodes.Ldarg, i);
EmitIntPtr(gen, fptr);
gen.EmitCalli(OpCodes.Calli, CallingConvention.ThisCall, mi.ReturnType, unmanagedParamTypes);
gen.Emit(OpCodes.Ret);
Delegate d = dm.CreateDelegate(typeof(T));
return (T)(object)d;
}
/// <summary>
/// exports a delegate as an IntPtr for use in unmanaged code and manages its life cycle to keep it from getting freed
/// </summary>
protected IntPtr ExportDelegate(Delegate d)
{
IntPtr retPtr = Marshal.GetFunctionPointerForDelegate(d);
listLiveDelegates.Add(d);
return retPtr;
}
//need to hold on to these callbacks to make sure they dont get GCed while unmanaged code has a pointer to them
List<Delegate> listLiveDelegates = new List<Delegate>();
}
public class StaticCoreCommon : ExternalCore
{
//keep in mind that we may need to make the console thread safe if we ever do any zany multithreaded things
public StaticCoreCommon(IExternalCoreAccessor accessor)
: base(accessor)
{
}
EmuFile Console;
public void Initialize()
{
Console = new EmuFile(mAccessor);
if (Log.HACK_LOG_STREAM != null)
Console.BaseStream = Log.HACK_LOG_STREAM;
else
Console.BaseStream = System.Console.OpenStandardOutput();
mAccessor.Signal("SET_CONSOLE", IntPtr.Zero, null, Console.UnmanagedOpaque);
}
public void RegisterClientSignal(CoreAccessor.SignalCallbackDelegate ClientSignal)
{
mAccessor.Signal("SET_CLIENT_SIGNAL", IntPtr.Zero, null, ExportDelegate(ClientSignal));
}
public override IntPtr ClientSignal(string type, IntPtr obj, string param, IntPtr value)
{
return IntPtr.Zero;
}
}
}