From 24fcb61470a110b614bae6d8d79ef866aea751b4 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Mon, 14 Aug 2023 13:32:02 -0400
Subject: [PATCH] =?UTF-8?q?=C2=96=20gdk:=20Add=20SDL=5FGDKGetDefaultUser,?=
=?UTF-8?q?=20SDL=5FGetPrefPath=20implementation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
VisualC-GDK/SDL/SDL.vcxproj | 2 +-
VisualC-GDK/SDL/SDL.vcxproj.filters | 6 +-
docs/README-gdk.md | 6 +
include/SDL_system.h | 16 ++-
src/core/gdk/SDL_gdk.cpp | 21 ++++
src/filesystem/gdk/SDL_sysfilesystem.cpp | 140 +++++++++++++++++++++
src/filesystem/windows/SDL_sysfilesystem.c | 16 ---
7 files changed, 186 insertions(+), 21 deletions(-)
create mode 100644 src/filesystem/gdk/SDL_sysfilesystem.cpp
diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj
index c8208b41f7e8..e6741d92e176 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -563,7 +563,7 @@
<ClCompile Include="..\..\src\events\SDL_touch.c" />
<ClCompile Include="..\..\src\events\SDL_windowevents.c" />
<ClCompile Include="..\..\src\file\SDL_rwops.c" />
- <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c" />
+ <ClCompile Include="..\..\src\filesystem\gdk\SDL_sysfilesystem.cpp" />
<ClCompile Include="..\..\src\haptic\dummy\SDL_syshaptic.c" />
<ClCompile Include="..\..\src\haptic\SDL_haptic.c" />
<ClCompile Include="..\..\src\haptic\windows\SDL_dinputhaptic.c" />
diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters
index 02dc97ef30f0..9fd2712a367f 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj.filters
+++ b/VisualC-GDK/SDL/SDL.vcxproj.filters
@@ -31,7 +31,7 @@
<Filter Include="filesystem">
<UniqueIdentifier>{377061e4-3856-4f05-b916-0d3b360df0f6}</UniqueIdentifier>
</Filter>
- <Filter Include="filesystem\windows">
+ <Filter Include="filesystem\gdk">
<UniqueIdentifier>{226a6643-1c65-4c7f-92aa-861313d974bb}</UniqueIdentifier>
</Filter>
<Filter Include="haptic">
@@ -925,8 +925,8 @@
<ClCompile Include="..\..\src\file\SDL_rwops.c">
<Filter>file</Filter>
</ClCompile>
- <ClCompile Include="..\..\src\filesystem\windows\SDL_sysfilesystem.c">
- <Filter>filesystem\windows</Filter>
+ <ClCompile Include="..\..\src\filesystem\gdk\SDL_sysfilesystem.cpp">
+ <Filter>filesystem\gdk</Filter>
</ClCompile>
<ClCompile Include="..\..\src\haptic\SDL_haptic.c">
<Filter>haptic</Filter>
diff --git a/docs/README-gdk.md b/docs/README-gdk.md
index b8ba2b4337a5..248a418e792a 100644
--- a/docs/README-gdk.md
+++ b/docs/README-gdk.md
@@ -29,6 +29,12 @@ The Windows GDK port supports the full set of Win32 APIs, renderers, controllers
* Global task queue callbacks are dispatched during `SDL_PumpEvents` (which is also called internally if using `SDL_PollEvent`).
* You can get the handle of the global task queue through `SDL_GDKGetTaskQueue`, if needed. When done with the queue, be sure to use `XTaskQueueCloseHandle` to decrement the reference count (otherwise it will cause a resource leak).
+* Single-player games have some additional features available:
+ * Call `SDL_GDKGetDefaultUser` to get the default XUserHandle pointer.
+ * `SDL_GetPrefPath` still works, but only for single-player titles.
+
+These functions mostly wrap around async APIs, and thus should be treated as synchronous alternatives. Also note that the single-player functions return on any OS errors, so be sure to validate the return values!
+
* What doesn't work:
* Compilation with anything other than through the included Visual C++ solution file
diff --git a/include/SDL_system.h b/include/SDL_system.h
index 4b7eaddcc0ed..261ddabf4fb0 100644
--- a/include/SDL_system.h
+++ b/include/SDL_system.h
@@ -593,7 +593,8 @@ extern DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void
/* Functions used only by GDK */
#if defined(__GDK__)
-typedef struct XTaskQueueObject * XTaskQueueHandle;
+typedef struct XTaskQueueObject *XTaskQueueHandle;
+typedef struct XUser *XUserHandle;
/**
* Gets a reference to the global async task queue handle for GDK,
@@ -610,6 +611,19 @@ typedef struct XTaskQueueObject * XTaskQueueHandle;
*/
extern DECLSPEC int SDLCALL SDL_GDKGetTaskQueue(XTaskQueueHandle * outTaskQueue);
+/**
+ * Gets a reference to the default user handle for GDK.
+ *
+ * This is effectively a synchronous version of XUserAddAsync, which always
+ * prefers the default user and allows a sign-in UI.
+ *
+ * \param outUserHandle a pointer to be filled in with the default user handle.
+ * \returns 0 if success, -1 if any error occurs.
+ *
+ * \since This function is available since SDL 2.28.0.
+ */
+extern DECLSPEC int SDLCALL SDL_GDKGetDefaultUser(XUserHandle * outUserHandle);
+
#endif
/* Ends C function definitions when using C++ */
diff --git a/src/core/gdk/SDL_gdk.cpp b/src/core/gdk/SDL_gdk.cpp
index 61617fdcd9f3..251e9b9ff97e 100644
--- a/src/core/gdk/SDL_gdk.cpp
+++ b/src/core/gdk/SDL_gdk.cpp
@@ -218,3 +218,24 @@ SDL_GDKSuspendComplete()
SetEvent(plmSuspendComplete);
}
}
+
+extern "C" DECLSPEC int
+SDL_GDKGetDefaultUser(XUserHandle *outUserHandle)
+{
+ XAsyncBlock block = { 0 };
+ HRESULT result;
+
+ if (FAILED(result = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, &block))) {
+ return WIN_SetErrorFromHRESULT("XUserAddAsync", result);
+ }
+
+ do {
+ result = XUserAddResult(&block, outUserHandle);
+ } while (result == E_PENDING);
+ if (FAILED(result)) {
+ return WIN_SetErrorFromHRESULT("XUserAddResult", result);
+ }
+
+ return 0;
+}
+
diff --git a/src/filesystem/gdk/SDL_sysfilesystem.cpp b/src/filesystem/gdk/SDL_sysfilesystem.cpp
new file mode 100644
index 000000000000..517524bdb2be
--- /dev/null
+++ b/src/filesystem/gdk/SDL_sysfilesystem.cpp
@@ -0,0 +1,140 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2023 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"
+
+#ifdef SDL_FILESYSTEM_XBOX
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent filesystem routines */
+
+#include "../../core/windows/SDL_windows.h"
+#include "SDL_hints.h"
+#include "SDL_system.h"
+#include "SDL_filesystem.h"
+#include <XGameSaveFiles.h>
+
+char *
+SDL_GetBasePath(void)
+{
+ /* NOTE: This function is a UTF8 version of the Win32 SDL_GetBasePath()!
+ * The GDK actually _recommends_ the 'A' functions over the 'W' functions :o
+ */
+ DWORD buflen = 128;
+ CHAR *path = NULL;
+ DWORD len = 0;
+ int i;
+
+ while (SDL_TRUE) {
+ void *ptr = SDL_realloc(path, buflen * sizeof(CHAR));
+ if (ptr == NULL) {
+ SDL_free(path);
+ SDL_OutOfMemory();
+ return NULL;
+ }
+
+ path = (CHAR *)ptr;
+
+ len = GetModuleFileNameA(NULL, path, buflen);
+ /* if it truncated, then len >= buflen - 1 */
+ /* if there was enough room (or failure), len < buflen - 1 */
+ if (len < buflen - 1) {
+ break;
+ }
+
+ /* buffer too small? Try again. */
+ buflen *= 2;
+ }
+
+ if (len == 0) {
+ SDL_free(path);
+ WIN_SetError("Couldn't locate our .exe");
+ return NULL;
+ }
+
+ for (i = len - 1; i > 0; i--) {
+ if (path[i] == '\\') {
+ break;
+ }
+ }
+
+ SDL_assert(i > 0); /* Should have been an absolute path. */
+ path[i + 1] = '\0'; /* chop off filename. */
+
+ return path;
+}
+
+char *
+SDL_GetPrefPath(const char *org, const char *app)
+{
+ XUserHandle user = NULL;
+ XAsyncBlock block = { 0 };
+ char *folderPath;
+ HRESULT result;
+ const char *csid = SDL_GetHint("SDL_GDK_SERVICE_CONFIGURATION_ID");
+
+ if (app == NULL) {
+ SDL_InvalidParamError("app");
+ return NULL;
+ }
+
+ /* This should be set before calling SDL_GetPrefPath! */
+ if (csid == NULL) {
+ SDL_LogWarn(SDL_LOG_CATEGORY_SYSTEM, "Set SDL_GDK_SERVICE_CONFIGURATION_ID before calling SDL_GetPrefPath!");
+ return SDL_strdup("T:\\");
+ }
+
+ if (SDL_GDKGetDefaultUser(&user) < 0) {
+ /* Error already set, just return */
+ return NULL;
+ }
+
+ if (FAILED(result = XGameSaveFilesGetFolderWithUiAsync(user, csid, &block))) {
+ WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiAsync", result);
+ return NULL;
+ }
+
+ folderPath = (char*) SDL_malloc(MAX_PATH);
+ do {
+ result = XGameSaveFilesGetFolderWithUiResult(&block, MAX_PATH, folderPath);
+ } while (result == E_PENDING);
+ if (FAILED(result)) {
+ WIN_SetErrorFromHRESULT("XGameSaveFilesGetFolderWithUiResult", result);
+ SDL_free(folderPath);
+ return NULL;
+ }
+
+ /* We aren't using 'app' here because the container rules are a lot more
+ * strict than the NTFS rules, so it will most likely be invalid :(
+ */
+ SDL_strlcat(folderPath, "\\SDLPrefPath\\", MAX_PATH);
+ if (CreateDirectoryA(folderPath, NULL) == FALSE) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS) {
+ WIN_SetError("CreateDirectoryA");
+ SDL_free(folderPath);
+ return NULL;
+ }
+ }
+ return folderPath;
+}
+
+#endif /* SDL_FILESYSTEM_XBOX */
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c
index 06842706f1f3..8f512b7dc7c5 100644
--- a/src/filesystem/windows/SDL_sysfilesystem.c
+++ b/src/filesystem/windows/SDL_sysfilesystem.c
@@ -170,20 +170,4 @@ char *SDL_GetPrefPath(const char *org, const char *app)
#endif /* SDL_FILESYSTEM_WINDOWS */
-#ifdef SDL_FILESYSTEM_XBOX
-#include "SDL_filesystem.h"
-#include "SDL_error.h"
-char *SDL_GetBasePath(void)
-{
- SDL_Unsupported();
- return NULL;
-}
-
-char *SDL_GetPrefPath(const char *org, const char *app)
-{
- SDL_Unsupported();
- return NULL;
-}
-#endif /* SDL_FILESYSTEM_XBOX */
-
/* vi: set ts=4 sw=4 expandtab: */