diff --git a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs index 3f8b69b826..cac10512d7 100644 --- a/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs +++ b/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs @@ -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); } diff --git a/BizHawk.Emulation.Common/ScreenArranger.cs b/BizHawk.Emulation.Common/ScreenArranger.cs new file mode 100644 index 0000000000..e05c0bd13c --- /dev/null +++ b/BizHawk.Emulation.Common/ScreenArranger.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.InteropServices; + +/// +/// Provides a way to arrange displays inside a frame buffer. +/// +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; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs index 4d2af81548..924e0cbcef 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs @@ -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++) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_Settable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_Settable.cs index a077d71831..e05a66e17b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_Settable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_Settable.cs @@ -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 + unsafe partial class MelonDS : ISettable { - 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() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_VideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_VideoProvider.cs index ed0e61b59c..b32595a2ab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_VideoProvider.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS_VideoProvider.cs @@ -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; /// /// for a single screen /// - 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; }