diff options
author | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
---|---|---|
committer | WlodekM <[email protected]> | 2024-06-16 10:35:45 +0300 |
commit | abef6da56913f1c55528103e60a50451a39628b1 (patch) | |
tree | b3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Options.c |
initial commit
Diffstat (limited to 'src/Options.c')
-rw-r--r-- | src/Options.c | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/Options.c b/src/Options.c new file mode 100644 index 0000000..d571a75 --- /dev/null +++ b/src/Options.c @@ -0,0 +1,227 @@ +#include "Options.h" +#include "String.h" +#include "ExtMath.h" +#include "Platform.h" +#include "Stream.h" +#include "Errors.h" +#include "Utils.h" +#include "Logger.h" +#include "PackedCol.h" + +struct StringsBuffer Options; +static struct StringsBuffer changedOpts; +cc_result Options_LoadResult; +static cc_bool savingPaused; +#if defined CC_BUILD_WEB || defined CC_BUILD_MOBILE || defined CC_BUILD_CONSOLE + #define OPTIONS_SAVE_IMMEDIATELY +#endif + +void Options_Free(void) { + StringsBuffer_Clear(&Options); + StringsBuffer_Clear(&changedOpts); +} + +static cc_bool HasChanged(const cc_string* key) { + cc_string entry; + int i; + + for (i = 0; i < changedOpts.count; i++) { + entry = StringsBuffer_UNSAFE_Get(&changedOpts, i); + if (String_CaselessEquals(&entry, key)) return true; + } + return false; +} + +static cc_bool Options_LoadFilter(const cc_string* entry) { + cc_string key, value; + String_UNSAFE_Separate(entry, '=', &key, &value); + return !HasChanged(&key); +} + +void Options_Load(void) { + /* Increase from max 512 to 2048 per entry */ + StringsBuffer_SetLengthBits(&Options, 11); + Options_LoadResult = EntryList_Load(&Options, "options-default.txt", '=', NULL); + Options_LoadResult = EntryList_Load(&Options, "options.txt", '=', NULL); +} + +void Options_Reload(void) { + cc_string entry, key, value; + int i; + + /* Reset all the unchanged options */ + for (i = Options.count - 1; i >= 0; i--) { + entry = StringsBuffer_UNSAFE_Get(&Options, i); + String_UNSAFE_Separate(&entry, '=', &key, &value); + + if (HasChanged(&key)) continue; + StringsBuffer_Remove(&Options, i); + } + /* Load only options which have not changed */ + Options_LoadResult = EntryList_Load(&Options, "options.txt", '=', Options_LoadFilter); +} + +static void SaveOptions(void) { + EntryList_Save(&Options, "options.txt"); + StringsBuffer_Clear(&changedOpts); +} + +void Options_SaveIfChanged(void) { + if (!changedOpts.count) return; + + Options_Reload(); + SaveOptions(); +} + +void Options_PauseSaving(void) { savingPaused = true; } + +void Options_ResumeSaving(void) { + savingPaused = false; +#if defined OPTIONS_SAVE_IMMEDIATELY + SaveOptions(); +#endif +} + + +cc_bool Options_UNSAFE_Get(const char* keyRaw, cc_string* value) { + int idx; + cc_string key = String_FromReadonly(keyRaw); + + *value = EntryList_UNSAFE_Get(&Options, &key, '='); + if (value->length) return true; + + /* Fallback to without '-' (e.g. "hacks-fly" to "fly") */ + /* Needed for some very old options.txt files */ + idx = String_IndexOf(&key, '-'); + if (idx == -1) return false; + key = String_UNSAFE_SubstringAt(&key, idx + 1); + + *value = EntryList_UNSAFE_Get(&Options, &key, '='); + return value->length > 0; +} + +void Options_Get(const char* key, cc_string* value, const char* defValue) { + cc_string str; + Options_UNSAFE_Get(key, &str); + value->length = 0; + + if (str.length) { + String_AppendString(value, &str); + } else { + String_AppendConst(value, defValue); + } +} + +int Options_GetInt(const char* key, int min, int max, int defValue) { + cc_string str; + int value; + if (!Options_UNSAFE_Get(key, &str)) return defValue; + if (!Convert_ParseInt(&str, &value)) return defValue; + + Math_Clamp(value, min, max); + return value; +} + +cc_bool Options_GetBool(const char* key, cc_bool defValue) { + cc_string str; + cc_bool value; + if (!Options_UNSAFE_Get(key, &str)) return defValue; + if (!Convert_ParseBool(&str, &value)) return defValue; + + return value; +} + +float Options_GetFloat(const char* key, float min, float max, float defValue) { + cc_string str; + float value; + if (!Options_UNSAFE_Get(key, &str)) return defValue; + if (!Convert_ParseFloat(&str, &value)) return defValue; + + Math_Clamp(value, min, max); + return value; +} + +int Options_GetEnum(const char* key, int defValue, const char* const* names, int namesCount) { + cc_string str; + if (!Options_UNSAFE_Get(key, &str)) return defValue; + return Utils_ParseEnum(&str, defValue, names, namesCount); +} + +cc_bool Options_GetColor(const char* key, cc_uint8* rgb) { + cc_string value, parts[3]; + if (!Options_UNSAFE_Get(key, &value)) return false; + if (PackedCol_TryParseHex(&value, rgb)) return true; + + /* Try parsing as R,G,B instead */ + return String_UNSAFE_Split(&value, ',', parts, 3) + && Convert_ParseUInt8(&parts[0], &rgb[0]) + && Convert_ParseUInt8(&parts[1], &rgb[1]) + && Convert_ParseUInt8(&parts[2], &rgb[2]); +} + + +void Options_SetBool(const char* keyRaw, cc_bool value) { + static const cc_string str_true = String_FromConst("True"); + static const cc_string str_false = String_FromConst("False"); + Options_Set(keyRaw, value ? &str_true : &str_false); +} + +void Options_SetInt(const char* keyRaw, int value) { + cc_string str; char strBuffer[STRING_INT_CHARS]; + String_InitArray(str, strBuffer); + String_AppendInt(&str, value); + Options_Set(keyRaw, &str); +} + +void Options_Set(const char* keyRaw, const cc_string* value) { + cc_string key = String_FromReadonly(keyRaw); + Options_SetString(&key, value); +} + +void Options_SetString(const cc_string* key, const cc_string* value) { + if (!value || !value->length) { + if (!EntryList_Remove(&Options, key, '=')) return; + } else { + EntryList_Set(&Options, key, value, '='); + } + +#if defined OPTIONS_SAVE_IMMEDIATELY + if (!savingPaused) SaveOptions(); +#endif + + if (HasChanged(key)) return; + StringsBuffer_Add(&changedOpts, key); +} + +void Options_SetSecure(const char* opt, const cc_string* src) { + char data[2000], encData[1500+1]; + cc_string tmp, enc; + cc_result res; + if (!src->length) return; + + String_InitArray(enc, encData); + res = Platform_Encrypt(src->buffer, src->length, &enc); + if (res) { Platform_Log2("Error %e encrypting option %c", &res, opt); return; } + + /* base64 encode the data, as user might edit options.txt with a text editor */ + if (enc.length > 1500) Logger_Abort("too large to base64"); + tmp.buffer = data; + tmp.length = Convert_ToBase64(enc.buffer, enc.length, data); + tmp.capacity = tmp.length; + Options_Set(opt, &tmp); +} + +void Options_GetSecure(const char* opt, cc_string* dst) { + cc_uint8 data[1500]; + int dataLen; + cc_string raw; + cc_result res; + + Options_UNSAFE_Get(opt, &raw); + if (!raw.length) return; + if (raw.length > 2000) Logger_Abort("too large to base64"); + + dataLen = Convert_FromBase64(raw.buffer, raw.length, data); + res = Platform_Decrypt(data, dataLen, dst); + if (res) Platform_Log2("Error %e decrypting option %c", &res, opt); +} |