diff --git a/include/SDL3/SDL_storage.h b/include/SDL3/SDL_storage.h index 1892b8cf29..303bdc4904 100644 --- a/include/SDL3/SDL_storage.h +++ b/include/SDL3/SDL_storage.h @@ -29,7 +29,7 @@ #define SDL_storage_h_ #include -#include +#include #include #include @@ -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 4d496caead..1111d2a781 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"); } - return storage->iface.writeFile(storage->userdata, path, source, length); + 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.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 e1088b160f..e624a541d4 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(); + char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); + if (fullpath) { + result = SDL_GetPathInfo(fullpath, info); + + SDL_free(fullpath); } - stream = SDL_IOFromFile(fullpath, "rb"); - SDL_free(fullpath); - - result = SDL_SizeIO(stream); - SDL_CloseIO(stream); - if (result < 0) { - return result; - } - - /* 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(); - } - stream = SDL_IOFromFile(fullpath, "wb"); - SDL_free(fullpath); + char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path); + if (fullpath) { + SDL_IOStream *stream = SDL_IOFromFile(fullpath, "wb"); - /* FIXME: Should SDL_WriteIO use u64 now...? */ - fullwrite = (SDL_WriteIO(stream, source, (size_t) length) == length); - SDL_CloseIO(stream); - return fullwrite - 1; + 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); + } + return result; } -static Uint64 GENERIC_StorageSpaceRemaining(void *userdata) +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_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 1b41b149af..cb61c3a932 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)