Start migrating commands / variables to ObjC++

FossilOrigin-Name: f4c57c1df00e1fed4dd28bbac2f4862e2a58ddfbfbddb17c56abab386d115ccb
This commit is contained in:
Jonathan Schleifer 2024-08-03 15:12:18 +00:00
parent 0bbe1c8bee
commit faf0a4096e

View file

@ -5,96 +5,137 @@
enum { ID_VAR, ID_COMMAND, ID_ALIAS }; enum { ID_VAR, ID_COMMAND, ID_ALIAS };
struct ident { @interface Ident : OFObject
int type; // one of ID_* above @property(nonatomic) int type; // one of ID_* above
char *name; @property(nonatomic) char *name;
int min, max; // ID_VAR @property(nonatomic) int min, max; // ID_VAR
int *storage; // ID_VAR @property(nonatomic) int *storage; // ID_VAR
void (*fun)(); // ID_VAR, ID_COMMAND @property(nonatomic) void (*fun)(); // ID_VAR, ID_COMMAND
int narg; // ID_VAR, ID_COMMAND @property(nonatomic) int narg; // ID_VAR, ID_COMMAND
char *action; // ID_ALIAS @property(nonatomic) char *action; // ID_ALIAS
bool persist; @property(nonatomic) bool persist;
}; @end
@implementation Ident
@end
void void
itoa(char *s, int i) itoa(char *s, int i)
{ {
sprintf_s(s)("%d", i); sprintf_s(s)("%d", i);
}; }
char * char *
exchangestr(char *o, char *n) exchangestr(char *o, char *n)
{ {
gp()->deallocstr(o); gp()->deallocstr(o);
return newstring(n); return newstring(n);
}; }
hashtable<ident> *idents = NULL; // contains ALL vars/commands/aliases // contains ALL vars/commands/aliases
OFMutableDictionary<OFString *, Ident *> *idents;
void void
alias(char *name, char *action) alias(char *name, char *action)
{ {
ident *b = idents->access(name); @autoreleasepool {
Ident *b = idents[@(name)];
if (!b) { if (!b) {
name = newstring(name); Ident *b = [[Ident alloc] init];
ident b = { b.type = ID_ALIAS;
ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), true}; b.name = newstring(name);
idents->access(name, &b); b.action = newstring(action);
b.persist = true;
idents[@(name)] = b;
} else { } else {
if (b->type == ID_ALIAS) if (b.type == ID_ALIAS)
b->action = exchangestr(b->action, action); b.action = exchangestr(b.action, action);
else else
conoutf( conoutf(
"cannot redefine builtin %s with an alias", name); "cannot redefine builtin %s with an alias",
}; name);
}; }
}
}
COMMAND(alias, ARG_2STR); COMMAND(alias, ARG_2STR)
// variable's and commands are registered through globals, see cube.h
int int
variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), variable(char *name, int min, int cur, int max, int *storage, void (*fun)(),
bool persist) bool persist)
{ {
if (!idents) if (idents == nil)
idents = new hashtable<ident>; idents = [[OFMutableDictionary alloc] init];
ident v = {ID_VAR, name, min, max, storage, fun, 0, 0, persist};
idents->access(name, &v); @autoreleasepool {
Ident *v = [[Ident alloc] init];
v.type = ID_VAR;
v.name = name;
v.min = min;
v.max = max;
v.storage = storage;
v.fun = fun;
v.persist = persist;
idents[@(name)] = v;
}
return cur; return cur;
}; }
void void
setvar(char *name, int i) setvar(char *name, int i)
{ {
*idents->access(name)->storage = i; @autoreleasepool {
}; *idents[@(name)].storage = i;
}
}
int int
getvar(char *name) getvar(char *name)
{ {
return *idents->access(name)->storage; @autoreleasepool {
}; return *idents[@(name)].storage;
}
}
bool bool
identexists(char *name) identexists(char *name)
{ {
return idents->access(name) != NULL; @autoreleasepool {
}; return (idents[@(name)] != nil);
}
}
char * char *
getalias(char *name) getalias(char *name)
{ {
ident *i = idents->access(name); @autoreleasepool {
return i && i->type == ID_ALIAS ? i->action : NULL; Ident *i = idents[@(name)];
}; return i != nil && i.type == ID_ALIAS ? i.action : NULL;
}
}
bool bool
addcommand(char *name, void (*fun)(), int narg) addcommand(char *name, void (*fun)(), int narg)
{ {
if (!idents) if (idents == nil)
idents = new hashtable<ident>; idents = [[OFMutableDictionary alloc] init];
ident c = {ID_COMMAND, name, 0, 0, 0, fun, narg, 0, false};
idents->access(name, &c); @autoreleasepool {
Ident *c = [[Ident alloc] init];
c.type = ID_COMMAND;
c.name = name;
c.fun = fun;
c.narg = narg;
idents[@(name)] = c;
}
return false; return false;
}; }
char * char *
parseexp(char *&p, int right) // parse any nested set of () or [] parseexp(char *&p, int right) // parse any nested set of () or []
@ -113,8 +154,8 @@ parseexp(char *&p, int right) // parse any nested set of () or []
p--; p--;
conoutf("missing \"%c\"", right); conoutf("missing \"%c\"", right);
return NULL; return NULL;
}; }
}; }
char *s = newstring(word, p - word - 1); char *s = newstring(word, p - word - 1);
if (left == '(') { if (left == '(') {
string t; string t;
@ -122,9 +163,9 @@ parseexp(char *&p, int right) // parse any nested set of () or []
execute( execute(
s)); // evaluate () exps directly, and substitute result s)); // evaluate () exps directly, and substitute result
s = exchangestr(s, t); s = exchangestr(s, t);
}; }
return s; return s;
}; }
char * char *
parseword(char *&p) // parse single argument, including expressions parseword(char *&p) // parse single argument, including expressions
@ -140,7 +181,7 @@ parseword(char *&p) // parse single argument, including expressions
if (*p == '\"') if (*p == '\"')
p++; p++;
return s; return s;
}; }
if (*p == '(') if (*p == '(')
return parseexp(p, ')'); return parseexp(p, ')');
if (*p == '[') if (*p == '[')
@ -150,24 +191,29 @@ parseword(char *&p) // parse single argument, including expressions
if (p - word == 0) if (p - word == 0)
return NULL; return NULL;
return newstring(word, p - word); return newstring(word, p - word);
}; }
char * char *
lookup(char *n) // find value of ident referenced with $ in exp lookup(char *n) // find value of ident referenced with $ in exp
{ {
ident *ID = idents->access(n + 1); @autoreleasepool {
if (ID) Ident *ID = idents[@(n + 1)];
switch (ID->type) {
if (ID != nil) {
switch (ID.type) {
case ID_VAR: case ID_VAR:
string t; string t;
itoa(t, *(ID->storage)); itoa(t, *(ID.storage));
return exchangestr(n, t); return exchangestr(n, t);
case ID_ALIAS: case ID_ALIAS:
return exchangestr(n, ID->action); return exchangestr(n, ID.action);
}; }
}
}
conoutf("unknown alias lookup: %s", n + 1); conoutf("unknown alias lookup: %s", n + 1);
return n; return n;
}; }
int int
execute(char *p, bool isdown) // all evaluation happens here, recursively execute(char *p, bool isdown) // all evaluation happens here, recursively
@ -187,11 +233,11 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively
if (!s) { if (!s) {
numargs = i; numargs = i;
s = ""; s = "";
}; }
if (*s == '$') if (*s == '$')
s = lookup(s); // substitute variables s = lookup(s); // substitute variables
w[i] = s; w[i] = s;
}; }
p += strcspn(p, ";\n\0"); p += strcspn(p, ";\n\0");
cont = *p++ != cont = *p++ !=
@ -202,186 +248,234 @@ execute(char *p, bool isdown) // all evaluation happens here, recursively
if (!*c) if (!*c)
continue; // empty statement continue; // empty statement
ident *ID = idents->access(c); @autoreleasepool {
if (!ID) { Ident *ID = idents[@(c)];
if (ID == nil) {
val = ATOI(c); val = ATOI(c);
if (!val && *c != '0') if (!val && *c != '0')
conoutf("unknown command: %s", c); conoutf("unknown command: %s", c);
} else } else {
switch (ID->type) { switch (ID.type) {
case ID_COMMAND: // game defined commands // game defined commands
switch (ID->narg) // use very ad-hoc function case ID_COMMAND:
// signature, and just call it // use very ad-hoc function signature,
{ // and just call it
switch (ID.narg) {
case ARG_1INT: case ARG_1INT:
if (isdown) if (isdown)
((void(__cdecl *)(int))ID->fun)( ((void(__cdecl *)(
int))ID.fun)(
ATOI(w[1])); ATOI(w[1]));
break; break;
case ARG_2INT: case ARG_2INT:
if (isdown) if (isdown)
((void(__cdecl *)( ((void(__cdecl *)(
int, int))ID->fun)( int, int))ID.fun)(
ATOI(w[1]), ATOI(w[2])); ATOI(w[1]),
ATOI(w[2]));
break; break;
case ARG_3INT: case ARG_3INT:
if (isdown) if (isdown)
((void(__cdecl *)(int, int, ((void(__cdecl *)(int,
int))ID->fun)(ATOI(w[1]), int, int))ID.fun)(
ATOI(w[2]), ATOI(w[3])); ATOI(w[1]),
ATOI(w[2]),
ATOI(w[3]));
break; break;
case ARG_4INT: case ARG_4INT:
if (isdown) if (isdown)
((void(__cdecl *)(int, int, int, ((void(__cdecl *)(int,
int))ID->fun)(ATOI(w[1]), int, int,
ATOI(w[2]), ATOI(w[3]), int))ID.fun)(
ATOI(w[1]),
ATOI(w[2]),
ATOI(w[3]),
ATOI(w[4])); ATOI(w[4]));
break; break;
case ARG_NONE: case ARG_NONE:
if (isdown) if (isdown)
((void(__cdecl *)())ID->fun)(); ((void(__cdecl *)())
ID.fun)();
break; break;
case ARG_1STR: case ARG_1STR:
if (isdown) if (isdown)
((void(__cdecl *)( ((void(__cdecl *)(
char *))ID->fun)(w[1]); char *))ID.fun)(
w[1]);
break; break;
case ARG_2STR: case ARG_2STR:
if (isdown) if (isdown)
((void(__cdecl *)( ((void(__cdecl *)(
char *, char *))ID->fun)( char *,
char *))ID.fun)(
w[1], w[2]); w[1], w[2]);
break; break;
case ARG_3STR: case ARG_3STR:
if (isdown) if (isdown)
((void(__cdecl *)(char *, ((void(__cdecl *)(
char *, char *))ID->fun)( char *, char *,
char *))ID.fun)(
w[1], w[2], w[3]); w[1], w[2], w[3]);
break; break;
case ARG_5STR: case ARG_5STR:
if (isdown) if (isdown)
((void(__cdecl *)(char *, ((void(__cdecl *)(
char *, char *, char *, char *, char *,
char *))ID->fun)(w[1], w[2], char *, char *,
w[3], w[4], w[5]); char *))ID.fun)(
w[1], w[2], w[3],
w[4], w[5]);
break; break;
case ARG_DOWN: case ARG_DOWN:
((void(__cdecl *)(bool))ID->fun)( ((void(__cdecl *)(bool))ID.fun)(
isdown); isdown);
break; break;
case ARG_DWN1: case ARG_DWN1:
((void(__cdecl *)(bool, ((void(__cdecl *)(
char *))ID->fun)(isdown, w[1]); bool, char *))ID.fun)(
isdown, w[1]);
break; break;
case ARG_1EXP: case ARG_1EXP:
if (isdown) if (isdown)
val = ((int(__cdecl *)( val = ((int(__cdecl *)(
int))ID->fun)( int))ID.fun)(
execute(w[1])); execute(w[1]));
break; break;
case ARG_2EXP: case ARG_2EXP:
if (isdown) if (isdown)
val = ((int(__cdecl *)(int, val = ((int(__cdecl *)(
int))ID->fun)(execute(w[1]), int, int))ID.fun)(
execute(w[1]),
execute(w[2])); execute(w[2]));
break; break;
case ARG_1EST: case ARG_1EST:
if (isdown) if (isdown)
val = ((int(__cdecl *)( val = ((int(__cdecl *)(
char *))ID->fun)(w[1]); char *))ID.fun)(
w[1]);
break; break;
case ARG_2EST: case ARG_2EST:
if (isdown) if (isdown)
val = ((int(__cdecl *)( val = ((int(__cdecl *)(
char *, char *))ID->fun)( char *,
char *))ID.fun)(
w[1], w[2]); w[1], w[2]);
break; break;
case ARG_VARI: case ARG_VARI:
if (isdown) { if (isdown) {
string r; // limit, remove // limit, remove
string r;
r[0] = 0; r[0] = 0;
for (int i = 1; i < numargs; for (int i = 1;
i++) { i < numargs; i++) {
strcat_s(r, // make
w[i]); // make
// string-list // string-list
// out of all // out of all
// arguments // arguments
if (i == numargs - 1) strcat_s(
r, w[i]);
if (i ==
numargs - 1)
break; break;
strcat_s(r, " "); strcat_s(
}; r, " ");
}
((void(__cdecl *)( ((void(__cdecl *)(
char *))ID->fun)(r); char *))ID.fun)(r);
break; break;
} }
}; }
break; break;
case ID_VAR: // game defined variabled // game defined variables
case ID_VAR:
if (isdown) { if (isdown) {
if (!w[1][0]) if (!w[1][0])
// var with no value
// just prints its
// current value
conoutf("%s = %d", c, conoutf("%s = %d", c,
*ID->storage); // var with *ID.storage);
// no value
// just
// prints its
// current
// value
else { else {
if (ID->min > ID->max) { if (ID.min > ID.max) {
conoutf("variable is " conoutf("variab"
"read-only"); "le is "
"read-"
"only");
} else { } else {
int i1 = ATOI(w[1]); int i1 =
if (i1 < ID->min || ATOI(w[1]);
i1 > ID->max) { if (i1 <
ID.min ||
i1 >
ID.max) {
// clamp
// to
// valid
// range
i1 = i1 =
i1 < ID->min i1 < ID.min
? ID->min ? ID.min
: ID->max; // clamp to valid range : ID.max;
conoutf( conoutf(
"valid " "va"
"range for " "li"
"%s is " "d "
"%d..%d", "ra"
c, ID->min, "ng"
ID->max); "e "
"fo"
"r "
"%s"
" i"
"s "
"%d"
".."
"%"
"d",
c,
ID.min,
ID.max);
} }
*ID->storage = i1; *ID.storage =
}; i1;
if (ID->fun) }
((void(__cdecl *)())ID if (ID.fun)
->fun)(); // call // call trigger
// trigger // function if
// function
// if
// available // available
}; ((void(__cdecl
}; *)())ID
.fun)();
}
}
break; break;
case ID_ALIAS: // alias, also used as functions and // alias, also used as functions and (global)
// (global) variables // variables
case ID_ALIAS:
for (int i = 1; i < numargs; i++) { for (int i = 1; i < numargs; i++) {
sprintf_sd(t)("arg%d", // set any arguments as
i); // set any arguments as (global) // (global) arg values so
// arg values so functions can // functions can access them
// access them sprintf_sd(t)("arg%d", i);
alias(t, w[i]); alias(t, w[i]);
}; }
char *action = newstring( // create new string here because alias
ID->action); // create new string here // could rebind itself
// because alias could rebind char *action = newstring(ID.action);
// itself
val = execute(action, isdown); val = execute(action, isdown);
gp()->deallocstr(action); gp()->deallocstr(action);
break; break;
}; }
}
}
loopj(numargs) gp()->deallocstr(w[j]); loopj(numargs) gp()->deallocstr(w[j]);
}; }
return val; return val;
}; }
// tab-completion of all idents // tab-completion of all idents
@ -391,7 +485,7 @@ void
resetcomplete() resetcomplete()
{ {
completesize = 0; completesize = 0;
}; }
void void
complete(char *s) complete(char *s)
@ -401,25 +495,26 @@ complete(char *s)
strcpy_s(t, s); strcpy_s(t, s);
strcpy_s(s, "/"); strcpy_s(s, "/");
strcat_s(s, t); strcat_s(s, t);
}; }
if (!s[1]) if (!s[1])
return; return;
if (!completesize) { if (!completesize) {
completesize = (int)strlen(s) - 1; completesize = (int)strlen(s) - 1;
completeidx = 0; completeidx = 0;
}; }
int idx = 0; __block int idx = 0;
enumerate( [idents enumerateKeysAndObjectsUsingBlock:^(
idents, ident *, id, OFString *name, Ident *ident, bool *stop) {
if (strncmp(id->name, s + 1, completesize) == 0 && if (strncmp(ident.name, s + 1, completesize) == 0 &&
idx++ == completeidx) { idx++ == completeidx) {
strcpy_s(s, "/"); strcpy_s(s, "/");
strcat_s(s, id->name); strcat_s(s, ident.name);
};); }
}];
completeidx++; completeidx++;
if (completeidx >= idx) if (completeidx >= idx)
completeidx = 0; completeidx = 0;
}; }
bool bool
execfile(char *cfgfile) execfile(char *cfgfile)
@ -432,14 +527,14 @@ execfile(char *cfgfile)
execute(buf); execute(buf);
free(buf); free(buf);
return true; return true;
}; }
void void
exec(char *cfgfile) exec(char *cfgfile)
{ {
if (!execfile(cfgfile)) if (!execfile(cfgfile))
conoutf("could not read \"%s\"", cfgfile); conoutf("could not read \"%s\"", cfgfile);
}; }
void void
writecfg() writecfg()
@ -453,20 +548,23 @@ writecfg()
"autoexec.cfg to override anything\n\n"); "autoexec.cfg to override anything\n\n");
writeclientinfo(f); writeclientinfo(f);
fprintf(f, "\n"); fprintf(f, "\n");
enumerate( [idents enumerateKeysAndObjectsUsingBlock:^(
idents, ident *, ID, if (ID->type == ID_VAR && ID->persist) { OFString *name, Ident *ident, bool *stop) {
fprintf(f, "%s %d\n", ID->name, *ID->storage); if (ident.type == ID_VAR && ident.persist) {
};); fprintf(f, "%s %d\n", ident.name, *ident.storage);
}
}];
fprintf(f, "\n"); fprintf(f, "\n");
writebinds(f); writebinds(f);
fprintf(f, "\n"); fprintf(f, "\n");
enumerate( [idents enumerateKeysAndObjectsUsingBlock:^(
idents, ident *, ID, OFString *name, Ident *ident, bool *stop) {
if (ID->type == ID_ALIAS && !strstr(ID->name, "nextmap_")) { if (ident.type == ID_ALIAS && !strstr(ident.name, "nextmap_")) {
fprintf(f, "alias \"%s\" [%s]\n", ID->name, ID->action); fprintf(f, "alias \"%s\" [%s]\n", ident.name, ident.action);
};); }
}];
fclose(f); fclose(f);
}; }
COMMAND(writecfg, ARG_NONE); COMMAND(writecfg, ARG_NONE);
@ -480,13 +578,14 @@ intset(char *name, int v)
string b; string b;
itoa(b, v); itoa(b, v);
alias(name, b); alias(name, b);
}; }
void void
ifthen(char *cond, char *thenp, char *elsep) ifthen(char *cond, char *thenp, char *elsep)
{ {
execute(cond[0] != '0' ? thenp : elsep); execute(cond[0] != '0' ? thenp : elsep);
}; }
void void
loopa(char *times, char *body) loopa(char *times, char *body)
{ {
@ -495,26 +594,28 @@ loopa(char *times, char *body)
{ {
intset("i", i); intset("i", i);
execute(body); execute(body);
}; }
}; }
void void
whilea(char *cond, char *body) whilea(char *cond, char *body)
{ {
while (execute(cond)) while (execute(cond))
execute(body); execute(body);
}; // can't get any simpler than this :) } // can't get any simpler than this :)
void void
onrelease(bool on, char *body) onrelease(bool on, char *body)
{ {
if (!on) if (!on)
execute(body); execute(body);
}; }
void void
concat(char *s) concat(char *s)
{ {
alias("s", s); alias("s", s);
}; }
void void
concatword(char *s) concatword(char *s)
@ -523,7 +624,7 @@ concatword(char *s)
if (*a != ' ') if (*a != ' ')
a++; a++;
concat(s); concat(s);
}; }
int int
listlen(char *a) listlen(char *a)
@ -535,7 +636,7 @@ listlen(char *a)
if (*a++ == ' ') if (*a++ == ' ')
n++; n++;
return n + 1; return n + 1;
}; }
void void
at(char *s, char *pos) at(char *s, char *pos)
@ -544,7 +645,7 @@ at(char *s, char *pos)
loopi(n) s += strspn(s += strcspn(s, " \0"), " "); loopi(n) s += strspn(s += strcspn(s, " \0"), " ");
s[strcspn(s, " \0")] = 0; s[strcspn(s, " \0")] = 0;
concat(s); concat(s);
}; }
COMMANDN(loop, loopa, ARG_2STR); COMMANDN(loop, loopa, ARG_2STR);
COMMANDN(while, whilea, ARG_2STR); COMMANDN(while, whilea, ARG_2STR);
@ -560,68 +661,68 @@ int
add(int a, int b) add(int a, int b)
{ {
return a + b; return a + b;
}; }
COMMANDN(+, add, ARG_2EXP); COMMANDN(+, add, ARG_2EXP);
int int
mul(int a, int b) mul(int a, int b)
{ {
return a * b; return a * b;
}; }
COMMANDN(*, mul, ARG_2EXP); COMMANDN(*, mul, ARG_2EXP);
int int
sub(int a, int b) sub(int a, int b)
{ {
return a - b; return a - b;
}; }
COMMANDN(-, sub, ARG_2EXP); COMMANDN(-, sub, ARG_2EXP);
int int
divi(int a, int b) divi(int a, int b)
{ {
return b ? a / b : 0; return b ? a / b : 0;
}; }
COMMANDN(div, divi, ARG_2EXP); COMMANDN(div, divi, ARG_2EXP);
int int
mod(int a, int b) mod(int a, int b)
{ {
return b ? a % b : 0; return b ? a % b : 0;
}; }
COMMAND(mod, ARG_2EXP); COMMAND(mod, ARG_2EXP);
int int
equal(int a, int b) equal(int a, int b)
{ {
return (int)(a == b); return (int)(a == b);
}; }
COMMANDN(=, equal, ARG_2EXP); COMMANDN(=, equal, ARG_2EXP);
int int
lt(int a, int b) lt(int a, int b)
{ {
return (int)(a < b); return (int)(a < b);
}; }
COMMANDN(<, lt, ARG_2EXP); COMMANDN(<, lt, ARG_2EXP);
int int
gt(int a, int b) gt(int a, int b)
{ {
return (int)(a > b); return (int)(a > b);
}; }
COMMANDN(>, gt, ARG_2EXP); COMMANDN(>, gt, ARG_2EXP);
int int
strcmpa(char *a, char *b) strcmpa(char *a, char *b)
{ {
return strcmp(a, b) == 0; return strcmp(a, b) == 0;
}; }
COMMANDN(strcmp, strcmpa, ARG_2EST); COMMANDN(strcmp, strcmpa, ARG_2EST);
int int
rndn(int a) rndn(int a)
{ {
return a > 0 ? rnd(a) : 0; return a > 0 ? rnd(a) : 0;
}; }
COMMANDN(rnd, rndn, ARG_1EXP); COMMANDN(rnd, rndn, ARG_1EXP);
int int
explastmillis() explastmillis()
{ {
return lastmillis; return lastmillis;
}; }
COMMANDN(millis, explastmillis, ARG_1EXP); COMMANDN(millis, explastmillis, ARG_1EXP);