diff --git a/Core/ArkanoidController.h b/Core/ArkanoidController.h index 2d547297..6d87d3d0 100644 --- a/Core/ArkanoidController.h +++ b/Core/ArkanoidController.h @@ -5,9 +5,9 @@ class ArkanoidController : public BaseControlDevice { private: - uint32_t _stateBuffer; - bool _buttonPressed; - int32_t _xPosition; + uint32_t _stateBuffer = 0; + bool _buttonPressed = false; + int32_t _xPosition = 0; bool IsButtonPressed(); diff --git a/Core/ControlManager.cpp b/Core/ControlManager.cpp index 2465852d..0038eeb9 100644 --- a/Core/ControlManager.cpp +++ b/Core/ControlManager.cpp @@ -3,6 +3,7 @@ #include "StandardController.h" #include "Zapper.h" #include "ArkanoidController.h" +#include "OekaKidsTablet.h" #include "EmulationSettings.h" #include "Console.h" #include "GameServerConnection.h" @@ -124,19 +125,15 @@ void ControlManager::UpdateControlDevices() bool fourScore = EmulationSettings::CheckFlag(EmulationFlags::HasFourScore); ExpansionPortDevice expansionDevice = EmulationSettings::GetExpansionDevice(); - shared_ptr arkanoidController; if(EmulationSettings::GetConsoleType() != ConsoleType::Famicom) { expansionDevice = ExpansionPortDevice::None; } else if(expansionDevice != ExpansionPortDevice::FourPlayerAdapter) { fourScore = false; - if(expansionDevice == ExpansionPortDevice::ArkanoidController) { - arkanoidController.reset(new ArkanoidController(2)); - } } for(int i = 0; i < 2; i++) { shared_ptr device; - if(fourScore || expansionDevice == ExpansionPortDevice::ArkanoidController || i == 1 && expansionDevice == ExpansionPortDevice::Zapper) { + if(fourScore || EmulationSettings::GetConsoleType() == ConsoleType::Famicom) { //Need to set standard controller in all slots if four score (to allow emulation to work correctly) device.reset(new StandardController(i)); } else { @@ -152,10 +149,13 @@ void ControlManager::UpdateControlDevices() if(fourScore) { std::dynamic_pointer_cast(device)->AddAdditionalController(shared_ptr(new StandardController(i + 2))); - } else if(i == 1 && expansionDevice == ExpansionPortDevice::Zapper) { - std::dynamic_pointer_cast(device)->AddAdditionalController(shared_ptr(new Zapper(2))); - } else if(expansionDevice == ExpansionPortDevice::ArkanoidController) { - std::dynamic_pointer_cast(device)->AddAdditionalController(arkanoidController); + } else if(i == 1 || expansionDevice == ExpansionPortDevice::ArkanoidController) { + switch(expansionDevice) { + case ExpansionPortDevice::Zapper: std::dynamic_pointer_cast(device)->AddAdditionalController(shared_ptr(new Zapper(2))); break; + case ExpansionPortDevice::ArkanoidController: std::dynamic_pointer_cast(device)->AddAdditionalController(shared_ptr(new ArkanoidController(2))); break; + case ExpansionPortDevice::OekaKidsTablet: std::dynamic_pointer_cast(device)->AddAdditionalController(shared_ptr(new OekaKidsTablet(2))); break; + default: break; + } } } else { @@ -199,15 +199,33 @@ uint8_t ControlManager::ReadRAM(uint16_t addr) return 0; } +template +shared_ptr ControlManager::GetExpansionDevice() +{ + shared_ptr controller; + controller = std::dynamic_pointer_cast(GetControlDevice(1)); + if(controller) { + shared_ptr expansionDevice; + expansionDevice = std::dynamic_pointer_cast(controller->GetAdditionalController()); + return expansionDevice; + } + return nullptr; +} + void ControlManager::WriteRAM(uint16_t addr, uint8_t value) { //$4016 writes bool previousState = _refreshState; _refreshState = (value & 0x01) == 0x01; - - if(previousState && !_refreshState) { - //Refresh controller once strobe bit is disabled - RefreshAllPorts(); + + auto tablet = GetExpansionDevice(); + if(tablet) { + tablet->WriteRam(value); + } else { + if(previousState && !_refreshState) { + //Refresh controller once strobe bit is disabled + RefreshAllPorts(); + } } } diff --git a/Core/ControlManager.h b/Core/ControlManager.h index 9595c0dd..02dccf60 100644 --- a/Core/ControlManager.h +++ b/Core/ControlManager.h @@ -29,6 +29,8 @@ class ControlManager : public Snapshotable, public IMemoryHandler bool _isLagging = false; bool _refreshState = false; + template shared_ptr GetExpansionDevice(); + virtual shared_ptr GetZapper(uint8_t port); static void RegisterControlDevice(shared_ptr controlDevice, uint8_t port); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 70832f61..8bfd22b5 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -505,6 +505,7 @@ + @@ -681,6 +682,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 56f11a82..af2e4bf3 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -838,6 +838,9 @@ Nes\Mappers + + Nes\Controllers + @@ -1005,5 +1008,8 @@ VideoDecoder + + Nes\Controllers + \ No newline at end of file diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 5a332110..3167e237 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -161,6 +161,7 @@ enum class ExpansionPortDevice Zapper = 1, FourPlayerAdapter = 2, ArkanoidController = 3, + OekaKidsTablet = 4, }; struct KeyMapping diff --git a/Core/GameDatabase.cpp b/Core/GameDatabase.cpp index cdbaf109..1600e445 100644 --- a/Core/GameDatabase.cpp +++ b/Core/GameDatabase.cpp @@ -113,6 +113,10 @@ void GameDatabase::InitializeInputDevices(string inputType, GameSystem system) } else { controllers[1] = ControllerType::ArkanoidController; } + } else if(inputType.compare("OekaKidsTablet") == 0) { + MessageManager::Log("[DB] Input: Oeka Kids Tablet connected"); + system = GameSystem::Famicom; + expDevice = ExpansionPortDevice::OekaKidsTablet; } else { MessageManager::Log("[DB] Input: 2 standard controllers connected"); } diff --git a/Core/OekaKidsTablet.cpp b/Core/OekaKidsTablet.cpp new file mode 100644 index 00000000..2dbe0a39 --- /dev/null +++ b/Core/OekaKidsTablet.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "OekaKidsTablet.h" +#include "ControlManager.h" +#include "GameServerConnection.h" + +void OekaKidsTablet::StreamState(bool saving) +{ + BaseControlDevice::StreamState(saving); + Stream(_xPosition, _yPosition, _touch, _click); +} + +uint32_t OekaKidsTablet::GetNetPlayState() +{ + //Used by netplay + uint32_t state = _xPosition | (_yPosition << 8); + + if(_touch) { + state |= 0x40000000; + } + + if(_click) { + state |= 0x80000000; + } + + return state; +} + +uint8_t OekaKidsTablet::ProcessNetPlayState(uint32_t netplayState) +{ + _xPosition = netplayState & 0xFF; + _yPosition = (netplayState >> 8) & 0xFF; + + _touch = ((netplayState >> 30) & 0x01) == 0x01; + _click = ((netplayState >> 31) & 0x01) == 0x01; + + return RefreshState(); +} + +uint8_t OekaKidsTablet::RefreshState() +{ + if(_strobe) { + if(_shift) { + return (_stateBuffer & 0x40000) ? 0x00 : 0x08; + } else { + return 0x04; + } + } else { + return 0x00; + } +} + +uint8_t OekaKidsTablet::GetPortOutput() +{ + return GetControlState(); +} + +void OekaKidsTablet::WriteRam(uint8_t value) +{ + _strobe = (value & 0x01) == 0x01; + bool shift = ((value >> 1) & 0x01) == 0x01; + + if(_strobe) { + if(!_shift && shift) { + _stateBuffer <<= 1; + } + _shift = shift; + } else { + if(!GameServerConnection::GetNetPlayDevice(_port)) { + MousePosition position = ControlManager::GetMousePosition(); + _xPosition = (int32_t)((double)std::max(0, position.X + 8) / 256.0 * 240); + _yPosition = (int32_t)((double)std::max(0, position.Y - 14) / 240.0 * 256); + + if(!EmulationSettings::CheckFlag(EmulationFlags::InBackground) || EmulationSettings::CheckFlag(EmulationFlags::AllowBackgroundInput)) { + _touch = position.Y >= 48 || ControlManager::IsMouseButtonPressed(MouseButton::LeftButton); + _click = ControlManager::IsMouseButtonPressed(MouseButton::LeftButton); + } else { + _touch = false; + _click = false; + } + } + + _stateBuffer = ((_xPosition & 0xFF) << 10) | ((_yPosition & 0xFF) << 2) | (_touch ? 0x02 : 0x00) | (_click ? 0x01 : 0x00); + } +} diff --git a/Core/OekaKidsTablet.h b/Core/OekaKidsTablet.h new file mode 100644 index 00000000..cb61120e --- /dev/null +++ b/Core/OekaKidsTablet.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "BaseControlDevice.h" + +class OekaKidsTablet : public BaseControlDevice +{ +private: + bool _strobe = false; + bool _shift = false; + uint32_t _stateBuffer = 0; + + bool _click = false; + bool _touch = false; + int32_t _xPosition = 0; + int32_t _yPosition = 0; + +protected: + virtual uint8_t RefreshState(); + uint8_t ProcessNetPlayState(uint32_t netplayState); + void StreamState(bool saving); + +public: + using BaseControlDevice::BaseControlDevice; + + virtual uint8_t GetPortOutput(); + virtual uint32_t GetNetPlayState(); + + void WriteRam(uint8_t value); +}; \ No newline at end of file diff --git a/Core/StandardController.cpp b/Core/StandardController.cpp index 3ab1d126..b0b48329 100644 --- a/Core/StandardController.cpp +++ b/Core/StandardController.cpp @@ -4,6 +4,7 @@ #include "PPU.h" #include "EmulationSettings.h" #include "ArkanoidController.h" +#include "OekaKidsTablet.h" void StandardController::StreamState(bool saving) { @@ -65,9 +66,9 @@ uint8_t StandardController::GetPortOutput() _stateBuffer >>= 1; if(_famiconDevice && _additionalController) { - if(_hasZapper) { + if(std::dynamic_pointer_cast(_additionalController) || std::dynamic_pointer_cast(_additionalController)) { returnValue |= _additionalController->GetPortOutput(); - } else if(_hasArkanoidController) { + } else if(std::dynamic_pointer_cast(_additionalController)) { returnValue |= std::dynamic_pointer_cast(_additionalController)->GetExpansionPortOutput(_port); } else { returnValue |= (_stateBufferFamicom & 0x01) << 1; @@ -86,11 +87,11 @@ void StandardController::RefreshStateBuffer() { _stateBuffer = GetControlState(); _lastButtonState = _stateBuffer; - if(_additionalController && !_hasZapper) { + if(_additionalController && !std::dynamic_pointer_cast(_additionalController)) { //Next 8 bits = Gamepad 3/4 if(_famiconDevice) { //Four player adapter (Famicom) - if(_hasArkanoidController) { + if(std::dynamic_pointer_cast(_additionalController) || std::dynamic_pointer_cast(_additionalController)) { _additionalController->RefreshStateBuffer(); } else { _stateBufferFamicom = _additionalController->GetControlState(); @@ -118,14 +119,6 @@ uint8_t StandardController::RefreshState() void StandardController::AddAdditionalController(shared_ptr controller) { - _hasZapper = false; - _hasArkanoidController = false; - - if(std::dynamic_pointer_cast(controller)) { - _hasZapper = true; - } else if(std::dynamic_pointer_cast(controller)) { - _hasArkanoidController = true; - } _additionalController = controller; } diff --git a/Core/StandardController.h b/Core/StandardController.h index 0afd6f86..5d4e9b64 100644 --- a/Core/StandardController.h +++ b/Core/StandardController.h @@ -10,8 +10,6 @@ private: uint32_t _stateBufferFamicom = 0; uint8_t _lastButtonState = 0; - bool _hasZapper = false; - bool _hasArkanoidController = false; shared_ptr _additionalController; uint8_t GetButtonState(); diff --git a/Core/VsZapper.h b/Core/VsZapper.h index 94894790..c76e42b7 100644 --- a/Core/VsZapper.h +++ b/Core/VsZapper.h @@ -5,7 +5,7 @@ class VsZapper : public Zapper { private: - uint32_t _stateBuffer; + uint32_t _stateBuffer = 0; protected: uint8_t RefreshState(); diff --git a/Core/Zapper.h b/Core/Zapper.h index 2da00c80..3c3d5a88 100644 --- a/Core/Zapper.h +++ b/Core/Zapper.h @@ -16,9 +16,9 @@ struct ZapperButtonState class Zapper : public BaseControlDevice { private: - bool _pulled; - int32_t _xPosition; - int32_t _yPosition; + bool _pulled = false; + int32_t _xPosition = -1; + int32_t _yPosition = -1; protected: virtual uint8_t RefreshState(); diff --git a/GUI.NET/Controls/ctrlRenderer.cs b/GUI.NET/Controls/ctrlRenderer.cs index 9599a8f4..718ab43f 100644 --- a/GUI.NET/Controls/ctrlRenderer.cs +++ b/GUI.NET/Controls/ctrlRenderer.cs @@ -20,6 +20,11 @@ namespace Mesen.GUI.Controls InitializeComponent(); } + private bool NeedMouseIcon + { + get { return InteropEmu.GetExpansionDevice() == InteropEmu.ExpansionPortDevice.OekaKidsTablet || InteropEmu.HasZapper(); } + } + private void ShowMouse() { if(_cursorHidden) { @@ -38,7 +43,7 @@ namespace Mesen.GUI.Controls protected override void OnDoubleClick(EventArgs e) { - if(!InteropEmu.HasZapper() && !InteropEmu.HasArkanoidPaddle()) { + if(!this.NeedMouseIcon && !InteropEmu.HasArkanoidPaddle()) { //Disable double clicking (used to switch to fullscreen mode) when using zapper/arkanoid controller base.OnDoubleClick(e); } @@ -48,13 +53,13 @@ namespace Mesen.GUI.Controls { if(!InteropEmu.IsRunning() || InteropEmu.IsPaused() || !InteropEmu.HasArkanoidPaddle()) { ShowMouse(); - } else if(InteropEmu.HasArkanoidPaddle() && !InteropEmu.HasZapper()) { + } else if(InteropEmu.HasArkanoidPaddle() && !this.NeedMouseIcon) { HideMouse(); } tmrMouse.Stop(); - if(InteropEmu.HasZapper()) { + if(this.NeedMouseIcon) { this.Cursor = Cursors.Cross; } else { this.Cursor = Cursors.Default; diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index 5cc6fdf7..22e42391 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -89,6 +89,7 @@ Zapper Four Player Adapter Arkanoid Controller + Oeka Kids Tablet RP2C03 diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index 985b9731..5afcf0c9 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -460,6 +460,7 @@ Zapper Adaptador para 4 jugadores Control Arkanoid + Oeka Kids Tablet RP2C03 diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 9816ef43..0076bc9a 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -460,6 +460,7 @@ Zapper Adapteur pour 4 joueurs Manette Arkanoid + Tablette Oeka Kids RP2C03 diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index e682c473..ae251f71 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -452,6 +452,7 @@ ガン 4-Player Adapter Arkanoidコントローラ + おえかきっずタブレット RP2C03 diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index 4bfbdb6b..263a8ed0 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -460,6 +460,7 @@ Zapper Адаптер на 4 игрока Arkanoid + Oeka Kids Tablet RP2C03 diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index debe4261..5e498056 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -390,6 +390,7 @@ namespace Mesen.GUI Zapper = 1, FourPlayerAdapter = 2, ArkanoidController = 3, + OekaKidsTablet = 4, } public enum VsInputType