refactor pt 1; getting each thing right and then will push to main
This commit is contained in:
+286
-257
@@ -1,276 +1,305 @@
|
||||
#include "settings.h"
|
||||
|
||||
static void _settings_setting_load(Settings* self, const std::string& line)
|
||||
{
|
||||
for (s32 i = 0; i < SETTINGS_COUNT; i++)
|
||||
{
|
||||
const auto& entry = SETTINGS_ENTRIES[i];
|
||||
const std::string& key = entry.key;
|
||||
void* target = (u8*)self + entry.offset;
|
||||
static void _settings_setting_load(Settings* self, const std::string& line) {
|
||||
for (int i = 0; i < SETTINGS_COUNT; i++) {
|
||||
const auto& entry = SETTINGS_ENTRIES[i];
|
||||
const std::string& key = entry.key;
|
||||
void* target = (u8*)self + entry.offset;
|
||||
|
||||
auto match_key = [&](const std::string& full) -> const char*
|
||||
{
|
||||
if (!line.starts_with(full))
|
||||
return nullptr;
|
||||
auto match_key = [&](const std::string& full) -> const char* {
|
||||
if (!line.starts_with(full))
|
||||
return nullptr;
|
||||
|
||||
size_t p = full.size();
|
||||
while (p < line.size() && std::isspace((u8)line[p])) ++p;
|
||||
if (p < line.size() && line[p] == '=')
|
||||
return line.c_str() + p + 1;
|
||||
return nullptr;
|
||||
};
|
||||
size_t p = full.size();
|
||||
while (p < line.size() && std::isspace((u8)line[p]))
|
||||
++p;
|
||||
if (p < line.size() && line[p] == '=')
|
||||
return line.c_str() + p + 1;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const char* value = nullptr;
|
||||
const char* value = nullptr;
|
||||
|
||||
switch (entry.type)
|
||||
{
|
||||
case TYPE_INT:
|
||||
if ((value = match_key(key))) { *(s32*)target = std::atoi(value); return; }
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if ((value = match_key(key))) { *(bool*)target = string_to_bool(value); return; }
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
if ((value = match_key(key))) { *(f32*)target = std::atof(value); return; }
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
if ((value = match_key(key))) { *(std::string*)target = value; return; }
|
||||
break;
|
||||
case TYPE_IVEC2:
|
||||
{
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
|
||||
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH:
|
||||
{
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "W"))) { v->x = std::atoi(value); return; }
|
||||
if ((value = match_key(key + "H"))) { v->y = std::atoi(value); return; }
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC2:
|
||||
{
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH:
|
||||
{
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "W"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "H"))) { v->y = std::atof(value); return; }
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC3:
|
||||
{
|
||||
vec3* v = (vec3*)target;
|
||||
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
|
||||
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC4:
|
||||
{
|
||||
vec4* v = (vec4*)target;
|
||||
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
|
||||
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
|
||||
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
|
||||
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
|
||||
}
|
||||
|
||||
std::string settings_path_get(void)
|
||||
{
|
||||
std::string filePath = preferences_path_get() + SETTINGS_PATH;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry)
|
||||
{
|
||||
u8* selfPointer = (u8*)self;
|
||||
std::string value;
|
||||
|
||||
switch (entry.type)
|
||||
{
|
||||
case TYPE_INT:
|
||||
value = std::format("{}", *(s32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
value = std::format("{}", *(bool*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
{
|
||||
const std::string data = *reinterpret_cast<const std::string*>(selfPointer + entry.offset);
|
||||
if (!data.empty())
|
||||
out << entry.key << "=" << data.c_str() << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2:
|
||||
{
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "X=" << data->x << "\n";
|
||||
out << entry.key << "Y=" << data->y << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH:
|
||||
{
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << data->x << "\n";
|
||||
out << entry.key << "H=" << data->y << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2:
|
||||
{
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "X=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH:
|
||||
{
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC3:
|
||||
{
|
||||
vec3* data = (vec3*)(selfPointer + entry.offset);
|
||||
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
|
||||
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
|
||||
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC4:
|
||||
{
|
||||
vec4* data = (vec4*)(selfPointer + entry.offset);
|
||||
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
|
||||
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
|
||||
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
|
||||
out << entry.key << "A=" << std::format(SETTINGS_FLOAT_FORMAT, data->a) << "\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_save(Settings* self)
|
||||
{
|
||||
const std::string path = settings_path_get();
|
||||
const std::filesystem::path filesystemPath(path);
|
||||
const std::filesystem::path directory = filesystemPath.parent_path();
|
||||
|
||||
if (!directory.empty())
|
||||
{
|
||||
std::error_code errorCode;
|
||||
std::filesystem::create_directories(directory, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
log_error(std::format(SETTINGS_DIRECTORY_ERROR, directory.string(), errorCode.message()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string data;
|
||||
if (std::filesystem::exists(filesystemPath))
|
||||
{
|
||||
if (std::ifstream in(path, std::ios::binary); in)
|
||||
data.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::filesystem::path temp = filesystemPath;
|
||||
temp += SETTINGS_TEMPORARY_EXTENSION;
|
||||
|
||||
std::ofstream out(temp, std::ios::binary | std::ios::trunc);
|
||||
if (!out)
|
||||
{
|
||||
log_error(std::format(SETTINGS_INIT_ERROR, temp.string()));
|
||||
switch (entry.type) {
|
||||
case TYPE_INT:
|
||||
if ((value = match_key(key))) {
|
||||
*(int*)target = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
|
||||
out << SETTINGS_SECTION << "\n";
|
||||
for (s32 i = 0; i < SETTINGS_COUNT; i++)
|
||||
_settings_setting_write(self, out, SETTINGS_ENTRIES[i]);
|
||||
|
||||
out << "\n" << SETTINGS_SECTION_IMGUI << "\n";
|
||||
out << data;
|
||||
|
||||
out.flush();
|
||||
|
||||
if (!out.good())
|
||||
{
|
||||
log_error(std::format(SETTINGS_SAVE_ERROR, temp.string()));
|
||||
}
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if ((value = match_key(key))) {
|
||||
*(bool*)target = string_to_bool(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
if ((value = match_key(key))) {
|
||||
*(f32*)target = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
if ((value = match_key(key))) {
|
||||
*(std::string*)target = value;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TYPE_IVEC2: {
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "X"))) {
|
||||
v->x = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "Y"))) {
|
||||
v->y = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH: {
|
||||
ivec2* v = (ivec2*)target;
|
||||
if ((value = match_key(key + "W"))) {
|
||||
v->x = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "H"))) {
|
||||
v->y = std::atoi(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC2: {
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "X"))) {
|
||||
v->x = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "Y"))) {
|
||||
v->y = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH: {
|
||||
vec2* v = (vec2*)target;
|
||||
if ((value = match_key(key + "W"))) {
|
||||
v->x = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "H"))) {
|
||||
v->y = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
};
|
||||
case TYPE_VEC3: {
|
||||
vec3* v = (vec3*)target;
|
||||
if ((value = match_key(key + "R"))) {
|
||||
v->x = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "G"))) {
|
||||
v->y = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "B"))) {
|
||||
v->z = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC4: {
|
||||
vec4* v = (vec4*)target;
|
||||
if ((value = match_key(key + "R"))) {
|
||||
v->x = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "G"))) {
|
||||
v->y = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "B"))) {
|
||||
v->z = std::atof(value);
|
||||
return;
|
||||
}
|
||||
if ((value = match_key(key + "A"))) {
|
||||
v->w = std::atof(value);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
|
||||
}
|
||||
|
||||
std::string settings_path_get(void) {
|
||||
std::string filePath = preferences_path_get() + SETTINGS_PATH;
|
||||
return filePath;
|
||||
}
|
||||
|
||||
static void _settings_setting_write(Settings* self, std::ostream& out, SettingsEntry entry) {
|
||||
u8* selfPointer = (u8*)self;
|
||||
std::string value;
|
||||
|
||||
switch (entry.type) {
|
||||
case TYPE_INT:
|
||||
value = std::format("{}", *(int*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
value = std::format("{}", *(bool*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
value = std::format("{:.3f}", *(f32*)(selfPointer + entry.offset));
|
||||
out << entry.key << "=" << value << "\n";
|
||||
break;
|
||||
case TYPE_STRING: {
|
||||
const std::string data = *reinterpret_cast<const std::string*>(selfPointer + entry.offset);
|
||||
if (!data.empty())
|
||||
out << entry.key << "=" << data.c_str() << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2: {
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "X=" << data->x << "\n";
|
||||
out << entry.key << "Y=" << data->y << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_IVEC2_WH: {
|
||||
ivec2* data = (ivec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << data->x << "\n";
|
||||
out << entry.key << "H=" << data->y << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2: {
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "X=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC2_WH: {
|
||||
vec2* data = (vec2*)(selfPointer + entry.offset);
|
||||
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
|
||||
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC3: {
|
||||
vec3* data = (vec3*)(selfPointer + entry.offset);
|
||||
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
|
||||
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
|
||||
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
|
||||
break;
|
||||
}
|
||||
case TYPE_VEC4: {
|
||||
vec4* data = (vec4*)(selfPointer + entry.offset);
|
||||
out << entry.key << "R=" << std::format(SETTINGS_FLOAT_FORMAT, data->r) << "\n";
|
||||
out << entry.key << "G=" << std::format(SETTINGS_FLOAT_FORMAT, data->g) << "\n";
|
||||
out << entry.key << "B=" << std::format(SETTINGS_FLOAT_FORMAT, data->b) << "\n";
|
||||
out << entry.key << "A=" << std::format(SETTINGS_FLOAT_FORMAT, data->a) << "\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void settings_save(Settings* self) {
|
||||
const std::string path = settings_path_get();
|
||||
const std::filesystem::path filesystemPath(path);
|
||||
const std::filesystem::path directory = filesystemPath.parent_path();
|
||||
|
||||
if (!directory.empty()) {
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
// Windows can block rename if target exists; try remove+rename
|
||||
std::filesystem::remove(filesystemPath, errorCode);
|
||||
errorCode = {};
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode)
|
||||
{
|
||||
log_error(std::format(SETTINGS_SAVE_FINALIZE_ERROR, filesystemPath.string(), errorCode.message()));
|
||||
std::filesystem::remove(temp);
|
||||
return;
|
||||
}
|
||||
std::filesystem::create_directories(directory, errorCode);
|
||||
if (errorCode) {
|
||||
log_error(std::format(SETTINGS_DIRECTORY_ERROR, directory.string(), errorCode.message()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_info(std::format(SETTINGS_SAVE_INFO, path));
|
||||
std::string data;
|
||||
if (std::filesystem::exists(filesystemPath)) {
|
||||
if (std::ifstream in(path, std::ios::binary); in)
|
||||
data.assign(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::filesystem::path temp = filesystemPath;
|
||||
temp += SETTINGS_TEMPORARY_EXTENSION;
|
||||
|
||||
std::ofstream out(temp, std::ios::binary | std::ios::trunc);
|
||||
if (!out) {
|
||||
log_error(std::format(SETTINGS_INIT_ERROR, temp.string()));
|
||||
return;
|
||||
}
|
||||
|
||||
out << SETTINGS_SECTION << "\n";
|
||||
for (int i = 0; i < SETTINGS_COUNT; i++)
|
||||
_settings_setting_write(self, out, SETTINGS_ENTRIES[i]);
|
||||
|
||||
out << "\n" << SETTINGS_SECTION_IMGUI << "\n";
|
||||
out << data;
|
||||
|
||||
out.flush();
|
||||
|
||||
if (!out.good()) {
|
||||
log_error(std::format(SETTINGS_SAVE_ERROR, temp.string()));
|
||||
return;
|
||||
}
|
||||
|
||||
out.close();
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode) {
|
||||
// Windows can block rename if target exists; try remove+rename
|
||||
std::filesystem::remove(filesystemPath, errorCode);
|
||||
errorCode = {};
|
||||
std::filesystem::rename(temp, filesystemPath, errorCode);
|
||||
if (errorCode) {
|
||||
log_error(std::format(SETTINGS_SAVE_FINALIZE_ERROR, filesystemPath.string(), errorCode.message()));
|
||||
std::filesystem::remove(temp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_info(std::format(SETTINGS_SAVE_INFO, path));
|
||||
}
|
||||
|
||||
void settings_init(Settings* self)
|
||||
{
|
||||
const std::string path = settings_path_get();
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
void settings_init(Settings* self) {
|
||||
const std::string path = settings_path_get();
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
|
||||
if (file)
|
||||
log_info(std::format(SETTINGS_INIT_INFO, path));
|
||||
else
|
||||
{
|
||||
log_warning(std::format(SETTINGS_INIT_WARNING, path));
|
||||
settings_save(self);
|
||||
std::ofstream out(path, std::ios::binary | std::ios::app);
|
||||
out << SETTINGS_IMGUI_DEFAULT;
|
||||
out.flush();
|
||||
out.close();
|
||||
file.open(path, std::ios::binary);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
bool inSettingsSection = false;
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
if (line == SETTINGS_SECTION)
|
||||
{
|
||||
inSettingsSection = true;
|
||||
continue;
|
||||
}
|
||||
if (line.empty()) continue;
|
||||
if (line == SETTINGS_SECTION_IMGUI) break;
|
||||
if (inSettingsSection) _settings_setting_load(self, line);
|
||||
if (file)
|
||||
log_info(std::format(SETTINGS_INIT_INFO, path));
|
||||
else {
|
||||
log_warning(std::format(SETTINGS_INIT_WARNING, path));
|
||||
settings_save(self);
|
||||
std::ofstream out(path, std::ios::binary | std::ios::app);
|
||||
out << SETTINGS_IMGUI_DEFAULT;
|
||||
out.flush();
|
||||
out.close();
|
||||
file.open(path, std::ios::binary);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
bool inSettingsSection = false;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
if (line == SETTINGS_SECTION) {
|
||||
inSettingsSection = true;
|
||||
continue;
|
||||
}
|
||||
if (line.empty())
|
||||
continue;
|
||||
if (line == SETTINGS_SECTION_IMGUI)
|
||||
break;
|
||||
if (inSettingsSection)
|
||||
_settings_setting_load(self, line);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user