diff --git a/src/dusk/engine/engine.c b/src/dusk/engine/engine.c index 0d56d967..998be76c 100644 --- a/src/dusk/engine/engine.c +++ b/src/dusk/engine/engine.c @@ -21,7 +21,7 @@ #endif #include "system/system.h" #include "console/console.h" -#include "save/savemanager.h"\ +#include "save/save.h"\ engine_t ENGINE; @@ -39,7 +39,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) { errorChain(systemInit()); errorChain(inputInit()); errorChain(assetInit()); - errorChain(saveManagerInit()); + errorChain(saveInit()); errorChain(localeManagerInit()); errorChain(displayInit()); errorChain(uiInit()); @@ -67,7 +67,7 @@ errorret_t engineUpdate(void) { #ifdef DUSK_NETWORK errorChain(networkUpdate()); #endif - errorChain(saveManagerUpdate()); + errorChain(saveUpdate()); timeUpdate(); inputUpdate(); consoleUpdate(); @@ -96,7 +96,7 @@ errorret_t engineDispose(void) { errorChain(uiDispose()); consoleDispose(); errorChain(displayDispose()); - errorChain(saveManagerDispose()); + errorChain(saveDispose()); errorChain(assetDispose()); errorOk(); diff --git a/src/dusk/save/CMakeLists.txt b/src/dusk/save/CMakeLists.txt index 4106c963..50551e1d 100644 --- a/src/dusk/save/CMakeLists.txt +++ b/src/dusk/save/CMakeLists.txt @@ -6,9 +6,8 @@ # Sources target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC - savemanager.c + save.c savedevice.c saveslot.c savesettings.c - savestatus.c ) diff --git a/src/dusk/save/save.c b/src/dusk/save/save.c new file mode 100644 index 00000000..0c22ca7e --- /dev/null +++ b/src/dusk/save/save.c @@ -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(); +} \ No newline at end of file diff --git a/src/dusk/save/savemanager.h b/src/dusk/save/save.h similarity index 51% rename from src/dusk/save/savemanager.h rename to src/dusk/save/save.h index 8878e11e..71f91a42 100644 --- a/src/dusk/save/savemanager.h +++ b/src/dusk/save/save.h @@ -9,21 +9,17 @@ #include "savedevice.h" #include "saveslot.h" #include "savesettings.h" -#include "savestatus.h" typedef struct { // File state savesettings_t settings; + saveslotcache_t caches[SAVE_SLOT_COUNT]; saveslot_t slot; uint8_t slotCurrent; bool_t settingsDirty; 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 savedevice_t devices[SAVE_DEVICE_COUNT]; uint8_t deviceCurrent; @@ -31,34 +27,34 @@ typedef struct { bool_t noAvailableDeviceFound; savedevicestatecallback_t findAvailableCallback; 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. * * @return Error state if any. */ -errorret_t saveManagerInit(); +errorret_t saveInit(); /** - * Updates the save manager. + * Updates the save system. * * @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 * save device if no available save devices are found. * * @param callback The callback to fire when an available device is found. * @param user The user data to pass to the callback. */ -void saveManagerFindAvailableDevice( +void saveFindAvailableDevice( savedevicestatecallback_t callback, void *user ); @@ -69,73 +65,39 @@ void saveManagerFindAvailableDevice( * @param device The save device to fire the callback for. * @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. * * @return Error state if any. */ -errorret_t saveManagerSaveSettings(); +errorret_t saveSaveSettings(); /** * Loads the current settings from the current device. * * @return Error state if any. */ -errorret_t saveManagerLoadSettings(); +errorret_t saveLoadSettings(); /** * Saves the current slot to the current device, at the current slot index. * * @return Error state if any. */ -errorret_t saveManagerSaveSlot(); +errorret_t saveSaveSlot(); /** * Loads the current slot from the current device, at the current slot index. * * @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- - * glance information for each into SAVE_MANAGER.slotStatuses. This does not - * affect the currently loaded slot or the current slot index. + * Disposes of the save system. * * @return Error state if any. */ -errorret_t saveManagerRefreshSlotStatuses(); - -/** - * 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(); \ No newline at end of file +errorret_t saveDispose(); \ No newline at end of file diff --git a/src/dusk/save/savedevice.c b/src/dusk/save/savedevice.c index 7b806140..cf17b5c4 100644 --- a/src/dusk/save/savedevice.c +++ b/src/dusk/save/savedevice.c @@ -82,7 +82,7 @@ void saveDeviceCheckAvailability( errorret_t saveDeviceSlotWrite( savedevice_t *device, saveslot_t *slot, - uint8_t slotIndex + const uint8_t slotIndex ) { assertNotNull(device, "device cannot be null"); assertNotNull(slot, "slot cannot be null"); @@ -97,7 +97,7 @@ errorret_t saveDeviceSlotWrite( errorret_t saveDeviceSlotRead( savedevice_t *device, saveslot_t *slot, - uint8_t slotIndex + const uint8_t slotIndex ) { assertNotNull(device, "device cannot be null"); assertNotNull(slot, "slot cannot be null"); diff --git a/src/dusk/save/savedevice.h b/src/dusk/save/savedevice.h index 2011369c..ae6e9de6 100644 --- a/src/dusk/save/savedevice.h +++ b/src/dusk/save/savedevice.h @@ -85,7 +85,7 @@ void saveDeviceCheckAvailability( errorret_t saveDeviceSlotWrite( savedevice_t *device, saveslot_t *slot, - uint8_t slotIndex + const uint8_t slotIndex ); /** @@ -99,7 +99,7 @@ errorret_t saveDeviceSlotWrite( errorret_t saveDeviceSlotRead( savedevice_t *device, saveslot_t *slot, - uint8_t slotIndex + const uint8_t slotIndex ); /** diff --git a/src/dusk/save/savemanager.c b/src/dusk/save/savemanager.c deleted file mode 100644 index 9829a424..00000000 --- a/src/dusk/save/savemanager.c +++ /dev/null @@ -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(); -} \ No newline at end of file diff --git a/src/dusk/save/saveslot.c b/src/dusk/save/saveslot.c index dc51b6a2..bea3f860 100644 --- a/src/dusk/save/saveslot.c +++ b/src/dusk/save/saveslot.c @@ -9,6 +9,7 @@ #include "assert/assert.h" #include "util/memory.h" #include "savejson.h" +#include "time/time.h" void saveSlotInit(saveslot_t *slot) { assertNotNull(slot, "Slot cannot be null"); @@ -17,11 +18,16 @@ void saveSlotInit(saveslot_t *slot) { slot->version = 1; } -bool_t saveSlotInUse(saveslot_t *slot) { +bool_t saveSlotInUse(saveslotcache_t *slot) { assertNotNull(slot, "Slot cannot be null"); 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( saveslot_t *slot, yyjson_mut_doc *doc, @@ -31,8 +37,12 @@ errorret_t saveSlotWriteJSON( assertNotNull(doc, "Doc cannot be null"); assertNotNull(object, "Object cannot be null"); - writeString("name", slot->name); - writeTime("time", slot->time); + // Update time + slot->cachedData.time = timeGetEpoch(); + + writeString("name", slot->cachedData.name); + writeTime("time", slot->cachedData.time); + writeInt32("playerLevel", slot->cachedData.playerLevel); errorOk(); } @@ -43,8 +53,9 @@ errorret_t saveSlotReadJSON(saveslot_t *slot, yyjson_val *object) { char_t saveJsonStringBuffer[SAVE_JSON_STRING_BUFFER_SIZE]; - readString("name", slot->name, "", SAVE_SLOT_NAME_LENGTH); - readTime("time", slot->time); + readString("name", slot->cachedData.name, "", SAVE_SLOT_NAME_LENGTH); + readTime("time", slot->cachedData.time); + readInt32("playerLevel", slot->cachedData.playerLevel, 1); errorOk(); } \ No newline at end of file diff --git a/src/dusk/save/saveslot.h b/src/dusk/save/saveslot.h index 7135d173..3c2dc841 100644 --- a/src/dusk/save/saveslot.h +++ b/src/dusk/save/saveslot.h @@ -11,15 +11,20 @@ #include "yyjson.h" #define SAVE_SLOT_NAME_LENGTH 8 -#define SAVE_SLOT_COUNT 4 +#define SAVE_SLOT_COUNT 3 #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 { uint8_t version; uint8_t dataType; - char_t name[SAVE_SLOT_NAME_LENGTH + 1];// 8 characters + null terminator - dusktimeepoch_t time; + saveslotcache_t cachedData; } saveslot_t; #pragma pack(pop) @@ -39,7 +44,15 @@ void saveSlotInit(saveslot_t *slot); * @param slot The save slot to check. * @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. diff --git a/src/dusk/save/savestatus.c b/src/dusk/save/savestatus.c deleted file mode 100644 index 894f49db..00000000 --- a/src/dusk/save/savestatus.c +++ /dev/null @@ -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; -} diff --git a/src/dusk/save/savestatus.h b/src/dusk/save/savestatus.h deleted file mode 100644 index fcaefbd6..00000000 --- a/src/dusk/save/savestatus.h +++ /dev/null @@ -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); diff --git a/src/dusk/scene/initial/sceneinitial.c b/src/dusk/scene/initial/sceneinitial.c index 4c77e4d4..b669e65c 100644 --- a/src/dusk/scene/initial/sceneinitial.c +++ b/src/dusk/scene/initial/sceneinitial.c @@ -11,7 +11,7 @@ #include "error/error.h" #include "console/console.h" -#include "save/savemanager.h" +#include "save/save.h" int32_t testData = 69; @@ -19,10 +19,20 @@ void testCallback(savedevice_t *device, void *user) { if(device == NULL) { consolePrint("No save device found."); } else { - uint8_t index = (uint8_t)(device - &SAVE_MANAGER.devices[0]); + uint8_t index = (uint8_t)(device - &SAVE.devices[0]); consolePrint( "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)); consolePrint("Going to find a save device."); - saveManagerFindAvailableDevice( + saveFindAvailableDevice( testCallback, &testData ); diff --git a/src/duskdolphin/save/savedevicedolphincard.c b/src/duskdolphin/save/savedevicedolphincard.c index 3addfe05..c7325260 100644 --- a/src/duskdolphin/save/savedevicedolphincard.c +++ b/src/duskdolphin/save/savedevicedolphincard.c @@ -7,7 +7,7 @@ #include "save/savedevice.h" #include "save/savedevicedolphincard.h" -#include "save/savemanager.h" +#include "save/save.h" #include "assert/assert.h" errorret_t saveDeviceDolphinCardInit(savedevice_t *device) { @@ -15,11 +15,11 @@ errorret_t saveDeviceDolphinCardInit(savedevice_t *device) { 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 - // 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). - 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; errorOk(); diff --git a/src/duskdolphin/save/savedevicedolphincard.h b/src/duskdolphin/save/savedevicedolphincard.h index 71de92f0..7eabc6d5 100644 --- a/src/duskdolphin/save/savedevicedolphincard.h +++ b/src/duskdolphin/save/savedevicedolphincard.h @@ -18,7 +18,7 @@ #endif 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. int32_t channel; uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32))); diff --git a/src/dusklinux/save/savedevicelinux.h b/src/dusklinux/save/savedevicelinux.h index 9671c24d..c1920ec3 100644 --- a/src/dusklinux/save/savedevicelinux.h +++ b/src/dusklinux/save/savedevicelinux.h @@ -8,12 +8,10 @@ #pragma once #include "error/error.h" -// Saves live under $HOME/, one JSON file -// per save slot plus a separate settings file. #define SAVE_DEVICE_LINUX_DIRECTORY_NAME ".dusk/saves" #define SAVE_DEVICE_LINUX_SETTINGS_FILENAME "settings.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 { void *nothing;