diff --git a/port/include/input.h b/port/include/input.h index 6171c26961..ca5a3b8138 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -121,6 +121,12 @@ enum mouselockmode { MLOCK_AUTO = 2 }; +enum japaneseButtonLayout { + JAPANESE_LAYOUT_AUTO = 0, // auto-detect based on controller type + JAPANESE_LAYOUT_OFF = 1, // use standard button layout + JAPANESE_LAYOUT_ON = 2 // use Japanese button layout +}; + // returns bitmask of connected controllers or -1 if failed s32 inputInit(void); @@ -181,6 +187,12 @@ s32 inputKeyJustPressed(u32 vk); // idx is controller index, contbtn is one of the CONT_ constants s32 inputButtonPressed(s32 idx, u32 contbtn); +// Remaps UI button for a controller (handles Japanese layout) +u32 inputConfirmCancelButtonSwap(int cidx, u32 button); + +// Returns 1 if the given controller index is a Nintendo Switch controller, 0 otherwise +int inputControllerIsNintendoSwitch(int cidx); + // bind virtkey vk to n64 pad #idx's button/axis ck as represented by its contkey value // if bind is -1, picks a bind slot automatically void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk); @@ -270,4 +282,4 @@ const char *inputGetClipboard(void); // returns keymod values u32 inputGetKeyModState(void); -#endif +#endif \ No newline at end of file diff --git a/port/src/input.c b/port/src/input.c index 41e0fa0387..29050f1d74 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -11,6 +11,7 @@ #include "utils.h" #include "system.h" #include "fs.h" +#include "constants.h" #if !SDL_VERSION_ATLEAST(2, 0, 14) // this was added in 2.0.14 @@ -46,6 +47,7 @@ static SDL_GameController *pads[INPUT_MAX_CONTROLLERS]; .swapSticks = 1, \ .deviceIndex = -1, \ .cancelCButtons = 0, \ + .japaneseButtonLayout = 0, \ } static struct controllercfg { @@ -58,6 +60,7 @@ static struct controllercfg { s32 swapSticks; s32 deviceIndex; s32 cancelCButtons; + s32 japaneseButtonLayout; } padsCfg[INPUT_MAX_CONTROLLERS] = { CONTROLLERCFG_DEFAULT, CONTROLLERCFG_DEFAULT, @@ -67,6 +70,7 @@ static struct controllercfg { static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][INPUT_MAX_BINDS]; static char bindStrs[MAXCONTROLLERS][CK_TOTAL_COUNT][MAX_BIND_STR]; +static int inputIsNintendoSwitchController(SDL_GameController *controller); static s32 fakeControllers = 0; static s32 firstController = 0; @@ -346,6 +350,23 @@ static inline void inputInitController(const s32 cidx, const s32 jidx) SDL_JoystickGetGUIDString(guid, guidStr, sizeof(guidStr)); sysLogPrintf(LOG_NOTE, "input: GUID for controller %d: %s", jidx, guidStr); } + + // Set default key binds for this controller + inputSetDefaultKeyBinds(cidx, 0); +} + +u32 inputConfirmCancelButtonSwap(int cidx, u32 button) { + int japaneseActive = 0; + if (padsCfg[cidx].japaneseButtonLayout == JAPANESE_LAYOUT_ON) { + japaneseActive = 1; + } else if (padsCfg[cidx].japaneseButtonLayout == JAPANESE_LAYOUT_AUTO) { + japaneseActive = pads[cidx] && inputIsNintendoSwitchController(pads[cidx]); + } + if (japaneseActive) { + if (button == BUTTON_UI_ACCEPT) return BUTTON_UI_CANCEL; + if (button == BUTTON_UI_CANCEL) return BUTTON_UI_ACCEPT; + } + return button; } static inline void inputCloseController(const s32 cidx) @@ -748,11 +769,62 @@ s32 inputInit(void) return connectedMask; } +static int inputIsNintendoSwitchController(SDL_GameController *controller) { +#if defined(SDL_VERSION_ATLEAST) +#if SDL_VERSION_ATLEAST(2, 0, 12) + SDL_GameControllerType type = SDL_GameControllerGetType(controller); + if ( +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) + type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO +#else + 0 +#endif +#if SDL_VERSION_ATLEAST(2, 24, 0) +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) + || type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT +#endif +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) + || type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT +#endif +#if defined(SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR) + || type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR +#endif +#endif // SDL_VERSION_ATLEAST(2, 24, 0) + ) { + return 1; + } +#endif // SDL_VERSION_ATLEAST(2, 0, 12) +#endif // defined(SDL_VERSION_ATLEAST) +#ifdef SDL_JOYSTICK_VENDOR_NINTENDO + SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller); + if (joy && SDL_JoystickGetVendor(joy) == SDL_JOYSTICK_VENDOR_NINTENDO) { + return 1; + } +#else + // Check for Nintendo controllers by vendor ID (includes Nintendo Switch, Wii, etc.) + SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller); + if (joy && SDL_JoystickGetVendor(joy) == 0x057e) { + return 1; + } +#endif + return 0; +} + +int inputControllerIsNintendoSwitch(int cidx) { + return (cidx >= 0 && cidx < INPUT_MAX_CONTROLLERS && pads[cidx]) + ? inputIsNintendoSwitchController(pads[cidx]) : 0; +} + static inline s32 inputBindPressed(const s32 idx, const u32 ck) { + u32 swapped_ck = ck; + if (inputConfirmCancelButtonSwap(idx, ck)) { + if (ck == CK_A) swapped_ck = CK_B; + else if (ck == CK_B) swapped_ck = CK_A; + } for (s32 i = 0; i < INPUT_MAX_BINDS; ++i) { - if (binds[idx][ck][i]) { - if (inputKeyPressed(binds[idx][ck][i])) { + if (binds[idx][swapped_ck][i]) { + if (inputKeyPressed(binds[idx][swapped_ck][i])) { return 1; } } @@ -1532,6 +1604,7 @@ PD_CONSTRUCTOR static void inputConfigInit(void) configRegisterInt(strFmt("%s.CancelCButtons", secname), &padsCfg[c].cancelCButtons, 0, 1); configRegisterInt(strFmt("%s.SwapSticks", secname), &padsCfg[c].swapSticks, 0, 1); configRegisterInt(strFmt("%s.ControllerIndex", secname), &padsCfg[c].deviceIndex, -1, 0x7FFFFFFF); + configRegisterInt(strFmt("%s.JapaneseButtonLayout", secname), &padsCfg[c].japaneseButtonLayout, 0, 2); secname[13] = '.'; for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 5632153346..e6f962691e 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -1742,8 +1742,31 @@ static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *i } const s32 key = inputGetLastKey(); - if (key && key != VK_ESCAPE) { - inputKeyBind(g_ExtMenuPlayer, g_BindContKey, g_BindIndex, (key == VK_DELETE ? 0 : key)); + if (key && key != VK_DELETE && key != VK_ESCAPE) { + s32 adjustedKey = key; + + // Handle Nintendo Switch controller button swapping for Japanese layout + if (inputControllerIsNintendoSwitch(g_ExtMenuPlayer) && + inputConfirmCancelButtonSwap(g_ExtMenuPlayer, key) && + key >= VK_JOY_BEGIN && key < VK_TOTAL_COUNT) { + + s32 keyControllerIdx = (key - VK_JOY_BEGIN) / INPUT_MAX_CONTROLLER_BUTTONS; + if (keyControllerIdx == g_ExtMenuPlayer) { + s32 buttonInController = (key - VK_JOY_BEGIN) % INPUT_MAX_CONTROLLER_BUTTONS; + + // Swap A and B buttons for Japanese layout + if (buttonInController == CK_A) { + adjustedKey = key + (CK_B - CK_A); + } else if (buttonInController == CK_B) { + adjustedKey = key + (CK_A - CK_B); + } + } + } + + inputKeyBind(g_ExtMenuPlayer, g_BindContKey, g_BindIndex, adjustedKey); + menuPopDialog(); + } else if (key == VK_DELETE) { + inputKeyBind(g_ExtMenuPlayer, g_BindContKey, g_BindIndex, 0); menuPopDialog(); } @@ -1752,9 +1775,12 @@ static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *i static const char *menutextBind(struct menuitem *item) { - return g_PlayerExtCfg[g_ExtMenuPlayer].extcontrols ? - menuBinds[item - g_ExtendedBindsMenuItems].name : - menuBinds[item - g_ExtendedBindsMenuItems].n64name; + int idx = item - g_ExtendedBindsMenuItems; + u32 ck = menuBinds[idx].ck; + + return g_PlayerExtCfg[g_ExtMenuPlayer].extcontrols ? + menuBinds[idx].name : + menuBinds[idx].n64name; } static MenuItemHandlerResult menuhandlerBind(s32 operation, struct menuitem *item, union handlerdata *data) @@ -1922,4 +1948,4 @@ struct menudialogdef g_ExtendedMenuDialog = { NULL, MENUDIALOGFLAG_LITERAL_TEXT, NULL, -}; +}; \ No newline at end of file diff --git a/src/game/menu.c b/src/game/menu.c index c9f4cca3a6..06cf69631d 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -4830,14 +4830,16 @@ void menuProcessInput(void) } #ifndef PLATFORM_N64 - // separate buttons for UI accept/cancel - if (buttonsnow & BUTTON_UI_ACCEPT) { - inputs.select = 1; - } - - if (buttonsnow & BUTTON_UI_CANCEL) { - inputs.back = 1; - } + // Use remapped UI buttons for accept/cancel (handles Japanese layout) + u32 acceptBtn = inputConfirmCancelButtonSwap(contpadnums[i], BUTTON_UI_ACCEPT); + u32 cancelBtn = inputConfirmCancelButtonSwap(contpadnums[i], BUTTON_UI_CANCEL); + + if (buttonsnow & acceptBtn) { + inputs.select = 1; + } + if (buttonsnow & cancelBtn) { + inputs.back = 1; + } #endif if (buttonsnow & B_BUTTON) {