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:
2026-08-16 20:14:16 -05:00
parent e3f10e0926
commit 674f86b18a
11 changed files with 774 additions and 14 deletions
+2
View File
@@ -8,4 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
savemanager.c
savedevice.c
saveslot.c
savesettings.c
)
+42
View File
@@ -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));
+55 -1
View File
@@ -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.
*/
-11
View File
@@ -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;
+377
View File
@@ -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); \
}
+55
View File
@@ -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];
+40 -2
View File
@@ -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();
+40
View File
@@ -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();
}
+49
View File
@@ -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);
+50
View File
@@ -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();
}
+64
View File
@@ -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);