From 3ccfc262e4e0304f6d5f793ecfa3548be495c4ad Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Wed, 30 Oct 2024 21:39:24 +0100 Subject: [PATCH 1/7] Add DS Motion Pak emulation --- src/CMakeLists.txt | 1 + src/GBACart.cpp | 3 + src/GBACart.h | 20 ++++ src/GBACartMotionPak.cpp | 134 +++++++++++++++++++++++ src/NDS.h | 1 + src/Platform.h | 36 ++++++ src/frontend/qt_sdl/EmuInstance.cpp | 2 + src/frontend/qt_sdl/EmuInstance.h | 4 + src/frontend/qt_sdl/EmuInstanceInput.cpp | 39 ++++++- src/frontend/qt_sdl/Platform.cpp | 6 + src/frontend/qt_sdl/Window.cpp | 3 +- src/frontend/qt_sdl/main.cpp | 4 + 12 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 src/GBACartMotionPak.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f947d11..894f1a91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(core STATIC FATStorage.cpp FIFO.h GBACart.cpp + GBACartMotionPak.cpp GPU.cpp GPU2D.cpp GPU2D_Soft.cpp diff --git a/src/GBACart.cpp b/src/GBACart.cpp index a62aca6b..0bfb7358 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -874,6 +874,9 @@ void GBACartSlot::LoadAddon(void* userdata, int type) noexcept case GBAAddon_RumblePak: Cart = std::make_unique(userdata); break; + case GBAAddon_MotionPak: + Cart = std::make_unique(userdata); + break; default: Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); diff --git a/src/GBACart.h b/src/GBACart.h index 726a234d..26ec4d06 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -33,6 +33,7 @@ enum CartType GameSolarSensor = 0x102, RAMExpansion = 0x201, RumblePak = 0x202, + MotionPak = 0x203, }; // CartCommon -- base code shared by all cart types @@ -211,6 +212,25 @@ private: u16 RumbleState = 0; }; +// CartMotionPak -- DS Motion Pak (Kionix/homebrew) +class CartMotionPak : public CartCommon +{ +public: + CartMotionPak(void* userdata); + ~CartMotionPak() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; + u16 ShiftVal = 0; +}; + // possible inputs for GBA carts that might accept user input enum { diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp new file mode 100644 index 00000000..7d3e17a4 --- /dev/null +++ b/src/GBACartMotionPak.cpp @@ -0,0 +1,134 @@ +/* + Copyright 2016-2024 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include "NDS.h" +#include "GBACart.h" +#include "Platform.h" +#include +#include "math.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace GBACart +{ + +CartMotionPak::CartMotionPak(void* userdata) : + CartCommon(MotionPak), + UserData(userdata) +{ +} + +CartMotionPak::~CartMotionPak() = default; + +void CartMotionPak::Reset() +{ + ShiftVal = 0; +} + +void CartMotionPak::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + file->Var16(&ShiftVal); +} + +u16 CartMotionPak::ROMRead(u32 addr) const +{ + // CHECKME: Does this apply to the Kionix/homebrew cart as well? + return 0xFCFF; +} + +static int AccelerationToMotionPak(float accel) +{ + const float GRAVITY_M_S2 = 9.80665f; + const int COUNTS_PER_G = 819; + const int CENTER = 2048; + + return std::clamp( + (int) ((accel / GRAVITY_M_S2 * COUNTS_PER_G) + CENTER + 0.5), + 0, 4095 + ); +} + +static int RotationToMotionPak(float rot) +{ + const float DEGREES_PER_RAD = 180 / M_PI; + const float COUNTS_PER_DEG_PER_SEC = 0.825; + const int CENTER = 1680; + + return std::clamp( + (int) ((rot * DEGREES_PER_RAD * COUNTS_PER_DEG_PER_SEC) + CENTER + 0.5), + 0, 4095 + ); +} + +u8 CartMotionPak::SRAMRead(u32 addr) +{ + // CHECKME: SRAM address mask + addr &= 0xFFFF; + + switch (addr) + { + case 0: + // Read next byte + break; + case 2: + // Read X acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData)) << 4; + // CHECKME: First byte returned when reading acceleration/rotation + return 0; + case 4: + // Read Y acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData)) << 4; + return 0; + case 6: + // Read Z acceleration + ShiftVal = AccelerationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData)) << 4; + return 0; + case 8: + // Read Z rotation + ShiftVal = RotationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionRotationZ, UserData)) << 4; + return 0; + case 10: + // Identify cart + ShiftVal = 0xF00F; + return 0; + case 12: + case 14: + case 16: + case 18: + // Read/enable analog inputs + // + // These are not connected by defualt and require do-it-yourself cart + // modification, so there is no reason to emulate them. + ShiftVal = 0; + break; + } + + // Read high byte from the emulated shift register + u8 val = ShiftVal >> 8; + ShiftVal <<= 8; + return val; +} + +} + +} diff --git a/src/NDS.h b/src/NDS.h index b2bfb385..218a33b2 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -211,6 +211,7 @@ enum { GBAAddon_RAMExpansion = 1, GBAAddon_RumblePak = 2, + GBAAddon_MotionPak = 3, }; class SPU; diff --git a/src/Platform.h b/src/Platform.h index bef66593..ee714378 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -331,6 +331,42 @@ void Addon_RumbleStart(u32 len, void* userdata); // rumble effects on the connected game controller, if available. void Addon_RumbleStop(void* userdata); +enum MotionQueryType +{ + /** + * @brief X axis acceleration, measured in SI meters per second squared. + * On a DS, the X axis refers to the top screen X-axis (left ... right). + */ + MotionAccelerationX, + /** + * @brief Y axis acceleration, measured in SI meters per second squared. + * On a DS, the Y axis refers to the top screen Y-axis (bottom ... top). + */ + MotionAccelerationY, + /** + * @brief Z axis acceleration, measured in SI meters per second squared. + * On a DS, the Z axis refers to the axis perpendicular to the top screen (farther ... closer). + */ + MotionAccelerationZ, + /** + * @brief X axis rotation, measured in radians per second. + */ + MotionRotationX, + /** + * @brief Y axis rotation, measured in radians per second. + */ + MotionRotationY, + /** + * @brief Z axis rotation, measured in radians per second. + */ + MotionRotationZ, +}; + +// Called by the DS Motion Pak emulation to query the game controller's +// aceelration and rotation, if available. +// @param type The value being queried. +float Addon_MotionQuery(MotionQueryType type, void* userdata); + struct DynamicLibrary; /** diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index e9550f8c..3ea44c7c 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -2090,6 +2090,8 @@ QString EmuInstance::gbaAddonName(int addon) return "Rumble Pak"; case GBAAddon_RAMExpansion: return "Memory expansion"; + case GBAAddon_MotionPak: + return "Motion Pak"; } return "???"; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index 658247f0..e299bdf7 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -21,6 +21,7 @@ #include +#include "Platform.h" #include "main.h" #include "NDS.h" #include "EmuThread.h" @@ -142,6 +143,7 @@ public: void inputLoadConfig(); void inputRumbleStart(melonDS::u32 len_ms); void inputRumbleStop(); + float inputMotionQuery(melonDS::Platform::MotionQueryType type); void setJoystick(int id); int getJoystickID() { return joystickID; } @@ -318,6 +320,8 @@ private: int joystickID; SDL_Joystick* joystick; SDL_GameController* controller; + bool hasAccelerometer = false; + bool hasGyroscope = false; bool hasRumble = false; bool isRumbling = false; diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index bb06c242..d76f8a0e 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -19,6 +19,9 @@ #include #include +#include "Platform.h" +#include "SDL_gamecontroller.h" +#include "SDL_sensor.h" #include "main.h" #include "Config.h" @@ -77,6 +80,8 @@ void EmuInstance::inputInit() joystick = nullptr; controller = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; isRumbling = false; inputLoadConfig(); } @@ -124,6 +129,24 @@ void EmuInstance::inputRumbleStop() } } +float EmuInstance::inputMotionQuery(melonDS::Platform::MotionQueryType type) +{ + float values[3]; + if (type <= melonDS::Platform::MotionAccelerationZ) + { + if (controller && hasAccelerometer) + if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_ACCEL, values, 3) == 0) + return values[type % 3]; + } + else if (type <= melonDS::Platform::MotionRotationZ) + { + if (controller && hasGyroscope) + if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_GYRO, values, 3) == 0) + return values[type % 3]; + } + return 0.0f; +} + void EmuInstance::setJoystick(int id) { @@ -143,6 +166,8 @@ void EmuInstance::openJoystick() controller = nullptr; joystick = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; return; } @@ -159,9 +184,17 @@ void EmuInstance::openJoystick() if (controller) { if (SDL_GameControllerHasRumble(controller)) - { + { hasRumble = true; - } + } + if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_ACCEL)) + { + hasAccelerometer = SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE) == 0; + } + if (SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)) + { + hasGyroscope = SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) == 0; + } } } @@ -172,6 +205,8 @@ void EmuInstance::closeJoystick() SDL_GameControllerClose(controller); controller = nullptr; hasRumble = false; + hasAccelerometer = false; + hasGyroscope = false; } if (joystick) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 541b51f2..5a912f13 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -35,6 +35,7 @@ #include "Platform.h" #include "Config.h" +#include "EmuInstance.h" #include "main.h" #include "CameraManager.h" #include "Net.h" @@ -559,6 +560,11 @@ void Addon_RumbleStop(void* userdata) ((EmuInstance*)userdata)->inputRumbleStop(); } +float Addon_MotionQuery(MotionQueryType type, void* userdata) +{ + return ((EmuInstance*)userdata)->inputMotionQuery(type); +} + DynamicLibrary* DynamicLibrary_Load(const char* lib) { return (DynamicLibrary*) SDL_LoadObject(lib); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 430f0d6e..4a80ad90 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -16,6 +16,7 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include "NDS.h" #include #include #include @@ -319,7 +320,7 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu * submenu = menu->addMenu("Insert add-on cart"); QAction *act; - int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, -1}; + int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, GBAAddon_MotionPak, -1}; for (int i = 0; addons[i] != -1; i++) { int addon = addons[i]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index d940340a..103dc5cf 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -307,6 +307,10 @@ int main(int argc, char** argv) { printf("SDL couldn't init joystick\n"); } + if (SDL_Init(SDL_INIT_SENSOR) < 0) + { + printf("SDL couldn't init motion sensors\n"); + } if (SDL_Init(SDL_INIT_AUDIO) < 0) { const char* err = SDL_GetError(); From 593642ff5707a2b1e93b5fe51adaaf50ae02c1e7 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Wed, 30 Oct 2024 22:50:07 +0100 Subject: [PATCH 2/7] Add retail Motion Pak emulation, Guitar Grip emulation --- src/GBACart.cpp | 31 ++++++- src/GBACart.h | 50 ++++++++++-- src/GBACartMotionPak.cpp | 80 +++++++++++++++++-- src/NDS.h | 4 +- src/Platform.h | 12 +++ src/frontend/qt_sdl/Config.cpp | 8 ++ src/frontend/qt_sdl/EmuInstance.cpp | 8 +- src/frontend/qt_sdl/EmuInstance.h | 6 ++ src/frontend/qt_sdl/EmuInstanceInput.cpp | 7 +- .../qt_sdl/InputConfig/InputConfigDialog.h | 8 ++ src/frontend/qt_sdl/Platform.cpp | 12 +++ src/frontend/qt_sdl/Window.cpp | 9 ++- 12 files changed, 214 insertions(+), 21 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 0bfb7358..5e0a261a 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -724,6 +724,27 @@ void CartRumblePak::ROMWrite(u32 addr, u16 val) } } +CartGuitarGrip::CartGuitarGrip(void* userdata) : + CartCommon(GuitarGrip), + UserData(userdata) +{ +} + +CartGuitarGrip::~CartGuitarGrip() = default; + +u16 CartGuitarGrip::ROMRead(u32 addr) const +{ + return 0xF9FF; +} + +u8 CartGuitarGrip::SRAMRead(u32 addr) +{ + return ~((Platform::Addon_KeyDown(Platform::KeyGuitarGripGreen, UserData) ? 0x40 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripRed, UserData) ? 0x20 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripYellow, UserData) ? 0x10 : 0) + | (Platform::Addon_KeyDown(Platform::KeyGuitarGripBlue, UserData) ? 0x08 : 0)); +} + GBACartSlot::GBACartSlot(melonDS::NDS& nds, std::unique_ptr&& cart) noexcept : NDS(nds), Cart(std::move(cart)) { } @@ -874,8 +895,14 @@ void GBACartSlot::LoadAddon(void* userdata, int type) noexcept case GBAAddon_RumblePak: Cart = std::make_unique(userdata); break; - case GBAAddon_MotionPak: - Cart = std::make_unique(userdata); + case GBAAddon_MotionPakHomebrew: + Cart = std::make_unique(userdata); + break; + case GBAAddon_MotionPakRetail: + Cart = std::make_unique(userdata); + break; + case GBAAddon_GuitarGrip: + Cart = std::make_unique(userdata); break; default: diff --git a/src/GBACart.h b/src/GBACart.h index 26ec4d06..9ab42f1a 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -33,7 +33,9 @@ enum CartType GameSolarSensor = 0x102, RAMExpansion = 0x201, RumblePak = 0x202, - MotionPak = 0x203, + MotionPakHomebrew = 0x203, + MotionPakRetail = 0x204, + GuitarGrip = 0x205, }; // CartCommon -- base code shared by all cart types @@ -212,12 +214,26 @@ private: u16 RumbleState = 0; }; -// CartMotionPak -- DS Motion Pak (Kionix/homebrew) -class CartMotionPak : public CartCommon +// CartGuitarGrip -- DS Guitar Grip (used in various NDS games) +class CartGuitarGrip : public CartCommon { public: - CartMotionPak(void* userdata); - ~CartMotionPak() override; + CartGuitarGrip(void* userdata); + ~CartGuitarGrip() override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; +}; + +// CartMotionPakHomebrew -- DS Motion Pak (Homebrew) +class CartMotionPakHomebrew : public CartCommon +{ +public: + CartMotionPakHomebrew(void* userdata); + ~CartMotionPakHomebrew() override; void Reset() override; @@ -231,11 +247,35 @@ private: u16 ShiftVal = 0; }; +// CartMotionPakRetail -- DS Motion Pack (Retail) +class CartMotionPakRetail : public CartCommon +{ +public: + CartMotionPakRetail(void* userdata); + ~CartMotionPakRetail() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + u8 SRAMRead(u32 addr) override; + +private: + void* UserData; + u8 Value; + u8 Step = 16; +}; + // possible inputs for GBA carts that might accept user input enum { Input_SolarSensorDown = 0, Input_SolarSensorUp, + Input_GuitarGripGreen, + Input_GuitarGripRed, + Input_GuitarGripYellow, + Input_GuitarGripBlue, }; class GBACartSlot diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp index 7d3e17a4..9d564622 100644 --- a/src/GBACartMotionPak.cpp +++ b/src/GBACartMotionPak.cpp @@ -31,28 +31,28 @@ using Platform::LogLevel; namespace GBACart { -CartMotionPak::CartMotionPak(void* userdata) : - CartCommon(MotionPak), +CartMotionPakHomebrew::CartMotionPakHomebrew(void* userdata) : + CartCommon(MotionPakHomebrew), UserData(userdata) { } -CartMotionPak::~CartMotionPak() = default; +CartMotionPakHomebrew::~CartMotionPakHomebrew() = default; -void CartMotionPak::Reset() +void CartMotionPakHomebrew::Reset() { ShiftVal = 0; } -void CartMotionPak::DoSavestate(Savestate* file) +void CartMotionPakHomebrew::DoSavestate(Savestate* file) { CartCommon::DoSavestate(file); file->Var16(&ShiftVal); } -u16 CartMotionPak::ROMRead(u32 addr) const +u16 CartMotionPakHomebrew::ROMRead(u32 addr) const { - // CHECKME: Does this apply to the Kionix/homebrew cart as well? + // CHECKME: Does this apply to the homebrew cart as well? return 0xFCFF; } @@ -80,7 +80,7 @@ static int RotationToMotionPak(float rot) ); } -u8 CartMotionPak::SRAMRead(u32 addr) +u8 CartMotionPakHomebrew::SRAMRead(u32 addr) { // CHECKME: SRAM address mask addr &= 0xFFFF; @@ -129,6 +129,70 @@ u8 CartMotionPak::SRAMRead(u32 addr) return val; } +static int AccelerationToMotionPakRetail(float accel) +{ + // Formula provided by xperia64 (melonDS/#74) + const float GRAVITY_M_S2 = 9.80665f; + const float VOLTAGE = 3.3f; + + return std::clamp( + (int) ((accel * (VOLTAGE / 5) / GRAVITY_M_S2 + (VOLTAGE / 2)) * 256 / VOLTAGE), + 0, 254 + ); +} + +CartMotionPakRetail::CartMotionPakRetail(void* userdata) : + CartCommon(MotionPakRetail), + UserData(userdata) +{ +} + +CartMotionPakRetail::~CartMotionPakRetail() = default; + +void CartMotionPakRetail::Reset() +{ + Value = 0; + Step = 16; +} + +void CartMotionPakRetail::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + file->Var8(&Value); + file->Var8(&Step); +} + +u16 CartMotionPakRetail::ROMRead(u32 addr) const +{ + return 0xFCFF; +} + +u8 CartMotionPakRetail::SRAMRead(u32 addr) +{ + switch (Step) + { + case 0: // Synchronization - read 0xFF + Value = 0xFF; + break; + case 4: // X acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationX, UserData)); + break; + case 8: // Y acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationY, UserData)); + break; + case 12: // Z acceleration + Value = AccelerationToMotionPakRetail(Platform::Addon_MotionQuery(Platform::MotionAccelerationZ, UserData)); + break; + case 16: // Synchronization - read 0b00 + Step = 0; + return 0; + } + + int shift = 6 - ((Step & 3) * 2); + Step++; + return (Value >> shift) & 0x03; +} + } } diff --git a/src/NDS.h b/src/NDS.h index 218a33b2..3dfed01d 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -211,7 +211,9 @@ enum { GBAAddon_RAMExpansion = 1, GBAAddon_RumblePak = 2, - GBAAddon_MotionPak = 3, + GBAAddon_MotionPakHomebrew = 3, + GBAAddon_MotionPakRetail = 4, + GBAAddon_GuitarGrip = 5, }; class SPU; diff --git a/src/Platform.h b/src/Platform.h index ee714378..d7108b20 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -322,6 +322,18 @@ void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, v // interface for addon inputs +enum KeyType +{ + KeyGuitarGripGreen, + KeyGuitarGripRed, + KeyGuitarGripYellow, + KeyGuitarGripBlue, +}; + +// Check if a given key is being pressed. +// @param type The type of the key to check. +bool Addon_KeyDown(KeyType type, void* userdata); + // Called by the DS Rumble Pak emulation to start the necessary // rumble effects on the connected game controller, if available. // @param len The duration of the controller rumble effect in milliseconds. diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 02be5b65..90eb6a6c 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -169,6 +169,10 @@ LegacyEntry LegacyFile[] = {"HKKey_PowerButton", 0, "Keyboard.HK_PowerButton", true}, {"HKKey_VolumeUp", 0, "Keyboard.HK_VolumeUp", true}, {"HKKey_VolumeDown", 0, "Keyboard.HK_VolumeDown", true}, + {"HKKey_GuitarGripGreen", 0, "Keyboard.HK_GuitarGripGreen", true}, + {"HKKey_GuitarGripRed", 0, "Keyboard.HK_GuitarGripRed", true}, + {"HKKey_GuitarGripYellow", 0, "Keyboard.HK_GuitarGripYellow", true}, + {"HKKey_GuitarGripBlue", 0, "Keyboard.HK_GuitarGripBlue", true}, {"HKJoy_Lid", 0, "Joystick.HK_Lid", true}, {"HKJoy_Mic", 0, "Joystick.HK_Mic", true}, @@ -185,6 +189,10 @@ LegacyEntry LegacyFile[] = {"HKJoy_PowerButton", 0, "Joystick.HK_PowerButton", true}, {"HKJoy_VolumeUp", 0, "Joystick.HK_VolumeUp", true}, {"HKJoy_VolumeDown", 0, "Joystick.HK_VolumeDown", true}, + {"HKJoy_GuitarGripGreen", 0, "Joystick.HK_GuitarGripGreen", true}, + {"HKJoy_GuitarGripRed", 0, "Joystick.HK_GuitarGripRed", true}, + {"HKJoy_GuitarGripYellow", 0, "Joystick.HK_GuitarGripYellow", true}, + {"HKJoy_GuitarGripBlue", 0, "Joystick.HK_GuitarGripBlue", true}, {"JoystickID", 0, "JoystickID", true}, diff --git a/src/frontend/qt_sdl/EmuInstance.cpp b/src/frontend/qt_sdl/EmuInstance.cpp index 3ea44c7c..07a4a41d 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -2090,8 +2090,12 @@ QString EmuInstance::gbaAddonName(int addon) return "Rumble Pak"; case GBAAddon_RAMExpansion: return "Memory expansion"; - case GBAAddon_MotionPak: - return "Motion Pak"; + case GBAAddon_MotionPakHomebrew: + return "Motion Pak (Homebrew)"; + case GBAAddon_MotionPakRetail: + return "Motion Pack (Retail)"; + case GBAAddon_GuitarGrip: + return "Guitar Grip"; } return "???"; diff --git a/src/frontend/qt_sdl/EmuInstance.h b/src/frontend/qt_sdl/EmuInstance.h index e299bdf7..4752eb12 100644 --- a/src/frontend/qt_sdl/EmuInstance.h +++ b/src/frontend/qt_sdl/EmuInstance.h @@ -51,6 +51,10 @@ enum HK_SlowMo, HK_FastForwardToggle, HK_SlowMoToggle, + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, HK_MAX }; @@ -143,6 +147,8 @@ public: void inputLoadConfig(); void inputRumbleStart(melonDS::u32 len_ms); void inputRumbleStop(); + + bool inputHotkeyDown(int id) { return hotkeyDown(id); } float inputMotionQuery(melonDS::Platform::MotionQueryType type); void setJoystick(int id); diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index d76f8a0e..eb47635f 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -62,7 +62,11 @@ const char* EmuInstance::hotkeyNames[HK_MAX] = "HK_VolumeDown", "HK_SlowMo", "HK_FastForwardToggle", - "HK_SlowMoToggle" + "HK_SlowMoToggle", + "HK_GuitarGripGreen", + "HK_GuitarGripRed", + "HK_GuitarGripYellow", + "HK_GuitarGripBlue" }; @@ -208,7 +212,6 @@ void EmuInstance::closeJoystick() hasAccelerometer = false; hasGyroscope = false; } - if (joystick) { SDL_JoystickClose(joystick); diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 3337228f..5f016b5b 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -32,12 +32,20 @@ static constexpr std::initializer_list hk_addons = { HK_SolarSensorIncrease, HK_SolarSensorDecrease, + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, }; static constexpr std::initializer_list hk_addons_labels = { "[Boktai] Sunlight + ", "[Boktai] Sunlight - ", + "[Guitar Grip] Green", + "[Guitar Grip] Red", + "[Guitar Grip] Yellow", + "[Guitar Grip] Blue", }; static_assert(hk_addons.size() == hk_addons_labels.size()); diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 5a912f13..f7f44d1e 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -550,6 +550,18 @@ void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, v return camManager[num]->captureFrame(frame, width, height, yuv); } +static const int hotkeyMap[] = { + HK_GuitarGripGreen, + HK_GuitarGripRed, + HK_GuitarGripYellow, + HK_GuitarGripBlue, +}; + +bool Addon_KeyDown(KeyType type, void* userdata) +{ + return ((EmuInstance*)userdata)->inputHotkeyDown(hotkeyMap[type]); +} + void Addon_RumbleStart(u32 len, void* userdata) { ((EmuInstance*)userdata)->inputRumbleStart(len); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 4a80ad90..f90cad3d 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -320,7 +320,14 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) : QMenu * submenu = menu->addMenu("Insert add-on cart"); QAction *act; - int addons[] = {GBAAddon_RAMExpansion, GBAAddon_RumblePak, GBAAddon_MotionPak, -1}; + int addons[] = { + GBAAddon_RAMExpansion, + GBAAddon_RumblePak, + GBAAddon_MotionPakHomebrew, + GBAAddon_MotionPakRetail, + GBAAddon_GuitarGrip, + -1 + }; for (int i = 0; addons[i] != -1; i++) { int addon = addons[i]; From f9a426bba60590bb0272d204b75b18a8140bef84 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Thu, 31 Oct 2024 14:34:30 +0100 Subject: [PATCH 3/7] Simplify Motion Pak acceleration conversion formula --- src/GBACartMotionPak.cpp | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp index 9d564622..2d77022f 100644 --- a/src/GBACartMotionPak.cpp +++ b/src/GBACartMotionPak.cpp @@ -59,15 +59,23 @@ u16 CartMotionPakHomebrew::ROMRead(u32 addr) const static int AccelerationToMotionPak(float accel) { const float GRAVITY_M_S2 = 9.80665f; - const int COUNTS_PER_G = 819; - const int CENTER = 2048; return std::clamp( - (int) ((accel / GRAVITY_M_S2 * COUNTS_PER_G) + CENTER + 0.5), + (int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 4096), 0, 4095 ); } +static int AccelerationToMotionPakRetail(float accel) +{ + const float GRAVITY_M_S2 = 9.80665f; + + return std::clamp( + (int) ((accel / (5 * GRAVITY_M_S2) + 0.5) * 256), + 0, 254 + ); +} + static int RotationToMotionPak(float rot) { const float DEGREES_PER_RAD = 180 / M_PI; @@ -129,18 +137,6 @@ u8 CartMotionPakHomebrew::SRAMRead(u32 addr) return val; } -static int AccelerationToMotionPakRetail(float accel) -{ - // Formula provided by xperia64 (melonDS/#74) - const float GRAVITY_M_S2 = 9.80665f; - const float VOLTAGE = 3.3f; - - return std::clamp( - (int) ((accel * (VOLTAGE / 5) / GRAVITY_M_S2 + (VOLTAGE / 2)) * 256 / VOLTAGE), - 0, 254 - ); -} - CartMotionPakRetail::CartMotionPakRetail(void* userdata) : CartCommon(MotionPakRetail), UserData(userdata) From 742cff5dbc968d4c5784b80515be60ed1120a366 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Thu, 31 Oct 2024 15:46:43 +0100 Subject: [PATCH 4/7] Fix Motion Pak emulation axes --- src/GBACartMotionPak.cpp | 3 ++- src/frontend/qt_sdl/EmuInstanceInput.cpp | 26 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp index 2d77022f..047b0f97 100644 --- a/src/GBACartMotionPak.cpp +++ b/src/GBACartMotionPak.cpp @@ -113,7 +113,8 @@ u8 CartMotionPakHomebrew::SRAMRead(u32 addr) return 0; case 8: // Read Z rotation - ShiftVal = RotationToMotionPak(Platform::Addon_MotionQuery(Platform::MotionRotationZ, UserData)) << 4; + // CHECKME: This is a guess, compare with real hardware + ShiftVal = RotationToMotionPak(-Platform::Addon_MotionQuery(Platform::MotionRotationZ, UserData)) << 4; return 0; case 10: // Identify cart diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index eb47635f..fb3b5f63 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -140,13 +140,35 @@ float EmuInstance::inputMotionQuery(melonDS::Platform::MotionQueryType type) { if (controller && hasAccelerometer) if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_ACCEL, values, 3) == 0) - return values[type % 3]; + { + // Map values from DS console orientation to SDL controller orientation. + switch (type) + { + case melonDS::Platform::MotionAccelerationX: + return values[0]; + case melonDS::Platform::MotionAccelerationY: + return -values[2]; + case melonDS::Platform::MotionAccelerationZ: + return values[1]; + } + } } else if (type <= melonDS::Platform::MotionRotationZ) { if (controller && hasGyroscope) if (SDL_GameControllerGetSensorData(controller, SDL_SENSOR_GYRO, values, 3) == 0) - return values[type % 3]; + { + // Map values from DS console orientation to SDL controller orientation. + switch (type) + { + case melonDS::Platform::MotionRotationX: + return values[0]; + case melonDS::Platform::MotionRotationY: + return -values[2]; + case melonDS::Platform::MotionRotationZ: + return values[1]; + } + } } return 0.0f; } From c392a4c3996bf7348ebb3826b0ccec242de58b14 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Thu, 31 Oct 2024 17:03:39 +0100 Subject: [PATCH 5/7] Motion Pak: Emulate console laying on a flat table when motion input is not detected --- src/frontend/qt_sdl/EmuInstanceInput.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/EmuInstanceInput.cpp b/src/frontend/qt_sdl/EmuInstanceInput.cpp index fb3b5f63..a0a40fd2 100644 --- a/src/frontend/qt_sdl/EmuInstanceInput.cpp +++ b/src/frontend/qt_sdl/EmuInstanceInput.cpp @@ -170,6 +170,8 @@ float EmuInstance::inputMotionQuery(melonDS::Platform::MotionQueryType type) } } } + if (type == melonDS::Platform::MotionAccelerationZ) + return SDL_STANDARD_GRAVITY; return 0.0f; } From ec5f388860745de567dc8650dfd9faac91a11211 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Fri, 1 Nov 2024 00:14:44 +0100 Subject: [PATCH 6/7] Motion Pak: Add comment --- src/GBACartMotionPak.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp index 047b0f97..44743464 100644 --- a/src/GBACartMotionPak.cpp +++ b/src/GBACartMotionPak.cpp @@ -161,6 +161,7 @@ void CartMotionPakRetail::DoSavestate(Savestate* file) u16 CartMotionPakRetail::ROMRead(u32 addr) const { + // CHECKME: Retail cartridge seems to return 0x7C7C. return 0xFCFF; } From 30c93d827ff60ea6da572dc458d8bb372476115f Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Tue, 5 Nov 2024 18:49:31 +0100 Subject: [PATCH 7/7] GBACartMotionPak: Update comment --- src/GBACartMotionPak.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GBACartMotionPak.cpp b/src/GBACartMotionPak.cpp index 44743464..c213bb7f 100644 --- a/src/GBACartMotionPak.cpp +++ b/src/GBACartMotionPak.cpp @@ -161,7 +161,7 @@ void CartMotionPakRetail::DoSavestate(Savestate* file) u16 CartMotionPakRetail::ROMRead(u32 addr) const { - // CHECKME: Retail cartridge seems to return 0x7C7C. + // A9-A8 is pulled low on a real Motion Pack. return 0xFCFF; }