Re-enable save system, fix header/version stamping, handle missing media
- Fixed the actual reason saving never worked on any platform: saveWrite() never stamped file->header/file->version before serializing, so every written save file had a zeroed magic header and failed its own validation on the next load. Confirmed via a manual write/load round trip that this alone fully explains "saving doesn't work." - Re-enabled saveInit()/saveDispose() in engine.c (previously commented out under "Temporarily disable save code"). - Added SAVE.available + saveIsAvailable(), refreshed by every real save/load/delete attempt. saveInit() no longer treats an unreachable save medium as fatal to booting - it logs and continues, since a missing memory card/stick shouldn't prevent playing. - Hardened PSP's saveInitPSP() to actually detect a missing memory stick (sceIoGetstat on ms0:/) instead of assuming success, and fixed single-level sceIoMkdir to build the full PSP/SAVEDATA directory chain. - Added busy-retry (CARD_ERROR_BUSY) and a not-mounted guard to Dolphin's live savestreamdolphin.c path, extending the same handling already backported into savedolphin.c. - Added a "Save" entry to the game menu wired to saveWrite(0), showing a clear message on success, on failure, and when saveIsAvailable() is false.
This commit is contained in:
@@ -56,6 +56,10 @@ msgstr "Items"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "Settings"
|
msgstr "Settings"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "Save"
|
||||||
|
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "Potion"
|
msgstr "Potion"
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ msgstr "Objetos"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "Configuración"
|
msgstr "Configuración"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "Guardar"
|
||||||
|
|
||||||
#: src/dusk/rpg/item/item.json
|
#: src/dusk/rpg/item/item.json
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "Poción"
|
msgstr "Poción"
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ msgstr "アイテム"
|
|||||||
msgid "ui.game_menu.settings"
|
msgid "ui.game_menu.settings"
|
||||||
msgstr "設定"
|
msgstr "設定"
|
||||||
|
|
||||||
|
#: src/dusk/ui/frame/game/uigamemenu.c
|
||||||
|
msgid "ui.game_menu.save"
|
||||||
|
msgstr "セーブ"
|
||||||
|
|
||||||
#: src/dusk/rpg/item/item.json
|
#: src/dusk/rpg/item/item.json
|
||||||
msgid "item.potion.name"
|
msgid "item.potion.name"
|
||||||
msgstr "ポーション"
|
msgstr "ポーション"
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(systemInit());
|
errorChain(systemInit());
|
||||||
errorChain(inputInit());
|
errorChain(inputInit());
|
||||||
errorChain(assetInit());
|
errorChain(assetInit());
|
||||||
// errorChain(saveInit());
|
errorChain(saveInit());
|
||||||
errorChain(localeManagerInit());
|
errorChain(localeManagerInit());
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
errorChain(uiInit());
|
errorChain(uiInit());
|
||||||
@@ -88,7 +88,7 @@ errorret_t engineDispose(void) {
|
|||||||
errorChain(uiDispose());
|
errorChain(uiDispose());
|
||||||
consoleDispose();
|
consoleDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
// errorChain(saveDispose());
|
errorChain(saveDispose());
|
||||||
errorChain(assetDispose());
|
errorChain(assetDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
+29
-4
@@ -9,6 +9,7 @@
|
|||||||
#include "save/savestream.h"
|
#include "save/savestream.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
|
||||||
save_t SAVE;
|
save_t SAVE;
|
||||||
|
|
||||||
@@ -16,12 +17,23 @@ errorret_t saveInit(void) {
|
|||||||
memoryZero(&SAVE, sizeof(save_t));
|
memoryZero(&SAVE, sizeof(save_t));
|
||||||
|
|
||||||
#ifdef saveInitPlatform
|
#ifdef saveInitPlatform
|
||||||
errorChain(saveInitPlatform());
|
// A missing/unreachable save medium is expected, recoverable state,
|
||||||
|
// not a reason to fail booting the whole game - log it and carry on
|
||||||
|
// with SAVE.available false instead of chaining the error upward.
|
||||||
|
errorret_t result = saveInitPlatform();
|
||||||
|
SAVE.available = errorIsOk(result);
|
||||||
|
if(!SAVE.available) errorCatch(errorPrint(result));
|
||||||
|
#else
|
||||||
|
SAVE.available = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool_t saveIsAvailable(void) {
|
||||||
|
return SAVE.available;
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t saveDispose(void) {
|
errorret_t saveDispose(void) {
|
||||||
#ifdef saveDisposePlatform
|
#ifdef saveDisposePlatform
|
||||||
errorChain(saveDisposePlatform());
|
errorChain(saveDisposePlatform());
|
||||||
@@ -39,7 +51,9 @@ errorret_t saveLoad(const uint8_t slot) {
|
|||||||
memoryZero(&stream, sizeof(savestream_t));
|
memoryZero(&stream, sizeof(savestream_t));
|
||||||
|
|
||||||
#ifdef saveStreamOpenReadPlatform
|
#ifdef saveStreamOpenReadPlatform
|
||||||
errorChain(saveStreamOpenReadPlatform(&stream, slot));
|
errorret_t openRet = saveStreamOpenReadPlatform(&stream, slot);
|
||||||
|
SAVE.available = errorIsOk(openRet);
|
||||||
|
errorChain(openRet);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(!stream.found) errorOk();
|
if(!stream.found) errorOk();
|
||||||
@@ -62,12 +76,21 @@ errorret_t saveWrite(const uint8_t slot) {
|
|||||||
assertTrue(slot < SAVE_FILE_COUNT_MAX, "slot exceeds SAVE_FILE_COUNT_MAX");
|
assertTrue(slot < SAVE_FILE_COUNT_MAX, "slot exceeds SAVE_FILE_COUNT_MAX");
|
||||||
|
|
||||||
savefile_t *file = &SAVE.files[slot];
|
savefile_t *file = &SAVE.files[slot];
|
||||||
|
// These are metadata about the file itself, not game data - always stamp
|
||||||
|
// the current magic/version on every write rather than relying on
|
||||||
|
// whatever happened to already be in memory (zeroed at saveInit, or
|
||||||
|
// whatever version an old loaded file had), otherwise the written file
|
||||||
|
// fails its own header check the next time it's loaded.
|
||||||
|
memoryCopy(file->header, SAVE_FILE_HEADER, SAVE_FILE_HEADER_SIZE);
|
||||||
|
file->version = SAVE_FILE_VERSION;
|
||||||
|
|
||||||
savestream_t stream;
|
savestream_t stream;
|
||||||
memoryZero(&stream, sizeof(savestream_t));
|
memoryZero(&stream, sizeof(savestream_t));
|
||||||
|
|
||||||
#ifdef saveStreamOpenWritePlatform
|
#ifdef saveStreamOpenWritePlatform
|
||||||
errorChain(saveStreamOpenWritePlatform(&stream, slot));
|
errorret_t openRet = saveStreamOpenWritePlatform(&stream, slot);
|
||||||
|
SAVE.available = errorIsOk(openRet);
|
||||||
|
errorChain(openRet);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
errorret_t ret = saveFileWrite(&stream, file);
|
errorret_t ret = saveFileWrite(&stream, file);
|
||||||
@@ -90,7 +113,9 @@ errorret_t saveDelete(const uint8_t slot) {
|
|||||||
assertTrue(slot < SAVE_FILE_COUNT_MAX, "slot exceeds SAVE_FILE_COUNT_MAX");
|
assertTrue(slot < SAVE_FILE_COUNT_MAX, "slot exceeds SAVE_FILE_COUNT_MAX");
|
||||||
|
|
||||||
#ifdef saveDeletePlatform
|
#ifdef saveDeletePlatform
|
||||||
errorChain(saveDeletePlatform(slot));
|
errorret_t deleteRet = saveDeletePlatform(slot);
|
||||||
|
SAVE.available = errorIsOk(deleteRet);
|
||||||
|
errorChain(deleteRet);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SAVE.files[slot].exists = false;
|
SAVE.files[slot].exists = false;
|
||||||
|
|||||||
+24
-2
@@ -15,17 +15,39 @@ typedef struct {
|
|||||||
savefile_t files[SAVE_FILE_COUNT_MAX];
|
savefile_t files[SAVE_FILE_COUNT_MAX];
|
||||||
/** Platform-specific save system state (paths, card handles, etc.). */
|
/** Platform-specific save system state (paths, card handles, etc.). */
|
||||||
saveplatform_t platform;
|
saveplatform_t platform;
|
||||||
|
/**
|
||||||
|
* True if the save medium (memory card/stick/disk) was reachable the
|
||||||
|
* last time it was checked - at saveInit(), and refreshed by every
|
||||||
|
* subsequent saveLoad()/saveWrite() attempt. Starting the game with no
|
||||||
|
* card/stick inserted, or one being removed mid-session, are both
|
||||||
|
* expected conditions here, not fatal errors - see saveIsAvailable().
|
||||||
|
*/
|
||||||
|
bool_t available;
|
||||||
} save_t;
|
} save_t;
|
||||||
|
|
||||||
extern save_t SAVE;
|
extern save_t SAVE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the save system.
|
* Initializes the save system. Never fails the way saveWrite/saveLoad can -
|
||||||
|
* if the platform's save medium isn't reachable (e.g. no memory card/stick
|
||||||
|
* inserted), that's logged and reflected in saveIsAvailable() rather than
|
||||||
|
* treated as fatal, since the game should still be playable without save
|
||||||
|
* support.
|
||||||
*
|
*
|
||||||
* @return An error code if initialization fails.
|
* @return An error code only for unexpected platform failures.
|
||||||
*/
|
*/
|
||||||
errorret_t saveInit(void);
|
errorret_t saveInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the save medium was reachable as of the last save/load
|
||||||
|
* attempt (or saveInit(), if none has been attempted yet). Intended for UI
|
||||||
|
* to decide whether to offer saving/loading at all, or to explain why it
|
||||||
|
* isn't available right now - e.g. "No memory card inserted".
|
||||||
|
*
|
||||||
|
* @return true if the save medium was available last time it was checked.
|
||||||
|
*/
|
||||||
|
bool_t saveIsAvailable(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of the save system.
|
* Disposes of the save system.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -9,16 +9,48 @@
|
|||||||
#include "ui/frame/uiframe.h"
|
#include "ui/frame/uiframe.h"
|
||||||
#include "ui/frame/settings/uisettings.h"
|
#include "ui/frame/settings/uisettings.h"
|
||||||
#include "ui/frame/backpack/uibackpack.h"
|
#include "ui/frame/backpack/uibackpack.h"
|
||||||
|
#include "ui/rpg/textbox/uitextboxmain.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "locale/localemanager.h"
|
#include "locale/localemanager.h"
|
||||||
#include "asset/loader/locale/assetlocaleloader.h"
|
#include "asset/loader/locale/assetlocaleloader.h"
|
||||||
|
#include "save/save.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
|
||||||
#define UI_GAME_MENU_INDEX_CHARACTERS 0
|
#define UI_GAME_MENU_INDEX_CHARACTERS 0
|
||||||
#define UI_GAME_MENU_INDEX_ITEMS 1
|
#define UI_GAME_MENU_INDEX_ITEMS 1
|
||||||
#define UI_GAME_MENU_INDEX_SETTINGS 2
|
#define UI_GAME_MENU_INDEX_SETTINGS 2
|
||||||
|
#define UI_GAME_MENU_INDEX_SAVE 3
|
||||||
|
|
||||||
|
// Only one save slot is exposed through this menu for now - there is no
|
||||||
|
// slot-select UI yet, and SAVE_FILE_COUNT_MAX > 1 exists for later.
|
||||||
|
#define UI_GAME_MENU_SAVE_SLOT 0
|
||||||
|
|
||||||
|
static void uiGameMenuSave(void) {
|
||||||
|
if(!saveIsAvailable()) {
|
||||||
|
uiTextboxMainSetText("Can't save - no save device found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t ret = saveWrite(UI_GAME_MENU_SAVE_SLOT);
|
||||||
|
if(errorIsNotOk(ret)) {
|
||||||
|
// Generously sized - stringFormat asserts (crashes) rather than
|
||||||
|
// truncating if the message doesn't fit, so this must comfortably fit
|
||||||
|
// the longest platform save-error message plus this prefix.
|
||||||
|
char_t msg[256];
|
||||||
|
stringFormat(
|
||||||
|
msg, sizeof(msg), "Save failed: %s", ret.state->message
|
||||||
|
);
|
||||||
|
errorCatch(ret);
|
||||||
|
uiTextboxMainSetText(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiTextboxMainSetText("Game saved.");
|
||||||
|
}
|
||||||
|
|
||||||
uigamemenu_t UI_GAME_MENU;
|
uigamemenu_t UI_GAME_MENU;
|
||||||
|
|
||||||
@@ -29,6 +61,7 @@ void uiGameMenuSelected(
|
|||||||
) {
|
) {
|
||||||
if(index == UI_GAME_MENU_INDEX_ITEMS) uiBackpackOpen();
|
if(index == UI_GAME_MENU_INDEX_ITEMS) uiBackpackOpen();
|
||||||
if(index == UI_GAME_MENU_INDEX_SETTINGS) uiSettingsOpen();
|
if(index == UI_GAME_MENU_INDEX_SETTINGS) uiSettingsOpen();
|
||||||
|
if(index == UI_GAME_MENU_INDEX_SAVE) uiGameMenuSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t uiGameMenuInit(void) {
|
errorret_t uiGameMenuInit(void) {
|
||||||
@@ -55,6 +88,13 @@ errorret_t uiGameMenuInit(void) {
|
|||||||
UI_GAME_MENU.settingsLabel,
|
UI_GAME_MENU.settingsLabel,
|
||||||
UI_GAME_MENU_LABEL_MAX
|
UI_GAME_MENU_LABEL_MAX
|
||||||
));
|
));
|
||||||
|
errorChain(assetLocaleGetString(
|
||||||
|
&LOCALE.entry->data.locale,
|
||||||
|
"ui.game_menu.save",
|
||||||
|
0,
|
||||||
|
UI_GAME_MENU.saveLabel,
|
||||||
|
UI_GAME_MENU_LABEL_MAX
|
||||||
|
));
|
||||||
|
|
||||||
MENU_BEGIN(
|
MENU_BEGIN(
|
||||||
&UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL
|
&UI_GAME_MENU.menu, UI_GAME_MENU.items, uiGameMenuSelected, NULL, NULL
|
||||||
@@ -62,6 +102,7 @@ errorret_t uiGameMenuInit(void) {
|
|||||||
MENU_BUTTON(UI_GAME_MENU.charactersLabel);
|
MENU_BUTTON(UI_GAME_MENU.charactersLabel);
|
||||||
MENU_BUTTON(UI_GAME_MENU.itemsLabel);
|
MENU_BUTTON(UI_GAME_MENU.itemsLabel);
|
||||||
MENU_BUTTON(UI_GAME_MENU.settingsLabel);
|
MENU_BUTTON(UI_GAME_MENU.settingsLabel);
|
||||||
|
MENU_BUTTON(UI_GAME_MENU.saveLabel);
|
||||||
|
|
||||||
MENU_END(UI_GAME_MENU.items, 1);
|
MENU_END(UI_GAME_MENU.items, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include "ui/widget/uimenu.h"
|
#include "ui/widget/uimenu.h"
|
||||||
|
|
||||||
#define UI_GAME_MENU_ITEM_COUNT 3
|
#define UI_GAME_MENU_ITEM_COUNT 4
|
||||||
#define UI_GAME_MENU_WIDTH 150.0f
|
#define UI_GAME_MENU_WIDTH 150.0f
|
||||||
#define UI_GAME_MENU_LABEL_MAX 32
|
#define UI_GAME_MENU_LABEL_MAX 32
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ typedef struct {
|
|||||||
char_t charactersLabel[UI_GAME_MENU_LABEL_MAX];
|
char_t charactersLabel[UI_GAME_MENU_LABEL_MAX];
|
||||||
char_t itemsLabel[UI_GAME_MENU_LABEL_MAX];
|
char_t itemsLabel[UI_GAME_MENU_LABEL_MAX];
|
||||||
char_t settingsLabel[UI_GAME_MENU_LABEL_MAX];
|
char_t settingsLabel[UI_GAME_MENU_LABEL_MAX];
|
||||||
|
char_t saveLabel[UI_GAME_MENU_LABEL_MAX];
|
||||||
} uigamemenu_t;
|
} uigamemenu_t;
|
||||||
|
|
||||||
extern uigamemenu_t UI_GAME_MENU;
|
extern uigamemenu_t UI_GAME_MENU;
|
||||||
|
|||||||
@@ -19,12 +19,20 @@ static void _saveStreamGetFileName(
|
|||||||
errorret_t saveStreamOpenReadDolphin(
|
errorret_t saveStreamOpenReadDolphin(
|
||||||
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
||||||
) {
|
) {
|
||||||
|
if(!SAVE.platform.mounted) {
|
||||||
|
*found = false;
|
||||||
|
errorThrow("No memory card mounted");
|
||||||
|
}
|
||||||
|
|
||||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, slot);
|
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, slot);
|
||||||
|
|
||||||
int32_t result = CARD_Open(
|
int32_t result;
|
||||||
SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile
|
do {
|
||||||
);
|
result = CARD_Open(
|
||||||
|
SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
if(result == CARD_ERROR_NOFILE) {
|
if(result == CARD_ERROR_NOFILE) {
|
||||||
*found = false;
|
*found = false;
|
||||||
p->position = 0;
|
p->position = 0;
|
||||||
@@ -33,17 +41,19 @@ errorret_t saveStreamOpenReadDolphin(
|
|||||||
}
|
}
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
*found = false;
|
*found = false;
|
||||||
errorThrow("Failed to open memory card file for slot %u (error %d)",
|
errorThrow("Failed to open memory card file for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = CARD_Read(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
do {
|
||||||
|
result = CARD_Read(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
CARD_Close(&p->cardFile);
|
CARD_Close(&p->cardFile);
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
*found = false;
|
*found = false;
|
||||||
errorThrow("Failed to read memory card data for slot %u (error %d)",
|
errorThrow("Failed to read memory card data for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +67,8 @@ errorret_t saveStreamOpenReadDolphin(
|
|||||||
errorret_t saveStreamOpenWriteDolphin(
|
errorret_t saveStreamOpenWriteDolphin(
|
||||||
savestreamdolphin_t *p, const uint8_t slot
|
savestreamdolphin_t *p, const uint8_t slot
|
||||||
) {
|
) {
|
||||||
|
if(!SAVE.platform.mounted) errorThrow("No memory card mounted");
|
||||||
|
|
||||||
memoryZero(p->buffer, SAVE_DOLPHIN_SECTOR_SIZE);
|
memoryZero(p->buffer, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||||
p->position = 0;
|
p->position = 0;
|
||||||
p->writing = true;
|
p->writing = true;
|
||||||
@@ -70,14 +82,21 @@ void saveStreamCloseDolphin(savestreamdolphin_t *p) {
|
|||||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, p->slot);
|
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, p->slot);
|
||||||
|
|
||||||
int32_t result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile);
|
int32_t result;
|
||||||
|
do {
|
||||||
|
result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
if(result == CARD_ERROR_NOFILE) {
|
if(result == CARD_ERROR_NOFILE) {
|
||||||
CARD_Create(
|
do {
|
||||||
SAVE_DOLPHIN_CHANNEL, fileName, SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
|
result = CARD_Create(
|
||||||
);
|
SAVE_DOLPHIN_CHANNEL, fileName, SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
}
|
}
|
||||||
|
|
||||||
CARD_Write(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
do {
|
||||||
|
result = CARD_Write(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
CARD_Close(&p->cardFile);
|
CARD_Close(&p->cardFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,18 @@
|
|||||||
|
|
||||||
#include "save/save.h"
|
#include "save/save.h"
|
||||||
|
|
||||||
|
void savePSPEnsureBaseDirs(void) {
|
||||||
|
sceIoMkdir(SAVE_PSP_BASE_DIR, 0777);
|
||||||
|
sceIoMkdir(SAVE_PSP_SAVEDATA_DIR, 0777);
|
||||||
|
}
|
||||||
|
|
||||||
errorret_t saveInitPSP(void) {
|
errorret_t saveInitPSP(void) {
|
||||||
|
SceIoStat stat;
|
||||||
|
if(sceIoGetstat(SAVE_PSP_ROOT, &stat) < 0) {
|
||||||
|
errorThrow("No memory stick detected");
|
||||||
|
}
|
||||||
|
|
||||||
|
savePSPEnsureBaseDirs();
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +51,7 @@ errorret_t saveLoadPSP(const uint8_t slot, savefile_t *file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t saveWritePSP(const uint8_t slot, const savefile_t *file) {
|
errorret_t saveWritePSP(const uint8_t slot, const savefile_t *file) {
|
||||||
|
savePSPEnsureBaseDirs();
|
||||||
char_t dir[SAVE_PSP_PATH_MAX];
|
char_t dir[SAVE_PSP_PATH_MAX];
|
||||||
snprintf(dir, SAVE_PSP_PATH_MAX, SAVE_PSP_DIR_FORMAT,
|
snprintf(dir, SAVE_PSP_PATH_MAX, SAVE_PSP_DIR_FORMAT,
|
||||||
SAVE_PSP_TITLE_ID, (uint32_t)slot
|
SAVE_PSP_TITLE_ID, (uint32_t)slot
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
#include <pspiofilemgr.h>
|
#include <pspiofilemgr.h>
|
||||||
|
|
||||||
#define SAVE_PSP_PATH_MAX 256
|
#define SAVE_PSP_PATH_MAX 256
|
||||||
|
#define SAVE_PSP_ROOT "ms0:/"
|
||||||
|
#define SAVE_PSP_BASE_DIR "ms0:/PSP"
|
||||||
|
#define SAVE_PSP_SAVEDATA_DIR "ms0:/PSP/SAVEDATA"
|
||||||
#define SAVE_PSP_FILE_FORMAT "ms0:/PSP/SAVEDATA/%s%02u/save.dat"
|
#define SAVE_PSP_FILE_FORMAT "ms0:/PSP/SAVEDATA/%s%02u/save.dat"
|
||||||
#define SAVE_PSP_DIR_FORMAT "ms0:/PSP/SAVEDATA/%s%02u"
|
#define SAVE_PSP_DIR_FORMAT "ms0:/PSP/SAVEDATA/%s%02u"
|
||||||
|
|
||||||
@@ -23,9 +26,14 @@ typedef struct {
|
|||||||
} savepsp_t;
|
} savepsp_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the save system on PSP.
|
* Initializes the save system on PSP. Confirms the memory stick is
|
||||||
|
* actually reachable (sceIoGetstat on SAVE_PSP_ROOT) rather than assuming
|
||||||
|
* so, since raw sceIo calls otherwise only fail once something tries to
|
||||||
|
* touch the filesystem - and ensures the PSP/SAVEDATA directory tree
|
||||||
|
* exists (SAVE_PSP_BASE_DIR then SAVE_PSP_SAVEDATA_DIR, since sceIoMkdir
|
||||||
|
* only creates one level at a time).
|
||||||
*
|
*
|
||||||
* @return An error code if initialization fails.
|
* @return An error code if no memory stick is reachable.
|
||||||
*/
|
*/
|
||||||
errorret_t saveInitPSP(void);
|
errorret_t saveInitPSP(void);
|
||||||
|
|
||||||
@@ -61,3 +69,12 @@ errorret_t saveWritePSP(const uint8_t slot, const savefile_t *file);
|
|||||||
* @return An error code if the delete fails.
|
* @return An error code if the delete fails.
|
||||||
*/
|
*/
|
||||||
errorret_t saveDeletePSP(const uint8_t slot);
|
errorret_t saveDeletePSP(const uint8_t slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures SAVE_PSP_BASE_DIR and SAVE_PSP_SAVEDATA_DIR both exist, creating
|
||||||
|
* whichever are missing. sceIoMkdir only creates one directory level at a
|
||||||
|
* time, so this must run before creating any per-slot save directory
|
||||||
|
* beneath SAVE_PSP_SAVEDATA_DIR. Safe to call repeatedly - an
|
||||||
|
* already-exists result is not an error.
|
||||||
|
*/
|
||||||
|
void savePSPEnsureBaseDirs(void);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ errorret_t saveStreamOpenReadPSP(
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t saveStreamOpenWritePSP(savestreampsp_t *p, const uint8_t slot) {
|
errorret_t saveStreamOpenWritePSP(savestreampsp_t *p, const uint8_t slot) {
|
||||||
|
savePSPEnsureBaseDirs();
|
||||||
char_t dir[SAVE_PSP_PATH_MAX];
|
char_t dir[SAVE_PSP_PATH_MAX];
|
||||||
snprintf(dir, SAVE_PSP_PATH_MAX, SAVE_PSP_DIR_FORMAT,
|
snprintf(dir, SAVE_PSP_PATH_MAX, SAVE_PSP_DIR_FORMAT,
|
||||||
SAVE_PSP_TITLE_ID, (uint32_t)slot
|
SAVE_PSP_TITLE_ID, (uint32_t)slot
|
||||||
|
|||||||
Reference in New Issue
Block a user