#include "SDL_config.h"
#include "SDL_dynapi.h"
#if SDL_DYNAMIC_API
#if defined(__OS2__)
#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>
#include <dos.h>
#endif
#include "SDL.h"
#include "SDL_syswm.h"
#include "SDL_vulkan.h"
#define SDL_DYNAPI_VERSION 1
static void SDL_InitDynamicAPI(void);
#define DISABLE_JUMP_MAGIC 1
#if DISABLE_JUMP_MAGIC
#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
#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
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;
#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) static rc SDLCALL fn##_DEFAULT params;
#include "SDL_dynapi_procs.h"
#undef SDL_DYNAPI_PROC
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
};
#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
#error Write me.
#endif
#if DISABLE_JUMP_MAGIC
#define SDL_DYNAPI_PROC(rc,fn,params,args,ret) \
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(,,)
#else
#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]; \
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
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) {
return -1;
} else if (tablesize > sizeof (jump_table)) {
return -1;
}
#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
if (output_jump_table != &jump_table) {
jump_table.SDL_memcpy(output_jump_table, &jump_table, tablesize);
}
return 0;
}
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);
}
#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 <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!";
#if (defined(WIN32) || defined(_WIN32) || defined(__CYGWIN__)) && !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
MessageBoxA(NULL, msg, caption, MB_OK | MB_ICONERROR);
#elif defined(HAVE_STDIO_H)
fprintf(stderr, "\n\n%s\n%s\n\n", caption, msg);
fflush(stderr);
#endif
}
#if defined(__WATCOMC__)
void SDL_ExitProcess(int exitcode);
#pragma aux SDL_ExitProcess aborts;
#endif
SDL_NORETURN void SDL_ExitProcess(int exitcode);
static void
SDL_InitDynamicAPILocked(void)
{
const char *libname = SDL_getenv_REAL("SDL_DYNAMIC_API");
SDL_DYNAPI_ENTRYFN entry = NULL;
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 environment variable. Using the default SDL.");
}
}
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 environment variable. Using the default SDL.");
} else {
use_internal = SDL_FALSE;
}
}
if (use_internal) {
if (initialize_jumptable(SDL_DYNAPI_VERSION, &jump_table, sizeof (jump_table)) < 0) {
dynapi_warn("Failed to initialize internal SDL dynapi. As this would otherwise crash, we have to abort now.");
SDL_ExitProcess(86);
}
}
}
static void
SDL_InitDynamicAPI(void)
{
static SDL_bool already_initialized = SDL_FALSE;
#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