2017-04-18 03:49:08 +00:00
//TODO: this isn't "the libretro api" anymore
//it's more like a bridge
//I may need to rename stuff to make it sound more like a bridge
//(the bridge wraps libretro API and presents a very different interface)
using System ;
using System.Diagnostics ;
using System.IO ;
using System.IO.Pipes ;
using System.Collections.Generic ;
using System.Runtime.InteropServices ;
using System.IO.MemoryMappedFiles ;
using BizHawk.Common ;
namespace BizHawk.Emulation.Cores.Libretro
{
public unsafe partial class LibretroApi : IDisposable
{
InstanceDll instanceDll , instanceDllCore ;
string InstanceName ;
//YUCK
public LibretroCore core ;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr DllInit ( IntPtr dllModule ) ;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void MessageApi ( eMessage msg ) ;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2017-06-12 07:31:47 +00:00
delegate void BufferApi ( BufId id , void * ptr , ulong size ) ; //size_t
2017-04-18 03:49:08 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void SetVariableApi ( string key , string value ) ;
//it's NOT the original plan to make this public
//however -- i need to merge the API and the core. theyre too closely related
public CommStruct * comm ;
MessageApi Message ;
BufferApi _copyBuffer ; //TODO: consider making private and wrapping
BufferApi _setBuffer ; //TODO: consider making private and wrapping
SetVariableApi SetVariable ;
public LibretroApi ( string dllPath , string corePath )
{
2019-12-19 04:16:42 +00:00
T GetTypedDelegate < T > ( string proc ) where T : Delegate = > ( T ) Marshal . GetDelegateForFunctionPointer ( instanceDll . GetProcAddrOrThrow ( proc ) , typeof ( T ) ) ;
2017-04-18 03:49:08 +00:00
InstanceName = "libretro_" + Guid . NewGuid ( ) . ToString ( ) ;
var pipeName = InstanceName ;
instanceDll = new InstanceDll ( dllPath ) ;
instanceDllCore = new InstanceDll ( corePath ) ;
2019-12-19 04:16:42 +00:00
var dllinit = GetTypedDelegate < DllInit > ( "DllInit" ) ;
Message = GetTypedDelegate < MessageApi > ( "Message" ) ;
_copyBuffer = GetTypedDelegate < BufferApi > ( "CopyBuffer" ) ;
_setBuffer = GetTypedDelegate < BufferApi > ( "SetBuffer" ) ;
SetVariable = GetTypedDelegate < SetVariableApi > ( "SetVariable" ) ;
2017-04-18 03:49:08 +00:00
comm = ( CommStruct * ) dllinit ( instanceDllCore . HModule ) . ToPointer ( ) ;
//TODO: (stash function pointers locally and thunk to IntPtr)
//ALSO: this should be done by the core, I think, not the API. No smarts should be in here
comm - > env . retro_perf_callback . get_cpu_features = IntPtr . Zero ;
//retro_perf_callback.get_cpu_features = new LibRetro.retro_get_cpu_features_t(() => (ulong)(
2019-12-21 08:17:43 +00:00
// (ProcessorFeatureImports.IsProcessorFeaturePresent(ProcessorFeatureImports.ProcessorFeature.InstructionsXMMIAvailable) ? LibRetro.RETRO_SIMD.SSE : 0) |
// (ProcessorFeatureImports.IsProcessorFeaturePresent(ProcessorFeatureImports.ProcessorFeature.InstructionsXMMI64Available) ? LibRetro.RETRO_SIMD.SSE2 : 0) |
// (ProcessorFeatureImports.IsProcessorFeaturePresent(ProcessorFeatureImports.ProcessorFeature.InstructionsSSE3Available) ? LibRetro.RETRO_SIMD.SSE3 : 0) |
// (ProcessorFeatureImports.IsProcessorFeaturePresent(ProcessorFeatureImports.ProcessorFeature.InstructionsMMXAvailable) ? LibRetro.RETRO_SIMD.MMX : 0)
2017-04-18 03:49:08 +00:00
// ));
//retro_perf_callback.get_perf_counter = new LibRetro.retro_perf_get_counter_t(() => System.Diagnostics.Stopwatch.GetTimestamp());
//retro_perf_callback.get_time_usec = new LibRetro.retro_perf_get_time_usec_t(() => DateTime.Now.Ticks / 10);
//retro_perf_callback.perf_log = new LibRetro.retro_perf_log_t(() => { });
//retro_perf_callback.perf_register = new LibRetro.retro_perf_register_t((ref LibRetro.retro_perf_counter counter) => { });
//retro_perf_callback.perf_start = new LibRetro.retro_perf_start_t((ref LibRetro.retro_perf_counter counter) => { });
//retro_perf_callback.perf_stop = new LibRetro.retro_perf_stop_t((ref LibRetro.retro_perf_counter counter) => { });
}
public void Dispose ( )
{
//TODO: better termination of course
instanceDllCore . Dispose ( ) ;
instanceDll . Dispose ( ) ;
}
public RetroDescription CalculateDescription ( )
{
var descr = new RetroDescription ( ) ;
descr . LibraryName = new string ( comm - > env . retro_system_info . library_name ) ;
descr . LibraryVersion = new string ( comm - > env . retro_system_info . library_version ) ;
descr . ValidExtensions = new string ( comm - > env . retro_system_info . valid_extensions ) ;
descr . NeedsRomAsPath = comm - > env . retro_system_info . need_fullpath ;
descr . NeedsArchives = comm - > env . retro_system_info . block_extract ;
descr . SupportsNoGame = comm - > env . support_no_game ;
return descr ;
}
/// <summary>
/// Copy an ascii string into libretro. It keeps the copy.
/// </summary>
public void CopyAscii ( BufId id , string str )
{
fixed ( char * cp = str )
2017-06-12 07:31:47 +00:00
_copyBuffer ( id , cp , ( ulong ) str . Length + 1 ) ;
2017-04-18 03:49:08 +00:00
}
/// <summary>
/// Copy a buffer into libretro. It keeps the copy.
/// </summary>
public void CopyBytes ( BufId id , byte [ ] bytes )
{
fixed ( byte * bp = bytes )
2017-06-12 07:31:47 +00:00
_copyBuffer ( id , bp , ( ulong ) bytes . Length ) ;
2017-04-18 03:49:08 +00:00
}
/// <summary>
/// Locks a buffer and sets it into libretro. You must pass a delegate to be executed while that buffer is locked.
/// This is meant to be used for avoiding a memcpy for large roms (which the core is then just going to memcpy again on its own)
/// The memcpy has to happen at some point (libretro semantics specify [not literally, the docs dont say] that the core should finish using the buffer before its init returns)
/// but this limits it to once.
/// Moreover, this keeps the c++ side from having to free strings when they're no longer used (and memory management is trickier there, so we try to avoid it)
/// </summary>
public void SetBytes ( BufId id , byte [ ] bytes , Action andThen )
{
fixed ( byte * bp = bytes )
{
2017-06-12 07:31:47 +00:00
_setBuffer ( id , bp , ( ulong ) bytes . Length ) ;
2017-04-18 03:49:08 +00:00
andThen ( ) ;
}
}
/// <summary>
/// see SetBytes
/// </summary>
public void SetAscii ( BufId id , string str , Action andThen )
{
fixed ( byte * cp = System . Text . Encoding . ASCII . GetBytes ( str + "\0" ) )
{
2017-06-12 07:31:47 +00:00
_setBuffer ( id , cp , ( ulong ) str . Length + 1 ) ;
2017-04-18 03:49:08 +00:00
andThen ( ) ;
}
}
public unsafe struct CommStructEnv
{
public retro_system_info retro_system_info ;
public retro_system_av_info retro_system_av_info ;
2017-06-13 13:16:25 +00:00
2017-08-05 05:59:17 +00:00
public ulong retro_serialize_size_initial ; //size_t :(
public ulong retro_serialize_size ; //size_t :(
2017-06-13 13:16:25 +00:00
2017-04-18 03:49:08 +00:00
public uint retro_region ;
public uint retro_api_version ;
public retro_pixel_format pixel_format ; //default is 0 -- RETRO_PIXEL_FORMAT_0RGB1555
public int rotation_ccw ;
public bool support_no_game ;
public IntPtr core_get_proc_address ; //this is.. a callback.. or something.. right?
public retro_game_geometry retro_game_geometry ;
public bool retro_game_geometry_dirty ; //c# can clear this when it's acknowledged (but I think we might handle it from here? not sure)
public int variable_count ;
public char * * variable_keys ;
public char * * variable_comments ;
//c# sets these with thunked callbacks
public retro_perf_callback retro_perf_callback ;
//various stashed stuff solely for c# convenience
public ulong processor_features ;
public int fb_width , fb_height ; //core sets these; c# picks up, and..
public int * fb_bufptr ; //..sets this for the core to spill its data nito
}
public struct CommStruct
{
//the cmd being executed
public eMessage cmd ;
//the status of the core
public eStatus status ;
//the SIG or BRK that the core is halted in
public eMessage reason ;
//flexible in/out parameters
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
public uint id , addr , value , size ;
public uint port , device , index , slot ;
[MarshalAs(UnmanagedType.Struct)]
public CommStructEnv env ;
//this should always be used in pairs
2017-06-12 07:31:47 +00:00
public fixed ulong buf [ ( int ) BufId . BufId_Num ] ; //actually a pointer, but can't marshal IntPtr, so dumb
public fixed ulong buf_size [ ( int ) BufId . BufId_Num ] ; //actually a size_t
2017-04-18 03:49:08 +00:00
//utilities
public bool GetBoolValue ( ) { return value ! = 0 ; } //should this be here or by the other helpers? I dont know
}
public retro_system_av_info AVInfo { get { return comm - > env . retro_system_av_info ; } }
} //class
}