From c0cd8c8142b70c70da4dddc32244da6593cb6bb0 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Fri, 25 Aug 2023 10:39:39 -0400
Subject: [PATCH] gdk: Add SDL_GDKGetDefaultUser, SDL_GetPrefPath
implementation
---
VisualC-GDK/SDL/SDL.vcxproj | 2 +-
VisualC-GDK/SDL/SDL.vcxproj.filters | 6 +-
docs/README-gdk.md | 6 ++
include/SDL3/SDL_system.h | 16 +++-
src/core/gdk/SDL_gdk.cpp | 20 +++++
src/filesystem/gdk/SDL_sysfilesystem.cpp | 96 ++++++++++++++++++++++
src/filesystem/windows/SDL_sysfilesystem.c | 20 -----
7 files changed, 141 insertions(+), 25 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 fd9d84557ae6..f4419c9ee20d 100644
--- a/VisualC-GDK/SDL/SDL.vcxproj
+++ b/VisualC-GDK/SDL/SDL.vcxproj
@@ -582,7 +582,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.c" />
<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 81dee5cc3d56..f55ad0342a17 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">
@@ -916,8 +916,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.c">
+ <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 a785e653047b..fefe16357941 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/SDL3/SDL_system.h b/include/SDL3/SDL_system.h
index b934066f8339..98d67c96127a 100644
--- a/include/SDL3/SDL_system.h
+++ b/include/SDL3/SDL_system.h
@@ -623,7 +623,8 @@ extern DECLSPEC void SDLCALL SDL_OnApplicationDidChangeStatusBarOrientation(void
/* Functions used only by GDK */
#ifdef __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,
@@ -641,6 +642,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 d883a3c7bcc9..c2dd14e244d2 100644
--- a/src/core/gdk/SDL_gdk.cpp
+++ b/src/core/gdk/SDL_gdk.cpp
@@ -214,3 +214,23 @@ 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..30a2b3489e55
--- /dev/null
+++ b/src/filesystem/gdk/SDL_sysfilesystem.cpp
@@ -0,0 +1,96 @@
+/*
+ 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)
+{
+ return SDL_strdup("G:\\");
+}
+
+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 2dffe4c2a2a1..3027fb00e2b0 100644
--- a/src/filesystem/windows/SDL_sysfilesystem.c
+++ b/src/filesystem/windows/SDL_sysfilesystem.c
@@ -332,23 +332,3 @@ char *SDL_GetUserFolder(SDL_Folder folder)
return retval;
}
#endif /* SDL_FILESYSTEM_WINDOWS */
-
-#ifdef SDL_FILESYSTEM_XBOX
-char *SDL_GetBasePath(void)
-{
- SDL_Unsupported();
- return NULL;
-}
-
-char *SDL_GetPrefPath(const char *org, const char *app)
-{
- SDL_Unsupported();
- return NULL;
-}
-
-char *SDL_GetUserFolder(SDL_Folder folder)
-{
- SDL_Unsupported();
- return NULL;
-}
-#endif /* SDL_FILESYSTEM_XBOX */