From c1dab7745ad029d04295d11fc6119cd3dc6e3b8d Mon Sep 17 00:00:00 2001
From: Semphriss <[EMAIL REDACTED]>
Date: Thu, 4 May 2023 14:38:11 -0400
Subject: [PATCH] Add SDL_GetPath() for default OS folders (#7665)
---
include/SDL3/SDL_filesystem.h | 77 ++++++
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/filesystem/cocoa/SDL_sysfilesystem.m | 114 +++++++++
src/filesystem/dummy/SDL_sysfilesystem.c | 7 +
src/filesystem/unix/SDL_sysfilesystem.c | 284 +++++++++++++++++++++
src/filesystem/windows/SDL_sysfilesystem.c | 187 ++++++++++++++
8 files changed, 672 insertions(+)
diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index b45f41380f2b..8811a9ae956e 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -138,6 +138,83 @@ extern DECLSPEC char *SDLCALL SDL_GetBasePath(void);
*/
extern DECLSPEC char *SDLCALL SDL_GetPrefPath(const char *org, const char *app);
+/**
+ * The type of the OS-provided default folder for a specific purpose.
+ *
+ * Note that the Trash folder isn't included here, because trashing files usually
+ * involves extra OS-specific functionality to remember the file's original
+ * location.
+ *
+ * \sa SDL_GetPath
+ */
+typedef enum
+{
+ /** The folder which contains all of the current user's data, preferences,
+ and documents. It usually contains most of the other folders. If a
+ requested folder does not exist, the home folder can be considered a safe
+ fallback to store a user's documents. Supported on Windows, macOS and
+ Unix with XDG. */
+ SDL_FOLDER_HOME,
+ /** The folder of files that are displayed on the desktop. Note that the
+ existence of a desktop folder does not guarantee that the system does
+ show icons on its desktop; certain GNU/Linux distros with a graphical
+ environment may not have desktop icons. Supported on Windows, macOS and
+ Unix with XDG. */
+ SDL_FOLDER_DESKTOP,
+ /** General document files, possibly application-specific. This is a good
+ place to save a user's projects. Supported on Windows, macOS and Unix
+ with XDG. */
+ SDL_FOLDER_DOCUMENTS,
+ /** Generic landing folder for files downloaded from the internet. Supported
+ on Windows Vista and later, macOS and Unix with XDG. */
+ SDL_FOLDER_DOWNLOADS,
+ /** Music files that can be played using a standard music player (mp3,
+ ogg...). Supported on Windows, macOS and Unix with XDG. */
+ SDL_FOLDER_MUSIC,
+ /** Image files that can be displayed using a standard viewer (png,
+ jpg...). Supported on Windows, macOS and Unix with XDG. */
+ SDL_FOLDER_PICTURES,
+ /** Files that are meant to be shared with other users on the same
+ computer. Supported on macOS and Unix with XDG. */
+ SDL_FOLDER_PUBLICSHARE,
+ /** Save files for games. Supported on Windows Vista and later. */
+ SDL_FOLDER_SAVEDGAMES,
+ /** Application screenshots. Supported on Windows Vista and later. */
+ SDL_FOLDER_SCREENSHOTS,
+ /** Template files to be used when the user requests the desktop environment
+ to create a new file in a certain folder, such as "New Text File.txt".
+ Any file in the Templates folder can be used as a starting point for a
+ new file. Supported on Windows, macOS and Unix with XDG. */
+ SDL_FOLDER_TEMPLATES,
+ /** Video files that can be played using a standard video player (mp4,
+ webm...). On macOS, this is the "Movies" folder. Supported on Windows,
+ macOS and Unix with XDG. */
+ SDL_FOLDER_VIDEOS,
+} SDL_Folder;
+
+/**
+ * Finds the most suitable OS-provided folder for @p folder, and returns its
+ * path in OS-specific notation.
+ *
+ * Many OSes provide certain standard folders for certain purposes, such as
+ * storing pictures, music or videos for a certain user. This function gives
+ * the path for many of those special locations.
+ *
+ * Note that the function is expensive, and should be called once at the
+ * beginning of the execution and kept for as long as needed.
+ *
+ * The returned value is owned by the caller and should be freed with
+ * SDL_free().
+ *
+ * If NULL is returned, the error may be obtained with SDL_GetError().
+ *
+ * \returns Either a null-terminated C string containing the full path to the
+ * folder, or NULL if an error happened.
+ *
+ * \sa SDL_Folder
+ */
+extern DECLSPEC char *SDLCALL SDL_GetPath(SDL_Folder folder);
+
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 3870424b209d..3927b26234ac 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -851,6 +851,7 @@ SDL3_0.0.0 {
SDL_TryLockRWLockForWriting;
SDL_UnlockRWLock;
SDL_DestroyRWLock;
+ SDL_GetPath;
# 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 5c78a8c00e26..d96acd6150a2 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -877,3 +877,4 @@
#define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL
#define SDL_UnlockRWLock SDL_UnlockRWLock_REAL
#define SDL_DestroyRWLock SDL_DestroyRWLock_REAL
+#define SDL_GetPath SDL_GetPath_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index e59b2d9de32d..9ebfecd4e541 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -922,3 +922,4 @@ SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForReading,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForWriting,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_UnlockRWLock,(SDL_RWLock *a),(a),return)
SDL_DYNAPI_PROC(void,SDL_DestroyRWLock,(SDL_RWLock *a),(a),)
+SDL_DYNAPI_PROC(char*,SDL_GetPath,(SDL_Folder a),(a),return)
diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m
index ddf86ddcfd3e..c9880efe4596 100644
--- a/src/filesystem/cocoa/SDL_sysfilesystem.m
+++ b/src/filesystem/cocoa/SDL_sysfilesystem.m
@@ -132,4 +132,118 @@
}
}
+char *
+SDL_GetPath(SDL_Folder folder)
+{
+ @autoreleasepool {
+#if TARGET_OS_TV
+ SDL_SetError("tvOS does not have persistent storage");
+ return NULL;
+#else
+ char *retval = NULL;
+ const char* base;
+ NSArray *array;
+ NSSearchPathDirectory dir;
+ NSString *str;
+ char *ptr;
+
+ switch (folder)
+ {
+ case SDL_FOLDER_HOME:
+ base = SDL_getenv("HOME");
+
+ if (!base)
+ {
+ SDL_SetError("No $HOME environment variable available");
+ }
+
+ retval = SDL_strdup(base);
+
+ if (!retval)
+ SDL_OutOfMemory();
+
+ return retval;
+
+ case SDL_FOLDER_DESKTOP:
+ dir = NSDesktopDirectory;
+ break;
+
+ case SDL_FOLDER_DOCUMENTS:
+ dir = NSDocumentDirectory;
+ break;
+
+ case SDL_FOLDER_DOWNLOADS:
+ dir = NSDownloadsDirectory;
+ break;
+
+ case SDL_FOLDER_MUSIC:
+ dir = NSMusicDirectory;
+ break;
+
+ case SDL_FOLDER_PICTURES:
+ dir = NSPicturesDirectory;
+ break;
+
+ case SDL_FOLDER_PUBLICSHARE:
+ dir = NSSharedPublicDirectory;
+ break;
+
+ case SDL_FOLDER_SAVEDGAMES:
+ SDL_SetError("Saved games folder not supported on Cocoa");
+ return NULL;
+
+ case SDL_FOLDER_SCREENSHOTS:
+ SDL_SetError("Screenshots folder not supported on Cocoa");
+ return NULL;
+
+ case SDL_FOLDER_TEMPLATES:
+ SDL_SetError("Templates folder not supported on Cocoa");
+ return NULL;
+
+ case SDL_FOLDER_VIDEOS:
+ dir = NSMoviesDirectory;
+ break;
+
+ default:
+ SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
+ return NULL;
+ };
+
+ array = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);
+
+ if ([array count] <= 0)
+ {
+ SDL_SetError("Directory not found");
+ return NULL;
+ }
+
+ str = [array objectAtIndex:0];
+ base = [str fileSystemRepresentation];
+ if (!base)
+ {
+ SDL_SetError("Couldn't get folder path");
+ return NULL;
+ }
+
+ retval = SDL_strdup(base);
+ if (retval == NULL)
+ {
+ SDL_OutOfMemory();
+ return NULL;
+ }
+
+ for (ptr = retval + 1; *ptr; ptr++) {
+ if (*ptr == '/') {
+ *ptr = '\0';
+ mkdir(retval, 0700);
+ *ptr = '/';
+ }
+ }
+ mkdir(retval, 0700);
+
+ return retval;
+#endif /* TARGET_OS_TV */
+ }
+}
+
#endif /* SDL_FILESYSTEM_COCOA */
diff --git a/src/filesystem/dummy/SDL_sysfilesystem.c b/src/filesystem/dummy/SDL_sysfilesystem.c
index 644a3bc8a6b6..d341c740f5db 100644
--- a/src/filesystem/dummy/SDL_sysfilesystem.c
+++ b/src/filesystem/dummy/SDL_sysfilesystem.c
@@ -39,4 +39,11 @@ SDL_GetPrefPath(const char *org, const char *app)
return NULL;
}
+char *
+SDL_GetPath(SDL_Folder folder)
+{
+ SDL_Unsupported();
+ return NULL;
+}
+
#endif /* SDL_FILESYSTEM_DUMMY || SDL_FILESYSTEM_DISABLED */
diff --git a/src/filesystem/unix/SDL_sysfilesystem.c b/src/filesystem/unix/SDL_sysfilesystem.c
index cd4af65a7fbb..e3ca8e198739 100644
--- a/src/filesystem/unix/SDL_sysfilesystem.c
+++ b/src/filesystem/unix/SDL_sysfilesystem.c
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@@ -332,4 +333,287 @@ SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
+/*
+ The two functions below (prefixed with `xdg_`) have been copied from:
+ https://gitlab.freedesktop.org/xdg/xdg-user-dirs/-/blob/master/xdg-user-dir-lookup.c
+ and have been adapted to work with SDL. They are licensed under the following
+ terms:
+
+ Copyright (c) 2007 Red Hat, Inc.
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+static char *
+xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback)
+{
+ FILE *file;
+ char *home_dir, *config_home, *config_file;
+ char buffer[512];
+ char *user_dir;
+ char *p, *d;
+ int len;
+ int relative;
+ size_t l;
+
+ home_dir = SDL_getenv ("HOME");
+
+ if (home_dir == NULL)
+ goto error;
+
+ config_home = SDL_getenv ("XDG_CONFIG_HOME");
+ if (config_home == NULL || config_home[0] == 0)
+ {
+ l = SDL_strlen (home_dir) + SDL_strlen ("/.config/user-dirs.dirs") + 1;
+ config_file = (char*) SDL_malloc (l);
+ if (config_file == NULL)
+ goto error;
+
+ SDL_strlcpy (config_file, home_dir, l);
+ SDL_strlcat (config_file, "/.config/user-dirs.dirs", l);
+ }
+ else
+ {
+ l = SDL_strlen (config_home) + SDL_strlen ("/user-dirs.dirs") + 1;
+ config_file = (char*) SDL_malloc (l);
+ if (config_file == NULL)
+ goto error;
+
+ SDL_strlcpy (config_file, config_home, l);
+ SDL_strlcat (config_file, "/user-dirs.dirs", l);
+ }
+
+ file = fopen (config_file, "r");
+ SDL_free (config_file);
+ if (file == NULL)
+ goto error;
+
+ user_dir = NULL;
+ while (fgets (buffer, sizeof (buffer), file))
+ {
+ /* Remove newline at end */
+ len = SDL_strlen (buffer);
+ if (len > 0 && buffer[len-1] == '\n')
+ buffer[len-1] = 0;
+
+ p = buffer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (SDL_strncmp (p, "XDG_", 4) != 0)
+ continue;
+ p += 4;
+ if (SDL_strncmp (p, type, SDL_strlen (type)) != 0)
+ continue;
+ p += strlen (type);
+ if (SDL_strncmp (p, "_DIR", 4) != 0)
+ continue;
+ p += 4;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '=')
+ continue;
+ p++;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p != '"')
+ continue;
+ p++;
+
+ relative = 0;
+ if (SDL_strncmp (p, "$HOME/", 6) == 0)
+ {
+ p += 6;
+ relative = 1;
+ }
+ else if (*p != '/')
+ continue;
+
+ SDL_free (user_dir);
+ if (relative)
+ {
+ l = SDL_strlen (home_dir) + 1 + SDL_strlen (p) + 1;
+ user_dir = (char*) SDL_malloc (l);
+ if (user_dir == NULL)
+ goto error2;
+
+ SDL_strlcpy (user_dir, home_dir, l);
+ SDL_strlcat (user_dir, "/", l);
+ }
+ else
+ {
+ user_dir = (char*) SDL_malloc (SDL_strlen (p) + 1);
+ if (user_dir == NULL)
+ goto error2;
+
+ *user_dir = 0;
+ }
+
+ d = user_dir + SDL_strlen (user_dir);
+ while (*p && *p != '"')
+ {
+ if ((*p == '\\') && (*(p+1) != 0))
+ p++;
+ *d++ = *p++;
+ }
+ *d = 0;
+ }
+error2:
+ fclose (file);
+
+ if (user_dir)
+ return user_dir;
+
+ error:
+ if (fallback)
+ return SDL_strdup (fallback);
+ return NULL;
+}
+
+static char *
+xdg_user_dir_lookup (const char *type)
+{
+ char *dir, *home_dir, *user_dir;
+
+ dir = xdg_user_dir_lookup_with_fallback(type, NULL);
+ if (dir != NULL)
+ return dir;
+
+ home_dir = SDL_getenv("HOME");
+
+ if (home_dir == NULL)
+ return NULL;
+
+ /* Special case desktop for historical compatibility */
+ if (SDL_strcmp(type, "DESKTOP") == 0)
+ {
+ user_dir = (char*) SDL_malloc(SDL_strlen(home_dir) +
+ SDL_strlen("/Desktop") + 1);
+ if (user_dir == NULL)
+ return NULL;
+
+ strcpy(user_dir, home_dir);
+ strcat(user_dir, "/Desktop");
+ return user_dir;
+ }
+
+ return NULL;
+}
+
+char *
+SDL_GetPath(SDL_Folder folder)
+{
+ const char *param = NULL;
+ char *retval;
+
+ /* According to `man xdg-user-dir`, the possible values are:
+ DESKTOP
+ DOWNLOAD
+ TEMPLATES
+ PUBLICSHARE
+ DOCUMENTS
+ MUSIC
+ PICTURES
+ VIDEOS
+ */
+ switch(folder)
+ {
+ case SDL_FOLDER_HOME:
+ param = SDL_getenv("HOME");
+
+ if (!param)
+ {
+ SDL_SetError("No $HOME environment variable available");
+ }
+
+ retval = SDL_strdup(param);
+
+ if (!retval)
+ SDL_OutOfMemory();
+
+ return retval;
+
+ case SDL_FOLDER_DESKTOP:
+ param = "DESKTOP";
+ break;
+
+ case SDL_FOLDER_DOCUMENTS:
+ param = "DOCUMENTS";
+ break;
+
+ case SDL_FOLDER_DOWNLOADS:
+ param = "DOWNLOAD";
+ break;
+
+ case SDL_FOLDER_MUSIC:
+ param = "MUSIC";
+ break;
+
+ case SDL_FOLDER_PICTURES:
+ param = "PICTURES";
+ break;
+
+ case SDL_FOLDER_PUBLICSHARE:
+ param = "PUBLICSHARE";
+ break;
+
+ case SDL_FOLDER_SAVEDGAMES:
+ SDL_SetError("Saved Games folder unavailable on XDG");
+ return NULL;
+
+ case SDL_FOLDER_SCREENSHOTS:
+ SDL_SetError("Screenshots folder unavailable on XDG");
+ return NULL;
+
+ case SDL_FOLDER_TEMPLATES:
+ param = "TEMPLATES";
+ break;
+
+ case SDL_FOLDER_VIDEOS:
+ param = "VIDEOS";
+ break;
+
+ default:
+ SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
+ return NULL;
+ }
+
+ /* param *should* to be set to something at this point, but just in case */
+ if (!param)
+ {
+ SDL_SetError("No corresponding XDG user directory");
+ return NULL;
+ }
+
+ retval = xdg_user_dir_lookup(param);
+
+ if (!retval)
+ {
+ SDL_SetError("XDG directory not available");
+ }
+
+ return retval;
+}
+
#endif /* SDL_FILESYSTEM_UNIX */
diff --git a/src/filesystem/windows/SDL_sysfilesystem.c b/src/filesystem/windows/SDL_sysfilesystem.c
index 753ca091edaa..46acf098bb59 100644
--- a/src/filesystem/windows/SDL_sysfilesystem.c
+++ b/src/filesystem/windows/SDL_sysfilesystem.c
@@ -26,7 +26,14 @@
/* System dependent filesystem routines */
#include "../../core/windows/SDL_windows.h"
+#include <errhandlingapi.h>
+#include <fileapi.h>
#include <shlobj.h>
+#include <libloaderapi.h>
+/* Lowercase is necessary for Wine */
+#include <knownfolders.h>
+#include <initguid.h>
+#include <windows.h>
char *
SDL_GetBasePath(void)
@@ -166,6 +173,179 @@ SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
+char *
+SDL_GetPath(SDL_Folder folder)
+{
+ typedef HRESULT (*SHGKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *);
+ char *retval;
+ HMODULE lib = LoadLibrary(L"Shell32.dll");
+ SHGKFP SHGetKnownFolderPath_ = (SHGKFP) GetProcAddress(lib,
+ "SHGetKnownFolderPath");
+
+ if (!SHGetKnownFolderPath_)
+ {
+ int type;
+ HRESULT result;
+ wchar_t path[MAX_PATH];
+
+ switch (folder)
+ {
+ case SDL_FOLDER_HOME:
+ type = CSIDL_PROFILE;
+ break;
+
+ case SDL_FOLDER_DESKTOP:
+ type = CSIDL_DESKTOP;
+ break;
+
+ case SDL_FOLDER_DOCUMENTS:
+ type = CSIDL_MYDOCUMENTS;
+ break;
+
+ case SDL_FOLDER_DOWNLOADS:
+ SDL_SetError("Downloads folder unavailable before Vista");
+ return NULL;
+
+ case SDL_FOLDER_MUSIC:
+ type = CSIDL_MYMUSIC;
+ break;
+
+ case SDL_FOLDER_PICTURES:
+ type = CSIDL_MYPICTURES;
+ break;
+
+ case SDL_FOLDER_PUBLICSHARE:
+ SDL_SetError("Public share unavailable on Windows");
+ return NULL;
+
+ case SDL_FOLDER_SAVEDGAMES:
+ SDL_SetError("Saved games unavailable before Vista");
+ return NULL;
+
+ case SDL_FOLDER_SCREENSHOTS:
+ SDL_SetError("Screenshots folder unavailable before Vista");
+ return NULL;
+
+ case SDL_FOLDER_TEMPLATES:
+ type = CSIDL_TEMPLATES;
+ break;
+
+ case SDL_FOLDER_VIDEOS:
+ type = CSIDL_MYVIDEO;
+ break;
+
+ default:
+ SDL_SetError("Unsupported SDL_Folder on Windows before Vista: %d",
+ (int) folder);
+ return NULL;
+ };
+
+ /* Create the OS-specific folder if it doesn't already exist */
+ type |= CSIDL_FLAG_CREATE;
+
+#if 0
+ /* Apparently the oldest, but not supported in modern Windows */
+ HRESULT result = SHGetSpecialFolderPath(NULL, path, type, TRUE);
+#endif
+
+ /* Windows 2000/XP and later, deprecated as of Windows 10 (still
+ available), available in Wine (tested 6.0.3) */
+ result = SHGetFolderPathW(NULL, type, NULL, SHGFP_TYPE_CURRENT, path);
+
+ /* use `!= TRUE` for SHGetSpecialFolderPath */
+ if (result != S_OK)
+ {
+ SDL_SetError("Couldn't get folder, windows-specific error: %ld",
+ result);
+ return NULL;
+ }
+
+ retval = (char *) SDL_malloc((SDL_wcslen(path) + 1) * 2);
+ if (retval == NULL)
+ {
+ SDL_OutOfMemory();
+ return NULL;
+ }
+ retval = WIN_StringToUTF8W(path);
+ return retval;
+ }
+ else
+ {
+ KNOWNFOLDERID type;
+ HRESULT result;
+ wchar_t *path;
+
+ switch (folder)
+ {
+ case SDL_FOLDER_HOME:
+ type = FOLDERID_Profile;
+ break;
+
+ case SDL_FOLDER_DESKTOP:
+ type = FOLDERID_Desktop;
+ break;
+
+ case SDL_FOLDER_DOCUMENTS:
+ type = FOLDERID_Documents;
+ break;
+
+ case SDL_FOLDER_DOWNLOADS:
+ type = FOLDERID_Downloads;
+ break;
+
+ case SDL_FOLDER_MUSIC:
+ type = FOLDERID_Music;
+ break;
+
+ case SDL_FOLDER_PICTURES:
+ type = FOLDERID_Pictures;
+ break;
+
+ case SDL_FOLDER_PUBLICSHARE:
+ SDL_SetError("Public share unavailable on Windows");
+ return NULL;
+
+ case SDL_FOLDER_SAVEDGAMES:
+ type = FOLDERID_SavedGames;
+ break;
+
+ case SDL_FOLDER_SCREENSHOTS:
+ type = FOLDERID_Screenshots;
+ break;
+
+ case SDL_FOLDER_TEMPLATES:
+ type = FOLDERID_Templates;
+ break;
+
+ case SDL_FOLDER_VIDEOS:
+ type = FOLDERID_Videos;
+ break;
+
+ default:
+ SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
+ return NULL;
+ };
+
+ result = SHGetKnownFolderPath_(&type, KF_FLAG_CREATE, NULL, &path);
+
+ if (result != S_OK)
+ {
+ SDL_SetError("Couldn't get folder, windows-specific error: %ld",
+ result);
+ return NULL;
+ }
+
+ retval = (char *) SDL_malloc((SDL_wcslen(path) + 1) * 2);
+ if (retval == NULL)
+ {
+ SDL_OutOfMemory();
+ return NULL;
+ }
+ retval = WIN_StringToUTF8W(path);
+ return retval;
+ }
+}
+
#endif /* SDL_FILESYSTEM_WINDOWS */
#ifdef SDL_FILESYSTEM_XBOX
@@ -182,4 +362,11 @@ SDL_GetPrefPath(const char *org, const char *app)
SDL_Unsupported();
return NULL;
}
+
+char *
+SDL_GetPath(SDL_Folder folder)
+{
+ SDL_Unsupported();
+ return NULL;
+}
#endif /* SDL_FILESYSTEM_XBOX */