support custom screen layouts in MelonDS core (no UI yet)
This commit is contained in:
parent
59a7b24df9
commit
88904e6b44
|
@ -14,6 +14,7 @@ using BizHawk.Client.EmuHawk.FilterManager;
|
||||||
using BizHawk.Common.PathExtensions;
|
using BizHawk.Common.PathExtensions;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using BizHawk.Emulation.Cores.Sony.PSX;
|
using BizHawk.Emulation.Cores.Sony.PSX;
|
||||||
|
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||||
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
|
@ -395,7 +396,12 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Vector2 v = new Vector2(p.X, p.Y);
|
Vector2 v = new Vector2(p.X, p.Y);
|
||||||
v = _currentFilterProgram.UntransformPoint("default", v);
|
v = _currentFilterProgram.UntransformPoint("default", v);
|
||||||
if (Global.Emulator.SystemId == "NDS")
|
if (Global.Emulator.SystemId == "NDS")
|
||||||
v.Y = 2 * v.Y - (Global.Emulator as IVideoProvider).BufferHeight;
|
{
|
||||||
|
MelonDS core = Global.Emulator as MelonDS;
|
||||||
|
Point touchLocation = core.GetSettings().screenOptions.locations[1];
|
||||||
|
v.Y = (int)((double)core.BufferHeight / MelonDS.NATIVE_HEIGHT * (v.Y - touchLocation.Y));
|
||||||
|
v.X = (int)((double)core.BufferWidth / MelonDS.NATIVE_WIDTH * (v.X - touchLocation.X));
|
||||||
|
}
|
||||||
return new Point((int)v.X, (int)v.Y);
|
return new Point((int)v.X, (int)v.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a way to arrange displays inside a frame buffer.
|
||||||
|
/// </summary>
|
||||||
|
namespace BizHawk.Emulation.Common
|
||||||
|
{
|
||||||
|
public class ScreenArranger
|
||||||
|
{
|
||||||
|
public ScreenLayoutSettings layoutSettings;
|
||||||
|
private Size[] sizes;
|
||||||
|
|
||||||
|
public ScreenArranger(Size[] sizes)
|
||||||
|
{
|
||||||
|
this.sizes = sizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe int[] GenerateFramebuffer(int*[] src, int[] srcLength)
|
||||||
|
{
|
||||||
|
if (src.Length != layoutSettings.locations.Length)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int[] ret = new int[layoutSettings.finalSize.Width * layoutSettings.finalSize.Height];
|
||||||
|
for (int iBuf = 0; iBuf < src.Length; iBuf++)
|
||||||
|
{
|
||||||
|
int screen = layoutSettings.order[iBuf];
|
||||||
|
Size size = sizes[screen];
|
||||||
|
Point position = layoutSettings.locations[screen];
|
||||||
|
|
||||||
|
int minSrcX = Math.Max(-position.X, 0);
|
||||||
|
int maxSrcX = Math.Min(layoutSettings.finalSize.Width - position.X, size.Width);
|
||||||
|
int minDstX = Math.Max(position.X, 0);
|
||||||
|
|
||||||
|
int minSrcY = Math.Max(-position.Y, 0);
|
||||||
|
int maxSrcY = Math.Min(layoutSettings.finalSize.Height - position.Y, size.Height);
|
||||||
|
int minDstY = Math.Max(position.Y, 0);
|
||||||
|
|
||||||
|
if ((maxSrcX - 1) + (maxSrcY - 1) * size.Width > srcLength[iBuf])
|
||||||
|
throw new ArgumentException("The given source buffer is smaller than expected.");
|
||||||
|
|
||||||
|
for (int iY = minSrcY; iY < maxSrcY; iY++)
|
||||||
|
{
|
||||||
|
int dstIndex = minDstX + (minDstY + iY - minSrcY) * layoutSettings.finalSize.Width;
|
||||||
|
int srcIndex = minSrcX + iY * size.Width;
|
||||||
|
for (int iX = minSrcX; iX < maxSrcX; iX++)
|
||||||
|
ret[dstIndex++] = src[screen][srcIndex++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe int[] GenerateFramebuffer(int[][] src)
|
||||||
|
{
|
||||||
|
GCHandle[] handles = new GCHandle[src.Length];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < src.Length; i++)
|
||||||
|
handles[i] = GCHandle.Alloc(src[i], GCHandleType.Pinned);
|
||||||
|
|
||||||
|
int*[] srcBuffers = new int*[src.Length];
|
||||||
|
int[] lengths = new int[src.Length];
|
||||||
|
for (int i = 0; i < src.Length; i++)
|
||||||
|
{
|
||||||
|
srcBuffers[i] = (int*)handles[i].AddrOfPinnedObject();
|
||||||
|
lengths[i] = src[i].Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateFramebuffer(srcBuffers, lengths);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{ // unpin the memory
|
||||||
|
foreach (var h in handles)
|
||||||
|
if (h.IsAllocated) h.Free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScreenLayoutSettings
|
||||||
|
{
|
||||||
|
public Point[] locations;
|
||||||
|
public int[] order;
|
||||||
|
public Size finalSize;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,23 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
{
|
{
|
||||||
[Core("MelonDS", "Arisotura", isPorted: true, isReleased: false, "0.8.4-bizhawk-cbd9adac3", "https://github.com/TASVideos/melonDS", singleInstance: true)]
|
[Core("MelonDS", "Arisotura", false, false, null, null, true)]
|
||||||
public unsafe partial class MelonDS : IEmulator
|
unsafe public partial class MelonDS : IEmulator
|
||||||
{
|
{
|
||||||
private readonly BasicServiceProvider _serviceProvider;
|
private BasicServiceProvider _serviceProvider;
|
||||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||||
|
|
||||||
public ControllerDefinition ControllerDefinition { get; }
|
public ControllerDefinition ControllerDefinition { get; private set; }
|
||||||
|
|
||||||
public int Frame => GetFrameCount();
|
public int Frame => GetFrameCount();
|
||||||
|
|
||||||
|
@ -20,7 +25,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
public bool DeterministicEmulation => true;
|
public bool DeterministicEmulation => true;
|
||||||
|
|
||||||
internal CoreComm CoreComm { get; }
|
public CoreComm CoreComm { get; private set; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -68,18 +73,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
private static extern void FrameAdvance(short buttons, byte touchX, byte touchY);
|
private static extern void FrameAdvance(short buttons, byte touchX, byte touchY);
|
||||||
|
|
||||||
[CoreConstructor("NDS")]
|
[CoreConstructor("NDS")]
|
||||||
public MelonDS(byte[] file, CoreComm comm, object settings, object syncSettings)
|
public MelonDS(byte[] file, CoreComm comm, object settings, object syncsettings)
|
||||||
{
|
{
|
||||||
_serviceProvider = new BasicServiceProvider(this);
|
_serviceProvider = new BasicServiceProvider(this);
|
||||||
ControllerDefinition = new ControllerDefinition { Name = "NDS Controller" };
|
ControllerDefinition = new ControllerDefinition();
|
||||||
|
ControllerDefinition.Name = "NDS Controller";
|
||||||
|
ControllerDefinition.BoolButtons.Add("Left");
|
||||||
|
ControllerDefinition.BoolButtons.Add("Right");
|
||||||
ControllerDefinition.BoolButtons.Add("Up");
|
ControllerDefinition.BoolButtons.Add("Up");
|
||||||
ControllerDefinition.BoolButtons.Add("Down");
|
ControllerDefinition.BoolButtons.Add("Down");
|
||||||
ControllerDefinition.BoolButtons.Add("Right");
|
ControllerDefinition.BoolButtons.Add("A");
|
||||||
ControllerDefinition.BoolButtons.Add("Left");
|
|
||||||
ControllerDefinition.BoolButtons.Add("Y");
|
|
||||||
ControllerDefinition.BoolButtons.Add("B");
|
ControllerDefinition.BoolButtons.Add("B");
|
||||||
ControllerDefinition.BoolButtons.Add("X");
|
ControllerDefinition.BoolButtons.Add("X");
|
||||||
ControllerDefinition.BoolButtons.Add("A");
|
ControllerDefinition.BoolButtons.Add("Y");
|
||||||
ControllerDefinition.BoolButtons.Add("L");
|
ControllerDefinition.BoolButtons.Add("L");
|
||||||
ControllerDefinition.BoolButtons.Add("R");
|
ControllerDefinition.BoolButtons.Add("R");
|
||||||
ControllerDefinition.BoolButtons.Add("Start");
|
ControllerDefinition.BoolButtons.Add("Start");
|
||||||
|
@ -95,14 +101,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
CoreComm = comm;
|
CoreComm = comm;
|
||||||
|
|
||||||
|
SetUpFiles();
|
||||||
if (!Init())
|
if (!Init())
|
||||||
throw new Exception("Failed to init NDS.");
|
throw new Exception("Failed to init NDS.");
|
||||||
|
|
||||||
SetUpFiles();
|
|
||||||
PutSyncSettings(syncSettings as MelonSyncSettings);
|
|
||||||
|
|
||||||
InitMemoryDomains();
|
InitMemoryDomains();
|
||||||
|
|
||||||
|
screenArranger = new ScreenArranger(new Size[] { new Size(NATIVE_WIDTH, NATIVE_HEIGHT), new Size(NATIVE_WIDTH, NATIVE_HEIGHT) });
|
||||||
|
PutSettings(settings as MelonSettings);
|
||||||
|
PutSyncSettings(syncsettings as MelonSyncSettings);
|
||||||
|
|
||||||
|
|
||||||
fixed (byte* f = file)
|
fixed (byte* f = file)
|
||||||
{
|
{
|
||||||
LoadROM(f, file.Length);
|
LoadROM(f, file.Length);
|
||||||
|
@ -160,7 +168,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
byte[] bytes = File.ReadAllBytes(firmwarePath);
|
byte[] bytes = File.ReadAllBytes(firmwarePath);
|
||||||
|
|
||||||
// There are two regions for user settings
|
// There are two regions for user settings
|
||||||
int settingsLength = getUserSettingsLength();
|
int settingsLength = GetUserSettingsLength();
|
||||||
for (int i = bytes.Length - 0x200; i < bytes.Length - 0x200 + settingsLength; i++)
|
for (int i = bytes.Length - 0x200; i < bytes.Length - 0x200 + settingsLength; i++)
|
||||||
bytes[i] = 0xFF;
|
bytes[i] = 0xFF;
|
||||||
for (int i = bytes.Length - 0x100; i < bytes.Length - 0x100 + settingsLength; i++)
|
for (int i = bytes.Length - 0x100; i < bytes.Length - 0x100 + settingsLength; i++)
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
{
|
{
|
||||||
unsafe partial class MelonDS : ISettable<object, MelonDS.MelonSyncSettings>
|
unsafe partial class MelonDS : ISettable<MelonDS.MelonSettings, MelonDS.MelonSyncSettings>
|
||||||
{
|
{
|
||||||
public object GetSettings() => new object();
|
private MelonSettings _settings;
|
||||||
|
|
||||||
|
public MelonSettings GetSettings()
|
||||||
|
{
|
||||||
|
return _settings;
|
||||||
|
}
|
||||||
|
|
||||||
public MelonSyncSettings GetSyncSettings()
|
public MelonSyncSettings GetSyncSettings()
|
||||||
{
|
{
|
||||||
|
@ -24,7 +30,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool PutSettings(object o) => false;
|
public bool PutSettings(MelonSettings o)
|
||||||
|
{
|
||||||
|
if (o == null)
|
||||||
|
{
|
||||||
|
o = new MelonSettings();
|
||||||
|
o.screenOptions = new ScreenLayoutSettings();
|
||||||
|
o.screenOptions.locations = new Point[] { new Point(0, 0), new Point(0, NATIVE_HEIGHT) };
|
||||||
|
o.screenOptions.order = new int[] { 0, 1 };
|
||||||
|
o.screenOptions.finalSize = new Size(NATIVE_WIDTH, NATIVE_HEIGHT * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_settings = o;
|
||||||
|
screenArranger.layoutSettings = _settings.screenOptions;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool PutSyncSettings(MelonSyncSettings o)
|
public bool PutSyncSettings(MelonSyncSettings o)
|
||||||
{
|
{
|
||||||
|
@ -49,9 +70,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
[DllImport(dllPath)]
|
[DllImport(dllPath)]
|
||||||
private static extern bool GetUserSettings(byte* dst);
|
private static extern bool GetUserSettings(byte* dst);
|
||||||
[DllImport(dllPath)]
|
[DllImport(dllPath)]
|
||||||
private static extern int getUserSettingsLength();
|
private static extern int GetUserSettingsLength();
|
||||||
|
|
||||||
private static readonly int UserSettingsLength = getUserSettingsLength();
|
private static readonly int UserSettingsLength = GetUserSettingsLength();
|
||||||
|
|
||||||
[DllImport(dllPath)]
|
[DllImport(dllPath)]
|
||||||
private static extern void SetUserSettings(byte* src);
|
private static extern void SetUserSettings(byte* src);
|
||||||
|
@ -66,6 +87,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
[DllImport(dllPath)]
|
[DllImport(dllPath)]
|
||||||
private static extern uint GetTimeAtBoot();
|
private static extern uint GetTimeAtBoot();
|
||||||
|
|
||||||
|
public class MelonSettings
|
||||||
|
{
|
||||||
|
public ScreenLayoutSettings screenOptions;
|
||||||
|
}
|
||||||
|
|
||||||
public class MelonSyncSettings
|
public class MelonSyncSettings
|
||||||
{
|
{
|
||||||
public MelonSyncSettings()
|
public MelonSyncSettings()
|
||||||
|
|
|
@ -6,17 +6,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
{
|
{
|
||||||
unsafe partial class MelonDS : IVideoProvider
|
unsafe partial class MelonDS : IVideoProvider
|
||||||
{
|
{
|
||||||
private const int NATIVE_WIDTH = 256;
|
public const int NATIVE_WIDTH = 256;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// for a single screen
|
/// for a single screen
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int NATIVE_HEIGHT = 192;
|
public const int NATIVE_HEIGHT = 192;
|
||||||
|
|
||||||
public int VirtualWidth => NATIVE_WIDTH;
|
public int VirtualWidth => BufferWidth;
|
||||||
public int VirtualHeight => NATIVE_HEIGHT * 2;
|
public int VirtualHeight => BufferHeight;
|
||||||
|
|
||||||
public int BufferWidth => NATIVE_WIDTH;
|
public int BufferWidth => _settings.screenOptions.finalSize.Width;
|
||||||
public int BufferHeight => NATIVE_HEIGHT * 2;
|
public int BufferHeight => _settings.screenOptions.finalSize.Height;
|
||||||
|
|
||||||
public int VsyncNumerator => 60;
|
public int VsyncNumerator => 60;
|
||||||
|
|
||||||
|
@ -24,22 +24,28 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
public int BackgroundColor => 0;
|
public int BackgroundColor => 0;
|
||||||
|
|
||||||
|
ScreenArranger screenArranger;
|
||||||
|
|
||||||
|
|
||||||
[DllImport(dllPath)]
|
[DllImport(dllPath)]
|
||||||
private static extern void VideoBuffer32bit(int* dstBuffer);
|
private static extern int* GetTopScreenBuffer();
|
||||||
|
[DllImport(dllPath)]
|
||||||
|
private static extern int* GetBottomScreenBuffer();
|
||||||
|
[DllImport(dllPath)]
|
||||||
|
private static extern int GetScreenBufferSize();
|
||||||
|
|
||||||
// BizHawk needs to be able to modify the buffer when loading savestates.
|
// BizHawk needs to be able to modify the buffer when loading savestates.
|
||||||
private int[] buffer = new int[NATIVE_WIDTH * NATIVE_HEIGHT * 2];
|
private int[] buffer = null;
|
||||||
private bool getNewBuffer = true;
|
private bool getNewBuffer = true;
|
||||||
public int[] GetVideoBuffer()
|
public int[] GetVideoBuffer()
|
||||||
{
|
{
|
||||||
if (getNewBuffer)
|
if (getNewBuffer)
|
||||||
{
|
{
|
||||||
fixed (int* v = buffer)
|
|
||||||
{
|
|
||||||
VideoBuffer32bit(v);
|
|
||||||
}
|
|
||||||
getNewBuffer = false;
|
getNewBuffer = false;
|
||||||
|
|
||||||
|
int*[] buffers = new int*[] { GetTopScreenBuffer(), GetBottomScreenBuffer() };
|
||||||
|
int bufferSize = GetScreenBufferSize();
|
||||||
|
buffer = screenArranger.GenerateFramebuffer(buffers, new int[] { bufferSize, bufferSize});
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue