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
#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();
+1 -2
View File
@@ -6,9 +6,8 @@
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
savemanager.c
save.c
savedevice.c
saveslot.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 "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();
errorret_t saveDispose();
+2 -2
View File
@@ -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");
+2 -2
View File
@@ -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
);
/**
-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 "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();
}
+17 -4
View File
@@ -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.
-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 "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
);
+4 -4
View File
@@ -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();
+1 -1
View File
@@ -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)));
+1 -3
View File
@@ -8,12 +8,10 @@
#pragma once
#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_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;