SDL: Implement SDL_GetPath stub for all OSes

From 7f2ef4d02f35dbbe45230e09541b626bd2ecbc52 Mon Sep 17 00:00:00 2001
From: Semphris <[EMAIL REDACTED]>
Date: Fri, 5 May 2023 12:38:35 -0400
Subject: [PATCH] Implement SDL_GetPath stub for all OSes

---
 include/SDL3/SDL_filesystem.h                 |  47 +++++---
 src/filesystem/android/SDL_sysfilesystem.c    |   8 ++
 src/filesystem/cocoa/SDL_sysfilesystem.m      | 101 +++++++++---------
 src/filesystem/dummy/SDL_sysfilesystem.c      |   9 +-
 src/filesystem/emscripten/SDL_sysfilesystem.c |  25 +++++
 src/filesystem/haiku/SDL_sysfilesystem.cc     |  49 +++++++++
 src/filesystem/n3ds/SDL_sysfilesystem.c       |   7 ++
 src/filesystem/ps2/SDL_sysfilesystem.c        |   7 ++
 src/filesystem/psp/SDL_sysfilesystem.c        |   7 ++
 src/filesystem/riscos/SDL_sysfilesystem.c     |   7 ++
 src/filesystem/unix/SDL_sysfilesystem.c       | 100 +++++++++--------
 src/filesystem/vita/SDL_sysfilesystem.c       |   7 ++
 src/filesystem/winrt/SDL_sysfilesystem.cpp    |   7 ++
 13 files changed, 254 insertions(+), 127 deletions(-)

diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h
index 8811a9ae956e..91e710b8d39b 100644
--- a/include/SDL3/SDL_filesystem.h
+++ b/include/SDL3/SDL_filesystem.h
@@ -145,6 +145,24 @@ extern DECLSPEC char *SDLCALL SDL_GetPrefPath(const char *org, const char *app);
  * involves extra OS-specific functionality to remember the file's original
  * location.
  *
+ * The folders supported per platform are:
+ *
+ * |             | Windows | macOS/iOS | tvOS | Unix (XDG) | Haiku | Emscripten |
+ * | ----------- | ------- | --------- | ---- | ---------- | ----- | ---------- |
+ * | HOME        | X       | X         |      | X          | X     | X          |
+ * | DESKTOP     | X       | X         |      | X          | X     |            |
+ * | DOCUMENTS   | X       | X         |      | X          |       |            |
+ * | DOWNLOADS   | Vista+  | X         |      | X          |       |            |
+ * | MUSIC       | X       | X         |      | X          |       |            |
+ * | PICTURES    | X       | X         |      | X          |       |            |
+ * | PUBLICSHARE |         | X         |      | X          |       |            |
+ * | SAVEDGAMES  | Vista+  |           |      |            |       |            |
+ * | SCREENSHOTS | Vista+  |           |      |            |       |            |
+ * | TEMPLATES   | X       | X         |      | X          |       |            |
+ * | VIDEOS      | X       | X         |      | X          |       |            |
+ *
+ * Note that on macOS/iOS, the Videos folder is called "Movies".
+ *
  * \sa SDL_GetPath
  */
 typedef enum
@@ -152,43 +170,38 @@ 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. */
+      fallback to store a user's documents. */
   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. */
+      environment may not have desktop icons. */
   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. */
+  /** User document files, possibly application-specific. This is a good
+      place to save a user's projects. */
   SDL_FOLDER_DOCUMENTS,
-  /** Generic landing folder for files downloaded from the internet. Supported
-      on Windows Vista and later, macOS and Unix with XDG. */
+  /** Standard folder for user files downloaded from the internet. */
   SDL_FOLDER_DOWNLOADS,
   /** Music files that can be played using a standard music player (mp3,
-      ogg...). Supported on Windows, macOS and Unix with XDG. */
+      ogg...). */
   SDL_FOLDER_MUSIC,
   /** Image files that can be displayed using a standard viewer (png,
-      jpg...). Supported on Windows, macOS and Unix with XDG. */
+      jpg...). */
   SDL_FOLDER_PICTURES,
   /** Files that are meant to be shared with other users on the same
-      computer. Supported on macOS and Unix with XDG. */
+      computer. */
   SDL_FOLDER_PUBLICSHARE,
-  /** Save files for games. Supported on Windows Vista and later. */
+  /** Save files for games. */
   SDL_FOLDER_SAVEDGAMES,
-  /** Application screenshots. Supported on Windows Vista and later. */
+  /** Application screenshots. */
   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. */
+      new file. */
   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. */
+      webm...). */
   SDL_FOLDER_VIDEOS,
 } SDL_Folder;
 
diff --git a/src/filesystem/android/SDL_sysfilesystem.c b/src/filesystem/android/SDL_sysfilesystem.c
index e5849a89a8dc..8a2bd75c5d17 100644
--- a/src/filesystem/android/SDL_sysfilesystem.c
+++ b/src/filesystem/android/SDL_sysfilesystem.c
@@ -52,4 +52,12 @@ SDL_GetPrefPath(const char *org, const char *app)
     return NULL;
 }
 
+char *SDL_GetPath(SDL_Folder folder)
+{
+    /* TODO: see https://developer.android.com/reference/android/os/Environment#lfields
+       and https://stackoverflow.com/questions/39332085/get-path-to-pictures-directory */
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* SDL_FILESYSTEM_ANDROID */
diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m
index c9880efe4596..cffb2728a9c5 100644
--- a/src/filesystem/cocoa/SDL_sysfilesystem.m
+++ b/src/filesystem/cocoa/SDL_sysfilesystem.m
@@ -132,8 +132,7 @@
     }
 }
 
-char *
-SDL_GetPath(SDL_Folder folder)
+char *SDL_GetPath(SDL_Folder folder)
 {
     @autoreleasepool {
 #if TARGET_OS_TV
@@ -147,87 +146,83 @@
         NSString *str;
         char *ptr;
 
-        switch (folder)
-        {
-            case SDL_FOLDER_HOME:
-                base = SDL_getenv("HOME");
+        switch (folder) {
+        case SDL_FOLDER_HOME:
+            base = SDL_getenv("HOME");
 
-                if (!base)
-                {
-                    SDL_SetError("No $HOME environment variable available");
-                }
+            if (!base) {
+                SDL_SetError("No $HOME environment variable available");
+            }
 
-                retval = SDL_strdup(base);
+            retval = SDL_strdup(base);
 
-                if (!retval)
-                    SDL_OutOfMemory();
+            if (!retval) {
+                SDL_OutOfMemory();
+            }
 
-                return retval;
+            return retval;
 
-            case SDL_FOLDER_DESKTOP:
-                dir = NSDesktopDirectory;
-                break;
+        case SDL_FOLDER_DESKTOP:
+            dir = NSDesktopDirectory;
+            break;
 
-            case SDL_FOLDER_DOCUMENTS:
-                dir = NSDocumentDirectory;
-                break;
+        case SDL_FOLDER_DOCUMENTS:
+            dir = NSDocumentDirectory;
+            break;
 
-            case SDL_FOLDER_DOWNLOADS:
-                dir = NSDownloadsDirectory;
-                break;
+        case SDL_FOLDER_DOWNLOADS:
+            dir = NSDownloadsDirectory;
+            break;
 
-            case SDL_FOLDER_MUSIC:
-                dir = NSMusicDirectory;
-                break;
+        case SDL_FOLDER_MUSIC:
+            dir = NSMusicDirectory;
+            break;
 
-            case SDL_FOLDER_PICTURES:
-                dir = NSPicturesDirectory;
-                break;
+        case SDL_FOLDER_PICTURES:
+            dir = NSPicturesDirectory;
+            break;
 
-            case SDL_FOLDER_PUBLICSHARE:
-                dir = NSSharedPublicDirectory;
-                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_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_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_TEMPLATES:
+            SDL_SetError("Templates folder not supported on Cocoa");
+            return NULL;
 
-            case SDL_FOLDER_VIDEOS:
-                dir = NSMoviesDirectory;
-                break;
+        case SDL_FOLDER_VIDEOS:
+            dir = NSMoviesDirectory;
+            break;
 
-            default:
-                SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
-                return NULL;
+        default:
+            SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
+            return NULL;
         };
 
         array = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);
 
-        if ([array count] <= 0)
-        {
+        if ([array count] <= 0) {
             SDL_SetError("Directory not found");
             return NULL;
         }
 
         str = [array objectAtIndex:0];
         base = [str fileSystemRepresentation];
-        if (!base)
-        {
+        if (!base) {
             SDL_SetError("Couldn't get folder path");
             return NULL;
         }
 
         retval = SDL_strdup(base);
-        if (retval == NULL)
-        {
+        if (retval == NULL) {
             SDL_OutOfMemory();
             return NULL;
         }
diff --git a/src/filesystem/dummy/SDL_sysfilesystem.c b/src/filesystem/dummy/SDL_sysfilesystem.c
index d341c740f5db..58c91a37fac7 100644
--- a/src/filesystem/dummy/SDL_sysfilesystem.c
+++ b/src/filesystem/dummy/SDL_sysfilesystem.c
@@ -25,22 +25,19 @@
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 /* System dependent filesystem routines                                */
 
-char *
-SDL_GetBasePath(void)
+char *SDL_GetBasePath(void)
 {
     SDL_Unsupported();
     return NULL;
 }
 
-char *
-SDL_GetPrefPath(const char *org, const char *app)
+char *SDL_GetPrefPath(const char *org, const char *app)
 {
     SDL_Unsupported();
     return NULL;
 }
 
-char *
-SDL_GetPath(SDL_Folder folder)
+char *SDL_GetPath(SDL_Folder folder)
 {
     SDL_Unsupported();
     return NULL;
diff --git a/src/filesystem/emscripten/SDL_sysfilesystem.c b/src/filesystem/emscripten/SDL_sysfilesystem.c
index 3d8bba537b9c..de98dcf6827c 100644
--- a/src/filesystem/emscripten/SDL_sysfilesystem.c
+++ b/src/filesystem/emscripten/SDL_sysfilesystem.c
@@ -85,4 +85,29 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+char *SDL_GetPath(SDL_Folder folder)
+{
+    const char *home = NULL;
+    char *retval;
+
+    if (folder != SDL_FOLDER_HOME) {
+        SDL_SetError("Emscripten only supports the home folder");
+        return NULL;
+    }
+
+    home = SDL_getenv("HOME");
+    if (!home) {
+        SDL_SetError("No $HOME environment variable available");
+        return NULL;
+    }
+
+    retval = SDL_strdup(home);
+
+    if (!retval) {
+        SDL_OutOfMemory();
+    }
+
+    return retval;
+}
+
 #endif /* SDL_FILESYSTEM_EMSCRIPTEN */
diff --git a/src/filesystem/haiku/SDL_sysfilesystem.cc b/src/filesystem/haiku/SDL_sysfilesystem.cc
index c8378e7b93d4..9cb744ae22ee 100644
--- a/src/filesystem/haiku/SDL_sysfilesystem.cc
+++ b/src/filesystem/haiku/SDL_sysfilesystem.cc
@@ -99,4 +99,53 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+char *SDL_GetPath(SDL_Folder folder)
+{
+    const char *home = NULL;
+    char *retval;
+
+    home = SDL_getenv("HOME");
+    if (!home) {
+        SDL_SetError("No $HOME environment variable available");
+        return NULL;
+    }
+
+    switch (folder) {
+    case SDL_FOLDER_HOME:
+        retval = SDL_strdup(home);
+
+        if (!retval) {
+            SDL_OutOfMemory();
+        }
+
+        return retval;
+
+        /* TODO: Is Haiku's desktop folder always ~/Desktop/ ? */
+    case SDL_FOLDER_DESKTOP:
+        retval = (char *) SDL_malloc(SDL_strlen(home) + 10);
+
+        if (!retval) {
+            SDL_OutOfMemory();
+        }
+
+        SDL_strlcpy(retval, home, SDL_strlen(home) + 10);
+        SDL_strlcat(retval, "/Desktop/", SDL_strlen(home) + 10);
+
+        return retval;
+
+    case SDL_FOLDER_DOCUMENTS:
+    case SDL_FOLDER_DOWNLOADS:
+    case SDL_FOLDER_MUSIC:
+    case SDL_FOLDER_PICTURES:
+    case SDL_FOLDER_PUBLICSHARE:
+    case SDL_FOLDER_SAVEDGAMES:
+    case SDL_FOLDER_SCREENSHOTS:
+    case SDL_FOLDER_TEMPLATES:
+    case SDL_FOLDER_VIDEOS:
+    default:
+        SDL_SetError("Only HOME and DESKTOP available on Haiku");
+        return NULL;
+    }
+}
+
 #endif /* SDL_FILESYSTEM_HAIKU */
diff --git a/src/filesystem/n3ds/SDL_sysfilesystem.c b/src/filesystem/n3ds/SDL_sysfilesystem.c
index 928a4d7979c8..a2d8587c1970 100644
--- a/src/filesystem/n3ds/SDL_sysfilesystem.c
+++ b/src/filesystem/n3ds/SDL_sysfilesystem.c
@@ -61,6 +61,13 @@ SDL_GetPrefPath(const char *org, const char *app)
     return pref_path;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 SDL_FORCE_INLINE char *
 MakePrefPath(const char *app)
 {
diff --git a/src/filesystem/ps2/SDL_sysfilesystem.c b/src/filesystem/ps2/SDL_sysfilesystem.c
index e4aaad9024d0..e8e9e8282cad 100644
--- a/src/filesystem/ps2/SDL_sysfilesystem.c
+++ b/src/filesystem/ps2/SDL_sysfilesystem.c
@@ -104,4 +104,11 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* SDL_FILESYSTEM_PS2 */
diff --git a/src/filesystem/psp/SDL_sysfilesystem.c b/src/filesystem/psp/SDL_sysfilesystem.c
index e9194a5039c9..227f58d0e158 100644
--- a/src/filesystem/psp/SDL_sysfilesystem.c
+++ b/src/filesystem/psp/SDL_sysfilesystem.c
@@ -71,4 +71,11 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* SDL_FILESYSTEM_PSP */
diff --git a/src/filesystem/riscos/SDL_sysfilesystem.c b/src/filesystem/riscos/SDL_sysfilesystem.c
index 2c4cad88a298..96e4562efc6a 100644
--- a/src/filesystem/riscos/SDL_sysfilesystem.c
+++ b/src/filesystem/riscos/SDL_sysfilesystem.c
@@ -201,4 +201,11 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* SDL_FILESYSTEM_RISCOS */
diff --git a/src/filesystem/unix/SDL_sysfilesystem.c b/src/filesystem/unix/SDL_sysfilesystem.c
index e3ca8e198739..088afdee8397 100644
--- a/src/filesystem/unix/SDL_sysfilesystem.c
+++ b/src/filesystem/unix/SDL_sysfilesystem.c
@@ -521,8 +521,7 @@ xdg_user_dir_lookup (const char *type)
     return NULL;
 }
 
-char *
-SDL_GetPath(SDL_Folder folder)
+char *SDL_GetPath(SDL_Folder folder)
 {
     const char *param = NULL;
     char *retval;
@@ -537,80 +536,79 @@ SDL_GetPath(SDL_Folder folder)
         PICTURES
         VIDEOS
     */
-    switch(folder)
-    {
-        case SDL_FOLDER_HOME:
-            param = SDL_getenv("HOME");
+    switch(folder) {
+    case SDL_FOLDER_HOME:
+        param = SDL_getenv("HOME");
 
-            if (!param)
-            {
-                SDL_SetError("No $HOME environment variable available");
-            }
+        if (!param) {
+            SDL_SetError("No $HOME environment variable available");
+            return NULL;
+        }
 
-            retval = SDL_strdup(param);
+        retval = SDL_strdup(param);
 
-            if (!retval)
-                SDL_OutOfMemory();
+        if (!retval) {
+            SDL_OutOfMemory();
+        }
 
-            return retval;
+        return retval;
 
-        case SDL_FOLDER_DESKTOP:
-            param = "DESKTOP";
-            break;
+    case SDL_FOLDER_DESKTOP:
+        param = "DESKTOP";
+        break;
 
-        case SDL_FOLDER_DOCUMENTS:
-            param = "DOCUMENTS";
-            break;
+    case SDL_FOLDER_DOCUMENTS:
+        param = "DOCUMENTS";
+        break;
 
-        case SDL_FOLDER_DOWNLOADS:
-            param = "DOWNLOAD";
-            break;
+    case SDL_FOLDER_DOWNLOADS:
+        param = "DOWNLOAD";
+        break;
 
-        case SDL_FOLDER_MUSIC:
-            param = "MUSIC";
-            break;
+    case SDL_FOLDER_MUSIC:
+        param = "MUSIC";
+        break;
 
-        case SDL_FOLDER_PICTURES:
-            param = "PICTURES";
-            break;
+    case SDL_FOLDER_PICTURES:
+        param = "PICTURES";
+        break;
 
-        case SDL_FOLDER_PUBLICSHARE:
-            param = "PUBLICSHARE";
-            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_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_SCREENSHOTS:
+        SDL_SetError("Screenshots folder unavailable on XDG");
+        return NULL;
 
-        case SDL_FOLDER_TEMPLATES:
-            param = "TEMPLATES";
-            break;
+    case SDL_FOLDER_TEMPLATES:
+        param = "TEMPLATES";
+        break;
 
-        case SDL_FOLDER_VIDEOS:
-            param = "VIDEOS";
-            break;
+    case SDL_FOLDER_VIDEOS:
+        param = "VIDEOS";
+        break;
 
-        default:
-            SDL_SetError("Invalid SDL_Folder: %d", (int) folder);
-            return NULL;
+    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)
-    {
+    if (!param) {
         SDL_SetError("No corresponding XDG user directory");
         return NULL;
     }
 
     retval = xdg_user_dir_lookup(param);
 
-    if (!retval)
-    {
+    if (!retval) {
         SDL_SetError("XDG directory not available");
+        return NULL;
     }
 
     return retval;
diff --git a/src/filesystem/vita/SDL_sysfilesystem.c b/src/filesystem/vita/SDL_sysfilesystem.c
index 72aa2d25a348..dab1872c053a 100644
--- a/src/filesystem/vita/SDL_sysfilesystem.c
+++ b/src/filesystem/vita/SDL_sysfilesystem.c
@@ -85,4 +85,11 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* SDL_FILESYSTEM_VITA */
diff --git a/src/filesystem/winrt/SDL_sysfilesystem.cpp b/src/filesystem/winrt/SDL_sysfilesystem.cpp
index 50f4c6c12129..33218c60a0d4 100644
--- a/src/filesystem/winrt/SDL_sysfilesystem.cpp
+++ b/src/filesystem/winrt/SDL_sysfilesystem.cpp
@@ -232,4 +232,11 @@ SDL_GetPrefPath(const char *org, const char *app)
     return retval;
 }
 
+/* TODO */
+char *SDL_GetPath(SDL_Folder folder)
+{
+    SDL_Unsupported();
+    return NULL;
+}
+
 #endif /* __WINRT__ */