First pass of actual saving

This commit is contained in:
2026-08-17 09:18:44 -05:00
parent 08b4bbfe91
commit 092e259a06
15 changed files with 310 additions and 408 deletions
+4 -4
View File
@@ -21,7 +21,7 @@
#endif #endif
#include "system/system.h" #include "system/system.h"
#include "console/console.h" #include "console/console.h"
#include "save/savemanager.h"\ #include "save/save.h"\
engine_t ENGINE; engine_t ENGINE;
@@ -39,7 +39,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(systemInit()); errorChain(systemInit());
errorChain(inputInit()); errorChain(inputInit());
errorChain(assetInit()); errorChain(assetInit());
errorChain(saveManagerInit()); errorChain(saveInit());
errorChain(localeManagerInit()); errorChain(localeManagerInit());
errorChain(displayInit()); errorChain(displayInit());
errorChain(uiInit()); errorChain(uiInit());
@@ -67,7 +67,7 @@ errorret_t engineUpdate(void) {
#ifdef DUSK_NETWORK #ifdef DUSK_NETWORK
errorChain(networkUpdate()); errorChain(networkUpdate());
#endif #endif
errorChain(saveManagerUpdate()); errorChain(saveUpdate());
timeUpdate(); timeUpdate();
inputUpdate(); inputUpdate();
consoleUpdate(); consoleUpdate();
@@ -96,7 +96,7 @@ errorret_t engineDispose(void) {
errorChain(uiDispose()); errorChain(uiDispose());
consoleDispose(); consoleDispose();
errorChain(displayDispose()); errorChain(displayDispose());
errorChain(saveManagerDispose()); errorChain(saveDispose());
errorChain(assetDispose()); errorChain(assetDispose());
errorOk(); errorOk();
+1 -2
View File
@@ -6,9 +6,8 @@
# Sources # Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC PUBLIC
savemanager.c save.c
savedevice.c savedevice.c
saveslot.c saveslot.c
savesettings.c savesettings.c
savestatus.c
) )
+233
View File
@@ -0,0 +1,233 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "save.h"
#include "util/memory.h"
#include "assert/assert.h"
save_t SAVE;
errorret_t saveInit() {
memoryZero(&SAVE, sizeof(save_t));
SAVE.deviceCurrent = 0xFF;// No current device.
SAVE.slotCurrent = 0xFF;// No current slot.
// Initialize the slots and settings
saveSettingsInit(&SAVE.settings);
saveSlotInit(&SAVE.slot);
// Update caches to match default data.
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
SAVE.caches[i] = SAVE.slot.cachedData;
}
// Start by initializing each of the save devices.
savedevice_t *device = &SAVE.devices[0];
do {
errorChain(saveDeviceInit(device));
} while(device++ < &SAVE.devices[SAVE_DEVICE_COUNT - 1]);
errorOk();
}
errorret_t saveUpdate() {
assertIsMainThread("Invalid thread");
// Start by updating each device
savedevice_t *device = &SAVE.devices[0];
do {
saveDeviceUpdate(device);
} while(device++ < &SAVE.devices[SAVE_DEVICE_COUNT - 1]);
// We may be waiting for a device to become available
if(SAVE.findingAvailableDevice) {
assertNotNull(
SAVE.findAvailableCallback,
"Callback cannot be null while looking for devices"
);
// Yes, we have a device available, fire the callback.
if(SAVE.deviceCurrent == 0xFF) {
// Are we still looking or did we fail to find anything?
if(SAVE.noAvailableDeviceFound) {
SAVE.findingAvailableDevice = false;
SAVE.findAvailableCallback(
NULL,
SAVE.findAvailableUser
);
}
// Still searching
} else {
// Device was found, cache in the data.
errorChain(saveLoadSettings());
// Reset each slot and try load from file, updating cache along the way.
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
SAVE.slotCurrent = i;
saveSlotInit(&SAVE.slot);
errorChain(saveLoadSlot());// Load slot updates the cache.
}
// Reset slot, implying no slot has been selected.
SAVE.slotCurrent = 0xFF;
SAVE.findingAvailableDevice = false;
SAVE.findAvailableCallback(
&SAVE.devices[SAVE.deviceCurrent],
SAVE.findAvailableUser
);
}
}
errorOk();
}
void saveFindAvailableDevice(
savedevicestatecallback_t callback,
void *user
) {
assertIsMainThread("Invalid thread");
assertNotNull(callback, "Callback cannot be null");
assertFalse(
SAVE.findingAvailableDevice,
"Already finding an available device"
);
SAVE.findingAvailableDevice = true;
SAVE.findAvailableCallback = callback;
SAVE.findAvailableUser = user;
SAVE.noAvailableDeviceFound = false;
// Is there an available device marked already?
if(SAVE.deviceCurrent != 0xFF) {
// Yes, fire the callback next tick.
return;
}
// Do we have an available device not yet marked as current?
savedevice_t *device = &SAVE.devices[0];
do {
if(device->state != SAVE_DEVICE_STATE_AVAILABLE) continue;
// Yes this device is available, set as current device.
SAVE.deviceCurrent = (uint8_t)(device - &SAVE.devices[0]);
return;// Next tick will fire the callback.
} while(device++ < &SAVE.devices[SAVE_DEVICE_COUNT - 1]);
// No currently available device, check them IN ORDER, starting at 0
savedevice_t *deviceToCheck = &SAVE.devices[0];
assertNotNull(deviceToCheck, "Device to check cannot be null");
saveDeviceCheckAvailability(
deviceToCheck,
saveOnDeviceAvailabilityChecked,
NULL
);
}
void saveOnDeviceAvailabilityChecked(savedevice_t *device, void *user) {
assertNotNull(device, "Device cannot be null");
assertTrue(
SAVE.findingAvailableDevice,
"Not currently finding an available device"
);
// Did we already find a device?
if(SAVE.deviceCurrent != 0xFF) return;
// Is this device available?
if(device->state != SAVE_DEVICE_STATE_AVAILABLE) {
// Since it's unavailable we should tell the next device to check its
// availability. If there is no next device we give up.
savedevice_t *nextDevice = device + 1;
if(nextDevice >= &SAVE.devices[SAVE_DEVICE_COUNT]) {
// No more devices to check, we give up.
SAVE.noAvailableDeviceFound = true;
return;
}
// Check the next device's availability.
saveDeviceCheckAvailability(
nextDevice,
saveOnDeviceAvailabilityChecked,
NULL
);
return;
}
// Yes this device is available, set as current device and next tick will
// invoke callback
SAVE.deviceCurrent = (uint8_t)(device - &SAVE.devices[0]);
}
errorret_t saveSaveSettings() {
assertTrue(SAVE.deviceCurrent != 0xFF, "No current device");
if(!SAVE.settingsDirty) errorOk();
errorChain(saveDeviceSettingsWrite(
&SAVE.devices[SAVE.deviceCurrent],
&SAVE.settings
));
SAVE.settingsDirty = false;
errorOk();
}
errorret_t saveLoadSettings() {
assertTrue(SAVE.deviceCurrent != 0xFF, "No current device");
errorChain(saveDeviceSettingsRead(
&SAVE.devices[SAVE.deviceCurrent],
&SAVE.settings
));
SAVE.settingsDirty = false;
errorOk();
}
errorret_t saveSaveSlot() {
assertTrue(SAVE.deviceCurrent != 0xFF, "No current device");
assertTrue(SAVE.slotCurrent < SAVE_SLOT_COUNT, "Invalid slot index");
errorChain(saveDeviceSlotWrite(
&SAVE.devices[SAVE.deviceCurrent],
&SAVE.slot,
SAVE.slotCurrent
));
// Update cache
SAVE.caches[SAVE.slotCurrent] = SAVE.slot.cachedData;
SAVE.slotDirty = false;
errorOk();
}
errorret_t saveLoadSlot() {
assertTrue(SAVE.deviceCurrent != 0xFF, "No current device");
assertTrue(SAVE.slotCurrent < SAVE_SLOT_COUNT, "Invalid slot index");
errorChain(saveDeviceSlotRead(
&SAVE.devices[SAVE.deviceCurrent],
&SAVE.slot,
SAVE.slotCurrent
));
// Update cache
SAVE.caches[SAVE.slotCurrent] = SAVE.slot.cachedData;
SAVE.slotDirty = false;
errorOk();
}
errorret_t saveDispose() {
// Dispose each device.
savedevice_t *device = &SAVE.devices[0];
do {
saveDeviceDispose(device);
} while(device++ < &SAVE.devices[SAVE_DEVICE_COUNT - 1]);
errorOk();
}
@@ -9,21 +9,17 @@
#include "savedevice.h" #include "savedevice.h"
#include "saveslot.h" #include "saveslot.h"
#include "savesettings.h" #include "savesettings.h"
#include "savestatus.h"
typedef struct { typedef struct {
// File state // File state
savesettings_t settings; savesettings_t settings;
saveslotcache_t caches[SAVE_SLOT_COUNT];
saveslot_t slot; saveslot_t slot;
uint8_t slotCurrent; uint8_t slotCurrent;
bool_t settingsDirty; bool_t settingsDirty;
bool_t slotDirty; bool_t slotDirty;
// Basic info about each save file on the current device, refreshed
// whenever the current device changes.
savestatus_t slotStatuses[SAVE_SLOT_COUNT];
// Device state // Device state
savedevice_t devices[SAVE_DEVICE_COUNT]; savedevice_t devices[SAVE_DEVICE_COUNT];
uint8_t deviceCurrent; uint8_t deviceCurrent;
@@ -31,34 +27,34 @@ typedef struct {
bool_t noAvailableDeviceFound; bool_t noAvailableDeviceFound;
savedevicestatecallback_t findAvailableCallback; savedevicestatecallback_t findAvailableCallback;
void *findAvailableUser; void *findAvailableUser;
} savemanager_t; } save_t;
extern savemanager_t SAVE_MANAGER; extern save_t SAVE;
/** /**
* Initializes the save manager, this does not do anything related to mounting * Initializes the save system, this does not do anything related to mounting
* files, memory cards, etc, this is entirely prepping for that capability. * files, memory cards, etc, this is entirely prepping for that capability.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerInit(); errorret_t saveInit();
/** /**
* Updates the save manager. * Updates the save system.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerUpdate(); errorret_t saveUpdate();
/** /**
* Requests the save manager to try and find an available device. This will fire * Requests the save system to try and find an available device. This will fire
* the callback whenever an avaialble device is found, or will fire with a NULL * the callback whenever an avaialble device is found, or will fire with a NULL
* save device if no available save devices are found. * save device if no available save devices are found.
* *
* @param callback The callback to fire when an available device is found. * @param callback The callback to fire when an available device is found.
* @param user The user data to pass to the callback. * @param user The user data to pass to the callback.
*/ */
void saveManagerFindAvailableDevice( void saveFindAvailableDevice(
savedevicestatecallback_t callback, savedevicestatecallback_t callback,
void *user void *user
); );
@@ -69,73 +65,39 @@ void saveManagerFindAvailableDevice(
* @param device The save device to fire the callback for. * @param device The save device to fire the callback for.
* @param user Unused, present to match savedevicestatecallback_t. * @param user Unused, present to match savedevicestatecallback_t.
*/ */
void saveManagerOnDeviceAvailabilityChecked(savedevice_t *device, void *user); void saveOnDeviceAvailabilityChecked(savedevice_t *device, void *user);
/** /**
* Saves the current settings to the current device. * Saves the current settings to the current device.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerSaveSettings(); errorret_t saveSaveSettings();
/** /**
* Loads the current settings from the current device. * Loads the current settings from the current device.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerLoadSettings(); errorret_t saveLoadSettings();
/** /**
* Saves the current slot to the current device, at the current slot index. * Saves the current slot to the current device, at the current slot index.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerSaveSlot(); errorret_t saveSaveSlot();
/** /**
* Loads the current slot from the current device, at the current slot index. * Loads the current slot from the current device, at the current slot index.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerLoadSlot(); errorret_t saveLoadSlot();
/** /**
* Walks every slot index on the current device and reads the basic, at-a- * Disposes of the save system.
* glance information for each into SAVE_MANAGER.slotStatuses. This does not
* affect the currently loaded slot or the current slot index.
* *
* @return Error state if any. * @return Error state if any.
*/ */
errorret_t saveManagerRefreshSlotStatuses(); errorret_t saveDispose();
/**
* Sets the current slot index, and attempts to load that slot's data from
* the current device.
*
* @param slotIndex The slot index to make current.
* @return Error state if any.
*/
errorret_t saveManagerSetSlot(uint8_t slotIndex);
/**
* Saves both the current settings and the current slot to the current
* device.
*
* @return Error state if any.
*/
errorret_t saveManagerSave();
/**
* Loads both the current settings and the current slot from the current
* device.
*
* @return Error state if any.
*/
errorret_t saveManagerLoad();
/**
* Disposes of the save manager.
*
* @return Error state if any.
*/
errorret_t saveManagerDispose();
+2 -2
View File
@@ -82,7 +82,7 @@ void saveDeviceCheckAvailability(
errorret_t saveDeviceSlotWrite( errorret_t saveDeviceSlotWrite(
savedevice_t *device, savedevice_t *device,
saveslot_t *slot, saveslot_t *slot,
uint8_t slotIndex const uint8_t slotIndex
) { ) {
assertNotNull(device, "device cannot be null"); assertNotNull(device, "device cannot be null");
assertNotNull(slot, "slot cannot be null"); assertNotNull(slot, "slot cannot be null");
@@ -97,7 +97,7 @@ errorret_t saveDeviceSlotWrite(
errorret_t saveDeviceSlotRead( errorret_t saveDeviceSlotRead(
savedevice_t *device, savedevice_t *device,
saveslot_t *slot, saveslot_t *slot,
uint8_t slotIndex const uint8_t slotIndex
) { ) {
assertNotNull(device, "device cannot be null"); assertNotNull(device, "device cannot be null");
assertNotNull(slot, "slot cannot be null"); assertNotNull(slot, "slot cannot be null");
+2 -2
View File
@@ -85,7 +85,7 @@ void saveDeviceCheckAvailability(
errorret_t saveDeviceSlotWrite( errorret_t saveDeviceSlotWrite(
savedevice_t *device, savedevice_t *device,
saveslot_t *slot, saveslot_t *slot,
uint8_t slotIndex const uint8_t slotIndex
); );
/** /**
@@ -99,7 +99,7 @@ errorret_t saveDeviceSlotWrite(
errorret_t saveDeviceSlotRead( errorret_t saveDeviceSlotRead(
savedevice_t *device, savedevice_t *device,
saveslot_t *slot, saveslot_t *slot,
uint8_t slotIndex const uint8_t slotIndex
); );
/** /**
-265
View File
@@ -1,265 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "savemanager.h"
#include "util/memory.h"
#include "assert/assert.h"
savemanager_t SAVE_MANAGER;
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);
// Initialize the default (not in use) status for each save file.
savestatus_t *status = &SAVE_MANAGER.slotStatuses[0];
do {
saveStatusInit(status);
} while(status++ < &SAVE_MANAGER.slotStatuses[SAVE_SLOT_COUNT - 1]);
// Start by initializing each of the save devices.
savedevice_t *device = &SAVE_MANAGER.devices[0];
do {
saveDeviceInit(device);
} while(device++ < &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT - 1]);
errorOk();
}
errorret_t saveManagerUpdate() {
assertIsMainThread("Invalid thread");
// Start by updating each device
savedevice_t *device = &SAVE_MANAGER.devices[0];
do {
saveDeviceUpdate(device);
} while(device++ < &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT - 1]);
// We may be waiting for a device to become available
if(SAVE_MANAGER.findingAvailableDevice) {
assertNotNull(
SAVE_MANAGER.findAvailableCallback,
"Callback cannot be null while looking for devices"
);
// Yes, we have a device available, fire the callback.
if(SAVE_MANAGER.deviceCurrent == 0xFF) {
// Are we still looking or did we fail to find anything?
if(SAVE_MANAGER.noAvailableDeviceFound) {
SAVE_MANAGER.findingAvailableDevice = false;
SAVE_MANAGER.findAvailableCallback(
NULL,
SAVE_MANAGER.findAvailableUser
);
}
// Still searching
} else {
// Device was found!
SAVE_MANAGER.findingAvailableDevice = false;
SAVE_MANAGER.findAvailableCallback(
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
SAVE_MANAGER.findAvailableUser
);
}
}
errorOk();
}
void saveManagerFindAvailableDevice(
savedevicestatecallback_t callback,
void *user
) {
assertIsMainThread("Invalid thread");
assertNotNull(callback, "Callback cannot be null");
assertFalse(
SAVE_MANAGER.findingAvailableDevice,
"Already finding an available device"
);
SAVE_MANAGER.findingAvailableDevice = true;
SAVE_MANAGER.findAvailableCallback = callback;
SAVE_MANAGER.findAvailableUser = user;
SAVE_MANAGER.noAvailableDeviceFound = false;
// Is there an available device marked already?
if(SAVE_MANAGER.deviceCurrent != 0xFF) {
// Yes, fire the callback next tick.
return;
}
// Do we have an available device not yet marked as current?
savedevice_t *device = &SAVE_MANAGER.devices[0];
do {
if(device->state != SAVE_DEVICE_STATE_AVAILABLE) continue;
// Yes this device is available, set as current device.
SAVE_MANAGER.deviceCurrent = (uint8_t)(device - &SAVE_MANAGER.devices[0]);
return;// Next tick will fire the callback.
} while(device++ < &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT - 1]);
// No currently available device, check them IN ORDER, starting at 0
savedevice_t *deviceToCheck = &SAVE_MANAGER.devices[0];
assertNotNull(deviceToCheck, "Device to check cannot be null");
saveDeviceCheckAvailability(
deviceToCheck,
saveManagerOnDeviceAvailabilityChecked,
NULL
);
}
void saveManagerOnDeviceAvailabilityChecked(savedevice_t *device, void *user) {
assertNotNull(device, "Device cannot be null");
assertTrue(
SAVE_MANAGER.findingAvailableDevice,
"Not currently finding an available device"
);
// Did we already find a device?
if(SAVE_MANAGER.deviceCurrent != 0xFF) return;
// Is this device available?
if(device->state != SAVE_DEVICE_STATE_AVAILABLE) {
// Since it's unavailable we should tell the next device to check its
// availability. If there is no next device we give up.
savedevice_t *nextDevice = device + 1;
if(nextDevice >= &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT]) {
// No more devices to check, we give up.
SAVE_MANAGER.noAvailableDeviceFound = true;
return;
}
// Check the next device's availability.
saveDeviceCheckAvailability(
nextDevice,
saveManagerOnDeviceAvailabilityChecked,
NULL
);
return;
}
// Yes this device is available, set as current device and next tick will
// invoke callback
SAVE_MANAGER.deviceCurrent = (uint8_t)(device - &SAVE_MANAGER.devices[0]);
}
errorret_t saveManagerSaveSettings() {
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
if(!SAVE_MANAGER.settingsDirty) errorOk();
errorChain(saveDeviceSettingsWrite(
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
&SAVE_MANAGER.settings
));
SAVE_MANAGER.settingsDirty = false;
errorOk();
}
errorret_t saveManagerLoadSettings() {
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
errorChain(saveDeviceSettingsRead(
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
&SAVE_MANAGER.settings
));
SAVE_MANAGER.settingsDirty = false;
errorOk();
}
errorret_t saveManagerSaveSlot() {
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
assertTrue(SAVE_MANAGER.slotCurrent != 0xFF, "No current slot");
assertTrue(saveSlotInUse(&SAVE_MANAGER.slot), "Save slot is not valid");
if(!SAVE_MANAGER.slotDirty) errorOk();
errorChain(saveDeviceSlotWrite(
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
&SAVE_MANAGER.slot,
SAVE_MANAGER.slotCurrent
));
SAVE_MANAGER.slotDirty = false;
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
));
SAVE_MANAGER.slotDirty = false;
errorOk();
}
errorret_t saveManagerRefreshSlotStatuses() {
assertTrue(SAVE_MANAGER.deviceCurrent != 0xFF, "No current device");
saveslot_t slot;
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
saveSlotInit(&slot);
errorChain(saveDeviceSlotRead(
&SAVE_MANAGER.devices[SAVE_MANAGER.deviceCurrent],
&slot,
i
));
saveStatusFromSlot(&SAVE_MANAGER.slotStatuses[i], &slot);
}
errorOk();
}
errorret_t saveManagerSetSlot(uint8_t slotIndex) {
assertTrue(slotIndex != 0xFF, "Slot index cannot be 0xFF");
SAVE_MANAGER.slotCurrent = slotIndex;
SAVE_MANAGER.slotDirty = true;
errorChain(saveManagerLoadSlot());
errorOk();
}
errorret_t saveManagerSave() {
errorChain(saveManagerSaveSettings());
errorChain(saveManagerSaveSlot());
errorOk();
}
errorret_t saveManagerLoad() {
errorChain(saveManagerLoadSettings());
errorChain(saveManagerLoadSlot());
errorOk();
}
errorret_t saveManagerDispose() {
// Dispose each device.
savedevice_t *device = &SAVE_MANAGER.devices[0];
do {
saveDeviceDispose(device);
} while(device++ < &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT - 1]);
errorOk();
}
+16 -5
View File
@@ -9,6 +9,7 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include "savejson.h" #include "savejson.h"
#include "time/time.h"
void saveSlotInit(saveslot_t *slot) { void saveSlotInit(saveslot_t *slot) {
assertNotNull(slot, "Slot cannot be null"); assertNotNull(slot, "Slot cannot be null");
@@ -17,11 +18,16 @@ void saveSlotInit(saveslot_t *slot) {
slot->version = 1; slot->version = 1;
} }
bool_t saveSlotInUse(saveslot_t *slot) { bool_t saveSlotInUse(saveslotcache_t *slot) {
assertNotNull(slot, "Slot cannot be null"); assertNotNull(slot, "Slot cannot be null");
return slot->name[0] != '\0'; return slot->name[0] != '\0';
} }
bool_t saveSlotHasSaved(saveslotcache_t *slot) {
assertNotNull(slot, "Slot cannot be null");
return slot->time.time != 0;
}
errorret_t saveSlotWriteJSON( errorret_t saveSlotWriteJSON(
saveslot_t *slot, saveslot_t *slot,
yyjson_mut_doc *doc, yyjson_mut_doc *doc,
@@ -31,8 +37,12 @@ errorret_t saveSlotWriteJSON(
assertNotNull(doc, "Doc cannot be null"); assertNotNull(doc, "Doc cannot be null");
assertNotNull(object, "Object cannot be null"); assertNotNull(object, "Object cannot be null");
writeString("name", slot->name); // Update time
writeTime("time", slot->time); slot->cachedData.time = timeGetEpoch();
writeString("name", slot->cachedData.name);
writeTime("time", slot->cachedData.time);
writeInt32("playerLevel", slot->cachedData.playerLevel);
errorOk(); errorOk();
} }
@@ -43,8 +53,9 @@ errorret_t saveSlotReadJSON(saveslot_t *slot, yyjson_val *object) {
char_t saveJsonStringBuffer[SAVE_JSON_STRING_BUFFER_SIZE]; char_t saveJsonStringBuffer[SAVE_JSON_STRING_BUFFER_SIZE];
readString("name", slot->name, "", SAVE_SLOT_NAME_LENGTH); readString("name", slot->cachedData.name, "", SAVE_SLOT_NAME_LENGTH);
readTime("time", slot->time); readTime("time", slot->cachedData.time);
readInt32("playerLevel", slot->cachedData.playerLevel, 1);
errorOk(); errorOk();
} }
+17 -4
View File
@@ -11,15 +11,20 @@
#include "yyjson.h" #include "yyjson.h"
#define SAVE_SLOT_NAME_LENGTH 8 #define SAVE_SLOT_NAME_LENGTH 8
#define SAVE_SLOT_COUNT 4 #define SAVE_SLOT_COUNT 3
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct {
char_t name[SAVE_SLOT_NAME_LENGTH + 1];// 8 characters + null terminator
dusktimeepoch_t time;
int32_t playerLevel;
} saveslotcache_t;
typedef struct saveslot_s { typedef struct saveslot_s {
uint8_t version; uint8_t version;
uint8_t dataType; uint8_t dataType;
char_t name[SAVE_SLOT_NAME_LENGTH + 1];// 8 characters + null terminator saveslotcache_t cachedData;
dusktimeepoch_t time;
} saveslot_t; } saveslot_t;
#pragma pack(pop) #pragma pack(pop)
@@ -39,7 +44,15 @@ void saveSlotInit(saveslot_t *slot);
* @param slot The save slot to check. * @param slot The save slot to check.
* @return True if the save slot is in use, false otherwise. * @return True if the save slot is in use, false otherwise.
*/ */
bool_t saveSlotInUse(saveslot_t *slot); bool_t saveSlotInUse(saveslotcache_t *slot);
/**
* Returns whether or not the given save slot has ever saved (has an epoc non 0)
*
* @param slot The save slot to check.
* @return True if the save slot has ever saved, false otherwise.
*/
bool_t saveSlotHasSaved(saveslotcache_t *slot);
/** /**
* Writes the given save slot out to the given JSON object. * Writes the given save slot out to the given JSON object.
-25
View File
@@ -1,25 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "savestatus.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void saveStatusInit(savestatus_t *status) {
assertNotNull(status, "Status cannot be null");
memoryZero(status, sizeof(savestatus_t));
}
void saveStatusFromSlot(savestatus_t *status, saveslot_t *slot) {
assertNotNull(status, "Status cannot be null");
assertNotNull(slot, "Slot cannot be null");
status->inUse = saveSlotInUse(slot);
stringCopy(status->name, slot->name, SAVE_SLOT_NAME_LENGTH);
status->time = slot->time;
}
-34
View File
@@ -1,34 +0,0 @@
/**
* 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 "saveslot.h"
#pragma pack(push, 1)
typedef struct savestatus_s {
bool_t inUse;
char_t name[SAVE_SLOT_NAME_LENGTH + 1];
dusktimeepoch_t time;
} savestatus_t;
#pragma pack(pop)
/**
* Inits the save status with the default (not in use) state.
*
* @param status The save status to init.
*/
void saveStatusInit(savestatus_t *status);
/**
* Populates the save status with the basic, at-a-glance information taken
* from a fully loaded save slot.
*
* @param status The save status to populate.
* @param slot The save slot to read the basic information from.
*/
void saveStatusFromSlot(savestatus_t *status, saveslot_t *slot);
+13 -3
View File
@@ -11,7 +11,7 @@
#include "error/error.h" #include "error/error.h"
#include "console/console.h" #include "console/console.h"
#include "save/savemanager.h" #include "save/save.h"
int32_t testData = 69; int32_t testData = 69;
@@ -19,10 +19,20 @@ void testCallback(savedevice_t *device, void *user) {
if(device == NULL) { if(device == NULL) {
consolePrint("No save device found."); consolePrint("No save device found.");
} else { } else {
uint8_t index = (uint8_t)(device - &SAVE_MANAGER.devices[0]); uint8_t index = (uint8_t)(device - &SAVE.devices[0]);
consolePrint( consolePrint(
"Found save device %u: %s", (uint32_t)index, device->reasonKey "Found save device %u: %s", (uint32_t)index, device->reasonKey
); );
for(uint8_t i = 0; i < SAVE_SLOT_COUNT; i++) {
if(saveSlotInUse(&SAVE.caches[i])) {
consolePrint(
"Slot %u is in use: %s", (uint32_t)i, SAVE.caches[i].name
);
} else {
consolePrint("Slot %u is not in use.", (uint32_t)i);
}
}
} }
} }
@@ -31,7 +41,7 @@ errorret_t sceneInitialInit(scenedata_t *sceneData) {
memoryZero(&sceneData->initial, sizeof(sceneinitial_t)); memoryZero(&sceneData->initial, sizeof(sceneinitial_t));
consolePrint("Going to find a save device."); consolePrint("Going to find a save device.");
saveManagerFindAvailableDevice( saveFindAvailableDevice(
testCallback, testCallback,
&testData &testData
); );
+4 -4
View File
@@ -7,7 +7,7 @@
#include "save/savedevice.h" #include "save/savedevice.h"
#include "save/savedevicedolphincard.h" #include "save/savedevicedolphincard.h"
#include "save/savemanager.h" #include "save/save.h"
#include "assert/assert.h" #include "assert/assert.h"
errorret_t saveDeviceDolphinCardInit(savedevice_t *device) { errorret_t saveDeviceDolphinCardInit(savedevice_t *device) {
@@ -15,11 +15,11 @@ errorret_t saveDeviceDolphinCardInit(savedevice_t *device) {
device->state = SAVE_DEVICE_STATE_UNKNOWN; device->state = SAVE_DEVICE_STATE_UNKNOWN;
// Each savedevice_t in SAVE_MANAGER.devices maps to one physical memory // Each savedevice_t in SAVE.devices maps to one physical memory
// card slot - device 0 is slot A, device 1 is slot B, determined by this // card slot - device 0 is slot A, device 1 is slot B, determined by this
// device's position in that array (the same index math savemanager.c // device's position in that array (the same index math save.c
// itself uses to identify a device). // itself uses to identify a device).
uint8_t index = (uint8_t)(device - &SAVE_MANAGER.devices[0]); uint8_t index = (uint8_t)(device - &SAVE.devices[0]);
device->platform.channel = index == 0 ? CARD_SLOTA : CARD_SLOTB; device->platform.channel = index == 0 ? CARD_SLOTA : CARD_SLOTB;
errorOk(); errorOk();
+1 -1
View File
@@ -18,7 +18,7 @@
#endif #endif
typedef struct { typedef struct {
// Set from this device's index into SAVE_MANAGER.devices - device 0 is // Set from this device's index into SAVE.devices - device 0 is
// CARD_SLOTA, device 1 is CARD_SLOTB. // CARD_SLOTA, device 1 is CARD_SLOTB.
int32_t channel; int32_t channel;
uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32))); uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32)));
+1 -3
View File
@@ -8,12 +8,10 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
// Saves live under $HOME/<SAVE_DEVICE_LINUX_DIRECTORY_NAME>, one JSON file
// per save slot plus a separate settings file.
#define SAVE_DEVICE_LINUX_DIRECTORY_NAME ".dusk/saves" #define SAVE_DEVICE_LINUX_DIRECTORY_NAME ".dusk/saves"
#define SAVE_DEVICE_LINUX_SETTINGS_FILENAME "settings.json" #define SAVE_DEVICE_LINUX_SETTINGS_FILENAME "settings.json"
#define SAVE_DEVICE_LINUX_SLOT_FILENAME_FORMAT "slot%u.json" #define SAVE_DEVICE_LINUX_SLOT_FILENAME_FORMAT "slot%u.json"
#define SAVE_DEVICE_LINUX_PATH_MAX 512 #define SAVE_DEVICE_LINUX_PATH_MAX FILENAME_MAX
typedef struct { typedef struct {
void *nothing; void *nothing;