Unify save system into one save.h/.c; diverge storage format per platform
Renames savefile_t to saveslot_t and folds last session's standalone settings.h/.c module back in as savemeta_t, so there's one save system (SAVE.slots[] + SAVE.meta) instead of two parallel ones - while letting each platform pick its own physical format for the two concepts: - Linux now writes human-editable JSON (slot0.json, settings.json, ...) via yyjson's mutable writer API, so players can hand-fix a bad setting. - PSP folds meta into the same sceUtilitySavedata binary payload as its one save slot (SAVE_SLOT_COUNT_MAX=1 there - a future save picker will let players manage multiple named saves via the OS's own browser). - GameCube consolidates the 3 per-slot memory card files and the separate settings file into one combined card file. Also fixes two bugs surfaced while building this: the CRC finalize step seeked to a hardcoded offset (only safe for one section per file, breaks once meta+slots share a buffer), and save.c's async/sync dispatch left an unconditional fallback call that doesn't exist on PSP-only platforms. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savestream.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
@@ -65,129 +66,76 @@ errorret_t saveDisposeDolphin(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
errorret_t saveCombinedLoadDolphin(void) {
|
||||
savestream_t stream;
|
||||
memoryZero(&stream, sizeof(savestream_t));
|
||||
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Open(
|
||||
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
file->exists = false;
|
||||
errorOk();
|
||||
}
|
||||
if(result < 0) {
|
||||
file->exists = false;
|
||||
errorThrow("Failed to open memory card file for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
errorret_t openRet = saveStreamOpenReadPlatform(&stream);
|
||||
SAVE.available = errorIsOk(openRet);
|
||||
errorChain(openRet);
|
||||
|
||||
if(!stream.found) errorOk();
|
||||
|
||||
errorret_t ret = saveMetaSerializeRead(&stream, &SAVE.meta);
|
||||
for(uint8_t i = 0; errorIsOk(ret) && i < SAVE_SLOT_COUNT_MAX; i++) {
|
||||
ret = saveSlotSerializeRead(&stream, &SAVE.slots[i]);
|
||||
}
|
||||
|
||||
void *buffer = memoryAlign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
if(!buffer) {
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
errorThrow("Failed to allocate memory card read buffer");
|
||||
}
|
||||
#ifdef saveStreamClosePlatform
|
||||
saveStreamClosePlatform(&stream);
|
||||
#endif
|
||||
|
||||
do {
|
||||
result = CARD_Read(
|
||||
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
|
||||
if(result < 0) {
|
||||
memoryFree(buffer);
|
||||
file->exists = false;
|
||||
errorThrow("Failed to read memory card data for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
memoryCopy(file, buffer, sizeof(savefile_t));
|
||||
memoryFree(buffer);
|
||||
|
||||
file->exists = true;
|
||||
errorChain(ret);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
errorret_t saveCombinedWriteDolphin(void) {
|
||||
savestream_t stream;
|
||||
memoryZero(&stream, sizeof(savestream_t));
|
||||
|
||||
void *buffer = memoryAlign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
if(!buffer) {
|
||||
errorThrow("Failed to allocate memory card write buffer");
|
||||
}
|
||||
memoryZero(buffer, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
memoryCopy(buffer, file, sizeof(savefile_t));
|
||||
errorret_t openRet = saveStreamOpenWritePlatform(&stream);
|
||||
SAVE.available = errorIsOk(openRet);
|
||||
errorChain(openRet);
|
||||
|
||||
// Try open existing file first; create if absent.
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Open(
|
||||
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
do {
|
||||
result = CARD_Create(
|
||||
SAVE_DOLPHIN_CHANNEL,
|
||||
fileName,
|
||||
SAVE_DOLPHIN_SECTOR_SIZE,
|
||||
&SAVE.platform.cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
errorret_t ret = saveMetaSerializeWrite(&stream, &SAVE.meta);
|
||||
for(uint8_t i = 0; errorIsOk(ret) && i < SAVE_SLOT_COUNT_MAX; i++) {
|
||||
ret = saveSlotSerializeWrite(&stream, &SAVE.slots[i]);
|
||||
}
|
||||
|
||||
if(result < 0) {
|
||||
memoryFree(buffer);
|
||||
errorThrow("Failed to open/create memory card file for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
do {
|
||||
result = CARD_Write(
|
||||
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
memoryFree(buffer);
|
||||
|
||||
if(result < 0) {
|
||||
errorThrow("Failed to write memory card data for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
#ifdef saveStreamClosePlatform
|
||||
saveStreamClosePlatform(&stream);
|
||||
#endif
|
||||
|
||||
errorChain(ret);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteDolphin(const uint8_t slot) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Delete(SAVE_DOLPHIN_CHANNEL, fileName);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
if(result < 0 && result != CARD_ERROR_NOFILE) {
|
||||
errorThrow("Failed to delete memory card file for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
errorret_t saveSlotLoadDolphin(const uint8_t slot, saveslot_t *out) {
|
||||
(void)slot;
|
||||
(void)out;
|
||||
return saveCombinedLoadDolphin();
|
||||
}
|
||||
|
||||
void saveGetFileNameDolphin(
|
||||
const uint8_t slot, char_t *out, const size_t max
|
||||
) {
|
||||
snprintf(out, max, "%s_%u", SAVE_DOLPHIN_GAME_CODE, (uint32_t)slot);
|
||||
errorret_t saveSlotWriteDolphin(const uint8_t slot, saveslot_t *slotData) {
|
||||
(void)slot;
|
||||
(void)slotData;
|
||||
return saveCombinedWriteDolphin();
|
||||
}
|
||||
|
||||
errorret_t saveSlotDeleteDolphin(const uint8_t slot) {
|
||||
memoryZero(&SAVE.slots[slot], sizeof(saveslot_t));
|
||||
SAVE.slots[slot].exists = false;
|
||||
return saveCombinedWriteDolphin();
|
||||
}
|
||||
|
||||
errorret_t saveMetaLoadDolphin(savemeta_t *out) {
|
||||
(void)out;
|
||||
return saveCombinedLoadDolphin();
|
||||
}
|
||||
|
||||
errorret_t saveMetaWriteDolphin(savemeta_t *meta) {
|
||||
(void)meta;
|
||||
return saveCombinedWriteDolphin();
|
||||
}
|
||||
|
||||
const char_t *saveCardErrorStringDolphin(const int32_t result) {
|
||||
|
||||
Reference in New Issue
Block a user