diff --git a/include/SDL3/SDL_storage.h b/include/SDL3/SDL_storage.h index b57832cae0..1892b8cf29 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 e233484a0a..eec9befc08 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 f12c82ea58..8c0fd4b7ee 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 fa2c50937a..c9880ef5f2 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 2f0f0d7c84..4d496caead 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 c16c5cc251..363e2f0b6d 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 26296be9e4..e1088b160f 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; +}