SDL_StorageInterface Struct Reference

`#include <SDL_storage.h>`

## Data Fields

Uint32 | version
bool(* | close )(void *userdata)
bool(* | ready )(void *userdata)
bool(* | enumerate )(void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)
bool(* | info )(void *userdata, const char *path, SDL_PathInfo *info)
bool(* | read_file )(void *userdata, const char *path, void *destination, Uint64 length)
bool(* | write_file )(void *userdata, const char *path, const void *source, Uint64 length)
bool(* | mkdir )(void *userdata, const char *path)
bool(* | remove )(void *userdata, const char *path)
bool(* | rename )(void *userdata, const char *oldpath, const char *newpath)
bool(* | copy )(void *userdata, const char *oldpath, const char *newpath)
Uint64(* | space_remaining )(void *userdata)

## Detailed Description

# CategoryStorage

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:

1. 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!).
2. 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.
3. 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 == NULL) {
// Something bad happened!
} else {
// A bunch of stuff happens here
fclose(data);
}
}
}
void ReadSave(void)
{
FILE *save = fopen("saves/save0.sav", "rb");
if (save == NULL) {
// Something bad happened!
} else {
// A bunch of stuff happens here
fclose(save);
}
}
void WriteSave(void)
{
FILE *save = fopen("saves/save0.sav", "wb");
if (save == NULL) {
// Something bad happened!
} else {
// A bunch of stuff happens here
fclose(save);
}
}
NULL
#define NULL
Definition SDL_begin_code.h:417

Going over the bullet points again:

1. 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!).
2. 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.
3. 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 SDL_Storage, these types of problems are virtually impossible to trip over:

void ReadGameData(void)
{
extern char** fileNames;
extern size_t numFiles;
SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0);
if (title == NULL) {
// Something bad happened!
}
while (!SDL_StorageReady(title)) {
SDL_Delay(1);
}
for (size_t i = 0; i < numFiles; i += 1) {
void* dst;
Uint64 dstLen = 0;
if (SDL_GetStorageFileSize(title, fileNames[i], &dstLen) && dstLen > 0) {
dst = SDL_malloc(dstLen);
if (SDL_ReadStorageFile(title, fileNames[i], dst, dstLen)) {
// A bunch of stuff happens here
} else {
// Something bad happened!
}
SDL_free(dst);
} else {
// Something bad happened!
}
}
SDL_CloseStorage(title);
}
void ReadSave(void)
{
SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0);
if (user == NULL) {
// Something bad happened!
}
while (!SDL_StorageReady(user)) {
SDL_Delay(1);
}
Uint64 saveLen = 0;
if (SDL_GetStorageFileSize(user, "save0.sav", &saveLen) && saveLen > 0) {
void* dst = SDL_malloc(saveLen);
if (SDL_ReadStorageFile(user, "save0.sav", dst, saveLen)) {
// A bunch of stuff happens here
} else {
// Something bad happened!
}
SDL_free(dst);
} else {
// Something bad happened!
}
SDL_CloseStorage(user);
}
void WriteSave(void)
{
SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0);
if (user == NULL) {
// Something bad happened!
}
while (!SDL_StorageReady(user)) {
SDL_Delay(1);
}
extern void *saveData; // A bunch of stuff happened here...
extern Uint64 saveLen;
if (!SDL_WriteStorageFile(user, "save0.sav", saveData, saveLen)) {
// Something bad happened!
}
SDL_CloseStorage(user);
}
SDL_malloc
SDL_MALLOC void * SDL_malloc(size_t size)
Uint64
uint64_t Uint64
Definition SDL_stdinc.h:483
SDL_free
void SDL_free(void *mem)
SDL_OpenTitleStorage
SDL_Storage * SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props)
SDL_GetStorageFileSize
bool SDL_GetStorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length)
SDL_ReadStorageFile
bool SDL_ReadStorageFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length)
SDL_OpenUserStorage
SDL_Storage * SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props)
SDL_Storage
struct SDL_Storage SDL_Storage
Definition SDL_storage.h:332
SDL_CloseStorage
bool SDL_CloseStorage(SDL_Storage *storage)
SDL_WriteStorageFile
bool SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length)
SDL_StorageReady
bool SDL_StorageReady(SDL_Storage *storage)
SDL_Delay
void SDL_Delay(Uint32 ms)

Note the improvements that SDL_Storage makes:

1. What to Access: This code explicitly reads from a title or user storage device based on the context of the function.
2. How to Access: This code explicitly uses either a read or write function based on the context of the function.
3. 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 SDL_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 NULL 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. Function interface for SDL_Storage.

Apps that want to supply a custom implementation of SDL_Storage will fill in all the functions in this struct, and then pass it to SDL_OpenStorage to create a custom SDL_Storage object.

It is not usually necessary to do this; SDL provides standard implementations for many things you might expect to do with an SDL_Storage.

This structure should be initialized using SDL_INIT_INTERFACE()

Since
  This struct is available since SDL 3.2.0.

See also
  SDL_INIT_INTERFACE

Definition at line 274 of file SDL_storage.h.

## Field Documentation

## close

bool(* SDL_StorageInterface::close) (void *userdata)

Definition at line 280 of file SDL_storage.h.

## copy

bool(* SDL_StorageInterface::copy) (void *userdata, const char *oldpath, const char *newpath)

Definition at line 307 of file SDL_storage.h.

## enumerate

bool(* SDL_StorageInterface::enumerate) (void *userdata, const char *path, SDL_EnumerateDirectoryCallback callback, void *callback_userdata)

Definition at line 286 of file SDL_storage.h.

## info

bool(* SDL_StorageInterface::info) (void *userdata, const char *path, SDL_PathInfo *info)

Definition at line 289 of file SDL_storage.h.

## mkdir

bool(* SDL_StorageInterface::mkdir) (void *userdata, const char *path)

Definition at line 298 of file SDL_storage.h.

## read_file

bool(* SDL_StorageInterface::read_file) (void *userdata, const char *path, void *destination, Uint64 length)

Definition at line 292 of file SDL_storage.h.

## ready

bool(* SDL_StorageInterface::ready) (void *userdata)

Definition at line 283 of file SDL_storage.h.

## remove

bool(* SDL_StorageInterface::remove) (void *userdata, const char *path)

Definition at line 301 of file SDL_storage.h.

## rename

bool(* SDL_StorageInterface::rename) (void *userdata, const char *oldpath, const char *newpath)

Definition at line 304 of file SDL_storage.h.

## space_remaining

Uint64(* SDL_StorageInterface::space_remaining) (void *userdata)

Definition at line 310 of file SDL_storage.h.

## version

Uint32 SDL_StorageInterface::version

Definition at line 277 of file SDL_storage.h.

## write_file

bool(* SDL_StorageInterface::write_file) (void *userdata, const char *path, const void *source, Uint64 length)

Definition at line 295 of file SDL_storage.h.

The documentation for this struct was generated from the following file:

- SDL_storage.h