new libretro WIP with libco support. Still missing sound, saves, many small details. Will undergo heavy organizational revisions.
This commit is contained in:
parent
ca90853a88
commit
e9229747d7
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Libretro;
|
||||
using BizHawk.Emulation.Cores.Atari.Atari7800;
|
||||
using BizHawk.Emulation.Cores.Calculators;
|
||||
using BizHawk.Emulation.Cores.Computers.AppleII;
|
||||
|
@ -234,7 +235,7 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
string codePathPart = Path.GetFileNameWithoutExtension(nextComm.LaunchLibretroCore);
|
||||
|
||||
var retro = new LibRetroEmulator(nextComm, nextComm.LaunchLibretroCore);
|
||||
var retro = new LibretroCore(nextComm, nextComm.LaunchLibretroCore);
|
||||
nextEmulator = retro;
|
||||
|
||||
// kind of dirty.. we need to stash this, and then we can unstash it in a moment, in case the core doesnt fail
|
||||
|
@ -296,7 +297,7 @@ namespace BizHawk.Client.Common
|
|||
ret = HandleArchiveBinding(file);
|
||||
if (ret)
|
||||
{
|
||||
ret = retro.LoadData(file.ReadAllBytes());
|
||||
ret = retro.LoadData(file.ReadAllBytes(), file.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Text;
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Libretro;
|
||||
using BizHawk.Client.Common;
|
||||
|
||||
//these match strings from OpenAdvance. should we make them constants in there?
|
||||
|
@ -55,7 +56,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
RefreshLibretroCore(false);
|
||||
}
|
||||
|
||||
LibRetroEmulator.RetroDescription CurrentDescription;
|
||||
RetroDescription CurrentDescription;
|
||||
void RefreshLibretroCore(bool bootstrap)
|
||||
{
|
||||
txtLibretroCore.Text = "";
|
||||
|
@ -72,10 +73,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
//scan the current libretro core to see if it can be launched with NoGame,and other stuff
|
||||
try
|
||||
{
|
||||
//a stub corecomm. to reinforce that this won't touch the frontend at all!
|
||||
//LibRetroEmulator should be able to survive having this stub corecomm
|
||||
//OLD COMMENTS:
|
||||
////a stub corecomm. to reinforce that this won't touch the frontend at all!
|
||||
////LibRetroEmulator should be able to survive having this stub corecomm
|
||||
//NEW COMMENTS:
|
||||
//nope, we need to navigate to the dll path. this was a bad idea anyway. so many dlls get loaded, something to resolve them is needed
|
||||
var coreComm = new BizHawk.Emulation.Common.CoreComm(null, null);
|
||||
using (var retro = new LibRetroEmulator(coreComm, core))
|
||||
CoreFileProvider.SyncCoreCommInputSignals(coreComm);
|
||||
using (var retro = new LibretroCore(coreComm, core))
|
||||
{
|
||||
btnLibretroLaunchGame.Enabled = true;
|
||||
if (retro.Description.SupportsNoGame)
|
||||
|
@ -108,7 +113,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
foreach(var ext in CurrentDescription.ValidExtensions.Split('|'))
|
||||
sw.Write("*.{0};",ext);
|
||||
var filter = sw.ToString();
|
||||
filter = filter.Substring(0,filter.Length-1);
|
||||
filter = filter.Substring(0,filter.Length-1); //remove last semicolon
|
||||
List<string> args = new List<string>();
|
||||
args.Add("Rom Files");
|
||||
if (!CurrentDescription.NeedsArchives)
|
||||
|
|
|
@ -1188,9 +1188,17 @@
|
|||
<Compile Include="Consoles\PC Engine\VDC.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VDC.Render.cs" />
|
||||
<Compile Include="Consoles\PC Engine\VPC.cs" />
|
||||
<Compile Include="Libretro\LibRetro.cs" />
|
||||
<Compile Include="Libretro\LibRetroEmulator.cs" />
|
||||
<Compile Include="Libretro\LibRetroEmulator.InputCallbacks.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_CMD.cs" />
|
||||
<Compile Include="Libretro\LibretroApi.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_BRK.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_Delegates.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_Enums.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_SIG.cs" />
|
||||
<Compile Include="Libretro\LibretroApi_Structs.cs" />
|
||||
<Compile Include="Libretro\LibretroCore.cs" />
|
||||
<Compile Include="Libretro\LibretroCoreSettings.cs" />
|
||||
<Compile Include="Libretro\LibretroCore_Description.cs" />
|
||||
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
|
||||
<Compile Include="MemoryBlock.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\Genesis.cs" />
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
//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;
|
||||
|
||||
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
|
||||
public static unsafe extern void* CopyMemory(void* dest, void* src, ulong count);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate IntPtr DllInit(IntPtr dllModule);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void MessageApi(eMessage msg);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void BufferApi(BufId id, void* ptr, int size);
|
||||
|
||||
[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)
|
||||
{
|
||||
InstanceName = "libretro_" + Guid.NewGuid().ToString();
|
||||
|
||||
var pipeName = InstanceName;
|
||||
|
||||
instanceDll = new InstanceDll(dllPath);
|
||||
instanceDllCore = new InstanceDll(corePath);
|
||||
|
||||
var dllinit = (DllInit)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("DllInit"), typeof(DllInit));
|
||||
Message = (MessageApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("Message"), typeof(MessageApi));
|
||||
_copyBuffer = (BufferApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("CopyBuffer"), typeof(BufferApi));
|
||||
_setBuffer = (BufferApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("SetBuffer"), typeof(BufferApi));
|
||||
SetVariable = (SetVariableApi)Marshal.GetDelegateForFunctionPointer(instanceDll.GetProcAddress("SetVariable"), typeof(SetVariableApi));
|
||||
|
||||
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)(
|
||||
// (Win32PInvokes.IsProcessorFeaturePresent(Win32PInvokes.ProcessorFeature.InstructionsXMMIAvailable) ? LibRetro.RETRO_SIMD.SSE : 0) |
|
||||
// (Win32PInvokes.IsProcessorFeaturePresent(Win32PInvokes.ProcessorFeature.InstructionsXMMI64Available) ? LibRetro.RETRO_SIMD.SSE2 : 0) |
|
||||
// (Win32PInvokes.IsProcessorFeaturePresent(Win32PInvokes.ProcessorFeature.InstructionsSSE3Available) ? LibRetro.RETRO_SIMD.SSE3 : 0) |
|
||||
// (Win32PInvokes.IsProcessorFeaturePresent(Win32PInvokes.ProcessorFeature.InstructionsMMXAvailable) ? LibRetro.RETRO_SIMD.MMX : 0)
|
||||
// ));
|
||||
//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)
|
||||
_copyBuffer(id, cp, str.Length + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy a buffer into libretro. It keeps the copy.
|
||||
/// </summary>
|
||||
public void CopyBytes(BufId id, byte[] bytes)
|
||||
{
|
||||
fixed (byte* bp = bytes)
|
||||
_copyBuffer(id, bp, bytes.Length);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
_setBuffer(id, bp, bytes.Length);
|
||||
andThen();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// see SetBytes
|
||||
/// </summary>
|
||||
public void SetAscii(BufId id, string str, Action andThen)
|
||||
{
|
||||
fixed (byte* cp = System.Text.Encoding.ASCII.GetBytes(str+"\0"))
|
||||
{
|
||||
_setBuffer(id, cp, str.Length + 1);
|
||||
andThen();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe struct CommStructEnv
|
||||
{
|
||||
public retro_system_info retro_system_info;
|
||||
public retro_system_av_info retro_system_av_info;
|
||||
public uint retro_serialize_size; //size_t :(
|
||||
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
|
||||
public fixed uint buf[(int)BufId.BufId_Num]; //ACTUALLY A POINTER but can't marshal it :(
|
||||
public fixed int buf_size[(int)BufId.BufId_Num];
|
||||
|
||||
//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
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
unsafe partial class LibretroApi
|
||||
{
|
||||
bool Handle_BRK(eMessage msg)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case eMessage.BRK_InputState:
|
||||
comm->value = (uint)core.CB_InputState(comm->port, comm->device, comm->index, comm->id);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
} //switch(msg)
|
||||
|
||||
Message(eMessage.Resume);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
unsafe partial class LibretroApi
|
||||
{
|
||||
void WaitForCMD()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
if (comm->status == eStatus.eStatus_Idle)
|
||||
break;
|
||||
if (Handle_SIG(comm->reason)) continue;
|
||||
if (Handle_BRK(comm->reason)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
public void CMD_Run()
|
||||
{
|
||||
Message(eMessage.CMD_Run);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
public void CMD_SetEnvironment()
|
||||
{
|
||||
Message(eMessage.CMD_SetEnvironment);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
public bool CMD_LoadNoGame()
|
||||
{
|
||||
Message(eMessage.CMD_LoadNoGame);
|
||||
WaitForCMD();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CMD_LoadPath(string path)
|
||||
{
|
||||
SetAscii(BufId.Param0, path, ()=> {
|
||||
Message(eMessage.CMD_LoadPath);
|
||||
WaitForCMD();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CMD_LoadData(byte[] data, string id)
|
||||
{
|
||||
SetAscii(BufId.Param0, id, () =>
|
||||
{
|
||||
SetBytes(BufId.Param1, data, () =>
|
||||
{
|
||||
Message(eMessage.CMD_LoadData);
|
||||
WaitForCMD();
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CMD_Serialize(byte[] data)
|
||||
{
|
||||
bool ret = false;
|
||||
SetBytes(BufId.Param0, data, () =>
|
||||
{
|
||||
Message(eMessage.CMD_Serialize);
|
||||
WaitForCMD();
|
||||
ret = comm->GetBoolValue();
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool CMD_Unserialize(byte[] data)
|
||||
{
|
||||
bool ret = false;
|
||||
SetBytes(BufId.Param0, data, () =>
|
||||
{
|
||||
Message(eMessage.CMD_Unserialize);
|
||||
WaitForCMD();
|
||||
ret = comm->GetBoolValue();
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
unsafe partial class LibretroApi
|
||||
{
|
||||
public enum eMessage : int
|
||||
{
|
||||
NotSet,
|
||||
|
||||
Resume,
|
||||
|
||||
QUERY_FIRST,
|
||||
QUERY_LAST,
|
||||
|
||||
CMD_FIRST,
|
||||
CMD_SetEnvironment,
|
||||
CMD_LoadNoGame,
|
||||
CMD_LoadData,
|
||||
CMD_LoadPath,
|
||||
CMD_Deinit,
|
||||
CMD_Reset,
|
||||
CMD_Run,
|
||||
CMD_Serialize,
|
||||
CMD_Unserialize,
|
||||
CMD_LAST,
|
||||
|
||||
SIG_VideoUpdate,
|
||||
|
||||
BRK_InputState,
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE
|
||||
{
|
||||
NONE = 0,
|
||||
JOYPAD = 1,
|
||||
MOUSE = 2,
|
||||
KEYBOARD = 3,
|
||||
LIGHTGUN = 4,
|
||||
ANALOG = 5,
|
||||
POINTER = 6,
|
||||
SENSOR_ACCELEROMETER = 7
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_ANALOG
|
||||
{
|
||||
// LEFT / RIGHT?
|
||||
X = 0,
|
||||
Y = 1
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_MOUSE
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
LEFT = 2,
|
||||
RIGHT = 3
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_LIGHTGUN
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
TRIGGER = 2,
|
||||
CURSOR = 3,
|
||||
TURBO = 4,
|
||||
PAUSE = 5,
|
||||
START = 6
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_POINTER
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
PRESSED = 2
|
||||
};
|
||||
|
||||
public enum RETRO_KEY
|
||||
{
|
||||
UNKNOWN = 0,
|
||||
FIRST = 0,
|
||||
BACKSPACE = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
PAUSE = 19,
|
||||
ESCAPE = 27,
|
||||
SPACE = 32,
|
||||
EXCLAIM = 33,
|
||||
QUOTEDBL = 34,
|
||||
HASH = 35,
|
||||
DOLLAR = 36,
|
||||
AMPERSAND = 38,
|
||||
QUOTE = 39,
|
||||
LEFTPAREN = 40,
|
||||
RIGHTPAREN = 41,
|
||||
ASTERISK = 42,
|
||||
PLUS = 43,
|
||||
COMMA = 44,
|
||||
MINUS = 45,
|
||||
PERIOD = 46,
|
||||
SLASH = 47,
|
||||
_0 = 48,
|
||||
_1 = 49,
|
||||
_2 = 50,
|
||||
_3 = 51,
|
||||
_4 = 52,
|
||||
_5 = 53,
|
||||
_6 = 54,
|
||||
_7 = 55,
|
||||
_8 = 56,
|
||||
_9 = 57,
|
||||
COLON = 58,
|
||||
SEMICOLON = 59,
|
||||
LESS = 60,
|
||||
EQUALS = 61,
|
||||
GREATER = 62,
|
||||
QUESTION = 63,
|
||||
AT = 64,
|
||||
LEFTBRACKET = 91,
|
||||
BACKSLASH = 92,
|
||||
RIGHTBRACKET = 93,
|
||||
CARET = 94,
|
||||
UNDERSCORE = 95,
|
||||
BACKQUOTE = 96,
|
||||
a = 97,
|
||||
b = 98,
|
||||
c = 99,
|
||||
d = 100,
|
||||
e = 101,
|
||||
f = 102,
|
||||
g = 103,
|
||||
h = 104,
|
||||
i = 105,
|
||||
j = 106,
|
||||
k = 107,
|
||||
l = 108,
|
||||
m = 109,
|
||||
n = 110,
|
||||
o = 111,
|
||||
p = 112,
|
||||
q = 113,
|
||||
r = 114,
|
||||
s = 115,
|
||||
t = 116,
|
||||
u = 117,
|
||||
v = 118,
|
||||
w = 119,
|
||||
x = 120,
|
||||
y = 121,
|
||||
z = 122,
|
||||
DELETE = 127,
|
||||
|
||||
KP0 = 256,
|
||||
KP1 = 257,
|
||||
KP2 = 258,
|
||||
KP3 = 259,
|
||||
KP4 = 260,
|
||||
KP5 = 261,
|
||||
KP6 = 262,
|
||||
KP7 = 263,
|
||||
KP8 = 264,
|
||||
KP9 = 265,
|
||||
KP_PERIOD = 266,
|
||||
KP_DIVIDE = 267,
|
||||
KP_MULTIPLY = 268,
|
||||
KP_MINUS = 269,
|
||||
KP_PLUS = 270,
|
||||
KP_ENTER = 271,
|
||||
KP_EQUALS = 272,
|
||||
|
||||
UP = 273,
|
||||
DOWN = 274,
|
||||
RIGHT = 275,
|
||||
LEFT = 276,
|
||||
INSERT = 277,
|
||||
HOME = 278,
|
||||
END = 279,
|
||||
PAGEUP = 280,
|
||||
PAGEDOWN = 281,
|
||||
|
||||
F1 = 282,
|
||||
F2 = 283,
|
||||
F3 = 284,
|
||||
F4 = 285,
|
||||
F5 = 286,
|
||||
F6 = 287,
|
||||
F7 = 288,
|
||||
F8 = 289,
|
||||
F9 = 290,
|
||||
F10 = 291,
|
||||
F11 = 292,
|
||||
F12 = 293,
|
||||
F13 = 294,
|
||||
F14 = 295,
|
||||
F15 = 296,
|
||||
|
||||
NUMLOCK = 300,
|
||||
CAPSLOCK = 301,
|
||||
SCROLLOCK = 302,
|
||||
RSHIFT = 303,
|
||||
LSHIFT = 304,
|
||||
RCTRL = 305,
|
||||
LCTRL = 306,
|
||||
RALT = 307,
|
||||
LALT = 308,
|
||||
RMETA = 309,
|
||||
LMETA = 310,
|
||||
LSUPER = 311,
|
||||
RSUPER = 312,
|
||||
MODE = 313,
|
||||
COMPOSE = 314,
|
||||
|
||||
HELP = 315,
|
||||
PRINT = 316,
|
||||
SYSREQ = 317,
|
||||
BREAK = 318,
|
||||
MENU = 319,
|
||||
POWER = 320,
|
||||
EURO = 321,
|
||||
UNDO = 322,
|
||||
|
||||
LAST
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum RETRO_MOD
|
||||
{
|
||||
NONE = 0,
|
||||
SHIFT = 1,
|
||||
CTRL = 2,
|
||||
ALT = 4,
|
||||
META = 8,
|
||||
NUMLOCK = 16,
|
||||
CAPSLOCK = 32,
|
||||
SCROLLLOCK = 64
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_SENSOR_ACCELEROMETER
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
};
|
||||
|
||||
public enum RETRO_DEVICE_ID_JOYPAD
|
||||
{
|
||||
B = 0,
|
||||
Y = 1,
|
||||
SELECT = 2,
|
||||
START = 3,
|
||||
UP = 4,
|
||||
DOWN = 5,
|
||||
LEFT = 6,
|
||||
RIGHT = 7,
|
||||
A = 8,
|
||||
X = 9,
|
||||
L = 10,
|
||||
R = 11,
|
||||
L2 = 12,
|
||||
R2 = 13,
|
||||
L3 = 14,
|
||||
R3 = 15
|
||||
};
|
||||
|
||||
public enum eStatus : int
|
||||
{
|
||||
eStatus_Idle,
|
||||
eStatus_CMD,
|
||||
eStatus_BRK
|
||||
};
|
||||
|
||||
public enum BufId : int
|
||||
{
|
||||
Param0 = 0,
|
||||
Param1 = 1,
|
||||
SystemDirectory = 2,
|
||||
SaveDirectory = 3,
|
||||
CoreDirectory = 4,
|
||||
CoreAssetsDirectory = 5,
|
||||
BufId_Num
|
||||
}
|
||||
|
||||
//libretro enums:
|
||||
|
||||
public enum retro_pixel_format : uint
|
||||
{
|
||||
XRGB1555 = 0,
|
||||
XRGB8888 = 1,
|
||||
RGB565 = 2
|
||||
};
|
||||
|
||||
public enum retro_region : uint
|
||||
{
|
||||
NTSC = 0,
|
||||
PAL = 1
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
unsafe partial class LibretroApi
|
||||
{
|
||||
bool Handle_SIG(eMessage msg)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
|
||||
} //switch(msg)
|
||||
|
||||
Message(eMessage.Resume);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
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
|
||||
{
|
||||
partial class LibretroApi
|
||||
{
|
||||
|
||||
public struct retro_game_geometry
|
||||
{
|
||||
public uint base_width;
|
||||
public uint base_height;
|
||||
public uint max_width;
|
||||
public uint max_height;
|
||||
public float aspect_ratio;
|
||||
}
|
||||
|
||||
|
||||
public unsafe struct retro_system_info
|
||||
{
|
||||
public sbyte* library_name;
|
||||
public sbyte* library_version;
|
||||
public sbyte* valid_extensions;
|
||||
public bool need_fullpath;
|
||||
public bool block_extract;
|
||||
public short pad;
|
||||
}
|
||||
|
||||
public struct retro_system_timing
|
||||
{
|
||||
public double fps;
|
||||
public double sample_rate;
|
||||
}
|
||||
|
||||
public struct retro_system_av_info
|
||||
{
|
||||
public retro_game_geometry geometry;
|
||||
public retro_system_timing timing;
|
||||
}
|
||||
|
||||
//untested
|
||||
public struct retro_perf_counter
|
||||
{
|
||||
public string ident;
|
||||
public ulong start;
|
||||
public ulong total;
|
||||
public ulong call_cnt;
|
||||
|
||||
[MarshalAs(UnmanagedType.U1)]
|
||||
public bool registered;
|
||||
};
|
||||
|
||||
//perf callbacks
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long retro_perf_get_time_usec_t();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate long retro_perf_get_counter_t();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate ulong retro_get_cpu_features_t();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void retro_perf_log_t();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void retro_perf_register_t(ref retro_perf_counter counter);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void retro_perf_start_t(ref retro_perf_counter counter);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void retro_perf_stop_t(ref retro_perf_counter counter);
|
||||
|
||||
public struct retro_perf_callback
|
||||
{
|
||||
public IntPtr get_time_usec;
|
||||
public IntPtr get_cpu_features;
|
||||
public IntPtr get_perf_counter;
|
||||
public IntPtr perf_register;
|
||||
public IntPtr perf_start;
|
||||
public IntPtr perf_stop;
|
||||
public IntPtr perf_log;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
//TODO: it's a bit of a misnomer to call this a 'core'
|
||||
//that's libretro nomenclature for a particular core (nes, genesis, doom, etc.)
|
||||
//we should call this LibretroEmulator (yeah, it was originally called that)
|
||||
//Since it's an IEmulator.. but... I dont know. Yeah, that's probably best
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
[CoreAttributes("Libretro", "zeromus")]
|
||||
[ServiceNotApplicable(typeof(IDriveLight))]
|
||||
public unsafe partial class LibretroCore : IEmulator, ISettable<LibretroCore.Settings, LibretroCore.SyncSettings>,
|
||||
ISaveRam, IStatable, IVideoProvider, IInputPollable
|
||||
{
|
||||
private LibretroApi api;
|
||||
|
||||
public LibretroCore(CoreComm nextComm, string corePath)
|
||||
{
|
||||
//TODO: codepath just for inrospection (lighter weight; no speex, no controls, etc.)
|
||||
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
_SyncSettings = new SyncSettings();
|
||||
CoreComm = nextComm;
|
||||
|
||||
string dllPath = Path.Combine(CoreComm.CoreFileProvider.DllPath(), "LibretroBridge.dll");
|
||||
api = new LibretroApi(dllPath, corePath);
|
||||
|
||||
if (api.comm->env.retro_api_version != 1)
|
||||
throw new InvalidOperationException("Unsupported Libretro API version (or major error in interop)");
|
||||
|
||||
//SO: I think I need these paths set before I call retro_set_environment
|
||||
//and I need retro_set_environment set so I can find out if the core supports no-game
|
||||
//therefore, I need a complete environment (including pathing) before I can complete my introspection of the core.
|
||||
//Sucky, but that's life.
|
||||
//I dont even know for sure what paths I should use until... (what?)
|
||||
|
||||
|
||||
//not sure about each of these.. but we'll be doing things different than retroarch.
|
||||
//i think this may play better with our model [although we might could use a different save directory]
|
||||
api.CopyAscii(LibretroApi.BufId.SystemDirectory, Path.GetDirectoryName(corePath));
|
||||
api.CopyAscii(LibretroApi.BufId.SaveDirectory, Path.GetDirectoryName(corePath));
|
||||
api.CopyAscii(LibretroApi.BufId.CoreDirectory, Path.GetDirectoryName(corePath));
|
||||
api.CopyAscii(LibretroApi.BufId.CoreAssetsDirectory, Path.GetDirectoryName(corePath));
|
||||
|
||||
api.CMD_SetEnvironment();
|
||||
|
||||
//TODO: IT'S A BOWL OF SPAGHETTI! I KNOW, IM GOING TO FIX IT
|
||||
api.core = this;
|
||||
|
||||
Description = api.CalculateDescription();
|
||||
|
||||
ControllerDefinition = CreateControllerDefinition(_SyncSettings);
|
||||
}
|
||||
|
||||
bool disposed = false;
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
|
||||
//TODO
|
||||
//api.CMD_unload_cartridge();
|
||||
//api.CMD_term();
|
||||
|
||||
if(resampler != null)
|
||||
resampler.Dispose();
|
||||
|
||||
api.Dispose();
|
||||
|
||||
if(vidBufferHandle.IsAllocated)
|
||||
vidBufferHandle.Free();
|
||||
}
|
||||
|
||||
public RetroDescription Description { get; private set; }
|
||||
|
||||
public bool LoadData(byte[] data, string id)
|
||||
{
|
||||
bool ret = api.CMD_LoadData(data, id);
|
||||
LoadHandler();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool LoadPath(string path)
|
||||
{
|
||||
bool ret = api.CMD_LoadPath(path);
|
||||
LoadHandler();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool LoadNoGame()
|
||||
{
|
||||
bool ret = api.CMD_LoadNoGame();
|
||||
LoadHandler();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LoadHandler()
|
||||
{
|
||||
//this stuff can only happen after the game is loaded
|
||||
|
||||
//allocate a video buffer which will definitely be large enough
|
||||
SetVideoBuffer((int)api.comm->env.retro_system_av_info.geometry.base_width, (int)api.comm->env.retro_system_av_info.geometry.base_height);
|
||||
vidBuffer = new int[api.comm->env.retro_system_av_info.geometry.max_width * api.comm->env.retro_system_av_info.geometry.max_height];
|
||||
vidBufferHandle = GCHandle.Alloc(vidBuffer, GCHandleType.Pinned);
|
||||
api.comm->env.fb_bufptr = (int*)vidBufferHandle.AddrOfPinnedObject().ToPointer();
|
||||
//TODO: latch DAR? we may want to change it synchronously, or something
|
||||
|
||||
//TODO - libretro cores can return a varying serialize size over time. I tried to get them to write it in the docs...
|
||||
//UPDATE: well, they wrote in the docs that they CANT. they can ask the frontend if it's supported. (we wont support it unless we have to)
|
||||
savebuff = new byte[api.comm->env.retro_serialize_size];
|
||||
savebuff2 = new byte[savebuff.Length + 13];
|
||||
}
|
||||
|
||||
public IEmulatorServiceProvider ServiceProvider { get; private set; }
|
||||
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
return new Dictionary<string, RegisterValue>();
|
||||
}
|
||||
|
||||
public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
|
||||
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
|
||||
|
||||
public ITraceable Tracer { get; private set; }
|
||||
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
|
||||
|
||||
public bool CanStep(StepType type) { return false; }
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type) { throw new NotImplementedException(); }
|
||||
[FeatureNotImplemented]
|
||||
public void SetCpuRegister(string register, int value) { throw new NotImplementedException(); }
|
||||
[FeatureNotImplemented]
|
||||
public int TotalExecutedCycles { get { throw new NotImplementedException(); } }
|
||||
|
||||
public void FrameAdvance(bool render, bool rendersound)
|
||||
{
|
||||
api.CMD_Run();
|
||||
}
|
||||
|
||||
GCHandle vidBufferHandle;
|
||||
int[] vidBuffer;
|
||||
int vidWidth = -1, vidHeight = -1;
|
||||
|
||||
private void SetVideoBuffer(int width, int height)
|
||||
{
|
||||
//actually, we've already allocated a buffer with the given maximum size
|
||||
if (vidWidth == width && vidHeight == height) return;
|
||||
vidWidth = width;
|
||||
vidHeight = height;
|
||||
}
|
||||
|
||||
//video provider
|
||||
int IVideoProvider.BackgroundColor { get { return 0; } }
|
||||
int[] IVideoProvider.GetVideoBuffer() { return vidBuffer; }
|
||||
|
||||
public int VirtualWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
var dar = api.AVInfo.geometry.aspect_ratio;
|
||||
if(dar<=0)
|
||||
return vidWidth;
|
||||
else if (dar > 1.0f)
|
||||
return (int)(vidHeight * dar);
|
||||
else
|
||||
return vidWidth;
|
||||
}
|
||||
}
|
||||
public int VirtualHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
var dar = api.AVInfo.geometry.aspect_ratio;
|
||||
if(dar<=0)
|
||||
return vidHeight;
|
||||
if (dar < 1.0f)
|
||||
return (int)(vidWidth / dar);
|
||||
else
|
||||
return vidHeight;
|
||||
}
|
||||
}
|
||||
|
||||
int IVideoProvider.BufferWidth { get { return vidWidth; } }
|
||||
int IVideoProvider.BufferHeight { get { return vidHeight; } }
|
||||
|
||||
public static ControllerDefinition CreateControllerDefinition(SyncSettings syncSettings)
|
||||
{
|
||||
ControllerDefinition definition = new ControllerDefinition();
|
||||
definition.Name = "LibRetro Controls"; // <-- for compatibility
|
||||
|
||||
foreach(var item in new[] {
|
||||
"P1 {0} Up", "P1 {0} Down", "P1 {0} Left", "P1 {0} Right", "P1 {0} Select", "P1 {0} Start", "P1 {0} Y", "P1 {0} B", "P1 {0} X", "P1 {0} A", "P1 {0} L", "P1 {0} R",
|
||||
"P2 {0} Up", "P2 {0} Down", "P2 {0} Left", "P2 {0} Right", "P2 {0} Select", "P2 {0} Start", "P2 {0} Y", "P2 {0} B", "P2 {0} X", "P2 {0} A", "P2 {0} L", "P2 {0} R",
|
||||
})
|
||||
definition.BoolButtons.Add(string.Format(item,"RetroPad"));
|
||||
|
||||
definition.BoolButtons.Add("Pointer Pressed"); //TODO: this isnt showing up in the binding panel. I dont want to find out why.
|
||||
definition.FloatControls.Add("Pointer X");
|
||||
definition.FloatControls.Add("Pointer Y");
|
||||
definition.FloatRanges.Add(new ControllerDefinition.FloatRange(-32767, 0, 32767));
|
||||
definition.FloatRanges.Add(new ControllerDefinition.FloatRange(-32767, 0, 32767));
|
||||
|
||||
foreach (var key in new[]{
|
||||
"Key Backspace", "Key Tab", "Key Clear", "Key Return", "Key Pause", "Key Escape",
|
||||
"Key Space", "Key Exclaim", "Key QuoteDbl", "Key Hash", "Key Dollar", "Key Ampersand", "Key Quote", "Key LeftParen", "Key RightParen", "Key Asterisk", "Key Plus", "Key Comma", "Key Minus", "Key Period", "Key Slash",
|
||||
"Key 0", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9",
|
||||
"Key Colon", "Key Semicolon", "Key Less", "Key Equals", "Key Greater", "Key Question", "Key At", "Key LeftBracket", "Key Backslash", "Key RightBracket", "Key Caret", "Key Underscore", "Key Backquote",
|
||||
"Key A", "Key B", "Key C", "Key D", "Key E", "Key F", "Key G", "Key H", "Key I", "Key J", "Key K", "Key L", "Key M", "Key N", "Key O", "Key P", "Key Q", "Key R", "Key S", "Key T", "Key U", "Key V", "Key W", "Key X", "Key Y", "Key Z",
|
||||
"Key Delete",
|
||||
"Key KP0", "Key KP1", "Key KP2", "Key KP3", "Key KP4", "Key KP5", "Key KP6", "Key KP7", "Key KP8", "Key KP9",
|
||||
"Key KP_Period", "Key KP_Divide", "Key KP_Multiply", "Key KP_Minus", "Key KP_Plus", "Key KP_Enter", "Key KP_Equals",
|
||||
"Key Up", "Key Down", "Key Right", "Key Left", "Key Insert", "Key Home", "Key End", "Key PageUp", "Key PageDown",
|
||||
"Key F1", "Key F2", "Key F3", "Key F4", "Key F5", "Key F6", "Key F7", "Key F8", "Key F9", "Key F10", "Key F11", "Key F12", "Key F13", "Key F14", "Key F15",
|
||||
"Key NumLock", "Key CapsLock", "Key ScrollLock", "Key RShift", "Key LShift", "Key RCtrl", "Key LCtrl", "Key RAlt", "Key LAlt", "Key RMeta", "Key LMeta", "Key LSuper", "Key RSuper", "Key Mode", "Key Compose",
|
||||
"Key Help", "Key Print", "Key SysReq", "Key Break", "Key Menu", "Key Power", "Key Euro", "Key Undo"
|
||||
})
|
||||
{
|
||||
definition.BoolButtons.Add(key);
|
||||
definition.CategoryLabels[key] = "RetroKeyboard";
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get; private set; }
|
||||
public IController Controller { get; set; }
|
||||
|
||||
int timeFrameCounter;
|
||||
public int Frame { get { return timeFrameCounter; } set { timeFrameCounter = value; } }
|
||||
public int LagCount { get; set; }
|
||||
public bool IsLagFrame { get; set; }
|
||||
public string SystemId { get { return "Libretro"; } }
|
||||
public bool DeterministicEmulation { get { return false; } }
|
||||
public string BoardName { get; private set; }
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
//TODO
|
||||
return new byte[] { };
|
||||
}
|
||||
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
timeFrameCounter = 0;
|
||||
LagCount = 0;
|
||||
IsLagFrame = false;
|
||||
}
|
||||
|
||||
#region savestates
|
||||
|
||||
private byte[] savebuff, savebuff2;
|
||||
|
||||
public void SaveStateText(System.IO.TextWriter writer)
|
||||
{
|
||||
var temp = SaveStateBinary();
|
||||
temp.SaveAsHex(writer);
|
||||
}
|
||||
|
||||
public void LoadStateText(System.IO.TextReader reader)
|
||||
{
|
||||
string hex = reader.ReadLine();
|
||||
byte[] state = new byte[hex.Length / 2];
|
||||
state.ReadFromHex(hex);
|
||||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
}
|
||||
|
||||
public void SaveStateBinary(System.IO.BinaryWriter writer)
|
||||
{
|
||||
Console.WriteLine(api.comm->env.retro_serialize_size);
|
||||
api.CMD_Serialize(savebuff);
|
||||
writer.Write(savebuff.Length);
|
||||
writer.Write(savebuff);
|
||||
// other variables
|
||||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(System.IO.BinaryReader reader)
|
||||
{
|
||||
int newlen = reader.ReadInt32();
|
||||
if (newlen > savebuff.Length)
|
||||
throw new Exception("Unexpected buffer size");
|
||||
reader.Read(savebuff, 0, newlen);
|
||||
api.CMD_Unserialize(savebuff);
|
||||
// other variables
|
||||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new System.IO.MemoryStream(savebuff2, true);
|
||||
var bw = new System.IO.BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return savebuff2;
|
||||
}
|
||||
|
||||
public bool BinarySaveStatesPreferred { get { return true; } }
|
||||
|
||||
#endregion
|
||||
|
||||
public CoreComm CoreComm { get; private set; }
|
||||
|
||||
SpeexResampler resampler;
|
||||
|
||||
void InitAudio()
|
||||
{
|
||||
resampler = new SpeexResampler(6, 64081, 88200, 32041, 44100);
|
||||
}
|
||||
|
||||
void snes_audio_sample(ushort left, ushort right)
|
||||
{
|
||||
resampler.EnqueueSample((short)left, (short)right);
|
||||
}
|
||||
|
||||
} //class
|
||||
|
||||
} //namespace
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
|
||||
partial class LibretroCore
|
||||
{
|
||||
Settings _Settings = new Settings();
|
||||
SyncSettings _SyncSettings;
|
||||
|
||||
public class SyncSettings
|
||||
{
|
||||
public SyncSettings Clone()
|
||||
{
|
||||
return JsonConvert.DeserializeObject<SyncSettings>(JsonConvert.SerializeObject(this));
|
||||
}
|
||||
|
||||
public SyncSettings()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Settings
|
||||
{
|
||||
public void Validate()
|
||||
{
|
||||
}
|
||||
|
||||
public Settings()
|
||||
{
|
||||
SettingsUtil.SetDefaultValues(this);
|
||||
}
|
||||
|
||||
public Settings Clone()
|
||||
{
|
||||
return (Settings)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
public Settings GetSettings()
|
||||
{
|
||||
return _Settings.Clone();
|
||||
}
|
||||
|
||||
public SyncSettings GetSyncSettings()
|
||||
{
|
||||
return _SyncSettings.Clone();
|
||||
}
|
||||
|
||||
public bool PutSettings(Settings o)
|
||||
{
|
||||
_Settings.Validate();
|
||||
_Settings = o;
|
||||
|
||||
//TODO - store settings into core? or we can just keep doing it before frameadvance
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PutSyncSettings(SyncSettings o)
|
||||
{
|
||||
bool reboot = false;
|
||||
|
||||
//we could do it this way roughly if we need to
|
||||
//if(JsonConvert.SerializeObject(o.FIOConfig) != JsonConvert.SerializeObject(_SyncSettings.FIOConfig)
|
||||
|
||||
_SyncSettings = o;
|
||||
|
||||
return reboot;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
public class RetroDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// String containing a friendly display name for the core, but we probably shouldn't use this. I decided it's better to get the user used to using filenames as core 'codenames' instead.
|
||||
/// </summary>
|
||||
public string LibraryName;
|
||||
|
||||
/// <summary>
|
||||
/// String containing a friendly version number for the core library
|
||||
/// </summary>
|
||||
public string LibraryVersion;
|
||||
|
||||
/// <summary>
|
||||
/// List of extensions as "sfc|smc|fig" which this core accepts.
|
||||
/// </summary>
|
||||
public string ValidExtensions;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the core needs roms to be specified as paths (can't take rom data buffersS)
|
||||
/// </summary>
|
||||
public bool NeedsRomAsPath;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the core needs roms stored as archives (e.g. arcade roms). We probably shouldn't employ the dearchiver prompts when opening roms for these cores.
|
||||
/// </summary>
|
||||
public bool NeedsArchives;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the core can be run without a game provided (e.g. stand-alone games, like 2048)
|
||||
/// </summary>
|
||||
public bool SupportsNoGame;
|
||||
|
||||
/// <summary>
|
||||
/// Variables defined by the core
|
||||
/// </summary>
|
||||
public Dictionary<string, VariableDescription> Variables = new Dictionary<string, VariableDescription>();
|
||||
}
|
||||
|
||||
public class VariableDescription
|
||||
{
|
||||
public string Name;
|
||||
public string Description;
|
||||
public string[] Options;
|
||||
public string DefaultOption { get { return Options[0]; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} ({1}) = ({2})", Name, Description, string.Join("|", Options));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Libretro
|
||||
{
|
||||
partial class LibretroCore
|
||||
{
|
||||
//meanings (they are kind of hazy, but once we're done implementing this it will be completely defined by example)
|
||||
//port = console physical port?
|
||||
//device = logical device type
|
||||
//index = sub device index? (multitap?)
|
||||
//id = button id (or key id)
|
||||
public short CB_InputState(uint port, uint device, uint index, uint id)
|
||||
{
|
||||
//helpful debugging
|
||||
//Console.WriteLine("{0} {1} {2} {3}", port, device, index, id);
|
||||
|
||||
switch ((LibretroApi.RETRO_DEVICE)device)
|
||||
{
|
||||
case LibretroApi.RETRO_DEVICE.POINTER:
|
||||
{
|
||||
switch ((LibretroApi.RETRO_DEVICE_ID_POINTER)id)
|
||||
{
|
||||
case LibretroApi.RETRO_DEVICE_ID_POINTER.X: return (short)Controller.GetFloat("Pointer X");
|
||||
case LibretroApi.RETRO_DEVICE_ID_POINTER.Y: return (short)Controller.GetFloat("Pointer Y");
|
||||
case LibretroApi.RETRO_DEVICE_ID_POINTER.PRESSED: return (short)(Controller.IsPressed("Pointer Pressed") ? 1 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case LibretroApi.RETRO_DEVICE.KEYBOARD:
|
||||
{
|
||||
string button = "";
|
||||
switch ((LibretroApi.RETRO_KEY)id)
|
||||
{
|
||||
case LibretroApi.RETRO_KEY.BACKSPACE: button = "Backspace"; break;
|
||||
case LibretroApi.RETRO_KEY.TAB: button = "Tab"; break;
|
||||
case LibretroApi.RETRO_KEY.CLEAR: button = "Clear"; break;
|
||||
case LibretroApi.RETRO_KEY.RETURN: button = "Return"; break;
|
||||
case LibretroApi.RETRO_KEY.PAUSE: button = "Pause"; break;
|
||||
case LibretroApi.RETRO_KEY.ESCAPE: button = "Escape"; break;
|
||||
case LibretroApi.RETRO_KEY.SPACE: button = "Space"; break;
|
||||
case LibretroApi.RETRO_KEY.EXCLAIM: button = "Exclaim"; break;
|
||||
case LibretroApi.RETRO_KEY.QUOTEDBL: button = "QuoteDbl"; break;
|
||||
case LibretroApi.RETRO_KEY.HASH: button = "Hash"; break;
|
||||
case LibretroApi.RETRO_KEY.DOLLAR: button = "Dollar"; break;
|
||||
case LibretroApi.RETRO_KEY.AMPERSAND: button = "Ampersand"; break;
|
||||
case LibretroApi.RETRO_KEY.QUOTE: button = "Quote"; break;
|
||||
case LibretroApi.RETRO_KEY.LEFTPAREN: button = "LeftParen"; break;
|
||||
case LibretroApi.RETRO_KEY.RIGHTPAREN: button = "RightParen"; break;
|
||||
case LibretroApi.RETRO_KEY.ASTERISK: button = "Asterisk"; break;
|
||||
case LibretroApi.RETRO_KEY.PLUS: button = "Plus"; break;
|
||||
case LibretroApi.RETRO_KEY.COMMA: button = "Comma"; break;
|
||||
case LibretroApi.RETRO_KEY.MINUS: button = "Minus"; break;
|
||||
case LibretroApi.RETRO_KEY.PERIOD: button = "Period"; break;
|
||||
case LibretroApi.RETRO_KEY.SLASH: button = "Slash"; break;
|
||||
case LibretroApi.RETRO_KEY._0: button = "0"; break;
|
||||
case LibretroApi.RETRO_KEY._1: button = "1"; break;
|
||||
case LibretroApi.RETRO_KEY._2: button = "2"; break;
|
||||
case LibretroApi.RETRO_KEY._3: button = "3"; break;
|
||||
case LibretroApi.RETRO_KEY._4: button = "4"; break;
|
||||
case LibretroApi.RETRO_KEY._5: button = "5"; break;
|
||||
case LibretroApi.RETRO_KEY._6: button = "6"; break;
|
||||
case LibretroApi.RETRO_KEY._7: button = "7"; break;
|
||||
case LibretroApi.RETRO_KEY._8: button = "8"; break;
|
||||
case LibretroApi.RETRO_KEY._9: button = "9"; break;
|
||||
case LibretroApi.RETRO_KEY.COLON: button = "Colon"; break;
|
||||
case LibretroApi.RETRO_KEY.SEMICOLON: button = "Semicolon"; break;
|
||||
case LibretroApi.RETRO_KEY.LESS: button = "Less"; break;
|
||||
case LibretroApi.RETRO_KEY.EQUALS: button = "Equals"; break;
|
||||
case LibretroApi.RETRO_KEY.GREATER: button = "Greater"; break;
|
||||
case LibretroApi.RETRO_KEY.QUESTION: button = "Question"; break;
|
||||
case LibretroApi.RETRO_KEY.AT: button = "At"; break;
|
||||
case LibretroApi.RETRO_KEY.LEFTBRACKET: button = "LeftBracket"; break;
|
||||
case LibretroApi.RETRO_KEY.BACKSLASH: button = "Backslash"; break;
|
||||
case LibretroApi.RETRO_KEY.RIGHTBRACKET: button = "RightBracket"; break;
|
||||
case LibretroApi.RETRO_KEY.CARET: button = "Caret"; break;
|
||||
case LibretroApi.RETRO_KEY.UNDERSCORE: button = "Underscore"; break;
|
||||
case LibretroApi.RETRO_KEY.BACKQUOTE: button = "Backquote"; break;
|
||||
case LibretroApi.RETRO_KEY.a: button = "A"; break;
|
||||
case LibretroApi.RETRO_KEY.b: button = "B"; break;
|
||||
case LibretroApi.RETRO_KEY.c: button = "C"; break;
|
||||
case LibretroApi.RETRO_KEY.d: button = "D"; break;
|
||||
case LibretroApi.RETRO_KEY.e: button = "E"; break;
|
||||
case LibretroApi.RETRO_KEY.f: button = "F"; break;
|
||||
case LibretroApi.RETRO_KEY.g: button = "G"; break;
|
||||
case LibretroApi.RETRO_KEY.h: button = "H"; break;
|
||||
case LibretroApi.RETRO_KEY.i: button = "I"; break;
|
||||
case LibretroApi.RETRO_KEY.j: button = "J"; break;
|
||||
case LibretroApi.RETRO_KEY.k: button = "K"; break;
|
||||
case LibretroApi.RETRO_KEY.l: button = "L"; break;
|
||||
case LibretroApi.RETRO_KEY.m: button = "M"; break;
|
||||
case LibretroApi.RETRO_KEY.n: button = "N"; break;
|
||||
case LibretroApi.RETRO_KEY.o: button = "O"; break;
|
||||
case LibretroApi.RETRO_KEY.p: button = "P"; break;
|
||||
case LibretroApi.RETRO_KEY.q: button = "Q"; break;
|
||||
case LibretroApi.RETRO_KEY.r: button = "R"; break;
|
||||
case LibretroApi.RETRO_KEY.s: button = "S"; break;
|
||||
case LibretroApi.RETRO_KEY.t: button = "T"; break;
|
||||
case LibretroApi.RETRO_KEY.u: button = "U"; break;
|
||||
case LibretroApi.RETRO_KEY.v: button = "V"; break;
|
||||
case LibretroApi.RETRO_KEY.w: button = "W"; break;
|
||||
case LibretroApi.RETRO_KEY.x: button = "X"; break;
|
||||
case LibretroApi.RETRO_KEY.y: button = "Y"; break;
|
||||
case LibretroApi.RETRO_KEY.z: button = "Z"; break;
|
||||
case LibretroApi.RETRO_KEY.DELETE: button = "Delete"; break;
|
||||
|
||||
case LibretroApi.RETRO_KEY.KP0: button = "KP0"; break;
|
||||
case LibretroApi.RETRO_KEY.KP1: button = "KP1"; break;
|
||||
case LibretroApi.RETRO_KEY.KP2: button = "KP2"; break;
|
||||
case LibretroApi.RETRO_KEY.KP3: button = "KP3"; break;
|
||||
case LibretroApi.RETRO_KEY.KP4: button = "KP4"; break;
|
||||
case LibretroApi.RETRO_KEY.KP5: button = "KP5"; break;
|
||||
case LibretroApi.RETRO_KEY.KP6: button = "KP6"; break;
|
||||
case LibretroApi.RETRO_KEY.KP7: button = "KP7"; break;
|
||||
case LibretroApi.RETRO_KEY.KP8: button = "KP8"; break;
|
||||
case LibretroApi.RETRO_KEY.KP9: button = "KP9"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_PERIOD: button = "KP_Period"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_DIVIDE: button = "KP_Divide"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_MULTIPLY: button = "KP_Multiply"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_MINUS: button = "KP_Minus"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_PLUS: button = "KP_Plus"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_ENTER: button = "KP_Enter"; break;
|
||||
case LibretroApi.RETRO_KEY.KP_EQUALS: button = "KP_Equals"; break;
|
||||
|
||||
case LibretroApi.RETRO_KEY.UP: button = "Up"; break;
|
||||
case LibretroApi.RETRO_KEY.DOWN: button = "Down"; break;
|
||||
case LibretroApi.RETRO_KEY.RIGHT: button = "Right"; break;
|
||||
case LibretroApi.RETRO_KEY.LEFT: button = "Left"; break;
|
||||
case LibretroApi.RETRO_KEY.INSERT: button = "Insert"; break;
|
||||
case LibretroApi.RETRO_KEY.HOME: button = "Home"; break;
|
||||
case LibretroApi.RETRO_KEY.END: button = "End"; break;
|
||||
case LibretroApi.RETRO_KEY.PAGEUP: button = "PageUp"; break;
|
||||
case LibretroApi.RETRO_KEY.PAGEDOWN: button = "PageDown"; break;
|
||||
|
||||
case LibretroApi.RETRO_KEY.F1: button = "F1"; break;
|
||||
case LibretroApi.RETRO_KEY.F2: button = "F2"; break;
|
||||
case LibretroApi.RETRO_KEY.F3: button = "F3"; break;
|
||||
case LibretroApi.RETRO_KEY.F4: button = "F4"; break;
|
||||
case LibretroApi.RETRO_KEY.F5: button = "F5"; break;
|
||||
case LibretroApi.RETRO_KEY.F6: button = "F6"; break;
|
||||
case LibretroApi.RETRO_KEY.F7: button = "F7"; break;
|
||||
case LibretroApi.RETRO_KEY.F8: button = "F8"; break;
|
||||
case LibretroApi.RETRO_KEY.F9: button = "F9"; break;
|
||||
case LibretroApi.RETRO_KEY.F10: button = "F10"; break;
|
||||
case LibretroApi.RETRO_KEY.F11: button = "F11"; break;
|
||||
case LibretroApi.RETRO_KEY.F12: button = "F12"; break;
|
||||
case LibretroApi.RETRO_KEY.F13: button = "F13"; break;
|
||||
case LibretroApi.RETRO_KEY.F14: button = "F14"; break;
|
||||
case LibretroApi.RETRO_KEY.F15: button = "F15"; break;
|
||||
|
||||
case LibretroApi.RETRO_KEY.NUMLOCK: button = "NumLock"; break;
|
||||
case LibretroApi.RETRO_KEY.CAPSLOCK: button = "CapsLock"; break;
|
||||
case LibretroApi.RETRO_KEY.SCROLLOCK: button = "ScrollLock"; break;
|
||||
case LibretroApi.RETRO_KEY.RSHIFT: button = "RShift"; break;
|
||||
case LibretroApi.RETRO_KEY.LSHIFT: button = "LShift"; break;
|
||||
case LibretroApi.RETRO_KEY.RCTRL: button = "RCtrl"; break;
|
||||
case LibretroApi.RETRO_KEY.LCTRL: button = "LCtrl"; break;
|
||||
case LibretroApi.RETRO_KEY.RALT: button = "RAlt"; break;
|
||||
case LibretroApi.RETRO_KEY.LALT: button = "LAlt"; break;
|
||||
case LibretroApi.RETRO_KEY.RMETA: button = "RMeta"; break;
|
||||
case LibretroApi.RETRO_KEY.LMETA: button = "LMeta"; break;
|
||||
case LibretroApi.RETRO_KEY.LSUPER: button = "LSuper"; break;
|
||||
case LibretroApi.RETRO_KEY.RSUPER: button = "RSuper"; break;
|
||||
case LibretroApi.RETRO_KEY.MODE: button = "Mode"; break;
|
||||
case LibretroApi.RETRO_KEY.COMPOSE: button = "Compose"; break;
|
||||
|
||||
case LibretroApi.RETRO_KEY.HELP: button = "Help"; break;
|
||||
case LibretroApi.RETRO_KEY.PRINT: button = "Print"; break;
|
||||
case LibretroApi.RETRO_KEY.SYSREQ: button = "SysReq"; break;
|
||||
case LibretroApi.RETRO_KEY.BREAK: button = "Break"; break;
|
||||
case LibretroApi.RETRO_KEY.MENU: button = "Menu"; break;
|
||||
case LibretroApi.RETRO_KEY.POWER: button = "Power"; break;
|
||||
case LibretroApi.RETRO_KEY.EURO: button = "Euro"; break;
|
||||
case LibretroApi.RETRO_KEY.UNDO: button = "Undo"; break;
|
||||
}
|
||||
|
||||
return (short)(Controller.IsPressed("Key " + button) ? 1 : 0);
|
||||
}
|
||||
|
||||
case LibretroApi.RETRO_DEVICE.JOYPAD:
|
||||
{
|
||||
//The JOYPAD is sometimes called RetroPad (and we'll call it that in user-facing stuff cos retroarch does)
|
||||
//It is essentially a Super Nintendo controller, but with additional L2/R2/L3/R3 buttons, similar to a PS1 DualShock.
|
||||
|
||||
string button = "";
|
||||
switch ((LibretroApi.RETRO_DEVICE_ID_JOYPAD)id)
|
||||
{
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.A: button = "A"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.B: button = "B"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.X: button = "X"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.Y: button = "Y"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.UP: button = "Up"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.DOWN: button = "Down"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.LEFT: button = "Left"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.RIGHT: button = "Right"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.L: button = "L"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.R: button = "R"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.SELECT: button = "Select"; break;
|
||||
case LibretroApi.RETRO_DEVICE_ID_JOYPAD.START: button = "Start"; break;
|
||||
}
|
||||
|
||||
return (short)(GetButton(port+1, "RetroPad", button) ? 1 : 0);
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetButton(uint pnum, string type, string button)
|
||||
{
|
||||
string key = string.Format("P{0} {1} {2}", pnum, type, button);
|
||||
bool b = Controller.IsPressed(key);
|
||||
if (b == true)
|
||||
{
|
||||
return true; //debugging placeholder
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue