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:
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 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 "yyjson.h"
|
||||
|
||||
/**
|
||||
* Small helper around a yyjson mutable document, used to build up a save
|
||||
* file's fields one at a time before writing it out. Schema-agnostic -
|
||||
* knows nothing about saveslot_t/savemeta_t; the caller supplies field
|
||||
* names and values.
|
||||
*/
|
||||
typedef struct {
|
||||
yyjson_mut_doc *doc;
|
||||
yyjson_mut_val *root;
|
||||
} savejsonwriterlinux_t;
|
||||
|
||||
/**
|
||||
* Creates a new mutable JSON document with an empty root object.
|
||||
*
|
||||
* @param writer Writer to initialize.
|
||||
* @return An error if the document can't be allocated.
|
||||
*/
|
||||
errorret_t saveJsonWriterInitLinux(savejsonwriterlinux_t *writer);
|
||||
|
||||
/**
|
||||
* Adds a "version": <uint> field to the root object.
|
||||
*/
|
||||
void saveJsonWriterAddUInt32Linux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const uint32_t value
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a float field to the root object (written as a JSON number).
|
||||
*/
|
||||
void saveJsonWriterAddFloatLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const float_t value
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a string field to the root object. The value is copied into the
|
||||
* document, so the caller's buffer doesn't need to outlive the call.
|
||||
*/
|
||||
void saveJsonWriterAddStringLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const char_t *value
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a JSON array of booleans as a field on the root object.
|
||||
*/
|
||||
void saveJsonWriterAddBoolArrayLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const bool_t *values,
|
||||
const size_t count
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a JSON array of unsigned 8-bit integers as a field on the root
|
||||
* object.
|
||||
*/
|
||||
void saveJsonWriterAddUInt8ArrayLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *key, const uint8_t *values,
|
||||
const size_t count
|
||||
);
|
||||
|
||||
/**
|
||||
* Pretty-prints the document to the given file path, creating or
|
||||
* truncating it.
|
||||
*
|
||||
* @param writer Writer holding the document to write.
|
||||
* @param path Destination file path.
|
||||
* @return An error if the write fails.
|
||||
*/
|
||||
errorret_t saveJsonWriterSaveLinux(
|
||||
savejsonwriterlinux_t *writer, const char_t *path
|
||||
);
|
||||
|
||||
/**
|
||||
* Frees the document. Safe to call even if saveJsonWriterInitLinux()
|
||||
* failed partway.
|
||||
*
|
||||
* @param writer Writer to dispose.
|
||||
*/
|
||||
void saveJsonWriterDisposeLinux(savejsonwriterlinux_t *writer);
|
||||
|
||||
/**
|
||||
* Reads and parses a JSON file, returning its root object.
|
||||
*
|
||||
* @param path File path to read.
|
||||
* @param outDoc Receives the parsed document (must be freed via
|
||||
* yyjson_doc_free() once done, regardless of found/error outcome).
|
||||
* @param outRoot Receives the root object, or NULL if not found.
|
||||
* @param found Set to true if the file exists, false if it does not
|
||||
* (not finding the file is not an error).
|
||||
* @return An error if the file exists but fails to parse.
|
||||
*/
|
||||
errorret_t saveJsonReaderOpenLinux(
|
||||
const char_t *path, yyjson_doc **outDoc, yyjson_val **outRoot,
|
||||
bool_t *found
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a uint32 field, falling back to defaultValue if the key is
|
||||
* missing or not a number - a hand-edited file shouldn't hard-fail the
|
||||
* whole load over one bad/missing field.
|
||||
*/
|
||||
uint32_t saveJsonReadUInt32Linux(
|
||||
yyjson_val *root, const char_t *key, const uint32_t defaultValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a float field, falling back to defaultValue if the key is
|
||||
* missing or not a number.
|
||||
*/
|
||||
float_t saveJsonReadFloatLinux(
|
||||
yyjson_val *root, const char_t *key, const float_t defaultValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a string field into out, falling back to defaultValue if the key
|
||||
* is missing or not a string. Always null-terminates.
|
||||
*/
|
||||
void saveJsonReadStringLinux(
|
||||
yyjson_val *root, const char_t *key, char_t *out, const size_t maxLen,
|
||||
const char_t *defaultValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a JSON array of booleans into out, up to count entries. Missing
|
||||
* key, non-array value, or a shorter array all leave the remaining/all
|
||||
* entries untouched (caller should zero the buffer first).
|
||||
*/
|
||||
void saveJsonReadBoolArrayLinux(
|
||||
yyjson_val *root, const char_t *key, bool_t *out, const size_t count
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a JSON array of unsigned 8-bit integers into out, up to count
|
||||
* entries. Same forgiving semantics as saveJsonReadBoolArrayLinux().
|
||||
*/
|
||||
void saveJsonReadUInt8ArrayLinux(
|
||||
yyjson_val *root, const char_t *key, uint8_t *out, const size_t count
|
||||
);
|
||||
Reference in New Issue
Block a user