| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "SDL_internal.h"
- #include "../SDL_sysstorage.h"
- static char *GENERIC_INTERNAL_CreateFullPath(const char *base, const char *relative)
- {
- const char *rel = relative;
- #ifdef SDL_PLATFORM_ANDROID
- if (rel) {
- // Removes any leading slash
- if (rel[0] == '/' || rel[0] == '\\') {
- rel += 1;
- }
- }
- #endif
- char *result = NULL;
- SDL_asprintf(&result, "%s%s", base ? base : "", rel ? rel : "");
- return result;
- }
- static bool GENERIC_CloseStorage(void *userdata)
- {
- SDL_free(userdata);
- return true;
- }
- typedef struct GenericEnumerateData
- {
- size_t base_len;
- SDL_EnumerateDirectoryCallback real_callback;
- void *real_userdata;
- } GenericEnumerateData;
- static SDL_EnumerationResult SDLCALL GENERIC_EnumerateDirectory(void *userdata, const char *dirname, const char *fname)
- {
- // SDL_EnumerateDirectory will return the full path, so for Storage we
- // can take the base directory and add its length to the dirname string,
- // effectively trimming the root without having to strdup anything.
- const GenericEnumerateData *wrap_data = (GenericEnumerateData *)userdata;
- dirname += wrap_data->base_len; // skip the base, just return the part inside of the Storage.
- #ifdef SDL_PLATFORM_WINDOWS
- char *dirnamecpy = NULL;
- const size_t slen = SDL_strlen(dirname);
- if (slen && (dirname[slen - 1] == '\\')) {
- dirnamecpy = SDL_strdup(dirname);
- if (!dirnamecpy) {
- return SDL_ENUM_FAILURE;
- }
- dirnamecpy[slen - 1] = '/'; // storage layer always uses '/' path separators.
- dirname = dirnamecpy;
- }
- const SDL_EnumerationResult retval = wrap_data->real_callback(wrap_data->real_userdata, dirname, fname);
- SDL_free(dirnamecpy);
- return retval;
- #else
- return wrap_data->real_callback(wrap_data->real_userdata, dirname, fname);
- #endif
- }
- static bool GENERIC_EnumerateStorageDirectory(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)
- {
- bool result = false;
- GenericEnumerateData wrap_data;
- char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
- if (fullpath) {
- wrap_data.base_len = userdata ? SDL_strlen((char *)userdata) : 0;
- wrap_data.real_callback = callback;
- wrap_data.real_userdata = callback_userdata;
- result = SDL_EnumerateDirectory(fullpath, GENERIC_EnumerateDirectory, &wrap_data);
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_GetStoragePathInfo(void *userdata, const char *path, SDL_PathInfo *info)
- {
- bool result = false;
- char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
- if (fullpath) {
- result = SDL_GetPathInfo(fullpath, info);
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_ReadStorageFile(void *userdata, const char *path, void *destination, Uint64 length)
- {
- bool result = false;
- if (length > SDL_SIZE_MAX) {
- return SDL_SetError("Read size exceeds SDL_SIZE_MAX");
- }
- 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 = true;
- } else {
- SDL_SetError("File length did not exactly match the destination length");
- }
- SDL_CloseIO(stream);
- }
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_WriteStorageFile(void *userdata, const char *path, const void *source, Uint64 length)
- {
- // TODO: Recursively create subdirectories with SDL_CreateDirectory
- bool result = false;
- if (length > SDL_SIZE_MAX) {
- return SDL_SetError("Write size exceeds SDL_SIZE_MAX");
- }
- 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 = true;
- } else {
- SDL_SetError("Resulting file length did not exactly match the source length");
- }
- SDL_CloseIO(stream);
- }
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_CreateStorageDirectory(void *userdata, const char *path)
- {
- // TODO: Recursively create subdirectories with SDL_CreateDirectory
- bool result = false;
- char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
- if (fullpath) {
- result = SDL_CreateDirectory(fullpath);
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_RemoveStoragePath(void *userdata, const char *path)
- {
- bool result = false;
- char *fullpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, path);
- if (fullpath) {
- result = SDL_RemovePath(fullpath);
- SDL_free(fullpath);
- }
- return result;
- }
- static bool GENERIC_RenameStoragePath(void *userdata, const char *oldpath, const char *newpath)
- {
- bool result = false;
- 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 bool GENERIC_CopyStorageFile(void *userdata, const char *oldpath, const char *newpath)
- {
- bool result = false;
- char *fulloldpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, oldpath);
- char *fullnewpath = GENERIC_INTERNAL_CreateFullPath((char *)userdata, newpath);
- if (fulloldpath && fullnewpath) {
- result = SDL_CopyFile(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 = {
- sizeof(SDL_StorageInterface),
- GENERIC_CloseStorage,
- NULL, // ready
- GENERIC_EnumerateStorageDirectory,
- GENERIC_GetStoragePathInfo,
- GENERIC_ReadStorageFile,
- NULL, // write_file
- NULL, // mkdir
- NULL, // remove
- NULL, // rename
- NULL, // copy
- NULL // space_remaining
- };
- static SDL_Storage *GENERIC_Title_Create(const char *override, SDL_PropertiesID props)
- {
- SDL_Storage *result = NULL;
- char *basepath = NULL;
- if (override != NULL) {
- const size_t slen = SDL_strlen(override);
- if (slen > 0) {
- // make sure override has a path separator at the end. If you're not on Windows and used '\\', that's on you.
- const bool need_sep = ((override[slen - 1] != '/') && (override[slen - 1] != '\\'));
- if (SDL_asprintf(&basepath, "%s%s", override, need_sep ? "/" : "") == -1) {
- return NULL;
- }
- } else {
- // override == "" -> empty base (not "/")
- basepath = SDL_strdup("");
- }
- } else {
- const char *base = SDL_GetBasePath();
- // On Android, SDL_GetBasePath() can be NULL: use empty base.
- #ifdef SDL_PLATFORM_ANDROID
- basepath = base ? SDL_strdup(base) : SDL_strdup("");
- #else
- basepath = base ? SDL_strdup(base) : NULL;
- #endif
- }
- if (basepath != NULL) {
- result = SDL_OpenStorage(&GENERIC_title_iface, basepath);
- if (result == NULL) {
- SDL_free(basepath); // otherwise CloseStorage will free it.
- }
- }
- return result;
- }
- TitleStorageBootStrap GENERIC_titlebootstrap = {
- "generic",
- "SDL generic title storage driver",
- GENERIC_Title_Create
- };
- static const SDL_StorageInterface GENERIC_user_iface = {
- sizeof(SDL_StorageInterface),
- GENERIC_CloseStorage,
- NULL, // ready
- GENERIC_EnumerateStorageDirectory,
- GENERIC_GetStoragePathInfo,
- GENERIC_ReadStorageFile,
- GENERIC_WriteStorageFile,
- GENERIC_CreateStorageDirectory,
- GENERIC_RemoveStoragePath,
- GENERIC_RenameStoragePath,
- GENERIC_CopyStorageFile,
- GENERIC_GetStorageSpaceRemaining
- };
- static SDL_Storage *GENERIC_User_Create(const char *org, const char *app, SDL_PropertiesID props)
- {
- SDL_Storage *result;
- char *prefpath = SDL_GetPrefPath(org, app);
- if (prefpath == NULL) {
- return NULL;
- }
- result = SDL_OpenStorage(&GENERIC_user_iface, prefpath);
- if (result == NULL) {
- SDL_free(prefpath); // otherwise CloseStorage will free it.
- }
- return result;
- }
- UserStorageBootStrap GENERIC_userbootstrap = {
- "generic",
- "SDL generic user storage driver",
- GENERIC_User_Create
- };
- static const SDL_StorageInterface GENERIC_file_iface = {
- sizeof(SDL_StorageInterface),
- GENERIC_CloseStorage,
- NULL, // ready
- GENERIC_EnumerateStorageDirectory,
- GENERIC_GetStoragePathInfo,
- GENERIC_ReadStorageFile,
- GENERIC_WriteStorageFile,
- GENERIC_CreateStorageDirectory,
- GENERIC_RemoveStoragePath,
- GENERIC_RenameStoragePath,
- GENERIC_CopyStorageFile,
- GENERIC_GetStorageSpaceRemaining
- };
- SDL_Storage *GENERIC_OpenFileStorage(const char *path)
- {
- SDL_Storage *result;
- char *basepath = NULL;
- char *prepend = NULL;
- bool is_absolute = false;
- if (!path || !*path) {
- #ifdef SDL_PLATFORM_WINDOWS
- path = "C:/";
- #else
- path = "/";
- #endif
- }
- #ifdef SDL_PLATFORM_WINDOWS
- const char ch = (char) SDL_toupper(path[0]);
- is_absolute = (ch == '/') || // some sort of absolute Unix-style path.
- (ch == '\\') || // some sort of absolute Windows-style path.
- (((ch >= 'A') && (ch <= 'Z')) && (path[1] == ':') && ((path[2] == '\\') || (path[2] == '/'))); // an absolute path with a drive letter.
- #else
- is_absolute = (path[0] == '/'); // some sort of absolute Unix-style path.
- #endif
- if (!is_absolute) {
- prepend = SDL_GetCurrentDirectory();
- if (!prepend) {
- return NULL;
- }
- }
- const size_t len = SDL_strlen(path);
- const char *appended_separator = "";
- #ifdef SDL_PLATFORM_WINDOWS
- if ((path[len-1] != '/') && (path[len-1] != '\\')) {
- appended_separator = "/";
- }
- #else
- if (path[len-1] != '/') {
- appended_separator = "/";
- }
- #endif
- const int rc = SDL_asprintf(&basepath, "%s%s%s", prepend ? prepend : "", path, appended_separator);
- SDL_free(prepend);
- if (rc < 0) {
- return NULL;
- }
- result = SDL_OpenStorage(&GENERIC_file_iface, basepath);
- if (result == NULL) {
- SDL_free(basepath);
- }
- return result;
- }
|