From ec3ba387d1fff6c935925ae7c79dabcce01fbb5b Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 16 Mar 2024 09:08:00 -0700
Subject: [PATCH] Added SDL_OpenFileStorage() for local file storage
---
include/SDL3/SDL_storage.h | 24 +++++++++++-
src/dynapi/SDL_dynapi.sym | 1 +
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/storage/SDL_storage.c | 5 +++
src/storage/SDL_sysstorage.h | 2 +
src/storage/generic/SDL_genericstorage.c | 49 ++++++++++++++++++++++--
7 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/include/SDL3/SDL_storage.h b/include/SDL3/SDL_storage.h
index b57832cae006c..1892b8cf29bab 100644
--- a/include/SDL3/SDL_storage.h
+++ b/include/SDL3/SDL_storage.h
@@ -85,8 +85,8 @@ extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenTitleStorage(const char *override,
*
* While title storage can generally be kept open throughout runtime, user
* storage should only be opened when the client is ready to read/write files.
- * This allows the backend to properly batch R/W operations and flush them
- * when the container has been closed; ensuring safe and optimal save I/O.
+ * This allows the backend to properly batch file operations and flush them when
+ * the container has been closed; ensuring safe and optimal save I/O.
*
* \param org the name of your organization
* \param app the name of your application
@@ -107,6 +107,26 @@ extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenTitleStorage(const char *override,
*/
extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props);
+/**
+ * Opens up a container for local filesystem storage.
+ *
+ * This is provided for development and tools. Portable applications should use SDL_OpenTitleStorage() for access to game data and SDL_OpenUserStorage() for access to user data.
+ *
+ * \param path the base path prepended to all storage paths, or NULL for no base path
+ * \returns a filesystem storage container on success or NULL on failure; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_OpenStorage
+ * \sa SDL_CloseStorage
+ * \sa SDL_StorageReady
+ * \sa SDL_GetStorageFileSize
+ * \sa SDL_ReadStorageFile
+ * \sa SDL_WriteStorageFile
+ * \sa SDL_GetStorageSpaceRemaining
+ */
+extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenFileStorage(const char *path);
+
/**
* Opens up a container using a client-provided storage interface.
*
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index e233484a0a453..eec9befc08f8a 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -993,6 +993,7 @@ SDL3_0.0.0 {
SDL_RenamePath;
SDL_GetPathInfo;
SDL_FileTimeToWindows;
+ SDL_OpenFileStorage;
# 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 f12c82ea581fc..8c0fd4b7ee8d7 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1018,3 +1018,4 @@
#define SDL_RenamePath SDL_RenamePath_REAL
#define SDL_GetPathInfo SDL_GetPathInfo_REAL
#define SDL_FileTimeToWindows SDL_FileTimeToWindows_REAL
+#define SDL_OpenFileStorage SDL_OpenFileStorage_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index fa2c50937a91b..c9880ef5f29fb 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1043,3 +1043,4 @@ SDL_DYNAPI_PROC(int,SDL_RemovePath,(const char *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_RenamePath,(const char *a, const char *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetPathInfo,(const char *a, SDL_PathInfo *b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_FileTimeToWindows,(Sint64 a, Uint32 *b, Uint32 *c),(a,b,c),)
+SDL_DYNAPI_PROC(SDL_Storage*,SDL_OpenFileStorage,(const char *a),(a),return)
diff --git a/src/storage/SDL_storage.c b/src/storage/SDL_storage.c
index 2f0f0d7c849ff..4d496caead519 100644
--- a/src/storage/SDL_storage.c
+++ b/src/storage/SDL_storage.c
@@ -137,6 +137,11 @@ SDL_Storage *SDL_OpenUserStorage(const char *org, const char *app, SDL_Propertie
return storage;
}
+SDL_Storage *SDL_OpenFileStorage(const char *path)
+{
+ return GENERIC_OpenFileStorage(path);
+}
+
SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata)
{
SDL_Storage *storage;
diff --git a/src/storage/SDL_sysstorage.h b/src/storage/SDL_sysstorage.h
index c16c5cc25175c..363e2f0b6d89e 100644
--- a/src/storage/SDL_sysstorage.h
+++ b/src/storage/SDL_sysstorage.h
@@ -46,4 +46,6 @@ extern TitleStorageBootStrap GENERIC_titlebootstrap;
extern UserStorageBootStrap GENERIC_userbootstrap;
extern UserStorageBootStrap STEAM_userbootstrap;
+extern SDL_Storage *GENERIC_OpenFileStorage(const char *path);
+
#endif /* SDL_sysstorage_h_ */
diff --git a/src/storage/generic/SDL_genericstorage.c b/src/storage/generic/SDL_genericstorage.c
index 26296be9e4028..e1088b160f97a 100644
--- a/src/storage/generic/SDL_genericstorage.c
+++ b/src/storage/generic/SDL_genericstorage.c
@@ -25,10 +25,16 @@
static char *GENERIC_INTERNAL_CreateFullPath(const char *base, const char *relative)
{
- size_t fulllen = SDL_strlen(base) + SDL_strlen(relative) + 1;
- char *result = (char*) SDL_malloc(fulllen);
+ size_t len = 0;
+
+ if (base) {
+ len += SDL_strlen(base);
+ }
+ len += SDL_strlen(relative) + 1;
+
+ char *result = (char*) SDL_malloc(len);
if (result != NULL) {
- SDL_snprintf(result, fulllen, "%s%s", base, relative);
+ SDL_snprintf(result, len, "%s%s", base, relative);
}
return result;
}
@@ -186,3 +192,40 @@ UserStorageBootStrap GENERIC_userbootstrap = {
"SDL generic user storage driver",
GENERIC_User_Create
};
+
+static const SDL_StorageInterface GENERIC_file_iface = {
+ GENERIC_StorageClose,
+ GENERIC_StorageReady,
+ GENERIC_StorageFileSize,
+ GENERIC_StorageReadFile,
+ GENERIC_StorageWriteFile,
+ GENERIC_StorageSpaceRemaining
+};
+
+SDL_Storage *GENERIC_OpenFileStorage(const char *path)
+{
+ SDL_Storage *result;
+ size_t len = 0;
+ char *basepath = NULL;
+
+ if (path) {
+ len += SDL_strlen(path);
+ }
+ if (len > 0) {
+ if (path[len-1] == '/') {
+ basepath = SDL_strdup(path);
+ if (!basepath) {
+ return NULL;
+ }
+ } else {
+ if (SDL_asprintf(&basepath, "%s/", path) < 0) {
+ return NULL;
+ }
+ }
+ }
+ result = SDL_OpenStorage(&GENERIC_file_iface, basepath);
+ if (result == NULL) {
+ SDL_free(basepath);
+ }
+ return result;
+}