diff --git a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java index 691416c1c92cd..9a874b2ef1f6c 100644 --- a/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/HIDDeviceManager.java @@ -232,6 +232,7 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa final int XB360W_IFACE_PROTOCOL = 129; // Wireless final int[] SUPPORTED_VENDORS = { 0x0079, // GPD Win 2 + 0x0351, // CRKD 0x044f, // Thrustmaster 0x045e, // Microsoft 0x046d, // Logitech @@ -243,6 +244,7 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa 0x0f0d, // Hori 0x1038, // SteelSeries 0x11c9, // Nacon + 0x1209, // Generic 0x12ab, // Unknown 0x1430, // RedOctane 0x146b, // BigBen @@ -257,7 +259,9 @@ private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterfa 0x2c22, // Qanba 0x2dc8, // 8BitDo 0x3537, // GameSir + 0x3651, // CRKD 0x37d7, // Flydigi + 0x3958, // Red Octane Games 0x9886, // ASTRO Gaming }; @@ -279,6 +283,7 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa final int XB1_IFACE_SUBCLASS = 71; final int XB1_IFACE_PROTOCOL = 208; final int[] SUPPORTED_VENDORS = { + 0x0351, // CRKD 0x03f0, // HP 0x044f, // Thrustmaster 0x045e, // Microsoft @@ -287,6 +292,7 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa 0x0e6f, // PDP 0x0f0d, // Hori 0x10f5, // Turtle Beach + 0x1209, // Generic 0x1532, // Razer Wildcat 0x20d6, // PowerA 0x24c6, // PowerA @@ -296,7 +302,9 @@ private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterfa 0x2e95, // SCUF 0x3285, // Nacon 0x3537, // GameSir + 0x3651, // CRKD 0x366c, // ByoWave + 0x3958, // Red Octane Games }; if (usbInterface.getId() == 0 && diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 2bceed262b865..9936bfc5d5428 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -2291,7 +2291,8 @@ extern "C" { * - "0": Assume this is a generic controller. * - "1": Reset the controller to get metadata. * - * By default the controller is not reset. + * By default the controller is reset. This is so we can properly detect + * the controller type. * * This hint should be set before initializing joysticks and gamepads. * diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index b5e8bd2c460ec..aa58afe8a9267 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -902,6 +902,7 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de static const int xb360w_iface_protocol = 129; /* Wireless */ static const int supported_vendors[] = { 0x0079, /* GPD Win 2 */ + 0x0351, /* CRKD */ 0x044f, /* Thrustmaster */ 0x045e, /* Microsoft */ 0x046d, /* Logitech */ @@ -913,6 +914,7 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de 0x0f0d, /* Hori */ 0x1038, /* SteelSeries */ 0x11c9, /* Nacon */ + 0x1209, /* Generic */ 0x12ab, /* Unknown */ 0x1430, /* RedOctane */ 0x146b, /* BigBen */ @@ -927,7 +929,9 @@ static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_de 0x2c22, /* Qanba */ 0x2dc8, /* 8BitDo */ 0x3537, /* GameSir */ + 0x3651, /* CRKD */ 0x37d7, /* Flydigi */ + 0x3958, /* Red Octane Games */ 0x9886, /* ASTRO Gaming */ }; @@ -950,6 +954,7 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de static const int xb1_iface_subclass = 71; static const int xb1_iface_protocol = 208; static const int supported_vendors[] = { + 0x0351, /* CRKD */ 0x03f0, /* HP */ 0x044f, /* Thrustmaster */ 0x045e, /* Microsoft */ @@ -958,6 +963,7 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de 0x0e6f, /* PDP */ 0x0f0d, /* Hori */ 0x10f5, /* Turtle Beach */ + 0x1209, /* Generic */ 0x1532, /* Razer Wildcat */ 0x20d6, /* PowerA */ 0x24c6, /* PowerA */ @@ -967,7 +973,9 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de 0x2e95, /* SCUF */ 0x3285, /* Nacon */ 0x3537, /* GameSir */ + 0x3651, /* CRKD */ 0x366c, /* ByoWave */ + 0x3958, /* Red Octane Games */ }; if (intf_desc->bInterfaceNumber == 0 && diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.cpp index d05b0022752c4..68f889ac98631 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.cpp +++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp @@ -717,7 +717,46 @@ static bool GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool ena joystick->hwdata->report_sensors = enabled; return true; } - +static void GAMEINPUT_DrumUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) +{ +#if GAMEINPUT_API_VERSION >= 3 + IGameInputRawDeviceReport *rawState; + if (reading->GetRawReport(&rawState)) { + uint8_t rawData[40]; + SDL_memset(rawData, 0, sizeof(rawData)); + size_t len = rawState->GetRawData(sizeof(rawData), rawData); + Uint8 hat = 0; + uint16_t buttons = rawData[0] | rawData[1] << 8; + if (len >= 6) { + if ((buttons & 0x0100) || (rawData[4] & 0xf0)) { + hat |= SDL_HAT_UP; + } + if ((buttons & 0x0200) || (rawData[4] & 0x0f)) { + hat |= SDL_HAT_DOWN; + } + if (buttons & 0x0400) { + hat |= SDL_HAT_LEFT; + } + if (buttons & 0x0800) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(timestamp, joystick, 0, hat); + // The rest of the instruments are mapped to PS3 style inputs + // The PS3 used flags for dictating if a pad or a cymbal was hit, so we emulate that here + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, (buttons & 0x0008)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, (buttons & 0x0004)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (buttons & 0x1000)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (buttons & 0x2000)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, (rawData[3] & 0x0f) || (rawData[5] & 0xf0) || (buttons & 0x0010)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, (rawData[2] & 0xf0) || (buttons & 0x0020)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, (rawData[3] & 0xf0) || (rawData[4] & 0x0f) || (buttons & 0x0040)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, (rawData[2] & 0x0f) || (rawData[4] & 0xf0) || (buttons & 0x0080)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, rawData[2] || rawData[3]); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, rawData[4] || rawData[5]); + } + } +#endif // GAMEINPUT_API_VERSION >= 3 +} static void GAMEINPUT_GuitarUpdate(SDL_Joystick *joystick, IGameInputReading *reading, Uint64 timestamp) { #if GAMEINPUT_API_VERSION >= 3 @@ -916,6 +955,8 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) timestamp = SDL_US_TO_NS(reading->GetTimestamp() + g_GameInputTimestampOffset); if (internal_device->raw_type == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_GUITAR) { GAMEINPUT_GuitarUpdate(joystick, reading, timestamp); + } else if (internal_device->raw_type == SDL_GAMEINPUT_RAWTYPE_ROCK_BAND_DRUM_KIT) { + GAMEINPUT_DrumUpdate(joystick, reading, timestamp); } else if (GAMEINPUT_InternalIsGamepad(info)) { GAMEINPUT_GamepadUpdate(joystick, reading, timestamp); } else { diff --git a/src/joystick/hidapi/SDL_hidapi_gip.c b/src/joystick/hidapi/SDL_hidapi_gip.c index 18df94e772b90..26d8c5fd2d249 100644 --- a/src/joystick/hidapi/SDL_hidapi_gip.c +++ b/src/joystick/hidapi/SDL_hidapi_gip.c @@ -261,6 +261,9 @@ typedef enum GIP_TYPE_NAVIGATION_CONTROLLER = 4, GIP_TYPE_CHATPAD = 5, GIP_TYPE_HEADSET = 6, + GIP_TYPE_GUITAR = 7, + GIP_TYPE_DRUM_KIT = 8, + GIP_TYPE_LIVE_GUITAR = 9, } GIP_AttachmentType; typedef enum @@ -304,6 +307,11 @@ MAKE_GUID(GUID_IEliteButtons, 0x37d19ff7, 0xb5c6, 0x49d1, 0xa7, 0x5e, 0x03, 0xb2 MAKE_GUID(GUID_IGamepad, 0x082e402c, 0x07df, 0x45e1, 0xa5, 0xab, 0xa3, 0x12, 0x7a, 0xf1, 0x97, 0xb5); MAKE_GUID(GUID_NavigationController, 0xb8f31fe7, 0x7386, 0x40e9, 0xa9, 0xf8, 0x2f, 0x21, 0x26, 0x3a, 0xcf, 0xb7); MAKE_GUID(GUID_Wheel, 0x646979cf, 0x6b71, 0x4e96, 0x8d, 0xf9, 0x59, 0xe3, 0x98, 0xd7, 0x42, 0x0c); +MAKE_GUID(GUID_PDP_Guitar, 0x1a266af6, 0x3a46, 0x45e3, 0xb9, 0xb6, 0x0f, 0x2c, 0x0b, 0x2c, 0x1e, 0xbe); +MAKE_GUID(GUID_Madcatz_Guitar, 0x0d2ae438, 0x7f7d, 0x4933, 0x86, 0x93, 0x30, 0xfc, 0x55, 0x01, 0x8e, 0x77); +MAKE_GUID(GUID_PDP_DrumKit, 0xa503f9b0, 0x955e, 0x47c4, 0xa2, 0xed, 0xb1, 0x33, 0x6f, 0xa7, 0x70, 0x3e); +MAKE_GUID(GUID_Madcatz_DrumKit, 0x06182893, 0xCCE0, 0x4B85, 0x92, 0x71, 0x0A, 0x10, 0xDB, 0xAB, 0x7E, 0x07); +MAKE_GUID(GUID_GuitarHero_Live_Guitar, 0xfd12fdd9, 0x8e73, 0x47c7, 0xa2, 0x31, 0x96, 0x26, 0x8c, 0x38, 0x00, 0x9a); /* * The following GUIDs are observed, but the exact meanings aren't known, so @@ -1258,6 +1266,9 @@ static bool GIP_EnsureMetadata(GIP_Attachment *attachment) attachment->metadata_retries = 0; return GIP_SendSystemMessage(attachment, GIP_CMD_METADATA, 0, NULL, 0); } else { + if (attachment->device->reset_for_metadata) { + return true; + } return GIP_SetMetadataDefaults(attachment); } default: @@ -1592,6 +1603,31 @@ static bool GIP_HandleCommandMetadataRespose( expected_guid = &GUID_IHeadset; break; } + if (SDL_strcmp(type, "Activision.Xbox.Input.GH7") == 0) { + attachment->attachment_type = GIP_TYPE_LIVE_GUITAR; + expected_guid = &GUID_GuitarHero_Live_Guitar; + break; + } + if (SDL_strcmp(type, "MadCatz.Xbox.Guitar.Stratocaster") == 0) { + attachment->attachment_type = GIP_TYPE_GUITAR; + expected_guid = &GUID_Madcatz_Guitar; + break; + } + if (SDL_strcmp(type, "PDP.Xbox.Guitar.Jaguar") == 0) { + attachment->attachment_type = GIP_TYPE_GUITAR; + expected_guid = &GUID_PDP_Guitar; + break; + } + if (SDL_strcmp(type, "MadCatz.Xbox.Drums.Glam") == 0) { + attachment->attachment_type = GIP_TYPE_DRUM_KIT; + expected_guid = &GUID_Madcatz_DrumKit; + break; + } + if (SDL_strcmp(type, "PDP.Xbox.Drums.Tablah") == 0) { + attachment->attachment_type = GIP_TYPE_DRUM_KIT; + expected_guid = &GUID_PDP_DrumKit; + break; + } } found_expected_guid = !expected_guid; @@ -2027,6 +2063,99 @@ static void GIP_HandleGamepadReport( SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis); } +static void GIP_HandleGuitarReport( + GIP_Attachment *attachment, + SDL_Joystick *joystick, + Uint64 timestamp, + const Uint8 *bytes, + int num_bytes) +{ + static uint16_t s_GuitarButtons[] = { + 0x0010, // SDL_GAMEPAD_BUTTON_SOUTH + 0x0020, // SDL_GAMEPAD_BUTTON_EAST + 0x0040, // SDL_GAMEPAD_BUTTON_WEST + 0x0080, // SDL_GAMEPAD_BUTTON_NORTH + 0x0008, // SDL_GAMEPAD_BUTTON_BACK + 0, // The guide button is not available + 0x0004, // SDL_GAMEPAD_BUTTON_START + 0, // right joystick click unavailable + 0x4000, // SDL_GAMEPAD_BUTTON_RIGHT_STICK + 0x1000, // SDL_GAMEPAD_BUTTON_LEFT_SHOULDER + 0, // right shoulder unavailable + }; + Uint8 btnidx = 0, hat = 0; + uint16_t buttons = bytes[0] | bytes[1] << 8; + if (num_bytes >= 10) { + for (btnidx = 0; btnidx < SDL_arraysize(s_GuitarButtons); ++btnidx) { + uint16_t button_mask = s_GuitarButtons[btnidx]; + if (!button_mask) { + continue; + } + bool down = ((buttons & button_mask) != 0); + SDL_SendJoystickButton(timestamp, joystick, btnidx, down); + } + if (buttons & 0x0100) { + hat |= SDL_HAT_UP; + } + if (buttons & 0x0200) { + hat |= SDL_HAT_DOWN; + } + if (buttons & 0x0400) { + hat |= SDL_HAT_LEFT; + } + if (buttons & 0x0800) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(timestamp, joystick, 0, hat); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, (bytes[3] * 257) - 32768); + // PS3 RB guitars had tilt on right shoulder + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, bytes[2] >= 0xD0); + // PS3 RB guitars send L2 when using solo buttons + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, (bytes[6]) ? 32767 : -32768); + // Align pickup selector mappings with PS3 instruments + static const Sint16 effects_mappings[] = {-26880, -13568, -1792, 11008, 24576}; + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, effects_mappings[bytes[4] >> 4]); + } +} + +static void GIP_HandleDrumKitReport( + GIP_Attachment *attachment, + SDL_Joystick *joystick, + Uint64 timestamp, + const Uint8 *bytes, + int num_bytes) +{ + Uint8 hat = 0; + uint16_t buttons = bytes[0] | bytes[1] << 8; + if (num_bytes >= 6) { + if ((buttons & 0x0100) || (bytes[4] & 0xf0)) { + hat |= SDL_HAT_UP; + } + if ((buttons & 0x0200) || (bytes[4] & 0x0f)) { + hat |= SDL_HAT_DOWN; + } + if (buttons & 0x0400) { + hat |= SDL_HAT_LEFT; + } + if (buttons & 0x0800) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(timestamp, joystick, 0, hat); + // The rest of the instruments are mapped to PS3 style inputs + // The PS3 used flags for dictating if a pad or a cymbal was hit, so we emulate that here + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, (buttons & 0x0008)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, (buttons & 0x0004)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, (buttons & 0x1000)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, (buttons & 0x2000)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, (bytes[3] & 0x0f) || (bytes[5] & 0xf0) || (buttons & 0x0010)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, (bytes[2] & 0xf0) || (buttons & 0x0020)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, (bytes[3] & 0xf0) || (bytes[4] & 0x0f) || (buttons & 0x0040)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, (bytes[2] & 0x0f) || (bytes[4] & 0xf0) || (buttons & 0x0080)); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, bytes[2] || bytes[3]); + SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, bytes[4] || bytes[5]); + } +} + static void GIP_HandleArcadeStickReport( GIP_Attachment *attachment, SDL_Joystick *joystick, @@ -2149,7 +2278,7 @@ static bool GIP_HandleLLInputReport( return true; } - if (num_bytes < 14) { + if (num_bytes < 6) { SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GIP: Discarding too-short input report"); return false; } @@ -2167,6 +2296,12 @@ static bool GIP_HandleLLInputReport( case GIP_TYPE_FLIGHT_STICK: GIP_HandleFlightStickReport(attachment, joystick, timestamp, bytes, num_bytes); break; + case GIP_TYPE_GUITAR: + GIP_HandleGuitarReport(attachment, joystick, timestamp, bytes, num_bytes); + break; + case GIP_TYPE_DRUM_KIT: + GIP_HandleDrumKitReport(attachment, joystick, timestamp, bytes, num_bytes); + break; } if (attachment->features & GIP_FEATURE_ELITE_BUTTONS) { @@ -2654,7 +2789,7 @@ static bool HIDAPI_DriverGIP_InitDevice(SDL_HIDAPI_Device *device) return false; } ctx->device = device; - ctx->reset_for_metadata = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GIP_RESET_FOR_METADATA, false); + ctx->reset_for_metadata = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_GIP_RESET_FOR_METADATA, true); attachment = GIP_EnsureAttachment(ctx, 0); GIP_HandleQuirks(attachment); @@ -2739,6 +2874,18 @@ static bool HIDAPI_DriverGIP_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystic joystick->naxes += attachment->extra_axes - 1; } + if (attachment->attachment_type == GIP_TYPE_GUITAR) { + device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR; + } + + if (attachment->attachment_type == GIP_TYPE_DRUM_KIT) { + device->joystick_type = SDL_JOYSTICK_TYPE_DRUM_KIT; + } + + if (attachment->attachment_type == GIP_TYPE_LIVE_GUITAR) { + device->joystick_type = SDL_JOYSTICK_TYPE_GUITAR; + } + joystick->nhats = 1; return true; diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 26a9def361bc2..aec7af0b0065e 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -268,6 +268,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U static const int SUPPORTED_VENDORS[] = { 0x0079, // GPD Win 2 + 0x0351, // CRKD 0x044f, // Thrustmaster 0x045e, // Microsoft 0x046d, // Logitech @@ -279,6 +280,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U 0x0f0d, // Hori 0x1038, // SteelSeries 0x11c9, // Nacon + 0x1209, // Generic 0x12ab, // Unknown 0x1430, // RedOctane 0x146b, // BigBen @@ -293,7 +295,9 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U 0x2c22, // Qanba 0x2dc8, // 8BitDo 0x3537, // GameSir + 0x3651, // CRKD 0x37d7, // Flydigi + 0x3958, // Red Octane Games 0x9886, // ASTRO Gaming }; @@ -312,6 +316,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U interface_protocol == XBONE_IFACE_PROTOCOL) { static const int SUPPORTED_VENDORS[] = { + 0x0351, // CRKD 0x03f0, // HP 0x044f, // Thrustmaster 0x045e, // Microsoft @@ -320,6 +325,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U 0x0e6f, // PDP 0x0f0d, // Hori 0x10f5, // Turtle Beach + 0x1209, // Generic 0x1532, // Razer 0x20d6, // PowerA 0x24c6, // PowerA @@ -329,7 +335,9 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U 0x2e95, // SCUF 0x3285, // Nacon 0x3537, // GameSir + 0x3651, // CRKD 0x366c, // ByoWave + 0x3958, // Red Octane Games }; int i;