First pass of save
This commit is contained in:
@@ -10,6 +10,9 @@ msgid "ui.title"
|
||||
msgstr ""
|
||||
"Welcome"
|
||||
|
||||
msgid "save.linux.mkdirp_failed"
|
||||
msgstr "Failed to create save directory, check the disk is not full or write-protected."
|
||||
|
||||
#: src/dusk/ui/frame/settings/uisettings.c
|
||||
msgid "ui.settings.tabs.general"
|
||||
msgstr "General"
|
||||
|
||||
@@ -67,7 +67,7 @@ errorret_t engineUpdate(void) {
|
||||
#ifdef DUSK_NETWORK
|
||||
errorChain(networkUpdate());
|
||||
#endif
|
||||
// errorChain(saveManagerUpdate());
|
||||
errorChain(saveManagerUpdate());
|
||||
timeUpdate();
|
||||
inputUpdate();
|
||||
consoleUpdate();
|
||||
|
||||
@@ -7,4 +7,5 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savemanager.c
|
||||
savedevice.c
|
||||
)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/savedevice.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
errorret_t saveDeviceInit(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
|
||||
memoryZero(device, sizeof(savedevice_t));
|
||||
device->state = 0xFF;// We set this because the platform must define it.
|
||||
errorChain(saveDevicePlatformInit(device));
|
||||
|
||||
// The platform must put the device into a state we can work with.
|
||||
assertTrue(
|
||||
device->state == SAVE_DEVICE_STATE_UNKNOWN ||
|
||||
device->state == SAVE_DEVICE_STATE_UNAVAILABLE ||
|
||||
device->state == SAVE_DEVICE_STATE_AVAILABLE ||
|
||||
device->state == SAVE_DEVICE_STATE_ERRORED,
|
||||
"Save device must be in a state that allows checking availability"
|
||||
);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceUpdate(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertIsMainThread("Invalid thread");
|
||||
|
||||
// Perform a device update.
|
||||
errorChain(saveDevicePlatformUpdate(device));
|
||||
|
||||
// Fire the callback if desired.
|
||||
if(device->fireCallback) {
|
||||
device->fireCallback = false;
|
||||
if(device->stateCallback) device->stateCallback(device, device->user);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveDeviceFireCallback(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertIsMainThread("Invalid thread");
|
||||
assertFalse(device->fireCallback, "Device callback already fired?");
|
||||
device->fireCallback = true;
|
||||
}
|
||||
|
||||
void saveDeviceCheckAvailability(
|
||||
savedevice_t *device,
|
||||
savedevicestatecallback_t callback,
|
||||
void *user
|
||||
) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertNotNull(callback, "callback cannot be null");
|
||||
assertIsMainThread("Invalid thread");
|
||||
|
||||
// The device must be in a state that we allow checking the availability.
|
||||
assertTrue(
|
||||
device->state == SAVE_DEVICE_STATE_UNKNOWN ||
|
||||
device->state == SAVE_DEVICE_STATE_UNAVAILABLE ||
|
||||
device->state == SAVE_DEVICE_STATE_AVAILABLE ||
|
||||
device->state == SAVE_DEVICE_STATE_ERRORED,
|
||||
"Save device must be in a state that allows checking availability"
|
||||
);
|
||||
|
||||
// Set state data and callback.
|
||||
device->state = SAVE_DEVICE_STATE_CHECKING_AVAILABILITY;
|
||||
device->stateCallback = callback;
|
||||
device->user = user;
|
||||
|
||||
// Handoff to the platform to do its checks, it can opt to do this either
|
||||
// synchronously or asynchronously.
|
||||
saveDeviceCheckAvailabilityPlatform(device);
|
||||
}
|
||||
|
||||
errorret_t saveDeviceDispose(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
errorChain(saveDevicePlatformDispose(device));
|
||||
errorOk();
|
||||
}
|
||||
@@ -7,16 +7,75 @@
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/savedeviceplatform.h"
|
||||
|
||||
// Save device count e.g. Memory Cards count.
|
||||
#ifndef SAVE_DEVICE_COUNT
|
||||
#error "SAVE_DEVICE_COUNT must be defined"
|
||||
#endif
|
||||
|
||||
typedef struct savedevice_s savedevice_t;
|
||||
|
||||
typedef void (*savedevicestatecallback_t)(savedevice_t *device, void *user);
|
||||
|
||||
typedef enum {
|
||||
SAVE_DEVICE_STATE_UNKNOWN,
|
||||
SAVE_DEVICE_STATE_CHECKING_AVAILABILITY,
|
||||
SAVE_DEVICE_STATE_UNAVAILABLE,
|
||||
SAVE_DEVICE_STATE_AVAILABLE,
|
||||
SAVE_DEVICE_STATE_MOUNTING,
|
||||
SAVE_DEVICE_STATE_MOUNTED,
|
||||
SAVE_DEVICE_STATE_ERRORED
|
||||
} savedevicestate_t;
|
||||
|
||||
typedef struct {
|
||||
typedef struct savedevice_s {
|
||||
savedevicestate_t state;
|
||||
} savedevice_t;
|
||||
savedeviceplatform_t platform;
|
||||
const char_t *reasonKey;
|
||||
bool_t fireCallback;
|
||||
void *user;
|
||||
savedevicestatecallback_t stateCallback;
|
||||
} savedevice_t;
|
||||
|
||||
/**
|
||||
* Initializes the save device.
|
||||
*
|
||||
* @param device The save device to initialize.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceInit(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Updates the save device.
|
||||
*
|
||||
* @param device The save device to update.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceUpdate(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Internal method to fire the save callback, does it at the appropriate time.
|
||||
*
|
||||
* @param device The save device to fire the callback for.
|
||||
*/
|
||||
void saveDeviceFireCallback(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Requests the device to check its availability, this will call the callback
|
||||
* whence completed.
|
||||
*
|
||||
* @param device The save device to check availability.
|
||||
* @param callback The callback to call when the availability check is complete.
|
||||
* @param user User data to pass to the callback.
|
||||
*/
|
||||
void saveDeviceCheckAvailability(
|
||||
savedevice_t *device,
|
||||
savedevicestatecallback_t callback,
|
||||
void *user
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of the save device.
|
||||
*
|
||||
* @param device The save device to dispose.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceDispose(savedevice_t *device);
|
||||
@@ -8,3 +8,4 @@
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
typedef uint8_t saveslot_t;
|
||||
@@ -6,11 +6,147 @@
|
||||
*/
|
||||
|
||||
#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.
|
||||
|
||||
// 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 saveManagerDispose() {
|
||||
// Dispose each device.
|
||||
savedevice_t *device = &SAVE_MANAGER.devices[0];
|
||||
do {
|
||||
saveDeviceDispose(device);
|
||||
} while(device++ < &SAVE_MANAGER.devices[SAVE_DEVICE_COUNT - 1]);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -6,10 +6,16 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "savedevice.h"
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
savedevice_t devices[SAVE_DEVICE_COUNT];
|
||||
uint8_t deviceCurrent;
|
||||
|
||||
bool_t findingAvailableDevice;
|
||||
bool_t noAvailableDeviceFound;
|
||||
savedevicestatecallback_t findAvailableCallback;
|
||||
void *findAvailableUser;
|
||||
} savemanager_t;
|
||||
|
||||
extern savemanager_t SAVE_MANAGER;
|
||||
@@ -17,10 +23,42 @@ extern savemanager_t SAVE_MANAGER;
|
||||
/**
|
||||
* Initializes the save manager, 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();
|
||||
|
||||
/**
|
||||
* Updates the save manager.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerUpdate();
|
||||
|
||||
/**
|
||||
* Requests the save manager 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(
|
||||
savedevicestatecallback_t callback,
|
||||
void *user
|
||||
);
|
||||
|
||||
/**
|
||||
* Internal method to fire the save callback, does it at the appropriate time.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Disposes of the save manager.
|
||||
*
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveManagerDispose();
|
||||
@@ -9,13 +9,30 @@
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "error/error.h"
|
||||
#include "ui/frame/initial/uiinitialnocard.h"
|
||||
#include "ui/frame/initial/uiinitialcreatesave.h"
|
||||
|
||||
#include "console/console.h"
|
||||
#include "save/savemanager.h"
|
||||
|
||||
int32_t testData = 69;
|
||||
|
||||
void testCallback(savedevice_t *device, void *user) {
|
||||
if(device == NULL) {
|
||||
consolePrint("No save device found.");
|
||||
} else {
|
||||
consolePrint("Found save device: %s", device->reasonKey);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t sceneInitialInit(scenedata_t *sceneData) {
|
||||
assertNotNull(sceneData, "Scene data cannot be null");
|
||||
memoryZero(&sceneData->initial, sizeof(sceneinitial_t));
|
||||
|
||||
consolePrint("Going to find a save device.");
|
||||
saveManagerFindAvailableDevice(
|
||||
testCallback,
|
||||
&testData
|
||||
);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,5 +12,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(mainmenu)
|
||||
add_subdirectory(battle)
|
||||
add_subdirectory(backpack)
|
||||
add_subdirectory(initial)
|
||||
add_subdirectory(backpack)
|
||||
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "uiinitialcreatesave.h"
|
||||
#include "ui/frame/uiframe.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/color.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
|
||||
#define UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||
|
||||
uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||
|
||||
static void uiInitialCreateSaveSelected(
|
||||
const uimenu_t *menu,
|
||||
const uint8_t index,
|
||||
const uimenuitem_t *item
|
||||
) {
|
||||
UI_INITIAL_CREATE_SAVE.create = index == UI_INITIAL_CREATE_SAVE_INDEX_YES;
|
||||
uiInitialCreateSaveClose();
|
||||
}
|
||||
|
||||
static void uiInitialCreateSaveClosed(const uimenu_t *menu) {
|
||||
if(UI_INITIAL_CREATE_SAVE.callback != NULL) {
|
||||
UI_INITIAL_CREATE_SAVE.callback(
|
||||
UI_INITIAL_CREATE_SAVE.create, UI_INITIAL_CREATE_SAVE.user
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t uiInitialCreateSaveInit(void) {
|
||||
memoryZero(&UI_INITIAL_CREATE_SAVE, sizeof(uiinitialcreatesave_t));
|
||||
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.message",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.text,
|
||||
UI_INITIAL_CREATE_SAVE_TEXT_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.yes",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.yesLabel,
|
||||
UI_INITIAL_CREATE_SAVE_LABEL_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.create_save.no",
|
||||
0,
|
||||
UI_INITIAL_CREATE_SAVE.noLabel,
|
||||
UI_INITIAL_CREATE_SAVE_LABEL_MAX
|
||||
));
|
||||
|
||||
MENU_BEGIN(
|
||||
&UI_INITIAL_CREATE_SAVE.menu, UI_INITIAL_CREATE_SAVE.items,
|
||||
uiInitialCreateSaveSelected, uiInitialCreateSaveClosed, NULL
|
||||
);
|
||||
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.yesLabel);
|
||||
MENU_BUTTON(UI_INITIAL_CREATE_SAVE.noLabel);
|
||||
|
||||
MENU_END(UI_INITIAL_CREATE_SAVE.items, menuIndex);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t uiInitialCreateSaveDraw(void) {
|
||||
if(!uiMenuIsActive(&UI_INITIAL_CREATE_SAVE.menu)) errorOk();
|
||||
|
||||
spritebatchsprite_t backdropSprite = {
|
||||
.min = { 0.0f, 0.0f, 0.0f },
|
||||
.max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f },
|
||||
.uvMin = { 0.0f, 0.0f },
|
||||
.uvMax = { 1.0f, 1.0f }
|
||||
};
|
||||
shadermaterial_t backdropMaterial = {
|
||||
.unlit = {
|
||||
.color = UI_INITIAL_CREATE_SAVE_BACKDROP_COLOR,
|
||||
.texture = &TEXTURE_WHITE
|
||||
}
|
||||
};
|
||||
errorChain(
|
||||
spriteBatchBuffer(&backdropSprite, 1, &SHADER_UNLIT, backdropMaterial)
|
||||
);
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
int32_t textW, textH;
|
||||
textMeasure(UI_INITIAL_CREATE_SAVE.text, &FONT_DEFAULT, &textW, &textH);
|
||||
|
||||
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
float_t width = mathMax(
|
||||
(float_t)textW + (UI_FRAME_START_X * 2), UI_INITIAL_CREATE_SAVE_MIN_WIDTH
|
||||
);
|
||||
float_t height = (UI_FRAME_START_Y * 2) + rowHeight + UI_FRAME_PADDING_Y +
|
||||
rowHeight;
|
||||
float_t x = (float_t)SCREEN.scanX + ((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||
float_t y = (float_t)SCREEN.scanY + ((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||
|
||||
errorChain(uiFrameDraw(x, y, width, height));
|
||||
|
||||
float_t contentX = x + UI_FRAME_START_X;
|
||||
float_t contentY = y + UI_FRAME_START_Y;
|
||||
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||
|
||||
errorChain(textDraw(
|
||||
contentX, contentY, UI_INITIAL_CREATE_SAVE.text, COLOR_WHITE, &FONT_DEFAULT
|
||||
));
|
||||
|
||||
float_t buttonsY = contentY + rowHeight + UI_FRAME_PADDING_Y;
|
||||
errorChain(uiMenuDraw(
|
||||
&UI_INITIAL_CREATE_SAVE.menu, contentX, buttonsY, contentWidth, rowHeight
|
||||
));
|
||||
|
||||
errorChain(spriteBatchFlush());
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t uiInitialCreateSaveIsOpen(void) {
|
||||
return uiMenuIsActive(&UI_INITIAL_CREATE_SAVE.menu);
|
||||
}
|
||||
|
||||
void uiInitialCreateSaveOpen(
|
||||
uiinitialcreatesavecallback_t callback, void *user
|
||||
) {
|
||||
UI_INITIAL_CREATE_SAVE.callback = callback;
|
||||
UI_INITIAL_CREATE_SAVE.user = user;
|
||||
UI_INITIAL_CREATE_SAVE.create = false;
|
||||
uiMenuOpen(&UI_INITIAL_CREATE_SAVE.menu);
|
||||
}
|
||||
|
||||
void uiInitialCreateSaveClose(void) {
|
||||
uiMenuClose(&UI_INITIAL_CREATE_SAVE.menu);
|
||||
}
|
||||
|
||||
errorret_t uiInitialCreateSaveDispose(void) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "ui/widget/uimenu.h"
|
||||
|
||||
#define UI_INITIAL_CREATE_SAVE_INDEX_YES 0
|
||||
#define UI_INITIAL_CREATE_SAVE_INDEX_NO 1
|
||||
#define UI_INITIAL_CREATE_SAVE_ITEM_COUNT 2
|
||||
#define UI_INITIAL_CREATE_SAVE_MIN_WIDTH 160.0f
|
||||
#define UI_INITIAL_CREATE_SAVE_TEXT_MAX 256
|
||||
#define UI_INITIAL_CREATE_SAVE_LABEL_MAX 32
|
||||
|
||||
/**
|
||||
* Callback invoked once the create-save modal is dismissed.
|
||||
*
|
||||
* @param create True if Yes was selected, false if No was selected or
|
||||
* the dialog was backed out of.
|
||||
* @param user Arbitrary pointer passed to uiInitialCreateSaveOpen.
|
||||
*/
|
||||
typedef void (*uiinitialcreatesavecallback_t)(const bool_t create, void *user);
|
||||
|
||||
typedef struct {
|
||||
uimenu_t menu;
|
||||
uimenuitem_t items[UI_INITIAL_CREATE_SAVE_ITEM_COUNT];
|
||||
uiinitialcreatesavecallback_t callback;
|
||||
void *user;
|
||||
bool_t create;
|
||||
char_t text[UI_INITIAL_CREATE_SAVE_TEXT_MAX];
|
||||
char_t yesLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
|
||||
char_t noLabel[UI_INITIAL_CREATE_SAVE_LABEL_MAX];
|
||||
} uiinitialcreatesave_t;
|
||||
|
||||
extern uiinitialcreatesave_t UI_INITIAL_CREATE_SAVE;
|
||||
|
||||
/**
|
||||
* Initializes the create-save modal.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialCreateSaveInit(void);
|
||||
|
||||
/**
|
||||
* Draws the create-save modal: a semi-transparent black backdrop covering
|
||||
* the whole screen, then its own centered frame with a fixed message and
|
||||
* Yes/No buttons. No-op when not open.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialCreateSaveDraw(void);
|
||||
|
||||
/**
|
||||
* Returns true when the modal is currently open.
|
||||
*
|
||||
* @return True if open.
|
||||
*/
|
||||
bool_t uiInitialCreateSaveIsOpen(void);
|
||||
|
||||
/**
|
||||
* Opens the create-save modal. callback is invoked exactly once with the
|
||||
* result, whether dismissed by selecting a button or backing out.
|
||||
*
|
||||
* @param callback Called with the result once the modal closes. May be
|
||||
* NULL.
|
||||
* @param user Arbitrary pointer passed through to callback.
|
||||
*/
|
||||
void uiInitialCreateSaveOpen(uiinitialcreatesavecallback_t callback, void *user);
|
||||
|
||||
/**
|
||||
* Closes the modal. No-op when already closed.
|
||||
*/
|
||||
void uiInitialCreateSaveClose(void);
|
||||
|
||||
/**
|
||||
* Disposes of the modal.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialCreateSaveDispose(void);
|
||||
@@ -1,146 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "uiinitialnocard.h"
|
||||
#include "ui/frame/uiframe.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "display/text/text.h"
|
||||
#include "display/color.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/texture/texture.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "locale/localemanager.h"
|
||||
#include "asset/loader/locale/assetlocaleloader.h"
|
||||
|
||||
#define UI_INITIAL_NO_CARD_BACKDROP_COLOR color4b(0, 0, 0, 160)
|
||||
|
||||
uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||
|
||||
static void uiInitialNoCardSelected(
|
||||
const uimenu_t *menu,
|
||||
const uint8_t index,
|
||||
const uimenuitem_t *item
|
||||
) {
|
||||
UI_INITIAL_NO_CARD.retry = index == UI_INITIAL_NO_CARD_INDEX_RETRY;
|
||||
uiInitialNoCardClose();
|
||||
}
|
||||
|
||||
static void uiInitialNoCardClosed(const uimenu_t *menu) {
|
||||
if(UI_INITIAL_NO_CARD.callback != NULL) {
|
||||
UI_INITIAL_NO_CARD.callback(UI_INITIAL_NO_CARD.retry, UI_INITIAL_NO_CARD.user);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t uiInitialNoCardInit(void) {
|
||||
memoryZero(&UI_INITIAL_NO_CARD, sizeof(uiinitialnocard_t));
|
||||
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.message",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.text,
|
||||
UI_INITIAL_NO_CARD_TEXT_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.retry",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.retryLabel,
|
||||
UI_INITIAL_NO_CARD_LABEL_MAX
|
||||
));
|
||||
errorChain(assetLocaleGetString(
|
||||
&LOCALE.entry->data.locale,
|
||||
"ui.initial.no_card.continue",
|
||||
0,
|
||||
UI_INITIAL_NO_CARD.continueLabel,
|
||||
UI_INITIAL_NO_CARD_LABEL_MAX
|
||||
));
|
||||
|
||||
MENU_BEGIN(
|
||||
&UI_INITIAL_NO_CARD.menu, UI_INITIAL_NO_CARD.items,
|
||||
uiInitialNoCardSelected, uiInitialNoCardClosed, NULL
|
||||
);
|
||||
MENU_BUTTON(UI_INITIAL_NO_CARD.retryLabel);
|
||||
MENU_BUTTON(UI_INITIAL_NO_CARD.continueLabel);
|
||||
|
||||
MENU_END(UI_INITIAL_NO_CARD.items, menuIndex);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t uiInitialNoCardDraw(void) {
|
||||
if(!uiMenuIsActive(&UI_INITIAL_NO_CARD.menu)) errorOk();
|
||||
|
||||
spritebatchsprite_t backdropSprite = {
|
||||
.min = { 0.0f, 0.0f, 0.0f },
|
||||
.max = { (float_t)SCREEN.width, (float_t)SCREEN.height, 0.0f },
|
||||
.uvMin = { 0.0f, 0.0f },
|
||||
.uvMax = { 1.0f, 1.0f }
|
||||
};
|
||||
shadermaterial_t backdropMaterial = {
|
||||
.unlit = {
|
||||
.color = UI_INITIAL_NO_CARD_BACKDROP_COLOR,
|
||||
.texture = &TEXTURE_WHITE
|
||||
}
|
||||
};
|
||||
errorChain(
|
||||
spriteBatchBuffer(&backdropSprite, 1, &SHADER_UNLIT, backdropMaterial)
|
||||
);
|
||||
errorChain(spriteBatchFlush());
|
||||
|
||||
int32_t textW, textH;
|
||||
textMeasure(UI_INITIAL_NO_CARD.text, &FONT_DEFAULT, &textW, &textH);
|
||||
|
||||
float_t rowHeight = (float_t)FONT_DEFAULT.tileset->tileHeight;
|
||||
float_t width = mathMax(
|
||||
(float_t)textW + (UI_FRAME_START_X * 2), UI_INITIAL_NO_CARD_MIN_WIDTH
|
||||
);
|
||||
float_t height = (UI_FRAME_START_Y * 2) + (float_t)textH +
|
||||
UI_FRAME_PADDING_Y + rowHeight;
|
||||
float_t x = (float_t)SCREEN.scanX + ((float_t)SCREEN.scanWidth - width) * 0.5f;
|
||||
float_t y = (float_t)SCREEN.scanY + ((float_t)SCREEN.scanHeight - height) * 0.5f;
|
||||
|
||||
errorChain(uiFrameDraw(x, y, width, height));
|
||||
|
||||
float_t contentX = x + UI_FRAME_START_X;
|
||||
float_t contentY = y + UI_FRAME_START_Y;
|
||||
float_t contentWidth = width - (UI_FRAME_START_X * 2);
|
||||
|
||||
errorChain(textDraw(
|
||||
contentX, contentY, UI_INITIAL_NO_CARD.text, COLOR_WHITE, &FONT_DEFAULT
|
||||
));
|
||||
|
||||
float_t buttonsY = contentY + (float_t)textH + UI_FRAME_PADDING_Y;
|
||||
errorChain(uiMenuDraw(
|
||||
&UI_INITIAL_NO_CARD.menu, contentX, buttonsY, contentWidth, rowHeight
|
||||
));
|
||||
|
||||
errorChain(spriteBatchFlush());
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t uiInitialNoCardIsOpen(void) {
|
||||
return uiMenuIsActive(&UI_INITIAL_NO_CARD.menu);
|
||||
}
|
||||
|
||||
void uiInitialNoCardOpen(uiinitialnocardcallback_t callback, void *user) {
|
||||
UI_INITIAL_NO_CARD.callback = callback;
|
||||
UI_INITIAL_NO_CARD.user = user;
|
||||
UI_INITIAL_NO_CARD.retry = false;
|
||||
uiMenuOpen(&UI_INITIAL_NO_CARD.menu);
|
||||
}
|
||||
|
||||
void uiInitialNoCardClose(void) {
|
||||
uiMenuClose(&UI_INITIAL_NO_CARD.menu);
|
||||
}
|
||||
|
||||
errorret_t uiInitialNoCardDispose(void) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "ui/widget/uimenu.h"
|
||||
|
||||
#define UI_INITIAL_NO_CARD_INDEX_RETRY 0
|
||||
#define UI_INITIAL_NO_CARD_INDEX_CONTINUE 1
|
||||
#define UI_INITIAL_NO_CARD_ITEM_COUNT 2
|
||||
#define UI_INITIAL_NO_CARD_MIN_WIDTH 220.0f
|
||||
#define UI_INITIAL_NO_CARD_TEXT_MAX 256
|
||||
#define UI_INITIAL_NO_CARD_LABEL_MAX 32
|
||||
|
||||
/**
|
||||
* Callback invoked once the no-save-device modal is dismissed.
|
||||
*
|
||||
* @param retry True if Retry was selected, false if Continue Anyway was.
|
||||
* @param user Arbitrary pointer passed to uiInitialNoCardOpen.
|
||||
*/
|
||||
typedef void (*uiinitialnocardcallback_t)(const bool_t retry, void *user);
|
||||
|
||||
typedef struct {
|
||||
uimenu_t menu;
|
||||
uimenuitem_t items[UI_INITIAL_NO_CARD_ITEM_COUNT];
|
||||
uiinitialnocardcallback_t callback;
|
||||
void *user;
|
||||
bool_t retry;
|
||||
char_t text[UI_INITIAL_NO_CARD_TEXT_MAX];
|
||||
char_t retryLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
|
||||
char_t continueLabel[UI_INITIAL_NO_CARD_LABEL_MAX];
|
||||
} uiinitialnocard_t;
|
||||
|
||||
extern uiinitialnocard_t UI_INITIAL_NO_CARD;
|
||||
|
||||
/**
|
||||
* Initializes the no-save-device modal.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialNoCardInit(void);
|
||||
|
||||
/**
|
||||
* Draws the no-save-device modal: a semi-transparent black backdrop
|
||||
* covering the whole screen, then its own centered frame with a fixed
|
||||
* message and Retry/Continue Anyway buttons. No-op when not open.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialNoCardDraw(void);
|
||||
|
||||
/**
|
||||
* Returns true when the modal is currently open.
|
||||
*
|
||||
* @return True if open.
|
||||
*/
|
||||
bool_t uiInitialNoCardIsOpen(void);
|
||||
|
||||
/**
|
||||
* Opens the no-save-device modal. callback is invoked exactly once with
|
||||
* the result, whether dismissed by selecting a button or backing out.
|
||||
*
|
||||
* @param callback Called with the result once the modal closes. May be
|
||||
* NULL.
|
||||
* @param user Arbitrary pointer passed through to callback.
|
||||
*/
|
||||
void uiInitialNoCardOpen(uiinitialnocardcallback_t callback, void *user);
|
||||
|
||||
/**
|
||||
* Closes the modal. No-op when already closed.
|
||||
*/
|
||||
void uiInitialNoCardClose(void);
|
||||
|
||||
/**
|
||||
* Disposes of the modal.
|
||||
*
|
||||
* @return Any error that occurs.
|
||||
*/
|
||||
errorret_t uiInitialNoCardDispose(void);
|
||||
+38
-33
@@ -20,80 +20,76 @@
|
||||
#include "ui/frame/battle/uibattlehud.h"
|
||||
#include "ui/frame/backpack/uibackpack.h"
|
||||
#include "ui/frame/uiconfirm.h"
|
||||
#include "ui/frame/initial/uiinitialnocard.h"
|
||||
#include "ui/frame/initial/uiinitialcreatesave.h"
|
||||
#include "ui/rpg/textbox/uitextboxmain.h"
|
||||
#include "ui/rpg/textbox/uitextboxminilist.h"
|
||||
#include "ui/rpg/uiemoji.h"
|
||||
|
||||
uielement_t UI_ELEMENTS[] = {
|
||||
// ===========================================================================
|
||||
// Non-Rendered Components, just needs init and deinit
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiFrameInit,
|
||||
.dispose = uiFrameDispose
|
||||
},
|
||||
|
||||
// Fullbox under: above scene, below system UI.
|
||||
{
|
||||
.init = uiFullboxUnderInit,
|
||||
.update = uiFullboxUnderUpdate,
|
||||
.draw = uiFullboxUnderDraw
|
||||
},
|
||||
|
||||
// in world stuff
|
||||
// ===========================================================================
|
||||
// In-World Gameplay Components, sits below all UI but above gameplay.
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiEmojiInit,
|
||||
.update = uiEmojiUpdate,
|
||||
.draw = uiEmojiDraw,
|
||||
.dispose = uiEmojiDispose
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Low rendered components, usually gameplay non-world components.
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiFullboxUnderInit,
|
||||
.update = uiFullboxUnderUpdate,
|
||||
.draw = uiFullboxUnderDraw
|
||||
},
|
||||
|
||||
// Ingame menus
|
||||
// ===========================================================================
|
||||
// Most menus and non-gameplay components. Hierarchy is very important.
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiGameMenuInit,
|
||||
.draw = uiGameMenuDraw,
|
||||
.dispose = uiGameMenuDispose
|
||||
},
|
||||
|
||||
{
|
||||
.init = uiMainMenuInit,
|
||||
.update = uiMainMenuUpdate,
|
||||
.draw = uiMainMenuDraw,
|
||||
.dispose = uiMainMenuDispose
|
||||
},
|
||||
|
||||
{ .init = uiBattleHudInit, .draw = uiBattleHudDraw },
|
||||
|
||||
{
|
||||
.init = uiBattleHudInit,
|
||||
.draw = uiBattleHudDraw
|
||||
},
|
||||
{
|
||||
.init = uiBattleMenuInit,
|
||||
.update = uiBattleMenuUpdate,
|
||||
.draw = uiBattleMenuDraw
|
||||
},
|
||||
|
||||
{
|
||||
.init = uiBackpackInit,
|
||||
.draw = uiBackpackDraw,
|
||||
.dispose = uiBackpackDispose
|
||||
},
|
||||
|
||||
// Text stuffs
|
||||
// ===========================================================================
|
||||
// Modals, Popups and Dialogs, above most other UI things.
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiConfirmInit,
|
||||
.draw = uiConfirmDraw,
|
||||
.dispose = uiConfirmDispose
|
||||
},
|
||||
|
||||
{
|
||||
.init = uiInitialNoCardInit,
|
||||
.draw = uiInitialNoCardDraw,
|
||||
.dispose = uiInitialNoCardDispose
|
||||
},
|
||||
|
||||
{
|
||||
.init = uiInitialCreateSaveInit,
|
||||
.draw = uiInitialCreateSaveDraw,
|
||||
.dispose = uiInitialCreateSaveDispose
|
||||
},
|
||||
|
||||
{
|
||||
.init = uiTextboxMainInit,
|
||||
.update = uiTextboxMainUpdate,
|
||||
@@ -106,13 +102,14 @@ uielement_t UI_ELEMENTS[] = {
|
||||
.draw = uiTextboxMiniListDraw
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Overlayed components, can even outdraw all UI things.
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiTransitionInit,
|
||||
.update = uiTransitionUpdate,
|
||||
.draw = uiTransitionDraw
|
||||
},
|
||||
|
||||
// Fullbox over: above absolutely everything.
|
||||
{
|
||||
.init = uiFullboxOverInit,
|
||||
.update = uiFullboxOverUpdate,
|
||||
@@ -125,15 +122,23 @@ uielement_t UI_ELEMENTS[] = {
|
||||
.draw = uiLoadingDraw
|
||||
},
|
||||
|
||||
// ===========================================================================
|
||||
// Important rendering components, lives outside the primary game engine
|
||||
// ===========================================================================
|
||||
{
|
||||
.init = uiCropInit,
|
||||
.draw = uiCropDraw
|
||||
},
|
||||
|
||||
// Debug items
|
||||
// ===========================================================================
|
||||
// Debug components, disregards everything else.
|
||||
// ===========================================================================
|
||||
{ .draw = uiConsoleDraw },
|
||||
{ .draw = uiFPSDraw },
|
||||
{ .draw = uiPlayerPosDraw },
|
||||
|
||||
{ 0 } // Null terminator
|
||||
// ===========================================================================
|
||||
// Null terminator
|
||||
// ===========================================================================
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
@@ -16,5 +16,7 @@ add_subdirectory(input)
|
||||
if(DUSK_NETWORK)
|
||||
add_subdirectory(network)
|
||||
endif()
|
||||
add_subdirectory(save)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(util)
|
||||
@@ -5,6 +5,5 @@
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
uiinitialnocard.c
|
||||
uiinitialcreatesave.c
|
||||
savedevicelinux.c
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/savedevice.h"
|
||||
#include "save/savedevicelinux.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/mkdirp.h"
|
||||
|
||||
errorret_t saveDeviceLinuxInit(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
|
||||
device->state = SAVE_DEVICE_STATE_UNKNOWN;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeviceLinuxUpdate(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertIsMainThread("Invalid thread");
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveDeviceLinuxCheckAvailability(savedevice_t *device) {
|
||||
assertNotNull(device, "device cannot be null");
|
||||
assertIsMainThread("Invalid thread");
|
||||
assertTrue(
|
||||
device->state == SAVE_DEVICE_STATE_CHECKING_AVAILABILITY,
|
||||
"Device state incorrect?"
|
||||
);
|
||||
|
||||
// Mkdirp the save directory.
|
||||
int mkdirpResult = mkdirp(SAVE_DEVICE_LINUX_DIRECTORY, 0700);
|
||||
if(mkdirpResult != 0) {
|
||||
device->state = SAVE_DEVICE_STATE_UNAVAILABLE;
|
||||
device->reasonKey = "save.linux.mkdirp_failed";
|
||||
return saveDeviceFireCallback(device);
|
||||
}
|
||||
|
||||
// The save device is available.
|
||||
device->state = SAVE_DEVICE_STATE_AVAILABLE;
|
||||
device->reasonKey = NULL;
|
||||
saveDeviceFireCallback(device);
|
||||
}
|
||||
|
||||
errorret_t saveDeviceLinuxDispose(savedevice_t *device) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
|
||||
#define SAVE_DEVICE_LINUX_DIRECTORY "~/.dusk/saves"
|
||||
#define SAVE_DEVICE_LINUX_FILE "~/.dusk/saves/save.dat"
|
||||
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} savedeviceplatform_t;
|
||||
|
||||
typedef struct savedevice_s savedevice_t;
|
||||
|
||||
/**
|
||||
* Initializes the save device platform.
|
||||
*
|
||||
* @param device The save device platform to initialize.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceLinuxInit(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Updates the save device platform.
|
||||
*
|
||||
* @param device The save device platform to update.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceLinuxUpdate(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Requests the device to check its availability, this will call the callback
|
||||
* whence completed.
|
||||
*
|
||||
* @param device The save device platform to check availability.
|
||||
*/
|
||||
void saveDeviceLinuxCheckAvailability(savedevice_t *device);
|
||||
|
||||
/**
|
||||
* Disposes of the save device platform.
|
||||
*
|
||||
* @param device The save device platform to dispose.
|
||||
* @return Error state if any.
|
||||
*/
|
||||
errorret_t saveDeviceLinuxDispose(savedevice_t *device);
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "savedevicelinux.h"
|
||||
|
||||
#define SAVE_DEVICE_COUNT 1
|
||||
|
||||
#define saveDevicePlatformInit saveDeviceLinuxInit
|
||||
#define saveDevicePlatformUpdate saveDeviceLinuxUpdate
|
||||
#define saveDeviceCheckAvailabilityPlatform saveDeviceLinuxCheckAvailability
|
||||
#define saveDevicePlatformDispose saveDeviceLinuxDispose
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
mkdirp.c
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "util/mkdirp.h"
|
||||
#include "util/string.h"
|
||||
#include "assert/assert.h"
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
int_t mkdirp(const char_t *path, const int_t mode) {
|
||||
assertNotNull(path, "path cannot be null");
|
||||
assertStrLenMax(path, PATH_MAX, "path is too long");
|
||||
|
||||
char_t buffer[PATH_MAX];
|
||||
stringCopy(buffer, path, sizeof(buffer) - 1);
|
||||
|
||||
size_t len = strlen(buffer);
|
||||
if(len == 0) return -1;
|
||||
|
||||
// Strip a trailing slash so the loop below doesn't try to create the same
|
||||
// directory twice.
|
||||
if(buffer[len - 1] == '/') buffer[len - 1] = '\0';
|
||||
|
||||
for(char_t *p = buffer + 1; *p != '\0'; p++) {
|
||||
if(*p != '/') continue;
|
||||
|
||||
*p = '\0';
|
||||
if(mkdir(buffer, mode) != 0 && errno != EEXIST) return -1;
|
||||
*p = '/';
|
||||
}
|
||||
|
||||
if(mkdir(buffer, mode) != 0 && errno != EEXIST) return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
/**
|
||||
* Recursively creates a directory and any missing parent directories, similar
|
||||
* to the shell command `mkdir -p`. If the directory already exists, this is
|
||||
* considered a success.
|
||||
*
|
||||
* @param path The path of the directory to create.
|
||||
* @param mode The permissions to create the directories with.
|
||||
* @return 0 on success, -1 on failure (errno will be set).
|
||||
*/
|
||||
int_t mkdirp(const char_t *path, const int_t mode);
|
||||
Reference in New Issue
Block a user