Add SDL_storage

This commit is contained in:
Ethan Lee 2024-03-08 14:38:46 -05:00 committed by Sam Lantinga
parent 4fc749443f
commit 744227e6ab
16 changed files with 1035 additions and 0 deletions

View File

@ -494,6 +494,7 @@ sdl_glob_sources(
"${SDL3_SOURCE_DIR}/src/render/*/*.c"
"${SDL3_SOURCE_DIR}/src/sensor/*.c"
"${SDL3_SOURCE_DIR}/src/stdlib/*.c"
"${SDL3_SOURCE_DIR}/src/storage/*.c"
"${SDL3_SOURCE_DIR}/src/thread/*.c"
"${SDL3_SOURCE_DIR}/src/timer/*.c"
"${SDL3_SOURCE_DIR}/src/video/*.c"
@ -1753,6 +1754,14 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/unix/*.c")
set(HAVE_SDL_FILESYSTEM TRUE)
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
if(LINUX)
set(SDL_STORAGE_STEAM 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c")
endif()
set(HAVE_SDL_STORAGE 1)
set(SDL_TIMER_UNIX 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
set(HAVE_SDL_TIMERS TRUE)
@ -1972,6 +1981,14 @@ elseif(WINDOWS)
endif()
set(HAVE_SDL_FILESYSTEM TRUE)
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
if(NOT WINDOWS_STORE)
set(SDL_STORAGE_STEAM 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c")
endif()
set(HAVE_SDL_STORAGE 1)
# Libraries for Win32 native and MinGW
if(NOT WINDOWS_STORE)
sdl_link_dependency(base LIBS kernel32 user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32)
@ -2204,6 +2221,15 @@ elseif(APPLE)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/cocoa/*.m")
set(HAVE_SDL_FILESYSTEM TRUE)
# TODO: SDL_STORAGE_ICLOUD
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
if(DARWIN OR MACOSX)
set(SDL_STORAGE_STEAM 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/steam/*.c")
endif()
set(HAVE_SDL_STORAGE 1)
if(SDL_SENSOR)
if(IOS OR VISIONOS)
set(SDL_SENSOR_COREMOTION 1)
@ -2820,6 +2846,10 @@ if(NOT HAVE_SDL_FILESYSTEM)
set(SDL_FILESYSTEM_DUMMY 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/dummy/*.c")
endif()
if(NOT HAVE_SDL_STORAGE)
set(SDL_STORAGE_GENERIC 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/storage/generic/*.c")
endif()
if(NOT HAVE_SDL_LOCALE)
set(SDL_LOCALE_DUMMY 1)
sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/dummy/*.c")

View File

@ -368,6 +368,7 @@
<ClInclude Include="..\..\include\SDL3\SDL_scancode.h" />
<ClInclude Include="..\..\include\SDL3\SDL_sensor.h" />
<ClInclude Include="..\..\include\SDL3\SDL_stdinc.h" />
<ClInclude Include="..\..\include\SDL3\SDL_storage.h" />
<ClInclude Include="..\..\include\SDL3\SDL_surface.h" />
<ClInclude Include="..\..\include\SDL3\SDL_system.h" />
<ClInclude Include="..\..\include\SDL3\SDL_test.h" />
@ -778,6 +779,8 @@
<ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
<ClCompile Include="..\..\src\stdlib\SDL_string.c" />
<ClCompile Include="..\..\src\stdlib\SDL_strtokr.c" />
<ClCompile Include="..\..\src\storage\generic\SDL_genericstorage.c" />
<ClCompile Include="..\..\src\storage\SDL_storage.c" />
<ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
<ClCompile Include="..\..\src\thread\generic\SDL_sysrwlock.c" />
<ClCompile Include="..\..\src\thread\SDL_thread.c" />

View File

@ -82,6 +82,7 @@
<ClInclude Include="..\include\SDL3\SDL_scancode.h" />
<ClInclude Include="..\include\SDL3\SDL_sensor.h" />
<ClInclude Include="..\include\SDL3\SDL_stdinc.h" />
<ClInclude Include="..\include\SDL3\SDL_storage.h" />
<ClInclude Include="..\include\SDL3\SDL_surface.h" />
<ClInclude Include="..\include\SDL3\SDL_system.h" />
<ClInclude Include="..\include\SDL3\SDL_thread.h" />
@ -439,6 +440,8 @@
<ClCompile Include="..\src\stdlib\SDL_stdlib.c" />
<ClCompile Include="..\src\stdlib\SDL_string.c" />
<ClCompile Include="..\src\stdlib\SDL_strtokr.c" />
<ClCompile Include="..\src\storage\generic\SDL_genericstorage.c" />
<ClCompile Include="..\src\storage\SDL_storage.c" />
<ClCompile Include="..\src\thread\generic\SDL_syssem.c" />
<ClCompile Include="..\src\thread\SDL_thread.c" />
<ClCompile Include="..\src\thread\stdcpp\SDL_syscond.cpp">

View File

@ -291,6 +291,7 @@
<ClInclude Include="..\..\include\SDL3\SDL_scancode.h" />
<ClInclude Include="..\..\include\SDL3\SDL_sensor.h" />
<ClInclude Include="..\..\include\SDL3\SDL_stdinc.h" />
<ClInclude Include="..\..\include\SDL3\SDL_storage.h" />
<ClInclude Include="..\..\include\SDL3\SDL_surface.h" />
<ClInclude Include="..\..\include\SDL3\SDL_system.h" />
<ClInclude Include="..\..\include\SDL3\SDL_test.h" />
@ -635,6 +636,9 @@
<ClCompile Include="..\..\src\stdlib\SDL_stdlib.c" />
<ClCompile Include="..\..\src\stdlib\SDL_string.c" />
<ClCompile Include="..\..\src\stdlib\SDL_strtokr.c" />
<ClCompile Include="..\..\src\storage\generic\SDL_genericstorage.c" />
<ClCompile Include="..\..\src\storage\steam\SDL_steamstorage.c" />
<ClCompile Include="..\..\src\storage\SDL_storage.c" />
<ClCompile Include="..\..\src\thread\generic\SDL_syscond.c" />
<ClCompile Include="..\..\src\thread\generic\SDL_sysrwlock.c" />
<ClCompile Include="..\..\src\thread\SDL_thread.c" />

View File

@ -71,6 +71,7 @@
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_scancode.h>
#include <SDL3/SDL_sensor.h>
#include <SDL3/SDL_storage.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_system.h>
#include <SDL3/SDL_thread.h>

View File

@ -1904,6 +1904,24 @@ extern "C" {
*/
#define SDL_HINT_SHUTDOWN_DBUS_ON_QUIT "SDL_SHUTDOWN_DBUS_ON_QUIT"
/**
* A variable that specifies a backend to use for title storage.
*
* By default, SDL will try all available storage backends in a reasonable order until it finds one that can work, but this hint allows the app or user to force a specific target, such as "pc" if, say, you are on Steam but want to avoid SteamRemoteStorage for title data.
*
* This hint should be set before SDL is initialized.
*/
#define SDL_HINT_STORAGE_TITLE_DRIVER "SDL_STORAGE_TITLE_DRIVER"
/**
* A variable that specifies a backend to use for user storage.
*
* By default, SDL will try all available storage backends in a reasonable order until it finds one that can work, but this hint allows the app or user to force a specific target, such as "pc" if, say, you are on Steam but want to avoid SteamRemoteStorage for user data.
*
* This hint should be set before SDL is initialized.
*/
#define SDL_HINT_STORAGE_USER_DRIVER "SDL_STORAGE_USER_DRIVER"
/**
* Specifies whether SDL_THREAD_PRIORITY_TIME_CRITICAL should be treated as realtime.
*

276
include/SDL3/SDL_storage.h Normal file
View File

@ -0,0 +1,276 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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.
*/
/**
* \file SDL_storage.h
*
* Include file for storage container SDL API functions
*/
#ifndef SDL_storage_h_
#define SDL_storage_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_mutex.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* !!! FIXME: Don't let this ship without async R/W support!!! */
typedef struct SDL_StorageInterface
{
int (SDLCALL *close)(void *userdata);
SDL_bool (SDLCALL *ready)(void *userdata);
int (SDLCALL *fileSize)(void *userdata, const char *path, Uint64 *length);
int (SDLCALL *readFile)(void *userdata, const char *path, void *destination, Uint64 length);
int (SDLCALL *writeFile)(void *userdata, const char *path, const void *source, Uint64 length);
Uint64 (SDLCALL *spaceRemaining)(void *userdata);
} SDL_StorageInterface;
typedef struct SDL_Storage SDL_Storage;
/**
* Opens up a read-only container for the application's filesystem.
*
* \param override a path to override the backend's default title root
* \param props a property list that may contain backend-specific information
* \returns a title 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_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_TitleStorageReady
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props);
/**
* Opens up a container for a user's unique read/write filesystem.
*
* 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.
*
* \param org the name of your organization
* \param app the name of your application
* \param props a property list that may contain backend-specific information
* \returns a user 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_OpenTitleStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props);
/**
* Opens up a container using a client-provided storage interface.
*
* Applications do not need to use this function unless they are providing
* their own SDL_Storage implementation. If you just need an
* SDL_Storage, you should use the built-in implementations in SDL,
* like SDL_OpenTitleStorage() or SDL_OpenUserStorage().
*
* \param iface the function table to be used by this container
* \param userdata the pointer that will be passed to the store interface
* \returns a 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_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC SDL_Storage *SDLCALL SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata);
/**
* Closes and frees a storage container.
*
* \param storage a storage container to close
* \returns 0 if the container was freed with no errors, a negative value
* otherwise; call SDL_GetError() for more information. Even if the
* function returns an error, the container data will be freed; the
* error is only for informational purposes.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC int SDLCALL SDL_CloseStorage(SDL_Storage *storage);
/**
* Checks if the storage container is ready to use.
*
* This function should be called in regular intervals until it returns
* SDL_TRUE - however, it is not recommended to spinwait on this call, as
* the backend may depend on a synchronous message loop.
*
* \param storage a storage container to query
* \returns SDL_TRUE if the container is ready, SDL_FALSE otherwise
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC SDL_bool SDLCALL SDL_StorageReady(SDL_Storage *storage);
/**
* Query the size of a file within a storage container.
*
* \param storage a storage container to query
* \param path the relative path of the file to query
* \param length a pointer to be filled with the file's length
* \returns 0 if the file could be queried, a negative value otherwise; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageReadFile
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC int SDLCALL SDL_StorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length);
/**
* Synchronously read a file from a storage container into a client-provided buffer.
*
* \param storage a storage container to read from
* \param path the relative path of the file to read
* \param destination a client-provided buffer to read the file into
* \param length the length of the destination buffer
* \returns 0 if the file was read, a negative value otherwise; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageWriteFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC int SDLCALL SDL_StorageReadFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length);
/**
* Synchronously write a file from client memory into a storage container.
*
* \param storage a storage container to write to
* \param path the relative path of the file to write
* \param source a client-provided buffer to write from
* \param length the length of the source buffer
* \returns 0 if the file was written, a negative value otherwise; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageSpaceRemaining
*/
extern DECLSPEC int SDL_StorageWriteFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length);
/**
* Queries the remaining space in a storage container.
*
* \param storage a storage container to query
* \returns the amount of remaining space, in bytes
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenTitleStorage
* \sa SDL_OpenUserStorage
* \sa SDL_OpenStorage
* \sa SDL_CloseStorage
* \sa SDL_StorageReady
* \sa SDL_StorageFileSize
* \sa SDL_StorageReadFile
* \sa SDL_StorageReadFileAsync
* \sa SDL_StorageWriteFile
* \sa SDL_StorageWriteFileAsync
*/
extern DECLSPEC Uint64 SDLCALL SDL_StorageSpaceRemaining(SDL_Storage *storage);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_storage_h_ */

View File

@ -465,6 +465,10 @@
#cmakedefine SDL_FILESYSTEM_PS2 @SDL_FILESYSTEM_PS2@
#cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@
/* Enable system storage support */
#cmakedefine SDL_STORAGE_GENERIC @SDL_STORAGE_GENERIC@
#cmakedefine SDL_STORAGE_STEAM @SDL_STORAGE_STEAM@
/* Enable camera subsystem */
#cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@
/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */

View File

@ -979,6 +979,14 @@ SDL3_0.0.0 {
SDL_OpenIO;
SDL_CloseIO;
SDL_GetIOStatus;
SDL_OpenTitleStorage;
SDL_OpenUserStorage;
SDL_OpenStorage;
SDL_CloseStorage;
SDL_StorageReady;
SDL_StorageFileSize;
SDL_StorageReadFile;
SDL_StorageSpaceRemaining;
# extra symbols go here (don't modify this line)
local: *;
};

View File

@ -1004,3 +1004,11 @@
#define SDL_OpenIO SDL_OpenIO_REAL
#define SDL_CloseIO SDL_CloseIO_REAL
#define SDL_GetIOStatus SDL_GetIOStatus_REAL
#define SDL_OpenTitleStorage SDL_OpenTitleStorage_REAL
#define SDL_OpenUserStorage SDL_OpenUserStorage_REAL
#define SDL_OpenStorage SDL_OpenStorage_REAL
#define SDL_CloseStorage SDL_CloseStorage_REAL
#define SDL_StorageReady SDL_StorageReady_REAL
#define SDL_StorageFileSize SDL_StorageFileSize_REAL
#define SDL_StorageReadFile SDL_StorageReadFile_REAL
#define SDL_StorageSpaceRemaining SDL_StorageSpaceRemaining_REAL

View File

@ -1029,3 +1029,11 @@ SDL_DYNAPI_PROC(void,SDL_ShowOpenFolderDialog,(SDL_DialogFileCallback a, void *b
SDL_DYNAPI_PROC(SDL_IOStream*,SDL_OpenIO,(const SDL_IOStreamInterface *a, void *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_CloseIO,(SDL_IOStream *a),(a),return)
SDL_DYNAPI_PROC(SDL_IOStatus,SDL_GetIOStatus,(SDL_IOStream *a),(a),return)
SDL_DYNAPI_PROC(SDL_Storage*,SDL_OpenTitleStorage,(const char *a, SDL_PropertiesID b),(a,b),return)
SDL_DYNAPI_PROC(SDL_Storage*,SDL_OpenUserStorage,(const char *a, const char *b, SDL_PropertiesID c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Storage*,SDL_OpenStorage,(const SDL_StorageInterface *a, void *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_CloseStorage,(SDL_Storage *a),(a),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_StorageReady,(SDL_Storage *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_StorageFileSize,(SDL_Storage *a, const char *b, Uint64 *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_StorageReadFile,(SDL_Storage *a, const char *b, void *c, Uint64 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(Uint64,SDL_StorageSpaceRemaining,(SDL_Storage *a),(a),return)

222
src/storage/SDL_storage.c Normal file
View File

@ -0,0 +1,222 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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"
/* Available title storage drivers */
static TitleStorageBootStrap *titlebootstrap[] = {
&GENERIC_titlebootstrap,
};
/* Available user storage drivers */
static UserStorageBootStrap *userbootstrap[] = {
#ifdef SDL_STORAGE_STEAM
&STEAM_userbootstrap,
#endif
&GENERIC_userbootstrap,
};
struct SDL_Storage
{
SDL_StorageInterface iface;
void *userdata;
};
#define CHECK_STORAGE_MAGIC() \
if (!storage) { \
return SDL_SetError("Invalid storage container"); \
}
#define CHECK_STORAGE_MAGIC_RET(retval) \
if (!storage) { \
SDL_SetError("Invalid storage container"); \
return retval; \
}
SDL_Storage *SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props)
{
SDL_Storage *storage = NULL;
int i = 0;
/* Select the proper storage driver */
const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_TITLE_DRIVER);
if (driver_name && *driver_name != 0) {
const char *driver_attempt = driver_name;
while (driver_attempt && *driver_attempt != 0 && !storage) {
const char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt)
: SDL_strlen(driver_attempt);
for (i = 0; titlebootstrap[i]; ++i) {
if ((driver_attempt_len == SDL_strlen(titlebootstrap[i]->name)) &&
(SDL_strncasecmp(titlebootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
storage = titlebootstrap[i]->create(override, props);
break;
}
}
driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
}
} else {
for (i = 0; titlebootstrap[i]; ++i) {
storage = titlebootstrap[i]->create(override, props);
if (storage) {
break;
}
}
}
if (!storage) {
if (driver_name) {
SDL_SetError("%s not available", driver_name);
} else {
SDL_SetError("No available title storage driver");
}
}
return storage;
}
SDL_Storage *SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props)
{
SDL_Storage *storage = NULL;
int i = 0;
/* Select the proper storage driver */
const char *driver_name = SDL_GetHint(SDL_HINT_STORAGE_USER_DRIVER);
if (driver_name && *driver_name != 0) {
const char *driver_attempt = driver_name;
while (driver_attempt && *driver_attempt != 0 && !storage) {
const char *driver_attempt_end = SDL_strchr(driver_attempt, ',');
size_t driver_attempt_len = (driver_attempt_end) ? (driver_attempt_end - driver_attempt)
: SDL_strlen(driver_attempt);
for (i = 0; userbootstrap[i]; ++i) {
if ((driver_attempt_len == SDL_strlen(userbootstrap[i]->name)) &&
(SDL_strncasecmp(userbootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
storage = userbootstrap[i]->create(org, app, props);
break;
}
}
driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
}
} else {
for (i = 0; userbootstrap[i]; ++i) {
storage = userbootstrap[i]->create(org, app, props);
if (storage) {
break;
}
}
}
if (!storage) {
if (driver_name) {
SDL_SetError("%s not available", driver_name);
} else {
SDL_SetError("No available user storage driver");
}
}
return storage;
}
SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata)
{
SDL_Storage *storage;
if (iface->close == NULL || iface->ready == NULL || iface->fileSize == NULL) {
SDL_SetError("iface is missing required callbacks");
return NULL;
}
if ((iface->writeFile != NULL) != (iface->spaceRemaining != NULL)) {
SDL_SetError("Writeable containers must have both writeFile and spaceRemaining");
return NULL;
}
storage = (SDL_Storage*) SDL_malloc(sizeof(SDL_Storage));
if (storage == NULL) {
SDL_OutOfMemory();
return NULL;
}
SDL_memcpy(&storage->iface, iface, sizeof(SDL_StorageInterface));
storage->userdata = userdata;
return storage;
}
int SDL_CloseStorage(SDL_Storage *storage)
{
int retval;
CHECK_STORAGE_MAGIC()
retval = storage->iface.close(storage->userdata);
SDL_free(storage);
return retval;
}
SDL_bool SDL_StorageReady(SDL_Storage *storage)
{
CHECK_STORAGE_MAGIC_RET(SDL_FALSE)
return storage->iface.ready(storage->userdata);
}
int SDL_StorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length)
{
CHECK_STORAGE_MAGIC()
return storage->iface.fileSize(storage->userdata, path, length);
}
int SDL_StorageReadFile(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");
}
return storage->iface.readFile(storage->userdata, path, destination, length);
}
int SDL_StorageWriteFile(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");
}
return storage->iface.writeFile(storage->userdata, path, source, length);
}
Uint64 SDL_StorageSpaceRemaining(SDL_Storage *storage)
{
CHECK_STORAGE_MAGIC_RET(0)
if (storage->iface.spaceRemaining == NULL) {
SDL_SetError("Storage container does not have write capability");
return 0;
}
return storage->iface.spaceRemaining(storage->userdata);
}

View File

@ -0,0 +1,49 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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.
*/
#ifndef SDL_sysstorage_h_
#define SDL_sysstorage_h_
#include "SDL_internal.h"
typedef struct TitleStorageBootStrap
{
const char *name;
const char *desc;
SDL_Storage *(*create)(const char*, SDL_PropertiesID);
} TitleStorageBootStrap;
typedef struct UserStorageBootStrap
{
const char *name;
const char *desc;
SDL_Storage *(*create)(const char*, const char*, SDL_PropertiesID);
} UserStorageBootStrap;
/* Not all of these are available in a given build. Use #ifdefs, etc. */
extern TitleStorageBootStrap GENERIC_titlebootstrap;
/* Steam does not have title storage APIs */
extern UserStorageBootStrap GENERIC_userbootstrap;
extern UserStorageBootStrap STEAM_userbootstrap;
#endif /* SDL_sysstorage_h_ */

View File

@ -0,0 +1,188 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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)
{
size_t fulllen = SDL_strlen(base) + SDL_strlen(relative) + 1;
char *result = (char*) SDL_malloc(fulllen);
if (result != NULL) {
SDL_snprintf(result, fulllen, "%s%s", base, relative);
}
return result;
}
static int GENERIC_StorageClose(void *userdata)
{
SDL_free(userdata);
return 0;
}
static SDL_bool GENERIC_StorageReady(void *userdata)
{
return SDL_TRUE;
}
static int GENERIC_StorageFileSize(void *userdata, const char *path, Uint64 *length)
{
SDL_IOStream *stream;
Sint64 result;
char *fullpath = GENERIC_INTERNAL_CreateFullPath((char*) userdata, path);
if (fullpath == NULL) {
return SDL_OutOfMemory();
}
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;
}
static int GENERIC_StorageReadFile(void *userdata, const char *path, void *destination, Uint64 length)
{
SDL_IOStream *stream;
char *fullpath;
int fullread;
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();
}
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;
}
static int GENERIC_StorageWriteFile(void *userdata, const char *path, const void *source, Uint64 length)
{
/* TODO: Recursively create subdirectories with SDL_mkdir */
SDL_IOStream *stream;
char *fullpath;
int fullwrite;
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);
/* FIXME: Should SDL_WriteIO use u64 now...? */
fullwrite = (SDL_WriteIO(stream, source, (size_t) length) == length);
SDL_CloseIO(stream);
return fullwrite - 1;
}
static Uint64 GENERIC_StorageSpaceRemaining(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
};
static SDL_Storage *GENERIC_Title_Create(const char *override, SDL_PropertiesID props)
{
SDL_Storage *result;
char *basepath;
if (override != NULL) {
basepath = SDL_strdup(override);
} else {
basepath = SDL_GetBasePath();
}
if (basepath == NULL) {
return 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 = {
GENERIC_StorageClose,
GENERIC_StorageReady,
GENERIC_StorageFileSize,
GENERIC_StorageReadFile,
GENERIC_StorageWriteFile,
GENERIC_StorageSpaceRemaining
};
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
};

View File

@ -0,0 +1,199 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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"
#include <stdbool.h> /* Needed by Steamworks */
// !!! FIXME: Async API can use SteamRemoteStorage_ReadFileAsync
// !!! FIXME: Async API can use SteamRemoteStorage_WriteFileAsync
#define STEAM_PROC(ret, func, parms) \
typedef ret (*steamfntype_##func) parms;
#include "SDL_steamstorage_proc.h"
typedef struct STEAM_RemoteStorage
{
void *libsteam_api;
#define STEAM_PROC(ret, func, parms) \
steamfntype_##func func;
#include "SDL_steamstorage_proc.h"
} STEAM_RemoteStorage;
static int STEAM_UserStorageClose(void *userdata)
{
int result = 0;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
result = SDL_SetError("SteamRemoteStorage unavailable");
} else if (!steam->SteamAPI_ISteamRemoteStorage_EndFileWriteBatch(steamremotestorage)) {
result = SDL_SetError("SteamRemoteStorage()->EndFileWriteBatch() failed");
}
SDL_UnloadObject(steam->libsteam_api);
SDL_free(steam);
return result;
}
static SDL_bool STEAM_UserStorageReady(void *userdata)
{
return SDL_TRUE;
}
static int STEAM_UserStorageFileSize(void *userdata, const char *path, Uint64 *length)
{
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);
return 0;
}
static int STEAM_UserStorageReadFile(void *userdata, const char *path, void *destination, Uint64 length)
{
int retval;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
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;
}
static int STEAM_UserStorageWriteFile(void *userdata, const char *path, const void *source, Uint64 length)
{
int retval;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
return SDL_SetError("SteamRemoteStorage unavailable");
}
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;
}
static Uint64 STEAM_UserStorageSpaceRemaining(void *userdata)
{
Uint64 total, remaining;
STEAM_RemoteStorage *steam = (STEAM_RemoteStorage*) userdata;
void *steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
SDL_SetError("SteamRemoteStorage unavailable");
return 0;
}
if (!steam->SteamAPI_ISteamRemoteStorage_GetQuota(steamremotestorage, &total, &remaining)) {
SDL_SetError("SteamRemoteStorage()->GetQuota failed");
return 0;
}
return remaining;
}
static const SDL_StorageInterface STEAM_user_iface = {
STEAM_UserStorageClose,
STEAM_UserStorageReady,
STEAM_UserStorageFileSize,
STEAM_UserStorageReadFile,
STEAM_UserStorageWriteFile,
STEAM_UserStorageSpaceRemaining
};
static SDL_Storage *STEAM_User_Create(const char *org, const char *app, SDL_PropertiesID props)
{
SDL_Storage *result;
STEAM_RemoteStorage *steam;
void *steamremotestorage;
steam = (STEAM_RemoteStorage*) SDL_malloc(sizeof(STEAM_RemoteStorage));
if (steam == NULL) {
SDL_OutOfMemory();
return NULL;
}
steam->libsteam_api = SDL_LoadObject(
#if defined(_WIN64)
"steam_api64.dll"
#elif defined(_WIN32)
"steam_api.dll"
#elif defined(__APPLE__)
"libsteam_api.dylib"
#else
"libsteam_api.so"
#endif
);
if (steam->libsteam_api == NULL) {
SDL_free(steam);
return NULL;
}
#define STEAM_PROC(ret, func, parms) \
steam->func = (steamfntype_##func) SDL_LoadFunction(steam->libsteam_api, #func); \
if (steam->func == NULL) { \
SDL_SetError("Could not load function " #func); \
goto steamfail; \
}
#include "SDL_steamstorage_proc.h"
steamremotestorage = steam->SteamAPI_SteamRemoteStorage_v016();
if (steamremotestorage == NULL) {
SDL_SetError("SteamRemoteStorage unavailable");
goto steamfail;
}
if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount(steamremotestorage)) {
SDL_SetError("Steam cloud is disabled for this user");
goto steamfail;
}
if (!steam->SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp(steamremotestorage)) {
SDL_SetError("Steam cloud is disabled for this application");
goto steamfail;
}
if (!steam->SteamAPI_ISteamRemoteStorage_BeginFileWriteBatch(steamremotestorage)) {
SDL_SetError("SteamRemoteStorage()->BeginFileWriteBatch failed");
goto steamfail;
}
result = SDL_OpenStorage(&STEAM_user_iface, steam);
if (result == NULL) {
goto steamfail;
}
return result;
steamfail:
SDL_UnloadObject(steam->libsteam_api);
SDL_free(steam);
return NULL;
}
UserStorageBootStrap STEAM_userbootstrap = {
"steam",
"SDL Steam user storage driver",
STEAM_User_Create
};

View File

@ -0,0 +1,14 @@
STEAM_PROC(void*, SteamAPI_SteamRemoteStorage_v016, (void))
STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_IsCloudEnabledForAccount, (void*))
STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_IsCloudEnabledForApp, (void*))
STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_BeginFileWriteBatch, (void*))
STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_EndFileWriteBatch, (void*))
STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_GetFileSize, (void*, const char*))
STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_FileRead, (void*, const char*, void*, Sint32))
STEAM_PROC(Sint32, SteamAPI_ISteamRemoteStorage_FileWrite, (void*, const char*, const void*, Sint32))
STEAM_PROC(bool, SteamAPI_ISteamRemoteStorage_GetQuota, (void*, Uint64*, Uint64*))
#undef STEAM_PROC