SDL: gamepad: Replace GetNumMappings/GetMappingByIndex with a single function.

From dd47da8a5c2eeba75798ab796f66ee2d291da8a3 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 29 Nov 2023 11:15:48 -0500
Subject: [PATCH] gamepad: Replace GetNumMappings/GetMappingByIndex with a
 single function.

Now it returns an array and optional count, to match other SDL3 APIs.
---
 build-scripts/SDL_migration.cocci |  10 ---
 docs/README-migration.md          |   4 +-
 include/SDL3/SDL_gamepad.h        |  17 ++---
 include/SDL3/SDL_oldnames.h       |   4 --
 src/dynapi/SDL_dynapi.sym         |   3 +-
 src/dynapi/SDL_dynapi_overrides.h |   3 +-
 src/dynapi/SDL_dynapi_procs.h     |   3 +-
 src/joystick/SDL_gamepad.c        | 100 +++++++++++++++++++-----------
 test/testcontroller.c             |  13 ++--
 9 files changed, 78 insertions(+), 79 deletions(-)

diff --git a/build-scripts/SDL_migration.cocci b/build-scripts/SDL_migration.cocci
index 50e17402d077..fb27a66f644d 100644
--- a/build-scripts/SDL_migration.cocci
+++ b/build-scripts/SDL_migration.cocci
@@ -1240,21 +1240,11 @@ typedef SDL_GameControllerButton, SDL_GamepadButton;
   (...)
 @@
 @@
-- SDL_GameControllerMappingForIndex
-+ SDL_GetGamepadMappingForIndex
-  (...)
-@@
-@@
 - SDL_GameControllerName
 + SDL_GetGamepadName
   (...)
 @@
 @@
-- SDL_GameControllerNumMappings
-+ SDL_GetNumGamepadMappings
-  (...)
-@@
-@@
 - SDL_GameControllerOpen
 + SDL_OpenGamepad
   (...)
diff --git a/docs/README-migration.md b/docs/README-migration.md
index 60de306e73c1..14d172f76bda 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -468,9 +468,7 @@ The following functions have been renamed:
 * SDL_GameControllerIsSensorEnabled() => SDL_GamepadSensorEnabled()
 * SDL_GameControllerMapping() => SDL_GetGamepadMapping()
 * SDL_GameControllerMappingForGUID() => SDL_GetGamepadMappingForGUID()
-* SDL_GameControllerMappingForIndex() => SDL_GetGamepadMappingForIndex()
 * SDL_GameControllerName() => SDL_GetGamepadName()
-* SDL_GameControllerNumMappings() => SDL_GetNumGamepadMappings()
 * SDL_GameControllerOpen() => SDL_OpenGamepad()
 * SDL_GameControllerPath() => SDL_GetGamepadPath()
 * SDL_GameControllerRumble() => SDL_RumbleGamepad()
@@ -490,6 +488,8 @@ The following functions have been removed:
 * SDL_GameControllerNameForIndex() - replaced with SDL_GetGamepadInstanceName()
 * SDL_GameControllerPathForIndex() - replaced with SDL_GetGamepadInstancePath()
 * SDL_GameControllerTypeForIndex() - replaced with SDL_GetGamepadInstanceType()
+* SDL_GameControllerNumMappings() - replaced with SDL_GetGamepadMappings()
+* SDL_GameControllerMappingForIndex() replaced with SDL_GetGamepadMappings()
 
 The following symbols have been renamed:
 * SDL_CONTROLLER_AXIS_INVALID => SDL_GAMEPAD_AXIS_INVALID
diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h
index f0bf60e1603f..6baeaf305af5 100644
--- a/include/SDL3/SDL_gamepad.h
+++ b/include/SDL3/SDL_gamepad.h
@@ -311,25 +311,16 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromFile(const char *file);
  */
 extern DECLSPEC int SDLCALL SDL_ReloadGamepadMappings(void);
 
-/**
- * Get the number of mappings installed.
- *
- * \returns the number of mappings.
- *
- * \since This function is available since SDL 3.0.0.
- */
-extern DECLSPEC int SDLCALL SDL_GetNumGamepadMappings(void);
-
 /**
  * Get the mapping at a particular index.
  *
- * \param mapping_index mapping index
- * \returns the mapping string. Must be freed with SDL_free(). Returns NULL if
- *          the index is out of range.
+ * \param count On return, set to the number of mappings returned. Can be NULL.
+ * \returns an array of the mapping strings, NULL-terminated. Must be freed with SDL_free().
+ *          Returns NULL on error.
  *
  * \since This function is available since SDL 3.0.0.
  */
-extern DECLSPEC char * SDLCALL SDL_GetGamepadMappingForIndex(int mapping_index);
+extern DECLSPEC char ** SDLCALL SDL_GetGamepadMappings(int *count);
 
 /**
  * Get the gamepad mapping string for a given GUID.
diff --git a/include/SDL3/SDL_oldnames.h b/include/SDL3/SDL_oldnames.h
index bfe64d4f5d8a..5e5042d9e4dc 100644
--- a/include/SDL3/SDL_oldnames.h
+++ b/include/SDL3/SDL_oldnames.h
@@ -235,9 +235,7 @@
 #define SDL_GameControllerIsSensorEnabled SDL_GamepadSensorEnabled
 #define SDL_GameControllerMapping SDL_GetGamepadMapping
 #define SDL_GameControllerMappingForGUID SDL_GetGamepadMappingForGUID
-#define SDL_GameControllerMappingForIndex SDL_GetGamepadMappingForIndex
 #define SDL_GameControllerName SDL_GetGamepadName
-#define SDL_GameControllerNumMappings SDL_GetNumGamepadMappings
 #define SDL_GameControllerOpen SDL_OpenGamepad
 #define SDL_GameControllerPath SDL_GetGamepadPath
 #define SDL_GameControllerRumble SDL_RumbleGamepad
@@ -680,9 +678,7 @@
 #define SDL_GameControllerMapping SDL_GameControllerMapping_renamed_SDL_GetGamepadMapping
 #define SDL_GameControllerMappingForDeviceIndex SDL_GameControllerMappingForDeviceIndex_renamed_SDL_GetGamepadMappingForDeviceIndex
 #define SDL_GameControllerMappingForGUID SDL_GameControllerMappingForGUID_renamed_SDL_GetGamepadMappingForGUID
-#define SDL_GameControllerMappingForIndex SDL_GameControllerMappingForIndex_renamed_SDL_GetGamepadMappingForIndex
 #define SDL_GameControllerName SDL_GameControllerName_renamed_SDL_GetGamepadName
-#define SDL_GameControllerNumMappings SDL_GameControllerNumMappings_renamed_SDL_GetNumGamepadMappings
 #define SDL_GameControllerOpen SDL_GameControllerOpen_renamed_SDL_OpenGamepad
 #define SDL_GameControllerPath SDL_GameControllerPath_renamed_SDL_GetGamepadPath
 #define SDL_GameControllerRumble SDL_GameControllerRumble_renamed_SDL_RumbleGamepad
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 5194954994a7..4e8c1cf8721d 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -189,7 +189,6 @@ SDL3_0.0.0 {
     SDL_GetGamepadJoystick;
     SDL_GetGamepadMapping;
     SDL_GetGamepadMappingForGUID;
-    SDL_GetGamepadMappingForIndex;
     SDL_GetGamepadName;
     SDL_GetGamepadPath;
     SDL_GetGamepadPlayerIndex;
@@ -250,7 +249,6 @@ SDL3_0.0.0 {
     SDL_GetMouseState;
     SDL_GetNaturalDisplayOrientation;
     SDL_GetNumAllocations;
-    SDL_GetNumGamepadMappings;
     SDL_GetNumGamepadTouchpadFingers;
     SDL_GetNumGamepadTouchpads;
     SDL_GetNumJoystickAxes;
@@ -963,6 +961,7 @@ SDL3_0.0.0 {
     SDL_GetBooleanProperty;
     SDL_CreateTextureWithProperties;
     SDL_CreateRendererWithProperties;
+    SDL_GetGamepadMappings;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 478005db8f2a..8676e44138ca 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -213,7 +213,6 @@
 #define SDL_GetGamepadJoystick SDL_GetGamepadJoystick_REAL
 #define SDL_GetGamepadMapping SDL_GetGamepadMapping_REAL
 #define SDL_GetGamepadMappingForGUID SDL_GetGamepadMappingForGUID_REAL
-#define SDL_GetGamepadMappingForIndex SDL_GetGamepadMappingForIndex_REAL
 #define SDL_GetGamepadName SDL_GetGamepadName_REAL
 #define SDL_GetGamepadPath SDL_GetGamepadPath_REAL
 #define SDL_GetGamepadPlayerIndex SDL_GetGamepadPlayerIndex_REAL
@@ -274,7 +273,6 @@
 #define SDL_GetMouseState SDL_GetMouseState_REAL
 #define SDL_GetNaturalDisplayOrientation SDL_GetNaturalDisplayOrientation_REAL
 #define SDL_GetNumAllocations SDL_GetNumAllocations_REAL
-#define SDL_GetNumGamepadMappings SDL_GetNumGamepadMappings_REAL
 #define SDL_GetNumGamepadTouchpadFingers SDL_GetNumGamepadTouchpadFingers_REAL
 #define SDL_GetNumGamepadTouchpads SDL_GetNumGamepadTouchpads_REAL
 #define SDL_GetNumJoystickAxes SDL_GetNumJoystickAxes_REAL
@@ -988,3 +986,4 @@
 #define SDL_GetBooleanProperty SDL_GetBooleanProperty_REAL
 #define SDL_CreateTextureWithProperties SDL_CreateTextureWithProperties_REAL
 #define SDL_CreateRendererWithProperties SDL_CreateRendererWithProperties_REAL
+#define SDL_GetGamepadMappings SDL_GetGamepadMappings_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 17567ae673fd..5cce83c8abaf 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -272,7 +272,6 @@ SDL_DYNAPI_PROC(Uint16,SDL_GetGamepadInstanceVendor,(SDL_JoystickID a),(a),retur
 SDL_DYNAPI_PROC(SDL_Joystick*,SDL_GetGamepadJoystick,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(char*,SDL_GetGamepadMapping,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(char*,SDL_GetGamepadMappingForGUID,(SDL_JoystickGUID a),(a),return)
-SDL_DYNAPI_PROC(char*,SDL_GetGamepadMappingForIndex,(int a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetGamepadName,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetGamepadPath,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetGamepadPlayerIndex,(SDL_Gamepad *a),(a),return)
@@ -333,7 +332,6 @@ SDL_DYNAPI_PROC(SDL_Window*,SDL_GetMouseFocus,(void),(),return)
 SDL_DYNAPI_PROC(Uint32,SDL_GetMouseState,(float *a, float *b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetNaturalDisplayOrientation,(SDL_DisplayID a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumAllocations,(void),(),return)
-SDL_DYNAPI_PROC(int,SDL_GetNumGamepadMappings,(void),(),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpadFingers,(SDL_Gamepad *a, int b),(a,b),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumGamepadTouchpads,(SDL_Gamepad *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumJoystickAxes,(SDL_Joystick *a),(a),return)
@@ -1013,3 +1011,4 @@ SDL_DYNAPI_PROC(int,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, S
 SDL_DYNAPI_PROC(SDL_bool,SDL_GetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)
 SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTextureWithProperties,(SDL_Renderer *a, SDL_PropertiesID b),(a,b),return)
 SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateRendererWithProperties,(SDL_PropertiesID a),(a),return)
+SDL_DYNAPI_PROC(char**,SDL_GetGamepadMappings,(int *a),(a),return)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index ec1120db7190..8347857e2644 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -2002,29 +2002,6 @@ int SDL_AddGamepadMapping(const char *mapping)
     return retval;
 }
 
-/*
- *  Get the number of mappings installed
- */
-int SDL_GetNumGamepadMappings(void)
-{
-    int num_mappings = 0;
-
-    SDL_LockJoysticks();
-    {
-        GamepadMapping_t *mapping;
-
-        for (mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
-            if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
-                continue;
-            }
-            ++num_mappings;
-        }
-    }
-    SDL_UnlockJoysticks();
-
-    return num_mappings;
-}
-
 /*
  * Create a mapping string for a mapping
  */
@@ -2081,33 +2058,82 @@ static char *CreateMappingString(GamepadMapping_t *mapping, SDL_JoystickGUID gui
     return pMappingString;
 }
 
-/*
- *  Get the mapping at a particular index.
- */
-char *SDL_GetGamepadMappingForIndex(int mapping_index)
+char **SDL_GetGamepadMappings(int *count)
 {
-    char *retval = NULL;
+    int num_mappings = 0;
+    char **retval = NULL;
+    char **mappings = NULL;
+
+    if (count) {
+        *count = 0;
+    }
 
     SDL_LockJoysticks();
-    {
-        GamepadMapping_t *mapping;
 
-        for (mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
+    for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
+        if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
+            continue;
+        }
+        num_mappings++;
+    }
+
+    size_t final_allocation = sizeof (char *);  // for the NULL terminator element.
+    SDL_bool failed = SDL_FALSE;
+    mappings = (char **) SDL_calloc(num_mappings + 1, sizeof (char *));
+    if (!mappings) {
+        failed = SDL_TRUE;
+        SDL_OutOfMemory();
+    } else {
+        size_t i = 0;
+        for (GamepadMapping_t *mapping = s_pSupportedGamepads; mapping; mapping = mapping->next) {
             if (SDL_memcmp(&mapping->guid, &s_zeroGUID, sizeof(mapping->guid)) == 0) {
                 continue;
             }
-            if (mapping_index == 0) {
-                retval = CreateMappingString(mapping, mapping->guid);
-                break;
+
+            char *mappingstr = CreateMappingString(mapping, mapping->guid);
+            if (!mappingstr) {
+                failed = SDL_TRUE;
+                break;  // error string is already set.
             }
-            --mapping_index;
+
+            SDL_assert(i < num_mappings);
+            mappings[i++] = mappingstr;
+
+            final_allocation += SDL_strlen(mappingstr) + 1 + sizeof (char *);
         }
     }
+
     SDL_UnlockJoysticks();
 
-    if (!retval) {
-        SDL_SetError("Mapping not available");
+    if (!failed) {
+        retval = (char **) SDL_malloc(final_allocation);
+        if (!retval) {
+            SDL_OutOfMemory();
+        } else {
+            final_allocation -= (sizeof (char *) * num_mappings + 1);
+            char *strptr = (char *) (retval + (num_mappings + 1));
+            for (int i = 0; i < num_mappings; i++) {
+                retval[i] = strptr;
+                const size_t slen = SDL_strlcpy(strptr, mappings[i], final_allocation) + 1;
+                SDL_assert(final_allocation >= slen);
+                final_allocation -= slen;
+                strptr += slen;
+            }
+            retval[num_mappings] = NULL;
+
+            if (count) {
+                *count = num_mappings;
+            }
+        }
+    }
+
+    if (mappings) {
+        for (int i = 0; i < num_mappings; i++) {
+            SDL_free(mappings[i]);
+        }
+        SDL_free(mappings);
     }
+
     return retval;
 }
 
diff --git a/test/testcontroller.c b/test/testcontroller.c
index e7d7260bc6e0..f6d406e01fcb 100644
--- a/test/testcontroller.c
+++ b/test/testcontroller.c
@@ -1871,12 +1871,12 @@ static void loop(void *arg)
 
 int main(int argc, char *argv[])
 {
+    SDL_bool show_mappings = SDL_FALSE;
     int i;
     float content_scale;
     int screen_width, screen_height;
     SDL_Rect area;
     int gamepad_index = -1;
-    SDL_bool show_mappings = SDL_FALSE;
     SDLTest_CommonState *state;
 
     /* Initialize test framework */
@@ -1937,16 +1937,15 @@ int main(int argc, char *argv[])
     SDL_AddGamepadMappingsFromFile("gamecontrollerdb.txt");
 
     if (show_mappings) {
+        int count = 0;
+        char **mappings = SDL_GetGamepadMappings(&count);
         int map_i;
         SDL_Log("Supported mappings:\n");
-        for (map_i = 0; map_i < SDL_GetNumGamepadMappings(); ++map_i) {
-            char *mapping = SDL_GetGamepadMappingForIndex(map_i);
-            if (mapping) {
-                SDL_Log("\t%s\n", mapping);
-                SDL_free(mapping);
-            }
+        for (map_i = 0; map_i < count; ++map_i) {
+            SDL_Log("\t%s\n", mappings[map_i]);
         }
         SDL_Log("\n");
+        SDL_free(mappings);
     }
 
     /* Create a window to display gamepad state */