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.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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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.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++)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue