#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) {
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)
{
const GenericEnumerateData *wrap_data = (GenericEnumerateData *)userdata;
dirname += wrap_data->base_len;
#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] = '/'; 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) {
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)
{
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) {
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)
{
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)
{
return SDL_MAX_UINT64;
}
static const SDL_StorageInterface GENERIC_title_iface = {
sizeof(SDL_StorageInterface),
GENERIC_CloseStorage,
NULL, GENERIC_EnumerateStorageDirectory,
GENERIC_GetStoragePathInfo,
GENERIC_ReadStorageFile,
NULL, NULL, NULL, NULL, NULL, NULL };
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) {
const bool need_sep = ((override[slen - 1] != '/') && (override[slen - 1] != '\\'));
if (SDL_asprintf(&basepath, "%s%s", override, need_sep ? "/" : "") == -1) {
return NULL;
}
} else {
basepath = SDL_strdup("");
}
} else {
const char *base = SDL_GetBasePath();
basepath = base ? SDL_strdup(base) : NULL;
}
if (basepath != NULL) {
result = SDL_OpenStorage(&GENERIC_title_iface, basepath);
if (result == NULL) {
SDL_free(basepath); }
}
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, 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); }
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, 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;
#ifdef SDL_PLATFORM_ANDROID
if (!path || !*path) {
path = "./";
}
#else
if (!path || !*path) {
#ifdef SDL_PLATFORM_WINDOWS
path = "C:/";
#else
path = "/";
#endif
}
bool is_absolute = false;
#ifdef SDL_PLATFORM_WINDOWS
const char ch = (char) SDL_toupper(path[0]);
is_absolute = (ch == '/') || (ch == '\\') || (((ch >= 'A') && (ch <= 'Z')) && (path[1] == ':') && ((path[2] == '\\') || (path[2] == '/'))); #else
is_absolute = (path[0] == '/'); #endif
if (!is_absolute) {
prepend = SDL_GetCurrentDirectory();
if (!prepend) {
return NULL;
}
}
#endif
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;
}