From 792463cb133f65645feb48743bbc03ef8eb96bdd Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 13 Sep 2022 21:46:21 +0100 Subject: Move towards C23, improve events and vcall macros Another big one. Here's a list of things: - Since the upcoming C23 standardises typeof(), use it as an extension for the time being in order to allow passing arbitrary types as macro/codegen parameters. It wouldn't have been a big leap to do this even without standardisation since it's apparently an easy extension to implement - and also, to be honest, this project is essentially glued to Clang anyway so who cares. - Likewise, bool, true and false are becoming pre-defined, so pre-pre-define them now in order to get the benefit of not having to remember one header everywhere. - Really ungodly/amazing vcall macro stuff now allows us to call C++ virtual functions like regular C functions. It's pretty cool! - Events can now take arbitrary parameters and come in two types: regular events and predicates. All this makes the base code even uglier but makes the feature implementation nicer. In other words, it places more of the cognitive burden on myself and less on other people who might want to contribute. This is a good tradeoff, because I'm a genius. --- compile | 24 +++++++----- compile.bat | 13 +++++-- src/3p/chibicc/chibicc.h | 4 +- src/3p/chibicc/tokenize.c | 2 +- src/ac.c | 5 +-- src/alias.c | 1 - src/autojump.c | 2 - src/bind.c | 2 - src/bitbuf.h | 2 - src/build/cmeta.c | 87 +++++++++++++++++++++++++++++++---------- src/build/cmeta.h | 6 +-- src/build/codegen.c | 84 +++++++++++++++++++++++++++++++++------- src/build/mkentprops.c | 1 - src/build/mkgamedata.c | 1 - src/build/vec.h | 1 - src/con_.c | 53 +++++++++++++------------ src/con_.h | 2 - src/democustom.c | 4 +- src/demorec.c | 1 - src/engineapi.c | 8 ++-- src/ent.c | 4 +- src/event.h | 56 ++++++++++++++++++++++----- src/extmalloc.c | 26 +++++-------- src/feature.h | 2 - src/fov.c | 9 ++--- src/fov.h | 2 - src/gamedata.c | 2 - src/gamedata.h | 2 - src/gameinfo.c | 3 +- src/hook.h | 2 - src/kv.c | 2 - src/kv.h | 2 - src/l4dwarp.c | 8 ++-- src/os.h | 2 - src/portalcolours.c | 1 - src/sst.c | 14 +++---- src/sst.h | 4 +- src/vcall.h | 98 +++++++++++++++++++++++++++++++++++++++++------ src/x86util.h | 4 +- 39 files changed, 361 insertions(+), 185 deletions(-) diff --git a/compile b/compile index b2aa205..3c35ed1 100755 --- a/compile +++ b/compile @@ -3,10 +3,14 @@ case "`uname -s`" in # weird people using Windows Bash might type ./compile, help them out :) - *NT*) - echo "You're on Windows, idiot! Running compile.bat for you." + *NT*) # msys2 or busybox-w32 + echo "You're on Windows, idiot! Running compile.bat for you.">&2 exec cmd /c compile.bat ;; esac +case "`uname -r`" in + *Microsoft*) + echo "NOTE: building inside WSL. Use compile.bat to build for Windows!">&2 +esac mkdir -p .build/include @@ -33,8 +37,10 @@ cc() { # ugly annoying special case if [ "$_mn" = " -DMODULE_NAME=con_" ]; then _mn=" -DMODULE_NAME=con" elif [ "$_mn" = "-DMODULE_NAME=sst" ]; then _mn=; fi + # note: using typeof and bool from C23 - see detailed comment in compile.bat $CC -m32 -c -flto -fpic $cflags $warnings -I.build/include \ - -D_FILE_OFFSET_BITS=64$_mn -o ".build/${_bn%%.c}.o" "src/$1" + -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64$_mn \ + -Dtypeof=__typeof -include stdbool.h -o ".build/${_bn%%.c}.o" "src/$1" } ld() { @@ -71,12 +77,12 @@ if [ "$dbg" = 1 ]; then src="$src \ udis86.c" fi -$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -o .build/codegen \ - src/build/codegen.c src/build/cmeta.c -$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -o .build/mkgamedata \ - src/build/mkgamedata.c src/kv.c -$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -o .build/mkentprops \ - src/build/mkentprops.c src/kv.c +$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -include stdbool.h \ + -o .build/codegen src/build/codegen.c src/build/cmeta.c +$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -include stdbool.h \ + -o .build/mkgamedata src/build/mkgamedata.c src/kv.c +$HOSTCC -O2 -fuse-ld=lld $warnings -D_FILE_OFFSET_BITS=64 -include stdbool.h \ + -o .build/mkentprops src/build/mkentprops.c src/kv.c .build/codegen `for s in $src; do echo "src/$s"; done` .build/mkgamedata gamedata/engine.kv gamedata/gamelib.kv gamedata/inputsystem.kv .build/mkentprops gamedata/entprops.kv diff --git a/compile.bat b/compile.bat index 8005b24..f48453e 100644 --- a/compile.bat +++ b/compile.bat @@ -35,8 +35,13 @@ set dmodname= -DMODULE_NAME=%basename% if "%dmodname%"==" -DMODULE_NAME=con_" set dmodname= -DMODULE_NAME=con if "%dmodname%"==" -DMODULE_NAME=sst" set dmodname= set objs=%objs% .build/%basename%.o +:: note: we use a couple of C23 things now because otherwise we'd have to wait a +:: year to get anything done. typeof=__typeof prevents pedantic warnings caused +:: by typeof still technically being an extension, and stdbool gives us +:: predefined bool/true/false before compilers start doing that by default %CC% -m32 -c -flto %cflags% %warnings% -I.build/include -D_CRT_SECURE_NO_WARNINGS -D_DLL ^ --DWIN32_LEAN_AND_MEAN -DNOMINMAX%dmodname% -o .build/%basename%.o %1 || exit /b +-DWIN32_LEAN_AND_MEAN -DNOMINMAX%dmodname% -Dtypeof=__typeof -include stdbool.h ^ +-o .build/%basename%.o %1 || exit /b goto :eof :src @@ -77,11 +82,11 @@ setlocal DisableDelayedExpansion if "%dbg%"=="1" set src=%src% src/dbg.c if "%dbg%"=="1" set src=%src% src/udis86.c -%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -ladvapi32 ^ +%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -include stdbool.h -ladvapi32 ^ -o .build/codegen.exe src/build/codegen.c src/build/cmeta.c || exit /b -%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS ^ +%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -include stdbool.h ^ -o .build/mkgamedata.exe src/build/mkgamedata.c src/kv.c || exit /b -%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -ladvapi32 ^ +%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -include stdbool.h -ladvapi32 ^ -o .build/mkentprops.exe src/build/mkentprops.c src/kv.c || exit /b .build\codegen.exe%src% || exit /b .build\mkgamedata.exe gamedata/engine.kv gamedata/gamelib.kv gamedata/inputsystem.kv || exit /b diff --git a/src/3p/chibicc/chibicc.h b/src/3p/chibicc/chibicc.h index dd810ec..2a80ecf 100644 --- a/src/3p/chibicc/chibicc.h +++ b/src/3p/chibicc/chibicc.h @@ -7,7 +7,7 @@ #include #include #include -#include +//#include #include #include #include @@ -93,7 +93,7 @@ _Noreturn void error(char *fmt, ...) __attribute__((format(printf, 1, 2))); _Noreturn void error_at(char *loc, char *fmt, ...) __attribute__((format(printf, 2, 3))); _Noreturn void error_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3))); void warn_tok(Token *tok, char *fmt, ...) __attribute__((format(printf, 2, 3))); -bool equal(Token *tok, char *op); +bool equal(const Token *tok, const char *op); Token *skip(Token *tok, char *op); bool consume(Token **rest, Token *tok, char *str); void convert_pp_tokens(Token *tok); diff --git a/src/3p/chibicc/tokenize.c b/src/3p/chibicc/tokenize.c index 8ed414e..3b15df9 100644 --- a/src/3p/chibicc/tokenize.c +++ b/src/3p/chibicc/tokenize.c @@ -77,7 +77,7 @@ void warn_tok(Token *tok, char *fmt, ...) { } // Consumes the current token if it matches `op`. -bool equal(Token *tok, char *op) { +bool equal(const Token *tok, const char *op) { return memcmp(tok->loc, op, tok->len) == 0 && op[tok->len] == '\0'; } diff --git a/src/ac.c b/src/ac.c index 4d21a48..326894a 100644 --- a/src/ac.c +++ b/src/ac.c @@ -15,7 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include "bind.h" @@ -144,7 +143,7 @@ static void startlockdown(void) { HANDLE_EVENT(Tick) { #ifdef _WIN32 - static int fewticks = 0; + static uint fewticks = 0; // just check this every so often (roughly 0.1-0.3s depending on game) if (lockdown && !(++fewticks & 7)) inhook_check(); #endif @@ -198,7 +197,7 @@ static void VCALLCONV hook_DispatchInputEvent(void *this, static bool find_DispatchInputEvent(void) { #ifdef _WIN32 - // Crazy pointer-chasing path to get to DispachInputEvent (to log keypresses + // Crazy pointer-chasing path to get to DispatchInputEvent (to log keypresses // and their associated binds): // IGameUIFuncs interface // -> CGameUIFuncs::GetDesktopResolution vfunc diff --git a/src/alias.c b/src/alias.c index b94a6d5..765f735 100644 --- a/src/alias.c +++ b/src/alias.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include "alias.h" diff --git a/src/autojump.c b/src/autojump.c index c4f54f4..d26ab9e 100644 --- a/src/autojump.c +++ b/src/autojump.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "con_.h" #include "engineapi.h" #include "errmsg.h" diff --git a/src/bind.c b/src/bind.c index 762259d..41fd437 100644 --- a/src/bind.c +++ b/src/bind.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "con_.h" #include "dbg.h" #include "errmsg.h" diff --git a/src/bitbuf.h b/src/bitbuf.h index 104c9a1..8700af3 100644 --- a/src/bitbuf.h +++ b/src/bitbuf.h @@ -17,8 +17,6 @@ #ifndef INC_BITBUF_H #define INC_BITBUF_H -#include - #include "intdefs.h" // NOTE: This code assumes it's running on a little endian machine, because, diff --git a/src/build/cmeta.c b/src/build/cmeta.c index 260d33f..7f314c7 100644 --- a/src/build/cmeta.c +++ b/src/build/cmeta.c @@ -14,13 +14,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include "../intdefs.h" #include "../os.h" #include "cmeta.h" +#include "vec.h" /* * This file does C metadata parsing/scraping for the build system. This @@ -73,16 +73,16 @@ Type *array_of(Type *base, int len) { #include "../3p/chibicc/tokenize.c" // one more copypaste from preprocess.c for #include and then I'm // done I promise -static char *join_tokens(Token *tok, Token *end) { +static char *join_tokens(const Token *tok, const Token *end) { int len = 1; - for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + for (const Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { if (t != tok && t->has_space) len++; len += t->len; } char *buf = calloc(1, len); int pos = 0; - for (Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { + for (const Token *t = tok; t != end && t->kind != TK_EOF; t = t->next) { if (t != tok && t->has_space) buf[pos++] = ' '; strncpy(buf + pos, t->loc, t->len); @@ -166,7 +166,7 @@ const struct cmeta *cmeta_loadfile(const os_char *f) { // picks it anyway, and gives far better diagnostics. void cmeta_includes(const struct cmeta *cm, void (*cb)(const char *f, bool issys, void *ctxt), void *ctxt) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // #, include, "string" while (tp) { if (!tp->at_bol || !equal(tp, "#")) { tp = tp->next; continue; } @@ -187,7 +187,7 @@ void cmeta_includes(const struct cmeta *cm, else if (equal(tp, "<")) { tp = tp->next; if (!tp) break; - Token *end = tp; + const Token *end = tp; while (!equal(end, ">")) { end = end->next; if (!end) return; // shouldn't happen in valid source obviously @@ -210,7 +210,7 @@ void cmeta_includes(const struct cmeta *cm, // we're not writing something weird. Don't write something weird! void cmeta_conmacros(const struct cmeta *cm, void (*cb)(const char *, bool, bool)) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // DEF_xyz, (, name while (tp) { bool isplusminus = false, isvar = false; @@ -271,7 +271,7 @@ void cmeta_conmacros(const struct cmeta *cm, } const char *cmeta_findfeatmacro(const struct cmeta *cm) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next) return 0; // FEATURE, ( while (tp) { if (equal(tp, "FEATURE") && equal(tp->next, "(")) { @@ -288,7 +288,7 @@ const char *cmeta_findfeatmacro(const struct cmeta *cm) { void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( enum cmeta_featmacro type, const char *param, void *ctxt), void *ctxt) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; if (!tp || !tp->next) return; while (tp) { int type = -1; @@ -336,26 +336,73 @@ void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( } } -void cmeta_evdefmacros(const struct cmeta *cm, - void (*cb_def)(const char *name)) { - Token *tp = (Token *)cm; +struct vec_str VEC(const char *); + +static void pushmacroarg(const Token *last, const char *start, + struct vec_str *list) { + int len = last->loc - start + last->len; + char *dup = malloc(len + 1); + if (!dup) die1("couldn't allocate memory"); + memcpy(dup, start, len); + dup[len] = '\0'; + if (!vec_push(list, dup)) die1("couldn't append to array"); +} + +// XXX: maybe this should be used for the other functions too. it'd be less ugly +// and handle closing parentheses better, but alloc for tokens we don't care +// about. probably a worthy tradeoff? +static const Token *macroargs(const Token *t, struct vec_str *list) { + int paren = 1; + const Token *last; // avoids copying extra ws/comments in + for (const char *start = t->loc; t; last = t, t = t->next) { + if (equal(t, "(")) { + ++paren; + } + else if (equal(t, ")")) { + if (!--paren) { + pushmacroarg(last, start, list); + return t->next; + } + } + else if (paren == 1 && equal(t, ",")) { + pushmacroarg(last, start, list); + t = t->next; + if (t) start = t->loc; // slightly annoying... + } + } + // I guess we handle this here. + fprintf(stderr, "cmeta: fatal: unexpected EOF in %s\n", t->filename); + exit(2); +} + +void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name, + const char *const *params, int nparams, bool predicate)) { + const Token *tp = (const Token *)cm; if (!tp || !tp->next || !tp->next->next) return; // DEF_EVENT, (, name while (tp) { + bool predicate = true; if (equal(tp, "DEF_EVENT") && equal(tp->next, "(")) { - tp = tp->next->next; - char *name = malloc(tp->len + 1); - if (!name) die1("couldn't allocate memory"); - memcpy(name, tp->loc, tp->len); - name[tp->len] = '\0'; - cb_def(name); + predicate = false; } - tp = tp->next; + else if (!equal(tp, "DEF_PREDICATE") || !equal(tp->next, "(")) { + tp = tp->next; + continue; + } + tp = tp->next->next; + struct vec_str args = {0}; + tp = macroargs(tp, &args); + if (args.sz == 0) { + fprintf(stderr, "cmeta: fatal: missing event parameters in %s\n", + tp->filename); + exit(2); + } + cb(args.data[0], args.data + 1, args.sz - 1, predicate); } } void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname, void (*cb_handler)(const char *evname, const char *modname)) { - Token *tp = (Token *)cm; + const Token *tp = (const Token *)cm; while (tp) { if (equal(tp, "HANDLE_EVENT") && equal(tp->next, "(")) { tp = tp->next->next; diff --git a/src/build/cmeta.h b/src/build/cmeta.h index 40c4ac5..48a2d6d 100644 --- a/src/build/cmeta.h +++ b/src/build/cmeta.h @@ -17,8 +17,6 @@ #ifndef INC_CMETA_H #define INC_CMETA_H -#include - #include "../os.h" struct cmeta; @@ -76,8 +74,8 @@ void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( * Iterates through all event-related macros and takes note of which events are * defined, giving a callback for each. */ -void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name)); - +void cmeta_evdefmacros(const struct cmeta *cm, void (*cb)(const char *name, + const char *const *params, int nparams, bool predicate)); /* * Iterates through all event-related macros and gives a callback for each event * that is handled by the given module. diff --git a/src/build/codegen.c b/src/build/codegen.c index 04a9058..f5231c0 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include @@ -139,12 +138,15 @@ push: if (!vec_push(vecp, param)) die("couldn't allocate memory"); DECL_SKIPLIST(static, event, struct event, const char *, 4) struct event { - const char *name; + usize name; // string, but tagged pointer - see below + const char *const *params; + int nparams; + //char pad[4]; struct vec_usize handlers; // strings, but with tagged pointers - see below struct skiplist_hdr_event hdr; }; static inline int cmp_event(struct event *e, const char *s) { - return strcmp(e->name, s); + return strcmp((const char *)(e->name & ~1ull), s); } static inline struct skiplist_hdr_event *hdr_event(struct event *e) { return &e->hdr; @@ -152,12 +154,15 @@ static inline struct skiplist_hdr_event *hdr_event(struct event *e) { DEF_SKIPLIST(static, event, cmp_event, hdr_event) static struct skiplist_hdr_event events = {0}; -static void onevdef(const char *name) { +static void onevdef(const char *name, const char *const *params, int nparams, + bool predicate) { struct event *e = skiplist_get_event(&events, name); if (!e) { struct event *e = malloc(sizeof(*e)); if (!e) die("couldn't allocate memory"); - e->name = name; + // hack: using unused pointer bit to distinguish the two types of event + e->name = (usize)name | predicate; + e->params = params; e->nparams = nparams; e->handlers = (struct vec_usize){0}; e->hdr = (struct skiplist_hdr_event){0}; skiplist_insert_event(&events, name, e); @@ -445,19 +450,70 @@ _( "") H_() for (const struct event *e = events.x[0]; e; e = e->hdr.x[0]) { _( "") -F( "void _evemit_%s(void) {", e->name) - for (usize *pp = e->handlers.data; - pp - e->handlers.data < e->handlers.sz; ++pp) { - const char *modname = (const char *)(*pp & ~1ull); -F( " void _evhandler_%s_%s(void);", modname, e->name) // blegh. - if (*pp & 1ull) { - // note: has_* variables are already included by this point (above) -F( " if (has_%s) _evhandler_%s_%s();", modname, modname, e->name) + // gotta break from the string emit macros for a sec in order to do the + // somewhat more complicated task sometimes referred to as a "for loop" + fprintf(out, "%s_%s(", e->name & 1 ? "bool CHECK" : "void EMIT", + (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "typeof(%s) a%d, ", e->params[n], n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "typeof(%s) a%d", e->params[e->nparams - 1], + e->nparams); } else { -F( " _evhandler_%s_%s();", modname, e->name) + // just unilaterally doing void for now. when we're fully on C23 + // eventually we can unilaterally do nothing instead + fputs("void", out); } +_( ") {") + for (usize *pp = e->handlers.data; + pp - e->handlers.data < e->handlers.sz; ++pp) { + const char *modname = (const char *)(*pp & ~1ull); + fprintf(out, "\t%s _evhandler_%s_%s(", e->name & 1 ? "bool" : "void", + modname, (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "typeof(%s) a%d, ", e->params[n], n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "typeof(%s) a%d", e->params[e->nparams - 1], + e->nparams); + } + else { + fputs("void", out); + } + fputs(");\n\t", out); + // conditional and non-conditional cases - in theory could be + // unified a bit but this is easier to make output relatively pretty + // note: has_* variables are already included by this point (above) + if (e->name & 1) { + if (*pp & 1) fprintf(out, "if (has_%s && !", modname); + else fprintf(out, "if (!"); + fprintf(out, "_evhandler_%s_%s(", modname, + (const char *)(e->name & ~1ull)); + // XXX: much repetitive drivel here + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "a%d,", n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "a%d", e->nparams); + } + fputs(")) return false;\n", out); + } + else { + if (*pp & 1) fprintf(out, "if (has_%s) ", modname); + fprintf(out, "_evhandler_%s_%s(", modname, + (const char *)(e->name & ~1ull)); + for (int n = 0; n < (int)e->nparams - 1; ++n) { + fprintf(out, "a%d,", n + 1); + } + if (e->nparams && strcmp(e->params[0], "void")) { + fprintf(out, "a%d", e->nparams); + } + fputs(");\n", out); + } } + if (e->name & 1) fputs("\treturn true;\n", out); _( "}") } if (fclose(out) == EOF) die("couldn't fully write evglue.gen.h"); diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c index 300dafa..fdb6982 100644 --- a/src/build/mkentprops.c +++ b/src/build/mkentprops.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c index 7d7a425..fdb2aef 100644 --- a/src/build/mkgamedata.c +++ b/src/build/mkgamedata.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include diff --git a/src/build/vec.h b/src/build/vec.h index 50b0a3b..6b4dffb 100644 --- a/src/build/vec.h +++ b/src/build/vec.h @@ -4,7 +4,6 @@ #define INC_VEC_H #include -#include #include #include "../intdefs.h" diff --git a/src/con_.c b/src/con_.c index cb3db5b..f8b24f6 100644 --- a/src/con_.c +++ b/src/con_.c @@ -15,7 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include // should be implied by stdlib but glibc is dumb (offsetof) #include #include @@ -64,8 +63,12 @@ DECL_VFUNC_DYN(struct con_var *, FindVar, const char *) DECL_VFUNC_DYN(struct con_cmd *, FindCommand, const char *) DECL_VFUNC_DYN(void, CallGlobalChangeCallbacks, struct con_var *, const char *, float) -DECL_VFUNC_CDECLDYN(void, ConsoleColorPrintf, const struct con_colour *, - const char *, ...) +// sad: since adding the cool abstraction, we can't do varargs (because you +// can't pass varargs to other varargs of course). we only get a pointer to it +// via VFUNC so just declare the typedef here - I don't wanna write any more +// macros today. +typedef void (*ConsoleColorPrintf_func)(void *, const struct con_colour *, + const char *, ...); static inline void initval(struct con_var *v) { // v->strlen is set to defaultval len in _DEF_CVAR so we don't need to call @@ -81,7 +84,7 @@ static inline void initval(struct con_var *v) { // required, we call the Internal* virtual functions by actual virtual lookup. // since the vtables are filled dynamically (below), we store this index; other // indices are just offset from this one since the 3-or-4 functions are all -// right next to each other. the #defines allow us to still use the nice VCALL +// right next to each other. the #defines allow us to still use the nice vcall // stuff. static int vtidx_InternalSetValue; #define vtidx_InternalSetFloatValue (vtidx_InternalSetValue + 1) @@ -180,10 +183,10 @@ static void VCALLCONV ChangeStringValue(struct con_var *this, const char *s, // do need callbacks for at least one feature, so do our own minimal thing if (this->cb) this->cb(this); // also call global callbacks, as is polite. - VCALL(_con_iface, CallGlobalChangeCallbacks, this, old, oldf); + CallGlobalChangeCallbacks(_con_iface, this, old, oldf); } -static void VCALLCONV InternalSetValue(struct con_var *this, const char *v) { +static void VCALLCONV InternalSetValue_impl(struct con_var *this, const char *v) { float oldf = this->fval; float newf = atof(v); char tmp[32]; @@ -198,7 +201,7 @@ static void VCALLCONV InternalSetValue(struct con_var *this, const char *v) { if (!(this->base.flags & CON_NOPRINT)) ChangeStringValue(this, v, oldf); } -static void VCALLCONV InternalSetFloatValue(struct con_var *this, float v) { +static void VCALLCONV InternalSetFloatValue_impl(struct con_var *this, float v) { if (v == this->fval) return; ClampValue(this, &v); float old = this->fval; @@ -210,7 +213,7 @@ static void VCALLCONV InternalSetFloatValue(struct con_var *this, float v) { } } -static void VCALLCONV InternalSetIntValue(struct con_var *this, int v) { +static void VCALLCONV InternalSetIntValue_impl(struct con_var *this, int v) { if (v == this->ival) return; float f = (float)v; if (ClampValue(this, &f)) v = (int)f; @@ -233,22 +236,22 @@ DECL_VFUNC_DYN(void, InternalSetColorValue, struct con_colour) static void VCALLCONV SetValue_str_thunk(void *thisoff, const char *v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - VCALL(&this->parent->base, InternalSetValue, v); + InternalSetValue(&this->parent->base, v); } static void VCALLCONV SetValue_f_thunk(void *thisoff, float v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - VCALL(&this->parent->base, InternalSetFloatValue, v); + InternalSetFloatValue(&this->parent->base, v); } static void VCALLCONV SetValue_i_thunk(void *thisoff, int v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - VCALL(&this->parent->base, InternalSetIntValue, v); + InternalSetIntValue(&this->parent->base, v); } static void VCALLCONV SetValue_colour_thunk(void *thisoff, struct con_colour v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - VCALL(&this->parent->base, InternalSetColorValue, v); + InternalSetColorValue(&this->parent->base, v); } // more misc thunks, hopefully these just compile to a sub and a jmp @@ -322,13 +325,13 @@ void *_con_vtab_iconvar[7] = { }; void con_reg(void *cmd_or_var) { - VCALL(_con_iface, RegisterConCommand, cmd_or_var); + RegisterConCommand(_con_iface, cmd_or_var); } void con_init(void) { // FIXME: ConsoleColorPrintf isn't working in Portal 2, possible regression? _con_colourmsgf = VFUNC(_con_iface, ConsoleColorPrintf); - dllid = VCALL(_con_iface, AllocateDLLIdentifier); + dllid = AllocateDLLIdentifier(_con_iface); void **pc = _con_vtab_cmd + 3 + NVDTOR, **pv = _con_vtab_var + 3 + NVDTOR, **pi = _con_vtab_iconvar @@ -362,13 +365,13 @@ void con_init(void) { *pv++ = (void *)&Init; // var-specific vtidx_InternalSetValue = pv - _con_vtab_var; - *pv++ = (void *)&InternalSetValue; - *pv++ = (void *)&InternalSetFloatValue; - *pv++ = (void *)&InternalSetIntValue; + *pv++ = (void *)&InternalSetValue_impl; + *pv++ = (void *)&InternalSetFloatValue_impl; + *pv++ = (void *)&InternalSetIntValue_impl; if (GAMETYPE_MATCHES(L4D2x) || GAMETYPE_MATCHES(Portal2)) { // ugh, annoying // This is InternalSetColorValue, but that's basically the same thing, // when you think about it. - *pv++ = (void *)&InternalSetIntValue; + *pv++ = (void *)&InternalSetIntValue_impl; } *pv++ = (void *)&ClampValue;; *pv++ = (void *)&ChangeStringValue; @@ -415,15 +418,15 @@ bool con_detect(int pluginver) { // *actually* calls the const-overloaded FindVar on other branches, // which just happens to still work fine. From there, we can figure out // the actual ABI to use to avoid spectacular crashes. - if (VCALL(_con_iface, FindCommandBase_p2, "portal2_square_portals")) { + if (FindCommandBase_p2(_con_iface, "portal2_square_portals")) { _gametype_tag |= _gametype_tag_Portal2; return true; } - if (VCALL(_con_iface, FindCommand_nonp2, "l4d2_snd_adrenaline")) { + if (FindCommand_nonp2(_con_iface, "l4d2_snd_adrenaline")) { // while we're here, also distinguish Survivors, the stupid Japanese // arcade game a few people seem to care about for some reason // (which for some other reason also has some vtable changes) - if (VCALL(_con_iface, FindVar_nonp2, "avatarbasemodel")) { + if (FindVar_nonp2(_con_iface, "avatarbasemodel")) { _gametype_tag |= _gametype_tag_L4DS; } else { @@ -431,7 +434,7 @@ bool con_detect(int pluginver) { } return true; } - if (VCALL(_con_iface, FindVar_nonp2, "z_difficulty")) { + if (FindVar_nonp2(_con_iface, "z_difficulty")) { _gametype_tag |= _gametype_tag_L4D1; return true; } @@ -463,16 +466,16 @@ bool con_detect(int pluginver) { } void con_disconnect(void) { - VCALL(_con_iface, UnregisterConCommands, dllid); + UnregisterConCommands(_con_iface, dllid); freevars(); } struct con_var *con_findvar(const char *name) { - return VCALL(_con_iface, FindVar, name); + return FindVar(_con_iface, name); } struct con_cmd *con_findcmd(const char *name) { - return VCALL(_con_iface, FindCommand, name); + return FindCommand(_con_iface, name); } #define GETTER(T, N, M) T N(const struct con_var *v) { return v->parent->M; } diff --git a/src/con_.h b/src/con_.h index 1de9bc5..a43294c 100644 --- a/src/con_.h +++ b/src/con_.h @@ -18,8 +18,6 @@ #ifndef INC_CON_H #define INC_CON_H -#include - #include "intdefs.h" #if defined(__GNUC__) || defined(__clang__) diff --git a/src/democustom.c b/src/democustom.c index c7de77b..f8c7c9d 100644 --- a/src/democustom.c +++ b/src/democustom.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "bitbuf.h" #include "con_.h" #include "democustom.h" @@ -121,7 +119,7 @@ INIT { // > there might be some other l4d2 versions where it's 11 but idk // So here we have to figure out the network protocol version! // NOTE: assuming engclient != null as GEBN index relies on client version - int buildnum = VCALL(engclient, GetEngineBuildNumber); + int buildnum = GetEngineBuildNumber(engclient); // condition is redundant until other GetEngineBuildNumber offsets are added // if (GAMETYPE_MATCHES(L4D2)) { nbits_msgtype = 6; diff --git a/src/demorec.c b/src/demorec.c index 50128f0..4434534 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -15,7 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include "con_.h" diff --git a/src/engineapi.c b/src/engineapi.c index cef085b..8cc3dd6 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include // used in generated code #include // " @@ -42,7 +41,6 @@ DECL_VFUNC(void *, GetGlobalVars, 1) void *globalvars; DECL_VFUNC_DYN(void *, GetAllServerClasses) -DECL_VFUNC_DYN(int, GetEngineBuildNumber) DECL_VFUNC(int, GetEngineBuildNumber_newl4d2, 99) // duping gamedata entry, yuck @@ -71,7 +69,7 @@ bool engineapi_init(int pluginver) { // } void *pim = factory_server("PlayerInfoManager002", 0); - if (pim) globalvars = VCALL(pim, GetGlobalVars); + if (pim) globalvars = GetGlobalVars(pim); void *srvdll; // TODO(compat): add this back when there's gamedata for 009 (no point atm) @@ -93,7 +91,7 @@ bool engineapi_init(int pluginver) { // till gamedata is set up, so we have to have a bit of redundant logic here // to bootstrap things. if (GAMETYPE_MATCHES(L4D2) && GAMETYPE_MATCHES(Client013) && - VCALL(engclient, GetEngineBuildNumber_newl4d2) >= 2200) { + GetEngineBuildNumber_newl4d2(engclient) >= 2200) { _gametype_tag |= _gametype_tag_TheLastStand; } @@ -104,7 +102,7 @@ bool engineapi_init(int pluginver) { if (!gameinfo_init()) { con_disconnect(); return false; } if (has_vtidx_GetAllServerClasses && has_sz_SendProp && has_off_SP_varname && has_off_SP_offset) { - initentprops(VCALL(srvdll, GetAllServerClasses)); + initentprops(GetAllServerClasses(srvdll)); } return true; } diff --git a/src/ent.c b/src/ent.c index 9d69bf0..4a457af 100644 --- a/src/ent.c +++ b/src/ent.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "engineapi.h" #include "errmsg.h" #include "feature.h" @@ -37,7 +35,7 @@ void *ent_getedict(int idx) { return mem_offset(*edicts, sz_edict * idx); } else { - return VCALL(engserver, PEntityOfEntIndex, idx); + return PEntityOfEntIndex(engserver, idx); } } diff --git a/src/event.h b/src/event.h index e439201..86a443e 100644 --- a/src/event.h +++ b/src/event.h @@ -20,15 +20,53 @@ #define _EVENT_CAT4_(a, b, c, d) a##b##c##d #define _EVENT_CAT4(a, b, c, d) _EVENT_CAT4_(a, b, c, d) -#define DECL_EVENT(evname) void _evemit_##evname(void); -#define DEF_EVENT(evname) \ - DECL_EVENT(evname) \ - static inline void _evown_##evname(void) { _evemit_##evname(); } -#define EMIT_EVENT(evname) _evown_##evname() - -#define HANDLE_EVENT(evname) \ - void _EVENT_CAT4(_evhandler_, MODULE_NAME, _, evname)(void) \ - /* function body here */ +/* + * Declares an event defined somewhere in the codebase, allowing a handler to be + * defined with HANDLE_EVENT() below. Takes an optional list of types for + * parameters. The handler will be called every time the event is emitted by the + * declaring module. + */ +#define DECL_EVENT(evname, ...) typedef void _must_declare_event_##evname; + +/* + * Declares a predicate - a special type of even returning bool. Predicates are + * used to determine whether some other action should be performed, and + * generally should not have side effects, since they get short-circuited and + * thus won't always fire when a check is being performed. + */ +#define DECL_PREDICATE(evname, ...) typedef bool _must_declare_event_##evname; + +/* + * Defines an event belonging to this module. Doing so allows EMIT_() to + * be called to fire handlers in all modules. Two modules (i.e. source files) + * cannot define an event (or predicate) with the same name. + */ +#define DEF_EVENT(event, ...) void EMIT_##event(__VA_ARGS__); + +/* + * Defines a predicate belonging to this module. Doing so allows CHECK_() + * to be called to determine whether to perform some action. Predicates share a + * namespace with events and two modules cannot define two things with the same + * name. + */ +#define DEF_PREDICATE(pred, ...) bool CHECK_##pred(__VA_ARGS__); + +/* + * Begins an event handler function that gets hooked up to an event by the code + * generation system. This is type-generic: if the event is a regular event, + * the function will return void; if it is a predicate it will return bool. + * Takes a function argument list which must match the type lists given to the + * above DEF/DECL macros. + * + * Note again that predicates are not guaranteed to fire at all due to + * short-circuiting and thus generally should not have side effects. + * + * In the current event implementation, each source file may handle only one of + * each event type, as any more wouldn't be too useful anyway. + */ +#define HANDLE_EVENT(evname, ...) \ + _must_declare_event_##evname _EVENT_CAT4(_evhandler_, MODULE_NAME, _, \ + evname)(__VA_ARGS__) /* function body here */ #endif diff --git a/src/extmalloc.c b/src/extmalloc.c index 9117db0..edd1d54 100644 --- a/src/extmalloc.c +++ b/src/extmalloc.c @@ -36,25 +36,17 @@ IMPORT void *g_pMemAlloc; // affected by naming (overloads are grouped, and *reversed* inside of a // group!?), we get this amusing ABI difference between platforms: #ifdef _WIN32 -DECL_VFUNC(void *, Alloc, 1, usize sz) -DECL_VFUNC(void *, Realloc, 3, void *mem, usize sz) -DECL_VFUNC(void, Free, 5, void *mem) +DECL_VFUNC(void *, Alloc, 1, usize) +DECL_VFUNC(void *, Realloc, 3, void *, usize) +DECL_VFUNC(void, Free, 5, void *) #else -DECL_VFUNC(void *, Alloc, 0, usize sz) -DECL_VFUNC(void *, Realloc, 1, void *mem, usize sz) -DECL_VFUNC(void, Free, 2, void *mem) +DECL_VFUNC(void *, Alloc, 0, usize) +DECL_VFUNC(void *, Realloc, 1, void *, usize) +DECL_VFUNC(void, Free, 2, void *) #endif -void *extmalloc(usize sz) { - return VCALL(g_pMemAlloc, Alloc, sz); -} - -void *extrealloc(void *mem, usize sz) { - return VCALL(g_pMemAlloc, Realloc, mem, sz); -} - -void extfree(void *mem) { - VCALL(g_pMemAlloc, Free, mem); -} +void *extmalloc(usize sz) { return Alloc(g_pMemAlloc, sz); } +void *extrealloc(void *mem, usize sz) { return Realloc(g_pMemAlloc, mem, sz); } +void extfree(void *mem) { Free(g_pMemAlloc, mem); } // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/feature.h b/src/feature.h index 5277bca..e1e4688 100644 --- a/src/feature.h +++ b/src/feature.h @@ -17,8 +17,6 @@ #ifndef INC_FEATURE_H #define INC_FEATURE_H -#include - #define _FEATURE_CAT1(a, b) a##b #define _FEATURE_CAT(a, b) _FEATURE_CAT1(a, b) diff --git a/src/fov.c b/src/fov.c index c7c72ed..9ab1137 100644 --- a/src/fov.c +++ b/src/fov.c @@ -17,8 +17,6 @@ // TODO(linux): theoretically, probably ifdef out the cvar-replacement stuff; we // expect any game that's been ported to linux to already have fov_desired -#include - #include "con_.h" #include "engineapi.h" #include "errmsg.h" @@ -74,10 +72,9 @@ static void fovcb(struct con_var *v) { } // ensure FOV is applied on load, if the engine wouldn't do that itself -HANDLE_EVENT(ClientActive) { - if (real_fov_desired == fov_desired) { - void *player = ent_get(1); // " - if (player) orig_SetDefaultFOV(player, con_getvari(fov_desired)); +HANDLE_EVENT(ClientActive, struct edict *player) { + if (player && real_fov_desired == fov_desired) { + orig_SetDefaultFOV(player, con_getvari(fov_desired)); } } diff --git a/src/fov.h b/src/fov.h index a057fb7..63cd170 100644 --- a/src/fov.h +++ b/src/fov.h @@ -17,8 +17,6 @@ #ifndef INC_FOV_H #define INC_FOV_H -#include - bool fov_init(bool has_ent); void fov_end(void); diff --git a/src/gamedata.c b/src/gamedata.c index 7b89257..639b2f8 100644 --- a/src/gamedata.c +++ b/src/gamedata.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "gametype.h" // same as gamedata.h, not worth putting in its own thing diff --git a/src/gamedata.h b/src/gamedata.h index 678d572..471e5bb 100644 --- a/src/gamedata.h +++ b/src/gamedata.h @@ -17,8 +17,6 @@ #ifndef INC_GAMEDATA_H #define INC_GAMEDATA_H -#include - #ifdef _WIN32 #define NVDTOR 1 #else diff --git a/src/gameinfo.c b/src/gameinfo.c index b371e76..f8b5311 100644 --- a/src/gameinfo.c +++ b/src/gameinfo.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #ifdef _WIN32 #include #endif @@ -260,7 +259,7 @@ bool gameinfo_init(void) { // to respect the system code page setting, otherwise some users using e.g. // Cyrillic folder names and successfully loading their speedgames won't be // able to load SST. Thanks Windows! - const char *lcpgamedir = VCALL(engclient, GetGameDirectory); + const char *lcpgamedir = GetGameDirectory(engclient); int gamedirlen = MultiByteToWideChar(CP_ACP, 0, lcpgamedir, strlen(lcpgamedir), gamedir, sizeof(gamedir) / sizeof(*gamedir)); if (!gamedirlen) { diff --git a/src/hook.h b/src/hook.h index ff1acff..8d91508 100644 --- a/src/hook.h +++ b/src/hook.h @@ -17,8 +17,6 @@ #ifndef INC_HOOK_H #define INC_HOOK_H -#include - #include "intdefs.h" bool hook_init(void); diff --git a/src/kv.c b/src/kv.c index 7720d46..019d017 100644 --- a/src/kv.c +++ b/src/kv.c @@ -14,8 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include - #include "intdefs.h" #include "kv.h" #include "unreachable.h" diff --git a/src/kv.h b/src/kv.h index 689f5bd..63a9146 100644 --- a/src/kv.h +++ b/src/kv.h @@ -17,8 +17,6 @@ #ifndef INC_KV_H #define INC_KV_H -#include - #include "intdefs.h" /* diff --git a/src/l4dwarp.c b/src/l4dwarp.c index 5412b64..5540763 100644 --- a/src/l4dwarp.c +++ b/src/l4dwarp.c @@ -34,14 +34,14 @@ REQUIRE_GAMEDATA(off_eyeang) REQUIRE_GAMEDATA(vtidx_Teleport) DECL_VFUNC_DYN(void *, GetBaseEntity) -DECL_VFUNC_DYN(void, Teleport, const struct vec3f *pos, const struct vec3f *ang, - const struct vec3f *vel) +DECL_VFUNC_DYN(void, Teleport, const struct vec3f */*pos*/, + const struct vec3f */*pos*/, const struct vec3f */*vel*/) DEF_CCMD_HERE_UNREG(sst_l4d_testwarp, "Simulate a bot warping to you", CON_SERVERSIDE | CON_CHEAT) { struct edict *ed = ent_getedict(con_cmdclient + 1); if (!ed) { errmsg_errorx("couldn't access player entity"); return; } - void *e = VCALL(ed->ent_unknown, GetBaseEntity); // is this call required? + void *e = GetBaseEntity(ed->ent_unknown); // is this call required? struct vec3f *org = mem_offset(e, off_entpos); struct vec3f *ang = mem_offset(e, off_eyeang); // L4D idle warps go up to 10 units behind relative to whatever angle the @@ -49,7 +49,7 @@ DEF_CCMD_HERE_UNREG(sst_l4d_testwarp, "Simulate a bot warping to you", // displacing vertically float pitch = ang->x * M_PI / 180, yaw = ang->y * M_PI / 180; float shift = -10 * cos(pitch); - VCALL(e, Teleport, &(struct vec3f){org->x + shift * cos(yaw), + Teleport(e, &(struct vec3f){org->x + shift * cos(yaw), org->y + shift * sin(yaw), org->z}, 0, &(struct vec3f){0, 0, 0}); } diff --git a/src/os.h b/src/os.h index 3d3ab00..c954d1a 100644 --- a/src/os.h +++ b/src/os.h @@ -17,8 +17,6 @@ #ifndef INC_OS_H #define INC_OS_H -#include - /* * Here we declare an absolute ton of wrappers, macros, compatibility shims, * reimplementations and so on to try in vain to sweep the inconsistencies diff --git a/src/portalcolours.c b/src/portalcolours.c index 231aa01..2231da4 100644 --- a/src/portalcolours.c +++ b/src/portalcolours.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include "con_.h" diff --git a/src/sst.c b/src/sst.c index c959d65..af15c66 100644 --- a/src/sst.c +++ b/src/sst.c @@ -14,7 +14,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -#include #include #ifdef _WIN32 @@ -429,20 +428,17 @@ DECL_VFUNC_DYN(void, ServerCommand, const char *) DEF_CVAR(_sst_onload_echo, "EXPERIMENTAL! Don't rely on this existing!", "", CON_HIDDEN) -DEF_EVENT(ClientActive) -DEF_EVENT(Tick) +DEF_EVENT(ClientActive, struct edict */*player*/) +DEF_EVENT(Tick, bool /*simulating*/) // Quick and easy server tick event. Eventually, we might want a deeper hook // for anything timing-sensitive, but this will do for our current needs. static void VCALLCONV GameFrame(void *this, bool simulating) { - EMIT_EVENT(Tick); + EMIT_Tick(simulating); } static void VCALLCONV ClientActive(void *this, struct edict *player) { - // XXX: it's kind of dumb that we get handed the edict here then go look it - // up again in fov.c but I can't be bothered refactoring any further now - // that this finally works, do something later lol - EMIT_EVENT(ClientActive); + EMIT_ClientActive(player); // continuing dumb portal hack. didn't even seem worth adding a feature for if (has_vtidx_ServerCommand && con_getvarstr(_sst_onload_echo)[0]) { @@ -454,7 +450,7 @@ static void VCALLCONV ClientActive(void *this, struct edict *player) { memcpy(s + 6, con_getvarstr(_sst_onload_echo), _sst_onload_echo->strlen); memcpy(s + 6 + _sst_onload_echo->strlen - 1, "\"\n", 3); - VCALL(engserver, ServerCommand, s); + ServerCommand(engserver, s); free(s); } } diff --git a/src/sst.h b/src/sst.h index d432480..a959e00 100644 --- a/src/sst.h +++ b/src/sst.h @@ -21,8 +21,8 @@ /* misc stuff that doesn't belong anywhere else */ -DECL_EVENT(ClientActive) -DECL_EVENT(Tick) +DECL_EVENT(ClientActive, struct edict */*player*/) +DECL_EVENT(Tick, bool /*simulating*/) extern void *clientlib; diff --git a/src/vcall.h b/src/vcall.h index caee70a..778ba09 100644 --- a/src/vcall.h +++ b/src/vcall.h @@ -35,32 +35,108 @@ #define VCALLCONV #endif +// black magic argument list maker thingmy, similar to the PPMAGIC_MAP thing, +// but slightly different, so we get to have all this nonsense twice. not sorry + +// note: arg numbering is counting down instead of up because it's easier to +// treat the vararg macros like a stack, and doesn't actually matter otherwise +// also note: I just did 16. that should be enough +#define _VCALL_ARG00() +#define _VCALL_ARG01(t) typeof(t) a1 +#define _VCALL_ARG02(t1, t2) typeof(t1) a2, typeof(t2) a1 +#define _VCALL_ARG03(t1, t2, t3) typeof(t1) a3, typeof(t2) a2, typeof(t3) a1 +#define _VCALL_ARG04(t1, t2, t3, t4) \ + typeof(t1) a4, typeof(t2) a3, typeof(t3) a2, typeof(t4) a1 +#define _VCALL_ARG05(t, ...) typeof(t) a5, _VCALL_ARG04(__VA_ARGS__) +#define _VCALL_ARG06(t, ...) typeof(t) a6, _VCALL_ARG05(__VA_ARGS__) +#define _VCALL_ARG07(t, ...) typeof(t) a7, _VCALL_ARG06(__VA_ARGS__) +#define _VCALL_ARG08(t, ...) typeof(t) a8, _VCALL_ARG07(__VA_ARGS__) +#define _VCALL_ARG09(t, ...) typeof(t) a9, _VCALL_ARG08(__VA_ARGS__) +#define _VCALL_ARG10(t, ...) typeof(t) a10, _VCALL_ARG09(__VA_ARGS__) +#define _VCALL_ARG11(t, ...) typeof(t) a11, _VCALL_ARG10(__VA_ARGS__) +#define _VCALL_ARG12(t, ...) typeof(t) a12, _VCALL_ARG11(__VA_ARGS__) +#define _VCALL_ARG13(t, ...) typeof(t) a13, _VCALL_ARG12(__VA_ARGS__) +#define _VCALL_ARG14(t, ...) typeof(t) a14, _VCALL_ARG14(__VA_ARGS__) +#define _VCALL_ARG15(t, ...) typeof(t) a15, _VCALL_ARG15(__VA_ARGS__) +#define _VCALL_ARG16(t, ...) typeof(t) a16, _VCALL_ARG16(__VA_ARGS__) + +#define _VCALL_ARG_N(x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, \ + x11, x12, x13, x14, x15, x16, N, ...) \ + _VCALL_ARG##N + +#define _VCALL_ARGLIST(...) \ + _VCALL_ARG_N(__VA_ARGS__ __VA_OPT__(,) \ + 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01, 00) \ + (__VA_ARGS__) + +// aannd we need these as well... +#define _VCALL_PASS00() +#define _VCALL_PASS01() a1 +#define _VCALL_PASS02() a2, a1 +#define _VCALL_PASS03() a3, a2, a1 +#define _VCALL_PASS04() a4, a3, a2, a1 +#define _VCALL_PASS05() a5, _VCALL_PASS04() +#define _VCALL_PASS06() a6, _VCALL_PASS05() +#define _VCALL_PASS07() a7, _VCALL_PASS06() +#define _VCALL_PASS08() a8, _VCALL_PASS07() +#define _VCALL_PASS09() a9, _VCALL_PASS08() +#define _VCALL_PASS10() a10, _VCALL_PASS09() +#define _VCALL_PASS11() a11, _VCALL_PASS10() +#define _VCALL_PASS12() a12, _VCALL_PASS11() +#define _VCALL_PASS13() a13, _VCALL_PASS12() +#define _VCALL_PASS14() a14, _VCALL_PASS13() +#define _VCALL_PASS15() a15, _VCALL_PASS14() +#define _VCALL_PASS16() a16, _VCALL_PASS15() +#define _VCALL_PASS_N(x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, \ + x12, x13, x14, x15, x16, N, ...) \ + _VCALL_PASS##N + +#define _VCALL_PASSARGS(...) \ + _VCALL_PASS_N(__VA_ARGS__ __VA_OPT__(,) 16, 15, 14, 13, 12, 11, 10, 09, \ + 08, 07, 06, 05, 04, 03, 02, 01, 00)() + +#define VFUNC(x, name) ((*(name##_func **)(x))[vtidx_##name]) +#define VCALL(x, name, ...) VFUNC(x, name)(x, ##__VA_ARGS__) + +// even more magic: return keyword only if not void +#define _VCALL_RETKW_(x, n, ...) n +#define _VCALL_RETKW(...) _VCALL_RETKW_(__VA_ARGS__, return,) +#define _VCALL_RET_void() x, , +#define _VCALL_RET(t) _VCALL_RETKW(_VCALL_RET_##t()) + +// I thought static inline was supposed to prevent unused warnings??? +#if defined(__GNUC__) || defined(__clang__) +#define _VCALL_UNUSED __attribute__((unused)) +#else +#define _VCALL_UNUSED +#endif + #define _DECL_VFUNC_DYN(ret, conv, name, ...) \ - /* XXX: GCC extension, seems worthwhile vs having two macros for one thing. - Replace with __VA_OPT__(,) whenever that gets fully standardised. */ \ - typedef ret (*conv name##_func)(void *this, ##__VA_ARGS__); + typedef typeof(ret) (*conv name##_func)(void * __VA_OPT__(,) __VA_ARGS__); \ + static inline _VCALL_UNUSED typeof(ret) name(void *this __VA_OPT__(,) \ + _VCALL_ARGLIST(__VA_ARGS__)) { \ + _VCALL_RET(ret) VCALL(this, name __VA_OPT__(,) \ + _VCALL_PASSARGS(__VA_ARGS__)); \ + } #define _DECL_VFUNC(ret, conv, name, idx, ...) \ enum { vtidx_##name = (idx) }; \ - _DECL_VFUNC_DYN(ret, conv, name, ##__VA_ARGS__) + _DECL_VFUNC_DYN(ret, conv, name __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with a known index */ #define DECL_VFUNC(ret, name, idx, ...) \ - _DECL_VFUNC(ret, VCALLCONV, name, idx, ##__VA_ARGS__) + _DECL_VFUNC(ret, VCALLCONV, name, idx __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with a known index, without thiscall convention */ #define DECL_VFUNC_CDECL(ret, name, idx, ...) \ - _DECL_VFUNC(ret, , name, idx, ##__VA_ARGS__) + _DECL_VFUNC(ret, , name, idx __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with an index defined elsewhere */ #define DECL_VFUNC_DYN(ret, name, ...) \ - _DECL_VFUNC_DYN(ret, VCALLCONV, name, ##__VA_ARGS__) + _DECL_VFUNC_DYN(ret, VCALLCONV, name __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with an index defined elsewhere, without thiscall */ #define DECL_VFUNC_CDECLDYN(ret, name, ...) \ - _DECL_VFUNC_DYN(ret, , name, ##__VA_ARGS__) - -#define VFUNC(x, name) ((*(name##_func **)(x))[vtidx_##name]) -#define VCALL(x, name, ...) VFUNC(x, name)(x, ##__VA_ARGS__) + _DECL_VFUNC_DYN(ret, , name __VA_OPT__(,) __VA_ARGS__) #endif diff --git a/src/x86util.h b/src/x86util.h index 0ae89ae..85a824e 100644 --- a/src/x86util.h +++ b/src/x86util.h @@ -17,8 +17,6 @@ #ifndef INC_X86UTIL_H #define INC_X86UTIL_H -#include - #include "errmsg.h" #include "x86.h" @@ -35,3 +33,5 @@ } while (0) #endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3