sdl2-compat: dynapi: Hooked up the Dynamic API!

From 21776488edb1ec7ff54df2656faf9acec43931a7 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Tue, 29 Nov 2022 14:11:19 -0500
Subject: [PATCH] dynapi: Hooked up the Dynamic API!

Now you can just export SDL_DYNAMIC_API instead of LD_LIBRARY_PATH.  :)
---
 CMakeLists.txt                    |   1 +
 src/Makefile.darwin               |   2 +-
 src/Makefile.linux                |   2 +-
 src/Makefile.mingw                |   2 +-
 src/Makefile.vc                   |   2 +-
 src/dynapi/SDL_dynapi.c           | 480 +++++++++++++++
 src/dynapi/SDL_dynapi.h           |  81 +++
 src/dynapi/SDL_dynapi_overrides.h | 896 +++++++++++++++++++++++++++
 src/dynapi/SDL_dynapi_procs.h     | 981 ++++++++++++++++++++++++++++++
 src/sdl2_compat.c                 |  12 +
 10 files changed, 2455 insertions(+), 4 deletions(-)
 create mode 100644 src/dynapi/SDL_dynapi.c
 create mode 100644 src/dynapi/SDL_dynapi.h
 create mode 100644 src/dynapi/SDL_dynapi_overrides.h
 create mode 100644 src/dynapi/SDL_dynapi_procs.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 27f64f8..bcc6381 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -104,6 +104,7 @@ endif()
 
 set(SDL2COMPAT_SRCS
   src/sdl2_compat.c
+  src/dynapi/SDL_dynapi.c
   ${OSX_SRCS}
   ${WIN32_SRCS}
 )
diff --git a/src/Makefile.darwin b/src/Makefile.darwin
index bc9990c..7555839 100644
--- a/src/Makefile.darwin
+++ b/src/Makefile.darwin
@@ -30,7 +30,7 @@ endif
 
 DYLIB = libSDL2-2.0.0.dylib
 
-OBJ = sdl2_compat.o sdl2_compat_objc.o
+OBJ = sdl2_compat.o sdl2_compat_objc.o dynapi/SDL_dynapi.o
 
 .SUFFIXES:
 .SUFFIXES: .o .c .m
diff --git a/src/Makefile.linux b/src/Makefile.linux
index 0ee8d01..50c9b3e 100644
--- a/src/Makefile.linux
+++ b/src/Makefile.linux
@@ -17,7 +17,7 @@ LDLIBS  = -ldl
 
 SHLIB = libSDL2-2.0.so.0.2900.0
 
-OBJ = sdl2_compat.o
+OBJ = sdl2_compat.o dynapi/SDL_dynapi.o
 
 .SUFFIXES:
 .SUFFIXES: .o .c
diff --git a/src/Makefile.mingw b/src/Makefile.mingw
index 33c41d8..e3d55a2 100644
--- a/src/Makefile.mingw
+++ b/src/Makefile.mingw
@@ -25,7 +25,7 @@ LDLIBS  = -lkernel32 -luser32
 LIB = libSDL2.dll.a
 DLL = SDL2.dll
 
-OBJ = sdl2_compat.o version.o
+OBJ = sdl2_compat.o dynapi/SDL_dynapi.o version.o
 
 .SUFFIXES:
 .SUFFIXES: .o .c .rc
diff --git a/src/Makefile.vc b/src/Makefile.vc
index dfe40fb..59b48d7 100644
--- a/src/Makefile.vc
+++ b/src/Makefile.vc
@@ -25,7 +25,7 @@ CFLAGS = $(CFLAGS) /arch:SSE
 DLLNAME = SDL2.dll
 IMPNAME = SDL2.lib
 
-OBJ = sdl2_compat.obj version.res
+OBJ = sdl2_compat.obj dynapi/SDL_dynapi.obj version.res
 
 all: $(DLLNAME)
 
diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c
new file mode 100644
index 0000000..1b7cbcb
--- /dev/null
+++ b/src/dynapi/SDL_dynapi.c
@@ -0,0 +1,480 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../sdl3_include_wrapper.h"
+
+#include "SDL_dynapi.h"
+
+#if SDL_DYNAMIC_API
+
+#define SDL_DYNAMIC_API_ENVVAR "SDL_DYNAMIC_API"
+
+#if defined(__OS2__)
+#define INCL_DOS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include <dos.h>
+#endif
+
+
+/* This is the version of the dynamic API. This doesn't match the SDL version
+   and should not change until there's been a major revamp in API/ABI.
+   So 2.0.5 adds functions over 2.0.4? This number doesn't change;
+   the sizeof (jump_table) changes instead. But 2.1.0 changes how a function
+   works in an incompatible way or removes a function? This number changes,
+   since sizeof (jump_table) isn't sufficient anymore. It's likely
+   we'll forget to bump every time we add a function, so this is the
+   failsafe switch for major API change decisions. Respect it and use it
+   sparingly. */
+#define SDL_DYNAPI_VERSION 1
+
+static void SDL_InitDynamicAPI(void);
+
+/* BE CAREFUL CALLING ANY SDL CODE IN HERE, IT WILL BLOW UP.
+   Even self-contained stuff might call SDL_Error and break everything. */
+
+
+/* behold, the macro salsa! */
+
+/* !!! FIXME: ...disabled...until we write it.  :) */
+#define DISABLE_JUMP_MAGIC 1
+
+#if DISABLE_JUMP_MAGIC
+/* Can't use the macro for varargs nonsense. This is atrocious. */
+#define SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, logname, prio) \
+    _static void SDLCALL SDL_Log##logname##name(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        va_list ap; initcall; va_start(ap, fmt); \
+        jump_table.SDL_LogMessageV(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \
+        va_end(ap); \
+    }
+
+#define SDL_DYNAPI_VARARGS(_static, name, initcall) \
+    _static int SDLCALL SDL_SetError##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        char buf[128], *str = buf; \
+        int result; \
+        va_list ap; initcall; \
+        va_start(ap, fmt); \
+        result = jump_table.SDL_vsnprintf(buf, sizeof(buf), fmt, ap); \
+        va_end(ap); \
+        if (result >= 0 && (size_t)result >= sizeof(buf)) { \
+            size_t len = (size_t)result + 1; \
+            str = (char *)jump_table.SDL_malloc(len); \
+            if (str) { \
+                va_start(ap, fmt); \
+                result = jump_table.SDL_vsnprintf(str, len, fmt, ap); \
+                va_end(ap); \
+            } \
+        } \
+        if (result >= 0) { \
+            result = jump_table.SDL_SetError("%s", str); \
+        } \
+        if (str != buf) { \
+            jump_table.SDL_free(str); \
+        } \
+        return result; \
+    } \
+    _static int SDLCALL SDL_sscanf##name(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) { \
+        int retval; va_list ap; initcall; va_start(ap, fmt); \
+        retval = jump_table.SDL_vsscanf(buf, fmt, ap); \
+        va_end(ap); \
+        return retval; \
+    } \
+    _static int SDLCALL SDL_snprintf##name(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        int retval; va_list ap; initcall; va_start(ap, fmt); \
+        retval = jump_table.SDL_vsnprintf(buf, maxlen, fmt, ap); \
+        va_end(ap); \
+        return retval; \
+    } \
+    _static int SDLCALL SDL_asprintf##name(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        int retval; va_list ap; initcall; va_start(ap, fmt); \
+        retval = jump_table.SDL_vasprintf(strp, fmt, ap); \
+        va_end(ap); \
+        return retval; \
+    } \
+    _static void SDLCALL SDL_Log##name(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        va_list ap; initcall; va_start(ap, fmt); \
+        jump_table.SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \
+        va_end(ap); \
+    } \
+    _static void SDLCALL SDL_LogMessage##name(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        va_list ap; initcall; va_start(ap, fmt); \
+        jump_table.SDL_LogMessageV(category, priority, fmt, ap); \
+        va_end(ap); \
+    } \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Verbose, VERBOSE) \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Debug, DEBUG) \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Info, INFO) \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Warn, WARN) \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Error, ERROR) \
+    SDL_DYNAPI_VARARGS_LOGFN(_static, name, initcall, Critical, CRITICAL)
+#endif
+
+
+/* Typedefs for function pointers for jump table, and predeclare funcs */
+/* The DEFAULT funcs will init jump table and then call real function. */
+/* The REAL funcs are the actual functions, name-mangled to not clash. */
+#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
+    typedef rc (SDLCALL *SDL_DYNAPIFN_##fn) params; \
+    static rc SDLCALL fn##_DEFAULT params; \
+    extern rc SDLCALL fn##_REAL params;
+#include "SDL_dynapi_procs.h"
+#undef SDL_DYNAPI_PROC
+
+/* The jump table! */
+typedef struct {
+    #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) SDL_DYNAPIFN_##fn fn;
+    #include "SDL_dynapi_procs.h"
+    #undef SDL_DYNAPI_PROC
+} SDL_DYNAPI_jump_table;
+
+/* Predeclare the default functions for initializing the jump table. */
+#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) static rc SDLCALL fn##_DEFAULT params;
+#include "SDL_dynapi_procs.h"
+#undef SDL_DYNAPI_PROC
+
+/* The actual jump table. */
+static SDL_DYNAPI_jump_table jump_table = {
+    #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) fn##_DEFAULT,
+    #include "SDL_dynapi_procs.h"
+    #undef SDL_DYNAPI_PROC
+};
+
+/* Default functions init the function table then call right thing. */
+#if DISABLE_JUMP_MAGIC
+#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
+    static rc SDLCALL fn##_DEFAULT params { \
+        SDL_InitDynamicAPI(); \
+        ret jump_table.fn args; \
+    }
+#define SDL_DYNAPI_PROC_NO_VARARGS 1
+#include "SDL_dynapi_procs.h"
+#undef SDL_DYNAPI_PROC
+#undef SDL_DYNAPI_PROC_NO_VARARGS
+SDL_DYNAPI_VARARGS(static, _DEFAULT, SDL_InitDynamicAPI())
+#else
+/* !!! FIXME: need the jump magic. */
+#error Write me.
+#endif
+
+/* Public API functions to jump into the jump table. */
+#if DISABLE_JUMP_MAGIC
+#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
+    DECLSPEC rc SDLCALL fn params { ret jump_table.fn args; }
+#define SDL_DYNAPI_PROC_NO_VARARGS 1
+#include "SDL_dynapi_procs.h"
+#undef SDL_DYNAPI_PROC
+#undef SDL_DYNAPI_PROC_NO_VARARGS
+SDL_DYNAPI_VARARGS(DECLSPEC,,)
+#else
+/* !!! FIXME: need the jump magic. */
+#error Write me.
+#endif
+
+#define ENABLE_SDL_CALL_LOGGING 0
+#if ENABLE_SDL_CALL_LOGGING
+static int SDLCALL SDL_SetError_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) {
+    char buf[512]; /* !!! FIXME: dynamic allocation */ \
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_SetError");
+    va_start(ap, fmt);
+    SDL_vsnprintf_REAL(buf, sizeof (buf), fmt, ap);
+    va_end(ap);
+    return SDL_SetError_REAL("%s", buf);
+}
+static int SDLCALL SDL_sscanf_LOGSDLCALLS(const char *buf, SDL_SCANF_FORMAT_STRING const char *fmt, ...) {
+    int retval;
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_sscanf");
+    va_start(ap, fmt);
+    retval = SDL_vsscanf_REAL(buf, fmt, ap);
+    va_end(ap);
+    return retval;
+}
+static int SDLCALL SDL_snprintf_LOGSDLCALLS(SDL_OUT_Z_CAP(maxlen) char *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) {
+    int retval;
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_snprintf");
+    va_start(ap, fmt);
+    retval = SDL_vsnprintf_REAL(buf, maxlen, fmt, ap);
+    va_end(ap);
+    return retval;
+}
+static int SDLCALL SDL_asprintf_LOGSDLCALLS(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) {
+    int retval;
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_asprintf");
+    va_start(ap, fmt);
+    retval = SDL_vasprintf_REAL(strp, fmt, ap);
+    va_end(ap);
+    return retval;
+}
+static void SDLCALL SDL_Log_LOGSDLCALLS(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) {
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_Log");
+    va_start(ap, fmt);
+    SDL_LogMessageV_REAL(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap); \
+    va_end(ap);
+}
+static void SDLCALL SDL_LogMessage_LOGSDLCALLS(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) {
+    va_list ap;
+    SDL_Log_REAL("SDL2CALL SDL_LogMessage");
+    va_start(ap, fmt);
+    SDL_LogMessageV_REAL(category, priority, fmt, ap);
+    va_end(ap);
+}
+#define SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(logname, prio) \
+    static void SDLCALL SDL_Log##logname##_LOGSDLCALLS(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) { \
+        va_list ap; va_start(ap, fmt); \
+        SDL_Log_REAL("SDL2CALL SDL_Log%s", #logname); \
+        SDL_LogMessageV_REAL(category, SDL_LOG_PRIORITY_##prio, fmt, ap); \
+        va_end(ap); \
+    }
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Verbose, VERBOSE)
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Debug, DEBUG)
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Info, INFO)
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Warn, WARN)
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Error, ERROR)
+SDL_DYNAPI_VARARGS_LOGFN_LOGSDLCALLS(Critical, CRITICAL)
+#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
+    rc SDLCALL fn##_LOGSDLCALLS params { SDL_Log_REAL("SDL2CALL %s", #fn); ret fn##_REAL args; }
+#define SDL_DYNAPI_PROC_NO_VARARGS 1
+#include "SDL_dynapi_procs.h"
+#undef SDL_DYNAPI_PROC
+#undef SDL_DYNAPI_PROC_NO_VARARGS
+#endif
+
+/* we make this a static function so we can call the correct one without the
+   system's dynamic linker resolving to the wrong version of this. */
+static Sint32
+initialize_jumptable(Uint32 apiver, void *table, Uint32 tablesize)
+{
+    SDL_DYNAPI_jump_table *output_jump_table = (SDL_DYNAPI_jump_table *) table;
+
+    if (apiver != SDL_DYNAPI_VERSION) {
+        /* !!! FIXME: can maybe handle older versions? */
+        return -1;  /* not compatible. */
+    } else if (tablesize > sizeof (jump_table)) {
+        return -1;  /* newer version of SDL with functions we can't provide. */
+    }
+
+    /* Init our jump table first. */
+    #if ENABLE_SDL_CALL_LOGGING
+    {
+        const char *env = SDL_getenv_REAL("SDL_DYNAPI_LOG_CALLS");
+        const SDL_bool log_calls = (env && SDL_atoi_REAL(env));
+        if (log_calls) {
+            #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) jump_table.fn = fn##_LOGSDLCALLS;
+            #include "SDL_dynapi_procs.h"
+            #undef SDL_DYNAPI_PROC
+        } else {
+            #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) jump_table.fn = fn##_REAL;
+            #include "SDL_dynapi_procs.h"
+            #undef SDL_DYNAPI_PROC
+        }
+    }
+    #else
+        #define SDL_DYNAPI_PROC(rc,fn,params,args,ret) jump_table.fn = fn##_REAL;
+        #include "SDL_dynapi_procs.h"
+        #undef SDL_DYNAPI_PROC
+    #endif
+
+    /* Then the external table... */
+    if (output_jump_table != &jump_table) {
+        jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize);
+    }
+
+    /* Safe to call SDL functions now; jump table is initialized! */
+
+    return 0;  /* success! */
+}
+
+
+/* Here's the exported entry point that fills in the jump table. */
+/*  Use specific types when an "int" might suffice to keep this sane. */
+typedef Sint32 (SDLCALL *SDL_DYNAPI_ENTRYFN)(Uint32 apiver, void *table, Uint32 tablesize);
+extern DECLSPEC Sint32 SDLCALL SDL_DYNAPI_entry(Uint32, void *, Uint32);
+
+Sint32
+SDL_DYNAPI_entry(Uint32 apiver, void *table, Uint32 tablesize)
+{
+    return initialize_jumptable(apiver, table, tablesize);
+}
+
+
+/* Obviously we can't use SDL_LoadObject() to load SDL.  :)  */
+/* Also obviously, we never close the loaded library. */
+#if defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
+{
+    HANDLE lib = LoadLibraryA(fname);
+    void *retval = NULL;
+    if (lib) {
+        retval = GetProcAddress(lib, sym);
+        if (retval == NULL) {
+            FreeLibrary(lib);
+        }
+    }
+    return retval;
+}
+
+#elif defined(unix) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__) || defined(__QNX__)
+#include <stdlib.h>
+#include <dlfcn.h>
+static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
+{
+    void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
+    void *retval = NULL;
+    if (lib != NULL) {
+        retval = dlsym(lib, sym);
+        if (retval == NULL) {
+            dlclose(lib);
+        }
+    }
+    return retval;
+}
+
+#elif defined(__OS2__)
+static SDL_INLINE void *get_sdlapi_entry(const char *fname, const char *sym)
+{
+    HMODULE hmodule;
+    PFN retval = NULL;
+    char error[256];
+    if (DosLoadModule(error, sizeof(error), fname, &hmodule) == NO_ERROR) {
+        if (DosQueryProcAddr(hmodule, 0, sym, &retval) != NO_ERROR) {
+            DosFreeModule(hmodule);
+        }
+    }
+    return (void *)retval;
+}
+
+#else
+#error Please define your platform.
+#endif
+
+
+static void dynapi_warn(const char *msg)
+{
+    const char *caption = "SDL Dynamic API Failure!";
+    /* SDL_ShowSimpleMessageBox() is a too heavy for here. */
+    #if (defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
+    MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR);
+    #else
+    fprintf(stderr, "\n\n%s\n%s\n\n", caption, msg);
+    fflush(stderr);
+    #endif
+}
+
+/* This is not declared in any header, although it is shared between some
+    parts of SDL, because we don't want anything calling it without an
+    extremely good reason. */
+static SDL_NORETURN void
+DynApiExitProcess(int exitcode)
+{
+#if defined(_WIN32)
+    TerminateProcess(GetCurrentProcess(), exitcode);
+    ExitProcess(exitcode);
+#elif defined(__OS2__)
+    DosExit(EXIT_PROCESS, exitcode);
+#elif defined(__HAIKU__)  /* Haiku has _Exit, but it's not marked noreturn. */
+    _exit(exitcode);
+#else
+    _Exit(exitcode);  /* this (currently) covers everything else. */
+#endif
+}
+
+
+static void
+SDL_InitDynamicAPILocked(void)
+{
+    const char *libname = SDL_getenv_REAL(SDL_DYNAMIC_API_ENVVAR);
+    SDL_DYNAPI_ENTRYFN entry = NULL;  /* funcs from here by default. */
+    SDL_bool use_internal = SDL_TRUE;
+
+    if (libname) {
+        entry = (SDL_DYNAPI_ENTRYFN) get_sdlapi_entry(libname, "SDL_DYNAPI_entry");
+        if (!entry) {
+            dynapi_warn("Couldn't load overriding SDL library. Please fix or remove the " SDL_DYNAMIC_API_ENVVAR " environment variable. Using the default SDL.");
+            /* Just fill in the function pointers from this library, later. */
+        }
+    }
+
+    if (entry) {
+        if (entry(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) {
+            dynapi_warn("Couldn't override SDL library. Using a newer SDL build might help. Please fix or remove the " SDL_DYNAMIC_API_ENVVAR " environment variable. Using the default SDL.");
+            /* Just fill in the function pointers from this library, later. */
+        } else {
+            use_internal = SDL_FALSE;   /* We overrode SDL! Don't use the internal version! */
+        }
+    }
+
+    /* Just fill in the function pointers from this library. */
+    if (use_internal) {
+        if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) {
+            /* Now we're screwed. Should definitely abort now. */
+            dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now.");
+            DynApiExitProcess(86);
+        }
+    }
+
+    /* we intentionally never close the newly-loaded lib, of course. */
+}
+
+static void
+SDL_InitDynamicAPI(void)
+{
+    /* So the theory is that every function in the jump table defaults to
+     *  calling this function, and then replaces itself with a version that
+     *  doesn't call this function anymore. But it's possible that, in an
+     *  extreme corner case, you can have a second thread hit this function
+     *  while the jump table is being initialized by the first.
+     * In this case, a spinlock is really painful compared to what spinlocks
+     *  _should_ be used for, but this would only happen once, and should be
+     *  insanely rare, as you would have to spin a thread outside of SDL (as
+     *  SDL_CreateThread() would also call this function before building the
+     *  new thread).
+     */
+    static SDL_bool already_initialized = SDL_FALSE;
+
+    /* SDL_AtomicLock calls SDL mutex functions to emulate if
+       SDL_ATOMIC_DISABLED, which we can't do here, so in such a
+       configuration, you're on your own. */
+    #if !SDL_ATOMIC_DISABLED
+    static SDL_SpinLock lock = 0;
+    SDL_AtomicLock_REAL(&lock);
+    #endif
+
+    if (!already_initialized) {
+        SDL_InitDynamicAPILocked();
+        already_initialized = SDL_TRUE;
+    }
+
+    #if !SDL_ATOMIC_DISABLED
+    SDL_AtomicUnlock_REAL(&lock);
+    #endif
+}
+
+#endif  /* SDL_DYNAMIC_API */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/dynapi/SDL_dynapi.h b/src/dynapi/SDL_dynapi.h
new file mode 100644
index 0000000..d719462
--- /dev/null
+++ b/src/dynapi/SDL_dynapi.h
@@ -0,0 +1,81 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_dynapi_h_
+#define SDL_dynapi_h_
+
+/* IMPORTANT:
+   This is the master switch to disabling the dynamic API. We made it so you
+   have to hand-edit an internal source file in SDL to turn it off; you
+   can do it if you want it badly enough, but hopefully you won't want to.
+   You should understand the ramifications of turning this off: it makes it
+   hard to update your SDL in the field, and impossible if you've statically
+   linked SDL into your app. Understand that platforms change, and if we can't
+   drop in an updated SDL, your application can definitely break some time
+   in the future, even if it's fine today.
+   To be sure, as new system-level video and audio APIs are introduced, an
+   updated SDL can transparently take advantage of them, but your program will
+   not without this feature. Think hard before turning it off.
+*/
+#ifdef SDL_DYNAMIC_API  /* Tried to force it on the command line? */
+#error Nope, you have to edit this file to force this off.
+#endif
+
+#ifdef __APPLE__
+#include "TargetConditionals.h"
+#endif
+
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE  /* probably not useful on iOS. */
+#define SDL_DYNAMIC_API 0
+#elif defined(__ANDROID__) /* probably not useful on Android. */
+#define SDL_DYNAMIC_API 0
+#elif defined(__native_client__) && __native_client__  /* probably not useful on NACL. */
+#define SDL_DYNAMIC_API 0
+#elif defined(__EMSCRIPTEN__) && __EMSCRIPTEN__  /* probably not useful on Emscripten. */
+#define SDL_DYNAMIC_API 0
+#elif defined(SDL_BUILDING_WINRT) && SDL_BUILDING_WINRT  /* probably not useful on WinRT, given current .dll loading restrictions */
+#define SDL_DYNAMIC_API 0
+#elif defined(__PS2__) && __PS2__
+#define SDL_DYNAMIC_API 0
+#elif defined(__PSP__) && __PSP__
+#define SDL_DYNAMIC_API 0
+#elif defined(__riscos__) && __riscos__ /* probably not useful on RISC OS, since dlopen() can't be used when using static linking. */
+#define SDL_DYNAMIC_API 0
+#elif defined(__clang_analyzer__)
+#define SDL_DYNAMIC_API 0  /* Turn off for static analysis, so reports are more clear. */
+#elif defined(__VITA__)
+#define SDL_DYNAMIC_API 0  /* vitasdk doesn't support dynamic linking */
+#elif defined(__NGAGE__)
+#define SDL_DYNAMIC_API 0  /* The N-Gage doesn't support dynamic linking either */
+#elif defined(__3DS__)
+#define SDL_DYNAMIC_API 0  /* devkitARM doesn't support dynamic linking */
+#elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN)
+#define SDL_DYNAMIC_API 0  /* we need dlopen(), but don't have it.... */
+#endif
+
+/* everyone else. This is where we turn on the API if nothing forced it off. */
+#ifndef SDL_DYNAMIC_API
+#define SDL_DYNAMIC_API 1
+#endif
+
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
new file mode 100644
index 0000000..d93d508
--- /dev/null
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -0,0 +1,896 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* vi: set ts=4 sw=4 expandtab: */
+
+/* DO NOT EDIT THIS FILE BY HAND. It is autogenerated by gendynapi.pl. */
+
+#if !SDL_DYNAMIC_API
+#error You should not be here.
+#endif
+
+#define SDL_SetError SDL_SetError_REAL
+#define SDL_Log SDL_Log_REAL
+#define SDL_LogVerbose SDL_LogVerbose_REAL
+#define SDL_LogDebug SDL_LogDebug_REAL
+#define SDL_LogInfo SDL_LogInfo_REAL
+#define SDL_LogWarn SDL_LogWarn_REAL
+#define SDL_LogError SDL_LogError_REAL
+#define SDL_LogCritical SDL_LogCritical_REAL
+#define SDL_LogMessage SDL_LogMessage_REAL
+#define SDL_sscanf SDL_sscanf_REAL
+#define SDL_snprintf SDL_snprintf_REAL
+#define SDL_CreateThread SDL_CreateThread_REAL
+#define SDL_RWFromFP SDL_RWFromFP_REAL
+#define SDL_RegisterApp SDL_RegisterApp_REAL
+#define SDL_UnregisterApp SDL_UnregisterApp_REAL
+#define SDL_Direct3D9GetAdapterIndex SDL_Direct3D9GetAdapterIndex_REAL
+#define SDL_RenderGetD3D9Device SDL_RenderGetD3D9Device_REAL
+#define SDL_iPhoneSetAnimationCallback SDL_iPhoneSetAnimationCallback_REAL
+#define SDL_iPhoneSetEventPump SDL_iPhoneSetEventPump_REAL
+#define SDL_AndroidGetJNIEnv SDL_AndroidGetJNIEnv_REAL
+#define SDL_AndroidGetActivity SDL_AndroidGetActivity_REAL
+#define SDL_AndroidGetInternalStoragePath SDL_AndroidGetInternalStoragePath_REAL
+#define SDL_AndroidGetExternalStorageState SDL_AndroidGetExternalStorageState_REAL
+#define SDL_AndroidGetExternalStoragePath SDL_AndroidGetExternalStoragePath_REAL
+#define SDL_Init SDL_Init_REAL
+#define SDL_InitSubSystem SDL_InitSubSystem_REAL
+#define SDL_QuitSubSystem SDL_QuitSubSystem_REAL
+#define SDL_WasInit SDL_WasInit_REAL
+#define SDL_Quit SDL_Quit_REAL
+#define SDL_ReportAssertion SDL_ReportAssertion_REAL
+#define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL
+#define SDL_GetAssertionReport SDL_GetAssertionReport_REAL
+#define SDL_ResetAssertionReport SDL_ResetAssertionReport_REAL
+#define SDL_AtomicTryLock SDL_AtomicTryLock_REAL
+#define SDL_AtomicLock SDL_AtomicLock_REAL
+#define SDL_AtomicUnlock SDL_AtomicUnlock_REAL
+#define SDL_AtomicCAS SDL_AtomicCAS_REAL
+#define SDL_AtomicSet SDL_AtomicSet_REAL
+#define SDL_AtomicGet SDL_AtomicGet_REAL
+#define SDL_AtomicAdd SDL_AtomicAdd_REAL
+#define SDL_AtomicCASPtr SDL_AtomicCASPtr_REAL
+#define SDL_AtomicSetPtr SDL_AtomicSetPtr_REAL
+#define SDL_AtomicGetPtr SDL_AtomicGetPtr_REAL
+#define SDL_GetNumAudioDrivers SDL_GetNumAudioDrivers_REAL
+#define SDL_GetAudioDriver SDL_GetAudioDriver_REAL
+#define SDL_AudioInit SDL_AudioInit_REAL
+#define SDL_AudioQuit SDL_AudioQuit_REAL
+#define SDL_GetCurrentAudioDriver SDL_GetCurrentAudioDriver_REAL
+#define SDL_OpenAudio SDL_OpenAudio_REAL
+#define SDL_GetNumAudioDevices SDL_GetNumAudioDevices_REAL
+#define SDL_GetAudioDeviceName SDL_GetAudioDeviceName_REAL
+#define SDL_OpenAudioDevice SDL_OpenAudioDevice_REAL
+#define SDL_GetAudioStatus SDL_GetAudioStatus_REAL
+#define SDL_GetAudioDeviceStatus SDL_GetAudioDeviceStatus_REAL
+#define SDL_PauseAudio SDL_PauseAudio_REAL
+#define SDL_PauseAudioDevice SDL_PauseAudioDevice_REAL
+#define SDL_LoadWAV_RW SDL_LoadWAV_RW_REAL
+#define SDL_FreeWAV SDL_FreeWAV_REAL
+#define SDL_BuildAudioCVT SDL_BuildAudioCVT_REAL
+#define SDL_ConvertAudio SDL_ConvertAudio_REAL
+#define SDL_MixAudio SDL_MixAudio_REAL
+#define SDL_MixAudioFormat SDL_MixAudioFormat_REAL
+#define SDL_LockAudio SDL_LockAudio_REAL
+#define SDL_LockAudioDevice SDL_LockAudioDevice_REAL
+#define SDL_UnlockAudio SDL_UnlockAudio_REAL
+#define SDL_UnlockAudioDevice SDL_UnlockAudioDevice_REAL
+#define SDL_CloseAudio SDL_CloseAudio_REAL
+#define SDL_CloseAudioDevice SDL_CloseAudioDevice_REAL
+#define SDL_SetClipboardText SDL_SetClipboardText_REAL
+#define SDL_GetClipboardText SDL_GetClipboardText_REAL
+#define SDL_HasClipboardText SDL_HasClipboardText_REAL
+#define SDL_GetCPUCount SDL_GetCPUCount_REAL
+#define SDL_GetCPUCacheLineSize SDL_GetCPUCacheLineSize_REAL
+#define SDL_HasRDTSC SDL_HasRDTSC_REAL
+#define SDL_HasAltiVec SDL_HasAltiVec_REAL
+#define SDL_HasMMX SDL_HasMMX_REAL
+#define SDL_Has3DNow SDL_Has3DNow_REAL
+#define SDL_HasSSE SDL_HasSSE_REAL
+#define SDL_HasSSE2 SDL_HasSSE2_REAL
+#define SDL_HasSSE3 SDL_HasSSE3_REAL
+#define SDL_HasSSE41 SDL_HasSSE41_REAL
+#define SDL_HasSSE42 SDL_HasSSE42_REAL
+#define SDL_GetSystemRAM SDL_GetSystemRAM_REAL
+#define SDL_GetError SDL_GetError_REAL
+#define SDL_ClearError SDL_ClearError_REAL
+#define SDL_Error SDL_Error_REAL
+#define SDL_PumpEvents SDL_PumpEvents_REAL
+#define SDL_PeepEvents SDL_PeepEvents_REAL
+#define SDL_HasEvent SDL_HasEvent_REAL
+#define SDL_HasEvents SDL_HasEvents_REAL
+#define SDL_FlushEvent SDL_FlushEvent_REAL
+#define SDL_FlushEvents SDL_FlushEvents_REAL
+#define SDL_PollEvent SDL_PollEvent_REAL
+#define SDL_WaitEvent SDL_WaitEvent_REAL
+#define SDL_WaitEventTimeout SDL_WaitEventTimeout_REAL
+#define SDL_PushEvent SDL_PushEvent_REAL
+#define SDL_SetEventFilter SDL_SetEventFilter_REAL
+#define SDL_GetEventFilter SDL_GetEventFilter_REAL
+#define SDL_AddEventWatch SDL_AddEventWatch_REAL
+#define SDL_DelEventWatch SDL_DelEventWatch_REAL
+#define SDL_FilterEvents SDL_FilterEvents_REAL
+#define SDL_EventState SDL_EventState_REAL
+#define SDL_RegisterEvents SDL_RegisterEvents_REAL
+#define SDL_GetBasePath SDL_GetBasePath_REAL
+#define SDL_GetPrefPath SDL_GetPrefPath_REAL
+#define SDL_GameControllerAddMapping SDL_GameControllerAddMapping_REAL
+#define SDL_GameControllerMappingForGUID SDL_GameControllerMappingForGUID_REAL
+#define SDL_GameControllerMapping SDL_GameControllerMapping_REAL
+#define SDL_IsGameController SDL_IsGameController_REAL
+#define SDL_GameControllerNameForIndex SDL_GameControllerNameForIndex_REAL
+#define SDL_GameControllerOpen SDL_GameControllerOpen_REAL
+#define SDL_GameControllerName SDL_GameControllerName_REAL
+#define SDL_GameControllerGetAttached SDL_GameControllerGetAttached_REAL
+#def

(Patch may be truncated, please check the link at the top of this post.)