pce: basic rom loading and playing works
This commit is contained in:
parent
d82d67996f
commit
69d3dbc35f
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BizHawk.Common
|
||||||
|
{
|
||||||
|
public static class Mershul
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// TODO: Update to a version of .nyet that includes this
|
||||||
|
/// </summary>
|
||||||
|
public static unsafe string PtrToStringUtf8(IntPtr p)
|
||||||
|
{
|
||||||
|
byte* b = (byte*)p;
|
||||||
|
int len = 0;
|
||||||
|
while (*b++ != 0)
|
||||||
|
len++;
|
||||||
|
return Encoding.UTF8.GetString((byte*)p, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -195,10 +195,12 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsSpecialReadonlySection(Section<ulong> sec)
|
private bool IsSpecialReadonlySection(Section<ulong> sec)
|
||||||
{
|
{
|
||||||
|
// TODO: I don't think there are any more relro sections, right?
|
||||||
return sec.Name.Contains(".rel.ro")
|
return sec.Name.Contains(".rel.ro")
|
||||||
|| sec.Name.StartsWith(".got")
|
|| sec.Name.StartsWith(".got")
|
||||||
|| sec.Name == ".init_array"
|
|| sec.Name == ".init_array"
|
||||||
|| sec.Name == ".fini_array"
|
|| sec.Name == ".fini_array"
|
||||||
|
|| sec.Name == ".tbss"
|
||||||
|| sec == _imports
|
|| sec == _imports
|
||||||
|| sec == _sealed;
|
|| sec == _sealed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using BizHawk.BizInvoke;
|
using BizHawk.BizInvoke;
|
||||||
|
using BizHawk.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
|
@ -36,8 +37,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "__w_debug_puts")]
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "__w_debug_puts")]
|
||||||
public void DebugPuts(IntPtr s)
|
public void DebugPuts(IntPtr s)
|
||||||
{
|
{
|
||||||
// TODO: Should be PtrToStringUtf8
|
Console.WriteLine(Mershul.PtrToStringUtf8(s));
|
||||||
Console.WriteLine(Marshal.PtrToStringAnsi(s));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using BizHawk.BizInvoke;
|
using BizHawk.BizInvoke;
|
||||||
|
using BizHawk.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
|
@ -59,7 +60,11 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// true to skip video rendering
|
/// true to skip video rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SkipRendering;
|
public short SkipRendering;
|
||||||
|
/// <summary>
|
||||||
|
/// true to skip audion rendering
|
||||||
|
/// </summary>
|
||||||
|
public short SkipSoundening;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// a single command to run at the start of this frame
|
/// a single command to run at the start of this frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -129,9 +134,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public IntPtr _defaultDeviceShortName;
|
public IntPtr _defaultDeviceShortName;
|
||||||
public uint NumDevices;
|
public uint NumDevices;
|
||||||
|
|
||||||
public string ShortName => Marshal.PtrToStringAnsi(_shortName);
|
public string ShortName => Mershul.PtrToStringUtf8(_shortName);
|
||||||
public string FullName => Marshal.PtrToStringAnsi(_fullName);
|
public string FullName => Mershul.PtrToStringUtf8(_fullName);
|
||||||
public string DefaultDeviceShortName => Marshal.PtrToStringAnsi(_defaultDeviceShortName);
|
public string DefaultDeviceShortName => Mershul.PtrToStringUtf8(_defaultDeviceShortName);
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct NDeviceInfo
|
public struct NDeviceInfo
|
||||||
|
@ -143,9 +148,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public uint ByteLength;
|
public uint ByteLength;
|
||||||
public uint NumInputs;
|
public uint NumInputs;
|
||||||
|
|
||||||
public string ShortName => Marshal.PtrToStringAnsi(_shortName);
|
public string ShortName => Mershul.PtrToStringUtf8(_shortName);
|
||||||
public string FullName => Marshal.PtrToStringAnsi(_fullName);
|
public string FullName => Mershul.PtrToStringUtf8(_fullName);
|
||||||
public string Description => Marshal.PtrToStringAnsi(_description);
|
public string Description => Mershul.PtrToStringUtf8(_description);
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct NInputInfo
|
public struct NInputInfo
|
||||||
|
@ -158,15 +163,15 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public AxisFlags Flags;
|
public AxisFlags Flags;
|
||||||
public byte BitSize;
|
public byte BitSize;
|
||||||
|
|
||||||
public string SettingName => Marshal.PtrToStringAnsi(_settingName);
|
public string SettingName => Mershul.PtrToStringUtf8(_settingName);
|
||||||
public string Name => Marshal.PtrToStringAnsi(_name);
|
public string Name => Mershul.PtrToStringUtf8(_name);
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct NButtonInfo
|
public struct NButtonInfo
|
||||||
{
|
{
|
||||||
public IntPtr _excludeName;
|
public IntPtr _excludeName;
|
||||||
|
|
||||||
public string ExcludeName => Marshal.PtrToStringAnsi(_excludeName);
|
public string ExcludeName => Mershul.PtrToStringUtf8(_excludeName);
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct NAxisInfo
|
public struct NAxisInfo
|
||||||
|
@ -176,10 +181,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public IntPtr _nameNeg;
|
public IntPtr _nameNeg;
|
||||||
public IntPtr _namePos;
|
public IntPtr _namePos;
|
||||||
|
|
||||||
public string SettingsNameNeg => Marshal.PtrToStringAnsi(_settingsNameNeg);
|
public string SettingsNameNeg => Mershul.PtrToStringUtf8(_settingsNameNeg);
|
||||||
public string SettingsNamePos => Marshal.PtrToStringAnsi(_settingsNamePos);
|
public string SettingsNamePos => Mershul.PtrToStringUtf8(_settingsNamePos);
|
||||||
public string NameNeg => Marshal.PtrToStringAnsi(_nameNeg);
|
public string NameNeg => Mershul.PtrToStringUtf8(_nameNeg);
|
||||||
public string NamePos => Marshal.PtrToStringAnsi(_namePos);
|
public string NamePos => Mershul.PtrToStringUtf8(_namePos);
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct NSwitchInfo
|
public struct NSwitchInfo
|
||||||
|
@ -193,9 +198,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public IntPtr _name;
|
public IntPtr _name;
|
||||||
public IntPtr _description;
|
public IntPtr _description;
|
||||||
|
|
||||||
public string SettingName => Marshal.PtrToStringAnsi(_settingName);
|
public string SettingName => Mershul.PtrToStringUtf8(_settingName);
|
||||||
public string Name => Marshal.PtrToStringAnsi(_name);
|
public string Name => Mershul.PtrToStringUtf8(_name);
|
||||||
public string Description => Marshal.PtrToStringAnsi(_description);
|
public string Description => Mershul.PtrToStringUtf8(_description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
@ -210,8 +215,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
public int Color; // (msb)0RGB(lsb), -1 for unused.
|
public int Color; // (msb)0RGB(lsb), -1 for unused.
|
||||||
public int _Padding;
|
public int _Padding;
|
||||||
|
|
||||||
public string ShortName => Marshal.PtrToStringAnsi(_shortName);
|
public string ShortName => Mershul.PtrToStringUtf8(_shortName);
|
||||||
public string Name => Marshal.PtrToStringAnsi(_name);
|
public string Name => Mershul.PtrToStringUtf8(_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +241,10 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
[BizImport(CC, Compatibility = true)]
|
[BizImport(CC, Compatibility = true)]
|
||||||
public abstract NAxisInfo* GetAxis(uint port, uint dev, uint input);
|
public abstract NAxisInfo* GetAxis(uint port, uint dev, uint input);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set what input devices we're going to use
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="devices">MUST end with a null string</param>
|
||||||
[BizImport(CC, Compatibility = true)]
|
[BizImport(CC, Compatibility = true)]
|
||||||
public abstract void SetInputDevices(string[] devices);
|
public abstract void SetInputDevices(string[] devices);
|
||||||
|
|
||||||
|
@ -249,7 +258,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public class SystemInfo
|
public struct SystemInfo
|
||||||
{
|
{
|
||||||
public int MaxWidth;
|
public int MaxWidth;
|
||||||
public int MaxHeight;
|
public int MaxHeight;
|
||||||
|
@ -260,6 +269,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
|
|
||||||
[BizImport(CC, Compatibility = true)]
|
[BizImport(CC, Compatibility = true)]
|
||||||
public abstract SystemInfo GetSystemInfo();
|
public abstract SystemInfo* GetSystemInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
public WaterboxMemoryDomain(MemoryArea m, IMonitor monitor)
|
public WaterboxMemoryDomain(MemoryArea m, IMonitor monitor)
|
||||||
{
|
{
|
||||||
Name = Marshal.PtrToStringAnsi(m.Name);
|
Name = Mershul.PtrToStringUtf8(m.Name);
|
||||||
EndianType = (m.Flags & MemoryDomainFlags.YugeEndian) != 0 ? Endian.Big : Endian.Little;
|
EndianType = (m.Flags & MemoryDomainFlags.YugeEndian) != 0 ? Endian.Big : Endian.Little;
|
||||||
_data = m.Data;
|
_data = m.Data;
|
||||||
Size = m.Size;
|
Size = m.Size;
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
{
|
||||||
|
unsafe partial class NymaCore
|
||||||
|
{
|
||||||
|
private ControllerAdapter _controllerAdapter;
|
||||||
|
private readonly byte[] _inputPortData = new byte[16 * 16];
|
||||||
|
|
||||||
|
private void InitControls()
|
||||||
|
{
|
||||||
|
_controllerAdapter = new ControllerAdapter(_nyma, new string[0]);
|
||||||
|
_nyma.SetInputDevices(_controllerAdapter.Devices);
|
||||||
|
ControllerDefinition = _controllerAdapter.Definition;
|
||||||
|
}
|
||||||
|
protected delegate void ControllerThunk(IController c, byte[] b);
|
||||||
|
|
||||||
|
protected class ControllerAdapter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// allowed number of input ports. must match native
|
||||||
|
/// </summary>
|
||||||
|
private const int MAX_PORTS = 16;
|
||||||
|
/// <summary>
|
||||||
|
/// total maximum bytes on each input port. must match native
|
||||||
|
/// </summary>
|
||||||
|
private const int MAX_PORT_DATA = 16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Device list suitable to pass back to the core
|
||||||
|
/// </summary>
|
||||||
|
public string[] Devices { get; }
|
||||||
|
public ControllerDefinition Definition { get; }
|
||||||
|
public ControllerAdapter(LibNymaCore core, string[] config)
|
||||||
|
{
|
||||||
|
var ret = new ControllerDefinition
|
||||||
|
{
|
||||||
|
Name = "TODO"
|
||||||
|
};
|
||||||
|
|
||||||
|
var finalDevices = new List<string>();
|
||||||
|
|
||||||
|
var numPorts = core.GetNumPorts();
|
||||||
|
if (numPorts > MAX_PORTS)
|
||||||
|
throw new InvalidOperationException($"Too many input ports");
|
||||||
|
for (uint port = 0, devByteStart = 0; port < numPorts; port++, devByteStart += MAX_PORT_DATA)
|
||||||
|
{
|
||||||
|
var portInfo = *core.GetPort(port);
|
||||||
|
var deviceName = port < config.Length ? config[port] : portInfo.DefaultDeviceShortName;
|
||||||
|
finalDevices.Add(deviceName);
|
||||||
|
|
||||||
|
var devices = Enumerable.Range(0, (int)portInfo.NumDevices)
|
||||||
|
.Select(i => new { Index = (uint)i, Device = *core.GetDevice(port, (uint)i) })
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var device = devices.FirstOrDefault(a => a.Device.ShortName == deviceName);
|
||||||
|
if (device == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Warn: unknown controller device {deviceName}");
|
||||||
|
device = devices.FirstOrDefault(a => a.Device.ShortName == portInfo.DefaultDeviceShortName);
|
||||||
|
if (device == null)
|
||||||
|
throw new InvalidOperationException($"Fail: unknown controller device {portInfo.DefaultDeviceShortName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceInfo = device.Device;
|
||||||
|
if (deviceInfo.ByteLength > MAX_PORT_DATA)
|
||||||
|
throw new InvalidOperationException($"Input device {deviceInfo.ShortName} uses more than {MAX_PORT_DATA} bytes");
|
||||||
|
var category = portInfo.FullName + " - " + deviceInfo.FullName;
|
||||||
|
|
||||||
|
var inputs = Enumerable.Range(0, (int)deviceInfo.NumInputs)
|
||||||
|
.Select(i => new { Index = i, Data = *core.GetInput(port, device.Index, (uint)i) })
|
||||||
|
.OrderBy(a => a.Data.ConfigOrder);
|
||||||
|
|
||||||
|
foreach (var input in inputs)
|
||||||
|
{
|
||||||
|
var inputInfo = input.Data;
|
||||||
|
var bitSize = (int)inputInfo.BitSize;
|
||||||
|
var bitOffset = (int)inputInfo.BitOffset;
|
||||||
|
var byteStart = devByteStart + bitOffset / 8;
|
||||||
|
bitOffset %= 8;
|
||||||
|
var name = $"P{port + 1} {inputInfo.Name}";
|
||||||
|
switch (inputInfo.Type)
|
||||||
|
{
|
||||||
|
case LibNymaCore.InputType.PADDING:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.BUTTON:
|
||||||
|
case LibNymaCore.InputType.BUTTON_CAN_RAPID:
|
||||||
|
{
|
||||||
|
var data = *core.GetButton(port, device.Index, (uint)input.Index);
|
||||||
|
// TODO: Wire up data.ExcludeName
|
||||||
|
ret.BoolButtons.Add(name);
|
||||||
|
_thunks.Add((c, b) =>
|
||||||
|
{
|
||||||
|
if (c.IsPressed(name))
|
||||||
|
b[byteStart] |= (byte)(1 << bitOffset);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.SWITCH:
|
||||||
|
{
|
||||||
|
var data = *core.GetSwitch(port, device.Index, (uint)input.Index);
|
||||||
|
// TODO: Possibly bulebutton for 2 states?
|
||||||
|
ret.AxisControls.Add(name);
|
||||||
|
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
||||||
|
0, (int)data.DefaultPosition, (int)data.NumPositions - 1));
|
||||||
|
// HACK: Silently discard this until bizhawk fixes its shit
|
||||||
|
// _thunks.Add((c, b) =>
|
||||||
|
// {
|
||||||
|
// var val = (int)Math.Round(c.AxisValue(name));
|
||||||
|
// b[byteStart] |= (byte)(1 << bitOffset);
|
||||||
|
// });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.AXIS:
|
||||||
|
{
|
||||||
|
var data = core.GetAxis(port, device.Index, (uint)input.Index);
|
||||||
|
ret.AxisControls.Add(name);
|
||||||
|
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
||||||
|
0, 0x8000, 0xffff, (inputInfo.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
|
||||||
|
));
|
||||||
|
_thunks.Add((c, b) =>
|
||||||
|
{
|
||||||
|
var val = (ushort)Math.Round(c.AxisValue(name));
|
||||||
|
b[byteStart] = (byte)val;
|
||||||
|
b[byteStart + 1] = (byte)(val >> 8);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.AXIS_REL:
|
||||||
|
{
|
||||||
|
var data = core.GetAxis(port, device.Index, (uint)input.Index);
|
||||||
|
ret.AxisControls.Add(name);
|
||||||
|
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
||||||
|
-0x8000, 0, 0x7fff, (inputInfo.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
|
||||||
|
));
|
||||||
|
_thunks.Add((c, b) =>
|
||||||
|
{
|
||||||
|
var val = (short)Math.Round(c.AxisValue(name));
|
||||||
|
b[byteStart] = (byte)val;
|
||||||
|
b[byteStart + 1] = (byte)(val >> 8);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.POINTER_X:
|
||||||
|
{
|
||||||
|
throw new Exception("TODO: Axis ranges are ints????");
|
||||||
|
// ret.AxisControls.Add(name);
|
||||||
|
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1));
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
case LibNymaCore.InputType.POINTER_Y:
|
||||||
|
{
|
||||||
|
throw new Exception("TODO: Axis ranges are ints????");
|
||||||
|
// ret.AxisControls.Add(name);
|
||||||
|
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1, true));
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
// TODO: wire up statuses to something (not controller, of course)
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"Unimplemented button type {inputInfo.Type}");
|
||||||
|
}
|
||||||
|
ret.CategoryLabels[name] = category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Definition = ret;
|
||||||
|
finalDevices.Add(null);
|
||||||
|
Devices = finalDevices.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<Action<IController, byte[]>> _thunks = new List<Action<IController, byte[]>>();
|
||||||
|
|
||||||
|
public void SetBits(IController src, byte[] dest)
|
||||||
|
{
|
||||||
|
Array.Clear(dest, 0, dest.Length);
|
||||||
|
foreach (var t in _thunks)
|
||||||
|
t(src, dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Waterbox
|
namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
public unsafe abstract class NymaCore : WaterboxCore
|
public unsafe abstract partial class NymaCore : WaterboxCore
|
||||||
{
|
{
|
||||||
protected NymaCore(GameInfo game, byte[] rom, CoreComm comm, Configuration c)
|
protected NymaCore(GameInfo game, byte[] rom, CoreComm comm, Configuration c)
|
||||||
: base(comm, c)
|
: base(comm, c)
|
||||||
|
@ -15,9 +15,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
|
|
||||||
private LibNymaCore _nyma;
|
private LibNymaCore _nyma;
|
||||||
private ControllerAdapter _controllerAdapter;
|
|
||||||
private readonly byte[] _inputPortData = new byte[16 * 16];
|
|
||||||
|
|
||||||
protected T DoInit<T>(GameInfo game, byte[] rom, string filename, string extension)
|
protected T DoInit<T>(GameInfo game, byte[] rom, string filename, string extension)
|
||||||
where T : LibNymaCore
|
where T : LibNymaCore
|
||||||
{
|
{
|
||||||
|
@ -55,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
_exe.RemoveReadonlyFile(fn);
|
_exe.RemoveReadonlyFile(fn);
|
||||||
|
|
||||||
var info = _nyma.GetSystemInfo();
|
var info = *_nyma.GetSystemInfo();
|
||||||
_videoBuffer = new int[info.MaxWidth * info.MaxHeight];
|
_videoBuffer = new int[info.MaxWidth * info.MaxHeight];
|
||||||
BufferWidth = info.NominalWidth;
|
BufferWidth = info.NominalWidth;
|
||||||
BufferHeight = info.NominalHeight;
|
BufferHeight = info.NominalHeight;
|
||||||
|
@ -76,8 +73,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
VsyncNumerator = info.FpsFixed;
|
VsyncNumerator = info.FpsFixed;
|
||||||
VsyncDenominator = 1 << 24;
|
VsyncDenominator = 1 << 24;
|
||||||
|
|
||||||
_controllerAdapter = new ControllerAdapter(_nyma, new string[0]);
|
_soundBuffer = new short[22050 * 2];
|
||||||
_nyma.SetInputDevices(_controllerAdapter.Devices);
|
|
||||||
|
InitControls();
|
||||||
|
|
||||||
PostInit();
|
PostInit();
|
||||||
}
|
}
|
||||||
|
@ -94,7 +92,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_frameAdvanceInputLock = GCHandle.Alloc(_inputPortData, GCHandleType.Pinned);
|
_frameAdvanceInputLock = GCHandle.Alloc(_inputPortData, GCHandleType.Pinned);
|
||||||
var ret = new LibNymaCore.FrameInfo
|
var ret = new LibNymaCore.FrameInfo
|
||||||
{
|
{
|
||||||
SkipRendering = render ? 0 : 1,
|
SkipRendering = (short)(render ? 0 : 1),
|
||||||
|
SkipSoundening =(short)(rendersound ? 0 : 1),
|
||||||
Command = LibNymaCore.CommandType.NONE,
|
Command = LibNymaCore.CommandType.NONE,
|
||||||
InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject()
|
InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject()
|
||||||
};
|
};
|
||||||
|
@ -105,156 +104,6 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
_frameAdvanceInputLock.Free();
|
_frameAdvanceInputLock.Free();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected delegate void ControllerThunk(IController c, byte[] b);
|
|
||||||
|
|
||||||
protected class ControllerAdapter
|
|
||||||
{
|
|
||||||
public string[] Devices { get; }
|
|
||||||
public ControllerDefinition Definition { get; }
|
|
||||||
public ControllerAdapter(LibNymaCore core, string[] config)
|
|
||||||
{
|
|
||||||
var ret = new ControllerDefinition
|
|
||||||
{
|
|
||||||
Name = "TODO"
|
|
||||||
};
|
|
||||||
|
|
||||||
var finalDevices = new List<string>();
|
|
||||||
|
|
||||||
var numPorts = core.GetNumPorts();
|
|
||||||
for (uint i = 0, devByteStart = 0; i < numPorts; i++)
|
|
||||||
{
|
|
||||||
var port = *core.GetPort(i);
|
|
||||||
var devName = i < config.Length ? config[i] : port.DefaultDeviceShortName;
|
|
||||||
finalDevices.Add(devName);
|
|
||||||
|
|
||||||
var devices = Enumerable.Range(0, (int)port.NumDevices)
|
|
||||||
.Select(j => new { Index = (uint)j, Device = *core.GetDevice(i, (uint)j) })
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var device = devices.FirstOrDefault(a => a.Device.ShortName == devName);
|
|
||||||
if (device == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Warn: unknown controller device {devName}");
|
|
||||||
device = devices.FirstOrDefault(a => a.Device.ShortName == port.DefaultDeviceShortName);
|
|
||||||
if (device == null)
|
|
||||||
throw new InvalidOperationException($"Fail: unknown controller device {port.DefaultDeviceShortName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var dev = device.Device;
|
|
||||||
var category = port.FullName + " - " + dev.FullName;
|
|
||||||
|
|
||||||
var inputs = Enumerable.Range(0, (int)dev.NumInputs)
|
|
||||||
.Select(iix => new { Index = iix, Data = *core.GetInput(i, device.Index, (uint)iix) })
|
|
||||||
.OrderBy(a => a.Data.ConfigOrder);
|
|
||||||
|
|
||||||
foreach (var inputzz in inputs)
|
|
||||||
{
|
|
||||||
var input = inputzz.Data;
|
|
||||||
var bitSize = (int)input.BitSize;
|
|
||||||
var bitOffset = (int)input.BitOffset;
|
|
||||||
var byteStart = devByteStart + bitOffset / 8;
|
|
||||||
bitOffset %= 8;
|
|
||||||
var name = input.Name;
|
|
||||||
switch (input.Type)
|
|
||||||
{
|
|
||||||
case LibNymaCore.InputType.PADDING:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.BUTTON:
|
|
||||||
case LibNymaCore.InputType.BUTTON_CAN_RAPID:
|
|
||||||
{
|
|
||||||
var data = *core.GetButton(i, device.Index, (uint)inputzz.Index);
|
|
||||||
// TODO: Wire up data.ExcludeName
|
|
||||||
ret.BoolButtons.Add(name);
|
|
||||||
_thunks.Add((c, b) =>
|
|
||||||
{
|
|
||||||
if (c.IsPressed(name))
|
|
||||||
b[byteStart] |= (byte)(1 << bitOffset);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.SWITCH:
|
|
||||||
{
|
|
||||||
var data = *core.GetSwitch(i, device.Index, (uint)inputzz.Index);
|
|
||||||
// TODO: Possibly bulebutton for 2 states?
|
|
||||||
ret.AxisControls.Add(name);
|
|
||||||
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
|
||||||
0, (int)data.DefaultPosition, (int)data.NumPositions - 1));
|
|
||||||
_thunks.Add((c, b) =>
|
|
||||||
{
|
|
||||||
var val = (int)Math.Round(c.AxisValue(name));
|
|
||||||
b[byteStart] |= (byte)(1 << bitOffset);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.AXIS:
|
|
||||||
{
|
|
||||||
var data = core.GetAxis(i, device.Index, (uint)inputzz.Index);
|
|
||||||
ret.AxisControls.Add(name);
|
|
||||||
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
|
||||||
0, 0x8000, 0xffff, (input.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
|
|
||||||
));
|
|
||||||
_thunks.Add((c, b) =>
|
|
||||||
{
|
|
||||||
var val = (ushort)Math.Round(c.AxisValue(name));
|
|
||||||
b[byteStart] = (byte)val;
|
|
||||||
b[byteStart + 1] = (byte)(val >> 8);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.AXIS_REL:
|
|
||||||
{
|
|
||||||
var data = core.GetAxis(i, device.Index, (uint)inputzz.Index);
|
|
||||||
ret.AxisControls.Add(name);
|
|
||||||
ret.AxisRanges.Add(new ControllerDefinition.AxisRange(
|
|
||||||
-0x8000, 0, 0x7fff, (input.Flags & LibNymaCore.AxisFlags.INVERT_CO) != 0
|
|
||||||
));
|
|
||||||
_thunks.Add((c, b) =>
|
|
||||||
{
|
|
||||||
var val = (short)Math.Round(c.AxisValue(name));
|
|
||||||
b[byteStart] = (byte)val;
|
|
||||||
b[byteStart + 1] = (byte)(val >> 8);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.POINTER_X:
|
|
||||||
{
|
|
||||||
throw new Exception("TODO: Axis ranges are ints????");
|
|
||||||
// ret.AxisControls.Add(name);
|
|
||||||
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1));
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
case LibNymaCore.InputType.POINTER_Y:
|
|
||||||
{
|
|
||||||
throw new Exception("TODO: Axis ranges are ints????");
|
|
||||||
// ret.AxisControls.Add(name);
|
|
||||||
// ret.AxisRanges.Add(new ControllerDefinition.AxisRange(0, 0.5, 1, true));
|
|
||||||
// break;
|
|
||||||
}
|
|
||||||
// TODO: wire up statuses to something (not controller, of course)
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"Unimplemented button type {input.Type}");
|
|
||||||
}
|
|
||||||
ret.CategoryLabels[name] = category;
|
|
||||||
}
|
|
||||||
|
|
||||||
devByteStart += dev.ByteLength;
|
|
||||||
}
|
|
||||||
Definition = ret;
|
|
||||||
Devices = finalDevices.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly List<Action<IController, byte[]>> _thunks = new List<Action<IController, byte[]>>();
|
|
||||||
|
|
||||||
public void SetBits(IController src, byte[] dest)
|
|
||||||
{
|
|
||||||
Array.Clear(dest, 0, dest.Length);
|
|
||||||
foreach (var t in _thunks)
|
|
||||||
t(src, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayType Region { get; protected set; }
|
public DisplayType Region { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -274,7 +123,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
if (*q == 0)
|
if (*q == 0)
|
||||||
{
|
{
|
||||||
if (q > p)
|
if (q > p)
|
||||||
ret.Add(Marshal.PtrToStringAnsi((IntPtr)p));
|
ret.Add(Mershul.PtrToStringUtf8((IntPtr)p));
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
p = ++q;
|
p = ++q;
|
||||||
|
|
|
@ -197,9 +197,19 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
LagCount++;
|
LagCount++;
|
||||||
AdvanceRtc();
|
AdvanceRtc();
|
||||||
|
|
||||||
BufferWidth = frame.Width;
|
if (render)
|
||||||
BufferHeight = frame.Height;
|
{
|
||||||
_numSamples = frame.Samples;
|
BufferWidth = frame.Width;
|
||||||
|
BufferHeight = frame.Height;
|
||||||
|
}
|
||||||
|
if (rendersound)
|
||||||
|
{
|
||||||
|
_numSamples = frame.Samples;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_numSamples = 0;
|
||||||
|
}
|
||||||
|
|
||||||
FrameAdvancePost();
|
FrameAdvancePost();
|
||||||
}
|
}
|
||||||
|
@ -319,7 +329,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly short[] _soundBuffer;
|
protected short[] _soundBuffer;
|
||||||
protected int _numSamples;
|
protected int _numSamples;
|
||||||
public bool CanProvideAsync => false;
|
public bool CanProvideAsync => false;
|
||||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||||
|
|
|
@ -36,12 +36,13 @@ ECL_EXPORT bool Init(const InitData& data)
|
||||||
samples = new int16_t[22050 * 2];
|
samples = new int16_t[22050 * 2];
|
||||||
Surf = new MDFN_Surface(
|
Surf = new MDFN_Surface(
|
||||||
pixels, Game->fb_width, Game->fb_height, Game->fb_width,
|
pixels, Game->fb_width, Game->fb_height, Game->fb_width,
|
||||||
MDFN_PixelFormat(MDFN_COLORSPACE_RGB, 0, 8, 16, 24)
|
MDFN_PixelFormat(MDFN_COLORSPACE_RGB, 16, 8, 0, 24)
|
||||||
);
|
);
|
||||||
EES = new EmulateSpecStruct();
|
EES = new EmulateSpecStruct();
|
||||||
EES->surface = Surf;
|
EES->surface = Surf;
|
||||||
EES->VideoFormatChanged = true;
|
EES->VideoFormatChanged = true;
|
||||||
EES->LineWidths = new int32_t[Game->fb_height];
|
EES->LineWidths = new int32_t[Game->fb_height];
|
||||||
|
memset(EES->LineWidths, 0xff, Game->fb_height * sizeof(int32_t));
|
||||||
EES->SoundBuf = samples;
|
EES->SoundBuf = samples;
|
||||||
EES->SoundBufMaxSize = 22050;
|
EES->SoundBufMaxSize = 22050;
|
||||||
EES->SoundFormatChanged = true;
|
EES->SoundFormatChanged = true;
|
||||||
|
@ -71,7 +72,8 @@ ECL_EXPORT bool Init(const InitData& data)
|
||||||
struct MyFrameInfo: public FrameInfo
|
struct MyFrameInfo: public FrameInfo
|
||||||
{
|
{
|
||||||
// true to skip video rendering
|
// true to skip video rendering
|
||||||
int32_t SkipRendering;
|
int16_t SkipRendering;
|
||||||
|
int16_t SkipSoundening;
|
||||||
// a single MDFN_MSC_* command to run at the start of this frame; 0 if none
|
// a single MDFN_MSC_* command to run at the start of this frame; 0 if none
|
||||||
int32_t Command;
|
int32_t Command;
|
||||||
// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long
|
// raw data for each input port, assumed to be MAX_PORTS * MAX_PORT_DATA long
|
||||||
|
@ -92,24 +94,43 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo& frame)
|
||||||
|
|
||||||
EES->VideoFormatChanged = false;
|
EES->VideoFormatChanged = false;
|
||||||
EES->SoundFormatChanged = false;
|
EES->SoundFormatChanged = false;
|
||||||
frame.Cycles = EES->MasterCycles; // TODO: Was this supposed to be total or delta?
|
frame.Cycles = EES->MasterCycles;
|
||||||
memcpy(frame.SoundBuffer, EES->SoundBuf, EES->SoundBufSize * 4);
|
if (!frame.SkipSoundening)
|
||||||
frame.Samples = EES->SoundBufSize;
|
|
||||||
|
|
||||||
// TODO: Use linewidths
|
|
||||||
int w = EES->DisplayRect.w;
|
|
||||||
int h = EES->DisplayRect.h;
|
|
||||||
frame.Width = w;
|
|
||||||
frame.Height = h;
|
|
||||||
int srcp = Game->fb_width;
|
|
||||||
int dstp = Game->fb_height;
|
|
||||||
uint32_t* src = pixels + EES->DisplayRect.x + EES->DisplayRect.y * srcp;
|
|
||||||
uint32_t* dst = pixels;
|
|
||||||
for (int line = 0; line < h; line++)
|
|
||||||
{
|
{
|
||||||
memcpy(dst, src, w * 4);
|
memcpy(frame.SoundBuffer, EES->SoundBuf, EES->SoundBufSize * 4);
|
||||||
src += srcp;
|
frame.Samples = EES->SoundBufSize;
|
||||||
dst += dstp;
|
}
|
||||||
|
if (!frame.SkipRendering)
|
||||||
|
{
|
||||||
|
int h = EES->DisplayRect.h;
|
||||||
|
int lineStart = EES->DisplayRect.y;
|
||||||
|
int lineEnd = lineStart + h;
|
||||||
|
|
||||||
|
auto multiWidth = EES->LineWidths[0] != -1;
|
||||||
|
int w;
|
||||||
|
if (multiWidth)
|
||||||
|
{
|
||||||
|
w = 0;
|
||||||
|
for (int line = lineStart; line < lineEnd; line++)
|
||||||
|
w = std::max(w, EES->LineWidths[line]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
w = EES->DisplayRect.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.Width = w;
|
||||||
|
frame.Height = h;
|
||||||
|
int srcp = Game->fb_width;
|
||||||
|
int dstp = w;
|
||||||
|
uint32_t* src = pixels + EES->DisplayRect.x + EES->DisplayRect.y * srcp;
|
||||||
|
uint32_t* dst = frame.VideoBuffer;
|
||||||
|
for (int line = lineStart; line < lineEnd; line++)
|
||||||
|
{
|
||||||
|
memcpy(dst, src, (multiWidth ? EES->LineWidths[line] : w) * sizeof(uint32_t));
|
||||||
|
src += srcp;
|
||||||
|
dst += dstp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue