diff --git a/readme.txt b/readme.txt index 9ca9881..a52007d 100644 --- a/readme.txt +++ b/readme.txt @@ -18,6 +18,7 @@ Wii homebrew is WiiBrew (www.wiibrew.org). * Based on Snes9x 1.58 (with faster Blargg S-SMP module) * Wiimote, Nunchuk, Classic, Wii U Pro, and Gamecube controller support * Wii U GamePad support (requires homebrew injection into Wii U VC title) +* Retrode support for up to 4 players * SNES Superscope, Mouse, Justifier support * Cheat support * Satellaview (BS-X) support @@ -34,6 +35,10 @@ Wii homebrew is WiiBrew (www.wiibrew.org). | UPDATE HISTORY | •˜———–—––-- - —————————––––– ———–—––-- - —————————––––– ———–—––-- - ————————• +[4.4.4] + +* Added retrode support (thanks revvv!) + [4.4.3 - April 13, 2019] * Game-specific fixes (Chou Aniki, Rendering Rangers R2, Tenshi no Uta, Circuit USA) diff --git a/source/input.cpp b/source/input.cpp index fbfe0bf..ea0cb21 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -35,6 +35,10 @@ #include "snes9x/memmap.h" #include "snes9x/controls.h" +#ifdef HW_RVL +#include "utils/retrode.h" +#endif + #define ANALOG_SENSITIVITY 30 int rumbleRequest[4] = {0,0,0,0}; @@ -243,6 +247,7 @@ UpdatePads() { #ifdef HW_RVL WiiDRC_ScanPads(); + Retrode_ScanPads(); WPAD_ScanPads(); #endif @@ -442,6 +447,8 @@ static void decodepad (int chan) s16 wiidrc_ax = userInput[chan].wiidrcdata.stickX; s16 wiidrc_ay = userInput[chan].wiidrcdata.stickY; u32 wiidrcp = userInput[chan].wiidrcdata.btns_h; + + jp |= Retrode_ButtonsHeld(chan); #endif /*** diff --git a/source/utils/retrode.c b/source/utils/retrode.c new file mode 100644 index 0000000..d9ae6bf --- /dev/null +++ b/source/utils/retrode.c @@ -0,0 +1,122 @@ +#ifdef HW_RVL +#include + +static bool retrodeSetup = false; +static s32 deviceIdRetrode = 0; +static u8 endpointRetrode = 0; +static u8 bMaxPacketSizeRetrode = 0; + +static u32 jpRetrode[4]; + +static bool isRetrodeGamepad(usb_devdesc devdesc) +{ + if (devdesc.idVendor != 0x0403 || devdesc.idProduct != 0x97C1 || + devdesc.configurations == NULL || devdesc.configurations->interfaces == NULL || + devdesc.configurations->interfaces->endpoints == NULL) + { + return false; + } + return devdesc.configurations->interfaces->bInterfaceSubClass == 0; +} + +static u8 getEndpoint(usb_devdesc devdesc) +{ + if (devdesc.configurations == NULL || devdesc.configurations->interfaces == NULL || + devdesc.configurations->interfaces->endpoints == NULL) + { + return -1; + } + return devdesc.configurations->interfaces->endpoints->bEndpointAddress; +} + +static void openRetrode() +{ + usb_device_entry dev_entry[8]; + u8 dev_count; + if (USB_GetDeviceList(dev_entry, 8, USB_CLASS_HID, &dev_count) < 0) + { + return; + } + + // Retrode has two entries in USB_GetDeviceList(), one for gamepads and one for SNES mouse + for (int i = 0; i < dev_count; ++i) + { + s32 fd; + if (USB_OpenDevice(dev_entry[i].device_id, dev_entry[i].vid, dev_entry[i].pid, &fd) < 0) + { + continue; + } + + usb_devdesc devdesc; + if (USB_GetDescriptors(fd, &devdesc) < 0 || !isRetrodeGamepad(devdesc)) + { + USB_CloseDevice(&fd); + continue; + } + deviceIdRetrode = fd; + endpointRetrode = getEndpoint(devdesc); + bMaxPacketSizeRetrode = devdesc.bMaxPacketSize0; + } +} + +void Retrode_ScanPads() +{ + if(!retrodeSetup) + { + retrodeSetup = true; + openRetrode(); + } + + if (deviceIdRetrode == 0) + { + return; + } + + uint8_t ATTRIBUTE_ALIGN(32) buf[bMaxPacketSizeRetrode]; + + if (USB_ReadIntrMsg(deviceIdRetrode, endpointRetrode, sizeof(buf), buf) != 5) + { + return; + } + + // buf[0] contains the port returned + // you have to make 4 calls to get the status, even if you are only interested in one port + // because it is not sure which port is returned first + // 1 = left SNES + // 2 = right SNES + // 3 = left Genesis/MD + // 4 = right Genesis/MD + + // Retrode gamepad endpoint returns 5 bytes with gamepad events + u32 jp12 = 0; + jp12 |= ((buf[2] & 0x9C) == 0x9C) ? PAD_BUTTON_UP : 0; + jp12 |= ((buf[2] & 0x64) == 0x64) ? PAD_BUTTON_DOWN : 0; + jp12 |= ((buf[1] & 0x9C) == 0x9C) ? PAD_BUTTON_LEFT : 0; + jp12 |= ((buf[1] & 0x64) == 0x64) ? PAD_BUTTON_RIGHT : 0; + + jp12 |= (buf[3] & 0x10) ? PAD_BUTTON_A : 0; + jp12 |= (buf[3] & 0x01) ? PAD_BUTTON_B : 0; + jp12 |= (buf[3] & 0x20) ? PAD_BUTTON_X : 0; + jp12 |= (buf[3] & 0x02) ? PAD_BUTTON_Y : 0; + + jp12 |= (buf[3] & 0x40) ? PAD_TRIGGER_L : 0; + jp12 |= (buf[3] & 0x80) ? PAD_TRIGGER_R : 0; + + jp12 |= (buf[3] & 0x08) ? PAD_BUTTON_START : 0; + jp12 |= (buf[3] & 0x04) ? PAD_TRIGGER_Z : 0; // SNES select button maps to Z + + // Required, otherwise if the returned port isn't the one we are looking for, jp will be set to zero, + // and held buttons are not possible w/o saving the state. + jpRetrode[buf[0] - 1] = jp12; +} + +u32 Retrode_ButtonsHeld(int chan) +{ + if (deviceIdRetrode == 0) + { + return 0; + } + return jpRetrode[chan]; +} + +#endif diff --git a/source/utils/retrode.h b/source/utils/retrode.h new file mode 100644 index 0000000..8b069bf --- /dev/null +++ b/source/utils/retrode.h @@ -0,0 +1,15 @@ +#ifndef _RETRODE_H_ +#define _RETRODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +bool Retrode_ScanPads(); +u32 Retrode_ButtonsHeld(int chan); + +#ifdef __cplusplus +} +#endif + +#endif