Files
dusk/src/duskdolphin/save/savestreamdolphin.c
T
YourWishes aa0180571e 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>
2026-08-04 18:35:06 -05:00

121 lines
2.9 KiB
C

/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "save/save.h"
#include "save/savestreamdolphin.h"
#include "util/memory.h"
errorret_t saveStreamOpenReadDolphin(savestreamdolphin_t *p, bool_t *found) {
if(!SAVE.platform.mounted) {
*found = false;
errorThrow("No memory card mounted");
}
int32_t result;
do {
result = CARD_Open(
SAVE_DOLPHIN_CHANNEL, SAVE_DOLPHIN_FILE_NAME, &p->cardFile
);
} while(result == CARD_ERROR_BUSY);
if(result == CARD_ERROR_NOFILE) {
*found = false;
p->position = 0;
p->writing = false;
errorOk();
}
if(result < 0) {
*found = false;
errorThrow("Failed to open memory card file: %s (%d)",
saveCardErrorStringDolphin(result), result
);
}
do {
result = CARD_Read(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
} while(result == CARD_ERROR_BUSY);
CARD_Close(&p->cardFile);
if(result < 0) {
*found = false;
errorThrow("Failed to read memory card data: %s (%d)",
saveCardErrorStringDolphin(result), result
);
}
*found = true;
p->position = 0;
p->writing = false;
errorOk();
}
errorret_t saveStreamOpenWriteDolphin(savestreamdolphin_t *p) {
if(!SAVE.platform.mounted) errorThrow("No memory card mounted");
memoryZero(p->buffer, SAVE_DOLPHIN_SECTOR_SIZE);
p->position = 0;
p->writing = true;
errorOk();
}
void saveStreamCloseDolphin(savestreamdolphin_t *p) {
if(!p->writing) return;
int32_t result;
do {
result = CARD_Open(
SAVE_DOLPHIN_CHANNEL, SAVE_DOLPHIN_FILE_NAME, &p->cardFile
);
} while(result == CARD_ERROR_BUSY);
if(result == CARD_ERROR_NOFILE) {
do {
result = CARD_Create(
SAVE_DOLPHIN_CHANNEL, SAVE_DOLPHIN_FILE_NAME,
SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
);
} while(result == CARD_ERROR_BUSY);
}
do {
result = CARD_Write(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
} while(result == CARD_ERROR_BUSY);
CARD_Close(&p->cardFile);
}
errorret_t saveStreamReadBytesDolphin(
savestreamdolphin_t *p, void *buf, const size_t len
) {
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
errorThrow("Save stream read exceeds sector size");
}
memoryCopy(buf, p->buffer + p->position, len);
p->position += len;
errorOk();
}
errorret_t saveStreamWriteBytesDolphin(
savestreamdolphin_t *p, const void *buf, const size_t len
) {
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
errorThrow("Save stream write exceeds sector size");
}
memoryCopy(p->buffer + p->position, buf, len);
p->position += len;
errorOk();
}
errorret_t saveStreamSeekDolphin(savestreamdolphin_t *p, const size_t pos) {
if(pos >= SAVE_DOLPHIN_SECTOR_SIZE) {
errorThrow("Save stream seek out of range");
}
p->position = pos;
errorOk();
}
errorret_t saveStreamTellDolphin(savestreamdolphin_t *p, size_t *out) {
*out = p->position;
errorOk();
}