SDL: Added SDL_swprintf() and SDL_vswprintf()

From c9d8a04945570b47cc551ca1dc2c6d3ce74528ec Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 24 May 2023 09:41:22 -0700
Subject: [PATCH] Added SDL_swprintf() and SDL_vswprintf()

---
 include/SDL3/SDL_stdinc.h         |   8 ++
 src/dynapi/SDL_dynapi.c           |  10 ++
 src/dynapi/SDL_dynapi.sym         |   2 +
 src/dynapi/SDL_dynapi_overrides.h |   2 +
 src/dynapi/SDL_dynapi_procs.h     |   2 +
 src/dynapi/gendynapi.py           |   1 +
 src/stdlib/SDL_string.c           |  55 ++++++++++
 test/testautomation_stdlib.c      | 172 +++++++++++++++++++++++++++++-
 8 files changed, 250 insertions(+), 2 deletions(-)

diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index 61fc27952f19..7f82956d3fa5 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -286,6 +286,8 @@ typedef uint64_t Uint64;
 #define SDL_SCANF_FORMAT_STRING
 #define SDL_PRINTF_VARARG_FUNC( fmtargnumber )
 #define SDL_SCANF_VARARG_FUNC( fmtargnumber )
+#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber )
+#define SDL_WSCANF_VARARG_FUNC( fmtargnumber )
 #else
 #if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */
 #include <sal.h>
@@ -312,9 +314,13 @@ typedef uint64_t Uint64;
 #ifdef __GNUC__
 #define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 )))
 #define SDL_SCANF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __scanf__, fmtargnumber, fmtargnumber+1 )))
+#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, fmtargnumber+1 ))) */
+#define SDL_WSCANF_VARARG_FUNC( fmtargnumber ) /* __attribute__ (( format( __wscanf__, fmtargnumber, fmtargnumber+1 ))) */
 #else
 #define SDL_PRINTF_VARARG_FUNC( fmtargnumber )
 #define SDL_SCANF_VARARG_FUNC( fmtargnumber )
+#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber )
+#define SDL_WSCANF_VARARG_FUNC( fmtargnumber )
 #endif
 #endif /* SDL_DISABLE_ANALYZE_MACROS */
 
@@ -577,7 +583,9 @@ extern DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2,
 extern DECLSPEC int SDLCALL SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...) SDL_SCANF_VARARG_FUNC(2);
 extern DECLSPEC int SDLCALL SDL_vsscanf(const char *text, const char *fmt, va_list ap);
 extern DECLSPEC int SDLCALL SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ... ) SDL_PRINTF_VARARG_FUNC(3);
+extern DECLSPEC int SDLCALL SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ... ) SDL_WPRINTF_VARARG_FUNC(3);
 extern DECLSPEC int SDLCALL SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap);
+extern DECLSPEC int SDLCALL SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, const wchar_t *fmt, va_list ap);
 extern DECLSPEC int SDLCALL SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2);
 extern DECLSPEC int SDLCALL SDL_vasprintf(char **strp, const char *fmt, va_list ap);
 
diff --git a/src/dynapi/SDL_dynapi.c b/src/dynapi/SDL_dynapi.c
index cc97d232fc3b..d09fe9e6ef08 100644
--- a/src/dynapi/SDL_dynapi.c
+++ b/src/dynapi/SDL_dynapi.c
@@ -123,6 +123,16 @@ static void SDL_InitDynamicAPI(void);
         va_end(ap);                                                                                                                       \
         return retval;                                                                                                                    \
     }                                                                                                                                     \
+    _static int SDLCALL SDL_swprintf##name(SDL_OUT_Z_CAP(maxlen) wchar_t *buf, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...) \
+    {                                                                                                                                     \
+        int retval;                                                                                                                       \
+        va_list ap;                                                                                                                       \
+        initcall;                                                                                                                         \
+        va_start(ap, fmt);                                                                                                                \
+        retval = jump_table.SDL_vswprintf(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;                                                                                                                       \
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index c5bf0b0bc3fc..c306f532f758 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -859,6 +859,8 @@ SDL3_0.0.0 {
     SDL_GetWindowDisplayScale;
     SDL_GetWindowPixelDensity;
     SDL_wcstol;
+    SDL_swprintf;
+    SDL_vswprintf;
     # 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 40db83c912ce..7da9a97e5722 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -885,3 +885,5 @@
 #define SDL_GetWindowDisplayScale SDL_GetWindowDisplayScale_REAL
 #define SDL_GetWindowPixelDensity SDL_GetWindowPixelDensity_REAL
 #define SDL_wcstol SDL_wcstol_REAL
+#define SDL_swprintf SDL_swprintf_REAL
+#define SDL_vswprintf SDL_vswprintf_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index fa68b8969313..9722e2169f7e 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -41,6 +41,7 @@ SDL_DYNAPI_PROC(void,SDL_LogWarn,(int a, SDL_PRINTF_FORMAT_STRING const char *b,
 SDL_DYNAPI_PROC(int,SDL_SetError,(SDL_PRINTF_FORMAT_STRING const char *a, ...),(a),return)
 SDL_DYNAPI_PROC(int,SDL_asprintf,(char **a, SDL_PRINTF_FORMAT_STRING const char *b, ...),(a,b),return)
 SDL_DYNAPI_PROC(int,SDL_snprintf,(SDL_OUT_Z_CAP(b) char *a, size_t b, SDL_PRINTF_FORMAT_STRING const char *c, ...),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_swprintf,(SDL_OUT_Z_CAP(b) wchar_t *a, size_t b, SDL_PRINTF_FORMAT_STRING const wchar_t *c, ...),(a,b,c),return)
 SDL_DYNAPI_PROC(int,SDL_sscanf,(const char *a, SDL_SCANF_FORMAT_STRING const char *b, ...),(a,b),return)
 #endif
 
@@ -930,3 +931,4 @@ SDL_DYNAPI_PROC(float,SDL_GetDisplayContentScale,(SDL_DisplayID a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_GetWindowDisplayScale,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_GetWindowPixelDensity,(SDL_Window *a),(a),return)
 SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return)
+SDL_DYNAPI_PROC(int,SDL_vswprintf,(wchar_t *a, size_t b, const wchar_t *c, va_list d),(a,b,c,d),return)
diff --git a/src/dynapi/gendynapi.py b/src/dynapi/gendynapi.py
index ada149a826f3..0f6284ab268e 100755
--- a/src/dynapi/gendynapi.py
+++ b/src/dynapi/gendynapi.py
@@ -144,6 +144,7 @@ def main():
             func = func.replace("SDL_PRINTF_VARARG_FUNC(1)", "");
             func = func.replace("SDL_PRINTF_VARARG_FUNC(2)", "");
             func = func.replace("SDL_PRINTF_VARARG_FUNC(3)", "");
+            func = func.replace("SDL_WPRINTF_VARARG_FUNC(3)", "");
             func = func.replace("SDL_SCANF_VARARG_FUNC(2)", "");
             func = func.replace("__attribute__((analyzer_noreturn))", "");
             func = func.replace("SDL_MALLOC", "");
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index b6aae10f51c6..95c19fcab664 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -1466,6 +1466,18 @@ int SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FOR
     return retval;
 }
 
+int SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...)
+{
+    va_list ap;
+    int retval;
+
+    va_start(ap, fmt);
+    retval = SDL_vswprintf(text, maxlen, fmt, ap);
+    va_end(ap);
+
+    return retval;
+}
+
 #if defined(HAVE_LIBC) && defined(__WATCOMC__)
 /* _vsnprintf() doesn't ensure nul termination */
 int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, va_list ap)
@@ -1979,6 +1991,49 @@ int SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *f
 #undef TEXT_AND_LEN_ARGS
 #endif /* HAVE_VSNPRINTF */
 
+int SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, va_list ap)
+{
+    char *text_utf8 = NULL, *fmt_utf8 = NULL;
+    int retval;
+
+    if (fmt) {
+        fmt_utf8 = SDL_iconv_string("UTF-8", "WCHAR_T", (const char *)fmt, (SDL_wcslen(fmt) + 1) * sizeof(wchar_t));
+        if (!fmt_utf8) {
+            return -1;
+        }
+    }
+
+    if (!maxlen) {
+        /* We still need to generate the text to find the final text length */
+        maxlen = 1024;
+    }
+    text_utf8 = (char *)SDL_malloc(maxlen * 4);
+    if (!text_utf8) {
+        SDL_free(fmt_utf8);
+        return -1;
+    }
+
+    retval = SDL_vsnprintf(text_utf8, maxlen * 4, fmt_utf8, ap);
+
+    if (retval >= 0) {
+        wchar_t *text_wchar =  (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", text_utf8, SDL_strlen(text_utf8) + 1);
+        if (text_wchar) {
+            if (text) {
+                SDL_wcslcpy(text, text_wchar, maxlen);
+            }
+            retval = (int)SDL_wcslen(text_wchar);
+            SDL_free(text_wchar);
+        } else {
+            retval = -1;
+        }
+    }
+
+    SDL_free(text_utf8);
+    SDL_free(fmt_utf8);
+
+    return retval;
+}
+
 int SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
 {
     va_list ap;
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index d4ab8d25dcb5..448853a36e22 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -205,6 +205,169 @@ static int stdlib_snprintf(void *arg)
     return TEST_COMPLETED;
 }
 
+/**
+ * \brief Call to SDL_swprintf
+ */
+#undef SDL_swprintf
+static int stdlib_swprintf(void *arg)
+{
+    int result;
+    int predicted;
+    wchar_t text[1024];
+    const wchar_t *expected;
+    size_t size;
+
+    result = SDL_swprintf(text, sizeof(text), L"%s", "foo");
+    expected = L"foo";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%s\", \"foo\")");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+
+    result = SDL_swprintf(text, 2, L"%s", "foo");
+    expected = L"f";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%s\", \"foo\") with buffer size 2");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == 3, "Check result value, expected: 3, got: %d", result);
+
+    result = SDL_swprintf(NULL, 0, L"%s", "foo");
+    SDLTest_AssertPass("Call to SDL_swprintf(NULL, 0, \"%%s\", \"foo\")");
+    SDLTest_AssertCheck(result == 3, "Check result value, expected: 3, got: %d", result);
+
+    result = SDL_swprintf(text, 2, L"%s\n", "foo");
+    expected = L"f";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%s\\n\", \"foo\") with buffer size 2");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == 4, "Check result value, expected: 4, got: %d", result);
+
+    result = SDL_swprintf(text, sizeof(text), L"%f", 0.0);
+    predicted = SDL_swprintf(NULL, 0, L"%f", 0.0);
+    expected = L"0.000000";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%f\", 0.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%f", 1.0);
+    predicted = SDL_swprintf(NULL, 0, L"%f", 1.0);
+    expected = L"1.000000";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%f\", 1.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%.f", 1.0);
+    predicted = SDL_swprintf(NULL, 0, L"%.f", 1.0);
+    expected = L"1";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%.f\", 1.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%#.f", 1.0);
+    predicted = SDL_swprintf(NULL, 0, L"%#.f", 1.0);
+    expected = L"1.";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%#.f\", 1.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%f", 1.0 + 1.0 / 3.0);
+    predicted = SDL_swprintf(NULL, 0, L"%f", 1.0 + 1.0 / 3.0);
+    expected = L"1.333333";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%f\", 1.0 + 1.0 / 3.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%+f", 1.0 + 1.0 / 3.0);
+    predicted = SDL_swprintf(NULL, 0, L"%+f", 1.0 + 1.0 / 3.0);
+    expected = L"+1.333333";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%+f\", 1.0 + 1.0 / 3.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%.2f", 1.0 + 1.0 / 3.0);
+    predicted = SDL_swprintf(NULL, 0, L"%.2f", 1.0 + 1.0 / 3.0);
+    expected = L"1.33";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%.2f\", 1.0 + 1.0 / 3.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: %s, got: %s", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%6.2f", 1.0 + 1.0 / 3.0);
+    predicted = SDL_swprintf(NULL, 0, L"%6.2f", 1.0 + 1.0 / 3.0);
+    expected = L"  1.33";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%6.2f\", 1.0 + 1.0 / 3.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, sizeof(text), L"%06.2f", 1.0 + 1.0 / 3.0);
+    predicted = SDL_swprintf(NULL, 0, L"%06.2f", 1.0 + 1.0 / 3.0);
+    expected = L"001.33";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%06.2f\", 1.0 + 1.0 / 3.0)");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+    SDLTest_AssertCheck(result == SDL_wcslen(text), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(text), result);
+    SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+    result = SDL_swprintf(text, 5, L"%06.2f", 1.0 + 1.0 / 3.0);
+    expected = L"001.";
+    SDLTest_AssertPass("Call to SDL_swprintf(\"%%06.2f\", 1.0 + 1.0 / 3.0) with buffer size 5");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+    SDLTest_AssertCheck(result == 6, "Check result value, expected: 6, got: %d", result);
+
+    {
+        static struct
+        {
+            float value;
+            const wchar_t *expected_f;
+            const wchar_t *expected_g;
+        } f_and_g_test_cases[] = {
+            { 100.0f, L"100.000000", L"100" },
+            { -100.0f, L"-100.000000", L"-100" },
+            { 100.75f, L"100.750000", L"100.75" },
+            { -100.75f, L"-100.750000", L"-100.75" },
+            { ((100 * 60 * 1000) / 1001) / 100.0f, L"59.939999", L"59.94" },
+            { -((100 * 60 * 1000) / 1001) / 100.0f, L"-59.939999", L"-59.94" },
+            { ((100 * 120 * 1000) / 1001) / 100.0f, L"119.879997", L"119.88" },
+            { -((100 * 120 * 1000) / 1001) / 100.0f, L"-119.879997", L"-119.88" },
+            { 9.9999999f, L"10.000000", L"10" },
+            { -9.9999999f, L"-10.000000", L"-10" },
+        };
+        int i;
+
+        for (i = 0; i < SDL_arraysize(f_and_g_test_cases); ++i) {
+            float value = f_and_g_test_cases[i].value;
+
+            result = SDL_swprintf(text, sizeof(text), L"%f", value);
+            predicted = SDL_swprintf(NULL, 0, L"%f", value);
+            expected = f_and_g_test_cases[i].expected_f;
+            SDLTest_AssertPass("Call to SDL_swprintf(\"%%f\", %g)", value);
+            SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+            SDLTest_AssertCheck(result == SDL_wcslen(expected), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(expected), result);
+            SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+
+            result = SDL_swprintf(text, sizeof(text), L"%g", value);
+            predicted = SDL_swprintf(NULL, 0, L"%g", value);
+            expected = f_and_g_test_cases[i].expected_g;
+            SDLTest_AssertPass("Call to SDL_swprintf(\"%%g\", %g)", value);
+            SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+            SDLTest_AssertCheck(result == SDL_wcslen(expected), "Check result value, expected: %d, got: %d", (int)SDL_wcslen(expected), result);
+            SDLTest_AssertCheck(predicted == result, "Check predicted value, expected: %d, got: %d", result, predicted);
+        }
+    }
+
+    size = 64;
+    result = SDL_swprintf(text, sizeof(text), L"%zu %s", size, "test");
+    expected = L"64 test";
+    SDLTest_AssertPass("Call to SDL_swprintf(text, sizeof(text), \"%%zu %%s\", size, \"test\")");
+    SDLTest_AssertCheck(SDL_wcscmp(text, expected) == 0, "Check text, expected: '%s', got: '%s'", expected, text);
+    SDLTest_AssertCheck(result == 7, "Check result value, expected: 7, got: %d", result);
+
+    return TEST_COMPLETED;
+}
+
 #if defined(HAVE_WFORMAT) || defined(HAVE_WFORMAT_EXTRA_ARGS)
 #pragma GCC diagnostic pop
 #endif
@@ -635,14 +798,18 @@ static const SDLTest_TestCaseReference stdlibTest2 = {
 };
 
 static const SDLTest_TestCaseReference stdlibTest3 = {
-    stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED
+    stdlib_swprintf, "stdlib_swprintf", "Call to SDL_swprintf", TEST_ENABLED
 };
 
 static const SDLTest_TestCaseReference stdlibTest4 = {
-    stdlib_sscanf, "stdlib_sscanf", "Call to SDL_sscanf", TEST_ENABLED
+    stdlib_getsetenv, "stdlib_getsetenv", "Call to SDL_getenv and SDL_setenv", TEST_ENABLED
 };
 
 static const SDLTest_TestCaseReference stdlibTest5 = {
+    stdlib_sscanf, "stdlib_sscanf", "Call to SDL_sscanf", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference stdlibTest6 = {
     stdlib_aligned_alloc, "stdlib_aligned_alloc", "Call to SDL_aligned_alloc", TEST_ENABLED
 };
 
@@ -657,6 +824,7 @@ static const SDLTest_TestCaseReference *stdlibTests[] = {
     &stdlibTest3,
     &stdlibTest4,
     &stdlibTest5,
+    &stdlibTest6,
     &stdlibTestOverflow,
     NULL
 };