support custom screen layouts in MelonDS core (no UI yet)

This commit is contained in:
SuuperW 2020-03-22 18:40:52 -05:00
parent 59a7b24df9
commit 88904e6b44
5 changed files with 168 additions and 34 deletions

View File

@ -14,6 +14,7 @@ using BizHawk.Client.EmuHawk.FilterManager;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Sony.PSX;
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
using OpenTK;
@ -395,7 +396,12 @@ namespace BizHawk.Client.EmuHawk
Vector2 v = new Vector2(p.X, p.Y);
v = _currentFilterProgram.UntransformPoint("default", v);
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);
}

View File

@ -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;
}
}

View File

@ -1,18 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using BizHawk.Emulation.Common;
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)]
public unsafe partial class MelonDS : IEmulator
[Core("MelonDS", "Arisotura", false, false, null, null, true)]
unsafe public partial class MelonDS : IEmulator
{
private readonly BasicServiceProvider _serviceProvider;
private BasicServiceProvider _serviceProvider;
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
public ControllerDefinition ControllerDefinition { get; }
public ControllerDefinition ControllerDefinition { get; private set; }
public int Frame => GetFrameCount();
@ -20,7 +25,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public bool DeterministicEmulation => true;
internal CoreComm CoreComm { get; }
public CoreComm CoreComm { get; private set; }
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);
[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);
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("Down");
ControllerDefinition.BoolButtons.Add("Right");
ControllerDefinition.BoolButtons.Add("Left");
ControllerDefinition.BoolButtons.Add("Y");
ControllerDefinition.BoolButtons.Add("A");
ControllerDefinition.BoolButtons.Add("B");
ControllerDefinition.BoolButtons.Add("X");
ControllerDefinition.BoolButtons.Add("A");
ControllerDefinition.BoolButtons.Add("Y");
ControllerDefinition.BoolButtons.Add("L");
ControllerDefinition.BoolButtons.Add("R");
ControllerDefinition.BoolButtons.Add("Start");
@ -95,14 +101,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
CoreComm = comm;
SetUpFiles();
if (!Init())
throw new Exception("Failed to init NDS.");
SetUpFiles();
PutSyncSettings(syncSettings as MelonSyncSettings);
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)
{
LoadROM(f, file.Length);
@ -160,7 +168,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
byte[] bytes = File.ReadAllBytes(firmwarePath);
// 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++)
bytes[i] = 0xFF;
for (int i = bytes.Length - 0x100; i < bytes.Length - 0x100 + settingsLength; i++)

View File

@ -1,15 +1,21 @@
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
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()
{
@ -24,7 +30,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
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)
{
@ -49,9 +70,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[DllImport(dllPath)]
private static extern bool GetUserSettings(byte* dst);
[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)]
private static extern void SetUserSettings(byte* src);
@ -66,6 +87,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[DllImport(dllPath)]
private static extern uint GetTimeAtBoot();
public class MelonSettings
{
public ScreenLayoutSettings screenOptions;
}
public class MelonSyncSettings
{
public MelonSyncSettings()

View File

@ -6,17 +6,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : IVideoProvider
{
private const int NATIVE_WIDTH = 256;
public const int NATIVE_WIDTH = 256;
/// <summary>
/// for a single screen
/// </summary>
private const int NATIVE_HEIGHT = 192;
public const int NATIVE_HEIGHT = 192;
public int VirtualWidth => NATIVE_WIDTH;
public int VirtualHeight => NATIVE_HEIGHT * 2;
public int VirtualWidth => BufferWidth;
public int VirtualHeight => BufferHeight;
public int BufferWidth => NATIVE_WIDTH;
public int BufferHeight => NATIVE_HEIGHT * 2;
public int BufferWidth => _settings.screenOptions.finalSize.Width;
public int BufferHeight => _settings.screenOptions.finalSize.Height;
public int VsyncNumerator => 60;
@ -24,22 +24,28 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public int BackgroundColor => 0;
ScreenArranger screenArranger;
[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.
private int[] buffer = new int[NATIVE_WIDTH * NATIVE_HEIGHT * 2];
private int[] buffer = null;
private bool getNewBuffer = true;
public int[] GetVideoBuffer()
{
if (getNewBuffer)
{
fixed (int* v = buffer)
{
VideoBuffer32bit(v);
}
getNewBuffer = false;
int*[] buffers = new int*[] { GetTopScreenBuffer(), GetBottomScreenBuffer() };
int bufferSize = GetScreenBufferSize();
buffer = screenArranger.GenerateFramebuffer(buffers, new int[] { bufferSize, bufferSize});
}
return buffer;
}