#pragma once #include "nil.qh" #include "progname.qh" #include "static.qh" ERASEABLE void RegisterCvars(void(string name, string def, string desc, bool archive, string file) f) {} ERASEABLE bool cvar_value_issafe(string s) { if (strstrofs(s, "\"", 0) >= 0) return false; if (strstrofs(s, "\\", 0) >= 0) return false; if (strstrofs(s, ";", 0) >= 0) return false; if (strstrofs(s, "$", 0) >= 0) return false; if (strstrofs(s, "\r", 0) >= 0) return false; if (strstrofs(s, "\n", 0) >= 0) return false; return true; } /** escape the string to make it safe for consoles */ ERASEABLE string MakeConsoleSafe(string input) { input = strreplace("\n", "", input); input = strreplace("\\", "\\\\", input); input = strreplace("$", "$$", input); input = strreplace("\"", "\\\"", input); return input; } /** * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ... * +: all must match. this is the default * -: one must NOT match * * var>x * var=x * var<=x * var==x * var!=x * var===x * var!==x */ ERASEABLE bool expr_evaluate(string s) { bool ret = false; if (str2chr(s, 0) == '+') { s = substring(s, 1, -1); } else if (str2chr(s, 0) == '-') { ret = true; s = substring(s, 1, -1); } bool expr_fail = false; for (int i = 0, n = tokenize_console(s); i < n; ++i) { int o; string k, v; s = argv(i); #define X(expr) \ if (expr) \ continue; \ expr_fail = true; \ break; #define BINOP(op, len, expr) \ if ((o = strstrofs(s, op, 0)) >= 0) { \ k = substring(s, 0, o); \ v = substring(s, o + len, -1); \ X(expr); \ } BINOP(">=", 2, cvar(k) >= stof(v)); BINOP("<=", 2, cvar(k) <= stof(v)); BINOP(">", 1, cvar(k) > stof(v)); BINOP("<", 1, cvar(k) < stof(v)); BINOP("==", 2, cvar(k) == stof(v)); BINOP("!=", 2, cvar(k) != stof(v)); BINOP("===", 3, cvar_string(k) == v); BINOP("!==", 3, cvar_string(k) != v); { k = s; bool b = true; if (str2chr(k, 0) == '!') { k = substring(s, 1, -1); b = false; } float f = stof(k); bool isnum = ftos(f) == k; X(boolean(isnum ? f : cvar(k)) == b); } #undef BINOP #undef X } if (!expr_fail) { ret = !ret; } // now ret is true if we want to keep the item, and false if we want to get rid of it return ret; } ERASEABLE void RegisterCvars_Set(string name, string def, string desc, bool archive, string file) { localcmd(sprintf("\nset %1$s \"$%1$s\" \"%2$s\"\n", name, MakeConsoleSafe(desc))); if (archive) localcmd(sprintf("\nseta %1$s \"$%1$s\"\n", name)); } int RegisterCvars_Save_fd; ERASEABLE void RegisterCvars_Save(string name, string def, string desc, bool archive, string file) { if (!archive) return; fputs(RegisterCvars_Save_fd, sprintf("seta %s \"%s\"\n", name, def)); } STATIC_INIT_LATE(Cvars) { RegisterCvars(RegisterCvars_Set); RegisterCvars_Save_fd = fopen(sprintf("default%s.cfg", PROGNAME), FILE_WRITE); if (RegisterCvars_Save_fd >= 0) { RegisterCvars(RegisterCvars_Save); fclose(RegisterCvars_Save_fd); } } const noref bool default_bool = false; const noref int default_int = 0; const noref float default_float = 0; const noref string default_string = ""; const noref vector default_vector = '0 0 0'; #define repr_cvar_bool(x) ((x) ? "1" : "0") #define repr_cvar_int(x) (ftos(x)) #define repr_cvar_float(x) (ftos(x)) #define repr_cvar_string(x) (x) #define repr_cvar_vector(x) (sprintf("%v", x)) //pseudo prototypes: // void AUTOCVAR(, , default_cvar_value, string desc) // void AUTOCVAR_SAVE(, , default_cvar_value, string desc) // where default_cvar_value has type // e.g.: AUTOCVAR(mycvar, float, 2.5, "cvar description") #define __AUTOCVAR(file, archive, var, type, desc, default) \ ACCUMULATE void RegisterCvars(void(string, string, string, bool, string) f) \ { \ f( #var, repr_cvar_##type(default), desc, archive, file); \ } \ type autocvar_##var = default #define AUTOCVAR_5(file, archive, var, type, desc) \ __AUTOCVAR(file, archive, var, type, desc, default_##type) #define AUTOCVAR_6(file, archive, var, type, default, desc) \ __AUTOCVAR(file, archive, var, type, desc, default) #define _AUTOCVAR(...) EVAL__AUTOCVAR(OVERLOAD(AUTOCVAR, __FILE__, __VA_ARGS__)) #define EVAL__AUTOCVAR(...) __VA_ARGS__ #define AUTOCVAR_SAVE(...) _AUTOCVAR(true, __VA_ARGS__) #define AUTOCVAR(...) _AUTOCVAR(false, __VA_ARGS__)