diff -urN SDL2-2.32.0-orig/include/SDL_hints.h SDL2-2.32.0/include/SDL_hints.h
--- SDL2-2.32.0-orig/include/SDL_hints.h	2025-01-12 01:25:41 +0900
+++ SDL2-2.32.0/include/SDL_hints.h	2025-03-04 19:54:12 +0900
@@ -692,6 +692,43 @@
  */
 #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
 
+/**
+ *  \brief  A variable defining a map from physical to logical joystick buttons.
+ *
+ *  This can be used to remap buttons for programs which don't use the
+ *  GameController API.  The value should be a comma-separated list of
+ *  logical button IDs (that is, button IDs seen by the SDL program);
+ *  the first button ID corresponds to physical button ID 0, the second
+ *  to physical button ID 1, and so on.  The listed IDs must be within
+ *  the actual number of buttons on the device (so it is not possible to
+ *  add or remove buttons).  A logical button ID of -1 disables the
+ *  corresponding physical button.
+ *
+ *  For example, a mapping string of "1,2,0" for a 3-button joystick would
+ *  cause an SDL program to see button ID 1 when the controller's button 0
+ *  was pressed.
+ *
+ *  This setting applies to all joystick devices, and will also affect the
+ *  behavior of the GameController API.
+ */
+#define SDL_HINT_JOYSTICK_BUTTON_MAP "SDL_JOYSTICK_BUTTON_MAP"
+
+/**
+ *  \brief  A variable defining a map from joystick buttons to a joystick
+ *  hat switch.
+ *
+ *  This is similar to SDL_JOYSTICK_BUTTON_MAP, but allows mapping of
+ *  buttons to a hat switch (i.e., D-pad) instead of other buttons.
+ *  The hint value should be a list of four button IDs, which give the
+ *  buttons for the up, down, left, and right directions.  If button
+ *  remapping is also active, use remapped (logical) button IDs here.
+ *
+ *  This setting applies to all joystick devices, and will also affect the
+ *  behavior of the GameController API.  If this is set, any physical hat
+ *  switches will be ignored.
+ */
+#define SDL_HINT_JOYSTICK_HAT_BUTTONS "SDL_JOYSTICK_HAT_BUTTONS"
+
 /**
  * A variable containing a list of arcade stick style controllers.
  *
diff -urN SDL2-2.32.0-orig/src/joystick/SDL_joystick.c SDL2-2.32.0/src/joystick/SDL_joystick.c
--- SDL2-2.32.0-orig/src/joystick/SDL_joystick.c	2025-01-02 00:47:53 +0900
+++ SDL2-2.32.0/src/joystick/SDL_joystick.c	2025-03-04 19:52:48 +0900
@@ -850,14 +850,71 @@
     if (joystick->naxes > 0) {
         joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
     }
+    if (SDL_GetHint(SDL_HINT_JOYSTICK_HAT_BUTTONS)) {
+        joystick->nhats = 1;
+    }
     if (joystick->nhats > 0) {
-        joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(Uint8));
+        const char *map = SDL_GetHint(SDL_HINT_JOYSTICK_HAT_BUTTONS);
+        joystick->hats = (Uint8 *)SDL_malloc(joystick->nhats * sizeof(Uint8));
+        if (map) {
+            joystick->hat_buttons = SDL_calloc(4, sizeof(Uint8));
+            if (!joystick->hat_buttons) {
+                SDL_free(joystick->hats);  /* Trigger OOM below. */
+                joystick->hats = NULL;
+            } else {
+                int i;
+                for (i = 0; i < 4; i++) {
+                    long button;
+                    if (!*map) {
+                        button = 255;  /* Unmapped. */
+                    } else {
+                        button = strtol(map, (char **)&map, 10);
+                        if (button < 0 || button > 255) {
+                            button = 255;
+                        }
+                    }
+                    joystick->hat_buttons[i] = button;
+                    if (*map == ',') {
+                        map++;
+                    } else {
+                        map = "";
+                    }
+                }
+            }
+        }
     }
     if (joystick->nballs > 0) {
         joystick->balls = (struct balldelta *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
     }
     if (joystick->nbuttons > 0) {
+        const char *map = SDL_GetHint(SDL_HINT_JOYSTICK_BUTTON_MAP);
         joystick->buttons = (Uint8 *)SDL_calloc(joystick->nbuttons, sizeof(Uint8));
+        if (map && *map) {
+            joystick->button_map = (Uint8 *) SDL_calloc(joystick->nbuttons, sizeof(Uint8));
+            if (!joystick->button_map) {
+                SDL_free(joystick->buttons);  /* Trigger OOM below. */
+                joystick->buttons = NULL;
+            } else {
+                int i;
+                int physical;
+                for (i = 0; i < joystick->nbuttons; i++) {
+                    joystick->button_map[i] = i;
+                }
+                physical = 0;
+                while (physical < joystick->nbuttons && *map) {
+                    long logical = strtol(map, (char **)&map, 10);
+                    if (logical < 0 || logical >= joystick->nbuttons) {
+                        logical = 255;  /* Unmapped. */
+                    }
+                    joystick->button_map[physical] = (Uint8)logical;
+                    physical++;
+                    if (*map != ',') {
+                        break;
+                    }
+                    map++;
+                }
+            }
+        }
     }
     if (((joystick->naxes > 0) && !joystick->axes) || ((joystick->nhats > 0) && !joystick->hats) || ((joystick->nballs > 0) && !joystick->balls) || ((joystick->nbuttons > 0) && !joystick->buttons)) {
         SDL_OutOfMemory();
@@ -1624,6 +1681,8 @@
         SDL_free(joystick->hats);
         SDL_free(joystick->balls);
         SDL_free(joystick->buttons);
+        SDL_free(joystick->button_map);
+        SDL_free(joystick->hat_buttons);
         for (i = 0; i < joystick->ntouchpads; i++) {
             SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
             SDL_free(touchpad->fingers);
@@ -1933,6 +1992,11 @@
 
     SDL_AssertJoysticksLocked();
 
+    /* Ignore all physical hat events if using button-to-hat mapping */
+    if (joystick->hat_buttons) {
+        return 0;
+    }
+
     /* Make sure we're not getting garbage or duplicate events */
     if (axis >= joystick->naxes) {
         return 0;
@@ -2096,6 +2160,12 @@
     if (button >= joystick->nbuttons) {
         return 0;
     }
+    if (joystick->button_map) {
+        if (joystick->button_map[button] == 255) {
+            return 0;
+        }
+        button = joystick->button_map[button];
+    }
     if (state == joystick->buttons[button]) {
         return 0;
     }
@@ -2121,6 +2191,47 @@
         posted = SDL_PushEvent(&event) == 1;
     }
 #endif /* !SDL_EVENTS_DISABLED */
+
+    /* Handle button-to-hat mapping */
+    if (joystick->hat_buttons) {
+        int hat_value = SDL_HAT_CENTERED;
+        int hat_x = 0;  /* +1 = right */
+        int hat_y = 0;  /* +1 = down */
+        if (joystick->hat_buttons[0] < joystick->nbuttons && joystick->buttons[joystick->hat_buttons[0]]) {
+            hat_y--;
+        }
+        if (joystick->hat_buttons[1] < joystick->nbuttons && joystick->buttons[joystick->hat_buttons[1]]) {
+            hat_y++;
+        }
+        if (joystick->hat_buttons[2] < joystick->nbuttons && joystick->buttons[joystick->hat_buttons[2]]) {
+            hat_x--;
+        }
+        if (joystick->hat_buttons[3] < joystick->nbuttons && joystick->buttons[joystick->hat_buttons[3]]) {
+            hat_x++;
+        }
+        if (hat_x < 0) {
+            hat_value |= SDL_HAT_LEFT;
+        } else if (hat_x > 0) {
+            hat_value |= SDL_HAT_RIGHT;
+        }
+        if (hat_y < 0) {
+            hat_value |= SDL_HAT_UP;
+        } else if (hat_y > 0) {
+            hat_value |= SDL_HAT_DOWN;
+        }
+        joystick->hats[0] = hat_value;
+#if !SDL_EVENTS_DISABLED
+        if (SDL_GetEventState(SDL_JOYHATMOTION) == SDL_ENABLE) {
+            SDL_Event event;
+            event.jhat.type = SDL_JOYHATMOTION;
+            event.jhat.which = joystick->instance_id;
+            event.jhat.hat = 0;
+            event.jhat.value = hat_value;
+            posted |= SDL_PushEvent(&event) == 1;
+        }
+#endif /* !SDL_EVENTS_DISABLED */
+    }
+
     return posted;
 }
 
diff -urN SDL2-2.32.0-orig/src/joystick/SDL_sysjoystick.h SDL2-2.32.0/src/joystick/SDL_sysjoystick.h
--- SDL2-2.32.0-orig/src/joystick/SDL_sysjoystick.h	2025-01-02 00:47:53 +0900
+++ SDL2-2.32.0/src/joystick/SDL_sysjoystick.h	2025-03-04 19:52:48 +0900
@@ -96,6 +96,8 @@
 
     int nbuttons _guarded;   /* Number of buttons on the joystick */
     Uint8 *buttons _guarded; /* Current button states */
+    Uint8 *button_map _guarded;  /* Physical to logical button map, or NULL */
+    Uint8 *hat_buttons _guarded; /* Button to logical hat map, or NULL */
 
     int ntouchpads _guarded;                      /* Number of touchpads on the joystick */
     SDL_JoystickTouchpadInfo *touchpads _guarded; /* Current touchpad states */
