Add JSON (de)serialization for save slots/settings and device write/read hooks
Introduces saveslot/savesettings structs with a shared savejson.h macro toolkit (has/require/write/read per common type) for populating and parsing yyjson objects, wires saveManager save/load calls through new saveDevice slot/settings write/read entry points (still stubbed at the device level), and drops the old binary-format scaffolding in favor of JSON only.
This commit is contained in:
@@ -8,4 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savemanager.c
|
||||
savedevice.c
|
||||
saveslot.c
|
||||
savesettings.c
|
||||
)
|
||||
|
||||
@@ -79,6 +79,48 @@ void saveDeviceCheckAvailability(
|
||||
saveDeviceCheckAvailabilityPlatform(device);
|
||||
}
|
||||
|
||||
errorret_t saveDeviceSlotWrite(
|
||||
savedevice_t *device,
|
||||
saveslot_t *slot,
|
||||
uint8_t slotIndex
|
||||
) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertNotNull(slot, "slot cannot be null");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceSlotRead(
|
||||
savedevice_t *device,
|
||||
saveslot_t *slot,
|
||||
uint8_t slotIndex
|
||||
) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertNotNull(slot, "slot cannot be null");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceSettingsWrite(
|
||||
savedevice_t *device,
|
||||
savesettings_t *settings
|
||||
) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertNotNull(settings, "settings cannot be null");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceSettingsRead(
|
||||
savedevice_t *device,
|
||||
savesettings_t *settings
|
||||
) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertNotNull(settings, "settings cannot be null");
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceDispose(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
errorChain(saveDevicePlatformDispose(device));
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#endif
|
||||
|
||||
typedef struct savedevice_s savedevice_t;
|
||||
typedef struct savesettings_s savesettings_t;
|
||||
typedef struct saveslot_s saveslot_t;
|
||||
|
||||
typedef void (*savedevicestatecallback_t)(savedevice_t *device, void *user);
|
||||
|
||||
@@ -72,9 +74,61 @@ void saveDeviceCheckAvailability(
|
||||
void *user
|
||||
);
|
||||
|
||||
/**
|
||||
* Writes the given save slot to the device at the given slot index.
|
||||
*
|
||||
* @param device The save device to write to.
|
||||
* @param slot The save slot to write.
|
||||
* @param slotIndex The slot index to write to.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceSlotWrite(
|
||||
savedevice_t *device,
|
||||
saveslot_t *slot,
|
||||
uint8_t slotIndex
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads a save slot from the device at the given slot index.
|
||||
*
|
||||
* @param device The save device to read from.
|
||||
* @param slot The save slot to read into.
|
||||
* @param slotIndex The slot index to read from.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceSlotRead(
|
||||
savedevice_t *device,
|
||||
saveslot_t *slot,
|
||||
uint8_t slotIndex
|
||||
);
|
||||
|
||||
/**
|
||||
* Writes the given save settings to the device.
|
||||
*
|
||||
* @param device The save device to write to.
|
||||
* @param settings The save settings to write.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceSettingsWrite(
|
||||
savedevice_t *device,
|
||||
savesettings_t *settings
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads the save settings from the device.
|
||||
*
|
||||
* @param device The save device to read from.
|
||||
* @param settings The save settings to read into.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceSettingsRead(
|
||||
savedevice_t *device,
|
||||
savesettings_t *settings
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of the save device.
|
||||
*
|
||||
*
|
||||
* @param device The save device to dispose.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
typedef uint8_t saveslot_t;
|
||||
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "yyjson.h"
|
||||
#include "error/error.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/string.h"
|
||||
#include "time/timeepoch.h"
|
||||
|
||||
#define SAVE_JSON_STRING_BUFFER_SIZE 256
|
||||
|
||||
/**
|
||||
* Creates a new mutable JSON document with an empty root object, ready for
|
||||
* the writeX macros below to populate. Declares `doc` and `object` in the
|
||||
* calling scope.
|
||||
*/
|
||||
#define writeInit() \
|
||||
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); \
|
||||
assertNotNull(doc, "Failed to create JSON document"); \
|
||||
yyjson_mut_val *object = yyjson_mut_obj(doc); \
|
||||
yyjson_mut_doc_set_root(doc, object)
|
||||
|
||||
/**
|
||||
* Parses the given JSON text and exposes its root object for the readX
|
||||
* macros below to consume. Declares `jsonDoc` (keep it around to dispose
|
||||
* with yyjson_doc_free once reading is done) and `object` in the calling
|
||||
* scope. Errors if the text fails to parse or has no object root.
|
||||
*
|
||||
* @param jsonText The JSON text to parse.
|
||||
* @param jsonLength The length of the JSON text, in bytes.
|
||||
*/
|
||||
#define readInit(jsonText, jsonLength) \
|
||||
yyjson_doc *jsonDoc = yyjson_read((jsonText), (jsonLength), 0); \
|
||||
if(jsonDoc == NULL) errorThrow("Failed to parse save JSON"); \
|
||||
yyjson_val *object = yyjson_doc_get_root(jsonDoc); \
|
||||
if(object == NULL) errorThrow("Save JSON missing root object")
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasInt32(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireInt32(key) \
|
||||
if(!hasInt32(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes an int32_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeInt32(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_int(doc, object, key, (int64_t)(value))
|
||||
|
||||
/**
|
||||
* Reads an int32_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readInt32(key, dest, def) \
|
||||
(dest) = hasInt32(key) ? \
|
||||
(int32_t)yyjson_get_int(yyjson_obj_get(object, key)) : (int32_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasUInt32(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireUInt32(key) \
|
||||
if(!hasUInt32(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a uint32_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeUInt32(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_uint(doc, object, key, (uint64_t)(value))
|
||||
|
||||
/**
|
||||
* Reads a uint32_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readUInt32(key, dest, def) \
|
||||
(dest) = hasUInt32(key) ? \
|
||||
(uint32_t)yyjson_get_uint(yyjson_obj_get(object, key)) : (uint32_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasUInt8(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireUInt8(key) \
|
||||
if(!hasUInt8(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a uint8_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeUInt8(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_uint(doc, object, key, (uint64_t)(value))
|
||||
|
||||
/**
|
||||
* Reads a uint8_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readUInt8(key, dest, def) \
|
||||
(dest) = hasUInt8(key) ? \
|
||||
(uint8_t)yyjson_get_uint(yyjson_obj_get(object, key)) : (uint8_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasInt64(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireInt64(key) \
|
||||
if(!hasInt64(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes an int64_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeInt64(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_sint(doc, object, key, (int64_t)(value))
|
||||
|
||||
/**
|
||||
* Reads an int64_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readInt64(key, dest, def) \
|
||||
(dest) = hasInt64(key) ? \
|
||||
yyjson_get_sint(yyjson_obj_get(object, key)) : (int64_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasUInt64(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireUInt64(key) \
|
||||
if(!hasUInt64(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a uint64_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeUInt64(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_uint(doc, object, key, (uint64_t)(value))
|
||||
|
||||
/**
|
||||
* Reads a uint64_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readUInt64(key, dest, def) \
|
||||
(dest) = hasUInt64(key) ? \
|
||||
yyjson_get_uint(yyjson_obj_get(object, key)) : (uint64_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasFloat(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireFloat(key) \
|
||||
if(!hasFloat(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a float_t to the current JSON object, omitting the key entirely
|
||||
* if it matches the given default.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeFloat(key, value, def) \
|
||||
if((value) != (def)) yyjson_mut_obj_add_real(doc, object, key, (double)(value))
|
||||
|
||||
/**
|
||||
* Reads a float_t from the current JSON object, falling back to the given
|
||||
* default if the key is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination variable to assign to.
|
||||
* @param def The default value to use if the key is missing.
|
||||
*/
|
||||
#define readFloat(key, dest, def) \
|
||||
(dest) = hasFloat(key) ? \
|
||||
(float_t)yyjson_get_real(yyjson_obj_get(object, key)) : (float_t)(def)
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasString(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireString(key) \
|
||||
if(!hasString(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a string to the current JSON object, omitting the key entirely if
|
||||
* it matches the given default. The string is copied, so it does not need
|
||||
* to outlive the JSON document.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The value to write.
|
||||
* @param def The default value; if value equals this, the key is omitted.
|
||||
*/
|
||||
#define writeString(key, value, def) \
|
||||
if(!stringEquals((value), (def))) yyjson_mut_obj_add_strcpy(doc, object, key, value)
|
||||
|
||||
/**
|
||||
* Reads a string from the current JSON object into dest, falling back to
|
||||
* the given default if the key is missing. Errors if the JSON string is
|
||||
* longer than maxLength. Uses the caller's `saveJsonStringBuffer` local as
|
||||
* scratch space.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination buffer to copy into.
|
||||
* @param def The default value to use if the key is missing.
|
||||
* @param maxLength The maximum length of dest, excluding the null terminator.
|
||||
*/
|
||||
#define readString(key, dest, def, maxLength) { \
|
||||
yyjson_val *saveJsonStrVal = yyjson_obj_get(object, key); \
|
||||
if(saveJsonStrVal != NULL) { \
|
||||
if(yyjson_get_len(saveJsonStrVal) > (size_t)(maxLength)) { \
|
||||
errorThrow( \
|
||||
"Save JSON string '%s' exceeds max length of %d", \
|
||||
key, (int)(maxLength) \
|
||||
); \
|
||||
} \
|
||||
stringCopy(saveJsonStringBuffer, yyjson_get_str(saveJsonStrVal), (maxLength)); \
|
||||
} else { \
|
||||
stringCopy(saveJsonStringBuffer, (def), (maxLength)); \
|
||||
} \
|
||||
stringCopy((dest), saveJsonStringBuffer, (maxLength)); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given key exists on the current JSON object.
|
||||
*
|
||||
* @param key The key to check for.
|
||||
*/
|
||||
#define hasTime(key) (yyjson_obj_get(object, key) != NULL)
|
||||
|
||||
/**
|
||||
* Errors if the given key does not exist on the current JSON object.
|
||||
*
|
||||
* @param key The key that must exist.
|
||||
*/
|
||||
#define requireTime(key) \
|
||||
if(!hasTime(key)) errorThrow("Save JSON missing '%s' key", key)
|
||||
|
||||
/**
|
||||
* Writes a dusktimeepoch_t to the current JSON object as a nested object of
|
||||
* its time/timeZone/offsetTime fields.
|
||||
*
|
||||
* @param key The key to write to.
|
||||
* @param value The dusktimeepoch_t value to write.
|
||||
*/
|
||||
#define writeTime(key, value) { \
|
||||
yyjson_mut_val *saveJsonTimeObj = yyjson_mut_obj_add_obj(doc, object, key); \
|
||||
yyjson_mut_obj_add_real(doc, saveJsonTimeObj, "time", (value).time); \
|
||||
yyjson_mut_obj_add_real(doc, saveJsonTimeObj, "timeZone", (value).timeZone); \
|
||||
yyjson_mut_obj_add_real(doc, saveJsonTimeObj, "offsetTime", (value).offsetTime); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a dusktimeepoch_t from the current JSON object's nested time/
|
||||
* timeZone/offsetTime fields. Errors if the key or any sub-field is missing.
|
||||
*
|
||||
* @param key The key to read from.
|
||||
* @param dest The destination dusktimeepoch_t to assign to.
|
||||
*/
|
||||
#define readTime(key, dest) { \
|
||||
yyjson_val *saveJsonTimeObj = yyjson_obj_get(object, key); \
|
||||
if(saveJsonTimeObj == NULL) errorThrow("Save JSON missing '%s' key", key); \
|
||||
yyjson_val *saveJsonTimeTime = yyjson_obj_get(saveJsonTimeObj, "time"); \
|
||||
yyjson_val *saveJsonTimeZone = yyjson_obj_get(saveJsonTimeObj, "timeZone"); \
|
||||
yyjson_val *saveJsonTimeOffset = yyjson_obj_get(saveJsonTimeObj, "offsetTime"); \
|
||||
if(saveJsonTimeTime == NULL) errorThrow("Save JSON missing '%s.time' key", key); \
|
||||
if(saveJsonTimeZone == NULL) { \
|
||||
errorThrow("Save JSON missing '%s.timeZone' key", key); \
|
||||
} \
|
||||
if(saveJsonTimeOffset == NULL) { \
|
||||
errorThrow("Save JSON missing '%s.offsetTime' key", key); \
|
||||
} \
|
||||
(dest).time = yyjson_get_real(saveJsonTimeTime); \
|
||||
(dest).timeZone = yyjson_get_real(saveJsonTimeZone); \
|
||||
(dest).offsetTime = yyjson_get_real(saveJsonTimeOffset); \
|
||||
}
|
||||
@@ -15,6 +15,13 @@ errorret_t saveManagerInit() {
|
||||
memoryZero(&SAVE_MANAGER, sizeof(savemanager_t));
|
||||
|
||||
SAVE_MANAGER.deviceCurrent = 0xFF;// No current device.
|
||||
SAVE_MANAGER.slotCurrent = 0xFF;// No current slot.
|
||||
|
||||
// Initialize the default slot
|
||||
saveSlotInit(&SAVE_MANAGER.slot);
|
||||
|
||||
// Initialize the default settings.
|
||||
saveSettingsInit(&SAVE_MANAGER.settings);
|
||||
|
||||
// Start by initializing each of the save devices.
|
||||
savedevice_t *device = &SAVE_MANAGER.devices[0];
|
||||
@@ -141,6 +148,54 @@ void saveManagerOnDeviceAvailabilityChecked(savedevice_t *device, void *user) {
|
||||
SAVE_MANAGER.deviceCurrent = (uint8_t)(device - &SAVE_MANAGER.devices[0]);
|
||||
}
|
||||
|
||||
errorret_t saveManagerSaveSettings() {
|
||||
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
|
||||
|
||||
errorChain(saveDeviceSettingsWrite(
|
||||
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
|
||||
&SAVE_MANAGER.settings
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveManagerLoadSettings() {
|
||||
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
|
||||
|
||||
errorChain(saveDeviceSettingsRead(
|
||||
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
|
||||
&SAVE_MANAGER.settings
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveManagerSaveSlot() {
|
||||
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
|
||||
assertTrue(SAVE_MANAGER.slotCurrent != 0xFF, "No current slot");
|
||||
|
||||
errorChain(saveDeviceSlotWrite(
|
||||
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
|
||||
&SAVE_MANAGER.slot,
|
||||
SAVE_MANAGER.slotCurrent
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveManagerLoadSlot() {
|
||||
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
|
||||
assertTrue(SAVE_MANAGER.slotCurrent != 0xFF, "No current slot");
|
||||
|
||||
errorChain(saveDeviceSlotRead(
|
||||
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
|
||||
&SAVE_MANAGER.slot,
|
||||
SAVE_MANAGER.slotCurrent
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveManagerDispose() {
|
||||
// Dispose each device.
|
||||
savedevice_t *device = &SAVE_MANAGER.devices[0];
|
||||
|
||||
@@ -7,11 +7,21 @@
|
||||
|
||||
#pragma once
|
||||
#include "savedevice.h"
|
||||
#include "saveslot.h"
|
||||
#include "savesettings.h"
|
||||
|
||||
typedef struct {
|
||||
// File state
|
||||
savesettings_t settings;
|
||||
saveslot_t slot;
|
||||
|
||||
uint8_t slotCurrent;
|
||||
bool_t settingsDirty;
|
||||
bool_t slotDirty;
|
||||
|
||||
// Device state
|
||||
savedevice_t devices[SAVE_DEVICE_COUNT];
|
||||
uint8_t deviceCurrent;
|
||||
|
||||
bool_t findingAvailableDevice;
|
||||
bool_t noAvailableDeviceFound;
|
||||
savedevicestatecallback_t findAvailableCallback;
|
||||
@@ -56,9 +66,37 @@ void saveManagerFindAvailableDevice(
|
||||
*/
|
||||
void saveManagerOnDeviceAvailabilityChecked(savedevice_t *device, void *user);
|
||||
|
||||
/**
|
||||
* Saves the current settings to the current device.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerSaveSettings();
|
||||
|
||||
/**
|
||||
* Loads the current settings from the current device.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerLoadSettings();
|
||||
|
||||
/**
|
||||
* Saves the current slot to the current device, at the current slot index.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerSaveSlot();
|
||||
|
||||
/**
|
||||
* Loads the current slot from the current device, at the current slot index.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerLoadSlot();
|
||||
|
||||
/**
|
||||
* Disposes of the save manager.
|
||||
*
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerDispose();
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "savesettings.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "savejson.h"
|
||||
|
||||
void saveSettingsInit(savesettings_t *settings) {
|
||||
assertNotNull(settings, "Settings cannot be null");
|
||||
|
||||
memorySet(settings, 0, sizeof(savesettings_t));
|
||||
}
|
||||
|
||||
errorret_t saveSettingsWriteJSON(
|
||||
savesettings_t *settings,
|
||||
yyjson_mut_doc *doc,
|
||||
yyjson_mut_val *object
|
||||
) {
|
||||
assertNotNull(settings, "Settings cannot be null");
|
||||
assertNotNull(doc, "Doc cannot be null");
|
||||
assertNotNull(object, "Object cannot be null");
|
||||
|
||||
writeInt32("someSetting", settings->someSetting, 0);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveSettingsReadJSON(savesettings_t *settings, yyjson_val *object) {
|
||||
assertNotNull(settings, "Settings cannot be null");
|
||||
assertNotNull(object, "Object cannot be null");
|
||||
|
||||
readInt32("someSetting", settings->someSetting, 0);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "time/timeepoch.h"
|
||||
#include "savedevice.h"
|
||||
#include "yyjson.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct savesettings_s {
|
||||
int32_t someSetting;
|
||||
} savesettings_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* Inits the save settings with the default state, this is functionally "new
|
||||
* game" but will not set the player name, as that is what we use to determine
|
||||
* if a save slot is "in use" or not.
|
||||
*
|
||||
* @param settings The save settings to init.
|
||||
*/
|
||||
void saveSettingsInit(savesettings_t *settings);
|
||||
|
||||
/**
|
||||
* Writes the given save settings out to the given JSON object.
|
||||
*
|
||||
* @param settings The save settings to write.
|
||||
* @param doc The mutable JSON document that owns the object.
|
||||
* @param object The mutable JSON object to write into.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveSettingsWriteJSON(
|
||||
savesettings_t *settings,
|
||||
yyjson_mut_doc *doc,
|
||||
yyjson_mut_val *object
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads the given save settings in from the given JSON object.
|
||||
*
|
||||
* @param settings The save settings to read into.
|
||||
* @param object The JSON object to read from.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveSettingsReadJSON(savesettings_t *settings, yyjson_val *object);
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "saveslot.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "savejson.h"
|
||||
|
||||
void saveSlotInit(saveslot_t *slot) {
|
||||
assertNotNull(slot, "Slot cannot be null");
|
||||
|
||||
memorySet(slot, 0, sizeof(saveslot_t));
|
||||
slot->version = 1;
|
||||
}
|
||||
|
||||
bool_t saveSlotInUse(saveslot_t *slot) {
|
||||
assertNotNull(slot, "Slot cannot be null");
|
||||
return slot->name[0] != '\0';
|
||||
}
|
||||
|
||||
errorret_t saveSlotWriteJSON(
|
||||
saveslot_t *slot,
|
||||
yyjson_mut_doc *doc,
|
||||
yyjson_mut_val *object
|
||||
) {
|
||||
assertNotNull(slot, "Slot cannot be null");
|
||||
assertNotNull(doc, "Doc cannot be null");
|
||||
assertNotNull(object, "Object cannot be null");
|
||||
|
||||
writeString("name", slot->name, "");
|
||||
writeTime("time", slot->time);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveSlotReadJSON(saveslot_t *slot, yyjson_val *object) {
|
||||
assertNotNull(slot, "Slot cannot be null");
|
||||
assertNotNull(object, "Object cannot be null");
|
||||
|
||||
char_t saveJsonStringBuffer[SAVE_JSON_STRING_BUFFER_SIZE];
|
||||
|
||||
readString("name", slot->name, "", SAVE_SLOT_NAME_LENGTH);
|
||||
readTime("time", slot->time);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "time/timeepoch.h"
|
||||
#include "savedevice.h"
|
||||
#include "yyjson.h"
|
||||
|
||||
#define SAVE_SLOT_NAME_LENGTH 8
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct saveslot_s {
|
||||
uint8_t version;
|
||||
uint8_t dataType;
|
||||
|
||||
char_t name[SAVE_SLOT_NAME_LENGTH + 1];// 8 characters + null terminator
|
||||
dusktimeepoch_t time;
|
||||
} saveslot_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* Inits the save slot with the default state, this is functionally "new game"
|
||||
* but will not set the player name, as that is what we use to determine if a
|
||||
* save slot is "in use" or not.
|
||||
*
|
||||
* @param slot The save slot to init.
|
||||
*/
|
||||
void saveSlotInit(saveslot_t *slot);
|
||||
|
||||
/**
|
||||
* Checks if the save slot is in use, this is determined by checking if the
|
||||
* player name is set or not.
|
||||
*
|
||||
* @param slot The save slot to check.
|
||||
* @return True if the save slot is in use, false otherwise.
|
||||
*/
|
||||
bool_t saveSlotInUse(saveslot_t *slot);
|
||||
|
||||
/**
|
||||
* Writes the given save slot out to the given JSON object.
|
||||
*
|
||||
* @param slot The save slot to write.
|
||||
* @param doc The mutable JSON document that owns the object.
|
||||
* @param object The mutable JSON object to write into.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveSlotWriteJSON(
|
||||
saveslot_t *slot,
|
||||
yyjson_mut_doc *doc,
|
||||
yyjson_mut_val *object
|
||||
);
|
||||
|
||||
/**
|
||||
* Reads the given save slot in from the given JSON object.
|
||||
*
|
||||
* @param slot The save slot to read into.
|
||||
* @param object The JSON object to read from.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveSlotReadJSON(saveslot_t *slot, yyjson_val *object);
|
||||
Reference in New Issue
Block a user