Add retail Motion Pak emulation, Guitar Grip emulation

This commit is contained in:
Adrian Siekierka 2024-10-30 22:50:07 +01:00
parent 3ccfc262e4
commit 593642ff57
12 changed files with 214 additions and 21 deletions

View File

@ -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<CartCommon>&& cart) noexcept : NDS(nds), Cart(std::move(cart)) GBACartSlot::GBACartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& cart) noexcept : NDS(nds), Cart(std::move(cart))
{ {
} }
@ -874,8 +895,14 @@ void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
case GBAAddon_RumblePak: case GBAAddon_RumblePak:
Cart = std::make_unique<CartRumblePak>(userdata); Cart = std::make_unique<CartRumblePak>(userdata);
break; break;
case GBAAddon_MotionPak: case GBAAddon_MotionPakHomebrew:
Cart = std::make_unique<CartMotionPak>(userdata); Cart = std::make_unique<CartMotionPakHomebrew>(userdata);
break;
case GBAAddon_MotionPakRetail:
Cart = std::make_unique<CartMotionPakRetail>(userdata);
break;
case GBAAddon_GuitarGrip:
Cart = std::make_unique<CartGuitarGrip>(userdata);
break; break;
default: default:

View File

@ -33,7 +33,9 @@ enum CartType
GameSolarSensor = 0x102, GameSolarSensor = 0x102,
RAMExpansion = 0x201, RAMExpansion = 0x201,
RumblePak = 0x202, RumblePak = 0x202,
MotionPak = 0x203, MotionPakHomebrew = 0x203,
MotionPakRetail = 0x204,
GuitarGrip = 0x205,
}; };
// CartCommon -- base code shared by all cart types // CartCommon -- base code shared by all cart types
@ -212,12 +214,26 @@ private:
u16 RumbleState = 0; u16 RumbleState = 0;
}; };
// CartMotionPak -- DS Motion Pak (Kionix/homebrew) // CartGuitarGrip -- DS Guitar Grip (used in various NDS games)
class CartMotionPak : public CartCommon class CartGuitarGrip : public CartCommon
{ {
public: public:
CartMotionPak(void* userdata); CartGuitarGrip(void* userdata);
~CartMotionPak() override; ~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; void Reset() override;
@ -231,11 +247,35 @@ private:
u16 ShiftVal = 0; 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 // possible inputs for GBA carts that might accept user input
enum enum
{ {
Input_SolarSensorDown = 0, Input_SolarSensorDown = 0,
Input_SolarSensorUp, Input_SolarSensorUp,
Input_GuitarGripGreen,
Input_GuitarGripRed,
Input_GuitarGripYellow,
Input_GuitarGripBlue,
}; };
class GBACartSlot class GBACartSlot

View File

@ -31,28 +31,28 @@ using Platform::LogLevel;
namespace GBACart namespace GBACart
{ {
CartMotionPak::CartMotionPak(void* userdata) : CartMotionPakHomebrew::CartMotionPakHomebrew(void* userdata) :
CartCommon(MotionPak), CartCommon(MotionPakHomebrew),
UserData(userdata) UserData(userdata)
{ {
} }
CartMotionPak::~CartMotionPak() = default; CartMotionPakHomebrew::~CartMotionPakHomebrew() = default;
void CartMotionPak::Reset() void CartMotionPakHomebrew::Reset()
{ {
ShiftVal = 0; ShiftVal = 0;
} }
void CartMotionPak::DoSavestate(Savestate* file) void CartMotionPakHomebrew::DoSavestate(Savestate* file)
{ {
CartCommon::DoSavestate(file); CartCommon::DoSavestate(file);
file->Var16(&ShiftVal); 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; 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 // CHECKME: SRAM address mask
addr &= 0xFFFF; addr &= 0xFFFF;
@ -129,6 +129,70 @@ u8 CartMotionPak::SRAMRead(u32 addr)
return val; 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;
}
} }
} }

View File

@ -211,7 +211,9 @@ enum
{ {
GBAAddon_RAMExpansion = 1, GBAAddon_RAMExpansion = 1,
GBAAddon_RumblePak = 2, GBAAddon_RumblePak = 2,
GBAAddon_MotionPak = 3, GBAAddon_MotionPakHomebrew = 3,
GBAAddon_MotionPakRetail = 4,
GBAAddon_GuitarGrip = 5,
}; };
class SPU; class SPU;

View File

@ -322,6 +322,18 @@ void Camera_CaptureFrame(int num, u32* frame, int width, int height, bool yuv, v
// interface for addon inputs // 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 // Called by the DS Rumble Pak emulation to start the necessary
// rumble effects on the connected game controller, if available. // rumble effects on the connected game controller, if available.
// @param len The duration of the controller rumble effect in milliseconds. // @param len The duration of the controller rumble effect in milliseconds.

View File

@ -169,6 +169,10 @@ LegacyEntry LegacyFile[] =
{"HKKey_PowerButton", 0, "Keyboard.HK_PowerButton", true}, {"HKKey_PowerButton", 0, "Keyboard.HK_PowerButton", true},
{"HKKey_VolumeUp", 0, "Keyboard.HK_VolumeUp", true}, {"HKKey_VolumeUp", 0, "Keyboard.HK_VolumeUp", true},
{"HKKey_VolumeDown", 0, "Keyboard.HK_VolumeDown", 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_Lid", 0, "Joystick.HK_Lid", true},
{"HKJoy_Mic", 0, "Joystick.HK_Mic", true}, {"HKJoy_Mic", 0, "Joystick.HK_Mic", true},
@ -185,6 +189,10 @@ LegacyEntry LegacyFile[] =
{"HKJoy_PowerButton", 0, "Joystick.HK_PowerButton", true}, {"HKJoy_PowerButton", 0, "Joystick.HK_PowerButton", true},
{"HKJoy_VolumeUp", 0, "Joystick.HK_VolumeUp", true}, {"HKJoy_VolumeUp", 0, "Joystick.HK_VolumeUp", true},
{"HKJoy_VolumeDown", 0, "Joystick.HK_VolumeDown", 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}, {"JoystickID", 0, "JoystickID", true},

View File

@ -2090,8 +2090,12 @@ QString EmuInstance::gbaAddonName(int addon)
return "Rumble Pak"; return "Rumble Pak";
case GBAAddon_RAMExpansion: case GBAAddon_RAMExpansion:
return "Memory expansion"; return "Memory expansion";
case GBAAddon_MotionPak: case GBAAddon_MotionPakHomebrew:
return "Motion Pak"; return "Motion Pak (Homebrew)";
case GBAAddon_MotionPakRetail:
return "Motion Pack (Retail)";
case GBAAddon_GuitarGrip:
return "Guitar Grip";
} }
return "???"; return "???";

View File

@ -51,6 +51,10 @@ enum
HK_SlowMo, HK_SlowMo,
HK_FastForwardToggle, HK_FastForwardToggle,
HK_SlowMoToggle, HK_SlowMoToggle,
HK_GuitarGripGreen,
HK_GuitarGripRed,
HK_GuitarGripYellow,
HK_GuitarGripBlue,
HK_MAX HK_MAX
}; };
@ -143,6 +147,8 @@ public:
void inputLoadConfig(); void inputLoadConfig();
void inputRumbleStart(melonDS::u32 len_ms); void inputRumbleStart(melonDS::u32 len_ms);
void inputRumbleStop(); void inputRumbleStop();
bool inputHotkeyDown(int id) { return hotkeyDown(id); }
float inputMotionQuery(melonDS::Platform::MotionQueryType type); float inputMotionQuery(melonDS::Platform::MotionQueryType type);
void setJoystick(int id); void setJoystick(int id);

View File

@ -62,7 +62,11 @@ const char* EmuInstance::hotkeyNames[HK_MAX] =
"HK_VolumeDown", "HK_VolumeDown",
"HK_SlowMo", "HK_SlowMo",
"HK_FastForwardToggle", "HK_FastForwardToggle",
"HK_SlowMoToggle" "HK_SlowMoToggle",
"HK_GuitarGripGreen",
"HK_GuitarGripRed",
"HK_GuitarGripYellow",
"HK_GuitarGripBlue"
}; };
@ -208,7 +212,6 @@ void EmuInstance::closeJoystick()
hasAccelerometer = false; hasAccelerometer = false;
hasGyroscope = false; hasGyroscope = false;
} }
if (joystick) if (joystick)
{ {
SDL_JoystickClose(joystick); SDL_JoystickClose(joystick);

View File

@ -32,12 +32,20 @@ static constexpr std::initializer_list<int> hk_addons =
{ {
HK_SolarSensorIncrease, HK_SolarSensorIncrease,
HK_SolarSensorDecrease, HK_SolarSensorDecrease,
HK_GuitarGripGreen,
HK_GuitarGripRed,
HK_GuitarGripYellow,
HK_GuitarGripBlue,
}; };
static constexpr std::initializer_list<const char*> hk_addons_labels = static constexpr std::initializer_list<const char*> hk_addons_labels =
{ {
"[Boktai] Sunlight + ", "[Boktai] Sunlight + ",
"[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()); static_assert(hk_addons.size() == hk_addons_labels.size());

View File

@ -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); 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) void Addon_RumbleStart(u32 len, void* userdata)
{ {
((EmuInstance*)userdata)->inputRumbleStart(len); ((EmuInstance*)userdata)->inputRumbleStart(len);

View File

@ -320,7 +320,14 @@ MainWindow::MainWindow(int id, EmuInstance* inst, QWidget* parent) :
QMenu * submenu = menu->addMenu("Insert add-on cart"); QMenu * submenu = menu->addMenu("Insert add-on cart");
QAction *act; 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++) for (int i = 0; addons[i] != -1; i++)
{ {
int addon = addons[i]; int addon = addons[i];