Add versioned save slot/settings schemas with strict version checks

Splits saveslot_t/savesettings_t into a stable, version-independent
in-memory struct (save/slot/, save/settings/) and a separate frozen
per-schema-version struct (saveslotver1_t/savesettingsver1_t) used only
for JSON serialization, converted between manually. Every write now
stamps a "version" field (SAVE_SLOT_CURRENT/SAVE_SETTINGS_CURRENT), and
every read requires it to match exactly, rejecting anything else -
laying the groundwork for a real migration path instead of silently
misreading old/incompatible save data.
This commit is contained in:
2026-09-08 12:49:06 -05:00
parent 327c2bec70
commit 8139a0ea54
28 changed files with 634 additions and 115 deletions
+37 -14
View File
@@ -6,7 +6,7 @@
*/
#include "dusktest.h"
#include "save/savesettings.h"
#include "save/settings/savesettings.h"
#include "save/savejson.h"
#include "util/memory.h"
#include <stdlib.h>
@@ -51,14 +51,14 @@ static errorret_t settingsFromJSON(
// saveSettingsInit
// ============================================================
static void test_saveSettingsInit_zeroesSomeSetting(void **state) {
static void test_saveSettingsInit_resetsAudioMasterVolume(void **state) {
savesettings_t settings;
memoryZero(&settings, sizeof(settings));
settings.someSetting = 42;
settings.audioMasterVolume = 42.0f;
saveSettingsInit(&settings);
assert_int_equal(settings.someSetting, 0);
assert_true(settings.audioMasterVolume == 1.0f);
}
static void test_saveSettingsInit_nullAsserts(void **state) {
@@ -85,7 +85,6 @@ static void test_saveSettingsReadJSON_nullAsserts(void **state) {
savesettings_t settings;
saveSettingsInit(&settings);
writeInit();
yyjson_mut_obj_add_int(doc, object, "someSetting", 1);
size_t len;
char_t *json = yyjson_mut_write(doc, 0, &len);
@@ -105,7 +104,7 @@ static void test_saveSettingsReadJSON_nullAsserts(void **state) {
static void test_saveSettingsWriteReadJSON_roundTrip(void **state) {
savesettings_t written;
saveSettingsInit(&written);
written.someSetting = 12345;
written.audioMasterVolume = 2.5f;
char_t *json;
size_t len;
@@ -114,35 +113,59 @@ static void test_saveSettingsWriteReadJSON_roundTrip(void **state) {
savesettings_t read;
saveSettingsInit(&read);
read.someSetting = -1;// sentinel, should be overwritten
read.audioMasterVolume = -1.0f;// sentinel, should be overwritten
errorret_t readRet = settingsFromJSON(&read, json);
assert_true(errorIsOk(readRet));
assert_int_equal(read.someSetting, 12345);
assert_true(read.audioMasterVolume == 2.5f);
free(json);
}
static void test_saveSettingsReadJSON_missingKeyDefaultsToZero(void **state) {
static void test_saveSettingsReadJSON_missingKeyUsesDefault(void **state) {
savesettings_t settings;
saveSettingsInit(&settings);
settings.audioMasterVolume = 999.0f;// sentinel
errorret_t ret = settingsFromJSON(&settings, "{\"version\":1}");
assert_true(errorIsOk(ret));
assert_true(settings.audioMasterVolume == 1.0f);
}
// ============================================================
// saveSettingsReadJSON - version enforcement
// ============================================================
static void test_saveSettingsReadJSON_versionMissing_errors(void **state) {
savesettings_t settings;
saveSettingsInit(&settings);
settings.someSetting = 999;// sentinel
errorret_t ret = settingsFromJSON(&settings, "{}");
assert_true(errorIsOk(ret));
assert_int_equal(settings.someSetting, 0);
assert_true(errorIsNotOk(ret));
errorCatch(ret);
}
static void test_saveSettingsReadJSON_versionMismatch_errors(void **state) {
savesettings_t settings;
saveSettingsInit(&settings);
errorret_t ret = settingsFromJSON(&settings, "{\"version\":999}");
assert_true(errorIsNotOk(ret));
errorCatch(ret);
}
int main(void) {
assertInit();
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_saveSettingsInit_zeroesSomeSetting),
cmocka_unit_test(test_saveSettingsInit_resetsAudioMasterVolume),
cmocka_unit_test(test_saveSettingsInit_nullAsserts),
cmocka_unit_test(test_saveSettingsWriteJSON_nullAsserts),
cmocka_unit_test(test_saveSettingsReadJSON_nullAsserts),
cmocka_unit_test(test_saveSettingsWriteReadJSON_roundTrip),
cmocka_unit_test(test_saveSettingsReadJSON_missingKeyDefaultsToZero),
cmocka_unit_test(test_saveSettingsReadJSON_missingKeyUsesDefault),
cmocka_unit_test(test_saveSettingsReadJSON_versionMissing_errors),
cmocka_unit_test(test_saveSettingsReadJSON_versionMismatch_errors),
};
return cmocka_run_group_tests(tests, NULL, NULL);