First pass of actual saving
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savemanager.c
|
||||
save.c
|
||||
savedevice.c
|
||||
saveslot.c
|
||||
savesettings.c
|
||||
savestatus.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();
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user