From 985e5e82b593c0aa753eaf57c52f12e97e159965 Mon Sep 17 00:00:00 2001 From: NewGBAXL <81663474+NewGBAXL@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:36:51 -0400 Subject: [PATCH 1/2] Ready 2 Rumble --- include/config.h | 1 + mk64.ld | 12 ++ src/main.c | 19 +++ src/menus.c | 29 +++++ src/menus.h | 6 +- src/os/motor.c | 179 +++++++++++++++++++++++++++ src/rumble_init.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++ src/rumble_init.h | 55 +++++++++ src/save.c | 37 ++++++ 9 files changed, 640 insertions(+), 1 deletion(-) create mode 100644 src/os/motor.c create mode 100644 src/rumble_init.c create mode 100644 src/rumble_init.h diff --git a/include/config.h b/include/config.h index 85d1a89eb9..7137206dee 100644 --- a/include/config.h +++ b/include/config.h @@ -8,4 +8,5 @@ * */ #define ENABLE_CUSTOM_COURSE_ENGINE 0 +#define ENABLE_RUMBLE 0 #endif diff --git a/mk64.ld b/mk64.ld index 0fadd36cd6..c0324a379d 100644 --- a/mk64.ld +++ b/mk64.ld @@ -55,6 +55,9 @@ SECTIONS BUILD_DIR/src/cpu_vehicles_camera_path.jp.o(.text*); BUILD_DIR/src/camera.o(.text*); BUILD_DIR/src/render_player.o(.text*); + #if ENABLE_RUMBLE + BUILD_DIR/src/rumble_init.o(.text*); + #endif BUILD_DIR/src/kart_dma.o(.text*); BUILD_DIR/src/player_controller.o(.text*); BUILD_DIR/src/spawn_players.o(.text*); @@ -222,6 +225,9 @@ SECTIONS BUILD_DIR/src/data/path_spawn_metadata.o(.data*); BUILD_DIR/src/camera.o(.data*); BUILD_DIR/src/render_player.o(.data*); + #if ENABLE_RUMBLE + BUILD_DIR/src/rumble_init.o(.data*); + #endif BUILD_DIR/src/kart_dma.o(.data*); BUILD_DIR/src/data/kart_attributes.o(.data*); BUILD_DIR/src/player_controller.o(.data*); @@ -272,6 +278,9 @@ SECTIONS BUILD_DIR/src/cpu_vehicles_camera_path.jp.o(.rodata*); BUILD_DIR/src/camera.o(.rodata*); BUILD_DIR/src/render_player.o(.rodata*); + #if ENABLE_RUMBLE + BUILD_DIR/src/rumble_init.o(.rodata*); + #endif BUILD_DIR/src/player_controller.o(.rodata*); BUILD_DIR/src/spawn_players.o(.rodata*); BUILD_DIR/src/code_8003DC40.o(.rodata*); @@ -331,6 +340,9 @@ SECTIONS BUILD_DIR/src/cpu_vehicles_camera_path.jp.o(.bss*); BUILD_DIR/src/camera.o(.bss*); BUILD_DIR/src/render_player.o(.bss*); + #if ENABLE_RUMBLE + BUILD_DIR/src/rumble_init.o(.bss*); + #endif BUILD_DIR/src/spawn_players.o(.bss*); BUILD_DIR/src/code_80057C60.o(.bss*); BUILD_DIR/src/code_80057C60_var.o(.bss*); diff --git a/src/main.c b/src/main.c index 25d052f4d1..007b790c8f 100644 --- a/src/main.c +++ b/src/main.c @@ -348,9 +348,15 @@ void update_controller(s32 index) { void read_controllers(void) { OSMesg msg; +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osContStartReadData(&gSIEventMesgQueue); osRecvMesg(&gSIEventMesgQueue, &msg, OS_MESG_BLOCK); osContGetReadData(gControllerPads); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif update_controller(0); update_controller(1); update_controller(2); @@ -1171,11 +1177,20 @@ void update_gamestate(void) { void thread5_game_loop(UNUSED void* arg) { osCreateMesgQueue(&gGfxVblankQueue, gGfxMesgBuf, 1); osCreateMesgQueue(&gGameVblankQueue, &gGameMesgBuf, 1); + +#if ENABLE_RUMBLE + init_rumble_pak_scheduler_queue(); +#endif + init_controllers(); if (!wasSoftReset) { clear_nmi_buffer(); } +#if ENABLE_RUMBLE + create_thread_6(); +#endif + set_vblank_handler(2, &gGameVblankHandler, &gGameVblankQueue, (OSMesg) OS_EVENT_SW2); // These variables track stats such as player wins. // In the event of a console reset, it remembers them. @@ -1192,6 +1207,10 @@ void thread5_game_loop(UNUSED void* arg) { func_800C5CB8(); while (true) { +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif + func_800CB2C4(); // Update the gamestate if it has changed (racing, menus, credits, etc.). diff --git a/src/menus.c b/src/menus.c index 51f61d330f..5db58bdff6 100644 --- a/src/menus.c +++ b/src/menus.c @@ -259,6 +259,9 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) { switch (gSubMenuSelection) { case SUB_MENU_OPTION_RETURN_GAME_SELECT: case SUB_MENU_OPTION_SOUND_MODE: + #if ENABLE_RUMBLE + case SUB_MENU_OPTION_RUMBLE: + #endif case SUB_MENU_OPTION_COPY_CONTROLLER_PAK: case SUB_MENU_OPTION_ERASE_ALL_DATA: { tempVar = false; @@ -321,6 +324,23 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) { return; } break; + #if ENABLE_RUMBLE + case SUB_MENU_OPTION_RUMBLE: + if (!gRumble) { + //if rumble pak connected + gRumble = 1; + play_sound2(SOUND_MENU_SELECT); + set_rumble(); + + //else + //play_sound2(SOUND_MENU_FILE_NOT_FOUND); + } + else { + gRumble = 0; + play_sound2(SOUND_MENU_SELECT); + set_rumble(); + } + #endif case SUB_MENU_OPTION_COPY_CONTROLLER_PAK: switch (controller_pak_2_status()) { case PFS_INVALID_DATA: @@ -1990,6 +2010,15 @@ void set_sound_mode(void) { } } +/** + * Self explanatory, sets rumble state + */ +#if ENABLE_RUMBLE +void set_rumble(void) { + return; +} +#endif + /** * Checks is a fade render mode is active so menus can't be * interacted while a fade transition is active diff --git a/src/menus.h b/src/menus.h index 8b16558a65..3583a21c61 100644 --- a/src/menus.h +++ b/src/menus.h @@ -40,9 +40,12 @@ enum SubMenuSelectionType { SUB_MENU_OPTION_MIN = 0x15, SUB_MENU_OPTION_RETURN_GAME_SELECT = SUB_MENU_OPTION_MIN, SUB_MENU_OPTION_SOUND_MODE, +#if ENABLE_RUMBLE + SUB_MENU_OPTION_RUMBLE, +#endif SUB_MENU_OPTION_COPY_CONTROLLER_PAK, SUB_MENU_OPTION_ERASE_ALL_DATA, - SUB_MENU_OPTION_MAX = SUB_MENU_OPTION_ERASE_ALL_DATA, // 0x18 + SUB_MENU_OPTION_MAX = SUB_MENU_OPTION_ERASE_ALL_DATA, // 0x18/0x19 SUB_MENU_ERASE_MIN = 0x1E, SUB_MENU_ERASE_QUIT = SUB_MENU_ERASE_MIN, SUB_MENU_ERASE_ERASE, @@ -231,6 +234,7 @@ extern s8 gDebugMenuSelection; extern s8 gControllerPakMenuSelection; extern s8 gScreenModeListIndex; extern u8 gSoundMode; +extern u8 gRumble; extern s8 gPlayerCount; extern s8 gVersusResultCursorSelection; extern s8 gTimeTrialsResultCursorSelection; diff --git a/src/os/motor.c b/src/os/motor.c new file mode 100644 index 0000000000..b611db4fb8 --- /dev/null +++ b/src/os/motor.c @@ -0,0 +1,179 @@ +#include "PR/os_message.h" +#include "PR/os_pi.h" +#include "libultra_internal.h" +#include "controller.h" +#include "macros.h" + +void _MakeMotorData(int channel, u16 address, u8 *buffer, OSPifRam *mdata); +u32 __osMotorinitialized[MAXCONTROLLERS] = { 0, 0, 0, 0 }; +OSPifRam _MotorStopData[MAXCONTROLLERS]; +OSPifRam _MotorStartData[MAXCONTROLLERS]; +u8 _motorstopbuf[32]; +u8 _motorstartbuf[32]; + +s32 osMotorStop(OSPfs *pfs) { + int i; + s32 ret; + u8 *ptr; + __OSContRamReadFormat ramreadformat; + ptr = (u8 *) &__osPfsPifRam; + + if (!__osMotorinitialized[pfs->channel]) { + return PFS_ERR_INVALID; + } + __osSiGetAccess(); + + __osContLastCmd = CONT_CMD_WRITE_MEMPACK; + __osSiRawStartDma(OS_WRITE, &_MotorStopData[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + ret = __osSiRawStartDma(OS_READ, &__osPfsPifRam); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + ptr = (u8 *) &__osPfsPifRam; + + if (pfs->channel != 0) { + for (i = 0; i < pfs->channel; i++) { + ptr++; + } + } + + ramreadformat = *(__OSContRamReadFormat *) ptr; + ret = CHNL_ERR(ramreadformat); + if (ret == 0 && ramreadformat.datacrc != __osContDataCrc((u8 *) &_motorstopbuf)) { + ret = PFS_ERR_CONTRFAIL; + } + __osSiRelAccess(); + return ret; +} + +s32 osMotorStart(OSPfs *pfs) { + int i; + s32 ret; + u8 *ptr; + __OSContRamReadFormat ramreadformat; + + ptr = (u8 *) &__osPfsPifRam; + + if (!__osMotorinitialized[pfs->channel]) { + return PFS_ERR_INVALID; + } + + __osSiGetAccess(); + + __osContLastCmd = CONT_CMD_WRITE_MEMPACK; + __osSiRawStartDma(OS_WRITE, &_MotorStartData[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + ret = __osSiRawStartDma(OS_READ, &__osPfsPifRam); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + ptr = (u8 *) &__osPfsPifRam; + + if (pfs->channel != 0) { + for (i = 0; i < pfs->channel; i++) { + ptr++; + } + } + + ramreadformat = *(__OSContRamReadFormat *) ptr; + ret = CHNL_ERR(ramreadformat); + if (ret == 0 && ramreadformat.datacrc != __osContDataCrc((u8 *) &_motorstartbuf)) { + ret = PFS_ERR_CONTRFAIL; + } + __osSiRelAccess(); + return ret; +} + +void _MakeMotorData(int channel, u16 address, u8 *buffer, OSPifRam *mdata) { + u8 *ptr; + __OSContRamReadFormat ramreadformat; + int i; + + ptr = (u8 *) mdata->ramarray; + for (i = 0; i < ARRAY_COUNT(mdata->ramarray); i++) { + mdata->ramarray[i] = 0; + } + mdata->pifstatus = CONT_CMD_EXE; + ramreadformat.dummy = CONT_CMD_NOP; + ramreadformat.txsize = CONT_CMD_WRITE_MEMPACK_TX; + ramreadformat.rxsize = CONT_CMD_WRITE_MEMPACK_RX; + ramreadformat.cmd = CONT_CMD_WRITE_MEMPACK; + + ramreadformat.address = (address << 0x5) | __osContAddressCrc(address); + ramreadformat.datacrc = CONT_CMD_NOP; + for (i = 0; i < ARRAY_COUNT(ramreadformat.data); i++) { + ramreadformat.data[i] = *buffer++; + } + if (channel != 0) { + for (i = 0; i < channel; i++) { + *ptr++ = 0; + } + } + *(__OSContRamReadFormat *) ptr = ramreadformat; + ptr += sizeof(__OSContRamReadFormat); + ptr[0] = CONT_CMD_END; +} + +s32 osMotorInit(OSMesgQueue *mq, OSPfs *pfs, int channel) { + int i; + s32 ret; + u8 temp[32]; + pfs->queue = mq; + pfs->channel = channel; + pfs->status = 0; + pfs->activebank = 128; + + for (i = 0; i < ARRAY_COUNT(temp); i++) { + temp[i] = 254; + } + + ret = __osContRamWrite(mq, channel, 1024, temp, false); + if (ret == 2) { // TODO: remove magic constant + ret = __osContRamWrite(mq, channel, 1024, temp, false); + } + if (ret != 0) { + return ret; + } + + ret = __osContRamRead(mq, channel, 1024, temp); + if (ret == 2) { + ret = PFS_ERR_CONTRFAIL; // is this right? + } + if (ret != 0) { + return ret; + } + if (temp[31] == 254) { + return PFS_ERR_DEVICE; + } + + for (i = 0; i < ARRAY_COUNT(temp); i++) { + temp[i] = 128; + } + + ret = __osContRamWrite(mq, channel, 1024, temp, false); + if (ret == 2) { + ret = __osContRamWrite(mq, channel, 1024, temp, false); + } + if (ret != 0) { + return ret; + } + + ret = __osContRamRead(mq, channel, 1024, temp); + if (ret == 2) { + ret = PFS_ERR_CONTRFAIL; + } + if (ret != 0) { + return ret; + } + if (temp[31] != 128) { + return PFS_ERR_DEVICE; + } + + if (!__osMotorinitialized[channel]) { + for (i = 0; i < ARRAY_COUNT(_motorstartbuf); i++) { + _motorstartbuf[i] = 1; + _motorstopbuf[i] = 0; + } + _MakeMotorData(channel, 1536, _motorstartbuf, &_MotorStartData[channel]); + _MakeMotorData(channel, 1536, _motorstopbuf, &_MotorStopData[channel]); + __osMotorinitialized[channel] = 1; + } + return 0; +} diff --git a/src/rumble_init.c b/src/rumble_init.c new file mode 100644 index 0000000000..ba14ac32bf --- /dev/null +++ b/src/rumble_init.c @@ -0,0 +1,303 @@ +#include "config.h" + +#if ENABLE_RUMBLE + +#include +#include +#include "macros.h" + +#include "buffers/buffers.h" +#include "main.h" +#include "rumble_init.h" + +FORCE_BSS OSThread gRumblePakThread; + +FORCE_BSS OSPfs gRumblePakPfs; + +FORCE_BSS OSMesg gRumblePakSchedulerMesgBuf; +FORCE_BSS OSMesgQueue gRumblePakSchedulerMesgQueue; +FORCE_BSS OSMesg gRumbleThreadVIMesgBuf; +FORCE_BSS OSMesgQueue gRumbleThreadVIMesgQueue; + +FORCE_BSS struct RumbleData gRumbleDataQueue[3]; +FORCE_BSS struct RumbleSettings gCurrRumbleSettings; + +s32 sRumblePakThreadActive = FALSE; +s32 sRumblePakActive = FALSE; +s32 sRumblePakErrorCount = 0; +s32 gRumblePakTimer = 0; + +void init_rumble_pak_scheduler_queue(void) { + osCreateMesgQueue(&gRumblePakSchedulerMesgQueue, &gRumblePakSchedulerMesgBuf, 1); + osSendMesg(&gRumblePakSchedulerMesgQueue, (OSMesg) 0, OS_MESG_NOBLOCK); +} + +void block_until_rumble_pak_free(void) { + OSMesg msg; + osRecvMesg(&gRumblePakSchedulerMesgQueue, &msg, OS_MESG_BLOCK); +} + +void release_rumble_pak_control(void) { + osSendMesg(&gRumblePakSchedulerMesgQueue, (OSMesg) 0, OS_MESG_NOBLOCK); +} + +static void start_rumble(void) { + if (!sRumblePakActive) { + return; + } + + block_until_rumble_pak_free(); + + if (!osMotorStart(&gRumblePakPfs)) { + sRumblePakErrorCount = 0; + } else { + sRumblePakErrorCount++; + } + + release_rumble_pak_control(); +} + +static void stop_rumble(void) { + if (!sRumblePakActive) { + return; + } + + block_until_rumble_pak_free(); + + if (!osMotorStop(&gRumblePakPfs)) { + sRumblePakErrorCount = 0; + } else { + sRumblePakErrorCount++; + } + + release_rumble_pak_control(); +} + +static void update_rumble_pak(void) { + if (gResetTimer > 0) { + stop_rumble(); + return; + } + + if (gCurrRumbleSettings.unk08 > 0) { + gCurrRumbleSettings.unk08--; + start_rumble(); + } else if (gCurrRumbleSettings.unk04 > 0) { + gCurrRumbleSettings.unk04--; + + gCurrRumbleSettings.unk02 -= gCurrRumbleSettings.unk0E; + if (gCurrRumbleSettings.unk02 < 0) { + gCurrRumbleSettings.unk02 = 0; + } + + if (gCurrRumbleSettings.unk00 == 1) { + start_rumble(); + } else if (gCurrRumbleSettings.unk06 >= 0x100) { + gCurrRumbleSettings.unk06 -= 0x100; + start_rumble(); + } else { + gCurrRumbleSettings.unk06 += + ((gCurrRumbleSettings.unk02 * gCurrRumbleSettings.unk02 * gCurrRumbleSettings.unk02) / (1 << 9)) + 4; + + stop_rumble(); + } + } else { + gCurrRumbleSettings.unk04 = 0; + + if (gCurrRumbleSettings.unk0A >= 5) { + start_rumble(); + } else if ((gCurrRumbleSettings.unk0A >= 2) && (gNumVblanks % gCurrRumbleSettings.unk0C == 0)) { + start_rumble(); + } else { + stop_rumble(); + } + } + + if (gCurrRumbleSettings.unk0A > 0) { + gCurrRumbleSettings.unk0A--; + } +} + +static void update_rumble_data_queue(void) { + if (gRumbleDataQueue[0].comm) { + gCurrRumbleSettings.unk06 = 0; + gCurrRumbleSettings.unk08 = 4; + gCurrRumbleSettings.unk00 = gRumbleDataQueue[0].comm; + gCurrRumbleSettings.unk04 = gRumbleDataQueue[0].time; + gCurrRumbleSettings.unk02 = gRumbleDataQueue[0].level; + gCurrRumbleSettings.unk0E = gRumbleDataQueue[0].decay; + } + + gRumbleDataQueue[0] = gRumbleDataQueue[1]; + gRumbleDataQueue[1] = gRumbleDataQueue[2]; + + gRumbleDataQueue[2].comm = 0; +} + +void queue_rumble_data(s16 time, s16 level) { + if (gCurrDemoInput != NULL) { + return; + } + + if (level > 70) { + gRumbleDataQueue[2].comm = 1; + } else { + gRumbleDataQueue[2].comm = 2; + } + + gRumbleDataQueue[2].level = level; + gRumbleDataQueue[2].time = time; + gRumbleDataQueue[2].decay = 0; +} + +void queue_rumble_decay(s16 level) { + gRumbleDataQueue[2].decay = level; +} + +u8 is_rumble_finished_and_queue_empty(void) { + if (gCurrRumbleSettings.unk08 + gCurrRumbleSettings.unk04 >= 4) { + return FALSE; + } + + if (gRumbleDataQueue[0].comm != 0) { + return FALSE; + } + + if (gRumbleDataQueue[1].comm != 0) { + return FALSE; + } + + if (gRumbleDataQueue[2].comm != 0) { + return FALSE; + } + + return TRUE; +} + +void reset_rumble_timers_slip(void) { + if (gCurrDemoInput != NULL) { + return; + } + + if (gCurrRumbleSettings.unk0A == 0) { + gCurrRumbleSettings.unk0A = 7; + } + + if (gCurrRumbleSettings.unk0A < 4) { + gCurrRumbleSettings.unk0A = 4; + } + + gCurrRumbleSettings.unk0C = 7; +} + +void reset_rumble_timers_vibrate(s32 a0) { + if (gCurrDemoInput != NULL) { + return; + } + + if (gCurrRumbleSettings.unk0A == 0) { + gCurrRumbleSettings.unk0A = 7; + } + + if (gCurrRumbleSettings.unk0A < 4) { + gCurrRumbleSettings.unk0A = 4; + } + + if (a0 == 4) { + gCurrRumbleSettings.unk0C = 1; + } + + if (a0 == 3) { + gCurrRumbleSettings.unk0C = 2; + } + + if (a0 == 2) { + gCurrRumbleSettings.unk0C = 3; + } + + if (a0 == 1) { + gCurrRumbleSettings.unk0C = 4; + } + + if (a0 == 0) { + gCurrRumbleSettings.unk0C = 5; + } +} + +/* this is used when Mario is underwater */ +void queue_rumble_submerged(void) { + if (gCurrDemoInput != NULL) { + return; + } + + gCurrRumbleSettings.unk0A = 4; + gCurrRumbleSettings.unk0C = 4; +} + +static void thread6_rumble_loop(UNUSED void *a0) { + OSMesg msg; + + CN_DEBUG_PRINTF(("start motor thread\n")); + + cancel_rumble(); + sRumblePakThreadActive = TRUE; + + CN_DEBUG_PRINTF(("go motor thread\n")); + + while (TRUE) { + // Block until VI + osRecvMesg(&gRumbleThreadVIMesgQueue, &msg, OS_MESG_BLOCK); + + update_rumble_data_queue(); + update_rumble_pak(); + + if (sRumblePakActive) { + if (sRumblePakErrorCount >= 30) { + sRumblePakActive = FALSE; + } + } else if (gNumVblanks % 60 == 0) { + sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) == 0; + sRumblePakErrorCount = 0; + } + + if (gRumblePakTimer > 0) { + gRumblePakTimer--; + } + } +} + +void cancel_rumble(void) { + sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) == 0; + + if (sRumblePakActive) { + osMotorStop(&gRumblePakPfs); + } + + gRumbleDataQueue[0].comm = 0; + gRumbleDataQueue[1].comm = 0; + gRumbleDataQueue[2].comm = 0; + + gCurrRumbleSettings.unk04 = 0; + gCurrRumbleSettings.unk0A = 0; + + gRumblePakTimer = 0; +} + +void create_thread_6(void) { + osCreateMesgQueue(&gRumbleThreadVIMesgQueue, &gRumbleThreadVIMesgBuf, 1); + osCreateThread(&gRumblePakThread, 6, thread6_rumble_loop, NULL, gThread6Stack + 0x2000, 30); + osStartThread(&gRumblePakThread); +} + +void rumble_thread_update_vi(void) { + if (!sRumblePakThreadActive) { + return; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmultichar" + osSendMesg(&gRumbleThreadVIMesgQueue, (OSMesg) 'VRTC', OS_MESG_NOBLOCK); +#pragma GCC diagnostic pop +} + +#endif diff --git a/src/rumble_init.h b/src/rumble_init.h new file mode 100644 index 0000000000..936015c772 --- /dev/null +++ b/src/rumble_init.h @@ -0,0 +1,55 @@ +#ifndef RUMBLE_INIT_H +#define RUMBLE_INIT_H + +#include "config.h" + +#if ENABLE_RUMBLE + +struct RumbleData { + u8 comm; + u8 level; + s16 time; + s16 decay; +}; + +struct RumbleSettings { + s16 unk00; + s16 unk02; + s16 unk04; + s16 unk06; + s16 unk08; + s16 unk0A; + s16 unk0C; + s16 unk0E; +}; + +extern OSThread gRumblePakThread; + +extern OSPfs gRumblePakPfs; + +extern OSMesg gRumblePakSchedulerMesgBuf; +extern OSMesgQueue gRumblePakSchedulerMesgQueue; +extern OSMesg gRumbleThreadVIMesgBuf; +extern OSMesgQueue gRumbleThreadVIMesgQueue; + +extern struct RumbleData gRumbleDataQueue[3]; +extern struct RumbleSettings gCurrRumbleSettings; + +extern s32 gRumblePakTimer; + +void init_rumble_pak_scheduler_queue(void); +void block_until_rumble_pak_free(void); +void release_rumble_pak_control(void); +void queue_rumble_data(s16 a0, s16 a1); +void func_sh_8024C89C(s16 a0); +u8 is_rumble_finished_and_queue_empty(void); +void reset_rumble_timers(void); +void reset_rumble_timers_2(s32 a0); +void func_sh_8024CA04(void); +void cancel_rumble(void); +void create_thread_6(void); +void rumble_thread_update_vi(void); + +#endif // ENABLE_RUMBLE + +#endif // RUMBLE_INIT_H diff --git a/src/save.c b/src/save.c index e4c3af6f0a..bc626d529f 100644 --- a/src/save.c +++ b/src/save.c @@ -44,15 +44,28 @@ void func_800B45E0(s32 arg0) { &gSaveData.allCourseTimeTrialRecords.cupRecords[arg0 / 4].courseRecords[arg0 % 4]; courseTimeTrialRecordsPtr->checksum = checksum_time_trial_records(arg0); +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(courseTimeTrialRecordsPtr), (u8*) courseTimeTrialRecordsPtr, sizeof(CourseTimeTrialRecords)); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif } void write_save_data_grand_prix_points_and_sound_mode(void) { Stuff* main = &gSaveData.main; main->checksum[1] = compute_save_data_checksum_1(); main->checksum[2] = compute_save_data_checksum_2(); +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(main), (u8*) main, sizeof(Stuff)); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif + } void func_800B46D0(void) { @@ -137,7 +150,13 @@ u8 compute_save_data_checksum_2(void) { void load_save_data(void) { s32 i; +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongRead(&gSIEventMesgQueue, EEPROM_ADDR(&gSaveData), (u8*) &gSaveData, sizeof(SaveData)); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif // 16: 4 cup records * 4 course records? for (i = 0; i < 16; i++) { func_800B4A9C(i); @@ -214,7 +233,13 @@ void validate_save_data(void) { main->saveInfo.soundMode = backup->saveInfo.soundMode; main->checksum[1] = compute_save_data_checksum_backup_1(); main->checksum[2] = compute_save_data_checksum_backup_2(); +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(main), (u8*) main, sizeof(Stuff)); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif } update_save_data_backup(); return; @@ -457,8 +482,14 @@ void func_800B559C(s32 arg0) { bestRecord = &gSaveData.onlyBestTimeTrialRecords[x]; bestRecord->unknownBytes[6] = func_800B578C(x); bestRecord->unknownBytes[7] = func_800B5888(x); +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongWrite(&gSIEventMesgQueue, ((u32) (((u8*) bestRecord) - ((u8*) (&gSaveData)))) >> 3, bestRecord->bestThreelaps[0], 0x38); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif } /** @@ -514,7 +545,13 @@ void update_save_data_backup(void) { backup->saveInfo.soundMode = main->saveInfo.soundMode; backup->checksum[1] = compute_save_data_checksum_backup_1(); backup->checksum[2] = compute_save_data_checksum_backup_2(); +#if ENABLE_RUMBLE + block_until_rumble_pak_free(); +#endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(backup), (u8*) backup, sizeof(Stuff)); +#if ENABLE_RUMBLE + release_rumble_pak_control(); +#endif } u8 compute_save_data_checksum_backup_1(void) { From 24f6233f83b20fc0884b2b7f42044dad85d495ed Mon Sep 17 00:00:00 2001 From: NewGBAXL <81663474+NewGBAXL@users.noreply.github.com> Date: Sat, 21 Jun 2025 16:20:47 -0400 Subject: [PATCH 2/2] Update Rumble & add it to boosting Supports multiple controllers, wip rumble in menus --- mk64.ld | 11 ++ src/main.c | 6 +- src/main.h | 3 + src/menus.c | 39 ++--- src/menus.h | 6 +- src/player_controller.c | 16 +++ src/rumble_init.c | 306 +++++++++++++++++++++------------------- src/rumble_init.h | 24 ++-- src/save.c | 25 ++-- 9 files changed, 230 insertions(+), 206 deletions(-) diff --git a/mk64.ld b/mk64.ld index c0324a379d..cdf017809b 100644 --- a/mk64.ld +++ b/mk64.ld @@ -3,6 +3,8 @@ OUTPUT_ARCH (mips) +#include "config.h" + #define LINKER_MAIN 0x80000400 #define LINKER_ENDING 0x80280000 @@ -132,6 +134,9 @@ SECTIONS BUILD_DIR/src/os/guOrthoF.o(.text*); BUILD_DIR/src/os/osSetTime.o(.text*); BUILD_DIR/src/os/osEepromProbe.o(.text*); + #if ENABLE_RUMBLE + BUILD_DIR/src/os/motor.o(.text); + #endif BUILD_DIR/src/os/osPfsIsPlug.o(.text*); BUILD_DIR/src/os/osPfsInit.o(.text*); BUILD_DIR/src/os/osPfsNumFiles.o(.text*); @@ -262,6 +267,9 @@ SECTIONS BUILD_DIR/src/os/osViTable.o(.data*); BUILD_DIR/src/os/osCreatePiManager.o(.data*); BUILD_DIR/src/os/osContInit.o(.data*); + #if ENABLE_RUMBLE + BUILD_DIR/src/os/motor.o(.data*); + #endif BUILD_DIR/src/os/osAiSetNextBuffer.o(.data*); BUILD_DIR/src/os/__osSetHWIntrRoutine.o(.data*); BUILD_DIR/src/os/__osDequeueThread.o(.data*); @@ -367,6 +375,9 @@ SECTIONS BUILD_DIR/src/os/osSpTaskLoadGo.o(.bss*); BUILD_DIR/src/os/osContStartReadData.o(.bss*); BUILD_DIR/src/os/osContInit.o(.bss*); + #if ENABLE_RUMBLE + BUILD_DIR/src/os/motor.o(.bss*); + #endif BUILD_DIR/src/os/osPfsIsPlug.o(.bss*); BUILD_DIR/src/os/guRotateF.o(.bss*); BUILD_DIR/src/os/__osLeoInterrupt.o(.bss*); diff --git a/src/main.c b/src/main.c index 007b790c8f..e6a93feca6 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,7 @@ #include #include "crash_screen.h" #include "buffers/gfx_output_buffer.h" +#include "rumble_init.h" void func_80091B78(void); void audio_init(void); @@ -155,6 +156,9 @@ OSThread gAudioThread; ALIGNED8 u8 gAudioThreadStack[STACKSIZE]; UNUSED OSThread D_8015CD30; UNUSED ALIGNED8 u8 D_8015CD30_Stack[STACKSIZE / 2]; +#if ENABLE_RUMBLE +ALIGNED8 u8 gRumbleThreadStack[STACKSIZE]; +#endif ALIGNED8 u8 gGfxSPTaskYieldBuffer[4352]; ALIGNED8 u32 gGfxSPTaskStack[256]; @@ -1208,7 +1212,7 @@ void thread5_game_loop(UNUSED void* arg) { while (true) { #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif func_800CB2C4(); diff --git a/src/main.h b/src/main.h index 6d7ac7aac2..1bc5bbe7b1 100644 --- a/src/main.h +++ b/src/main.h @@ -191,6 +191,9 @@ extern OSThread gGameLoopThread; extern u8 gGameLoopThreadStack[]; extern OSThread gAudioThread; extern u8 gAudioThreadStack[]; +#if ENABLE_RUMBLE +extern u8 gRumbleThreadStack[]; +#endif extern u8 gGfxSPTaskYieldBuffer[]; extern u32 gGfxSPTaskStack[]; diff --git a/src/menus.c b/src/menus.c index 5db58bdff6..2a716a35e2 100644 --- a/src/menus.c +++ b/src/menus.c @@ -19,6 +19,7 @@ #include "save_data.h" #include #include "spawn_players.h" +#include "rumble_init.h" #if ENABLE_DEBUG_MODE #define DEBUG_MODE_TOGGLE true @@ -259,9 +260,6 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) { switch (gSubMenuSelection) { case SUB_MENU_OPTION_RETURN_GAME_SELECT: case SUB_MENU_OPTION_SOUND_MODE: - #if ENABLE_RUMBLE - case SUB_MENU_OPTION_RUMBLE: - #endif case SUB_MENU_OPTION_COPY_CONTROLLER_PAK: case SUB_MENU_OPTION_ERASE_ALL_DATA: { tempVar = false; @@ -324,23 +322,6 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) { return; } break; - #if ENABLE_RUMBLE - case SUB_MENU_OPTION_RUMBLE: - if (!gRumble) { - //if rumble pak connected - gRumble = 1; - play_sound2(SOUND_MENU_SELECT); - set_rumble(); - - //else - //play_sound2(SOUND_MENU_FILE_NOT_FOUND); - } - else { - gRumble = 0; - play_sound2(SOUND_MENU_SELECT); - set_rumble(); - } - #endif case SUB_MENU_OPTION_COPY_CONTROLLER_PAK: switch (controller_pak_2_status()) { case PFS_INVALID_DATA: @@ -1467,6 +1448,9 @@ void main_menu_act(struct Controller* controller, u16 controllerIdx) { gMenuTimingCounter++; if ((gMenuTimingCounter == 60) || !(gMenuTimingCounter % 300)) { play_sound2(SOUND_MENU_OK); +#if ENABLE_RUMBLE + queue_rumble_data(0, 5, 80); +#endif } } if (btnAndStick & B_BUTTON) { @@ -1672,6 +1656,9 @@ void player_select_menu_act(struct Controller* controller, u16 controllerIdx) { gMenuTimingCounter++; if (gMenuTimingCounter == 0x3C || !(gMenuTimingCounter % 300)) { play_sound2(SOUND_MENU_OK); +#if ENABLE_RUMBLE + queue_rumble_data(0, 5, 80); +#endif } } if (btnAndStick & B_BUTTON) { @@ -1777,6 +1764,9 @@ void course_select_menu_act(struct Controller* arg0, u16 controllerIdx) { if ((controllerIdx == PLAYER_ONE) && ((++gMenuTimingCounter == 0x3C) || ((gMenuTimingCounter % 300) == 0))) { play_sound2(SOUND_MENU_OK); + #if ENABLE_RUMBLE + queue_rumble_data(0, 5, 80); + #endif } if ((btnAndStick & B_BUTTON) != 0) { @@ -2010,15 +2000,6 @@ void set_sound_mode(void) { } } -/** - * Self explanatory, sets rumble state - */ -#if ENABLE_RUMBLE -void set_rumble(void) { - return; -} -#endif - /** * Checks is a fade render mode is active so menus can't be * interacted while a fade transition is active diff --git a/src/menus.h b/src/menus.h index 3583a21c61..8b16558a65 100644 --- a/src/menus.h +++ b/src/menus.h @@ -40,12 +40,9 @@ enum SubMenuSelectionType { SUB_MENU_OPTION_MIN = 0x15, SUB_MENU_OPTION_RETURN_GAME_SELECT = SUB_MENU_OPTION_MIN, SUB_MENU_OPTION_SOUND_MODE, -#if ENABLE_RUMBLE - SUB_MENU_OPTION_RUMBLE, -#endif SUB_MENU_OPTION_COPY_CONTROLLER_PAK, SUB_MENU_OPTION_ERASE_ALL_DATA, - SUB_MENU_OPTION_MAX = SUB_MENU_OPTION_ERASE_ALL_DATA, // 0x18/0x19 + SUB_MENU_OPTION_MAX = SUB_MENU_OPTION_ERASE_ALL_DATA, // 0x18 SUB_MENU_ERASE_MIN = 0x1E, SUB_MENU_ERASE_QUIT = SUB_MENU_ERASE_MIN, SUB_MENU_ERASE_ERASE, @@ -234,7 +231,6 @@ extern s8 gDebugMenuSelection; extern s8 gControllerPakMenuSelection; extern s8 gScreenModeListIndex; extern u8 gSoundMode; -extern u8 gRumble; extern s8 gPlayerCount; extern s8 gVersusResultCursorSelection; extern s8 gTimeTrialsResultCursorSelection; diff --git a/src/player_controller.c b/src/player_controller.c index c5d5282c17..225dbd169b 100644 --- a/src/player_controller.c +++ b/src/player_controller.c @@ -21,6 +21,7 @@ #include "code_80057C60.h" #include "cpu_vehicles_camera_path.h" #include "sounds.h" +#include "rumble_init.h" extern s32 D_8018D168; @@ -1932,6 +1933,11 @@ void apply_effect(Player* player, s8 playerIndex, s8 arg2) { } if ((player->effects & BOOST_EFFECT) == BOOST_EFFECT) { apply_boost_effect(player); +#if ENABLE_RUMBLE + if (playerIndex < 4 && (player->type & PLAYER_HUMAN) == PLAYER_HUMAN) { + queue_rumble_data(playerIndex, 60, 70); + } +#endif } if ((player->effects & BOOST_RAMP_ASPHALT_EFFECT) == BOOST_RAMP_ASPHALT_EFFECT) { apply_boost_ramp_asphalt_effect(player); @@ -1941,9 +1947,19 @@ void apply_effect(Player* player, s8 playerIndex, s8 arg2) { } if ((s32) (player->effects & HIT_EFFECT) == HIT_EFFECT) { apply_hit_effect(player, playerIndex); +#if ENABLE_RUMBLE + if (playerIndex < 4 && (player->type & PLAYER_HUMAN) == PLAYER_HUMAN) { + queue_rumble_data(playerIndex, 5, 80); + } +#endif } if ((player->effects & LIGHTNING_EFFECT) == LIGHTNING_EFFECT) { apply_lightning_effect(player, playerIndex); +#if ENABLE_RUMBLE + if (playerIndex < 4 && (player->type & PLAYER_HUMAN) == PLAYER_HUMAN) { + queue_rumble_data(playerIndex, 70, 60); + } +#endif } if ((player->effects & UNKNOWN_EFFECT_0x10000) == UNKNOWN_EFFECT_0x10000) { func_8008F3F4(player, playerIndex); diff --git a/src/rumble_init.c b/src/rumble_init.c index ba14ac32bf..9702cbb9bd 100644 --- a/src/rumble_init.c +++ b/src/rumble_init.c @@ -6,26 +6,25 @@ #include #include "macros.h" -#include "buffers/buffers.h" #include "main.h" #include "rumble_init.h" -FORCE_BSS OSThread gRumblePakThread; +OSThread gRumblePakThread; -FORCE_BSS OSPfs gRumblePakPfs; +OSPfs gRumblePakPfs[4]; -FORCE_BSS OSMesg gRumblePakSchedulerMesgBuf; -FORCE_BSS OSMesgQueue gRumblePakSchedulerMesgQueue; -FORCE_BSS OSMesg gRumbleThreadVIMesgBuf; -FORCE_BSS OSMesgQueue gRumbleThreadVIMesgQueue; +OSMesg gRumblePakSchedulerMesgBuf; +OSMesgQueue gRumblePakSchedulerMesgQueue; +OSMesg gRumbleThreadVIMesgBuf; +OSMesgQueue gRumbleThreadVIMesgQueue; -FORCE_BSS struct RumbleData gRumbleDataQueue[3]; -FORCE_BSS struct RumbleSettings gCurrRumbleSettings; +struct RumbleData gRumbleDataQueue[4][3]; +struct RumbleSettings gCurrRumbleSettings[4]; -s32 sRumblePakThreadActive = FALSE; -s32 sRumblePakActive = FALSE; -s32 sRumblePakErrorCount = 0; -s32 gRumblePakTimer = 0; +s32 sRumblePakThreadActive = false; +s32 sRumblePakActive[4] = { false, false, false, false }; +s32 sRumblePakErrorCount[4] = { 0, 0, 0, 0 }; +s32 gRumblePakTimer[4] = { 0, 0, 0, 0 }; void init_rumble_pak_scheduler_queue(void) { osCreateMesgQueue(&gRumblePakSchedulerMesgQueue, &gRumblePakSchedulerMesgBuf, 1); @@ -41,251 +40,262 @@ void release_rumble_pak_control(void) { osSendMesg(&gRumblePakSchedulerMesgQueue, (OSMesg) 0, OS_MESG_NOBLOCK); } -static void start_rumble(void) { - if (!sRumblePakActive) { +static void start_rumble(s16 port) { + if (!sRumblePakActive[port]) { return; } block_until_rumble_pak_free(); - if (!osMotorStart(&gRumblePakPfs)) { - sRumblePakErrorCount = 0; + if (!osMotorStart(&gRumblePakPfs[port])) { + sRumblePakErrorCount[port] = 0; } else { - sRumblePakErrorCount++; + sRumblePakErrorCount[port]++; } release_rumble_pak_control(); } -static void stop_rumble(void) { - if (!sRumblePakActive) { +static void stop_rumble(s16 port) { + if (!sRumblePakActive[port]) { return; } block_until_rumble_pak_free(); - if (!osMotorStop(&gRumblePakPfs)) { - sRumblePakErrorCount = 0; + if (!osMotorStop(&gRumblePakPfs[port])) { + sRumblePakErrorCount[port] = 0; } else { - sRumblePakErrorCount++; + sRumblePakErrorCount[port]++; } release_rumble_pak_control(); } -static void update_rumble_pak(void) { - if (gResetTimer > 0) { - stop_rumble(); +static void update_rumble_pak(s16 port) { + //todo: update with correct var from main + /*if (gResetTimer > 0) { + stop_rumble(port); return; - } + }*/ - if (gCurrRumbleSettings.unk08 > 0) { - gCurrRumbleSettings.unk08--; - start_rumble(); - } else if (gCurrRumbleSettings.unk04 > 0) { - gCurrRumbleSettings.unk04--; + if (gCurrRumbleSettings[port].unk08 > 0) { + gCurrRumbleSettings[port].unk08--; + start_rumble(port); + } else if (gCurrRumbleSettings[port].unk04 > 0) { + gCurrRumbleSettings[port].unk04--; - gCurrRumbleSettings.unk02 -= gCurrRumbleSettings.unk0E; - if (gCurrRumbleSettings.unk02 < 0) { - gCurrRumbleSettings.unk02 = 0; + gCurrRumbleSettings[port].unk02 -= gCurrRumbleSettings[port].unk0E; + if (gCurrRumbleSettings[port].unk02 < 0) { + gCurrRumbleSettings[port].unk02 = 0; } - if (gCurrRumbleSettings.unk00 == 1) { - start_rumble(); - } else if (gCurrRumbleSettings.unk06 >= 0x100) { - gCurrRumbleSettings.unk06 -= 0x100; - start_rumble(); + if (gCurrRumbleSettings[port].unk00 == 1) { + start_rumble(port); + } else if (gCurrRumbleSettings[port].unk06 >= 0x100) { + gCurrRumbleSettings[port].unk06 -= 0x100; + start_rumble(port); } else { - gCurrRumbleSettings.unk06 += - ((gCurrRumbleSettings.unk02 * gCurrRumbleSettings.unk02 * gCurrRumbleSettings.unk02) / (1 << 9)) + 4; + gCurrRumbleSettings[port].unk06 += + ((gCurrRumbleSettings[port].unk02 * gCurrRumbleSettings[port].unk02 * gCurrRumbleSettings[port].unk02) / (1 << 9)) + 4; - stop_rumble(); + stop_rumble(port); } } else { - gCurrRumbleSettings.unk04 = 0; + gCurrRumbleSettings[port].unk04 = 0; - if (gCurrRumbleSettings.unk0A >= 5) { - start_rumble(); - } else if ((gCurrRumbleSettings.unk0A >= 2) && (gNumVblanks % gCurrRumbleSettings.unk0C == 0)) { - start_rumble(); + if (gCurrRumbleSettings[port].unk0A >= 5) { + start_rumble(port); + } else if ((gCurrRumbleSettings[port].unk0A >= 2) && (sNumVBlanks % gCurrRumbleSettings[port].unk0C == 0)) { + start_rumble(port); } else { - stop_rumble(); + stop_rumble(port); } } - if (gCurrRumbleSettings.unk0A > 0) { - gCurrRumbleSettings.unk0A--; + if (gCurrRumbleSettings[port].unk0A > 0) { + gCurrRumbleSettings[port].unk0A--; } } -static void update_rumble_data_queue(void) { - if (gRumbleDataQueue[0].comm) { - gCurrRumbleSettings.unk06 = 0; - gCurrRumbleSettings.unk08 = 4; - gCurrRumbleSettings.unk00 = gRumbleDataQueue[0].comm; - gCurrRumbleSettings.unk04 = gRumbleDataQueue[0].time; - gCurrRumbleSettings.unk02 = gRumbleDataQueue[0].level; - gCurrRumbleSettings.unk0E = gRumbleDataQueue[0].decay; +static void update_rumble_pak_all(void) +{ + s16 port; + for (port = 0; port < 3; port++) { + update_rumble_pak(port); } - - gRumbleDataQueue[0] = gRumbleDataQueue[1]; - gRumbleDataQueue[1] = gRumbleDataQueue[2]; - - gRumbleDataQueue[2].comm = 0; } -void queue_rumble_data(s16 time, s16 level) { - if (gCurrDemoInput != NULL) { - return; +static void update_rumble_data_queue(s16 port) { + if (gRumbleDataQueue[port][0].comm) { + gCurrRumbleSettings[port].unk06 = 0; + gCurrRumbleSettings[port].unk08 = 4; + gCurrRumbleSettings[port].unk00 = gRumbleDataQueue[port][0].comm; + gCurrRumbleSettings[port].unk04 = gRumbleDataQueue[port][0].time; + gCurrRumbleSettings[port].unk02 = gRumbleDataQueue[port][0].level; + gCurrRumbleSettings[port].unk0E = gRumbleDataQueue[port][0].decay; } + gRumbleDataQueue[port][0] = gRumbleDataQueue[port][1]; + gRumbleDataQueue[port][1] = gRumbleDataQueue[port][2]; + + gRumbleDataQueue[port][2].comm = 0; +} + +void queue_rumble_data(s16 port, s16 time, s16 level) { if (level > 70) { - gRumbleDataQueue[2].comm = 1; + gRumbleDataQueue[port][2].comm = 1; } else { - gRumbleDataQueue[2].comm = 2; + gRumbleDataQueue[port][2].comm = 2; + } + + gRumbleDataQueue[port][2].level = level; + gRumbleDataQueue[port][2].time = time; + gRumbleDataQueue[port][2].decay = 0; +} + +static void update_rumble_data_queue_all(void) { + s16 port; + for (port = 0; port < 3; port++) { + update_rumble_data_queue(port); } +} - gRumbleDataQueue[2].level = level; - gRumbleDataQueue[2].time = time; - gRumbleDataQueue[2].decay = 0; +void queue_rumble_decay(s16 port, s16 level) { + gRumbleDataQueue[port][2].decay = level; } -void queue_rumble_decay(s16 level) { - gRumbleDataQueue[2].decay = level; +u8 is_rumble_finished_and_queue_empty_all() { + s16 port; + for (port = 0; port < 3; port++) { + if (!is_rumble_finished_and_queue_empty(port)) { + return false; + } + } + return true; } -u8 is_rumble_finished_and_queue_empty(void) { - if (gCurrRumbleSettings.unk08 + gCurrRumbleSettings.unk04 >= 4) { - return FALSE; +u8 is_rumble_finished_and_queue_empty(s16 port) { + if (gCurrRumbleSettings[port].unk08 + gCurrRumbleSettings[port].unk04 >= 4) { + return false; } - if (gRumbleDataQueue[0].comm != 0) { - return FALSE; + if (gRumbleDataQueue[port][0].comm != 0) { + return false; } - if (gRumbleDataQueue[1].comm != 0) { - return FALSE; + if (gRumbleDataQueue[port][1].comm != 0) { + return false; } - if (gRumbleDataQueue[2].comm != 0) { - return FALSE; + if (gRumbleDataQueue[port][2].comm != 0) { + return false; } - return TRUE; + return true; } -void reset_rumble_timers_slip(void) { - if (gCurrDemoInput != NULL) { - return; - } - - if (gCurrRumbleSettings.unk0A == 0) { - gCurrRumbleSettings.unk0A = 7; +void reset_rumble_timers_slip(s16 port) { + if (gCurrRumbleSettings[port].unk0A == 0) { + gCurrRumbleSettings[port].unk0A = 7; } - if (gCurrRumbleSettings.unk0A < 4) { - gCurrRumbleSettings.unk0A = 4; + if (gCurrRumbleSettings[port].unk0A < 4) { + gCurrRumbleSettings[port].unk0A = 4; } - gCurrRumbleSettings.unk0C = 7; + gCurrRumbleSettings[port].unk0C = 7; } -void reset_rumble_timers_vibrate(s32 a0) { - if (gCurrDemoInput != NULL) { - return; - } - - if (gCurrRumbleSettings.unk0A == 0) { - gCurrRumbleSettings.unk0A = 7; +void reset_rumble_timers_vibrate(s16 port, s32 a0) { + if (gCurrRumbleSettings[port].unk0A == 0) { + gCurrRumbleSettings[port].unk0A = 7; } - if (gCurrRumbleSettings.unk0A < 4) { - gCurrRumbleSettings.unk0A = 4; + if (gCurrRumbleSettings[port].unk0A < 4) { + gCurrRumbleSettings[port].unk0A = 4; } if (a0 == 4) { - gCurrRumbleSettings.unk0C = 1; + gCurrRumbleSettings[port].unk0C = 1; } if (a0 == 3) { - gCurrRumbleSettings.unk0C = 2; + gCurrRumbleSettings[port].unk0C = 2; } if (a0 == 2) { - gCurrRumbleSettings.unk0C = 3; + gCurrRumbleSettings[port].unk0C = 3; } if (a0 == 1) { - gCurrRumbleSettings.unk0C = 4; + gCurrRumbleSettings[port].unk0C = 4; } if (a0 == 0) { - gCurrRumbleSettings.unk0C = 5; - } -} - -/* this is used when Mario is underwater */ -void queue_rumble_submerged(void) { - if (gCurrDemoInput != NULL) { - return; + gCurrRumbleSettings[port].unk0C = 5; } - - gCurrRumbleSettings.unk0A = 4; - gCurrRumbleSettings.unk0C = 4; } static void thread6_rumble_loop(UNUSED void *a0) { OSMesg msg; + s16 port; - CN_DEBUG_PRINTF(("start motor thread\n")); + cancel_rumble_all(); + sRumblePakThreadActive = true; - cancel_rumble(); - sRumblePakThreadActive = TRUE; - - CN_DEBUG_PRINTF(("go motor thread\n")); - - while (TRUE) { + while (true) { // Block until VI osRecvMesg(&gRumbleThreadVIMesgQueue, &msg, OS_MESG_BLOCK); - update_rumble_data_queue(); - update_rumble_pak(); + update_rumble_data_queue_all(); + update_rumble_pak_all(); - if (sRumblePakActive) { - if (sRumblePakErrorCount >= 30) { - sRumblePakActive = FALSE; + for (port = 0; port < 3; port++) { + if (sRumblePakActive[port]) { + if (sRumblePakErrorCount[port] >= 30) { + sRumblePakActive[port] = false; + } + } + else if (sNumVBlanks % 60 == 0) { + sRumblePakActive[port] = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs[port], port) == 0; + sRumblePakErrorCount[port] = 0; } - } else if (gNumVblanks % 60 == 0) { - sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) == 0; - sRumblePakErrorCount = 0; - } - if (gRumblePakTimer > 0) { - gRumblePakTimer--; - } + if (gRumblePakTimer[port] > 0) { + gRumblePakTimer[port]--; + } + } } } -void cancel_rumble(void) { - sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) == 0; +void cancel_rumble_all() { + s16 port; + for (port = 0; port < 3; port++) { + cancel_rumble(port); + } +} + +void cancel_rumble(s16 port) { + sRumblePakActive[port] = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs[port], port) == 0; - if (sRumblePakActive) { - osMotorStop(&gRumblePakPfs); + if (sRumblePakActive[port]) { + osMotorStop(&gRumblePakPfs[port]); } - gRumbleDataQueue[0].comm = 0; - gRumbleDataQueue[1].comm = 0; - gRumbleDataQueue[2].comm = 0; + gRumbleDataQueue[port][0].comm = 0; + gRumbleDataQueue[port][1].comm = 0; + gRumbleDataQueue[port][2].comm = 0; - gCurrRumbleSettings.unk04 = 0; - gCurrRumbleSettings.unk0A = 0; + gCurrRumbleSettings[port].unk04 = 0; + gCurrRumbleSettings[port].unk0A = 0; - gRumblePakTimer = 0; + gRumblePakTimer[port] = 0; } void create_thread_6(void) { osCreateMesgQueue(&gRumbleThreadVIMesgQueue, &gRumbleThreadVIMesgBuf, 1); - osCreateThread(&gRumblePakThread, 6, thread6_rumble_loop, NULL, gThread6Stack + 0x2000, 30); + osCreateThread(&gRumblePakThread, 6, thread6_rumble_loop, NULL, gRumbleThreadStack + 0x2000, 30); osStartThread(&gRumblePakThread); } diff --git a/src/rumble_init.h b/src/rumble_init.h index 936015c772..4a229394b5 100644 --- a/src/rumble_init.h +++ b/src/rumble_init.h @@ -2,6 +2,7 @@ #define RUMBLE_INIT_H #include "config.h" +#include #if ENABLE_RUMBLE @@ -25,28 +26,29 @@ struct RumbleSettings { extern OSThread gRumblePakThread; -extern OSPfs gRumblePakPfs; +extern OSPfs gRumblePakPfs[4]; extern OSMesg gRumblePakSchedulerMesgBuf; extern OSMesgQueue gRumblePakSchedulerMesgQueue; extern OSMesg gRumbleThreadVIMesgBuf; extern OSMesgQueue gRumbleThreadVIMesgQueue; -extern struct RumbleData gRumbleDataQueue[3]; -extern struct RumbleSettings gCurrRumbleSettings; +extern struct RumbleData gRumbleDataQueue[4][3]; +extern struct RumbleSettings gCurrRumbleSettings[4]; -extern s32 gRumblePakTimer; +extern s32 gRumblePakTimer[4]; void init_rumble_pak_scheduler_queue(void); void block_until_rumble_pak_free(void); void release_rumble_pak_control(void); -void queue_rumble_data(s16 a0, s16 a1); -void func_sh_8024C89C(s16 a0); -u8 is_rumble_finished_and_queue_empty(void); -void reset_rumble_timers(void); -void reset_rumble_timers_2(s32 a0); -void func_sh_8024CA04(void); -void cancel_rumble(void); +void queue_rumble_data(s16 port, s16 time, s16 level); +void queue_rumble_decay(s16 port, s16 decay); +u8 is_rumble_finished_and_queue_empty_all(void); +u8 is_rumble_finished_and_queue_empty(s16 port); +void reset_rumble_timers_slip(s16 port); +void reset_rumble_timers_vibrate(s16 port, s32 a0); +void cancel_rumble_all(void); +void cancel_rumble(s16 port); void create_thread_6(void); void rumble_thread_update_vi(void); diff --git a/src/save.c b/src/save.c index bc626d529f..675339b3b9 100644 --- a/src/save.c +++ b/src/save.c @@ -10,6 +10,7 @@ #include "save_data.h" #include "staff_ghosts.h" #include "code_80057C60.h" +#include "rumble_init.h" /*** macros ***/ #define PFS_COMPANY_CODE(c0, c1) ((u16) (((c0) << 8) | ((c1)))) @@ -45,12 +46,12 @@ void func_800B45E0(s32 arg0) { courseTimeTrialRecordsPtr->checksum = checksum_time_trial_records(arg0); #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(courseTimeTrialRecordsPtr), (u8*) courseTimeTrialRecordsPtr, sizeof(CourseTimeTrialRecords)); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif } @@ -59,11 +60,11 @@ void write_save_data_grand_prix_points_and_sound_mode(void) { main->checksum[1] = compute_save_data_checksum_1(); main->checksum[2] = compute_save_data_checksum_2(); #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(main), (u8*) main, sizeof(Stuff)); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif } @@ -151,11 +152,11 @@ void load_save_data(void) { s32 i; #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongRead(&gSIEventMesgQueue, EEPROM_ADDR(&gSaveData), (u8*) &gSaveData, sizeof(SaveData)); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif // 16: 4 cup records * 4 course records? for (i = 0; i < 16; i++) { @@ -234,11 +235,11 @@ void validate_save_data(void) { main->checksum[1] = compute_save_data_checksum_backup_1(); main->checksum[2] = compute_save_data_checksum_backup_2(); #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(main), (u8*) main, sizeof(Stuff)); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif } update_save_data_backup(); @@ -483,12 +484,12 @@ void func_800B559C(s32 arg0) { bestRecord->unknownBytes[6] = func_800B578C(x); bestRecord->unknownBytes[7] = func_800B5888(x); #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongWrite(&gSIEventMesgQueue, ((u32) (((u8*) bestRecord) - ((u8*) (&gSaveData)))) >> 3, bestRecord->bestThreelaps[0], 0x38); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif } @@ -546,11 +547,11 @@ void update_save_data_backup(void) { backup->checksum[1] = compute_save_data_checksum_backup_1(); backup->checksum[2] = compute_save_data_checksum_backup_2(); #if ENABLE_RUMBLE - block_until_rumble_pak_free(); + //block_until_rumble_pak_free(); #endif osEepromLongWrite(&gSIEventMesgQueue, EEPROM_ADDR(backup), (u8*) backup, sizeof(Stuff)); #if ENABLE_RUMBLE - release_rumble_pak_control(); + //release_rumble_pak_control(); #endif }