SDL: Added filesystem operations to SDL_Storage

From f460f42e707aca4fc7e08fc8aaff899d9759af0d Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 16 Mar 2024 11:03:41 -0700
Subject: [PATCH] Added filesystem operations to SDL_Storage

---
 include/SDL3/SDL_storage.h               |  95 ++++++++++-
 src/storage/SDL_storage.c                | 116 +++++++++++--
 src/storage/generic/SDL_genericstorage.c | 198 ++++++++++++++---------
 src/storage/steam/SDL_steamstorage.c     |  55 ++++---
 4 files changed, 353 insertions(+), 111 deletions(-)

diff --git a/include/SDL3/SDL_storage.h b/include/SDL3/SDL_storage.h
index 1892b8cf29bab..303bdc49045da 100644
--- a/include/SDL3/SDL_storage.h
+++ b/include/SDL3/SDL_storage.h
@@ -29,7 +29,7 @@
 #define SDL_storage_h_
 
 #include <SDL3/SDL_stdinc.h>
-#include <SDL3/SDL_mutex.h>
+#include <SDL3/SDL_filesystem.h>
 #include <SDL3/SDL_properties.h>
 
 #include <SDL3/SDL_begin_code.h>
@@ -43,17 +43,36 @@ extern "C" {
 
 typedef struct SDL_StorageInterface
 {
+    /* Called when the storage is closed */
     int (SDLCALL *close)(void *userdata);
 
+    /* Optional, returns whether the storage is currently ready for access */
     SDL_bool (SDLCALL *ready)(void *userdata);
 
-    int (SDLCALL *fileSize)(void *userdata, const char *path, Uint64 *length);
+    /* Enumerate a directory, optional for write-only storage */
+    int (SDLCALL *enumerate)(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata);
 
-    int (SDLCALL *readFile)(void *userdata, const char *path, void *destination, Uint64 length);
+    /* Get path information, optional for write-only storage */
+    int (SDLCALL *info)(void *userdata, const char *path, SDL_PathInfo *info);
 
-    int (SDLCALL *writeFile)(void *userdata, const char *path, const void *source, Uint64 length);
+    /* Read a file from storage, optional for write-only storage */
+    int (SDLCALL *read_file)(void *userdata, const char *path, void *destination, Uint64 length);
+
+    /* Write a file to storage, optional for read-only storage */
+    int (SDLCALL *write_file)(void *userdata, const char *path, const void *source, Uint64 length);
+
+    /* Create a directory, optional for read-only storage */
+    int (SDLCALL *mkdir)(void *userdata, const char *path);
+
+    /* Remove a file or empty directory, optional for read-only storage */
+    int (SDLCALL *remove)(void *userdata, const char *path);
+
+    /* Rename a path, optional for read-only storage */
+    int (SDLCALL *rename)(void *userdata, const char *oldpath, const char *newpath);
+
+    /* Get the space remaining, optional for read-only storage */
+    Uint64 (SDLCALL *space_remaining)(void *userdata);
 
-    Uint64 (SDLCALL *spaceRemaining)(void *userdata);
 } SDL_StorageInterface;
 
 typedef struct SDL_Storage SDL_Storage;
@@ -267,6 +286,70 @@ extern DECLSPEC int SDLCALL SDL_ReadStorageFile(SDL_Storage *storage, const char
  */
 extern DECLSPEC int SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length);
 
+/**
+ * Create a directory in a writable storage container.
+ *
+ * \param storage a storage container
+ * \param path the path of the directory to create
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_CreateStorageDirectory(SDL_Storage *storage, const char *path);
+
+/**
+ * Enumerate a directory in a storage container.
+ *
+ * \param storage a storage container
+ * \param path the path of the directory to enumerate
+ * \param callback a function that is called for each entry in the directory
+ * \param userdata a pointer that is passed to `callback`
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_EnumerateStorageDirectory(SDL_Storage *storage, const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata);
+
+/**
+ * Remove a file or an empty directory in a writable storage container.
+ *
+ * \param storage a storage container
+ * \param path the path of the directory to enumerate
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_RemoveStoragePath(SDL_Storage *storage, const char *path);
+
+/**
+ * Rename a file or directory in a writable storage container.
+ *
+ * \param storage a storage container
+ * \param oldpath the old path
+ * \param newpath the new path
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_RenameStoragePath(SDL_Storage *storage, const char *oldpath, const char *newpath);
+
+/**
+ * Get information about a filesystem path in a storage container.
+ *
+ * \param storage a storage container
+ * \param path the path to query
+ * \param info a pointer filled in with information about the path
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern DECLSPEC int SDLCALL SDL_GetStoragePathInfo(SDL_Storage *storage, const char *path, SDL_PathInfo *info);
+
 /**
  * Queries the remaining space in a storage container.
  *
@@ -282,9 +365,7 @@ extern DECLSPEC int SDL_WriteStorageFile(SDL_Storage *storage, const char *path,
  * \sa SDL_StorageReady
  * \sa SDL_GetStorageFileSize
  * \sa SDL_ReadStorageFile
- * \sa SDL_ReadStorageFileAsync
  * \sa SDL_WriteStorageFile
- * \sa SDL_WriteStorageFileAsync
  */
 extern DECLSPEC Uint64 SDLCALL SDL_GetStorageSpaceRemaining(SDL_Storage *storage);
 
diff --git a/src/storage/SDL_storage.c b/src/storage/SDL_storage.c
index 4d496caead519..1111d2a7813ba 100644
--- a/src/storage/SDL_storage.c
+++ b/src/storage/SDL_storage.c
@@ -152,7 +152,7 @@ SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata)
     }
 
     storage = (SDL_Storage*) SDL_malloc(sizeof(SDL_Storage));
-    if (storage == NULL) {
+    if (!storage) {
         SDL_OutOfMemory();
         return NULL;
     }
@@ -185,41 +185,133 @@ SDL_bool SDL_StorageReady(SDL_Storage *storage)
 
 int SDL_GetStorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length)
 {
-    CHECK_STORAGE_MAGIC()
+    SDL_PathInfo info;
 
-    return storage->iface.fileSize(storage->userdata, path, length);
+    if (SDL_GetStoragePathInfo(storage, path, &info) < 0) {
+        return -1;
+    }
+    if (length) {
+        *length = info.size;
+    }
+    return 0;
 }
 
 int SDL_ReadStorageFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length)
 {
     CHECK_STORAGE_MAGIC()
 
-    if (storage->iface.readFile == NULL) {
-        return SDL_SetError("Storage container does not have read capability");
+    if (!path) {
+        return SDL_InvalidParamError("path");
     }
 
-    return storage->iface.readFile(storage->userdata, path, destination, length);
+    if (!storage->iface.read_file) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.read_file(storage->userdata, path, destination, length);
 }
 
 int SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length)
 {
     CHECK_STORAGE_MAGIC()
 
-    if (storage->iface.writeFile == NULL) {
-        return SDL_SetError("Storage container does not have write capability");
+    if (!path) {
+        return SDL_InvalidParamError("path");
+    }
+
+    if (!storage->iface.write_file) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.write_file(storage->userdata, path, source, length);
+}
+
+int SDL_CreateStorageDirectory(SDL_Storage *storage, const char *path)
+{
+    CHECK_STORAGE_MAGIC()
+
+    if (!path) {
+        return SDL_InvalidParamError("path");
+    }
+
+    if (!storage->iface.mkdir) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.mkdir(storage->userdata, path);
+}
+
+int SDL_EnumerateStorageDirectory(SDL_Storage *storage, const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata)
+{
+    CHECK_STORAGE_MAGIC()
+
+    if (!path) {
+        return SDL_InvalidParamError("path");
+    }
+
+    if (!storage->iface.enumerate) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.enumerate(storage->userdata, path, callback, userdata);
+}
+
+int SDL_RemoveStoragePath(SDL_Storage *storage, const char *path)
+{
+    CHECK_STORAGE_MAGIC()
+
+    if (!path) {
+        return SDL_InvalidParamError("path");
+    }
+
+    if (!storage->iface.remove) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.remove(storage->userdata, path);
+}
+
+int SDL_RenameStoragePath(SDL_Storage *storage, const char *oldpath, const char *newpath)
+{
+    CHECK_STORAGE_MAGIC()
+
+    if (!oldpath) {
+        return SDL_InvalidParamError("oldpath");
+    }
+    if (!newpath) {
+        return SDL_InvalidParamError("newpath");
+    }
+
+    if (!storage->iface.rename) {
+        return SDL_Unsupported();
+    }
+
+    return storage->iface.rename(storage->userdata, oldpath, newpath);
+}
+
+int SDL_GetStoragePathInfo(SDL_Storage *storage, const char *path, SDL_PathInfo *info)
+{
+    CHECK_STORAGE_MAGIC()
+
+    if (!path) {
+        return SDL_InvalidParamError("path");
+    }
+
+    if (!storage->iface.info) {
+        return SDL_Unsupported();
     }
 
-    return storage->iface.writeFile(storage->userdata, path, source, length);
+    return storage->iface.info(storage->userdata, path, info);
 }
 
 Uint64 SDL_GetStorageSpaceRemaining(SDL_Storage *storage)
 {
     CHECK_STORAGE_MAGIC_RET(0)
 
-    if (storage->iface.spaceRemaining == NULL) {
-        SDL_SetError("Storage container does not have write capability");
+    if (!storage->iface.space_remaining) {
+        SDL_Unsupported();
         return 0;
     }
 
-    return storage->iface.spaceRemaining(storage->userdata);
+    return storage->iface.space_remaining(storage->userdata);
 }
diff --git a/src/storage/generic/SDL_genericstorage.c b/src/storage/generic/SDL_genericstorage.c
index e1088b160f97a..e624a541d4b23 100644
--- a/src/storage/generic/SDL_genericstorage.c
+++ b/src/storage/generic/SDL_genericstorage.c
@@ -23,116 +23,160 @@
 
 #include "../SDL_sysstorage.h"
 
+
 static char *GENERIC_INTERNAL_CreateFullPath(const char *base, const char *relative)
 {
-    size_t len = 0;
-
-    if (base) {
-        len += SDL_strlen(base);
+    if (!base) {
+        return SDL_strdup(relative);
     }
-    len += SDL_strlen(relative) + 1;
 
-    char *result = (char*) SDL_malloc(len);
+    size_t len = SDL_strlen(base) + SDL_strlen(relative) + 1;
+    char *result = (char*)SDL_malloc(len);
     if (result != NULL) {
         SDL_snprintf(result, len, "%s%s", base, relative);
     }
     return result;
 }
 
-static int GENERIC_StorageClose(void *userdata)
+static int GENERIC_CloseStorage(void *userdata)
 {
     SDL_free(userdata);
     return 0;
 }
 
-static SDL_bool GENERIC_StorageReady(void *userdata)
+static int GENERIC_EnumerateStorageDirectory(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)
 {
-    return SDL_TRUE;
+    int result = -1;
+
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        result = SDL_EnumerateDirectory(fullpath, callback, callback_userdata);
+
+        SDL_free(fullpath);
+    }
+    return result;
 }
 
-static int GENERIC_StorageFileSize(void *userdata, const char *path, Uint64 *length)
+static int GENERIC_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
 {
-    SDL_IOStream *stream;
-    Sint64 result;
+    int result = -1;
 
-    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char*) userdata, path);
-    if (fullpath == NULL) {
-        return SDL_OutOfMemory();
-    }
-    stream = SDL_IOFromFile(fullpath, "rb");
-    SDL_free(fullpath);
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        result = SDL_GetPathInfo(fullpath, info);
 
-    result = SDL_SizeIO(stream);
-    SDL_CloseIO(stream);
-    if (result < 0) {
-        return result;
+        SDL_free(fullpath);
     }
-
-    /* FIXME: Should SDL_SizeIO use u64 now...? */
-    *length = (Uint64) result;
-    return 0;
+    return result;
 }
 
-static int GENERIC_StorageReadFile(void *userdata, const char *path, void *destination, Uint64 length)
+static int GENERIC_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
 {
-    SDL_IOStream *stream;
-    char *fullpath;
-    int fullread;
+    int result = -1;
 
     if (length > SDL_SIZE_MAX) {
         return SDL_SetError("Read size exceeds SDL_SIZE_MAX");
     }
 
-    fullpath = GENERIC_INTERNAL_CreateFullPath((char*) userdata, path);
-    if (fullpath == NULL) {
-        return SDL_OutOfMemory();
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        SDL_IOStream *stream = SDL_IOFromFile(fullpath, "rb");
+        if (stream) {
+            /* FIXME: Should SDL_ReadIO use u64 now...? */
+            if (SDL_ReadIO(stream, destination, (size_t)length) == length) {
+                result = 0;
+            }
+            SDL_CloseIO(stream);
+        }
+        SDL_free(fullpath);
     }
-    stream = SDL_IOFromFile(fullpath, "rb");
-    SDL_free(fullpath);
-
-    /* FIXME: Should SDL_ReadIO use u64 now...? */
-    fullread = (SDL_ReadIO(stream, destination, (size_t) length) == length);
-    SDL_CloseIO(stream);
-    return fullread - 1;
+    return result;
 }
 
-static int GENERIC_StorageWriteFile(void *userdata, const char *path, const void *source, Uint64 length)
+static int GENERIC_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
 {
-    /* TODO: Recursively create subdirectories with SDL_mkdir */
-    SDL_IOStream *stream;
-    char *fullpath;
-    int fullwrite;
+    /* TODO: Recursively create subdirectories with SDL_CreateDirectory */
+    int result = -1;
 
     if (length > SDL_SIZE_MAX) {
         return SDL_SetError("Write size exceeds SDL_SIZE_MAX");
     }
 
-    fullpath = GENERIC_INTERNAL_CreateFullPath((char*) userdata, path);
-    if (fullpath == NULL) {
-        return SDL_OutOfMemory();
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        SDL_IOStream *stream = SDL_IOFromFile(fullpath, "wb");
+
+        if (stream) {
+            /* FIXME: Should SDL_WriteIO use u64 now...? */
+            if (SDL_WriteIO(stream, source, (size_t)length) == length) {
+                result = 0;
+            }
+            SDL_CloseIO(stream);
+        }
+        SDL_free(fullpath);
     }
-    stream = SDL_IOFromFile(fullpath, "wb");
-    SDL_free(fullpath);
+    return result;
+}
 
-    /* FIXME: Should SDL_WriteIO use u64 now...? */
-    fullwrite = (SDL_WriteIO(stream, source, (size_t) length) == length);
-    SDL_CloseIO(stream);
-    return fullwrite - 1;
+static int GENERIC_CreateStorageDirectory(void *userdata, const char *path)
+{
+    /* TODO: Recursively create subdirectories with SDL_CreateDirectory */
+    int result = -1;
+
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        result = SDL_CreateDirectory(fullpath);
+
+        SDL_free(fullpath);
+    }
+    return result;
+}
+
+static int GENERIC_RemoveStoragePath(void *userdata, const char *path)
+{
+    int result = -1;
+
+    char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
+    if (fullpath) {
+        result = SDL_RemovePath(fullpath);
+
+        SDL_free(fullpath);
+    }
+    return result;
+}
+
+static int GENERIC_RenameStoragePath(void *userdata, const char *oldpath, const char *newpath)
+{
+    int result = -1;
+
+    char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath);
+    char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath);
+    if (fulloldpath && fullnewpath) {
+        result = SDL_RenamePath(fulloldpath, fullnewpath);
+    }
+    SDL_free(fulloldpath);
+    SDL_free(fullnewpath);
+
+    return result;
 }
 
-static Uint64 GENERIC_StorageSpaceRemaining(void *userdata)
+static Uint64 GENERIC_GetStorageSpaceRemaining(void *userdata)
 {
     /* TODO: There's totally a way to query a folder root's quota... */
     return SDL_MAX_UINT64;
 }
 
 static const SDL_StorageInterface GENERIC_title_iface = {
-    GENERIC_StorageClose,
-    GENERIC_StorageReady,
-    GENERIC_StorageFileSize,
-    GENERIC_StorageReadFile,
-    NULL,
-    NULL
+    GENERIC_CloseStorage,
+    NULL,   /* ready */
+    GENERIC_EnumerateStorageDirectory,
+    GENERIC_GetStoragePathInfo,
+    GENERIC_ReadStorageFile,
+    NULL,   /* write_file */
+    NULL,   /* mkdir */
+    NULL,   /* remove */
+    NULL,   /* rename */
+    NULL    /* space_remaining */
 };
 
 static SDL_Storage *GENERIC_Title_Create(const char *override, SDL_PropertiesID props)
@@ -163,12 +207,16 @@ TitleStorageBootStrap GENERIC_titlebootstrap = {
 };
 
 static const SDL_StorageInterface GENERIC_user_iface = {
-    GENERIC_StorageClose,
-    GENERIC_StorageReady,
-    GENERIC_StorageFileSize,
-    GENERIC_StorageReadFile,
-    GENERIC_StorageWriteFile,
-    GENERIC_StorageSpaceRemaining
+    GENERIC_CloseStorage,
+    NULL,   /* ready */
+    GENERIC_EnumerateStorageDirectory,
+    GENERIC_GetStoragePathInfo,
+    GENERIC_ReadStorageFile,
+    GENERIC_WriteStorageFile,
+    GENERIC_CreateStorageDirectory,
+    GENERIC_RemoveStoragePath,
+    GENERIC_RenameStoragePath,
+    GENERIC_GetStorageSpaceRemaining
 };
 
 static SDL_Storage *GENERIC_User_Create(const char *org, const char *app, SDL_PropertiesID props)
@@ -194,12 +242,16 @@ UserStorageBootStrap GENERIC_userbootstrap = {
 };
 
 static const SDL_StorageInterface GENERIC_file_iface = {
-    GENERIC_StorageClose,
-    GENERIC_StorageReady,
-    GENERIC_StorageFileSize,
-    GENERIC_StorageReadFile,
-    GENERIC_StorageWriteFile,
-    GENERIC_StorageSpaceRemaining
+    GENERIC_CloseStorage,
+    NULL,   /* ready */
+    GENERIC_EnumerateStorageDirectory,
+    GENERIC_GetStoragePathInfo,
+    GENERIC_ReadStorageFile,
+    GENERIC_WriteStorageFile,
+    GENERIC_CreateStorageDirectory,
+    GENERIC_RemoveStoragePath,
+    GENERIC_RenameStoragePath,
+    GENERIC_GetStorageSpaceRemaining
 };
 
 SDL_Storage *GENERIC_OpenFileStorage(const char *path)
diff --git a/src/storage/steam/SDL_steamstorage.c b/src/storage/steam/SDL_steamstorage.c
index 1b41b149af29b..cb61c3a932333 100644
--- a/src/storage/steam/SDL_steamstorage.c
+++ b/src/storage/steam/SDL_steamstorage.c
@@ -40,7 +40,7 @@ typedef struct STEAM_RemoteStorage
     #include "SDL_steamstorage_proc.h"
 } STEAM_RemoteStorage;
 
-static int STEAM_UserStorageClose(void *userdata)
+static int STEAM_CloseStorage(void *userdata)
 {
     int result = 0;
     STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
@@ -55,25 +55,30 @@ static int STEAM_UserStorageClose(void *userdata)
     return result;
 }
 
-static SDL_bool STEAM_UserStorageReady(void *userdata)
+static SDL_bool STEAM_StorageReady(void *userdata)
 {
     return SDL_TRUE;
 }
 
-static int STEAM_UserStorageFileSize(void *userdata, const char *path, Uint64 *length)
+static int STEAM_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
 {
     STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
     void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
     if (steamremotestorage == NULL) {
         return SDL_SetError("SteamRemoteStorage unavailable");
     }
-    *length = steam->SteamAPI_ISteamRemoteStorage_GetFileSize(steamremotestorage, path);
+
+    if (info) {
+        SDL_zerop(info);
+        info->type = SDL_PATHTYPE_FILE;
+        info->size = steam->SteamAPI_ISteamRemoteStorage_GetFileSize(steamremotestorage, path);
+    }
     return 0;
 }
 
-static int STEAM_UserStorageReadFile(void *userdata, const char *path, void *destination, Uint64 length)
+static int STEAM_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
 {
-    int retval;
+    int result = -1;
     STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
     void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
     if (steamremotestorage == NULL) {
@@ -82,13 +87,17 @@ static int STEAM_UserStorageReadFile(void *userdata, const char *path, void *des
     if (length > SDL_MAX_SINT32) {
         return SDL_SetError("SteamRemoteStorage only supports INT32_MAX read size");
     }
-    retval = steam->SteamAPI_ISteamRemoteStorage_FileRead(steamremotestorage, path, destination, (Sint32) length) == length;
-    return retval - 1;
+    if (steam->SteamAPI_ISteamRemoteStorage_FileRead(steamremotestorage, path, destination, (Sint32) length) == length) {
+        result = 0;
+    } else {
+        SDL_SetError("SteamAPI_ISteamRemoteStorage_FileRead() failed");
+    }
+    return result;
 }
 
-static int STEAM_UserStorageWriteFile(void *userdata, const char *path, const void *source, Uint64 length)
+static int STEAM_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
 {
-    int retval;
+    int result = -1;
     STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
     void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
     if (steamremotestorage == NULL) {
@@ -97,11 +106,15 @@ static int STEAM_UserStorageWriteFile(void *userdata, const char *path, const vo
     if (length > SDL_MAX_SINT32) {
         return SDL_SetError("SteamRemoteStorage only supports INT32_MAX write size");
     }
-    retval = steam->SteamAPI_ISteamRemoteStorage_FileWrite(steamremotestorage, path, source, (Sint32) length) == length;
-    return retval - 1;
+    if (steam->SteamAPI_ISteamRemoteStorage_FileWrite(steamremotestorage, path, source, (Sint32) length) == length) {
+        result = 0;
+    } else {
+        SDL_SetError("SteamAPI_ISteamRemoteStorage_FileRead() failed");
+    }
+    return result;
 }
 
-static Uint64 STEAM_UserStorageSpaceRemaining(void *userdata)
+static Uint64 STEAM_GetStorageSpaceRemaining(void *userdata)
 {
     Uint64 total, remaining;
     STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
@@ -118,12 +131,16 @@ static Uint64 STEAM_UserStorageSpaceRemaining(void *userdata)
 }
 
 static const SDL_StorageInterface STEAM_user_iface = {
-    STEAM_UserStorageClose,
-    STEAM_UserStorageReady,
-    STEAM_UserStorageFileSize,
-    STEAM_UserStorageReadFile,
-    STEAM_UserStorageWriteFile,
-    STEAM_UserStorageSpaceRemaining
+    STEAM_CloseStorage,
+    STEAM_StorageReady,
+    NULL,   /* enumerate */
+    STEAM_GetStoragePathInfo,
+    STEAM_ReadStorageFile,
+    STEAM_WriteStorageFile,
+    NULL,   /* mkdir */
+    NULL,   /* remove */
+    NULL,   /* rename */
+    STEAM_GetStorageSpaceRemaining
 };
 
 static SDL_Storage *STEAM_User_Create(const char *org, const char *app, SDL_PropertiesID props)