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 d3636752c3..0f61229b93 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 @@ -55,6 +57,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*); @@ -129,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*); @@ -222,6 +230,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*); @@ -256,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*); @@ -272,6 +286,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 +348,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*); @@ -355,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 044cc235f0..c4ad182fe3 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]; @@ -348,9 +352,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 +1181,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 +1211,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/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 5223158f56..a18f214d40 100644 --- a/src/menus.c +++ b/src/menus.c @@ -20,6 +20,7 @@ #include #include "spawn_players.h" #include "seq_ids.h" +#include "rumble_init.h" #if ENABLE_DEBUG_MODE #define DEBUG_MODE_TOGGLE true @@ -1448,6 +1449,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) { @@ -1653,6 +1657,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) { @@ -1758,6 +1765,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) { 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/player_controller.c b/src/player_controller.c index 7c9f5cc8ac..c30879ff69 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; @@ -1991,6 +1992,11 @@ void apply_effect(Player* player, s8 playerIndex, s8 arg2) { } if ((player->effects & MUSHROOM_EFFECT) == MUSHROOM_EFFECT) { apply_mushroom_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); @@ -2000,9 +2006,19 @@ void apply_effect(Player* player, s8 playerIndex, s8 arg2) { } if ((s32) (player->effects & SQUISH_EFFECT) == SQUISH_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 & TERRAIN_TUMBLE_EFFECT) == TERRAIN_TUMBLE_EFFECT) { func_8008F3F4(player, playerIndex); diff --git a/src/rumble_init.c b/src/rumble_init.c new file mode 100644 index 0000000000..9702cbb9bd --- /dev/null +++ b/src/rumble_init.c @@ -0,0 +1,313 @@ +#include "config.h" + +#if ENABLE_RUMBLE + +#include +#include +#include "macros.h" + +#include "main.h" +#include "rumble_init.h" + +OSThread gRumblePakThread; + +OSPfs gRumblePakPfs[4]; + +OSMesg gRumblePakSchedulerMesgBuf; +OSMesgQueue gRumblePakSchedulerMesgQueue; +OSMesg gRumbleThreadVIMesgBuf; +OSMesgQueue gRumbleThreadVIMesgQueue; + +struct RumbleData gRumbleDataQueue[4][3]; +struct RumbleSettings gCurrRumbleSettings[4]; + +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); + 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(s16 port) { + if (!sRumblePakActive[port]) { + return; + } + + block_until_rumble_pak_free(); + + if (!osMotorStart(&gRumblePakPfs[port])) { + sRumblePakErrorCount[port] = 0; + } else { + sRumblePakErrorCount[port]++; + } + + release_rumble_pak_control(); +} + +static void stop_rumble(s16 port) { + if (!sRumblePakActive[port]) { + return; + } + + block_until_rumble_pak_free(); + + if (!osMotorStop(&gRumblePakPfs[port])) { + sRumblePakErrorCount[port] = 0; + } else { + sRumblePakErrorCount[port]++; + } + + release_rumble_pak_control(); +} + +static void update_rumble_pak(s16 port) { + //todo: update with correct var from main + /*if (gResetTimer > 0) { + stop_rumble(port); + return; + }*/ + + if (gCurrRumbleSettings[port].unk08 > 0) { + gCurrRumbleSettings[port].unk08--; + start_rumble(port); + } else if (gCurrRumbleSettings[port].unk04 > 0) { + gCurrRumbleSettings[port].unk04--; + + gCurrRumbleSettings[port].unk02 -= gCurrRumbleSettings[port].unk0E; + if (gCurrRumbleSettings[port].unk02 < 0) { + gCurrRumbleSettings[port].unk02 = 0; + } + + if (gCurrRumbleSettings[port].unk00 == 1) { + start_rumble(port); + } else if (gCurrRumbleSettings[port].unk06 >= 0x100) { + gCurrRumbleSettings[port].unk06 -= 0x100; + start_rumble(port); + } else { + gCurrRumbleSettings[port].unk06 += + ((gCurrRumbleSettings[port].unk02 * gCurrRumbleSettings[port].unk02 * gCurrRumbleSettings[port].unk02) / (1 << 9)) + 4; + + stop_rumble(port); + } + } else { + gCurrRumbleSettings[port].unk04 = 0; + + 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(port); + } + } + + if (gCurrRumbleSettings[port].unk0A > 0) { + gCurrRumbleSettings[port].unk0A--; + } +} + +static void update_rumble_pak_all(void) +{ + s16 port; + for (port = 0; port < 3; port++) { + update_rumble_pak(port); + } +} + +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[port][2].comm = 1; + } else { + 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); + } +} + +void queue_rumble_decay(s16 port, s16 level) { + gRumbleDataQueue[port][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(s16 port) { + if (gCurrRumbleSettings[port].unk08 + gCurrRumbleSettings[port].unk04 >= 4) { + return false; + } + + if (gRumbleDataQueue[port][0].comm != 0) { + return false; + } + + if (gRumbleDataQueue[port][1].comm != 0) { + return false; + } + + if (gRumbleDataQueue[port][2].comm != 0) { + return false; + } + + return true; +} + +void reset_rumble_timers_slip(s16 port) { + if (gCurrRumbleSettings[port].unk0A == 0) { + gCurrRumbleSettings[port].unk0A = 7; + } + + if (gCurrRumbleSettings[port].unk0A < 4) { + gCurrRumbleSettings[port].unk0A = 4; + } + + gCurrRumbleSettings[port].unk0C = 7; +} + +void reset_rumble_timers_vibrate(s16 port, s32 a0) { + if (gCurrRumbleSettings[port].unk0A == 0) { + gCurrRumbleSettings[port].unk0A = 7; + } + + if (gCurrRumbleSettings[port].unk0A < 4) { + gCurrRumbleSettings[port].unk0A = 4; + } + + if (a0 == 4) { + gCurrRumbleSettings[port].unk0C = 1; + } + + if (a0 == 3) { + gCurrRumbleSettings[port].unk0C = 2; + } + + if (a0 == 2) { + gCurrRumbleSettings[port].unk0C = 3; + } + + if (a0 == 1) { + gCurrRumbleSettings[port].unk0C = 4; + } + + if (a0 == 0) { + gCurrRumbleSettings[port].unk0C = 5; + } +} + +static void thread6_rumble_loop(UNUSED void *a0) { + OSMesg msg; + s16 port; + + cancel_rumble_all(); + sRumblePakThreadActive = true; + + while (true) { + // Block until VI + osRecvMesg(&gRumbleThreadVIMesgQueue, &msg, OS_MESG_BLOCK); + + update_rumble_data_queue_all(); + update_rumble_pak_all(); + + 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; + } + + if (gRumblePakTimer[port] > 0) { + gRumblePakTimer[port]--; + } + } + } +} + +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[port]) { + osMotorStop(&gRumblePakPfs[port]); + } + + gRumbleDataQueue[port][0].comm = 0; + gRumbleDataQueue[port][1].comm = 0; + gRumbleDataQueue[port][2].comm = 0; + + gCurrRumbleSettings[port].unk04 = 0; + gCurrRumbleSettings[port].unk0A = 0; + + gRumblePakTimer[port] = 0; +} + +void create_thread_6(void) { + osCreateMesgQueue(&gRumbleThreadVIMesgQueue, &gRumbleThreadVIMesgBuf, 1); + osCreateThread(&gRumblePakThread, 6, thread6_rumble_loop, NULL, gRumbleThreadStack + 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..4a229394b5 --- /dev/null +++ b/src/rumble_init.h @@ -0,0 +1,57 @@ +#ifndef RUMBLE_INIT_H +#define RUMBLE_INIT_H + +#include "config.h" +#include + +#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[4]; + +extern OSMesg gRumblePakSchedulerMesgBuf; +extern OSMesgQueue gRumblePakSchedulerMesgQueue; +extern OSMesg gRumbleThreadVIMesgBuf; +extern OSMesgQueue gRumbleThreadVIMesgQueue; + +extern struct RumbleData gRumbleDataQueue[4][3]; +extern struct RumbleSettings gCurrRumbleSettings[4]; + +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 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); + +#endif // ENABLE_RUMBLE + +#endif // RUMBLE_INIT_H diff --git a/src/save.c b/src/save.c index 12d4631f78..821c633e6d 100644 --- a/src/save.c +++ b/src/save.c @@ -10,6 +10,7 @@ #include "save_data.h" #include "replays.h" #include "code_80057C60.h" +#include "rumble_init.h" /*** macros ***/ #define PFS_COMPANY_CODE(c0, c1) ((u16) (((c0) << 8) | ((c1)))) @@ -44,15 +45,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 +151,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 +234,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 +483,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 +546,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) {