Implemented native Gamecube controller support and corresponding emulator hint in ROM header (#408)

* Implemented native Gamecube controller support and corresponding emulator hint in ROM header

* Added X and Y buttons to VALID_BUTTONS in sm64.h

* Added workaround in controller type detection for mupen bug

* read GC controllers from port 2

* L to Z logic for GC controller uses a threshold

* Changed threshold for L trig to around 33%

Changed threshold based on feedback

* Added gGamecubeControllerPort

* fixed wrong check

Co-authored-by: Reonu <danileon95@gmail.com>
This commit is contained in:
Mr-Wiseguy
2022-09-21 22:30:36 -04:00
committed by GitHub
parent 6dbe379990
commit 4dfb8d9a64
15 changed files with 633 additions and 25 deletions

View File

@@ -15,9 +15,14 @@
.word 0x00000000 /* Unknown */ .word 0x00000000 /* Unknown */
.word 0x00000000 /* Unknown */ .word 0x00000000 /* Unknown */
.ascii INTERNAL_ROM_NAME /* Internal ROM name */ .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 */ .word 0x00000000 /* Unknown */
#endif
.word 0x0000004E /* Cartridge */ .word 0x0000004E /* Cartridge */
#if defined(EEP4K) #if defined(EEP4K) && !defined(USE_GAMECUBE_CONTROLLER)
.ascii "SM" /* Cartridge ID */ .ascii "SM" /* Cartridge ID */
#else #else
.ascii "ED" /* Cartridge ID */ .ascii "ED" /* Cartridge ID */

View File

@@ -28,3 +28,6 @@
*/ */
#define BORDER_HEIGHT_CONSOLE 0 #define BORDER_HEIGHT_CONSOLE 0
#define BORDER_HEIGHT_EMULATOR 0 #define BORDER_HEIGHT_EMULATOR 0
// Informs supported emulators to default to gamecube controller inputs
// #define USE_GAMECUBE_CONTROLLER

View File

@@ -63,6 +63,18 @@ typedef struct {
u8 error; u8 error;
} OSContPad; } 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 { typedef struct {
void *address; /* Ram pad Address: 11 bits */ void *address; /* Ram pad Address: 11 bits */
u8 databuffer[32]; /* address of the data buffer */ u8 databuffer[32]; /* address of the data buffer */
@@ -103,6 +115,7 @@ typedef struct {
#define CONT_ABSOLUTE 0x0001 #define CONT_ABSOLUTE 0x0001
#define CONT_RELATIVE 0x0002 #define CONT_RELATIVE 0x0002
#define CONT_JOYPORT 0x0004 #define CONT_JOYPORT 0x0004
#define CONT_GCN 0x0008
#define CONT_EEPROM 0x8000 #define CONT_EEPROM 0x8000
#define CONT_EEP16K 0x4000 #define CONT_EEP16K 0x4000
#define CONT_TYPE_MASK 0x1f07 #define CONT_TYPE_MASK 0x1f07
@@ -150,6 +163,25 @@ typedef struct {
#define L_CBUTTONS CONT_C #define L_CBUTTONS CONT_C
#define R_CBUTTONS CONT_F #define R_CBUTTONS CONT_F
#define D_CBUTTONS CONT_D #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 */ /* Controller error number */
@@ -162,6 +194,9 @@ typedef struct {
#define CONT_ERR_VOICE_WORD 14 #define CONT_ERR_VOICE_WORD 14
#define CONT_ERR_VOICE_NO_RESPONSE 15 #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) #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 osContReset( OSMesgQueue *mq, OSContStatus *status);
extern s32 osContStartQuery( OSMesgQueue *mq); extern s32 osContStartQuery( OSMesgQueue *mq);
extern s32 osContStartReadData(OSMesgQueue *mq); extern s32 osContStartReadData(OSMesgQueue *mq);
extern s32 osContStartReadDataEx(OSMesgQueue *mq);
#ifndef _HW_VERSION_1 #ifndef _HW_VERSION_1
extern s32 osContSetCh(u8 ch); extern s32 osContSetCh(u8 ch);
#endif #endif
extern void osContGetQuery(OSContStatus *status); extern void osContGetQuery(OSContStatus *status);
extern void osContGetReadData(OSContPad *pad); extern void osContGetReadData(OSContPad *pad);
extern void osContGetReadDataEx(OSContPadEx *pad);
#endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ #endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */

View File

@@ -62,12 +62,14 @@ extern "C" {
/* Rumble PAK interface */ /* Rumble PAK interface */
extern s32 osMotorInit(OSMesgQueue *mq, OSPfs *pfs, int controller_no); extern s32 osMotorInit(OSMesgQueue *mq, OSPfs *pfs, int controller_no);
extern s32 osMotorInitEx(OSMesgQueue *mq, OSPfs *pfs, int controller_no);
#if 1 #if 1
#define MOTOR_START 1 #define MOTOR_START 1
#define MOTOR_STOP 0 #define MOTOR_STOP 0
#define osMotorStart(x) __osMotorAccess((x), MOTOR_START) #define osMotorStart(x) __osMotorAccessEx((x), MOTOR_START)
#define osMotorStop(x) __osMotorAccess((x), MOTOR_STOP) #define osMotorStop(x) __osMotorAccessEx((x), MOTOR_STOP)
extern s32 __osMotorAccess(OSPfs *pfs, s32 flag); extern s32 __osMotorAccess(OSPfs *pfs, s32 flag);
extern s32 __osMotorAccessEx(OSPfs *pfs, s32 flag);
#else #else
extern s32 osMotorStop( OSPfs *pfs); extern s32 osMotorStop( OSPfs *pfs);
extern s32 osMotorStart(OSPfs *pfs); extern s32 osMotorStart(OSPfs *pfs);

View File

@@ -717,7 +717,8 @@ enum MarioActionFlags {
#define VALID_BUTTONS (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | \ #define VALID_BUTTONS (A_BUTTON | B_BUTTON | Z_TRIG | START_BUTTON | \
U_JPAD | D_JPAD | L_JPAD | R_JPAD | \ U_JPAD | D_JPAD | L_JPAD | R_JPAD | \
L_TRIG | R_TRIG | \ 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 ) #define C_BUTTONS (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS )

View File

@@ -42,7 +42,7 @@ struct Controller {
/*0x12*/ u16 buttonPressed; /*0x12*/ u16 buttonPressed;
/*0x14*/ u16 buttonReleased; /*0x14*/ u16 buttonReleased;
/*0x18*/ OSContStatus *statusData; /*0x18*/ OSContStatus *statusData;
/*0x1C*/ OSContPad *controllerData; /*0x1C*/ OSContPadEx *controllerData;
#if ENABLE_RUMBLE #if ENABLE_RUMBLE
/*0x20*/ s32 port; /*0x20*/ s32 port;
#endif #endif

View File

@@ -184,6 +184,8 @@ SECTIONS
lib/PR/audio/n_aspMain.o(.text*); lib/PR/audio/n_aspMain.o(.text*);
lib/PR/hvqm/hvqm2sp1.o(.text*); lib/PR/hvqm/hvqm2sp1.o(.text*);
_mainSegmentTextEnd = .; _mainSegmentTextEnd = .;
/* Overwrite a libultra function with its modified counterpart for GCN controller support */
__osContGetInitData = __osContGetInitDataEx;
/* data */ /* data */
BUILD_DIR/asm/n64_assert.o(.*data*); BUILD_DIR/asm/n64_assert.o(.*data*);

View File

@@ -425,7 +425,7 @@ void thread2_crash_screen(UNUSED void *arg) {
#if ENABLE_RUMBLE #if ENABLE_RUMBLE
block_until_rumble_pak_free(); block_until_rumble_pak_free();
#endif #endif
osContStartReadData(&gSIEventMesgQueue); osContStartReadDataEx(&gSIEventMesgQueue);
} }
read_controller_inputs(THREAD_2_CRASH_SCREEN); read_controller_inputs(THREAD_2_CRASH_SCREEN);
draw_crash_screen(thread); draw_crash_screen(thread);

View File

@@ -43,8 +43,9 @@ struct GfxPool *gGfxPool;
// OS Controllers // OS Controllers
OSContStatus gControllerStatuses[4]; OSContStatus gControllerStatuses[4];
OSContPad gControllerPads[4]; OSContPadEx gControllerPads[4];
u8 gControllerBits; 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 gIsConsole = TRUE; // Needs to be initialized before audio_reset_session is called
u8 gCacheEmulated = TRUE; u8 gCacheEmulated = TRUE;
u8 gBorderHeight; u8 gBorderHeight;
@@ -602,7 +603,7 @@ void read_controller_inputs(s32 threadID) {
if (threadID == THREAD_5_GAME_LOOP) { if (threadID == THREAD_5_GAME_LOOP) {
osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK); osRecvMesg(&gSIEventMesgQueue, &gMainReceivedMesg, OS_MESG_BLOCK);
} }
osContGetReadData(&gControllerPads[0]); osContGetReadDataEx(&gControllerPads[0]);
#if ENABLE_RUMBLE #if ENABLE_RUMBLE
release_rumble_pak_control(); release_rumble_pak_control();
#endif #endif
@@ -613,7 +614,18 @@ void read_controller_inputs(s32 threadID) {
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
struct Controller *controller = &gControllers[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 we're receiving inputs, update the controller struct with the new button info.
if (controller->controllerData != NULL) { if (controller->controllerData != NULL) {
controller->rawStickX = controller->controllerData->stick_x; controller->rawStickX = controller->controllerData->stick_x;
@@ -690,6 +702,15 @@ void init_controllers(void) {
gControllers[cont++].controllerData = &gControllerPads[port]; 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 // Game thread core
@@ -769,7 +790,7 @@ void thread5_game_loop(UNUSED void *arg) {
#if ENABLE_RUMBLE #if ENABLE_RUMBLE
block_until_rumble_pak_free(); block_until_rumble_pak_free();
#endif #endif
osContStartReadData(&gSIEventMesgQueue); osContStartReadDataEx(&gSIEventMesgQueue);
} }
audio_game_loop_tick(); audio_game_loop_tick();

View File

@@ -32,7 +32,7 @@ enum ZBmodes {
extern struct Controller gControllers[3]; extern struct Controller gControllers[3];
extern OSContStatus gControllerStatuses[4]; extern OSContStatus gControllerStatuses[4];
extern OSContPad gControllerPads[4]; extern OSContPadEx gControllerPads[4];
extern OSMesgQueue gGameVblankQueue; extern OSMesgQueue gGameVblankQueue;
extern OSMesgQueue gGfxVblankQueue; extern OSMesgQueue gGfxVblankQueue;
extern OSMesg gGameMesgBuf[1]; extern OSMesg gGameMesgBuf[1];
@@ -47,6 +47,7 @@ extern Gfx *gDisplayListHead;
extern u8 *gGfxPoolEnd; extern u8 *gGfxPoolEnd;
extern struct GfxPool *gGfxPool; extern struct GfxPool *gGfxPool;
extern u8 gControllerBits; extern u8 gControllerBits;
extern s8 gGamecubeControllerPort;
extern u8 gIsConsole; extern u8 gIsConsole;
extern u8 gCacheEmulated; extern u8 gCacheEmulated;
extern u8 gBorderHeight; extern u8 gBorderHeight;

File diff suppressed because it is too large Load Diff

View File

@@ -1865,7 +1865,11 @@ void init_mario_from_save_file(void) {
gMarioState->spawnInfo = &gPlayerSpawnInfos[0]; gMarioState->spawnInfo = &gPlayerSpawnInfos[0];
gMarioState->statusForCamera = &gPlayerCameraState[0]; gMarioState->statusForCamera = &gPlayerCameraState[0];
gMarioState->marioBodyState = &gBodyStates[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->animList = &gMarioAnimsBuf;
gMarioState->numCoins = 0; gMarioState->numCoins = 0;

View File

@@ -232,7 +232,7 @@ static void thread6_rumble_loop(UNUSED void *arg) {
sRumblePakActive = FALSE; sRumblePakActive = FALSE;
} }
} else if (gNumVblanks % 60 == 0) { } else if (gNumVblanks % 60 == 0) {
sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; sRumblePakActive = osMotorInitEx(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1;
sRumblePakErrorCount = 0; sRumblePakErrorCount = 0;
} }
@@ -243,7 +243,7 @@ static void thread6_rumble_loop(UNUSED void *arg) {
} }
void cancel_rumble(void) { void cancel_rumble(void) {
sRumblePakActive = osMotorInit(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1; sRumblePakActive = osMotorInitEx(&gSIEventMesgQueue, &gRumblePakPfs, gPlayer1Controller->port) < 1;
if (sRumblePakActive) { if (sRumblePakActive) {
osMotorStop(&gRumblePakPfs); osMotorStop(&gRumblePakPfs);

View File

@@ -103,8 +103,8 @@ static struct ObjView *D_801BE994; // store if View flag 0x40 set
u8 EUpad4[0x88]; u8 EUpad4[0x88];
#endif #endif
static OSContStatus D_801BAE60[4]; static OSContStatus D_801BAE60[4];
static OSContPad sGdContPads[4]; // @ 801BAE70 static OSContPadEx sGdContPads[4]; // @ 801BAE70
static OSContPad sPrevFrameCont[4]; // @ 801BAE88 static OSContPadEx sPrevFrameCont[4]; // @ 801BAE88
static u8 D_801BAEA0; static u8 D_801BAEA0;
static struct ObjGadget *sTimerGadgets[GD_NUM_TIMERS]; // @ 801BAEA8 static struct ObjGadget *sTimerGadgets[GD_NUM_TIMERS]; // @ 801BAEA8
static u32 D_801BAF28; // RAM addr offset? 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]. * 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 u32 i; // 24
u8 *src = (u8 *) p1cont; // 20 u8 *src = (u8 *) p1cont; // 20
u8 *dest = (u8 *) &sGdContPads[0]; // 1c u8 *dest = (u8 *) &sGdContPads[0]; // 1c
for (i = 0; i < sizeof(OSContPad); i++) { for (i = 0; i < sizeof(OSContPadEx); i++) {
dest[i] = src[i]; dest[i] = src[i];
} }
@@ -2397,8 +2397,8 @@ void start_view_dl(struct ObjView *view) {
void parse_p1_controller(void) { void parse_p1_controller(void) {
u32 i; u32 i;
struct GdControl *gdctrl = &gGdCtrl; struct GdControl *gdctrl = &gGdCtrl;
OSContPad *currInputs; OSContPadEx *currInputs;
OSContPad *prevInputs; OSContPadEx *prevInputs;
// Copy current inputs to previous // Copy current inputs to previous
u8 *src = (u8 *) gdctrl; 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; 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]; ((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 */ /* 252AF8 -> 252BAC; orig name: _InitControllers */
void gd_init_controllers(void) { void gd_init_controllers(void) {
OSContPad *p1cont = &sPrevFrameCont[0]; // 1c OSContPadEx *p1cont = &sPrevFrameCont[0]; // 1c
u32 i; // 18 u32 i; // 18
osCreateMesgQueue(&D_801BE830, D_801BE848, ARRAY_COUNT(D_801BE848)); osCreateMesgQueue(&D_801BE830, D_801BE848, ARRAY_COUNT(D_801BE848));
osSetEventMesg(OS_EVENT_SI, &D_801BE830, (OSMesg) OS_MESG_SI_COMPLETE); osSetEventMesg(OS_EVENT_SI, &D_801BE830, (OSMesg) OS_MESG_SI_COMPLETE);
osContInit(&D_801BE830, &D_801BAEA0, D_801BAE60); 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; ((u8 *) p1cont)[i] = 0;
} }
} }

View File

@@ -61,7 +61,7 @@ void gdm_init(void *blockpool, u32 size);
void gdm_setup(void); void gdm_setup(void);
void gdm_maketestdl(s32 id); void gdm_maketestdl(s32 id);
void gd_vblank(void); void gd_vblank(void);
void gd_copy_p1_contpad(OSContPad *p1cont); void gd_copy_p1_contpad(OSContPadEx *p1cont);
s32 gd_sfx_to_play(void); s32 gd_sfx_to_play(void);
Gfx *gdm_gettestdl(s32 id); Gfx *gdm_gettestdl(s32 id);
void gd_draw_rect(f32 ulx, f32 uly, f32 lrx, f32 lry); void gd_draw_rect(f32 ulx, f32 uly, f32 lrx, f32 lry);