diff --git a/asm/rom_header.s b/asm/rom_header.s index 0673aee8..13b25108 100644 --- a/asm/rom_header.s +++ b/asm/rom_header.s @@ -15,9 +15,14 @@ .word 0x00000000 /* Unknown */ .word 0x00000000 /* Unknown */ .ascii INTERNAL_ROM_NAME /* Internal ROM name */ +#if defined(USE_GAMECUBE_CONTROLLER) +/* Advanced homebrew ROM header bytes: https://n64brew.dev/wiki/ROM_Header#Advanced_Homebrew_ROM_Header */ +.word 0x82000000 +#else .word 0x00000000 /* Unknown */ +#endif .word 0x0000004E /* Cartridge */ -#if defined(EEP4K) +#if defined(EEP4K) && !defined(USE_GAMECUBE_CONTROLLER) .ascii "SM" /* Cartridge ID */ #else .ascii "ED" /* Cartridge ID */ diff --git a/include/config/config_rom.h b/include/config/config_rom.h index 14ddd21c..c6999fd6 100644 --- a/include/config/config_rom.h +++ b/include/config/config_rom.h @@ -28,3 +28,6 @@ */ #define BORDER_HEIGHT_CONSOLE 0 #define BORDER_HEIGHT_EMULATOR 0 + +// Informs supported emulators to default to gamecube controller inputs +// #define USE_GAMECUBE_CONTROLLER diff --git a/include/n64/PR/os_cont.h b/include/n64/PR/os_cont.h index 77032cfd..44299464 100644 --- a/include/n64/PR/os_cont.h +++ b/include/n64/PR/os_cont.h @@ -63,6 +63,18 @@ typedef struct { u8 error; } OSContPad; +// Custom extended controller pad struct that contains fields for gamecube controllers +typedef struct { + u16 button; + s8 stick_x; /* -80 <= stick_x <= 80 */ + s8 stick_y; /* -80 <= stick_y <= 80 */ + s8 c_stick_x; + s8 c_stick_y; + u8 l_trig; + u8 r_trig; + u8 errno; +} OSContPadEx; + typedef struct { void *address; /* Ram pad Address: 11 bits */ u8 databuffer[32]; /* address of the data buffer */ @@ -103,6 +115,7 @@ typedef struct { #define CONT_ABSOLUTE 0x0001 #define CONT_RELATIVE 0x0002 #define CONT_JOYPORT 0x0004 +#define CONT_GCN 0x0008 #define CONT_EEPROM 0x8000 #define CONT_EEP16K 0x4000 #define CONT_TYPE_MASK 0x1f07 @@ -150,6 +163,25 @@ typedef struct { #define L_CBUTTONS CONT_C #define R_CBUTTONS CONT_F #define D_CBUTTONS CONT_D +#define GCN_X_BUTTON 0x0040 +#define GCN_Y_BUTTON 0x0080 + +/* Gamecube controller buttons */ + +#define CONT_GCN_GET_ORIGIN 0x2000 +#define CONT_GCN_START 0x1000 +#define CONT_GCN_Y 0x0800 +#define CONT_GCN_X 0x0400 +#define CONT_GCN_B 0x0200 +#define CONT_GCN_A 0x0100 +#define CONT_GCN_USE_ORIGIN 0x0080 +#define CONT_GCN_L 0x0040 +#define CONT_GCN_R 0x0020 +#define CONT_GCN_Z 0x0010 +#define CONT_GCN_UP 0x0008 +#define CONT_GCN_DOWN 0x0004 +#define CONT_GCN_RIGHT 0x0002 +#define CONT_GCN_LEFT 0x0001 /* Controller error number */ @@ -162,6 +194,9 @@ typedef struct { #define CONT_ERR_VOICE_WORD 14 #define CONT_ERR_VOICE_NO_RESPONSE 15 +#define CONT_TYPE_N64 0 +#define CONT_TYPE_GCN 1 + #if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) @@ -178,6 +213,7 @@ typedef struct { * */ +extern u8 __osControllerTypes[MAXCONTROLLERS]; /************************************************************************** * @@ -191,11 +227,13 @@ extern s32 osContInit( OSMesgQueue *mq, u8 *bitpattern, OSContStatus *s extern s32 osContReset( OSMesgQueue *mq, OSContStatus *status); extern s32 osContStartQuery( OSMesgQueue *mq); extern s32 osContStartReadData(OSMesgQueue *mq); +extern s32 osContStartReadDataEx(OSMesgQueue *mq); #ifndef _HW_VERSION_1 extern s32 osContSetCh(u8 ch); #endif extern void osContGetQuery(OSContStatus *status); extern void osContGetReadData(OSContPad *pad); +extern void osContGetReadDataEx(OSContPadEx *pad); #endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ diff --git a/include/n64/PR/os_motor.h b/include/n64/PR/os_motor.h index fbecc395..d2b64160 100644 --- a/include/n64/PR/os_motor.h +++ b/include/n64/PR/os_motor.h @@ -62,12 +62,14 @@ extern "C" { /* Rumble PAK interface */ extern s32 osMotorInit(OSMesgQueue *mq, OSPfs *pfs, int controller_no); +extern s32 osMotorInitEx(OSMesgQueue *mq, OSPfs *pfs, int controller_no); #if 1 #define MOTOR_START 1 #define MOTOR_STOP 0 -#define osMotorStart(x) __osMotorAccess((x), MOTOR_START) -#define osMotorStop(x) __osMotorAccess((x), MOTOR_STOP) +#define osMotorStart(x) __osMotorAccessEx((x), MOTOR_START) +#define osMotorStop(x) __osMotorAccessEx((x), MOTOR_STOP) extern s32 __osMotorAccess(OSPfs *pfs, s32 flag); +extern s32 __osMotorAccessEx(OSPfs *pfs, s32 flag); #else extern s32 osMotorStop( OSPfs *pfs); extern s32 osMotorStart(OSPfs *pfs); diff --git a/include/sm64.h b/include/sm64.h index af7b7232..f9c44cd0 100644 --- a/include/sm64.h +++ b/include/sm64.h @@ -717,7 +717,8 @@ enum MarioActionFlags { #define VALID_BUTTONS (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | \ U_JPAD | D_JPAD | L_JPAD | R_JPAD | \ L_TRIG | R_TRIG | \ - U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS ) + U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS | \ + GCN_X_BUTTON | GCN_Y_BUTTON) #define C_BUTTONS (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS ) diff --git a/include/types.h b/include/types.h index 2e0f5189..42614925 100644 --- a/include/types.h +++ b/include/types.h @@ -42,7 +42,7 @@ struct Controller { /*0x12*/ u16 buttonPressed; /*0x14*/ u16 buttonReleased; /*0x18*/ OSContStatus *statusData; - /*0x1C*/ OSContPad *controllerData; + /*0x1C*/ OSContPadEx *controllerData; #if ENABLE_RUMBLE /*0x20*/ s32 port; #endif diff --git a/sm64.ld b/sm64.ld index ad668c81..13177458 100755 --- a/sm64.ld +++ b/sm64.ld @@ -184,6 +184,8 @@ SECTIONS lib/PR/audio/n_aspMain.o(.text*); lib/PR/hvqm/hvqm2sp1.o(.text*); _mainSegmentTextEnd = .; + /* Overwrite a libultra function with its modified counterpart for GCN controller support */ + __osContGetInitData = __osContGetInitDataEx; /* data */ BUILD_DIR/asm/n64_assert.o(.*data*); diff --git a/src/game/crash_screen.c b/src/game/crash_screen.c index f95b0286..0913e6ba 100644 --- a/src/game/crash_screen.c +++ b/src/game/crash_screen.c @@ -425,7 +425,7 @@ void thread2_crash_screen(UNUSED void *arg) { #if ENABLE_RUMBLE block_until_rumble_pak_free(); #endif - osContStartReadData(&gSIEventMesgQueue); + osContStartReadDataEx(&gSIEventMesgQueue); } read_controller_inputs(THREAD_2_CRASH_SCREEN); draw_crash_screen(thread); diff --git a/src/game/game_init.c b/src/game/game_init.c index 71c021cc..676c8f8a 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -43,8 +43,9 @@ struct GfxPool *gGfxPool; // OS Controllers OSContStatus gControllerStatuses[4]; -OSContPad gControllerPads[4]; +OSContPadEx gControllerPads[4]; u8 gControllerBits; +s8 gGamecubeControllerPort = -1; // HackerSM64: This is set to -1 if there's no GC controller, 0 if there's one in the first port and 1 if there's one in the second port. u8 gIsConsole = TRUE; // Needs to be initialized before audio_reset_session is called u8 gCacheEmulated = TRUE; u8 gBorderHeight; @@ -602,7 +603,7 @@ void read_controller_inputs(s32 threadID) { if (threadID == THREAD_5_GAME_LOOP) { osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK); } - osContGetReadData(&gControllerPads[0]); + osContGetReadDataEx(&gControllerPads[0]); #if ENABLE_RUMBLE release_rumble_pak_control(); #endif @@ -613,7 +614,18 @@ void read_controller_inputs(s32 threadID) { for (i = 0; i < 2; i++) { struct Controller *controller = &gControllers[i]; - + // HackerSM64: Swaps Z and L, only on console, and only when playing with a GameCube controller. + u32 oldButton = controller->controllerData->button; + if (gIsConsole && (gGamecubeControllerPort >= 0)) { + u32 newButton = oldButton & ~(Z_TRIG | L_TRIG); + if (oldButton & Z_TRIG) { + newButton |= L_TRIG; + } + if (controller->controllerData->l_trig > 85) { // How far the player has to press the L trigger for it to be considered a Z press. 64 is about 25%. 127 would be about 50%. + newButton |= Z_TRIG; + } + controller->controllerData->button = newButton; + } // if we're receiving inputs, update the controller struct with the new button info. if (controller->controllerData != NULL) { controller->rawStickX = controller->controllerData->stick_x; @@ -690,6 +702,15 @@ void init_controllers(void) { gControllers[cont++].controllerData = &gControllerPads[port]; } } + if (__osControllerTypes[1] == CONT_TYPE_GCN) { + gGamecubeControllerPort = 1; + gPlayer1Controller = &gControllers[1]; + } else { + if (__osControllerTypes[0] == CONT_TYPE_GCN) { + gGamecubeControllerPort = 0; + } + gPlayer1Controller = &gControllers[0]; + } } // Game thread core @@ -769,7 +790,7 @@ void thread5_game_loop(UNUSED void *arg) { #if ENABLE_RUMBLE block_until_rumble_pak_free(); #endif - osContStartReadData(&gSIEventMesgQueue); + osContStartReadDataEx(&gSIEventMesgQueue); } audio_game_loop_tick(); diff --git a/src/game/game_init.h b/src/game/game_init.h index 36916060..4eb2d021 100644 --- a/src/game/game_init.h +++ b/src/game/game_init.h @@ -32,7 +32,7 @@ enum ZBmodes { extern struct Controller gControllers[3]; extern OSContStatus gControllerStatuses[4]; -extern OSContPad gControllerPads[4]; +extern OSContPadEx gControllerPads[4]; extern OSMesgQueue gGameVblankQueue; extern OSMesgQueue gGfxVblankQueue; extern OSMesg gGameMesgBuf[1]; @@ -47,6 +47,7 @@ extern Gfx *gDisplayListHead; extern u8 *gGfxPoolEnd; extern struct GfxPool *gGfxPool; extern u8 gControllerBits; +extern s8 gGamecubeControllerPort; extern u8 gIsConsole; extern u8 gCacheEmulated; extern u8 gBorderHeight; diff --git a/src/game/gamecube_controller.c b/src/game/gamecube_controller.c new file mode 100644 index 00000000..0d02c8a3 --- /dev/null +++ b/src/game/gamecube_controller.c @@ -0,0 +1,531 @@ +#include "PR/os_internal.h" + +///////////////////////////////////////////////// +// Libultra structs and macros (from ultralib) // +///////////////////////////////////////////////// + +#define ARRLEN(x) ((s32)(sizeof(x) / sizeof(x[0]))) +#define CHNL_ERR(format) (((format).rxsize & CHNL_ERR_MASK) >> 4) + +#define CHNL_ERR_MASK 0xC0 /* Bit 6-7: channel errors */ + +typedef struct +{ + /* 0x0 */ u32 ramarray[15]; + /* 0x3C */ u32 pifstatus; +} OSPifRam; + +typedef struct +{ + /* 0x0 */ u8 dummy; + /* 0x1 */ u8 txsize; + /* 0x2 */ u8 rxsize; + /* 0x3 */ u8 cmd; + /* 0x4 */ u16 button; + /* 0x6 */ s8 stick_x; + /* 0x7 */ s8 stick_y; +} __OSContReadFormat; + +typedef struct +{ + /* 0x0 */ u8 dummy; + /* 0x1 */ u8 txsize; + /* 0x2 */ u8 rxsize; + /* 0x3 */ u8 cmd; + /* 0x4 */ u8 typeh; + /* 0x5 */ u8 typel; + /* 0x6 */ u8 status; + /* 0x7 */ u8 dummy1; +} __OSContRequesFormat; + +typedef struct +{ + /* 0x0 */ u8 txsize; + /* 0x1 */ u8 rxsize; + /* 0x2 */ u8 cmd; + /* 0x3 */ u8 typeh; + /* 0x4 */ u8 typel; + /* 0x5 */ u8 status; +} __OSContRequesFormatShort; + +typedef struct +{ + /* 0x0 */ u8 dummy; + /* 0x1 */ u8 txsize; + /* 0x2 */ u8 rxsize; + /* 0x3 */ u8 cmd; + /* 0x4 */ u8 addrh; + /* 0x5 */ u8 addrl; + /* 0x6 */ u8 data[BLOCKSIZE]; + /* 0x26 */ u8 datacrc; +} __OSContRamReadFormat; + +extern OSPifRam __osContPifRam; +extern u8 __osMaxControllers; + +#define CONT_CMD_READ_BUTTON 1 + +// Controller accessory addresses +// https://github.com/joeldipops/TransferBoy/blob/master/docs/TransferPakReference.md + +// Accesory detection +#define CONT_ADDR_DETECT 0x8000 +// Rumble +#define CONT_ADDR_RUMBLE 0xC000 +// Controller Pak +// Transfer Pak +#define CONT_ADDR_GB_POWER 0x8000 // Same as the detection address, but semantically different +#define CONT_ADDR_GB_BANK 0xA000 +#define CONT_ADDR_GB_STATUS 0xB000 + +// Addresses sent to controller accessories are in blocks, not bytes +#define CONT_BLOCKS(x) ((x) / BLOCKSIZE) + +// Block addresses of the above +#define CONT_BLOCK_DETECT CONT_BLOCKS(CONT_ADDR_DETECT) +#define CONT_BLOCK_RUMBLE CONT_BLOCKS(CONT_ADDR_RUMBLE) +#define CONT_BLOCK_GB_POWER CONT_BLOCKS(CONT_ADDR_GB_POWER) +#define CONT_BLOCK_GB_BANK CONT_BLOCKS(CONT_ADDR_GB_BANK) +#define CONT_BLOCK_GB_STATUS CONT_BLOCKS(CONT_ADDR_GB_STATUS) + +// Joybus commands +//from: http://en64.shoutwiki.com/wiki/SI_Registers_Detailed#CONT_CMD_Usage +#define CONT_CMD_REQUEST_STATUS 0 +#define CONT_CMD_READ_BUTTON 1 +#define CONT_CMD_READ_PAK 2 +#define CONT_CMD_WRITE_PAK 3 +#define CONT_CMD_READ_EEPROM 4 +#define CONT_CMD_WRITE_EEPROM 5 +#define CONT_CMD_READ36_VOICE 9 +#define CONT_CMD_WRITE20_VOICE 10 +#define CONT_CMD_READ2_VOICE 11 +#define CONT_CMD_WRITE4_VOICE 12 +#define CONT_CMD_SWRITE_VOICE 13 +#define CONT_CMD_CHANNEL_RESET 0xFD +#define CONT_CMD_RESET 0xFF +#define CONT_CMD_GCN_SHORTPOLL 0x40 + +// Bytes transmitted for each joybus command +#define CONT_CMD_REQUEST_STATUS_TX 1 +#define CONT_CMD_READ_BUTTON_TX 1 +#define CONT_CMD_READ_PAK_TX 3 +#define CONT_CMD_WRITE_PAK_TX 35 +#define CONT_CMD_READ_EEPROM_TX 2 +#define CONT_CMD_WRITE_EEPROM_TX 10 +#define CONT_CMD_READ36_VOICE_TX 3 +#define CONT_CMD_WRITE20_VOICE_TX 23 +#define CONT_CMD_READ2_VOICE_TX 3 +#define CONT_CMD_WRITE4_VOICE_TX 7 +#define CONT_CMD_SWRITE_VOICE_TX 3 +#define CONT_CMD_RESET_TX 1 +#define CONT_CMD_GCN_SHORTPOLL_TX 3 + +// Bytes received for each joybus command +#define CONT_CMD_REQUEST_STATUS_RX 3 +#define CONT_CMD_READ_BUTTON_RX 4 +#define CONT_CMD_READ_PAK_RX 33 +#define CONT_CMD_WRITE_PAK_RX 1 +#define CONT_CMD_READ_EEPROM_RX 8 +#define CONT_CMD_WRITE_EEPROM_RX 1 +#define CONT_CMD_READ36_VOICE_RX 37 +#define CONT_CMD_WRITE20_VOICE_RX 1 +#define CONT_CMD_READ2_VOICE_RX 3 +#define CONT_CMD_WRITE4_VOICE_RX 1 +#define CONT_CMD_SWRITE_VOICE_RX 1 +#define CONT_CMD_RESET_RX 3 +#define CONT_CMD_GCN_SHORTPOLL_RX 8 + +#define CONT_CMD_NOP 0xff +#define CONT_CMD_END 0xfe //indicates end of a command +#define CONT_CMD_EXE 1 //set pif ram status byte to this to do a command + +void __osSiGetAccess(void); +void __osSiRelAccess(void); + +//////////////////////// +// Gamecube additions // +//////////////////////// + +typedef struct +{ + /* 0x0 */ u8 dummy; + /* 0x1 */ u8 txsize; + /* 0x2 */ u8 rxsize; + /* 0x3 */ u8 cmd; + /* 0x4 */ u8 analog_mode; + /* 0x5 */ u8 rumble; + /* 0x6 */ u16 button; + /* 0x8 */ u8 stick_x; + /* 0x9 */ u8 stick_y; + /* 0xA */ u8 c_stick_x; + /* 0xB */ u8 c_stick_y; + /* 0xC */ u8 l_trig; + /* 0xD */ u8 r_trig; +} __OSContGCNShortPollFormat; +extern u8 __osContLastCmd; +u8 __osControllerTypes[MAXCONTROLLERS]; +u8 __osGamecubeRumbleEnabled[MAXCONTROLLERS]; + +#define GCN_C_STICK_THRESHOLD 38 + +static void __osPackReadData(void); +static u16 __osTranslateGCNButtons(u16, s32, s32); + +//////////////////// +// contreaddata.c // +//////////////////// + +s32 osContStartReadDataEx(OSMesgQueue* mq) { + s32 ret = 0; + + __osSiGetAccess(); + + if (__osContLastCmd != CONT_CMD_READ_BUTTON) { + __osPackReadData(); + ret = __osSiRawStartDma(OS_WRITE, __osContPifRam.ramarray); + osRecvMesg(mq, NULL, OS_MESG_BLOCK); + } + + ret = __osSiRawStartDma(OS_READ, __osContPifRam.ramarray); + __osContLastCmd = CONT_CMD_READ_BUTTON; + __osSiRelAccess(); + + return ret; +} + +void osContGetReadDataEx(OSContPadEx* data) { + u8* ptr = (u8*)__osContPifRam.ramarray; + __OSContReadFormat readformat; + __OSContGCNShortPollFormat readformatgcn; + int i; + + for (i = 0; i < __osMaxControllers; i++, data++) { + if (__osControllerTypes[i] == CONT_TYPE_GCN) { + s32 stick_x, stick_y, c_stick_x, c_stick_y; + readformatgcn = *(__OSContGCNShortPollFormat*)ptr; + stick_x = ((s32)readformatgcn.stick_x) - 128; + stick_y = ((s32)readformatgcn.stick_y) - 128; + data->stick_x = stick_x; + data->stick_y = stick_y; + c_stick_x = ((s32)readformatgcn.c_stick_x) - 128; + c_stick_y = ((s32)readformatgcn.c_stick_y) - 128; + data->c_stick_x = c_stick_x; + data->c_stick_y = c_stick_y; + data->button = __osTranslateGCNButtons(readformatgcn.button, c_stick_x, c_stick_y); + data->l_trig = readformatgcn.l_trig; + data->r_trig = readformatgcn.r_trig; + ptr += sizeof(__OSContGCNShortPollFormat); + } else { + readformat = *(__OSContReadFormat*)ptr; + data->errno = CHNL_ERR(readformat); + + if (data->errno != 0) { + continue; + } + + data->stick_x = readformat.stick_x; + data->stick_y = readformat.stick_y; + data->button = readformat.button; + data->c_stick_x = 0; + data->c_stick_y = 0; + data->l_trig = 0; + data->r_trig = 0; + ptr += sizeof(__OSContReadFormat); + } + } +} + +static void __osPackReadData(void) { + u8* ptr = (u8*)__osContPifRam.ramarray; + __OSContReadFormat readformat; + __OSContGCNShortPollFormat readformatgcn; + int i; + + for (i = 0; i < ARRLEN(__osContPifRam.ramarray); i++) { + __osContPifRam.ramarray[i] = 0; + } + + __osContPifRam.pifstatus = CONT_CMD_EXE; + readformat.dummy = CONT_CMD_NOP; + readformat.txsize = CONT_CMD_READ_BUTTON_TX; + readformat.rxsize = CONT_CMD_READ_BUTTON_RX; + readformat.cmd = CONT_CMD_READ_BUTTON; + readformat.button = 0xFFFF; + readformat.stick_x = -1; + readformat.stick_y = -1; + + readformatgcn.dummy = CONT_CMD_NOP; + readformatgcn.txsize = CONT_CMD_GCN_SHORTPOLL_TX; + readformatgcn.rxsize = CONT_CMD_GCN_SHORTPOLL_RX; + readformatgcn.cmd = CONT_CMD_GCN_SHORTPOLL; + readformatgcn.analog_mode = 3; + readformatgcn.rumble = 0; + readformatgcn.button = 0xFFFF; + readformatgcn.stick_x = -1; + readformatgcn.stick_y = -1; + + for (i = 0; i < __osMaxControllers; i++) { + if (__osControllerTypes[i] == CONT_TYPE_GCN) { + readformatgcn.rumble = __osGamecubeRumbleEnabled[i]; + *(__OSContGCNShortPollFormat*)ptr = readformatgcn; + ptr += sizeof(__OSContGCNShortPollFormat); + } else { + *(__OSContReadFormat*)ptr = readformat; + ptr += sizeof(__OSContReadFormat); + } + } + + *ptr = CONT_CMD_END; +} + +static u16 __osTranslateGCNButtons(u16 input, s32 c_stick_x, s32 c_stick_y) { + u16 ret = 0; + + // Face buttons + if (input & CONT_GCN_A) { + ret |= A_BUTTON; + } + if (input & CONT_GCN_B) { + ret |= B_BUTTON; + } + if (input & CONT_GCN_START) { + ret |= START_BUTTON; + } + if (input & CONT_GCN_X) { + ret |= GCN_X_BUTTON; + } + if (input & CONT_GCN_Y) { + ret |= GCN_Y_BUTTON; + } + + // Triggers & Z + if (input & CONT_GCN_Z) { + ret |= Z_TRIG; + } + if (input & CONT_GCN_R) { + ret |= R_TRIG; + } + if (input & CONT_GCN_L) { + ret |= L_TRIG; + } + + // D-Pad + if (input & CONT_GCN_UP) { + ret |= U_JPAD; + } + if (input & CONT_GCN_DOWN) { + ret |= D_JPAD; + } + if (input & CONT_GCN_LEFT) { + ret |= L_JPAD; + } + if (input & CONT_GCN_RIGHT) { + ret |= R_JPAD; + } + + // C-stick to C-buttons + if (c_stick_x > GCN_C_STICK_THRESHOLD) { + ret |= R_CBUTTONS; + } + if (c_stick_x < -GCN_C_STICK_THRESHOLD) { + ret |= L_CBUTTONS; + } + if (c_stick_y > GCN_C_STICK_THRESHOLD) { + ret |= U_CBUTTONS; + } + if (c_stick_y < -GCN_C_STICK_THRESHOLD) { + ret |= D_CBUTTONS; + } + + return ret; +} + +////////////////// +// controller.c // +////////////////// + +extern s32 __osContinitialized; + +extern OSPifRam __osContPifRam; +extern u8 __osContLastCmd; +extern u8 __osMaxControllers; +extern u8 __osControllerTypes[MAXCONTROLLERS]; +extern u8 __osGamecubeRumbleEnabled[MAXCONTROLLERS]; + +extern OSTimer __osEepromTimer; +extern OSMesgQueue __osEepromTimerQ; +extern OSMesg __osEepromTimerMsg; + +// Linker script will resolve references to the original function with this one instead +void __osContGetInitDataEx(u8* pattern, OSContStatus* data) { + u8* ptr; + __OSContRequesFormat requestHeader; + s32 i; + u8 bits; + + bits = 0; + ptr = (u8*)__osContPifRam.ramarray; + for (i = 0; i < __osMaxControllers; i++, ptr += sizeof(requestHeader), data++) { + requestHeader = *(__OSContRequesFormat*)ptr; + data->error = CHNL_ERR(requestHeader); + if (data->error == 0) { + data->type = requestHeader.typel << 8 | requestHeader.typeh; + + // Check if the input type is a gamecube controller + // Some mupen cores seem to send back a controller type of 0xFFFF if the core doesn't initialize the input plugin quickly enough, + // so check for that and set the input type as N64 controller if so. + if ((data->type & CONT_GCN) && (s16)data->type != -1) { + __osControllerTypes[i] = CONT_TYPE_GCN; + } else { + __osControllerTypes[i] = CONT_TYPE_N64; + } + + data->status = requestHeader.status; + + bits |= 1 << i; + } + } + *pattern = bits; +} + +///////////// +// motor.c // +///////////// + +static OSPifRam __MotorDataBuf[MAXCONTROLLERS]; + +#define READFORMAT(ptr) ((__OSContRamReadFormat*)(ptr)) + +s32 __osMotorAccessEx(OSPfs* pfs, s32 flag) { + int i; + s32 ret; + u8* ptr = (u8*)&__MotorDataBuf[pfs->channel]; + + if (!(pfs->status & PFS_MOTOR_INITIALIZED)) { + return 5; + } + + if (__osControllerTypes[pfs->channel] == CONT_TYPE_GCN) { + __osGamecubeRumbleEnabled[pfs->channel] = flag; + __osContLastCmd = CONT_CMD_END; + } else { + __osSiGetAccess(); + __MotorDataBuf[pfs->channel].pifstatus = CONT_CMD_EXE; + ptr += pfs->channel; + + for (i = 0; i < BLOCKSIZE; i++) { + READFORMAT(ptr)->data[i] = flag; + } + + __osContLastCmd = CONT_CMD_END; + __osSiRawStartDma(OS_WRITE, &__MotorDataBuf[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + __osSiRawStartDma(OS_READ, &__MotorDataBuf[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + + ret = READFORMAT(ptr)->rxsize & CHNL_ERR_MASK; + if (!ret) { + if (!flag) { + if (READFORMAT(ptr)->datacrc != 0) { + ret = PFS_ERR_CONTRFAIL; + } + } else { + if (READFORMAT(ptr)->datacrc != 0xEB) { + ret = PFS_ERR_CONTRFAIL; + } + } + } + __osSiRelAccess(); + } + + return ret; +} + +static void _MakeMotorData(int channel, OSPifRam *mdata) { + u8 *ptr = (u8 *)mdata->ramarray; + __OSContRamReadFormat ramreadformat; + int i; + + ramreadformat.dummy = CONT_CMD_NOP; + ramreadformat.txsize = CONT_CMD_WRITE_PAK_TX; + ramreadformat.rxsize = CONT_CMD_WRITE_PAK_RX; + ramreadformat.cmd = CONT_CMD_WRITE_PAK; + ramreadformat.addrh = CONT_BLOCK_RUMBLE >> 3; + ramreadformat.addrl = (u8)(__osContAddressCrc(CONT_BLOCK_RUMBLE) | (CONT_BLOCK_RUMBLE << 5)); + + if (channel != 0) { + for (i = 0; i < channel; i++) { + *ptr++ = CONT_CMD_REQUEST_STATUS; + } + } + + *READFORMAT(ptr) = ramreadformat; + ptr += sizeof(__OSContRamReadFormat); + ptr[0] = CONT_CMD_END; +} + +s32 osMotorInitEx(OSMesgQueue *mq, OSPfs *pfs, int channel) +{ + s32 ret; + u8 temp[32]; + + pfs->queue = mq; + pfs->channel = channel; + pfs->activebank = 0xFF; + pfs->status = 0; + + if (__osControllerTypes[pfs->channel] != CONT_TYPE_GCN) { + ret = __osPfsSelectBank(pfs, 0xFE); + + if (ret == PFS_ERR_NEW_PACK) { + ret = __osPfsSelectBank(pfs, 0x80); + } + + if (ret != 0) { + return ret; + } + + ret = __osContRamRead(mq, channel, CONT_BLOCK_DETECT, temp); + + if (ret == PFS_ERR_NEW_PACK) { + ret = PFS_ERR_CONTRFAIL; + } + + if (ret != 0) { + return ret; + } + + if (temp[31] == 254) { + return PFS_ERR_DEVICE; + } + + ret = __osPfsSelectBank(pfs, 0x80); + if (ret == PFS_ERR_NEW_PACK) { + ret = PFS_ERR_CONTRFAIL; + } + + if (ret != 0) { + return ret; + } + + ret = __osContRamRead(mq, channel, CONT_BLOCK_DETECT, temp); + if (ret == PFS_ERR_NEW_PACK) { + ret = PFS_ERR_CONTRFAIL; + } + + if (ret != 0) { + return ret; + } + + if (temp[31] != 0x80) { + return PFS_ERR_DEVICE; + } + + if (!(pfs->status & PFS_MOTOR_INITIALIZED)) { + _MakeMotorData(channel, &__MotorDataBuf[channel]); + } + } + + pfs->status = PFS_MOTOR_INITIALIZED; + return 0; +} diff --git a/src/game/mario.c b/src/game/mario.c index 90f4da1d..c3a35954 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1865,7 +1865,11 @@ void init_mario_from_save_file(void) { gMarioState->spawnInfo = &gPlayerSpawnInfos[0]; gMarioState->statusForCamera = &gPlayerCameraState[0]; gMarioState->marioBodyState = &gBodyStates[0]; - gMarioState->controller = &gControllers[0]; + if (__osControllerTypes[1] == CONT_TYPE_GCN) { + gMarioState->controller = &gControllers[1]; + } else { + gMarioState->controller = &gControllers[0]; + } gMarioState->animList = &gMarioAnimsBuf; gMarioState->numCoins = 0; diff --git a/src/game/rumble_init.c b/src/game/rumble_init.c index e9145841..b164798e 100644 --- a/src/game/rumble_init.c +++ b/src/game/rumble_init.c @@ -232,7 +232,7 @@ static void thread6_rumble_loop(UNUSED void *arg) { sRumblePakActive = FALSE; } } else if (gNumVblanks % 60 == 0) { - sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; + sRumblePakActive = osMotorInitEx(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; sRumblePakErrorCount = 0; } @@ -243,7 +243,7 @@ static void thread6_rumble_loop(UNUSED void *arg) { } void cancel_rumble(void) { - sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; + sRumblePakActive = osMotorInitEx(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; if (sRumblePakActive) { osMotorStop(&gRumblePakPfs); diff --git a/src/goddard/renderer.c b/src/goddard/renderer.c index 8063d4c5..db94bd73 100644 --- a/src/goddard/renderer.c +++ b/src/goddard/renderer.c @@ -103,8 +103,8 @@ static struct ObjView *D_801BE994; // store if View flag 0x40 set u8 EUpad4[0x88]; #endif static OSContStatus D_801BAE60[4]; -static OSContPad sGdContPads[4]; // @ 801BAE70 -static OSContPad sPrevFrameCont[4]; // @ 801BAE88 +static OSContPadEx sGdContPads[4]; // @ 801BAE70 +static OSContPadEx sPrevFrameCont[4]; // @ 801BAE88 static u8 D_801BAEA0; static struct ObjGadget *sTimerGadgets[GD_NUM_TIMERS]; // @ 801BAEA8 static u32 D_801BAF28; // RAM addr offset? @@ -1287,12 +1287,12 @@ void gd_vblank(void) { /** * Copies the player1 controller data from p1cont to sGdContPads[0]. */ -void gd_copy_p1_contpad(OSContPad *p1cont) { +void gd_copy_p1_contpad(OSContPadEx *p1cont) { u32 i; // 24 u8 *src = (u8 *) p1cont; // 20 u8 *dest = (u8 *) &sGdContPads[0]; // 1c - for (i = 0; i < sizeof(OSContPad); i++) { + for (i = 0; i < sizeof(OSContPadEx); i++) { dest[i] = src[i]; } @@ -2397,8 +2397,8 @@ void start_view_dl(struct ObjView *view) { void parse_p1_controller(void) { u32 i; struct GdControl *gdctrl = &gGdCtrl; - OSContPad *currInputs; - OSContPad *prevInputs; + OSContPadEx *currInputs; + OSContPadEx *prevInputs; // Copy current inputs to previous u8 *src = (u8 *) gdctrl; @@ -2509,7 +2509,7 @@ void parse_p1_controller(void) { gdctrl->csrY = sScreenView->parent->upperLeft.y + sScreenView->parent->lowerRight.y - 32.0f; } - for (i = 0; i < sizeof(OSContPad); i++) { + for (i = 0; i < sizeof(OSContPadEx); i++) { ((u8 *) prevInputs)[i] = ((u8 *) currInputs)[i]; } } @@ -2800,15 +2800,15 @@ s32 setup_view_buffers(const char *name, struct ObjView *view, UNUSED s32 ulx, U /* 252AF8 -> 252BAC; orig name: _InitControllers */ void gd_init_controllers(void) { - OSContPad *p1cont = &sPrevFrameCont[0]; // 1c + OSContPadEx *p1cont = &sPrevFrameCont[0]; // 1c u32 i; // 18 osCreateMesgQueue(&D_801BE830, D_801BE848, ARRAY_COUNT(D_801BE848)); osSetEventMesg(OS_EVENT_SI, &D_801BE830, (OSMesg) OS_MESG_SI_COMPLETE); osContInit(&D_801BE830, &D_801BAEA0, D_801BAE60); - osContStartReadData(&D_801BE830); + osContStartReadDataEx(&D_801BE830); - for (i = 0; i < sizeof(OSContPad); i++) { + for (i = 0; i < sizeof(OSContPadEx); i++) { ((u8 *) p1cont)[i] = 0; } } diff --git a/src/goddard/renderer.h b/src/goddard/renderer.h index f4a990c3..1d62dd76 100644 --- a/src/goddard/renderer.h +++ b/src/goddard/renderer.h @@ -61,7 +61,7 @@ void gdm_init(void *blockpool, u32 size); void gdm_setup(void); void gdm_maketestdl(s32 id); void gd_vblank(void); -void gd_copy_p1_contpad(OSContPad *p1cont); +void gd_copy_p1_contpad(OSContPadEx *p1cont); s32 gd_sfx_to_play(void); Gfx *gdm_gettestdl(s32 id); void gd_draw_rect(f32 ulx, f32 uly, f32 lrx, f32 lry);