-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Update documentation to reflect the recommended way SDL_StorageReady()
should be used
#12400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nightmareci
wants to merge
1
commit into
libsdl-org:main
Choose a base branch
from
nightmareci:storage-ready-docs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,41 +48,54 @@ | |
* Consider the following example: | ||
* | ||
* ```c | ||
* void ReadGameData(void) | ||
* bool 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) { | ||
* FILE *file = fopen(fileNames[i], "rwb"); | ||
* if (file == NULL) { | ||
* // Something bad happened! | ||
* return false; | ||
* } else { | ||
* // A bunch of stuff happens here | ||
* fclose(data); | ||
* fclose(file); | ||
* } | ||
* } | ||
* return true; | ||
* } | ||
* | ||
* void ReadSave(void) | ||
* typedef struct SaveData_T | ||
* { | ||
* FILE *save = fopen("saves/save0.sav", "rb"); | ||
* if (save == NULL) { | ||
* // Data for your saves here | ||
* } SaveData_T; | ||
* | ||
* bool ReadSaveData(SaveData_T *saveData) | ||
* { | ||
* FILE *saveFile = fopen("saves/save0.sav", "rb"); | ||
* if (saveFile == NULL) { | ||
* // Something bad happened! | ||
* return false; | ||
* } else { | ||
* // A bunch of stuff happens here | ||
* fclose(save); | ||
* // A bunch of stuff reading saveFile, and putting data into | ||
* // saveData, happens here | ||
* fclose(saveFile); | ||
* } | ||
* return true; | ||
* } | ||
* | ||
* void WriteSave(void) | ||
* bool WriteSaveData(SaveData_T *saveData) | ||
* { | ||
* FILE *save = fopen("saves/save0.sav", "wb"); | ||
* if (save == NULL) { | ||
* FILE *saveFile = fopen("saves/save0.sav", "wb"); | ||
* if (saveFile == NULL) { | ||
* // Something bad happened! | ||
* return false; | ||
* } else { | ||
* // A bunch of stuff happens here | ||
* fclose(save); | ||
* // A bunch of stuff looking at saveData, and writing to saveFile, | ||
* // happens here | ||
* fclose(saveFile); | ||
* } | ||
* return true; | ||
* } | ||
* ``` | ||
* | ||
|
@@ -116,92 +129,241 @@ | |
* trip over: | ||
* | ||
* ```c | ||
* void ReadGameData(void) | ||
* bool ReadGameData(SDL_Storage *storage) | ||
* { | ||
* 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) { | ||
* if (SDL_GetStorageFileSize(storage, fileNames[i], &dstLen) && dstLen > 0) { | ||
* dst = SDL_malloc(dstLen); | ||
* if (SDL_ReadStorageFile(title, fileNames[i], dst, dstLen)) { | ||
* if (!dst) { | ||
* // Something bad happened! | ||
* return false; | ||
* } | ||
* if (SDL_ReadStorageFile(storage, fileNames[i], dst, dstLen)) { | ||
* // A bunch of stuff happens here | ||
* } else { | ||
* // Something bad happened! | ||
* return false; | ||
* } | ||
* SDL_free(dst); | ||
* } else { | ||
* // Something bad happened! | ||
* return false; | ||
* } | ||
* } | ||
* | ||
* SDL_CloseStorage(title); | ||
* return true; | ||
* } | ||
* | ||
* void ReadSave(void) | ||
* bool ReadSaveData(SDL_Storage *storage, SaveData_T *saveData) | ||
* { | ||
* 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 | ||
* Uint64 dstLen = 0; | ||
* if (SDL_GetStorageFileSize(storage, "save0.sav", &dstLen) && dstLen > 0) { | ||
* void* dst = SDL_malloc(dstLen); | ||
* if (!dst) { | ||
* // Something bad happened! | ||
* return false; | ||
* } | ||
* if (SDL_ReadStorageFile(storage, "save0.sav", dst, dstLen)) { | ||
* // A bunch of stuff happens here, putting data into saveData | ||
* } else { | ||
* // Something bad happened! | ||
* SDL_free(dst); | ||
* return false; | ||
* } | ||
* SDL_free(dst); | ||
* } else { | ||
* // Something bad happened! | ||
* return false; | ||
* } | ||
* | ||
* SDL_CloseStorage(user); | ||
* } | ||
* | ||
* void WriteSave(void) | ||
* bool WriteSaveData(SDL_Storage *storage, SaveData_T *saveData) | ||
* { | ||
* SDL_Storage *user = SDL_OpenUserStorage("libsdl", "Storage Example", 0); | ||
* if (user == NULL) { | ||
* void *dst; | ||
* Uint64 dstLen; | ||
* | ||
* // A bunch of stuff happens here looking at saveData, setting up dst and | ||
* // dstLen. | ||
* | ||
* if (!SDL_WriteStorageFile(storage, "save0.sav", dst, dstLen)) { | ||
* // Something bad happened! | ||
* return false; | ||
* } else { | ||
* return true; | ||
* } | ||
* while (!SDL_StorageReady(user)) { | ||
* SDL_Delay(1); | ||
* } | ||
* | ||
* // You probably want to only read or only write save data at a time, not | ||
* // both, so you could use an enum to control that. | ||
* typedef enum SaveDataOp_T | ||
* { | ||
* SAVE_DATA_OP_NONE, | ||
* SAVE_DATA_OP_READ, | ||
* SAVE_DATA_OP_WRITE | ||
* } SaveDataOp_T; | ||
* | ||
* // main() function style | ||
* int main(int argc, char **argv) | ||
* { | ||
* const char * const userOrg = "libsdl"; | ||
* const char * const userApp = "Storage Example"; | ||
* bool readGameDataNow = false; | ||
* SaveDataOp_T saveDataOp; | ||
* SaveData_T saveData; | ||
* bool quit = false; | ||
* while (!quit) { | ||
* // This part is key: You must keep polling events when waiting on | ||
* // storage to be ready (SDL_StorageReady() returns true). Events | ||
* // polling can be required on some platforms for storage to become | ||
* // ready at all! | ||
* SDL_Event event; | ||
* while (SDL_PollEvent(&event)) { | ||
* switch (event.type) { | ||
* case SDL_EVENT_QUIT: | ||
* quit = true; | ||
* break; | ||
* // Other events processed here... | ||
* } | ||
* } | ||
* | ||
* // Game/graphics/sound/etc. logic here, only using data known to | ||
* // already be valid right now. Logic would set readGameDataNow and/or | ||
* // saveDataOp when needed, then could wait for them every logic tick | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's a tick? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A single step of updating the game logic. In classical 60 FPS games, it'd be a single frame. |
||
* // until they indicate the requested storage operation is done; | ||
* // readGameDataNow switching from true last tick to false this tick | ||
* // indicates game data is done reading, saveDataOp switching from | ||
* // SAVE_DATA_OP_READ/SAVE_DATA_OP_WRITE last tick to SAVE_DATA_OP_NONE | ||
* // this tick indicates the save operation is done. | ||
* | ||
* // Remember, these operations won't complete if the storage they use | ||
* // isn't yet ready, so your game logic should be able to wait multiple | ||
* // ticks in case the storage operation(s) didn't complete the same tick | ||
* // the operation(s) were requested! | ||
* if (saveDataOp == SAVE_DATA_OP_READ) { | ||
* SDL_Storage *user = SDL_OpenUserStorage(userOrg, userApp, 0); | ||
* if (!user) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } else if (SDL_StorageReady(user)) { | ||
* if (!ReadSaveData(user, &saveData)) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } | ||
* saveDataOp = SAVE_DATA_OP_NONE; | ||
* } | ||
* SDL_CloseStorage(user); | ||
* } else if (saveDataOp == SAVE_DATA_OP_WRITE) { | ||
* SDL_Storage *user = SDL_OpenUserStorage(userOrg, userApp, 0); | ||
* if (!user) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } else if (SDL_StorageReady(user)) { | ||
* if (!WriteSaveData(user, &saveData)) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } | ||
* saveDataOp = SAVE_DATA_OP_NONE; | ||
* } | ||
* SDL_CloseStorage(user); | ||
* } | ||
* if (readGameDataNow) { | ||
* SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0); | ||
* if (!title) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } else if (SDL_StorageReady(title)) { | ||
* if (!ReadGameData(title)) { | ||
* // Something bad happened! | ||
* return 1; | ||
* } | ||
* readGameDataNow = false; | ||
* } | ||
* SDL_CloseStorage(title); | ||
* } | ||
* } | ||
* return 0; | ||
* } | ||
* | ||
* extern void *saveData; // A bunch of stuff happened here... | ||
* extern Uint64 saveLen; | ||
* if (!SDL_WriteStorageFile(user, "save0.sav", saveData, saveLen)) { | ||
* // Something bad happened! | ||
* // App callbacks style | ||
* // SDL takes care of polling events for you with app callbacks, so you're | ||
* // good there already. | ||
* const char * const userOrg = "libsdl"; | ||
* const char * const userApp = "Storage Example"; | ||
* bool readGameDataNow = false; | ||
* SaveDataOp_T saveDataOp = SAVE_DATA_OP_NONE; | ||
* SaveData_T saveData; | ||
* SDL_AppResult SDL_AppIterate(void *appstate) | ||
* { | ||
* // Game/graphics/sound/etc. logic here, only using data known to already be | ||
* // valid right now. Logic would set readGameDataNow and/or saveDataOp when | ||
* // needed, then could wait for them every logic tick until they indicate | ||
* // the requested storage operation is done; readGameDataNow switching from | ||
* // true last tick to false this tick indicates game data is done reading, | ||
* // saveDataOp switching from SAVE_DATA_OP_READ/SAVE_DATA_OP_WRITE last tick | ||
* // to SAVE_DATA_OP_NONE this tick indicates the save operation is done. | ||
* | ||
* // Remember, these operations won't complete if the storage they use isn't | ||
* // yet ready, so your game logic should be able to wait multiple ticks in | ||
* // case the storage operation(s) didn't complete the same tick the | ||
* // operation(s) were requested! | ||
* if (saveDataOp == SAVE_DATA_OP_READ) { | ||
* SDL_Storage *user = SDL_OpenUserStorage(userOrg, userApp, 0); | ||
* if (!user) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } else if (SDL_StorageReady(user)) { | ||
* if (!ReadSaveData(user, &saveData)) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } | ||
* saveDataOp = SAVE_DATA_OP_NONE; | ||
* } | ||
* SDL_CloseStorage(user); | ||
* } else if (saveDataOp == SAVE_DATA_OP_WRITE) { | ||
* SDL_Storage *user = SDL_OpenUserStorage(userOrg, userApp, 0); | ||
* if (!user) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } else if (SDL_StorageReady(user)) { | ||
* if (!WriteSaveData(user, &saveData)) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } | ||
* saveDataOp = SAVE_DATA_OP_NONE; | ||
* } | ||
* SDL_CloseStorage(user); | ||
* } | ||
* if (readGameDataNow) { | ||
* SDL_Storage *title = SDL_OpenTitleStorage(NULL, 0); | ||
* if (!title) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } else if (SDL_StorageReady(title)) { | ||
* if (!ReadGameData(title)) { | ||
* // Something bad happened! | ||
* return SDL_APP_FAILURE; | ||
* } | ||
* readGameDataNow = false; | ||
* } | ||
* SDL_CloseStorage(title); | ||
* } | ||
* | ||
* SDL_CloseStorage(user); | ||
* return SDL_APP_CONTINUE; | ||
* } | ||
* ``` | ||
* | ||
* 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. | ||
* storage device based on the context of the operation. | ||
* | ||
* 2. **How to Access:** This code explicitly uses either a read or write | ||
* function based on the context of the function. | ||
* function based on the context of the operation. | ||
* | ||
* 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. | ||
|
@@ -450,7 +612,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CloseStorage(SDL_Storage *storage); | |
* | ||
* This function should be called in regular intervals until it returns true - | ||
* however, it is not recommended to spinwait on this call, as the backend may | ||
* depend on a synchronous message loop. You might instead poll this in your | ||
* depend on a synchronous message loop. You should instead poll this in your | ||
* game's main loop while processing events and drawing a loading screen. | ||
* | ||
* \param storage a storage container to query. | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.