From ba86c0288d3fed6ce6c348b8b0748b73a61a25b8 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Wed, 18 Nov 2020 11:19:34 +0000 Subject: [PATCH] Add rumble support --- libgambatte/libretro/libretro.cpp | 102 ++++++++++++++++++- libgambatte/libretro/libretro_core_options.h | 20 ++++ libgambatte/src/mem/cartridge.cpp | 40 +++++--- 3 files changed, 145 insertions(+), 17 deletions(-) diff --git a/libgambatte/libretro/libretro.cpp b/libgambatte/libretro/libretro.cpp index f63c7018..2fdb1d6d 100644 --- a/libgambatte/libretro/libretro.cpp +++ b/libgambatte/libretro/libretro.cpp @@ -513,6 +513,75 @@ static void check_frame_blend_variable(void) /* Interframe blending END */ /***************************/ +/************************/ +/* Rumble support START */ +/************************/ + +static struct retro_rumble_interface rumble = {0}; +static uint16_t rumble_strength_last = 0; +static uint16_t rumble_strength_up = 0; +static uint16_t rumble_strength_down = 0; +static uint16_t rumble_level = 0; +static bool rumble_active = false; + +void cartridge_set_rumble(unsigned active) +{ + if (!rumble.set_rumble_state || + !rumble_level) + return; + + if (active) + rumble_strength_up++; + else + rumble_strength_down++; + + rumble_active = true; +} + +static void apply_rumble(void) +{ + uint16_t strength; + + if (!rumble.set_rumble_state || + !rumble_level) + return; + + strength = (rumble_strength_up > 0) ? + (rumble_strength_up * rumble_level) / + (rumble_strength_up + rumble_strength_down) : 0; + + rumble_strength_up = 0; + rumble_strength_down = 0; + + if (strength == rumble_strength_last) + return; + + rumble.set_rumble_state(0, RETRO_RUMBLE_WEAK, strength); + rumble.set_rumble_state(0, RETRO_RUMBLE_STRONG, strength); + + rumble_strength_last = strength; +} + +static void deactivate_rumble(void) +{ + rumble_strength_up = 0; + rumble_strength_down = 0; + rumble_active = false; + + if (!rumble.set_rumble_state || + (rumble_strength_last == 0)) + return; + + rumble.set_rumble_state(0, RETRO_RUMBLE_WEAK, 0); + rumble.set_rumble_state(0, RETRO_RUMBLE_STRONG, 0); + + rumble_strength_last = 0; +} + +/**********************/ +/* Rumble support END */ +/**********************/ + bool file_present_in_system(std::string fname) { const char *systemdirtmp = NULL; @@ -769,6 +838,10 @@ void retro_deinit(void) video_buf = NULL; deinit_frame_blending(); libretro_supports_bitmasks = false; + + deactivate_rumble(); + memset(&rumble, 0, sizeof(struct retro_rumble_interface)); + rumble_level = 0; } void retro_set_environment(retro_environment_t cb) @@ -1045,10 +1118,10 @@ static void check_variables(void) darkFilterLevel = static_cast(atoi(var.value)); } gb.setDarkFilterLevel(darkFilterLevel); - - var.key = "gambatte_up_down_allowed"; - var.value = NULL; + up_down_allowed = false; + var.key = "gambatte_up_down_allowed"; + var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (!strcmp(var.value, "enabled")) @@ -1056,8 +1129,18 @@ static void check_variables(void) else up_down_allowed = false; } - else - up_down_allowed = false; + + rumble_level = 0; + var.key = "gambatte_rumble_level"; + var.value = NULL; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + rumble_level = atoi(var.value); + rumble_level = (rumble_level > 10) ? 10 : rumble_level; + rumble_level = (rumble_level > 0) ? ((0x1999 * rumble_level) + 0x5) : 0; + } + if (rumble_level == 0) + deactivate_rumble(); /* Interframe blending option has its own handler */ check_frame_blend_variable(); @@ -1352,6 +1435,11 @@ bool retro_load_game(const struct retro_game_info *info) return false; } + if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble)) + log_cb(RETRO_LOG_INFO, "Rumble environment supported.\n"); + else + log_cb(RETRO_LOG_INFO, "Rumble environment not supported.\n"); + struct retro_input_descriptor desc[] = { { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, @@ -1600,6 +1688,10 @@ void retro_run() audio_batch_cb(sound_buf.i16, read_avail); #endif + /* Apply any 'pending' rumble effects */ + if (rumble_active) + apply_rumble(); + frames_count++; bool updated = false; diff --git a/libgambatte/libretro/libretro_core_options.h b/libgambatte/libretro/libretro_core_options.h index cdb0d02d..6942e0eb 100644 --- a/libgambatte/libretro/libretro_core_options.h +++ b/libgambatte/libretro/libretro_core_options.h @@ -302,6 +302,26 @@ struct retro_core_option_definition option_defs_us[] = { }, "disabled" }, + { + "gambatte_rumble_level", + "Gamepad Rumble Strength", + "Enables haptic feedback effects for supported games (Pokemon Pinball, Perfect Dark...).", + { + { "0", NULL }, + { "1", NULL }, + { "2", NULL }, + { "3", NULL }, + { "4", NULL }, + { "5", NULL }, + { "6", NULL }, + { "7", NULL }, + { "8", NULL }, + { "9", NULL }, + { "10", NULL }, + { NULL, NULL }, + }, + "10" + }, #ifdef HAVE_NETWORK { "gambatte_show_gb_link_settings", diff --git a/libgambatte/src/mem/cartridge.cpp b/libgambatte/src/mem/cartridge.cpp index fe13d7e0..0be5b28b 100644 --- a/libgambatte/src/mem/cartridge.cpp +++ b/libgambatte/src/mem/cartridge.cpp @@ -24,6 +24,8 @@ #include #include +extern void cartridge_set_rumble(unsigned active); + namespace gambatte { @@ -417,6 +419,7 @@ namespace gambatte unsigned short rombank; unsigned char rambank; bool enableRam; + bool hasRumble; static unsigned adjustedRombank(const unsigned bank) { return bank; } void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, @@ -424,29 +427,41 @@ namespace gambatte } void setRombank() const { memptrs.setRombank(adjustedRombank(rombank) & (rombanks(memptrs) - 1));} public: - explicit Mbc5(MemPtrs &memptrs) + explicit Mbc5(MemPtrs &memptrs, bool rumble) : memptrs(memptrs), rombank(1), rambank(0), - enableRam(false) + enableRam(false), + hasRumble(rumble) { } virtual void romWrite(const unsigned P, const unsigned data) { - switch (P >> 13 & 3) { - case 0: + switch (P >> 12 & 0x7) { + case 0x0: + case 0x1: enableRam = (data & 0xF) == 0xA; setRambank(); break; - case 1: + case 0x2: + case 0x3: rombank = P < 0x3000 ? (rombank & 0x100) | data : (data << 8 & 0x100) | (rombank & 0xFF); setRombank(); break; - case 2: - rambank = data & 0xF; + case 0x4: + case 0x5: + if(hasRumble && ((P >> 12 & 0x7) == 4)) + { + cartridge_set_rumble((data >> 3) & 1); + rambank = (data & ~8) & 0x0f; + } + else + { + rambank = data & 0x0f; + } setRambank(); break; - case 3: + default: break; } } @@ -527,6 +542,7 @@ namespace gambatte unsigned rombanks = 2; bool cgb = false; enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1, HUC3 } type = PLAIN; + bool rumble = false; { unsigned i; @@ -558,9 +574,9 @@ namespace gambatte case 0x19: printf("MBC5 ROM loaded.\n"); type = MBC5; break; case 0x1A: printf("MBC5 ROM+RAM loaded.\n"); type = MBC5; break; case 0x1B: printf("MBC5 ROM+RAM+BATTERY loaded.\n"); type = MBC5; break; - case 0x1C: printf("MBC5+RUMBLE ROM not supported.\n"); type = MBC5; break; - case 0x1D: printf("MBC5+RUMBLE+RAM ROM not suported.\n"); type = MBC5; break; - case 0x1E: printf("MBC5+RUMBLE+RAM+BATTERY ROM not supported.\n"); type = MBC5; break; + case 0x1C: printf("MBC5+RUMBLE ROM loaded.\n"); type = MBC5; rumble = true; break; + case 0x1D: printf("MBC5+RUMBLE+RAM ROM loaded.\n"); type = MBC5; rumble = true; break; + case 0x1E: printf("MBC5+RUMBLE+RAM+BATTERY ROM loaded.\n"); type = MBC5; rumble = true; break; case 0x20: printf("MBC6 ROM not supported.\n"); return -1; case 0x22: printf("MBC7 ROM not supported.\n"); return -1; case 0xFC: printf("Pocket Camera ROM not supported.\n"); return -1; @@ -630,7 +646,7 @@ namespace gambatte break; case MBC2: mbc.reset(new Mbc2(memptrs_)); break; case MBC3: mbc.reset(new Mbc3(memptrs_, hasRtc(memptrs_.romdata()[0x147]) ? &rtc_ : 0)); break; - case MBC5: mbc.reset(new Mbc5(memptrs_)); break; + case MBC5: mbc.reset(new Mbc5(memptrs_, rumble)); break; case HUC1: mbc.reset(new HuC1(memptrs_)); break; case HUC3: huc3_.set(true);