ストレージAPIは低レベルAPI(SDLの場合ならばファイルシステムやI/Oストリーム)を使用する場合に生じる移植性の問題を抽象化するために設計された高レベルAPIである. このAPIはいくつかの理由で一般的なファイルシステムAPIと比べて厳格になっている:
次の例について考えてみる:
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) {
// 何か問題が発生した!
} else {
// ここで様々な処理を行う
fclose(data);
}
}
}
void ReadSave(void)
{
FILE *save = fopen("saves/save0.sav", "rb");
if (save == NULL) {
// 何か問題が発生した!
} else {
// ここで様々な処理を行う
fclose(save);
}
}
void WriteSave(void)
{
FILE *save = fopen("saves/save0.sav", "wb");
if (save == NULL) {
// 何か問題が発生した!
} else {
// ここで様々な処理を行う
fclose(save);
}
}
もう一度問題点を箇条書きする:
この思い込みのせいで, ファイルシステムに関するコードは移植性が低く, 次のような場合に失敗する:
SDL_Storageを使うと, これらの問題につまづくことはなくなる:
void ReadGameData(void)
{
extern char** fileNames;
extern size_t numFiles;
SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0);
if (title == NULL) {
// 何か問題が発生した!
}
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)) {
// ここで様々な処理を行う
} else {
// 何か問題が発生した!
}
SDL_free(dst);
} else {
// 何か問題が発生した!
}
}
SDL_CloseStorage(title);
}
void ReadSave(void)
{
SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0);
if (user == NULL) {
// 何か問題が発生した!
}
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)) {
// ここで様々な処理を行う
} else {
// 何か問題が発生した!
}
SDL_free(dst);
} else {
// 何か問題が発生した!
}
SDL_CloseStorage(user);
}
void WriteSave(void)
{
SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0);
if (user == NULL) {
// 何か問題が発生した!
}
while (!SDL_StorageReady(user)) {
SDL_Delay(1);
}
extern void *saveData; // ここで様々な処理を行う...
extern Uint64 saveLen;
if (!SDL_WriteStorageFile(user, "save0.sav", saveData, saveLen)) {
// 何か問題が発生した!
}
SDL_CloseStorage(user);
}
SDL_Storageによる改善点は:
その結果として, アプリケーションは環境とそのファイルシステムの増大する要求に対してより堅牢になった.
パブリックにアクセス可能なSDL_Storageバックエンドの例はSteamクラウドバックエンドである――プログラムを開始するとSteamworksを初期化できる. その後, SDLはSteamworksが初期化されたと認識し, アプリケーションがユーザストレージをオープンするとき自動的にISteamRemoteStorageを使用する. さらに重要なのは, ストレージをオープンするとファイルシステムの「バッチ」操作が始まったと認識され, ストレージをクローズすると終了したと認識されバッチが掃き出されることである. これはSteamでDynamic Cloud Syncに対応するために使用される. ユーザは自分のPCにデータを保存し, デバイスをスリープさせ, 全てのデバイスでセーブデータを完全に同期させて別のPCでゲームを続けることができるため, プログラムを再スタートさせずに途切れることなくプレーすることができる.
ストレージAPIの全てのパスは, UNIXスタイルの区切り文字('/')を使用する. パスで異なる区切り文字を使用すると, その環境の下層が受け入れる場合でも, 動作しない. これはストレージAPIのストレージ間と環境間の移植性の維持と, アプリケーションのコードをシンプルにするためである.
ストレージAPIでは相対ディレクトリ("."と"..")は使用できない.
全ての正しいUTF-8文字列(NUL文字が終端でパスが'/'で区切られている)はファイル名として使用できるが, 下層のストレージが対応していないため, その名前でのファイルの生成が拒否される場合もある.