Bladeren bron

Refactoring: move GUID operations out of Joystick

- SDL_JoystickGUID -> SDL_GUID (though we retain a type alias)
- Operations for GUID <-> String ops are now in
  src/SDL_guid.c and include/SDL_guid.h
- The corresponding Joystick operations delegate to SDL_guid.c
- Added test/testguid.c
Christoph Reichenbach 2 jaren geleden
bovenliggende
commit
3a20274ddf

+ 1 - 0
Makefile.in

@@ -80,6 +80,7 @@ HDRS = \
 	SDL_filesystem.h \
 	SDL_gamecontroller.h \
 	SDL_gesture.h \
+	SDL_guid.h \
 	SDL_haptic.h \
 	SDL_hidapi.h \
 	SDL_hints.h \

+ 1 - 1
Makefile.os2

@@ -67,7 +67,7 @@ CFLAGS_DLL+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION)
 CFLAGS_DLL+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION)
 CFLAGS_DLL+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION)
 
-SRCS = SDL.c SDL_assert.c SDL_error.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
+SRCS = SDL.c SDL_assert.c SDL_error.c SDL_guid.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
 SRCS+= SDL_getenv.c SDL_iconv.c SDL_malloc.c SDL_qsort.c SDL_stdlib.c SDL_string.c SDL_strtokr.c SDL_crc32.c
 SRCS+= SDL_cpuinfo.c SDL_atomic.c SDL_spinlock.c SDL_thread.c SDL_timer.c
 SRCS+= SDL_rwops.c SDL_power.c

+ 1 - 1
Makefile.w32

@@ -43,7 +43,7 @@ CFLAGS_DLL+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION)
 
 RCFLAGS = -q -r -bt=nt $(INCPATH)
 
-SRCS = SDL.c SDL_assert.c SDL_error.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
+SRCS = SDL.c SDL_assert.c SDL_error.c SDL_guid.c SDL_log.c SDL_dataqueue.c SDL_hints.c SDL_list.c
 SRCS+= SDL_getenv.c SDL_iconv.c SDL_malloc.c SDL_qsort.c SDL_stdlib.c SDL_string.c SDL_strtokr.c SDL_crc32.c
 SRCS+= SDL_cpuinfo.c SDL_atomic.c SDL_spinlock.c SDL_thread.c SDL_timer.c
 SRCS+= SDL_rwops.c SDL_power.c

+ 2 - 0
VisualC-WinRT/SDL-UWP.vcxproj

@@ -45,6 +45,7 @@
     <ClInclude Include="..\include\SDL_error.h" />
     <ClInclude Include="..\include\SDL_events.h" />
     <ClInclude Include="..\include\SDL_filesystem.h" />
+    <ClInclude Include="..\include\SDL_guid.h" />
     <ClInclude Include="..\include\SDL_haptic.h" />
     <ClInclude Include="..\include\SDL_hints.h" />
     <ClInclude Include="..\include\SDL_hidapi.h" />
@@ -301,6 +302,7 @@
     <ClCompile Include="..\src\SDL_dataqueue.c" />
     <ClCompile Include="..\src\SDL_list.c" />
     <ClCompile Include="..\src\SDL_error.c" />
+    <ClCompile Include="..\src\SDL_guid.c" />
     <ClCompile Include="..\src\SDL_hints.c" />
     <ClCompile Include="..\src\SDL_log.c" />
     <ClCompile Include="..\src\sensor\dummy\SDL_dummysensor.c" />

+ 6 - 0
VisualC-WinRT/SDL-UWP.vcxproj.filters

@@ -63,6 +63,9 @@
     <ClInclude Include="..\include\SDL_filesystem.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\include\SDL_guid.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="..\include\SDL_haptic.h">
       <Filter>Header Files</Filter>
     </ClInclude>
@@ -633,6 +636,9 @@
     <ClCompile Include="..\src\SDL_error.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="..\src\SDL_guid.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="..\src\SDL_hints.c">
       <Filter>Source Files</Filter>
     </ClCompile>

+ 2 - 0
VisualC/SDL/SDL.vcxproj

@@ -234,6 +234,7 @@
     <ClInclude Include="..\..\include\SDL_filesystem.h" />
     <ClInclude Include="..\..\include\SDL_gamecontroller.h" />
     <ClInclude Include="..\..\include\SDL_gesture.h" />
+    <ClInclude Include="..\..\include\SDL_guid.h" />
     <ClInclude Include="..\..\include\SDL_haptic.h" />
     <ClInclude Include="..\..\include\SDL_hints.h" />
     <ClInclude Include="..\..\include\SDL_hidapi.h" />
@@ -367,6 +368,7 @@
     <ClInclude Include="..\..\src\SDL_assert_c.h" />
     <ClInclude Include="..\..\src\SDL_dataqueue.h" />
     <ClInclude Include="..\..\src\SDL_error_c.h" />
+    <ClCompile Include="..\..\src\SDL_guid.c" />
     <ClInclude Include="..\..\src\SDL_hints_c.h" />
     <ClInclude Include="..\..\src\SDL_internal.h" />
     <ClInclude Include="..\..\src\SDL_list.h" />

+ 4 - 0
VisualC/SDL/SDL.vcxproj.filters

@@ -231,6 +231,9 @@
     <ClInclude Include="..\..\include\SDL_gesture.h">
       <Filter>API Headers</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\include\SDL_guid.h">
+      <Filter>API Headers</Filter>
+    </ClInclude>
     <ClInclude Include="..\..\include\SDL_haptic.h">
       <Filter>API Headers</Filter>
     </ClInclude>
@@ -833,6 +836,7 @@
     <ClCompile Include="..\..\src\SDL_assert.c" />
     <ClCompile Include="..\..\src\SDL_dataqueue.c" />
     <ClCompile Include="..\..\src\SDL_error.c" />
+    <ClCompile Include="..\..\src\SDL_guid.c" />
     <ClCompile Include="..\..\src\SDL_hints.c" />
     <ClCompile Include="..\..\src\SDL_list.c" />
     <ClCompile Include="..\..\src\audio\SDL_audio.c">

+ 100 - 0
include/SDL_guid.h

@@ -0,0 +1,100 @@
+/*
+  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.
+*/
+
+/**
+ *  \file SDL_guid.h
+ *
+ *  Include file for handling ::SDL_GUID values.
+ */
+
+#ifndef SDL_guid_h_
+#define SDL_guid_h_
+
+#include "SDL_stdinc.h"
+#include "SDL_error.h"
+
+#include "begin_code.h"
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * An SDL_GUID is a 128-bit identifier for an input device that
+ *   identifies that device across runs of SDL programs on the same
+ *   platform.  If the device is detached and then re-attached to a
+ *   different port, or if the base system is rebooted, the device
+ *   should still report the same GUID.
+ *
+ * GUIDs are as precise as possible but are not guaranteed to
+ *   distinguish physically distinct but equivalent devices.  For
+ *   example, two game controllers from the same vendor with the same
+ *   product ID and revision may have the same GUID.
+ *
+ * GUIDs may be platform-dependent (i.e., the same device may report
+ *   different GUIDs on different operating systems).
+ */
+typedef struct {
+    Uint8 data[16];
+} SDL_GUID;
+
+/* Function prototypes */
+
+/**
+ * Get an ASCII string representation for a given ::SDL_GUID.
+ *
+ * You should supply at least 33 bytes for pszGUID.
+ *
+ * \param guid the ::SDL_GUID you wish to convert to string
+ * \param pszGUID buffer in which to write the ASCII string
+ * \param cbGUID the size of pszGUID
+ *
+ * \since This function is available since SDL 2.tbd.
+ *
+ * \sa SDL_GUIDFromString
+ */
+extern DECLSPEC void SDLCALL SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID);
+
+/**
+ * Convert a GUID string into a ::SDL_GUID structure.
+ *
+ * Performs no error checking. If this function is given a string containing
+ * an invalid GUID, the function will silently succeed, but the GUID generated
+ * will not be useful.
+ *
+ * \param pchGUID string containing an ASCII representation of a GUID
+ * \returns a ::SDL_GUID structure.
+ *
+ * \since This function is available since SDL 2.tbd.
+ *
+ * \sa SDL_GUIDToString
+ */
+extern DECLSPEC SDL_GUID SDLCALL SDL_GUIDFromString(const char *pchGUID);
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include "close_code.h"
+
+#endif /* SDL_guid_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

+ 2 - 3
include/SDL_joystick.h

@@ -43,6 +43,7 @@
 
 #include "SDL_stdinc.h"
 #include "SDL_error.h"
+#include "SDL_guid.h"
 
 #include "begin_code.h"
 /* Set up for C function definitions, even when using C++ */
@@ -69,9 +70,7 @@ struct _SDL_Joystick;
 typedef struct _SDL_Joystick SDL_Joystick;
 
 /* A structure that encodes the stable unique id for a joystick device */
-typedef struct {
-    Uint8 data[16];
-} SDL_JoystickGUID;
+typedef SDL_GUID SDL_JoystickGUID;
 
 /**
  * This is a unique ID for a joystick for the time it is connected to the system,

+ 91 - 0
src/SDL_guid.c

@@ -0,0 +1,91 @@
+/*
+  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 "SDL_internal.h"
+
+
+#include "SDL_guid.h"
+
+/* convert the guid to a printable string */
+void SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)
+{
+    static const char k_rgchHexToASCII[] = "0123456789abcdef";
+    int i;
+
+    if ((pszGUID == NULL) || (cbGUID <= 0)) {
+        return;
+    }
+
+    for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
+        /* each input byte writes 2 ascii chars, and might write a null byte. */
+        /* If we don't have room for next input byte, stop */
+        unsigned char c = guid.data[i];
+
+        *pszGUID++ = k_rgchHexToASCII[c >> 4];
+        *pszGUID++ = k_rgchHexToASCII[c & 0x0F];
+    }
+    *pszGUID = '\0';
+}
+
+/*-----------------------------------------------------------------------------
+ * Purpose: Returns the 4 bit nibble for a hex character
+ * Input  : c -
+ * Output : unsigned char
+ *-----------------------------------------------------------------------------*/
+static unsigned char nibble(unsigned char c)
+{
+    if ((c >= '0') && (c <= '9')) {
+        return (c - '0');
+    }
+
+    if ((c >= 'A') && (c <= 'F')) {
+        return (c - 'A' + 0x0a);
+    }
+
+    if ((c >= 'a') && (c <= 'f')) {
+        return (c - 'a' + 0x0a);
+    }
+
+    /* received an invalid character, and no real way to return an error */
+    /* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
+    return 0;
+}
+
+/* convert the string version of a guid to the struct */
+SDL_GUID SDL_GUIDFromString(const char *pchGUID)
+{
+    SDL_GUID guid;
+    int maxoutputbytes= sizeof(guid);
+    size_t len = SDL_strlen(pchGUID);
+    Uint8 *p;
+    size_t i;
+
+    /* Make sure it's even */
+    len = (len) & ~0x1;
+
+    SDL_memset(&guid, 0x00, sizeof(guid));
+
+    p = (Uint8 *)&guid;
+    for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
+        *p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i+1]);
+    }
+
+    return guid;
+}

+ 2 - 0
src/dynapi/SDL2.exports

@@ -843,3 +843,5 @@
 ++'_SDL_JoystickAttachVirtualEx'.'SDL2.dll'.'SDL_JoystickAttachVirtualEx'
 ++'_SDL_GameControllerGetFirmwareVersion'.'SDL2.dll'.'SDL_GameControllerGetFirmwareVersion'
 ++'_SDL_JoystickGetFirmwareVersion'.'SDL2.dll'.'SDL_JoystickGetFirmwareVersion'
+++'_SDL_GUIDToString'.'SDL2.dll'.'SDL_GUIDToString'
+++'_SDL_GUIDFromString'.'SDL2.dll'.'SDL_GUIDFromString'

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -869,3 +869,5 @@
 #define SDL_JoystickAttachVirtualEx SDL_JoystickAttachVirtualEx_REAL
 #define SDL_GameControllerGetFirmwareVersion SDL_GameControllerGetFirmwareVersion_REAL
 #define SDL_JoystickGetFirmwareVersion SDL_JoystickGetFirmwareVersion_REAL
+#define SDL_GUIDToString SDL_GUIDToString_REAL
+#define SDL_GUIDFromString SDL_GUIDFromString_REAL

+ 2 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -944,3 +944,5 @@ SDL_DYNAPI_PROC(const char*,SDL_JoystickPath,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_JoystickAttachVirtualEx,(const SDL_VirtualJoystickDesc *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_GameControllerGetFirmwareVersion,(SDL_GameController *a),(a),return)
 SDL_DYNAPI_PROC(Uint16,SDL_JoystickGetFirmwareVersion,(SDL_Joystick *a),(a),return)
+SDL_DYNAPI_PROC(void,SDL_GUIDToString,(SDL_GUID a, char *b, int c),(a,b,c),)
+SDL_DYNAPI_PROC(SDL_GUID,SDL_GUIDFromString,(const char *a),(a),return)

+ 2 - 57
src/joystick/SDL_joystick.c

@@ -2735,68 +2735,13 @@ SDL_JoystickType SDL_JoystickGetType(SDL_Joystick *joystick)
 /* convert the guid to a printable string */
 void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
 {
-    static const char k_rgchHexToASCII[] = "0123456789abcdef";
-    int i;
-
-    if ((pszGUID == NULL) || (cbGUID <= 0)) {
-        return;
-    }
-
-    for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) {
-        /* each input byte writes 2 ascii chars, and might write a null byte. */
-        /* If we don't have room for next input byte, stop */
-        unsigned char c = guid.data[i];
-
-        *pszGUID++ = k_rgchHexToASCII[c >> 4];
-        *pszGUID++ = k_rgchHexToASCII[c & 0x0F];
-    }
-    *pszGUID = '\0';
-}
-
-/*-----------------------------------------------------------------------------
- * Purpose: Returns the 4 bit nibble for a hex character
- * Input  : c -
- * Output : unsigned char
- *-----------------------------------------------------------------------------*/
-static unsigned char nibble(unsigned char c)
-{
-    if ((c >= '0') && (c <= '9')) {
-        return (c - '0');
-    }
-
-    if ((c >= 'A') && (c <= 'F')) {
-        return (c - 'A' + 0x0a);
-    }
-
-    if ((c >= 'a') && (c <= 'f')) {
-        return (c - 'a' + 0x0a);
-    }
-
-    /* received an invalid character, and no real way to return an error */
-    /* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */
-    return 0;
+    SDL_GUIDToString(guid, pszGUID, cbGUID);
 }
 
 /* convert the string version of a joystick guid to the struct */
 SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
 {
-    SDL_JoystickGUID guid;
-    int maxoutputbytes= sizeof(guid);
-    size_t len = SDL_strlen(pchGUID);
-    Uint8 *p;
-    size_t i;
-
-    /* Make sure it's even */
-    len = (len) & ~0x1;
-
-    SDL_memset(&guid, 0x00, sizeof(guid));
-
-    p = (Uint8 *)&guid;
-    for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) {
-        *p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i+1]);
-    }
-
-    return guid;
+    return SDL_GUIDFromString(pchGUID);
 }
 
 /* update the power level for this joystick */

+ 24 - 20
test/CMakeLists.txt

@@ -85,6 +85,7 @@ add_executable(testgesture testgesture.c)
 add_executable(testgl2 testgl2.c)
 add_executable(testgles testgles.c)
 add_executable(testgles2 testgles2.c)
+add_executable(testguid testguid.c)
 add_executable(testhaptic testhaptic.c)
 add_executable(testhotplug testhotplug.c)
 add_executable(testrumble testrumble.c)
@@ -166,6 +167,7 @@ SET(ALL_TESTS
     testgl2
     testgles
     testgles2
+    testguid
     testhaptic
     testhittesting
     testhotplug
@@ -214,6 +216,7 @@ set(NONINTERACTIVE
     testatomic
     testerror
     testfilesystem
+    testguid
     testlocale
     testplatform
     testpower
@@ -313,35 +316,36 @@ if(PSP)
     # Build EBOOT files if building for PSP
     set(BUILD_EBOOT
         ${NEEDS_RESOURCES}
+        testatomic
+        testaudiocapture
+        testaudioinfo
         testbounds
-        testgl2
-        testsem
         testdisplayinfo
-        teststreaming
-        testgeometry
-        testfile
         testdraw2
-        testviewport
-        testhittesting
-        testoverlay2
-        testver
         testdrawchessboard
-        testsurround
-        testintersections
-        testmessage
-        testaudiocapture
         testerror
-        testatomic
-        testjoystick
-        testiconv
+        testfile
         testfilesystem
+        testgeometry
+        testgl2
+        testguid
+        testhittesting
+        testiconv
+        testintersections
+        testjoystick
+        testlock
+        testmessage
+        testoverlay2
         testplatform
-        testthread
+        testpower
         testqsort
-        testaudioinfo
-        testlock
+        testsem
+        teststreaming
+        testsurround
+        testthread
         testtimer
-        testpower
+        testver
+        testviewport
         testwm2
         torturethread
     )

+ 5 - 1
test/Makefile.in

@@ -40,6 +40,7 @@ TARGETS = \
 	testgamecontroller$(EXE) \
 	testgeometry$(EXE) \
 	testgesture$(EXE) \
+	testguid$(EXE) \
 	testhaptic$(EXE) \
 	testhittesting$(EXE) \
 	testhotplug$(EXE) \
@@ -201,7 +202,7 @@ testgeometry$(EXE): $(srcdir)/testgeometry.c $(srcdir)/testutils.c
 
 testgesture$(EXE): $(srcdir)/testgesture.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @MATHLIB@
- 
+
 testgl2$(EXE): $(srcdir)/testgl2.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @MATHLIB@
 
@@ -214,6 +215,9 @@ testgles2$(EXE): $(srcdir)/testgles2.c
 testgles2_sdf$(EXE): $(srcdir)/testgles2_sdf.c $(srcdir)/testutils.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS) @MATHLIB@
 
+testguid$(EXE): $(srcdir)/testguid.c
+	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
 testhaptic$(EXE): $(srcdir)/testhaptic.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 

+ 169 - 0
test/testguid.c

@@ -0,0 +1,169 @@
+/*
+  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.
+*/
+
+/**
+ * Automated tests for GUID processing
+ */
+
+#include <stdio.h>
+
+#include "SDL.h"
+
+/* Helpers */
+
+static int _error_count = 0;
+
+static int
+_require_eq(Uint64 expected, Uint64 actual, int line, char *msg)
+{
+    if (expected != actual) {
+        _error_count += 1;
+        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "[%s, L%d] %s: Actual %ld (0x%lx) != expected %ld (0x%lx)",
+                     __FILE__, line, msg, actual, actual, expected, expected);
+        return 0;
+    }
+    return 1;
+}
+
+#define ASSERT_EQ(MSG, EXPECTED, ACTUAL) _require_eq((EXPECTED), (ACTUAL), __LINE__, (MSG))
+
+/* Helpers */
+
+#define NUM_TEST_GUIDS 5
+
+static struct {
+    char *str;
+    Uint64 upper, lower;
+} test_guids[NUM_TEST_GUIDS] = {
+    { "0000000000000000"    "ffffffffffffffff",
+     0x0000000000000000,   0xfffffffffffffffflu },
+    { "0011223344556677"    "8091a2b3c4d5e6f0",
+     0x0011223344556677lu, 0x8091a2b3c4d5e6f0lu },
+    { "a011223344556677"    "8091a2b3c4d5e6f0",
+     0xa011223344556677lu, 0x8091a2b3c4d5e6f0lu },
+    { "a011223344556677"    "8091a2b3c4d5e6f1",
+     0xa011223344556677lu, 0x8091a2b3c4d5e6f1lu },
+    { "a011223344556677"    "8191a2b3c4d5e6f0",
+     0xa011223344556677lu, 0x8191a2b3c4d5e6f0lu },
+};
+
+static void
+upper_lower_to_bytestring(Uint8* out, Uint64 upper, Uint64 lower)
+{
+    Uint64 values[2];
+    int i, k;
+
+    values[0] = upper;
+    values [1] = lower;
+
+    for (i = 0; i < 2; ++i) {
+        Uint64 v = values[i];
+
+        for (k = 0; k < 8; ++k) {
+            *out++ = v >> 56;
+            v <<= 8;
+        }
+    }
+}
+
+/* ================= Test Case Implementation ================== */
+
+/**
+ * @brief Check String-to-GUID conversion
+ *
+ * @sa SDL_GUIDFromString
+ */
+static void
+TestGuidFromString(void)
+{
+    int i;
+
+    for (i = 0; i < NUM_TEST_GUIDS; ++i) {
+        Uint8 expected[16];
+        SDL_GUID guid;
+
+        upper_lower_to_bytestring(expected,
+                                  test_guids[i].upper, test_guids[i].lower);
+
+        guid = SDL_GUIDFromString(test_guids[i].str);
+        if (!ASSERT_EQ("GUID from string", 0, SDL_memcmp(expected, guid.data, 16))) {
+            SDL_Log("  GUID was: '%s'", test_guids[i].str);
+        }
+    }
+}
+
+/**
+ * @brief Check GUID-to-String conversion
+ *
+ * @sa SDL_GUIDToString
+ */
+static void
+TestGuidToString(void)
+{
+    int i;
+
+    for (i = 0; i < NUM_TEST_GUIDS; ++i) {
+        const int guid_str_offset = 4;
+        char guid_str_buf[64];
+        char *guid_str = guid_str_buf + guid_str_offset;
+        SDL_GUID guid;
+        int size;
+
+        upper_lower_to_bytestring(guid.data,
+                                  test_guids[i].upper, test_guids[i].lower);
+
+        /* Serialise to limited-length buffers */
+        for (size = 0; size <= 36; ++size) {
+            const Uint8 fill_char = size + 0xa0;
+            Uint32 expected_prefix;
+            Uint32 actual_prefix;
+            int written_size;
+
+            SDL_memset(guid_str_buf, fill_char, sizeof(guid_str_buf));
+            SDL_GUIDToString(guid, guid_str, size);
+
+            /* Check bytes before guid_str_buf */
+            expected_prefix = fill_char | (fill_char << 8) | (fill_char << 16) | (fill_char << 24);
+            SDL_memcpy(&actual_prefix, guid_str_buf, 4);
+            if (!ASSERT_EQ("String buffer memory before output untouched: ", expected_prefix, actual_prefix)) {
+                SDL_Log("  at size=%d", size);
+            }
+
+            /* Check that we did not overwrite too much */
+            written_size = 0;
+            while ((guid_str[written_size] & 0xff) != fill_char && written_size < 256) {
+                ++written_size;
+            }
+            if (!ASSERT_EQ("Output length is within expected bounds", 1, written_size <= size)) {
+                SDL_Log("  with lenght %d: wrote %d of %d permitted bytes",
+                        size, written_size, size);
+            }
+            if (size >= 33) {
+                if (!ASSERT_EQ("GUID string equality", 0, SDL_strcmp(guid_str, test_guids[i].str))) {
+                    SDL_Log("  from string: %s", test_guids[i].str);
+                }
+            }
+        }
+    }
+}
+
+int
+main(int argc, char *argv[])
+{
+    /* Enable standard application logging */
+    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
+
+    TestGuidFromString();
+    TestGuidToString();
+
+    return _error_count > 0;
+}

+ 1 - 1
test/watcom.mif

@@ -11,7 +11,7 @@ TARGETS = testatomic.exe testdisplayinfo.exe testbounds.exe testdraw2.exe &
           testdrawchessboard.exe testdropfile.exe testerror.exe testfile.exe &
           testfilesystem.exe testgamecontroller.exe testgeometry.exe testgesture.exe &
           testhittesting.exe testhotplug.exe testiconv.exe testime.exe testlocale.exe &
-          testintersections.exe testjoystick.exe testkeys.exe testloadso.exe &
+          testguid.exe testintersections.exe testjoystick.exe testkeys.exe testloadso.exe &
           testlock.exe testmessage.exe testoverlay2.exe testplatform.exe &
           testpower.exe testsensor.exe testrelative.exe testrendercopyex.exe &
           testrendertarget.exe testrumble.exe testscale.exe testsem.exe &