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:
@@ -9,7 +9,7 @@
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
#include "util/string.h"
|
||||
#include "save/settings.h"
|
||||
#include "save/save.h"
|
||||
|
||||
inputbuttondata_t INPUT_BUTTON_DATA[] = {
|
||||
#ifdef DUSK_INPUT_GAMEPAD
|
||||
@@ -188,5 +188,5 @@ float_t inputButtonGetValueDolphin(const inputbutton_t button) {
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneDolphin(const inputbutton_t button) {
|
||||
return settingsGet()->deadzone;
|
||||
return saveGetMeta()->deadzone;
|
||||
}
|
||||
@@ -8,6 +8,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savedolphin.c
|
||||
savestreamdolphin.c
|
||||
settingsdolphin.c
|
||||
settingsstreamdolphin.c
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/savefile.h"
|
||||
#include "save/saveslot.h"
|
||||
#include "save/savemeta.h"
|
||||
#include <gccore.h>
|
||||
|
||||
#define SAVE_DOLPHIN_FILE_NAME_MAX 32
|
||||
#define SAVE_DOLPHIN_SECTOR_SIZE 8192
|
||||
|
||||
#ifndef SAVE_DOLPHIN_GAME_CODE
|
||||
@@ -21,6 +21,17 @@
|
||||
#define SAVE_DOLPHIN_CHANNEL CARD_SLOTA
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Fixed memory card file name holding meta + every save slot, back to
|
||||
* back, in one file - GameCube memory cards are small enough that one
|
||||
* consolidated file (rather than one per slot, plus a separate one for
|
||||
* meta) meaningfully saves card space, and nothing needs true random
|
||||
* access into just one section (see saveCombinedLoadDolphin()).
|
||||
*/
|
||||
#ifndef SAVE_DOLPHIN_FILE_NAME
|
||||
#define SAVE_DOLPHIN_FILE_NAME "DUSK_SAVE"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
card_file cardFile;
|
||||
uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32)));
|
||||
@@ -42,42 +53,60 @@ errorret_t saveInitDolphin(void);
|
||||
errorret_t saveDisposeDolphin(void);
|
||||
|
||||
/**
|
||||
* Loads a save file from the memory card for the given slot.
|
||||
* Reads the one consolidated card file (SAVE_DOLPHIN_FILE_NAME) into
|
||||
* SAVE.meta and every SAVE.slots[i], in order. Not finding the file is
|
||||
* not an error - SAVE.meta/SAVE.slots simply keep their compiled
|
||||
* defaults.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param file Output save file data.
|
||||
* @return An error code if the load fails.
|
||||
* @return An error code if the card is mounted but the read/parse fails.
|
||||
*/
|
||||
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file);
|
||||
errorret_t saveCombinedLoadDolphin(void);
|
||||
|
||||
/**
|
||||
* Writes a save file to the memory card for the given slot.
|
||||
* Writes SAVE.meta and every SAVE.slots[i], in order, into the one
|
||||
* consolidated card file (SAVE_DOLPHIN_FILE_NAME), creating it if needed.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param file Save file data to write.
|
||||
* @return An error code if the write fails.
|
||||
*/
|
||||
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file);
|
||||
errorret_t saveCombinedWriteDolphin(void);
|
||||
|
||||
/**
|
||||
* Deletes the save file for the given slot from the memory card.
|
||||
* Save-slot platform entry point - always (re)reads the whole
|
||||
* consolidated file (see saveCombinedLoadDolphin()); slot/out are unused
|
||||
* since every slot is populated in the same pass.
|
||||
*/
|
||||
errorret_t saveSlotLoadDolphin(const uint8_t slot, saveslot_t *out);
|
||||
|
||||
/**
|
||||
* Save-slot platform entry point - always (re)writes the whole
|
||||
* consolidated file (see saveCombinedWriteDolphin()); slot/slotData are
|
||||
* unused since every slot is written in the same pass.
|
||||
*/
|
||||
errorret_t saveSlotWriteDolphin(const uint8_t slot, saveslot_t *slotData);
|
||||
|
||||
/**
|
||||
* Deletes a single save slot's data (zeroes it in memory) and re-writes
|
||||
* the consolidated file - the file itself always exists as long as any
|
||||
* slot or meta does, so "delete" can't remove the file wholesale.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @return An error code if the delete fails.
|
||||
*/
|
||||
errorret_t saveDeleteDolphin(const uint8_t slot);
|
||||
errorret_t saveSlotDeleteDolphin(const uint8_t slot);
|
||||
|
||||
/**
|
||||
* Builds the memory card file name for a given save slot, from
|
||||
* SAVE_DOLPHIN_GAME_CODE and the slot index.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param out Destination buffer for the file name.
|
||||
* @param max Size of out, in bytes.
|
||||
* Meta platform entry point - always (re)reads the whole consolidated
|
||||
* file (see saveCombinedLoadDolphin()); out is unused since meta is
|
||||
* populated in the same pass.
|
||||
*/
|
||||
void saveGetFileNameDolphin(
|
||||
const uint8_t slot, char_t *out, const size_t max
|
||||
);
|
||||
errorret_t saveMetaLoadDolphin(savemeta_t *out);
|
||||
|
||||
/**
|
||||
* Meta platform entry point - always (re)writes the whole consolidated
|
||||
* file (see saveCombinedWriteDolphin()); meta is unused since it's
|
||||
* written in the same pass.
|
||||
*/
|
||||
errorret_t saveMetaWriteDolphin(savemeta_t *meta);
|
||||
|
||||
/**
|
||||
* Describes a libogc CARD_ERROR_* result code (see
|
||||
|
||||
@@ -14,12 +14,17 @@ typedef savestreamdolphin_t saveplatformstream_t;
|
||||
|
||||
#define saveInitPlatform saveInitDolphin
|
||||
#define saveDisposePlatform saveDisposeDolphin
|
||||
#define saveDeletePlatform saveDeleteDolphin
|
||||
|
||||
#define saveStreamOpenReadPlatform(stream, slot) \
|
||||
saveStreamOpenReadDolphin(&(stream)->platform, &(stream)->found, slot)
|
||||
#define saveStreamOpenWritePlatform(stream, slot) \
|
||||
saveStreamOpenWriteDolphin(&(stream)->platform, slot)
|
||||
#define saveSlotDeletePlatform saveSlotDeleteDolphin
|
||||
#define saveSlotLoadPlatform saveSlotLoadDolphin
|
||||
#define saveSlotWritePlatform saveSlotWriteDolphin
|
||||
#define saveMetaLoadPlatform saveMetaLoadDolphin
|
||||
#define saveMetaWritePlatform saveMetaWriteDolphin
|
||||
|
||||
#define saveStreamOpenReadPlatform(stream) \
|
||||
saveStreamOpenReadDolphin(&(stream)->platform, &(stream)->found)
|
||||
#define saveStreamOpenWritePlatform(stream) \
|
||||
saveStreamOpenWriteDolphin(&(stream)->platform)
|
||||
#define saveStreamClosePlatform(stream) \
|
||||
saveStreamCloseDolphin(&(stream)->platform)
|
||||
#define saveStreamReadBytesPlatform(stream, buf, len) \
|
||||
@@ -28,3 +33,5 @@ typedef savestreamdolphin_t saveplatformstream_t;
|
||||
saveStreamWriteBytesDolphin(&(stream)->platform, buf, len)
|
||||
#define saveStreamSeekPlatform(stream, pos) \
|
||||
saveStreamSeekDolphin(&(stream)->platform, pos)
|
||||
#define saveStreamTellPlatform(stream, out) \
|
||||
saveStreamTellDolphin(&(stream)->platform, out)
|
||||
|
||||
@@ -8,29 +8,17 @@
|
||||
#include "save/save.h"
|
||||
#include "save/savestreamdolphin.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
static void _saveStreamGetFileName(
|
||||
char_t *out, const size_t max, const uint8_t slot
|
||||
) {
|
||||
snprintf(out, max, "%s_%u", SAVE_DOLPHIN_GAME_CODE, (uint32_t)slot);
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenReadDolphin(
|
||||
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
||||
) {
|
||||
errorret_t saveStreamOpenReadDolphin(savestreamdolphin_t *p, bool_t *found) {
|
||||
if(!SAVE.platform.mounted) {
|
||||
*found = false;
|
||||
errorThrow("No memory card mounted");
|
||||
}
|
||||
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, slot);
|
||||
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Open(
|
||||
SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile
|
||||
SAVE_DOLPHIN_CHANNEL, SAVE_DOLPHIN_FILE_NAME, &p->cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
@@ -41,8 +29,8 @@ errorret_t saveStreamOpenReadDolphin(
|
||||
}
|
||||
if(result < 0) {
|
||||
*found = false;
|
||||
errorThrow("Failed to open memory card file for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
errorThrow("Failed to open memory card file: %s (%d)",
|
||||
saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,44 +40,40 @@ errorret_t saveStreamOpenReadDolphin(
|
||||
CARD_Close(&p->cardFile);
|
||||
if(result < 0) {
|
||||
*found = false;
|
||||
errorThrow("Failed to read memory card data for slot %u: %s (%d)",
|
||||
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||
errorThrow("Failed to read memory card data: %s (%d)",
|
||||
saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
*found = true;
|
||||
p->position = 0;
|
||||
p->writing = false;
|
||||
p->slot = slot;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenWriteDolphin(
|
||||
savestreamdolphin_t *p, const uint8_t slot
|
||||
) {
|
||||
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;
|
||||
p->slot = slot;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveStreamCloseDolphin(savestreamdolphin_t *p) {
|
||||
if(!p->writing) return;
|
||||
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, p->slot);
|
||||
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile);
|
||||
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, fileName, SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
|
||||
SAVE_DOLPHIN_CHANNEL, SAVE_DOLPHIN_FILE_NAME,
|
||||
SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
}
|
||||
@@ -129,3 +113,8 @@ errorret_t saveStreamSeekDolphin(savestreamdolphin_t *p, const size_t pos) {
|
||||
p->position = pos;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamTellDolphin(savestreamdolphin_t *p, size_t *out) {
|
||||
*out = p->position;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -20,34 +20,28 @@ typedef struct {
|
||||
size_t position;
|
||||
/** True when opened for writing; flushes buffer to card on close. */
|
||||
bool_t writing;
|
||||
/** Slot index stored at open time so Close can derive the filename. */
|
||||
uint8_t slot;
|
||||
} savestreamdolphin_t;
|
||||
|
||||
/**
|
||||
* Opens a memory card slot for reading by loading its sector into buffer.
|
||||
* Opens the consolidated memory card file for reading by loading its
|
||||
* sector into buffer.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param found Set to true if the file exists, false if it does not.
|
||||
* @param slot Save slot index.
|
||||
* @return An error if reading the card fails for a reason other than
|
||||
* missing file.
|
||||
*/
|
||||
errorret_t saveStreamOpenReadDolphin(
|
||||
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
||||
);
|
||||
errorret_t saveStreamOpenReadDolphin(savestreamdolphin_t *p, bool_t *found);
|
||||
|
||||
/**
|
||||
* Opens a memory card slot for writing by zeroing the sector buffer.
|
||||
* The buffer is flushed to the card when savestreamCloseDolphin is called.
|
||||
* Opens the consolidated memory card file for writing by zeroing the
|
||||
* sector buffer. The buffer is flushed to the card when
|
||||
* saveStreamCloseDolphin is called.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param slot Save slot index.
|
||||
* @param p Stream to initialize.
|
||||
* @return An error if initialization fails.
|
||||
*/
|
||||
errorret_t saveStreamOpenWriteDolphin(
|
||||
savestreamdolphin_t *p, const uint8_t slot
|
||||
);
|
||||
errorret_t saveStreamOpenWriteDolphin(savestreamdolphin_t *p);
|
||||
|
||||
/**
|
||||
* Flushes the sector buffer to the memory card (write mode only) and
|
||||
@@ -89,3 +83,12 @@ errorret_t saveStreamWriteBytesDolphin(
|
||||
* @return An error if pos is out of range.
|
||||
*/
|
||||
errorret_t saveStreamSeekDolphin(savestreamdolphin_t *p, const size_t pos);
|
||||
|
||||
/**
|
||||
* Gets the current read/write position within the sector buffer.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param out Receives the current position.
|
||||
* @return An error - always succeeds, matches saveStreamTellImpl's shape.
|
||||
*/
|
||||
errorret_t saveStreamTellDolphin(savestreamdolphin_t *p, size_t *out);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/settings.h"
|
||||
|
||||
errorret_t settingsInitDolphin(void) {
|
||||
if(!SAVE.platform.mounted) {
|
||||
errorThrow("No memory card mounted");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsDisposeDolphin(void) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/settingsfile.h"
|
||||
|
||||
#ifndef SETTINGS_DOLPHIN_FILE_NAME
|
||||
#define SETTINGS_DOLPHIN_FILE_NAME "DUSK_CFG"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/** No dedicated state - settings reuse the memory card mount that the
|
||||
* game-save system (see savedolphin.h) already establishes. */
|
||||
uint8_t reserved;
|
||||
} settingsdolphin_t;
|
||||
|
||||
/**
|
||||
* Initializes the settings system on GameCube. Does not mount the memory
|
||||
* card itself - reuses the mount already established by the game-save
|
||||
* system (see saveInitDolphin()), since there's only one memory card
|
||||
* subsystem to go around. settingsInit() must therefore run after
|
||||
* saveInit() on this platform.
|
||||
*
|
||||
* @return An error code if no memory card is currently mounted.
|
||||
*/
|
||||
errorret_t settingsInitDolphin(void);
|
||||
|
||||
/**
|
||||
* Disposes of the settings system on GameCube. Does not unmount the
|
||||
* memory card - that's owned by the game-save system.
|
||||
*
|
||||
* @return An error code if disposal fails.
|
||||
*/
|
||||
errorret_t settingsDisposeDolphin(void);
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "save/settingsdolphin.h"
|
||||
#include "save/settingsstreamdolphin.h"
|
||||
|
||||
typedef settingsdolphin_t settingsplatform_t;
|
||||
typedef settingsstreamdolphin_t settingsplatformstream_t;
|
||||
|
||||
#define settingsInitPlatform settingsInitDolphin
|
||||
#define settingsDisposePlatform settingsDisposeDolphin
|
||||
|
||||
#define settingsStreamOpenReadPlatform(stream) \
|
||||
settingsStreamOpenReadDolphin(&(stream)->platform, &(stream)->found)
|
||||
#define settingsStreamOpenWritePlatform(stream) \
|
||||
settingsStreamOpenWriteDolphin(&(stream)->platform)
|
||||
#define settingsStreamClosePlatform(stream) \
|
||||
settingsStreamCloseDolphin(&(stream)->platform)
|
||||
#define settingsStreamReadBytesPlatform(stream, buf, len) \
|
||||
settingsStreamReadBytesDolphin(&(stream)->platform, buf, len)
|
||||
#define settingsStreamWriteBytesPlatform(stream, buf, len) \
|
||||
settingsStreamWriteBytesDolphin(&(stream)->platform, buf, len)
|
||||
#define settingsStreamSeekPlatform(stream, pos) \
|
||||
settingsStreamSeekDolphin(&(stream)->platform, pos)
|
||||
@@ -1,120 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/settingsdolphin.h"
|
||||
#include "save/settingsstreamdolphin.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
errorret_t settingsStreamOpenReadDolphin(
|
||||
settingsstreamdolphin_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, SETTINGS_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 settings 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 settings data: %s (%d)",
|
||||
saveCardErrorStringDolphin(result), result
|
||||
);
|
||||
}
|
||||
|
||||
*found = true;
|
||||
p->position = 0;
|
||||
p->writing = false;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamOpenWriteDolphin(settingsstreamdolphin_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 settingsStreamCloseDolphin(settingsstreamdolphin_t *p) {
|
||||
if(!p->writing) return;
|
||||
|
||||
int32_t result;
|
||||
do {
|
||||
result = CARD_Open(
|
||||
SAVE_DOLPHIN_CHANNEL, SETTINGS_DOLPHIN_FILE_NAME, &p->cardFile
|
||||
);
|
||||
} while(result == CARD_ERROR_BUSY);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
do {
|
||||
result = CARD_Create(
|
||||
SAVE_DOLPHIN_CHANNEL, SETTINGS_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 settingsStreamReadBytesDolphin(
|
||||
settingsstreamdolphin_t *p, void *buf, const size_t len
|
||||
) {
|
||||
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Settings stream read exceeds sector size");
|
||||
}
|
||||
memoryCopy(buf, p->buffer + p->position, len);
|
||||
p->position += len;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamWriteBytesDolphin(
|
||||
settingsstreamdolphin_t *p, const void *buf, const size_t len
|
||||
) {
|
||||
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Settings stream write exceeds sector size");
|
||||
}
|
||||
memoryCopy(p->buffer + p->position, buf, len);
|
||||
p->position += len;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamSeekDolphin(
|
||||
settingsstreamdolphin_t *p, const size_t pos
|
||||
) {
|
||||
if(pos >= SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Settings stream seek out of range");
|
||||
}
|
||||
p->position = pos;
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/savedolphin.h"
|
||||
#include <gccore.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
/** libogc memory card file handle. */
|
||||
card_file cardFile;
|
||||
/** In-memory sector buffer; all reads and writes operate on this. */
|
||||
uint8_t buffer[SAVE_DOLPHIN_SECTOR_SIZE] __attribute__((aligned(32)));
|
||||
/** Current read/write position within buffer. */
|
||||
size_t position;
|
||||
/** True when opened for writing; flushes buffer to card on close. */
|
||||
bool_t writing;
|
||||
} settingsstreamdolphin_t;
|
||||
|
||||
/**
|
||||
* Opens the settings memory card file for reading by loading its sector
|
||||
* into buffer.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param found Set to true if the file exists, false if it does not.
|
||||
* @return An error if reading the card fails for a reason other than
|
||||
* missing file.
|
||||
*/
|
||||
errorret_t settingsStreamOpenReadDolphin(
|
||||
settingsstreamdolphin_t *p, bool_t *found
|
||||
);
|
||||
|
||||
/**
|
||||
* Opens the settings memory card file for writing by zeroing the sector
|
||||
* buffer. The buffer is flushed to the card when
|
||||
* settingsStreamCloseDolphin is called.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @return An error if initialization fails.
|
||||
*/
|
||||
errorret_t settingsStreamOpenWriteDolphin(settingsstreamdolphin_t *p);
|
||||
|
||||
/**
|
||||
* Flushes the sector buffer to the memory card (write mode only) and
|
||||
* releases the card file handle.
|
||||
*
|
||||
* @param p Stream to close.
|
||||
*/
|
||||
void settingsStreamCloseDolphin(settingsstreamdolphin_t *p);
|
||||
|
||||
/**
|
||||
* Copies len bytes from the sector buffer at the current position into buf.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Destination buffer.
|
||||
* @param len Number of bytes to read.
|
||||
* @return An error if the read would exceed the sector size.
|
||||
*/
|
||||
errorret_t settingsStreamReadBytesDolphin(
|
||||
settingsstreamdolphin_t *p, void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Copies len bytes from buf into the sector buffer at the current position.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Source buffer.
|
||||
* @param len Number of bytes to write.
|
||||
* @return An error if the write would exceed the sector size.
|
||||
*/
|
||||
errorret_t settingsStreamWriteBytesDolphin(
|
||||
settingsstreamdolphin_t *p, const void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the current read/write position within the sector buffer.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param pos Target byte offset from the start of the sector.
|
||||
* @return An error if pos is out of range.
|
||||
*/
|
||||
errorret_t settingsStreamSeekDolphin(
|
||||
settingsstreamdolphin_t *p, const size_t pos
|
||||
);
|
||||
Reference in New Issue
Block a user