Split device settings out of the per-slot save file

Deadzone (and future prefs like locale) now live in their own
settingsfile_t/settings.c, loaded eagerly at boot and saved immediately
on Apply, instead of inside savefile_t - a setting shouldn't reset or
diverge just because the player is on a different save slot, and this
also fixes settings changes not actually reaching disk until the next
full game Save.

PSP settings use a new plain sceIo path rather than sceUtilitySavedata,
since that dialog would flash its native icon on every settings tweak.
GameCube reuses the save system's existing memory card mount rather than
mounting it twice (settingsInit() now runs after saveInit()).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 13:54:15 -05:00
parent 4d95415232
commit 7f7be39230
33 changed files with 1398 additions and 37 deletions
+2
View File
@@ -8,4 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
savelinux.c
savestreamlinux.c
settingslinux.c
settingsstreamlinux.c
)
+29
View File
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "save/settings.h"
#include "util/string.h"
#include <sys/stat.h>
#include <errno.h>
errorret_t settingsInitLinux(void) {
stringCopy(
SETTINGS.platform.path, SETTINGS_LINUX_PATH, SETTINGS_LINUX_PATH_MAX
);
if(mkdir(SETTINGS.platform.path, 0755) != 0 && errno != EEXIST) {
errorThrow(
"Failed to create settings directory: %s", SETTINGS.platform.path
);
}
errorOk();
}
errorret_t settingsDisposeLinux(void) {
errorOk();
}
+37
View File
@@ -0,0 +1,37 @@
/**
* 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 "save/settingsfile.h"
#define SETTINGS_LINUX_PATH_MAX FILENAME_MAX
#define SETTINGS_LINUX_FILE_FORMAT "%s/settings.dat"
#ifndef SETTINGS_LINUX_PATH
#define SETTINGS_LINUX_PATH "./saves"
#endif
typedef struct {
char_t path[SETTINGS_LINUX_PATH_MAX];
} settingslinux_t;
/**
* Initializes the settings system on Linux - ensures the settings
* directory exists, independent of whether the game-save system (see
* savelinux.h) has been initialized.
*
* @return An error code if initialization fails.
*/
errorret_t settingsInitLinux(void);
/**
* Disposes of the settings system on Linux.
*
* @return An error code if disposal fails.
*/
errorret_t settingsDisposeLinux(void);
+29
View File
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "save/settingslinux.h"
#include "save/settingsstreamlinux.h"
typedef settingslinux_t settingsplatform_t;
typedef settingsstreamlinux_t settingsplatformstream_t;
#define settingsInitPlatform settingsInitLinux
#define settingsDisposePlatform settingsDisposeLinux
#define settingsStreamOpenReadPlatform(stream) \
settingsStreamOpenReadLinux(&(stream)->platform, &(stream)->found)
#define settingsStreamOpenWritePlatform(stream) \
settingsStreamOpenWriteLinux(&(stream)->platform)
#define settingsStreamClosePlatform(stream) \
settingsStreamCloseLinux(&(stream)->platform)
#define settingsStreamReadBytesPlatform(stream, buf, len) \
settingsStreamReadBytesLinux(&(stream)->platform, buf, len)
#define settingsStreamWriteBytesPlatform(stream, buf, len) \
settingsStreamWriteBytesLinux(&(stream)->platform, buf, len)
#define settingsStreamSeekPlatform(stream, pos) \
settingsStreamSeekLinux(&(stream)->platform, pos)
+68
View File
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "save/settings.h"
#include "save/settingsstreamlinux.h"
#include "util/string.h"
static void _settingsStreamGetPath(char_t *out, const size_t max) {
snprintf(out, max, SETTINGS_LINUX_FILE_FORMAT, SETTINGS.platform.path);
}
errorret_t settingsStreamOpenReadLinux(
settingsstreamlinux_t *p, bool_t *found
) {
char_t path[SETTINGS_LINUX_PATH_MAX];
_settingsStreamGetPath(path, SETTINGS_LINUX_PATH_MAX);
p->file = fopen(path, "rb");
*found = (p->file != NULL);
errorOk();
}
errorret_t settingsStreamOpenWriteLinux(settingsstreamlinux_t *p) {
char_t path[SETTINGS_LINUX_PATH_MAX];
_settingsStreamGetPath(path, SETTINGS_LINUX_PATH_MAX);
p->file = fopen(path, "wb");
if(!p->file) {
errorThrow("Failed to open settings file for writing");
}
errorOk();
}
void settingsStreamCloseLinux(settingsstreamlinux_t *p) {
if(p->file) {
fclose(p->file);
p->file = NULL;
}
}
errorret_t settingsStreamReadBytesLinux(
settingsstreamlinux_t *p, void *buf, const size_t len
) {
if(fread(buf, 1, len, p->file) != len) {
errorThrow("Unexpected end of settings file");
}
errorOk();
}
errorret_t settingsStreamWriteBytesLinux(
settingsstreamlinux_t *p, const void *buf, const size_t len
) {
if(fwrite(buf, 1, len, p->file) != len) {
errorThrow("Failed to write settings data");
}
errorOk();
}
errorret_t settingsStreamSeekLinux(settingsstreamlinux_t *p, const size_t pos) {
if(fseek(p->file, (long)pos, SEEK_SET) != 0) {
errorThrow("Failed to seek in settings file");
}
errorOk();
}
+74
View File
@@ -0,0 +1,74 @@
/**
* 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 <stdio.h>
#include <stddef.h>
typedef struct {
FILE *file;
} settingsstreamlinux_t;
/**
* Opens the settings file for reading.
*
* @param p Stream to initialize.
* @param found Set to true if the file exists, false if it does not.
* @return An error if the open fails for a reason other than missing file.
*/
errorret_t settingsStreamOpenReadLinux(
settingsstreamlinux_t *p, bool_t *found
);
/**
* Opens the settings file for writing, creating or truncating it.
*
* @param p Stream to initialize.
* @return An error if the file cannot be opened for writing.
*/
errorret_t settingsStreamOpenWriteLinux(settingsstreamlinux_t *p);
/**
* Closes the file handle held by the stream.
*
* @param p Stream to close.
*/
void settingsStreamCloseLinux(settingsstreamlinux_t *p);
/**
* Reads len bytes from the stream into buf.
*
* @param p Active stream.
* @param buf Destination buffer.
* @param len Number of bytes to read.
* @return An error if fewer than len bytes are available.
*/
errorret_t settingsStreamReadBytesLinux(
settingsstreamlinux_t *p, void *buf, const size_t len
);
/**
* Writes len bytes from buf into the stream.
*
* @param p Active stream.
* @param buf Source buffer.
* @param len Number of bytes to write.
* @return An error if the write fails.
*/
errorret_t settingsStreamWriteBytesLinux(
settingsstreamlinux_t *p, const void *buf, const size_t len
);
/**
* Seeks to an absolute byte position within the stream.
*
* @param p Active stream.
* @param pos Target byte offset from the start of the file.
* @return An error if the seek fails.
*/
errorret_t settingsStreamSeekLinux(settingsstreamlinux_t *p, const size_t pos);