-#ifndef CVAR_H
-#define CVAR_H
+#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;
}
/** escape the string to make it safe for consoles */
+ERASEABLE
string MakeConsoleSafe(string input)
{
input = strreplace("\n", "", input);
return input;
}
-void cvar_describe(string name, string desc)
+/**
+ * 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
+ * var!==x
+ */
+ERASEABLE
+bool expr_evaluate(string s)
{
- localcmd(sprintf("\nset %1$s \"$%1$s\" \"%2$s\"\n", name, MakeConsoleSafe(desc)));
-}
+ 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;
-void cvar_archive(string name)
-{
- localcmd(sprintf("\nseta %1$s \"$%1$s\"\n", name));
+ #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)
{
- cvar_describe(name, desc);
- if (archive) cvar_archive(name);
+ 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;
#define repr_cvar_string(x) (x)
#define repr_cvar_vector(x) (sprintf("%v", x))
+//pseudo prototypes:
+// void AUTOCVAR(<cvar_name>, <qc_var_type>, default_cvar_value, string desc)
+// void AUTOCVAR_SAVE(<cvar_name>, <qc_var_type>, default_cvar_value, string desc)
+// where default_cvar_value has type <qc_var_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) \
+ ACCUMULATE void RegisterCvars(void(string, string, string, bool, string) f) \
{ \
f( #var, repr_cvar_##type(default), desc, archive, file); \
} \
__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(OVERLOAD(AUTOCVAR, __FILE__, __VA_ARGS__))
+#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__)
-
-#endif