Add support for NS2 GameCube controller
Filing this now in case someone else is looking at this...
Got the hardware today, not sure if I'll get to it this weekend. For all I know NS2 devices are a whole different type of device, so I'm not sure if this will be an easy add or a whole new driver file.
Did a quick "does it respond" check, and it does not! Neither k_eSwitchProprietaryCommandIDs_Status nor ReadInput succeed in USB mode, and I'm not able to connect via Bluetooth at the moment (attempts to pair result in AuthenticationFailed, connects succeed but shut down a few seconds later).
Like other Switch 2 devices it also has a C button (and it has a ZL button to mirror the original Z button, finally!) so even if the protocol was the same we'd have to make new device types and bindings for the new stuff.
Found my way here after many hours trying and failing to get my Switch 2 Pro Controller to connect to Windows. Absolutely nothing I tried was able to read inputs off it, but I wasn't doing anything low-level enough to inspect how the controller is actually sending data over the wire. I'm hopeful it's possible to add support for these devices!
For Wireshark enjoyers, someone published a pcapng:
https://docs.handheldlegend.com/s/link-zone
per later in the thread, that bit is misinfo, thankfully
looks like USB at least is much more straightforward than Switch 1 https://handheldlegend.github.io/procon2tool/
tool works for at least the SwPro 2 and the NSO GC pad
looks like the only critical things it's really missing are the analog triggers and the rumble, plus the issues with reconnecting. hopefully they can find all that soon
per later in the thread, that bit is misinfo, thankfully looks like USB at least is much more straightforward than Switch 1 https://handheldlegend.github.io/procon2tool/ tool works for at least the SwPro 2 and the NSO GC pad
looks like the only critical things it's really missing are the analog triggers and the rumble, plus the issues with reconnecting. hopefully they can find all that soon
Gyro as well
Based on what I know so far, S2 GCN Controllers uses a bthleenum.inf (Bluetooth Low Energy) Protocol. So far, I cannot find a looparound with this, so I have been using procon2tool Tool on Chrome to get the Controller to function.
Issues. Gates do not reach all of the Range. Analog Triggers do not function. Rumble does not function.
Based on what I know so far, S2 GCN Controllers uses a bthleenum.inf (Bluetooth Low Energy) Protocol. So far, I cannot find a looparound with this, so I have been using procon2tool Tool on Chrome to get the Controller to function.
Issues. Gates do not reach all of the Range. Analog Triggers do not function. Rumble does not function.
the gates issue is a real pain with things like steam, but something like Dolphin where the sticks and deadzones can be configured can at least be worked around
Controller works well in NS emulators, though of course no rumble and no gyro as well as very limited range of motion when using both the C stick and the left stick.
looks like USB at least is much more straightforward than Switch 1 https://handheldlegend.github.io/procon2tool/ tool works for at least the SwPro 2 and the NSO GC pad
https://github.com/mdqinc/SDL_GameControllerDB/commit/79b8ea1035256740c22fb1686fa0c77d201fe45f mappings while we wait for a more complete SDL HID impl
limited range of motion when using both the C stick and the left stick
seems to be missing axes calibration or something, range for the NSO GC thumbsticks on Mac and Linux are especially weird
Hey, I don't know if this helps in any way, shape, or form, but I got some readings on the analog triggers using this tool https://handheldlegend.github.io/procon2tool/ and hidapi and some C code. I am no expert and only did some tinkering because I just want to use my controller, so if it is useful for someone, great, and if not, also ok, just wanted to get it out there.
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <string.h>
#include <hidapi/hidapi.h>
#define VENDOR_ID 0x057e // Nintendo
#define PRODUCT_ID 0x2073 // NSO GameCube Controller
// Button-Definition for Byte 3, 4, 5
typedef struct {
int byte; // Byte in report
unsigned char mask;
const char *name;
} ButtonInfo;
const ButtonInfo buttons[] = {
{3, 0x01, "B"},
{3, 0x02, "A"},
{3, 0x04, "Y"},
{3, 0x08, "X"},
{3, 0x10, "R"},
{3, 0x20, "Z"},
{3, 0x40, "Start/Pause"},
{4, 0x01, "Dpad Down"},
{4, 0x02, "Dpad Right"},
{4, 0x04, "Dpad Left"},
{4, 0x08, "Dpad Up"},
{4, 0x10, "L"},
{4, 0x20, "ZL"},
{5, 0x01, "Home"},
{5, 0x02, "Capture"},
{5, 0x04, "GR"},
{5, 0x08, "GL"},
{5, 0x10, "Chat"},
};
#define NUM_BUTTONS (sizeof(buttons)/sizeof(ButtonInfo))
int main() {
int res;
unsigned char buf[65]; // Standard HID buffer (1 byte report ID + 64 data)
hid_device *handle;
// Initialize the hidapi library
res = hid_init();
if (res < 0) {
fprintf(stderr, "hid_init failed\n");
return 1;
}
// Open the device using the Vendor ID and Product ID
handle = hid_open(VENDOR_ID, PRODUCT_ID, NULL);
if (!handle) {
fprintf(stderr, "Unable to open device (NSO GC Controller)\n");
return 1;
}
printf("Reading HID reports from NSO GameCube Controller...\n\n");
while (1) {
res = hid_read(handle, buf, sizeof(buf));
if (res > 14) {
unsigned char left_trigger = buf[13];
unsigned char right_trigger = buf[14];
// Button-Zustand auslesen
printf("L:%3d | R:%3d | Buttons: ", left_trigger, right_trigger);
int first = 1;
for (size_t i = 0; i < NUM_BUTTONS; ++i) {
if (buf[buttons[i].byte] & buttons[i].mask) {
if (!first) printf(", ");
printf("%s", buttons[i].name);
first = 0;
}
}
if (first) printf("none");
printf(" \r");
fflush(stdout);
}
}
hid_close(handle);
hid_exit();
return 0;
}
Source to the web tool, which is probably the best source of info for direct interaction with the device(s) https://github.com/HandHeldLegend/handheldlegend.github.io/tree/master/procon2tool
The flibitOffice now has development hardware and SDKs for Switch 2 so I probably won't be able to contribute beyond reviewing someone else's PRs, out of abundance of caution - still eager to see this on PC and will help where I can!
https://github.com/libsdl-org/SDL/issues/13178#issuecomment-2974677818 How do you use this? I can't seem to build a project or anything that will get it to work.
Hey, I don't know if this helps in any way, shape, or form, but I got some readings on the analog triggers using this tool https://handheldlegend.github.io/procon2tool/ and hidapi and some C code. I am no expert and only did some tinkering because I just want to use my controller, so if it is useful for someone, great, and if not, also ok, just wanted to get it out there. #include <stdio.h> #include <stdlib.h> #include <wchar.h> #include <string.h> #include <hidapi/hidapi.h> #define VENDOR_ID 0x057e // Nintendo #define PRODUCT_ID 0x2073 // NSO GameCube Controller // Button-Definition for Byte 3, 4, 5 typedef struct { int byte; // Byte in report unsigned char mask; const char *name; } ButtonInfo; const ButtonInfo buttons[] = { {3, 0x01, "B"}, {3, 0x02, "A"}, {3, 0x04, "Y"}, {3, 0x08, "X"}, {3, 0x10, "R"}, {3, 0x20, "Z"}, {3, 0x40, "Start/Pause"}, {4, 0x01, "Dpad Down"}, {4, 0x02, "Dpad Right"}, {4, 0x04, "Dpad Left"}, {4, 0x08, "Dpad Up"}, {4, 0x10, "L"}, {4, 0x20, "ZL"}, {5, 0x01, "Home"}, {5, 0x02, "Capture"}, {5, 0x04, "GR"}, {5, 0x08, "GL"}, {5, 0x10, "Chat"}, }; #define NUM_BUTTONS (sizeof(buttons)/sizeof(ButtonInfo)) int main() { int res; unsigned char buf[65]; // Standard HID buffer (1 byte report ID + 64 data) hid_device *handle;
// Initialize the hidapi library res = hid_init(); if (res < 0) { fprintf(stderr, "hid_init failed\n"); return 1; } // Open the device using the Vendor ID and Product ID handle = hid_open(VENDOR_ID, PRODUCT_ID, NULL); if (!handle) { fprintf(stderr, "Unable to open device (NSO GC Controller)\n"); return 1; } printf("Reading HID reports from NSO GameCube Controller...\n\n"); while (1) { res = hid_read(handle, buf, sizeof(buf)); if (res > 14) { unsigned char left_trigger = buf[13]; unsigned char right_trigger = buf[14]; // Button-Zustand auslesen printf("L:%3d | R:%3d | Buttons: ", left_trigger, right_trigger); int first = 1; for (size_t i = 0; i < NUM_BUTTONS; ++i) { if (buf[buttons[i].byte] & buttons[i].mask) { if (!first) printf(", "); printf("%s", buttons[i].name); first = 0; } } if (first) printf("none"); printf(" \r"); fflush(stdout); } } hid_close(handle); hid_exit(); return 0;}
How do you use this? I can't seem to build a project or anything that will get it to work.
Well, I use it on my Mac on Terminal with clang and I installed hidapi via brew, so I create a .c file, compile it, and before running it, open the website I mentioned: https://handheldlegend.github.io/procon2tool/ Connect the controller there, and then run the C code, and now you should get some numbers.
P.S. Output should look something like this: L: 30 | R:170 | Buttons: A
@Nohzockt Thanks, I was just building the project wrong. Have you been able to get this to work in Dolphin? I can't quite seem to figure out what to put for the binding.
@Nohzockt Thanks, I was just building the project wrong. Have you been able to get this to work in Dolphin? I can't quite seem to figure out what to put for the binding.
Well, no, not really. I had an idea to work around it on Windows using VIGEM and hidapi, combined with the website. However, I’m not sure if it would even work, and I’m terrible with Visual Studio, so I didn’t try it. Currently, my code doesn’t allow me to use the analog triggers in Dolphin; you’d have to make another program, as I mentioned before.
Since the tool is using WebUSB would it make sense to write the initial driver as a libusb port of procon2tool? This would end up being similar to the Wii U GameCube adapter support which looks like a hid device but needed direct access to actually get working I/O. It's not the ideal scenario but would at least be a starting point for support without the use of a web browser.
Not sure if this is exactly what you meant, but this is the code for initial initialization with libusb, copying what the procon2tool did with WebUSB. Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
#include <unistd.h>
#define VENDOR_ID 0x057E
const uint16_t PRODUCT_IDS[] = {0x2066, 0x2067, 0x2069, 0x2073};
#define NUM_PRODUCT_IDS (sizeof(PRODUCT_IDS)/sizeof(PRODUCT_IDS[0]))
#define INTERFACE_NUM 1
const unsigned char DEFAULT_REPORT_DATA[] = {
0x03, 0x91, 0x00, 0x0d, 0x00, 0x08,
0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const unsigned char SET_LED_DATA[] = {
0x09, 0x91, 0x00, 0x07, 0x00, 0x08,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int find_bulk_out_endpoint(libusb_device_handle* handle, uint8_t* endpoint_out) {
libusb_device* dev = libusb_get_device(handle);
struct libusb_config_descriptor* config;
if (libusb_get_config_descriptor(dev, 0, &config) != 0)
return -1;
for (int i = 0; i < config->bNumInterfaces; i++) {
const struct libusb_interface* iface = &config->interface[i];
for (int j = 0; j < iface->num_altsetting; j++) {
const struct libusb_interface_descriptor* altsetting = &iface->altsetting[j];
if (altsetting->bInterfaceNumber == INTERFACE_NUM) {
for (int k = 0; k < altsetting->bNumEndpoints; k++) {
const struct libusb_endpoint_descriptor* ep = &altsetting->endpoint[k];
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK &&
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
*endpoint_out = ep->bEndpointAddress;
libusb_free_config_descriptor(config);
return 0;
}
}
}
}
}
libusb_free_config_descriptor(config);
return -1;
}
int main() {
libusb_context* ctx = NULL;
libusb_device_handle* handle = NULL;
libusb_init(&ctx);
printf("ProCon2 Enabler: Wait for supported device ...\n");
while (1) {
for (int i = 0; i < NUM_PRODUCT_IDS; i++) {
handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_IDS[i]);
if (handle) {
printf("Device found (PID: 0x%04X).\n", PRODUCT_IDS[i]);
break;
}
}
if (!handle) {
usleep(500 * 1000);
continue;
}
if (libusb_claim_interface(handle, INTERFACE_NUM) != 0) {
printf("Interface %d could not be claimed!\n", INTERFACE_NUM);
libusb_close(handle);
handle = NULL;
usleep(1000 * 1000);
continue;
}
printf("Interface %d successfully claimed.\n", INTERFACE_NUM);
uint8_t endpoint_out = 0;
if (find_bulk_out_endpoint(handle, &endpoint_out) != 0) {
printf("No Bulk OUT Endpoint found!\n");
libusb_release_interface(handle, INTERFACE_NUM);
libusb_close(handle);
handle = NULL;
usleep(1000 * 1000);
continue;
}
printf("Bulk OUT Endpoint: 0x%02X\n", endpoint_out);
int transferred;
int rc = libusb_bulk_transfer(handle, endpoint_out, (unsigned char*)DEFAULT_REPORT_DATA,
sizeof(DEFAULT_REPORT_DATA), &transferred, 1000);
if (rc == 0) {
printf("Default report sent (%d Bytes).\n", transferred);
} else {
printf("Error sending the default report: %s\n", libusb_error_name(rc));
}
rc = libusb_bulk_transfer(handle, endpoint_out, (unsigned char*)SET_LED_DATA,
sizeof(SET_LED_DATA), &transferred, 1000);
if (rc == 0) {
printf("LED report sent (%d Bytes).\n", transferred);
} else {
printf("Error sending the LED report: %s\n", libusb_error_name(rc));
}
printf("Device remains connected. Unplug the device to test the reconnect ...\n");
while (1) {
unsigned char pingbuf[1] = {0};
int res = libusb_bulk_transfer(handle, endpoint_out, pingbuf, 0, &transferred, 100);
if (res == LIBUSB_ERROR_NO_DEVICE) {
printf("Device removed, wait for reconnection ...\n");
break;
}
usleep(1000 * 1000);
}
libusb_release_interface(handle, INTERFACE_NUM);
libusb_close(handle);
handle = NULL;
}
libusb_exit(ctx);
return 0;
}
Just found out you don't even have to keep it open just run it once.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb-1.0/libusb.h>
#include <unistd.h>
#define VENDOR_ID 0x057E
const uint16_t PRODUCT_IDS[] = {0x2066, 0x2067, 0x2069, 0x2073};
#define NUM_PRODUCT_IDS (sizeof(PRODUCT_IDS)/sizeof(PRODUCT_IDS[0]))
#define INTERFACE_NUM 1
const unsigned char DEFAULT_REPORT_DATA[] = {
0x03, 0x91, 0x00, 0x0d, 0x00, 0x08,
0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const unsigned char SET_LED_DATA[] = {
0x09, 0x91, 0x00, 0x07, 0x00, 0x08,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
int find_bulk_out_endpoint(libusb_device_handle* handle, uint8_t* endpoint_out) {
libusb_device* dev = libusb_get_device(handle);
struct libusb_config_descriptor* config;
if (libusb_get_config_descriptor(dev, 0, &config) != 0)
return -1;
for (int i = 0; i < config->bNumInterfaces; i++) {
const struct libusb_interface* iface = &config->interface[i];
for (int j = 0; j < iface->num_altsetting; j++) {
const struct libusb_interface_descriptor* altsetting = &iface->altsetting[j];
if (altsetting->bInterfaceNumber == INTERFACE_NUM) {
for (int k = 0; k < altsetting->bNumEndpoints; k++) {
const struct libusb_endpoint_descriptor* ep = &altsetting->endpoint[k];
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK &&
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) {
*endpoint_out = ep->bEndpointAddress;
libusb_free_config_descriptor(config);
return 0;
}
}
}
}
}
libusb_free_config_descriptor(config);
return -1;
}
int main() {
libusb_context* ctx = NULL;
libusb_device_handle* handle = NULL;
libusb_init(&ctx);
printf("ProCon2 Enabler: Waiting for supported device ...\n");
for (int i = 0; i < NUM_PRODUCT_IDS; i++) {
handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_IDS[i]);
if (handle) {
printf("Device found (PID: 0x%04X).\n", PRODUCT_IDS[i]);
break;
}
}
if (!handle) {
printf("No supported device found!\n");
libusb_exit(ctx);
return 1;
}
if (libusb_claim_interface(handle, INTERFACE_NUM) != 0) {
printf("Interface %d could not be claimed!\n", INTERFACE_NUM);
libusb_close(handle);
libusb_exit(ctx);
return 1;
}
printf("Interface %d successfully claimed.\n", INTERFACE_NUM);
uint8_t endpoint_out = 0;
if (find_bulk_out_endpoint(handle, &endpoint_out) != 0) {
printf("No Bulk OUT Endpoint found!\n");
libusb_release_interface(handle, INTERFACE_NUM);
libusb_close(handle);
libusb_exit(ctx);
return 1;
}
printf("Bulk OUT Endpoint: 0x%02X\n", endpoint_out);
int transferred;
int rc = libusb_bulk_transfer(handle, endpoint_out, (unsigned char*)DEFAULT_REPORT_DATA,
sizeof(DEFAULT_REPORT_DATA), &transferred, 1000);
if (rc == 0) {
printf("Default report sent (%d Bytes).\n", transferred);
} else {
printf("Error sending the default report: %s\n", libusb_error_name(rc));
}
rc = libusb_bulk_transfer(handle, endpoint_out, (unsigned char*)SET_LED_DATA,
sizeof(SET_LED_DATA), &transferred, 1000);
if (rc == 0) {
printf("LED report sent (%d Bytes).\n", transferred);
} else {
printf("Error sending the LED report: %s\n", libusb_error_name(rc));
}
printf("Enabling done! You can now use the controller.\n");
libusb_release_interface(handle, INTERFACE_NUM);
libusb_close(handle);
libusb_exit(ctx);
return 0;
}
There's a project called BlueRetro which adds Bluetooth controller support as a mod for older consoles. They have their own NS2 GC controller implementation that seems to be fully complete (sans gyro), and could probably be used as a reference point, if needed.
Some relevant files:
https://github.com/darthcloud/BlueRetro/blob/master/main/adapter/wireless/sw2.c
https://github.com/darthcloud/BlueRetro/blob/master/main/bluetooth/hidp/sw2.c
Relevant issue: https://github.com/darthcloud/BlueRetro/issues/1249
@pod-person Although those links might be useful for rumble, gyro, and LED support, I don't see it being too much help for the analog triggers on the GameCube controller because the Switch 2 Pro Controller unfortunately doesn't have analog triggers. Thanks for your reply, though! We need any help we can get.
https://github.com/libsdl-org/SDL/issues/13178#issuecomment-2989186695
@Nohzockt Thank you so much for this!! We're almost there. Because of your https://github.com/libsdl-org/SDL/issues/13178#issuecomment-2974677818 reply, I believe we may be able to Frankenstein a driver. 😂 When reading your source code for the analog triggers, I can't seem to find how you're even detecting it. From what I can see, the trigger digital and analog inputs are merged into one byte. Because of my lack of knowledge in developing controllers, I might be asking a lot of questions. However, I hope I'm at least somewhat helpful. :)
@Nohzockt Thank you so much for this!! We're almost there. Because of your #13178 (comment) reply, I believe we may be able to Frankenstein a driver. 😂 When reading your source code for the analog triggers, I can't seem to find how you're even detecting it. From what I can see, the trigger digital and analog inputs are merged into one byte. Because of my lack of knowledge in developing controllers, I might be asking a lot of questions. However, I hope I'm at least somewhat helpful. :)
Well, getting the controller to function was done by the website: https://handheldlegend.github.io/procon2tool/ and now also possible with the libusb version I did. Reading the analog triggers was done using the information on here: https://docs.handheldlegend.com/s/link-zone/doc/input-format-usb-Ns1FRj0RDf#h-gc-nso-analog-trigger and then reading the controller output via hidapi. Hope this helps a bit with understanding, but I am no expert; this is the first time I have ever worked on something like this and only because I bought the controller and wanted to use it, so I am also just hoping this somehow helps 😅
@Nohzockt I noticed a Linux drive appear after I started using your release, and have been wondering how someone would go about removing/stopping the program from running. I'm having a hard time finding it in ProcExplorer.
@pod-person Although those links might be useful for rumble, gyro, and LED support, I don't see it being too much help for the analog triggers on the GameCube controller because the Switch 2 Pro Controller unfortunately doesn't have analog triggers. Thanks for your reply, though! We need any help we can get.
Hi, I believe you're mistaken. The links I sent include code for the GameCube controller.
I'm a little confused how you all are getting this far? I only have the Joy-Cons, not the Pro/GC controllers, but I assume it's the same.
The BT-LE protocol appears to be non-standard/proprietary, and both Windows and bluez fail to even finalise the pairing process. Does this not mean we need a kernel driver?
@Nohzockt I noticed a Linux drive appear after I started using your release, and have been wondering how someone would go about removing/stopping the program from running. I'm having a hard time finding it in ProcExplorer.
Which code do you mean? The one you run one time to enable you don’t really stop the functionality; it just stops if you unplug the controller, and if you want to use it, you have to run it again. If you mean the other one, well, just interrupt the program running in the terminal note it will still keep the basic functions of the controller working till it is unplugged(at least under Mac), and if you are running it in the background as some kind of daemon, I am not sure how to do that on Linux. I guess kill it via the command line under the name you compiled it. I am sorry if this didn't help, but I am unsure if I understood you correctly.
@Nohzockt If I remember correctly, a Linux drive appeared on the Windows File Explorer after running the .exe. A little window of a Linux desktop also flashed on and off the screen quickly.
Here's what the Linux drive looks like: https://imgur.com/a/WY55hb1 I tried right-clicking and going to its properties to see any details, but I got a pop-up saying the properties aren't available. If I click the drop-down arrow, the drop-down arrow disappears.
@Nohzockt If I remember correctly, a Linux drive appeared on the Windows File Explorer after running the .exe. A little window of a Linux desktop also flashed on and off the screen quickly.
Here's what the Linux drive looks like: https://imgur.com/a/WY55hb1 I tried right-clicking and going to its properties to see any details, but I got a pop-up saying the properties aren't available. If I click the drop-down arrow, the drop-down arrow disappears.
Hey, so I am not sure why that is. I tried it on my Windows computer and didn't get the same result, but after some research, I found out it may be connected to the WSL. I don't know why it showed up, but I really don't know if my code is the cause of that. Maybe because I coded it on a Unix system. Sorry I couldn't help much more.
@pod-person Although those links might be useful for rumble, gyro, and LED support, I don't see it being too much help for the analog triggers on the GameCube controller because the Switch 2 Pro Controller unfortunately doesn't have analog triggers. Thanks for your reply, though! We need any help we can get.
Hi, I believe you're mistaken. The links I sent include code for the GameCube controller.
Thanks for sending these links. I tried making something similar to the monitoring tool I already made for using the controllers via USB, and it was a great help. Because it is a bit bigger, and I don't like posting this much code into this issue, here I will link the code in a repo I made https://github.com/Nohzockt/Switch2-Controllers/blob/main/ns2-ble-monitor.py. Hope this helps, and thanks a lot.
@pod-person
Hi, I believe you're mistaken. The links I sent include code for the GameCube controller.
I apologize, I didn't see the code for the GameCube controller. Thank you for your help!