From de3878108177b73a4768af007b1deba89b8d3ed2 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sat, 17 Sep 2022 01:36:51 -0700 Subject: [PATCH] Implement Rumble for Nyma --- .../Waterbox/NymaCore.Controller.cs | 22 ++++++++++++++++--- .../Waterbox/NymaCore.cs | 7 +++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs index 367ca354a3..3befb73dcc 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.Controller.cs @@ -27,6 +27,7 @@ namespace BizHawk.Emulation.Cores.Waterbox _nyma.SetInputDevices(_controllerAdapter.Devices); ControllerDefinition = _controllerAdapter.Definition; } + protected delegate void ControllerThunk(IController c, byte[] b); protected class ControllerAdapter : IStatable @@ -244,14 +245,22 @@ namespace BizHawk.Emulation.Cores.Waterbox var val = c.AxisValue(name); b[byteStart] = (byte)val; b[byteStart + 1] = (byte)(val >> 8); - }); + }); break; } case InputType.Status: // TODO: wire up statuses to something (not controller, of course) break; case InputType.Rumble: - // TODO: wtf do we do here??? + ret.HapticsChannels.Add(name); + // this is a special case, we treat b here as output rather than input + // so these thunks are called after the frame has advanced + _rumblers.Add((c, b) => + { + // TODO: not entirely sure this is correct... + var val = b[byteStart] | (b[byteStart + 1] << 8); + c.SetHapticChannelStrength(name, val << 7); + }); break; default: { @@ -279,7 +288,8 @@ namespace BizHawk.Emulation.Cores.Waterbox private readonly byte[] _switchPreviousFrame; - private readonly List> _thunks = new List>(); + private readonly List _thunks = new(); + private readonly List _rumblers = new(); public void SetBits(IController src, byte[] dest) { @@ -288,6 +298,12 @@ namespace BizHawk.Emulation.Cores.Waterbox t(src, dest); } + public void DoRumble(IController dest, byte[] src) + { + foreach (var r in _rumblers) + r(dest, src); + } + private const ulong MAGIC = 9569546739673486731; public void SaveStateBinary(BinaryWriter writer) diff --git a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs index 7690619d38..7d11639233 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/NymaCore.cs @@ -224,9 +224,12 @@ namespace BizHawk.Emulation.Cores.Waterbox private Task _frameThreadProcActive; + private IController _currentController; + protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) { DriveLightOn = false; + _currentController = controller; // need to remember this for rumble _controllerAdapter.SetBits(controller, _inputPortData); _frameAdvanceInputLock = GCHandle.Alloc(_inputPortData, GCHandleType.Pinned); LibNymaCore.BizhawkFlags flags = 0; @@ -251,7 +254,7 @@ namespace BizHawk.Emulation.Cores.Waterbox : LibNymaCore.CommandType.NONE, InputPortData = (byte*)_frameAdvanceInputLock.AddrOfPinnedObject(), FrontendTime = GetRtcTime(SettingsQuery("nyma.rtcrealtime") != "0"), - DiskIndex = (int)controller.AxisValue("Disk Index") + DiskIndex = controller.AxisValue("Disk Index") }; if (_frameThreadStart != null) { @@ -261,6 +264,8 @@ namespace BizHawk.Emulation.Cores.Waterbox } protected override void FrameAdvancePost() { + _controllerAdapter.DoRumble(_currentController, _inputPortData); + if (_frameThreadProcActive != null) { // The nyma core unmanaged code should always release the threadproc to completion