The storage API is a high-level API designed to abstract away the portability issues that come up when using something lower-level (in SDL's case, this sits on top of the [Filesystem](CategoryFilesystem) and [IOStream](CategoryIOStream) subsystems).
More...
|
| Storage | SDL::OpenTitleStorage (StringParam override, PropertiesRef props) |
| | Opens up a read-only container for the application's filesystem.
|
| Storage | SDL::OpenUserStorage (StringParam org, StringParam app, PropertiesRef props) |
| | Opens up a container for a user's unique read/write filesystem.
|
| Storage | SDL::OpenFileStorage (StringParam path) |
| | Opens up a container for local filesystem storage.
|
| Storage | SDL::OpenStorage (const StorageInterface &iface, void *userdata) |
| | Opens up a container using a client-provided storage interface.
|
| bool | SDL::CloseStorage (StorageRaw storage) |
| | Closes and frees a storage container.
|
| bool | SDL::StorageReady (StorageRef storage) |
| | Checks if the storage container is ready to use.
|
| std::optional< Uint64 > | SDL::GetStorageFileSize (StorageRef storage, StringParam path) |
| | Query the size of a file within a storage container.
|
| bool | SDL::ReadStorageFile (StorageRef storage, StringParam path, TargetBytes destination) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| std::string | SDL::ReadStorageFile (StorageRef storage, StringParam path) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| template<class T> |
| std::vector< T > | SDL::ReadStorageFileAs (StorageRef storage, StringParam path) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| void | SDL::WriteStorageFile (StorageRef storage, StringParam path, SourceBytes source) |
| | Synchronously write a file from client memory into a storage container.
|
| void | SDL::CreateStorageDirectory (StorageRef storage, StringParam path) |
| | Create a directory in a writable storage container.
|
| void | SDL::EnumerateStorageDirectory (StorageRef storage, StringParam path, EnumerateDirectoryCallback callback, void *userdata) |
| | Enumerate a directory in a storage container through a callback function.
|
| void | SDL::EnumerateStorageDirectory (StorageRef storage, StringParam path, EnumerateDirectoryCB callback) |
| | Enumerate a directory in a storage container through a callback function.
|
| std::vector< Path > | SDL::EnumerateStorageDirectory (StorageRef storage, StringParam path) |
| | Enumerate a directory in a storage container through a callback function.
|
| void | SDL::RemoveStoragePath (StorageRef storage, StringParam path) |
| | Remove a file or an empty directory in a writable storage container.
|
| void | SDL::RenameStoragePath (StorageRef storage, StringParam oldpath, StringParam newpath) |
| | Rename a file or directory in a writable storage container.
|
| void | SDL::CopyStorageFile (StorageRef storage, StringParam oldpath, StringParam newpath) |
| | Copy a file in a writable storage container.
|
| PathInfo | SDL::GetStoragePathInfo (StorageRef storage, StringParam path) |
| | Get information about a filesystem path in a storage container.
|
| Uint64 | SDL::GetStorageSpaceRemaining (StorageRef storage) |
| | Queries the remaining space in a storage container.
|
| OwnArray< char * > | SDL::GlobStorageDirectory (StorageRef storage, StringParam path, StringParam pattern, GlobFlags flags) |
| | Enumerate a directory tree, filtered by pattern, and return a list.
|
| | SDL::Storage::Storage (StringParam override, PropertiesRef props) |
| | Opens up a read-only container for the application's filesystem.
|
| | SDL::Storage::Storage (StringParam org, StringParam app, PropertiesRef props) |
| | Opens up a container for a user's unique read/write filesystem.
|
| | SDL::Storage::Storage (StringParam path) |
| | Opens up a container for local filesystem storage.
|
| | SDL::Storage::Storage (const StorageInterface &iface, void *userdata) |
| | Opens up a container using a client-provided storage interface.
|
| bool | SDL::Storage::Close () |
| | Closes and frees a storage container.
|
| bool | SDL::Storage::Ready () |
| | Checks if the storage container is ready to use.
|
| std::optional< Uint64 > | SDL::Storage::GetFileSize (StringParam path) |
| | Query the size of a file within a storage container.
|
| bool | SDL::Storage::ReadFile (StringParam path, TargetBytes destination) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| std::string | SDL::Storage::ReadFile (StringParam path) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| template<class T> |
| std::vector< T > | SDL::Storage::ReadFileAs (StringParam path) |
| | Synchronously read a file from a storage container into a client-provided buffer.
|
| void | SDL::Storage::WriteFile (StringParam path, SourceBytes source) |
| | Synchronously write a file from client memory into a storage container.
|
| void | SDL::Storage::CreateDirectory (StringParam path) |
| | Create a directory in a writable storage container.
|
| void | SDL::Storage::EnumerateDirectory (StringParam path, EnumerateDirectoryCallback callback, void *userdata) |
| | Enumerate a directory in a storage container through a callback function.
|
| std::vector< Path > | SDL::Storage::EnumerateDirectory (StringParam path) |
| | Enumerate a directory in a storage container through a callback function.
|
| void | SDL::Storage::EnumerateDirectory (StringParam path, EnumerateDirectoryCB callback) |
| | Enumerate a directory in a storage container through a callback function.
|
| void | SDL::Storage::RemovePath (StringParam path) |
| | Remove a file or an empty directory in a writable storage container.
|
| void | SDL::Storage::RenamePath (StringParam oldpath, StringParam newpath) |
| | Rename a file or directory in a writable storage container.
|
| void | SDL::Storage::CopyFile (StringParam oldpath, StringParam newpath) |
| | Copy a file in a writable storage container.
|
| PathInfo | SDL::Storage::GetPathInfo (StringParam path) |
| | Get information about a filesystem path in a storage container.
|
| Uint64 | SDL::Storage::GetSpaceRemaining () |
| | Queries the remaining space in a storage container.
|
| OwnArray< char * > | SDL::Storage::GlobDirectory (StringParam path, StringParam pattern, GlobFlags flags) |
| | Enumerate a directory tree, filtered by pattern, and return a list.
|
The storage API is a high-level API designed to abstract away the portability issues that come up when using something lower-level (in SDL's case, this sits on top of the [Filesystem](CategoryFilesystem) and [IOStream](CategoryIOStream) subsystems).
It is significantly more restrictive than a typical filesystem API, for a number of reasons:
- What to Access: A common pitfall with existing filesystem APIs is the assumption that all storage is monolithic. However, many other platforms (game consoles in particular) are more strict about what type of filesystem is being accessed; for example, game content and user data are usually two separate storage devices with entirely different characteristics (and possibly different low-level APIs altogether!).
- How to Access: Another common mistake is applications assuming that all storage is universally writeable - again, many platforms treat game content and user data as two separate storage devices, and only user data is writeable while game content is read-only.
- When to Access: The most common portability issue with filesystem access is timing - you cannot always assume that the storage device is always accessible all of the time, nor can you assume that there are no limits to how long you have access to a particular device.
Consider the following example:
void ReadGameData(void)
{
extern char** fileNames;
extern size_t numFiles;
for (size_t i = 0; i < numFiles; i += 1) {
FILE *data = fopen(fileNames[i], "rwb");
if (data == nullptr) {
} else {
fclose(data);
}
}
}
void ReadSave(void)
{
FILE *save = fopen("saves/save0.sav", "rb");
if (save == nullptr) {
} else {
fclose(save);
}
}
void WriteSave(void)
{
FILE *save = fopen("saves/save0.sav", "wb");
if (save == nullptr) {
} else {
fclose(save);
}
}
Going over the bullet points again:
- What to Access: This code accesses a global filesystem; game data and saves are all presumed to be in the current working directory (which may or may not be the game's installation folder!).
- How to Access: This code assumes that content paths are writeable, and that save data is also writeable despite being in the same location as the game data.
- When to Access: This code assumes that they can be called at any time, since the filesystem is always accessible and has no limits on how long the filesystem is being accessed.
Due to these assumptions, the filesystem code is not portable and will fail under these common scenarios:
- The game is installed on a device that is read-only, both content loading and game saves will fail or crash outright
- Game/User storage is not implicitly mounted, so no files will be found for either scenario when a platform requires explicitly mounting filesystems
- Save data may not be safe since the I/O is not being flushed or validated, so an error occurring elsewhere in the program may result in missing/corrupted save data
When using Storage, these types of problems are virtually impossible to trip over:
void ReadGameData(void)
{
extern char** fileNames;
extern size_t numFiles;
if (title == nullptr) {
}
while (!title.Ready()) {
}
for (size_t i = 0; i < numFiles; i += 1) {
void* dst;
Uint64 dstLen = title.GetSize(fileNames[i]);
if (dstLen > 0) {
if (title.ReadFile(fileNames[i], dst, dstLen)) {
} else {
}
} else {
}
}
}
void ReadSave(void)
{
if (user == nullptr) {
}
while (!user.Ready()) {
}
Uint64 saveLen = user.GetFileSize();
if (saveLen > 0) {
if (user.ReadFile("save0.sav", dst, saveLen)) {
} else {
}
} else {
}
}
void WriteSave(void)
{
if (user == nullptr) {
}
while (!user.Ready()) {
}
extern void *saveData;
if (!user.WriteFile("save0.sav", saveData, saveLen)) {
}
}
An abstract interface for filesystem access.
Definition SDL3pp_storage.h:265
void * malloc(size_t size)
Allocate uninitialized memory.
Definition SDL3pp_stdinc.h:593
void free(void *mem)
Free allocated memory.
Definition SDL3pp_stdinc.h:679
::Uint64 Uint64
An unsigned 64-bit integer type.
Definition SDL3pp_stdinc.h:311
Storage OpenTitleStorage(StringParam override, PropertiesRef props)
Opens up a read-only container for the application's filesystem.
Definition SDL3pp_storage.h:837
Storage OpenUserStorage(StringParam org, StringParam app, PropertiesRef props)
Opens up a container for a user's unique read/write filesystem.
Definition SDL3pp_storage.h:886
void Delay(Uint32 ms)
Wait a specified number of milliseconds before returning.
Definition SDL3pp_timer.h:132
Note the improvements that Storage makes:
- What to Access: This code explicitly reads from a title or user storage device based on the context of the function.
- How to Access: This code explicitly uses either a read or write function based on the context of the function.
- When to Access: This code explicitly opens the device when it needs to, and closes it when it is finished working with the filesystem.
The result is an application that is significantly more robust against the increasing demands of platforms and their filesystems!
A publicly available example of an Storage backend is the Steam Cloud backend - you can initialize Steamworks when starting the program, and then SDL will recognize that Steamworks is initialized and automatically use ISteamRemoteStorage when the application opens user storage. More importantly, when you open storage it knows to begin a "batch" of filesystem operations, and when you close storage it knows to end and flush the batch. This is used by Steam to support Dynamic Cloud Sync ; users can save data on one PC, put the device to sleep, and then continue playing on another PC (and vice versa) with the save data fully synchronized across all devices, allowing for a seamless experience without having to do full restarts of the program.
Notes on valid paths
All paths in the Storage API use Unix-style path separators ('/'). Using a different path separator will not work, even if the underlying platform would otherwise accept it. This is to keep code using the Storage API portable between platforms and Storage implementations and simplify app code.
Paths with relative directories ("." and "..") are forbidden by the Storage API.
All valid UTF-8 strings (discounting the nullptr terminator character and the '/' path separator) are usable for filenames, however, an underlying Storage implementation may not support particularly strange sequences and refuse to create files with those names, etc.