SDL: Allow the product version to be different when trying to find a controller mapping

From 0b8b321f9e91c9aea6bce4a3e2a2da427fb92698 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 28 Aug 2022 23:01:30 -0700
Subject: [PATCH] Allow the product version to be different when trying to find
 a controller mapping

This is in the hope that revving the product version doesn't change the mapping, which is the case for some devices. In cases where it does, we just need to provide a mapping for each version of the product.
---
 src/joystick/SDL_gamecontroller.c | 32 +++++++++++++++++++++++++++++++
 src/joystick/SDL_joystick.c       | 21 ++++++++++++++++++++
 src/joystick/SDL_joystick_c.h     |  9 +++++++++
 3 files changed, 62 insertions(+)

diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 34998a3cd64..79e8a4fd189 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -685,6 +685,38 @@ static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickG
         }
     }
 
+    if (!exact_match) {
+        /* Try again, ignoring the version */
+        Uint16 vendor, product;
+
+        SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
+        if (vendor && product) {
+            SDL_JoystickGUID match_guid;
+
+            SDL_memcpy(&match_guid, &guid, sizeof(guid));
+            SDL_SetJoystickGUIDVersion(&match_guid, 0);
+
+            for (mapping = s_pSupportedControllers; mapping; mapping = mapping->next) {
+                SDL_JoystickGUID mapping_guid;
+
+                SDL_memcpy(&mapping_guid, &mapping->guid, sizeof(mapping_guid));
+                SDL_SetJoystickGUIDVersion(&mapping_guid, 0);
+
+                if (SDL_memcmp(&match_guid, &mapping_guid, sizeof(match_guid)) == 0) {
+                    /* Check to see if the CRC matches */
+                    const char *crc_string = SDL_strstr(mapping->mapping, "crc:");
+                    if (crc_string) {
+                        Uint16 mapping_crc = (Uint16)SDL_strtol(crc_string + 4, NULL, 16); 
+                        if (crc != mapping_crc) {
+                            continue;
+                        }
+                    }
+                    return mapping;
+                }
+            }
+        }
+    }
+
     if (!exact_match) {
 #if SDL_JOYSTICK_XINPUT
         if (SDL_IsJoystickXInput(guid)) {
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 72d28b8ef5b..117bf4c6851 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -2017,6 +2017,27 @@ SDL_CreateJoystickGUIDForName(const char *name)
     return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, name, 0, 0);
 }
 
+void SDL_SetJoystickGUIDVendor(SDL_JoystickGUID *guid, Uint16 vendor)
+{
+    Uint16 *guid16 = (Uint16 *)guid->data;
+
+    guid16[2] = SDL_SwapLE16(vendor);
+}
+
+void SDL_SetJoystickGUIDProduct(SDL_JoystickGUID *guid, Uint16 product)
+{
+    Uint16 *guid16 = (Uint16 *)guid->data;
+
+    guid16[4] = SDL_SwapLE16(product);
+}
+
+void SDL_SetJoystickGUIDVersion(SDL_JoystickGUID *guid, Uint16 version)
+{
+    Uint16 *guid16 = (Uint16 *)guid->data;
+
+    guid16[6] = SDL_SwapLE16(version);
+}
+
 void
 SDL_SetJoystickGUIDCRC(SDL_JoystickGUID *guid, Uint16 crc)
 {
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index ca72a1d75d4..d77f8d007f2 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -65,6 +65,15 @@ extern SDL_JoystickGUID SDL_CreateJoystickGUID(Uint16 bus, Uint16 vendor, Uint16
 /* Function to create a GUID for a joystick based on the name, with no VID/PID information */
 extern SDL_JoystickGUID SDL_CreateJoystickGUIDForName(const char *name);
 
+/* Function to set the vendor field of a joystick GUID */
+extern void SDL_SetJoystickGUIDVendor(SDL_JoystickGUID *guid, Uint16 vendor);
+
+/* Function to set the product field of a joystick GUID */
+extern void SDL_SetJoystickGUIDProduct(SDL_JoystickGUID *guid, Uint16 product);
+
+/* Function to set the version field of a joystick GUID */
+extern void SDL_SetJoystickGUIDVersion(SDL_JoystickGUID *guid, Uint16 version);
+
 /* Function to set the CRC field of a joystick GUID */
 extern void SDL_SetJoystickGUIDCRC(SDL_JoystickGUID *guid, Uint16 crc);