forked from Ports/perfect_dark
port: rework config system
it is no longer required to specify all possible config keys in config.c, you can just use configGet/Set
This commit is contained in:
1 parent
dce1f5492f
commit
4fef14f9fc
3 files changed
+106
-90
No files matched your search
+2
-1
@@ -237,7 +237,8 @@ COMMON_CFLAGS := $(C_DEFINES) -DAVOID_UB=1 $(INCLUDES) $(TARGET_CFLAGS) -g \
|
||||
-Wno-tautological-compare \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-value \
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-variable \
|
||||
-Wno-format-truncation
|
||||
|
||||
CFLAGS := $(COMMON_CFLAGS) -Wno-pointer-sign
|
||||
CXXFLAGS := $(COMMON_CFLAGS) -Wno-unused-function
|
||||
|
||||
@@ -5,13 +5,21 @@
|
||||
#define CONFIG_FNAME "./pd.ini"
|
||||
|
||||
void configInit(void);
|
||||
|
||||
// loads config from file (path extensions such as ! apply)
|
||||
s32 configLoad(const char *fname);
|
||||
|
||||
// saves config to file (path extensions such as ! apply)
|
||||
s32 configSave(const char *fname);
|
||||
|
||||
// get value of a config variable or create a new one with specified default
|
||||
// key is a string of the form "SECTION.KEY"
|
||||
s32 configGetInt(const char *key, s32 defval);
|
||||
f32 configGetFloat(const char *key, f32 defval);
|
||||
const char *configGetString(const char *key, const char *defval);
|
||||
|
||||
// set value of a config variable, will create it if it doesn't exist
|
||||
// key is a string of the form "SECTION.KEY"
|
||||
void configSetInt(const char *key, s32 val);
|
||||
void configSetFloat(const char *key, f32 val);
|
||||
void configSetString(const char *key, const char *val);
|
||||
+96
-89
@@ -1,5 +1,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <ctype.h>
|
||||
#include <PR/ultratypes.h>
|
||||
#include "fs.h"
|
||||
@@ -8,122 +9,97 @@
|
||||
|
||||
#define CONFIG_MAX_STR 512
|
||||
#define CONFIG_MAX_SECNAME 128
|
||||
|
||||
enum configtype {
|
||||
CONFIG_END,
|
||||
CONFIG_S32,
|
||||
CONFIG_F32,
|
||||
CONFIG_STR,
|
||||
};
|
||||
#define CONFIG_MAX_KEYNAME 256
|
||||
#define CONFIG_MAX_SETTINGS 128
|
||||
|
||||
struct configentry {
|
||||
const char *key;
|
||||
enum configtype type;
|
||||
union {
|
||||
char key[CONFIG_MAX_KEYNAME + 1];
|
||||
char strval[CONFIG_MAX_STR + 1];
|
||||
s32 s32val;
|
||||
f32 f32val;
|
||||
char strval[CONFIG_MAX_STR + 1];
|
||||
};
|
||||
} settings[] = {
|
||||
/* Video */
|
||||
{ "Video.DefaultFullscreen", CONFIG_S32, { .s32val = 0 } },
|
||||
{ "Video.DefaultWidth", CONFIG_S32, { .s32val = 640 } },
|
||||
{ "Video.DefaultHeight", CONFIG_S32, { .s32val = 480 } },
|
||||
{ "Video.VSync", CONFIG_S32, { .s32val = 1 } },
|
||||
{ "Video.FramerateLimit", CONFIG_S32, { .s32val = 0 } },
|
||||
{ "Video.FovY", CONFIG_F32, { .f32val = 60.f } },
|
||||
{ "Video.TextureFilter", CONFIG_S32, { .s32val = 1 } },
|
||||
{ "Video.MSAA", CONFIG_S32, { .s32val = 0 } },
|
||||
/* Audio */
|
||||
{ "Audio.BufferSize", CONFIG_S32, { .s32val = 512 } },
|
||||
{ "Audio.QueueLimit", CONFIG_S32, { .s32val = 8192 } },
|
||||
/* Input */
|
||||
{ "Input.MouseEnabled", CONFIG_S32, { .s32val = 1 } },
|
||||
{ "Input.MouseSpeedX", CONFIG_F32, { .f32val = 1.5f } },
|
||||
{ "Input.MouseSpeedY", CONFIG_F32, { .f32val = 1.5f } },
|
||||
{ "Input.LStickDeadzoneX", CONFIG_S32, { .s32val = 4096 } },
|
||||
{ "Input.LStickDeadzoneY", CONFIG_S32, { .s32val = 4096 } },
|
||||
{ "Input.LStickScaleX", CONFIG_F32, { .f32val = 1.f } },
|
||||
{ "Input.LStickScaleY", CONFIG_F32, { .f32val = 1.f } },
|
||||
{ "Input.RStickDeadzoneX", CONFIG_S32, { .s32val = 4096 } },
|
||||
{ "Input.RStickDeadzoneY", CONFIG_S32, { .s32val = 6144 } },
|
||||
{ "Input.RStickScaleX", CONFIG_F32, { .f32val = 1.f } },
|
||||
{ "Input.RStickScaleY", CONFIG_F32, { .f32val = 1.f } },
|
||||
{ "Input.SwapSticks", CONFIG_S32, { .s32val = 1 } },
|
||||
{ "Input.StickCButtons", CONFIG_S32, { .s32val = 1 } },
|
||||
{ "Input.RumbleScale", CONFIG_F32, { .f32val = 0.333f } },
|
||||
{ "Input.FirstGamepadNum", CONFIG_S32, { .s32val = 0 } },
|
||||
{ "Input.FakeGamepads", CONFIG_S32, { .s32val = 0 } },
|
||||
/* Game */
|
||||
{ "Game.BaseDir", CONFIG_STR, { .strval = "./data" } },
|
||||
{ "Game.MemorySize", CONFIG_S32, { .s32val = 16 } },
|
||||
{ NULL, CONFIG_END, { } },
|
||||
};
|
||||
s32 seclen;
|
||||
} settings[CONFIG_MAX_SETTINGS + 1];
|
||||
static s32 numSettings = 0;
|
||||
|
||||
static inline struct configentry *configFindEntry(const char *key)
|
||||
{
|
||||
for (struct configentry *cfg = settings; cfg->type; ++cfg) {
|
||||
if (!strcmp(key, cfg->key)) {
|
||||
return cfg;
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
if (!strncasecmp(settings[i].key, key, CONFIG_MAX_KEYNAME)) {
|
||||
return &settings[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline const char *configGetSection(const char *key)
|
||||
static inline struct configentry *configAddEntry(const char *key)
|
||||
{
|
||||
static char sec[CONFIG_MAX_SECNAME + 1];
|
||||
if (numSettings < CONFIG_MAX_SETTINGS) {
|
||||
struct configentry *cfg = &settings[numSettings++];
|
||||
strncpy(cfg->key, key, CONFIG_MAX_KEYNAME);
|
||||
const char *delim = strrchr(cfg->key, '.');
|
||||
cfg->seclen = delim ? (delim - cfg->key) : 0;
|
||||
return cfg;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the last dot in case we have a section with a '.'
|
||||
const char *dot = strrchr(key, '.');
|
||||
static inline struct configentry *configFindOrAddEntry(const char *key)
|
||||
{
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
if (!strncasecmp(settings[i].key, key, CONFIG_MAX_KEYNAME)) {
|
||||
return &settings[i];
|
||||
}
|
||||
}
|
||||
return configAddEntry(key);
|
||||
}
|
||||
|
||||
if (dot) {
|
||||
const u32 len = dot - key;
|
||||
if (len <= CONFIG_MAX_SECNAME) {
|
||||
memcpy(sec, key, len);
|
||||
sec[len] = '\0';
|
||||
static inline const char *configGetSection(char *sec, const struct configentry *cfg)
|
||||
{
|
||||
if (!cfg->seclen || cfg->seclen > CONFIG_MAX_SECNAME) {
|
||||
strncpy(sec, cfg->key, CONFIG_MAX_SECNAME);
|
||||
sec[CONFIG_MAX_SECNAME] = '\0';
|
||||
return sec;
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
memcpy(sec, cfg->key, cfg->seclen);
|
||||
sec[cfg->seclen] = '\0';
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
void configSetInt(const char *key, s32 val)
|
||||
{
|
||||
struct configentry *cfg = configFindEntry(key);
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->s32val = val;
|
||||
snprintf(cfg->strval, CONFIG_MAX_STR, "%d", val);
|
||||
}
|
||||
}
|
||||
|
||||
void configSetFloat(const char *key, f32 val)
|
||||
{
|
||||
struct configentry *cfg = configFindEntry(key);
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->f32val = val;
|
||||
snprintf(cfg->strval, CONFIG_MAX_STR, "%f", val);
|
||||
}
|
||||
}
|
||||
|
||||
void configSetString(const char *key, const char *val)
|
||||
{
|
||||
struct configentry *cfg = configFindEntry(key);
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
strncpy(cfg->strval, val, CONFIG_MAX_STR);
|
||||
cfg->strval[CONFIG_MAX_STR] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void configSetFromString(const char *key, const char *val)
|
||||
{
|
||||
struct configentry *cfg = configFindEntry(key);
|
||||
struct configentry *cfg = configFindOrAddEntry(key);
|
||||
if (cfg) {
|
||||
switch (cfg->type) {
|
||||
case CONFIG_S32: cfg->s32val = atoi(val); break;
|
||||
case CONFIG_F32: cfg->f32val = atof(val); break;
|
||||
case CONFIG_STR: strncpy(cfg->strval, val, CONFIG_MAX_STR); break;
|
||||
default: break;
|
||||
}
|
||||
strncpy(cfg->strval, val, CONFIG_MAX_STR);
|
||||
cfg->f32val = atof(val);
|
||||
cfg->s32val = atoi(val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +109,13 @@ s32 configGetInt(const char *key, s32 defval)
|
||||
if (cfg) {
|
||||
return cfg->s32val;
|
||||
}
|
||||
|
||||
cfg = configAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->s32val = defval;
|
||||
snprintf(cfg->strval, CONFIG_MAX_STR, "%d", defval);
|
||||
}
|
||||
|
||||
return defval;
|
||||
}
|
||||
|
||||
@@ -142,6 +125,13 @@ f32 configGetFloat(const char *key, f32 defval)
|
||||
if (cfg) {
|
||||
return cfg->f32val;
|
||||
}
|
||||
|
||||
cfg = configAddEntry(key);
|
||||
if (cfg) {
|
||||
cfg->f32val = defval;
|
||||
snprintf(cfg->strval, CONFIG_MAX_STR, "%f", defval);
|
||||
}
|
||||
|
||||
return defval;
|
||||
}
|
||||
|
||||
@@ -151,9 +141,31 @@ const char *configGetString(const char *key, const char *defval)
|
||||
if (cfg) {
|
||||
return cfg->strval;
|
||||
}
|
||||
|
||||
cfg = configAddEntry(key);
|
||||
if (cfg) {
|
||||
strncpy(cfg->strval, defval, CONFIG_MAX_STR);
|
||||
}
|
||||
|
||||
return defval;
|
||||
}
|
||||
|
||||
static s32 configCmp(const struct configentry *a, const struct configentry *b) {
|
||||
char tmpa[CONFIG_MAX_SECNAME + 1];
|
||||
char tmpb[CONFIG_MAX_SECNAME + 1];
|
||||
configGetSection(tmpa, a);
|
||||
configGetSection(tmpb, b);
|
||||
|
||||
// compare section names first
|
||||
const s32 seccmp = strncmp(tmpa, tmpb, CONFIG_MAX_SECNAME);
|
||||
if (seccmp) {
|
||||
return seccmp;
|
||||
}
|
||||
|
||||
// same section; sort keys by first letter
|
||||
return a->key[a->seclen + 1] - b->key[b->seclen + 1];
|
||||
}
|
||||
|
||||
s32 configSave(const char *fname)
|
||||
{
|
||||
FILE *f = fsFileOpenWrite(fname);
|
||||
@@ -161,24 +173,22 @@ s32 configSave(const char *fname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sort the config so that the sections appear in order
|
||||
qsort(settings, numSettings, sizeof(*settings), (void *)configCmp);
|
||||
|
||||
char tmpSec[CONFIG_MAX_SECNAME + 1] = { 0 };
|
||||
char curSec[CONFIG_MAX_SECNAME + 1] = { 0 };
|
||||
strncpy(curSec, configGetSection(settings[0].key), CONFIG_MAX_SECNAME);
|
||||
configGetSection(curSec, &settings[0]);
|
||||
fprintf(f, "[%s]\n", curSec);
|
||||
|
||||
for (struct configentry *cfg = settings; cfg->type; ++cfg) {
|
||||
const char *cfgSec = configGetSection(cfg->key);
|
||||
if (strncmp(curSec, cfgSec, CONFIG_MAX_SECNAME) != 0) {
|
||||
fprintf(f, "\n[%s]\n", cfgSec);
|
||||
strncpy(curSec, cfgSec, CONFIG_MAX_SECNAME);
|
||||
}
|
||||
|
||||
fprintf(f, "%s=", strrchr(cfg->key, '.') + 1);
|
||||
switch (cfg->type) {
|
||||
case CONFIG_S32: fprintf(f, "%d\n", cfg->s32val); break;
|
||||
case CONFIG_F32: fprintf(f, "%f\n", cfg->f32val); break;
|
||||
case CONFIG_STR: fprintf(f, "%s\n", cfg->strval); break;
|
||||
default: break;
|
||||
for (s32 i = 0; i < numSettings; ++i) {
|
||||
struct configentry *cfg = &settings[i];
|
||||
configGetSection(tmpSec, cfg);
|
||||
if (strncmp(curSec, tmpSec, CONFIG_MAX_SECNAME) != 0) {
|
||||
fprintf(f, "\n[%s]\n", tmpSec);
|
||||
strncpy(curSec, tmpSec, CONFIG_MAX_SECNAME);
|
||||
}
|
||||
fprintf(f, "%s=%s\n", cfg->key + cfg->seclen + 1, cfg->strval);
|
||||
}
|
||||
|
||||
fsFileClose(f);
|
||||
@@ -284,8 +294,5 @@ void configInit(void)
|
||||
{
|
||||
if (fsFileSize(CONFIG_FNAME) > 0) {
|
||||
configLoad(CONFIG_FNAME);
|
||||
} else {
|
||||
// config not found, save defaults
|
||||
configSave(CONFIG_FNAME);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user