SDL: Added the CRC of the joystick name to the GUID

From c1e087394020a8cb9d2a04a1eabbcc23a6a5b20d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 22 Aug 2022 16:46:55 -0700
Subject: [PATCH] Added the CRC of the joystick name to the GUID

This will make it possible to have mappings for different controllers
that have the same VID/PID. This happens frequently with some generic
controller boards that have been reused in many products.

Fixes https://github.com/libsdl-org/SDL/issues/6004
---
 src/joystick/SDL_gamecontroller.c             | 14 +++-
 src/joystick/SDL_joystick.c                   | 68 +++++++++++++------
 src/joystick/SDL_joystick_c.h                 |  2 +-
 src/joystick/SDL_sysjoystick.h                |  3 +-
 src/joystick/android/SDL_sysjoystick.c        | 10 +--
 src/joystick/bsd/SDL_bsdjoystick.c            | 14 ++--
 src/joystick/darwin/SDL_iokitjoystick.c       |  4 +-
 src/joystick/emscripten/SDL_sysjoystick.c     | 10 ++-
 src/joystick/haiku/SDL_haikujoystick.cc       | 14 ++--
 src/joystick/hidapi/SDL_hidapijoystick.c      | 41 +++++------
 src/joystick/iphoneos/SDL_mfijoystick.m       |  2 +-
 src/joystick/linux/SDL_sysjoystick.c          |  2 +-
 src/joystick/os2/SDL_os2joystick.c            | 16 +++--
 src/joystick/ps2/SDL_sysjoystick.c            | 12 ++--
 src/joystick/psp/SDL_sysjoystick.c            | 10 ++-
 src/joystick/virtual/SDL_virtualjoystick.c    |  2 +-
 src/joystick/vita/SDL_sysjoystick.c           | 14 ++--
 src/joystick/windows/SDL_dinputjoystick.c     |  4 +-
 src/joystick/windows/SDL_rawinputjoystick.c   | 40 +++++------
 .../windows/SDL_windows_gaming_input.c        |  2 +-
 src/joystick/windows/SDL_xinputjoystick.c     | 14 ++--
 21 files changed, 180 insertions(+), 118 deletions(-)

diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c
index 87d1cbf52b9..46bbac486de 100644
--- a/src/joystick/SDL_gamecontroller.c
+++ b/src/joystick/SDL_gamecontroller.c
@@ -538,7 +538,7 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
 
     SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string));
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
 
     if ((vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER) ||
         (vendor == USB_VENDOR_SHENZHEN && product == USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER)) {
@@ -664,9 +664,17 @@ static ControllerMapping_t *SDL_CreateMappingForWGIController(SDL_JoystickGUID g
 static ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID guid, SDL_bool exact_match)
 {
     ControllerMapping_t *mapping = s_pSupportedControllers;
+    SDL_JoystickGUID zero_crc_guid;
+
+    SDL_memcpy(&zero_crc_guid, &guid, sizeof(guid));
+    zero_crc_guid.data[3] = 0;
+    zero_crc_guid.data[4] = 0;
+    zero_crc_guid.data[5] = 0;
+    zero_crc_guid.data[6] = 0;
 
     while (mapping) {
-        if (SDL_memcmp(&guid, &mapping->guid, sizeof(guid)) == 0) {
+        if (SDL_memcmp(&guid, &mapping->guid, sizeof(guid)) == 0 ||
+            SDL_memcmp(&zero_crc_guid, &mapping->guid, sizeof(guid)) == 0) {
             return mapping;
         }
         mapping = mapping->next;
@@ -1836,7 +1844,7 @@ SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUID guid)
         return SDL_FALSE;
     }
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, &version);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, &version, NULL);
 
     if (SDL_GetHintBoolean("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", SDL_FALSE)) {
         /* We shouldn't ignore Steam's virtual gamepad since it's using the hints to filter out the real controllers so it can remap input for the virtual controller */
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 26b017bb9f5..ac0af330e3d 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -1768,19 +1768,23 @@ SDL_JoystickEventState(int state)
 #endif /* SDL_EVENTS_DISABLED */
 }
 
-void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version)
+void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)
 {
     Uint16 *guid16 = (Uint16 *)guid.data;
-
-    /* If the GUID fits the form of BUS 0000 VENDOR 0000 PRODUCT 0000, return the data */
-    if (/* guid16[0] is device bus type */
-        guid16[1] == 0x0000 &&
-        /* guid16[2] is vendor ID */
-        guid16[3] == 0x0000 &&
-        /* guid16[4] is product ID */
-        guid16[5] == 0x0000
-        /* guid16[6] is product version */
-    ) {
+    Uint16 bus = SDL_SwapLE16(guid16[0]);
+
+    if (bus < ' ' && guid16[3] == 0x0000 && guid16[5] == 0x0000) {
+        /* This GUID fits the standard form:
+         * 16-bit bus
+         * 16-bit CRC16 of the joystick name (can be zero)
+         * 16-bit vendor ID
+         * 16-bit zero
+         * 16-bit product ID
+         * 16-bit zero
+         * 16-bit version
+         * 8-bit driver identifier ('h' for HIDAPI, 'x' for XInput, etc.)
+         * 8-bit driver-dependent type info
+         */
         if (vendor) {
             *vendor = SDL_SwapLE16(guid16[2]);
         }
@@ -1790,6 +1794,27 @@ void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *prod
         if (version) {
             *version = SDL_SwapLE16(guid16[6]);
         }
+        if (crc16) {
+            *crc16 = SDL_SwapLE16(guid16[1]);
+        }
+    } else if (bus < ' ') {
+        /* This GUID fits the unknown VID/PID form:
+         * 16-bit bus
+         * 16-bit CRC16 of the joystick name (can be zero)
+         * 11 characters of the joystick name, null terminated
+         */
+        if (vendor) {
+            *vendor = 0;
+        }
+        if (product) {
+            *product = 0;
+        }
+        if (version) {
+            *version = 0;
+        }
+        if (crc16) {
+            *crc16 = SDL_SwapLE16(guid16[1]);
+        }
     } else {
         if (vendor) {
             *vendor = 0;
@@ -1800,6 +1825,9 @@ void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *prod
         if (version) {
             *version = 0;
         }
+        if (crc16) {
+            *crc16 = 0;
+        }
     }
 }
 
@@ -2045,7 +2073,7 @@ SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *nam
     SDL_GameControllerType type;
     Uint16 vendor, product;
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
     type = SDL_GetJoystickGameControllerTypeFromVIDPID(vendor, product, name, SDL_TRUE);
     if (type == SDL_CONTROLLER_TYPE_UNKNOWN) {
         if (SDL_IsJoystickXInput(guid)) {
@@ -2379,7 +2407,7 @@ static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_JoystickGUID guid)
         return (SDL_JoystickType)guid.data[15];
     }
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
     vidpid = MAKE_VIDPID(vendor, product);
 
     if (SDL_IsJoystickProductWheel(vidpid)) {
@@ -2567,7 +2595,7 @@ SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
     Uint16 product;
     SDL_GameControllerType type;
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
 
     /* Check the joystick blacklist */
     id = MAKE_VIDPID(vendor, product);
@@ -2618,7 +2646,7 @@ Uint16 SDL_JoystickGetDeviceVendor(int device_index)
     Uint16 vendor;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
     return vendor;
 }
 
@@ -2627,7 +2655,7 @@ Uint16 SDL_JoystickGetDeviceProduct(int device_index)
     Uint16 product;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
     return product;
 }
 
@@ -2636,7 +2664,7 @@ Uint16 SDL_JoystickGetDeviceProductVersion(int device_index)
     Uint16 version;
     SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
 
-    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version);
+    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
     return version;
 }
 
@@ -2700,7 +2728,7 @@ Uint16 SDL_JoystickGetVendor(SDL_Joystick *joystick)
     Uint16 vendor;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
     return vendor;
 }
 
@@ -2709,7 +2737,7 @@ Uint16 SDL_JoystickGetProduct(SDL_Joystick *joystick)
     Uint16 product;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL);
+    SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
     return product;
 }
 
@@ -2718,7 +2746,7 @@ Uint16 SDL_JoystickGetProductVersion(SDL_Joystick *joystick)
     Uint16 version;
     SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
 
-    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version);
+    SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
     return version;
 }
 
diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h
index db630c81721..94a8fe1b715 100644
--- a/src/joystick/SDL_joystick_c.h
+++ b/src/joystick/SDL_joystick_c.h
@@ -55,7 +55,7 @@ extern SDL_bool SDL_GetDriverAndJoystickIndex(int device_index, struct _SDL_Joys
 extern int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id);
 
 /* Function to extract information from an SDL joystick GUID */
-extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version);
+extern void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16);
 
 /* Function to standardize the name for a controller
    This should be freed with SDL_free() when no longer needed
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index d5c8c0e21c6..79a3720f7ef 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -125,9 +125,10 @@ struct _SDL_Joystick
 };
 
 /* Device bus definitions */
-#define SDL_HARDWARE_BUS_VIRTUAL    0x00
+#define SDL_HARDWARE_BUS_UNKNOWN    0x00
 #define SDL_HARDWARE_BUS_USB        0x03
 #define SDL_HARDWARE_BUS_BLUETOOTH  0x05
+#define SDL_HARDWARE_BUS_VIRTUAL    0xFF
 
 /* Joystick capability flags for GetCapabilities() */
 #define SDL_JOYCAP_LED              0x01
diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c
index 104724afd89..581e6e6a9ff 100644
--- a/src/joystick/android/SDL_sysjoystick.c
+++ b/src/joystick/android/SDL_sysjoystick.c
@@ -360,7 +360,7 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
     /* We only need 16 bits for each of these; space them out to fill 128. */
     /* Byteswap so devices get same GUID on little/big endian platforms. */
     *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
-    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, desc, SDL_strlen(desc)));
 
     if (vendor_id && product_id) {
         *guid16++ = SDL_SwapLE16(vendor_id);
@@ -368,12 +368,8 @@ Android_AddJoystick(int device_id, const char *name, const char *desc, int vendo
         *guid16++ = SDL_SwapLE16(product_id);
         *guid16++ = 0;
     } else {
-        Uint32 crc = 0;
-        SDL_crc32(crc, desc, SDL_strlen(desc));
-        SDL_memcpy(guid16, desc, SDL_min(2*sizeof(*guid16), SDL_strlen(desc)));
-        guid16 += 2;
-        *(Uint32 *)guid16 = SDL_SwapLE32(crc);
-        guid16 += 2;
+        SDL_strlcpy((char*)guid16, name, 4*sizeof(Uint16));
+        guid16 += 4;
     }
 
     *guid16++ = SDL_SwapLE16(button_mask);
diff --git a/src/joystick/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c
index d7b4d473084..55538c74062 100644
--- a/src/joystick/bsd/SDL_bsdjoystick.c
+++ b/src/joystick/bsd/SDL_bsdjoystick.c
@@ -700,13 +700,17 @@ BSD_JoystickQuit(void)
 }
 
 static SDL_JoystickGUID
-BSD_JoystickGetDeviceGUID( int device_index )
+BSD_JoystickGetDeviceGUID(int device_index)
 {
+    /* the GUID is just the name for now */
+    const char *name = BSD_JoystickGetDeviceName(device_index);
     SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = BSD_JoystickGetDeviceName( device_index );
-    SDL_zero( guid );
-    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
+    SDL_zero(guid);
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
     return guid;
 }
 
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 6b6945ea0d6..befbfda33b4 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -504,7 +504,7 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
 
     if (vendor && product) {
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, pDevice->product, SDL_strlen(pDevice->product)));
         *guid16++ = SDL_SwapLE16((Uint16)vendor);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16((Uint16)product);
@@ -513,7 +513,7 @@ GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice)
         *guid16++ = 0;
     } else {
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, pDevice->product, SDL_strlen(pDevice->product)));
         SDL_strlcpy((char*)guid16, pDevice->product, sizeof(pDevice->guid.data) - 4);
     }
 
diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c
index 1664a907cf1..628a979c382 100644
--- a/src/joystick/emscripten/SDL_sysjoystick.c
+++ b/src/joystick/emscripten/SDL_sysjoystick.c
@@ -394,11 +394,15 @@ EMSCRIPTEN_JoystickClose(SDL_Joystick *joystick)
 static SDL_JoystickGUID
 EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
 {
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
+    /* the GUID is just the name for now */
     const char *name = EMSCRIPTEN_JoystickGetDeviceName(device_index);
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
     SDL_zero(guid);
-    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
     return guid;
 }
 
diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc
index c0c6e012cf9..dc2cc89745e 100644
--- a/src/joystick/haiku/SDL_haikujoystick.cc
+++ b/src/joystick/haiku/SDL_haikujoystick.cc
@@ -248,13 +248,17 @@ extern "C"
         SDL_joyname[0] = NULL;
     }
 
-    static SDL_JoystickGUID HAIKU_JoystickGetDeviceGUID( int device_index )
+    static SDL_JoystickGUID HAIKU_JoystickGetDeviceGUID(int device_index)
     {
+        /* the GUID is just the name for now */
+        const char *name = HAIKU_JoystickGetDeviceName(device_index);
         SDL_JoystickGUID guid;
-        /* the GUID is just the first 16 chars of the name for now */
-        const char *name = HAIKU_JoystickGetDeviceName( device_index );
-        SDL_zero( guid );
-        SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
+        Uint16 *guid16 = (Uint16 *)guid.data;
+
+        SDL_zero(guid);
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+        SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
         return guid;
     }
 
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index 0bb7954b662..d95abd45989 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -603,26 +603,6 @@ HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_H
     device->interface_protocol = info->interface_protocol;
     device->usage_page = info->usage_page;
     device->usage = info->usage;
-    {
-        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
-        const Uint16 vendor = device->vendor_id;
-        const Uint16 product = device->product_id;
-        const Uint16 version = device->version;
-        Uint16 *guid16 = (Uint16 *)device->guid.data;
-
-        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(vendor);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(product);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(version);
-        *guid16++ = 0;
-
-        /* Note that this is a HIDAPI device for special handling elsewhere */
-        device->guid.data[14] = 'h';
-        device->guid.data[15] = 0;
-    }
     device->dev_lock = SDL_CreateMutex();
 
     /* Need the device name before getting the driver to know whether to ignore this device */
@@ -654,6 +634,27 @@ HIDAPI_AddDevice(const struct SDL_hid_device_info *info, int num_children, SDL_H
         }
     }
 
+    {
+        /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
+        const Uint16 vendor = device->vendor_id;
+        const Uint16 product = device->product_id;
+        const Uint16 version = device->version;
+        Uint16 *guid16 = (Uint16 *)device->guid.data;
+
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, device->name, SDL_strlen(device->name)));
+        *guid16++ = SDL_SwapLE16(vendor);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(product);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(version);
+        *guid16++ = 0;
+
+        /* Note that this is a HIDAPI device for special handling elsewhere */
+        device->guid.data[14] = 'h';
+        device->guid.data[15] = 0;
+    }
+
     if (num_children > 0) {
         int i;
 
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index 662d820dc23..372cfcc809d 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -475,7 +475,7 @@ @interface GCMicroGamepad (SDL)
     /* We only need 16 bits for each of these; space them out to fill 128. */
     /* Byteswap so devices get same GUID on little/big endian platforms. */
     *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
-    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
     *guid16++ = SDL_SwapLE16(vendor);
     *guid16++ = 0;
     *guid16++ = SDL_SwapLE16(product);
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index 307dd56427a..497b1849012 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -241,7 +241,7 @@ IsJoystick(const char *path, int fd, char **name_return, SDL_JoystickGUID *guid)
     /* We only need 16 bits for each of these; space them out to fill 128. */
     /* Byteswap so devices get same GUID on little/big endian platforms. */
     *guid16++ = SDL_SwapLE16(inpid.bustype);
-    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
 
     if (inpid.vendor && inpid.product) {
         *guid16++ = SDL_SwapLE16(inpid.vendor);
diff --git a/src/joystick/os2/SDL_os2joystick.c b/src/joystick/os2/SDL_os2joystick.c
index 1c292ff4dd7..e49fa32899e 100644
--- a/src/joystick/os2/SDL_os2joystick.c
+++ b/src/joystick/os2/SDL_os2joystick.c
@@ -399,12 +399,16 @@ static void OS2_JoystickSetDevicePlayerIndex(int device_index, int player_index)
 
 static SDL_JoystickGUID OS2_JoystickGetDeviceGUID(int device_index)
 {
-	SDL_JoystickGUID guid;
-	/* the GUID is just the first 16 chars of the name for now */
-	const char *name = OS2_JoystickGetDeviceName(device_index);
-	SDL_zero(guid);
-	SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
-	return guid;
+    /* the GUID is just the name for now */
+    const char *name = OS2_JoystickGetDeviceName(device_index);
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
+    SDL_zero(guid);
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
+    return guid;
 }
 
 static SDL_JoystickID OS2_JoystickGetDeviceInstanceID(int device_index)
diff --git a/src/joystick/ps2/SDL_sysjoystick.c b/src/joystick/ps2/SDL_sysjoystick.c
index f9d1120d25e..cf4a82b960e 100644
--- a/src/joystick/ps2/SDL_sysjoystick.c
+++ b/src/joystick/ps2/SDL_sysjoystick.c
@@ -167,13 +167,17 @@ static void PS2_JoystickSetDevicePlayerIndex(int device_index, int player_index)
 }
 
 /* Function to return the stable GUID for a plugged in device */
-static SDL_JoystickGUID PS2_JoystickGetDeviceGUID( int device_index )
+static SDL_JoystickGUID PS2_JoystickGetDeviceGUID(int device_index)
 {
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
+    /* the GUID is just the name for now */
     const char *name = PS2_JoystickGetDeviceName(device_index);
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
     SDL_zero(guid);
-    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
     return guid;
 }
 
diff --git a/src/joystick/psp/SDL_sysjoystick.c b/src/joystick/psp/SDL_sysjoystick.c
index df3be5681b0..762027346ff 100644
--- a/src/joystick/psp/SDL_sysjoystick.c
+++ b/src/joystick/psp/SDL_sysjoystick.c
@@ -134,11 +134,15 @@ static void PSP_JoystickSetDevicePlayerIndex(int device_index, int player_index)
 
 static SDL_JoystickGUID PSP_JoystickGetDeviceGUID(int device_index)
 {
-    SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
+    /* the GUID is just the name for now */
     const char *name = PSP_JoystickGetDeviceName(device_index);
+    SDL_JoystickGUID guid;
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
     SDL_zero(guid);
-    SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
     return guid;
 }
 
diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c
index 273c75e5a12..9849a8c9668 100644
--- a/src/joystick/virtual/SDL_virtualjoystick.c
+++ b/src/joystick/virtual/SDL_virtualjoystick.c
@@ -198,7 +198,7 @@ SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc)
     /* Byteswap so devices get same GUID on little/big endian platforms. */
     guid16 = (Uint16 *)hwdata->guid.data;
     *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_VIRTUAL);
-    *guid16++ = 0;
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
     *guid16++ = SDL_SwapLE16(hwdata->desc.vendor_id);
     *guid16++ = 0;
     *guid16++ = SDL_SwapLE16(hwdata->desc.product_id);
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index 7b384e7e9de..a88983291da 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -341,13 +341,17 @@ void VITA_JoystickQuit(void)
 {
 }
 
-SDL_JoystickGUID VITA_JoystickGetDeviceGUID( int device_index )
+SDL_JoystickGUID VITA_JoystickGetDeviceGUID(int device_index)
 {
+    /* the GUID is just the name for now */
+    const char *name = VITA_JoystickGetDeviceName(device_index);
     SDL_JoystickGUID guid;
-    /* the GUID is just the first 16 chars of the name for now */
-    const char *name = VITA_JoystickGetDeviceName( device_index );
-    SDL_zero( guid );
-    SDL_memcpy( &guid, name, SDL_min( sizeof(guid), SDL_strlen( name ) ) );
+    Uint16 *guid16 = (Uint16 *)guid.data;
+
+    SDL_zero(guid);
+    *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_UNKNOWN);
+    *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
+    SDL_strlcpy((char*)guid16, name, sizeof(guid.data) - 4);
     return guid;
 }
 
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index 8631ca6d787..38040c3893b 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -501,7 +501,7 @@ EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
     guid16 = (Uint16 *)pNewJoystick->guid.data;
     if (vendor && product) {
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, pNewJoystick->joystickname, SDL_strlen(pNewJoystick->joystickname)));
         *guid16++ = SDL_SwapLE16(vendor);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16(product);
@@ -510,7 +510,7 @@ EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInstance, LPVOID pContext)
         *guid16++ = 0;
     } else {
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_BLUETOOTH);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, pNewJoystick->joystickname, SDL_strlen(pNewJoystick->joystickname)));
         SDL_strlcpy((char*)guid16, pNewJoystick->joystickname, sizeof(pNewJoystick->guid.data) - 4);
     }
 
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index cff656b2669..d760a7db2d8 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -755,26 +755,6 @@ RAWINPUT_AddDevice(HANDLE hDevice)
     CHECK(device->preparsed_data = (PHIDP_PREPARSED_DATA)SDL_calloc(size, sizeof(BYTE)));
     CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_PREPARSEDDATA, device->preparsed_data, &size) != (UINT)-1);
 
-    {
-        const Uint16 vendor = device->vendor_id;
-        const Uint16 product = device->product_id;
-        const Uint16 version = device->version;
-        Uint16 *guid16 = (Uint16 *)device->guid.data;
-
-        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(vendor);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(product);
-        *guid16++ = 0;
-        *guid16++ = SDL_SwapLE16(version);
-        *guid16++ = 0;
-
-        /* Note that this is a RAWINPUT device for special handling elsewhere */
-        device->guid.data[14] = 'r';
-        device->guid.data[15] = 0;
-    }
-
     hFile = CreateFileA(dev_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
     CHECK(hFile != INVALID_HANDLE_VALUE);
 
@@ -799,6 +779,26 @@ RAWINPUT_AddDevice(HANDLE hDevice)
             SDL_free(product_string);
         }
     }
+    {
+        const Uint16 vendor = device->vendor_id;
+        const Uint16 product = device->product_id;
+        const Uint16 version = device->version;
+        Uint16 *guid16 = (Uint16 *)device->guid.data;
+
+        *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, device->name, SDL_strlen(device->name)));
+        *guid16++ = SDL_SwapLE16(vendor);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(product);
+        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(version);
+        *guid16++ = 0;
+
+        /* Note that this is a RAWINPUT device for special handling elsewhere */
+        device->guid.data[14] = 'r';
+        device->guid.data[15] = 0;
+    }
+
     device->path = SDL_strdup(dev_name);
 
     CloseHandle(hFile);
diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c
index c32c889978f..b64df7f45ed 100644
--- a/src/joystick/windows/SDL_windows_gaming_input.c
+++ b/src/joystick/windows/SDL_windows_gaming_input.c
@@ -320,7 +320,7 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde
 
         /* FIXME: Is there any way to tell whether this is a Bluetooth device? */
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
         *guid16++ = SDL_SwapLE16(vendor);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16(product);
diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index dcf2940ff92..a7026a0bb59 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -279,13 +279,19 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
     }
 
     pNewJoystick->bXInputDevice = SDL_TRUE;
+    pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, GetXInputName(userid, SubType));
+    if (!pNewJoystick->joystickname) {
+        SDL_free(pNewJoystick);
+        return; /* better luck next time? */
+    }
+    SDL_snprintf(pNewJoystick->path, sizeof(pNewJoystick->path), "XInput#%d", userid);
     if (!SDL_XInputUseOldJoystickMapping()) {
         Uint16 *guid16 = (Uint16 *)pNewJoystick->guid.data;
 
         GuessXInputDevice(userid, &vendor, &product, &version);
 
         *guid16++ = SDL_SwapLE16(SDL_HARDWARE_BUS_USB);
-        *guid16++ = 0;
+        *guid16++ = SDL_SwapLE16(SDL_crc16(0, pNewJoystick->joystickname, SDL_strlen(pNewJoystick->joystickname)));
         *guid16++ = SDL_SwapLE16(vendor);
         *guid16++ = 0;
         *guid16++ = SDL_SwapLE16(product);
@@ -299,12 +305,6 @@ AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pContext)
     }
     pNewJoystick->SubType = SubType;
     pNewJoystick->XInputUserId = userid;
-    pNewJoystick->joystickname = SDL_CreateJoystickName(vendor, product, NULL, GetXInputName(userid, SubType));
-    if (!pNewJoystick->joystickname) {
-        SDL_free(pNewJoystick);
-        return; /* better luck next time? */
-    }
-    SDL_snprintf(pNewJoystick->path, sizeof(pNewJoystick->path), "XInput#%d", userid);
 
     if (SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)) {
         SDL_free(pNewJoystick);