diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 345a4a08..f909525b 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -775,6 +775,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)) { } @@ -906,8 +927,14 @@ std::unique_ptr LoadAddon(int type, void* userdata) // JP Boktai 3 cart = CreateFakeSolarSensorROM("U33J", nullptr, 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: Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); diff --git a/src/GBACart.h b/src/GBACart.h index 1ec7bff9..4a67719b 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, }; // See https://problemkaputt.de/gbatek.htm#gbacartridgeheader for details @@ -233,12 +235,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; @@ -252,11 +268,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 74b235f3..e7340c76 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -221,7 +221,9 @@ enum GBAAddon_SolarSensorBoktai1 = 3, GBAAddon_SolarSensorBoktai2 = 4, GBAAddon_SolarSensorBoktai3 = 5, - GBAAddon_MotionPak = 6, + GBAAddon_MotionPakHomebrew = 6, + GBAAddon_MotionPakRetail = 7, + GBAAddon_GuitarGrip = 8, }; class SPU; diff --git a/src/Platform.h b/src/Platform.h index 085e1e96..67d1a5b0 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 b81060d3..86516a91 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 203f0bd4..c3229a37 100644 --- a/src/frontend/qt_sdl/EmuInstance.cpp +++ b/src/frontend/qt_sdl/EmuInstance.cpp @@ -2148,8 +2148,12 @@ QString EmuInstance::gbaAddonName(int addon) return "Solar Sensor (Boktai 2)"; case GBAAddon_SolarSensorBoktai3: return "Solar Sensor (Boktai 3)"; - 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 10269c62..c83f30a8 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 d295640d..e866e2e7 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" }; @@ -212,7 +216,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 89483b7f..47857f94 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 0334fecd..0fcb6d55 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 a33789cf..3b6f2ead 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -321,7 +321,18 @@ 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_SolarSensorBoktai1, GBAAddon_SolarSensorBoktai2, GBAAddon_SolarSensorBoktai3, GBAAddon_MotionPak, -1}; + int addons[] = { + GBAAddon_RAMExpansion, + GBAAddon_RumblePak, + GBAAddon_SolarSensorBoktai1, + GBAAddon_SolarSensorBoktai2, + GBAAddon_SolarSensorBoktai3, + GBAAddon_MotionPakHomebrew, + GBAAddon_MotionPakRetail, + GBAAddon_GuitarGrip, + -1 + }; + for (int i = 0; addons[i] != -1; i++) { int addon = addons[i];