diff options
-rw-r--r-- | src/ac.c | 110 | ||||
-rw-r--r-- | src/alias.c | 6 | ||||
-rw-r--r-- | src/autojump.c | 15 | ||||
-rw-r--r-- | src/bind.c | 5 | ||||
-rw-r--r-- | src/bitbuf.h | 7 | ||||
-rw-r--r-- | src/build/codegen.c | 9 | ||||
-rw-r--r-- | src/build/mkentprops.c | 4 | ||||
-rw-r--r-- | src/build/mkgamedata.c | 2 | ||||
-rw-r--r-- | src/chunklets/msg.c | 6 | ||||
-rw-r--r-- | src/democustom.c | 3 | ||||
-rw-r--r-- | src/demorec.c | 15 | ||||
-rw-r--r-- | src/engineapi.c | 5 | ||||
-rw-r--r-- | src/ent.c | 16 | ||||
-rw-r--r-- | src/extmalloc.c | 9 | ||||
-rw-r--r-- | src/fastfwd.c | 46 | ||||
-rw-r--r-- | src/fixes.c | 14 | ||||
-rw-r--r-- | src/fov.c | 11 | ||||
-rw-r--r-- | src/gameinfo.c | 9 | ||||
-rw-r--r-- | src/gameserver.c | 5 | ||||
-rw-r--r-- | src/hook.c | 5 | ||||
-rw-r--r-- | src/hud.c | 19 | ||||
-rw-r--r-- | src/kv.c | 4 | ||||
-rw-r--r-- | src/kvsys.c | 5 | ||||
-rw-r--r-- | src/l4dmm.c | 27 | ||||
-rw-r--r-- | src/l4dreset.c | 34 | ||||
-rw-r--r-- | src/l4dwarp.c | 5 | ||||
-rw-r--r-- | src/langext.h | 64 | ||||
-rw-r--r-- | src/nomute.c | 10 | ||||
-rw-r--r-- | src/noreturn.h | 11 | ||||
-rw-r--r-- | src/nosleep.c | 5 | ||||
-rw-r--r-- | src/os.c | 13 | ||||
-rw-r--r-- | src/os.h | 10 | ||||
-rw-r--r-- | src/portalcolours.c | 7 | ||||
-rw-r--r-- | src/rinput.c | 31 | ||||
-rw-r--r-- | src/sst.c | 79 | ||||
-rw-r--r-- | src/unreachable.h | 14 | ||||
-rw-r--r-- | src/x86util.h | 7 | ||||
-rw-r--r-- | test/kv.test.c | 2 |
38 files changed, 356 insertions, 293 deletions
@@ -40,6 +40,7 @@ #include "gamedata.h" #include "gametype.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "ppmagic.h" @@ -102,34 +103,37 @@ static ulong inhooktid; static ssize __stdcall kproc(int code, usize wp, ssize lp) { KBDLLHOOKSTRUCT *data = (KBDLLHOOKSTRUCT *)lp; - if (enabled && data->flags & LLKHF_INJECTED && - GetForegroundWindow() == gamewin) { - // maybe this input is reasonable, but log it for closer inspection - // TODO(rta): figure out what to do with this stuff - // something like the following, but with a proper abstraction... - //uchar buf[28 + 16], *p = buf; - //msg_putasz4(p, 2); p += 1; - // msg_putssz5(p, 8); memcpy(p + 1, "FakeKey", 7); p += 8; - // msg_putmsz4(p, 2); p += 1; - // msg_putssz5(p, 3); memcpy(p + 1, "vk", 2); p += 3; - // p += msg_putu32(p, data->vkCode); - // msg_putssz5(p, 3); memcpy(p + 1, "scan", 4); p += 5; - // p += msg_putu32(p, data->scanCode); - //++keybox->nonce; - //// append mac at end of message - //crypto_aead_lock_djb(buf, p, keybox->shr, keybox->nonce_bytes, 0, 0, - // buf, p - buf); - //democustom_write(buf, p - buf + 16); + if_cold (enabled && data->flags & LLKHF_INJECTED) { + // fast-path the next branch because alt-tabbed speed is irrelevant + if_hot (GetForegroundWindow() == gamewin) { + // maybe this input is reasonable, but log it for closer inspection + // TODO(rta): figure out what to do with this stuff + // something like the following, but with a proper abstraction... + //uchar buf[28 + 16], *p = buf; + //msg_putasz4(p, 2); p += 1; + // msg_putssz5(p, 8); memcpy(p + 1, "FakeKey", 7); p += 8; + // msg_putmsz4(p, 2); p += 1; + // msg_putssz5(p, 3); memcpy(p + 1, "vk", 2); p += 3; + // p += msg_putu32(p, data->vkCode); + // msg_putssz5(p, 3); memcpy(p + 1, "scan", 4); p += 5; + // p += msg_putu32(p, data->scanCode); + //++keybox->nonce; + //// append mac at end of message + //crypto_aead_lock_djb(buf, p, keybox->shr, keybox->nonce_bytes, 0, + // 0, buf, p - buf); + //democustom_write(buf, p - buf + 16); + } } return CallNextHookEx(0, code, wp, lp); } static ssize __stdcall mproc(int code, usize wp, ssize lp) { MSLLHOOKSTRUCT *data = (MSLLHOOKSTRUCT *)lp; - if (enabled && data->flags & LLMHF_INJECTED && - GetForegroundWindow() == gamewin) { - // no way this input would ever be reasonable. just discard it - return 1; + if_cold (enabled && data->flags & LLMHF_INJECTED) { + if_hot (GetForegroundWindow() == gamewin) { + // no way this input would ever be reasonable. just discard it + return 1; + } } return CallNextHookEx(0, code, wp, lp); } @@ -138,7 +142,7 @@ static ssize __stdcall mproc(int code, usize wp, ssize lp) { // hook gets silently removed. plus, we don't wanna incur latency anyway. static ulong __stdcall inhookthrmain(void *param) { volatile int *sig = param; - if (!SetWindowsHookExW(WH_KEYBOARD_LL, (HOOKPROC)&kproc, 0, 0) || + if_cold (!SetWindowsHookExW(WH_KEYBOARD_LL, (HOOKPROC)&kproc, 0, 0) || !SetWindowsHookExW(WH_MOUSE_LL, (HOOKPROC)&mproc, 0, 0)) { fastspin_raise(sig, 2); return -1; @@ -151,29 +155,31 @@ static ulong __stdcall inhookthrmain(void *param) { static ssize orig_wndproc; static ssize __stdcall hook_wndproc(void *wnd, uint msg, usize wp, ssize lp) { - if (msg == WM_COPYDATA && enabled) return DefWindowProcW(wnd, msg, wp, lp); + if_cold (msg == WM_COPYDATA && enabled) { + return DefWindowProcW(wnd, msg, wp, lp); + } return CallWindowProcA((WNDPROC)orig_wndproc, wnd, msg, wp, lp); } -static bool win32_init(void) { +static inline bool win32_init(void) { // note: using A instead of W to avoid some weirdness with handles... gamewin = FindWindowA("Valve001", 0); // note: error messages here are a bit cryptic on purpose, but easy to find // in the code. in other words, we're hiding in plain sight :-) - if (!gamewin) { + if_cold (!gamewin) { errmsg_errorsys("failed to find window"); return false; } orig_wndproc = SetWindowLongPtrA(gamewin, GWLP_WNDPROC, (ssize)&hook_wndproc); - if (!orig_wndproc) { // XXX: assuming 0 won't be legitimately returned + if_cold (!orig_wndproc) { // XXX: assuming 0 won't be legitimately returned errmsg_errorsys("failed to attach message handler"); return false; } return true; } -static void win32_end(void) { +static inline void win32_end(void) { // no error handling here because we'd crash either way. good luck! SetWindowLongPtrA(gamewin, GWLP_WNDPROC, orig_wndproc); } @@ -188,7 +194,7 @@ static void inhook_check(void) { if (WaitForSingleObject(inhookthr, 0) == WAIT_OBJECT_0) { ulong status; GetExitCodeThread(inhookthr, &status); - if (status) { + if_cold (status) { // XXX: if this ever happens, it's a disaster! users might not // notice their run just dying all of a sudden. with any luck it // won't matter in practice but... this kind of sucks. @@ -209,7 +215,7 @@ static void inhook_stop(void) { // assume WAIT_OBJECT_0 ulong status; GetExitCodeThread(inhookthr, &status); - if (status) { + if_cold (status) { // not much else we can do now! errmsg_errorx("message loop didn't shut down cleanly\n"); } @@ -224,17 +230,18 @@ static void inhook_stop(void) { #endif bool ac_enable(void) { - if (enabled) return true; + if (!enabled) { #ifdef _WIN32 - volatile int sig = 0; - inhook_start(&sig); - fastspin_wait(&sig); - if (sig == 2) { // else 1 for success - con_warn("** sst: ERROR starting message loop, can't continue! **"); - CloseHandle(inhookthr); - return false; - } + volatile int sig = 0; + inhook_start(&sig); + fastspin_wait(&sig); + if_cold (sig == 2) { // else 1 for success + con_warn("** sst: ERROR starting message loop, can't continue! **"); + CloseHandle(inhookthr); + return false; + } #endif + } enabled = true; return true; } @@ -248,10 +255,11 @@ HANDLE_EVENT(Tick, bool simulating) { } void ac_disable(void) { - if (!enabled) return; + if (enabled) { #ifdef _WIN32 - inhook_stop(); + inhook_stop(); #endif + } enabled = false; } @@ -308,7 +316,7 @@ static bool find_Key_Event(void) { // -> CGame::DispatchAllStoredGameMessages vfunc // -> First call instruction (either DispatchInputEvent or Key_Event) void *gameuifuncs = factory_engine("VENGINE_GAMEUIFUNCS_VERSION005", 0); - if (!gameuifuncs) { + if_cold (!gameuifuncs) { errmsg_errorx("couldn't get engine game UI interface"); return false; } @@ -355,7 +363,7 @@ ok2: } HANDLE_EVENT(AllowPluginLoading, bool loading) { - if (enabled && demorec_demonum() != -1) { + if_cold(enabled) if_hot(demorec_demonum() != -1) { con_warn("sst: plugins cannot be %s while recording a run\n", loading ? "loaded" : "unloaded"); return false; @@ -375,39 +383,39 @@ PREINIT { } INIT { - if (!find_Key_Event()) return false; + if_cold (!find_Key_Event()) return false; orig_Key_Event = (Key_Event_func)hook_inline((void *)orig_Key_Event, (void *)&hook_Key_Event); - if (!orig_Key_Event) { + if_cold (!orig_Key_Event) { errmsg_errorsys("couldn't hook Key_Event function"); return false; } #ifdef _WIN32 keybox = VirtualAlloc(0, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - if (!keybox) { + if_cold (!keybox) { errmsg_errorsys("couldn't allocate memory for session state"); return false; } - if (!VirtualLock(keybox, 4096)) { + if_cold (!VirtualLock(keybox, 4096)) { errmsg_errorsys("couldn't secure session state"); goto e2; } - if (WerRegisterExcludedMemoryBlock(keybox, 4096) != S_OK) { + if_cold (WerRegisterExcludedMemoryBlock(keybox, 4096) != S_OK) { // FIXME: stringify errors properly here errmsg_errorx("couldn't secure session state"); goto e2; } - if (!win32_init()) goto e; + if_cold (!win32_init()) goto e; #else keybox = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (keybox == MAP_FAILED) { + if_cold (keybox == MAP_FAILED) { errmsg_errorstd("couldn't allocate memory for session state"); return false; } // linux-specific madvise stuff (there are some equivalents in OpenBSD and // FreeBSD, if anyone's wondering, but we don't need to worry about those) - if (madvise(keybox, 4096, MADV_DONTFORK) == -1 || + if_cold (madvise(keybox, 4096, MADV_DONTFORK) == -1 || madvise(keybox, 4096, MADV_DONTDUMP) == - 1 || mlock(keybox, 4096) == -1) { errmsg_errorstd("couldn't secure session state"); diff --git a/src/alias.c b/src/alias.c index 6af7467..e6f5f7e 100644 --- a/src/alias.c +++ b/src/alias.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -94,10 +94,10 @@ INIT { if (GAMETYPE_MATCHES(Portal2)) return false; struct con_cmd *cmd_alias = con_findcmd("alias"); - if (!find_alias_head(con_getcmdcb(cmd_alias))) { + if_cold (!find_alias_head(con_getcmdcb(cmd_alias))) { errmsg_warnx("couldn't find alias list"); return false; - }; + } con_reg(sst_alias_clear); con_reg(sst_alias_remove); return true; diff --git a/src/autojump.c b/src/autojump.c index 1bfc170..cc44573 100644 --- a/src/autojump.c +++ b/src/autojump.c @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,8 +20,9 @@ #include "feature.h" #include "gamedata.h" #include "gametype.h" -#include "intdefs.h" #include "hook.h" +#include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "vcall.h" @@ -76,7 +77,7 @@ static bool unprot(void *gm) { // reimplementing cheats check for dumb and bad reasons, see below static struct con_var *sv_cheats; static void cheatcb(struct con_var *this) { - if (this->ival && !con_getvari(sv_cheats)) { + if (this->ival) if_cold(!con_getvari(sv_cheats)) { con_warn("Can't use cheat cvar sst_autojump, unless server has " "sv_cheats set to 1.\n"); con_setvari(this, 0); @@ -85,17 +86,17 @@ static void cheatcb(struct con_var *this) { INIT { gmsv = factory_server("GameMovement001", 0); - if (!gmsv) { + if_cold (!gmsv) { errmsg_errorx("couldn't get server-side game movement interface"); return false; } - if (!unprot(gmsv)) return false; + if_cold (!unprot(gmsv)) return false; gmcl = factory_client("GameMovement001", 0); - if (!gmcl) { + if_cold (!gmcl) { errmsg_errorx("couldn't get client-side game movement interface"); return false; } - if (!unprot(gmcl)) return false; + if_cold (!unprot(gmcl)) return false; origsv = (CheckJumpButton_func)hook_vtable(*(void ***)gmsv, vtidx_CheckJumpButton, (void *)&hooksv); origcl = (CheckJumpButton_func)hook_vtable(*(void ***)gmcl, @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include "feature.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "x86.h" #include "x86util.h" @@ -57,7 +58,7 @@ static bool find_keyinfo(con_cmdcb klbc_cb) { INIT { struct con_cmd *cmd_key_listboundkeys = con_findcmd("key_listboundkeys"); con_cmdcb cb = con_getcmdcb(cmd_key_listboundkeys); - if (!find_keyinfo(cb)) { + if_cold (!find_keyinfo(cb)) { errmsg_warnx("couldn't find key binding list"); return false; } diff --git a/src/bitbuf.h b/src/bitbuf.h index 404dc9d..9e0fe19 100644 --- a/src/bitbuf.h +++ b/src/bitbuf.h @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -77,11 +77,12 @@ static inline void bitbuf_appendbuf(struct bitbuf *bb, const char *buf, // shift the stored value (if it were big endian, the shift would have // to be the other way, or something) _bitbuf_append(bb, *p >> (unalign << 3), (bitbuf_align - unalign) << 3); - buf += sizeof(bitbuf_cell) - unalign; + buf += (int)sizeof(bitbuf_cell) - unalign; len -= unalign; } bitbuf_cell *aligned = (bitbuf_cell *)buf; - for (; len >= sizeof(bitbuf_cell); len -= sizeof(bitbuf_cell), ++aligned) { + for (; len >= (int)sizeof(bitbuf_cell); len -= (int)sizeof(bitbuf_cell), + ++aligned) { _bitbuf_append(bb, *aligned, bitbuf_cell_bits); } // unaligned end bytes diff --git a/src/build/codegen.c b/src/build/codegen.c index 3cf4d8b..70b5e12 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include <string.h> #include "../intdefs.h" +#include "../langext.h" #include "../os.h" #include "cmeta.h" #include "skiplist.h" @@ -45,8 +46,8 @@ static struct conent { static int nconents; #define PUT(name_, isvar_, unreg_) do { \ - if (nconents == sizeof(conents) / sizeof(*conents)) { \ - fprintf(stderr, "codegen: out of space; make ents bigger!\n"); \ + if (nconents == countof(conents)) { \ + fprintf(stderr, "codegen: out of space; make conents bigger!\n"); \ exit(1); \ } \ conents[nconents].name = name_; \ @@ -116,7 +117,7 @@ static struct skiplist_hdr_feature_bydesc features_bydesc = {0}; static void onfeatinfo(enum cmeta_featmacro type, const char *param, void *ctxt) { struct feature *f = ctxt; - switch (type) { + switch_exhaust_enum (cmeta_featmacro, type) { case CMETA_FEAT_REQUIRE:; bool optional = false; goto dep; case CMETA_FEAT_REQUEST: optional = true; dep:; struct feature *dep = skiplist_get_feature(&features, param); diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c index 2043a9b..fdbb8af 100644 --- a/src/build/mkentprops.c +++ b/src/build/mkentprops.c @@ -20,7 +20,7 @@ #include "../intdefs.h" #include "../kv.h" -#include "../noreturn.h" +#include "../langext.h" #include "../os.h" #include "skiplist.h" #include "vec.h" @@ -73,7 +73,7 @@ static noreturn badparse(struct parsestate *state, const char *e) { static void kv_cb(enum kv_token type, const char *p, uint len, void *ctxt) { struct parsestate *state = ctxt; - switch (type) { + switch_exhaust_enum (kv_token, type) { case KV_IDENT: case KV_IDENT_QUOTED: state->lastvar = malloc(len + 1); if (!state->lastvar) die("couldn't allocate memory"); diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c index bf359b2..266a411 100644 --- a/src/build/mkgamedata.c +++ b/src/build/mkgamedata.c @@ -20,7 +20,7 @@ #include "../intdefs.h" #include "../kv.h" -#include "../noreturn.h" +#include "../langext.h" #include "../os.h" #include "vec.h" diff --git a/src/chunklets/msg.c b/src/chunklets/msg.c index d99edc1..5b688cb 100644 --- a/src/chunklets/msg.c +++ b/src/chunklets/msg.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -155,9 +155,7 @@ int msg_putu32(unsigned char *out, unsigned int val) { } int msg_puts(unsigned char *out, long long val) { - if (val >= -2147483648 && val <= 2147483647) { - return msg_puts32(out, val); - } + if (val >= -2147483648 && val <= 2147483647) return msg_puts32(out, val); out[0] = 0xD3; doput64(out, val); return 9; diff --git a/src/democustom.c b/src/democustom.c index 4c1baf2..30fc4ee 100644 --- a/src/democustom.c +++ b/src/democustom.c @@ -24,6 +24,7 @@ #include "feature.h" #include "gamedata.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "ppmagic.h" #include "vcall.h" @@ -46,7 +47,7 @@ static union { bitbuf_cell _align; // just in case... } bb_buf; static struct bitbuf bb = { - {bb_buf.x}, sizeof(bb_buf), sizeof(bb_buf) * 8, 0, false, false, "SST" + {bb_buf.x}, ssizeof(bb_buf), ssizeof(bb_buf) * 8, 0, false, false, "SST" }; static const void *createhdr(struct bitbuf *msg, int len, bool last) { diff --git a/src/demorec.c b/src/demorec.c index 8abba77..4a8efb5 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -26,6 +26,7 @@ #include "gameinfo.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "ppmagic.h" @@ -99,7 +100,7 @@ static struct con_cmd *cmd_record, *cmd_stop; static con_cmdcb orig_record_cb, orig_stop_cb; static void hook_record_cb(const struct con_cmdargs *args) { - if (!CHECK_DemoControlAllowed()) return; + if_cold (!CHECK_DemoControlAllowed()) return; bool was = *recording; if (!was && args->argc == 2 || args->argc == 3) { // safety check: make sure a directory exists, otherwise recording @@ -162,7 +163,7 @@ static void hook_record_cb(const struct con_cmdargs *args) { } static void hook_stop_cb(const struct con_cmdargs *args) { - if (!CHECK_DemoControlAllowed()) return; + if_cold (!CHECK_DemoControlAllowed()) return; wantstop = true; orig_stop_cb(args); wantstop = false; @@ -258,21 +259,21 @@ INIT { orig_record_cb = con_getcmdcb(cmd_record); cmd_stop = con_findcmd("stop"); orig_stop_cb = con_getcmdcb(cmd_stop); - if (!find_demorecorder()) { + if_cold (!find_demorecorder()) { errmsg_errorx("couldn't find demo recorder instance"); return false; } void **vtable = mem_loadptr(demorecorder); // XXX: 16 is totally arbitrary here! figure out proper bounds later - if (!os_mprot(vtable, 16 * sizeof(void *), PAGE_READWRITE)) { + if_cold (!os_mprot(vtable, 16 * sizeof(void *), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); return false; } - if (!find_recmembers(vtable[vtidx_StopRecording])) { + if_cold (!find_recmembers(vtable[vtidx_StopRecording])) { errmsg_errorx("couldn't find recording state variables"); return false; } - if (!find_demoname(vtable[vtidx_StartRecording])) { + if_cold (!find_demoname(vtable[vtidx_StartRecording])) { errmsg_errorx("couldn't find demo basename variable"); return false; } @@ -290,7 +291,7 @@ INIT { } END { - if (!sst_userunloaded) return; + if_hot (!sst_userunloaded) return; // avoid dumb edge case if someone somehow records and immediately unloads if (*recording && *demonum == 0) *demonum = 1; void **vtable = *(void ***)demorecorder; diff --git a/src/engineapi.c b/src/engineapi.c index f3c8c6d..04e6a8c 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -24,6 +24,7 @@ #include "gameinfo.h" #include "gametype.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" // " #include "os.h" #include "vcall.h" @@ -50,7 +51,7 @@ DECL_VFUNC_DYN(void *, GetAllServerClasses) #include <entpropsinit.gen.h> // generated by build/mkentprops.c bool engineapi_init(int pluginver) { - if (!con_detect(pluginver)) return false; + if_cold (!con_detect(pluginver)) return false; pluginhandler = factory_engine("ISERVERPLUGINHELPERS001", 0); if (engclient = factory_engine("VEngineClient015", 0)) { @@ -104,7 +105,7 @@ bool engineapi_init(int pluginver) { gamedata_init(); con_init(); - if (!gameinfo_init()) { con_disconnect(); return false; } + if_cold (!gameinfo_init()) { con_disconnect(); return false; } return true; } @@ -22,6 +22,7 @@ #include "gamedata.h" #include "gametype.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "vcall.h" #include "x86.h" @@ -35,8 +36,8 @@ static struct edict **edicts = 0; struct edict *ent_getedict(int idx) { if (edicts) { // globalvars->edicts seems to be null when disconnected - if (!*edicts) return 0; - return mem_offset(*edicts, sz_edict * idx); + if_hot (*edicts) return mem_offset(*edicts, sz_edict * idx); + return 0; } else { return PEntityOfEntIndex(engserver, idx); @@ -45,8 +46,8 @@ struct edict *ent_getedict(int idx) { void *ent_get(int idx) { struct edict *e = ent_getedict(idx); - if (!e) return 0; - return e->ent_unknown; + if_hot(e) return e->ent_unknown; + return 0; } struct CEntityFactory { @@ -143,14 +144,14 @@ void **ent_findvtable(const struct CEntityFactory *factory, const char *classname) { #ifdef _WIN32 ctor_func ctor = findctor(factory, classname); - if (!ctor) return 0; + if_cold (!ctor) return 0; const uchar *insns = (const uchar *)ctor; // the constructor itself should do *(void**)this = &vtable; almost right // away, so look for the first immediate load into indirect register for (const uchar *p = insns; p - insns < 32;) { if (p[0] == X86_MOVMIW && (p[1] & 0xF8) == 0) return mem_loadptr(p + 2); int len = x86_len(p); - if (len == -1) { + if_cold (len == -1) { errmsg_errorx("unknown or invalid instruction looking for %s " "vtable pointer", classname); return 0; @@ -166,7 +167,8 @@ void **ent_findvtable(const struct CEntityFactory *factory, INIT { #ifdef _WIN32 // TODO(linux): above struct con_cmd *dumpentityfactories = con_findcmd("dumpentityfactories"); - if (!dumpentityfactories || !find_entfactorydict(dumpentityfactories->cb)) { + if_cold (!dumpentityfactories || + !find_entfactorydict(dumpentityfactories->cb)) { errmsg_warnx("server entity factories unavailable"); } #endif diff --git a/src/extmalloc.c b/src/extmalloc.c index a23dc2a..d80ab8d 100644 --- a/src/extmalloc.c +++ b/src/extmalloc.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,6 +41,8 @@ void extfree(void *mem) { Free(g_pMemAlloc, mem); } #else +#include "langext.h" + void Error(const char *fmt, ...); // stub left out of con_.h (not that useful) // Linux Source doesn't seem to bother with the custom allocator stuff at all. @@ -48,15 +50,16 @@ void Error(const char *fmt, ...); // stub left out of con_.h (not that useful) // right, not a privilege. Like func_vehicle. void *extmalloc(usize sz) { void *ret = malloc(sz); - if (!ret) Error("sst: out of memory"); + if_cold (!ret) Error("sst: out of memory"); return ret; } void *extrealloc(void *mem, usize sz) { void *ret = realloc(mem, sz); - if (!ret) Error("sst: out of memory"); + if_cold (!ret) Error("sst: out of memory"); return ret; } // note: extfree is #defined to free in the header + #endif // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/fastfwd.c b/src/fastfwd.c index 92d7623..e287770 100644 --- a/src/fastfwd.c +++ b/src/fastfwd.c @@ -26,6 +26,7 @@ #include "feature.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "ppmagic.h" @@ -46,19 +47,14 @@ static float *realtime, *host_frametime; static float skiptime = 0.0, skiprate; static void hook_Host_AccumulateTime(float dt) { float skipinc = skiprate * dt; - if (skiptime > skipinc) { - skiptime -= skipinc; - *realtime += skipinc; - *host_frametime = skipinc; - } - else if (skiptime > 0) { - *realtime += skiptime; - *host_frametime = skiptime; - skiptime = 0; - } - else { + if_hot (!skiptime) { orig_Host_AccumulateTime(dt); + return; } + if_random (skiptime <= skipinc) skipinc = skiptime; // should become fcmovbe + skiptime -= skipinc; + *realtime += skipinc; + *host_frametime = skipinc; } void fastfwd(float seconds, float timescale) { @@ -194,67 +190,67 @@ PREINIT { INIT { void *hldsapi = factory_engine("VENGINE_HLDS_API_VERSION002", 0); - if (!hldsapi) { + if_cold (!hldsapi) { errmsg_errorx("couldn't find HLDS API interface"); return false; } void *enginetool = factory_engine("VENGINETOOL003", 0); - if (!enginetool) { + if_cold (!enginetool) { errmsg_errorx("missing engine tool interface"); return false; } // behold: the greatest pointer chase of all time realtime = find_float((*(void ***)enginetool)[vtidx_GetRealTime]); - if (!realtime) { + if_cold (!realtime) { errmsg_errorx("couldn't find realtime variable"); return false; } host_frametime = find_float((*(void ***)enginetool)[vtidx_HostFrameTime]); - if (!host_frametime) { + if_cold (!host_frametime) { errmsg_errorx("couldn't find host_frametime variable"); return false; } void *eng = find_eng((*(void ***)hldsapi)[vtidx_RunFrame]); - if (!eng) { + if_cold (!eng) { errmsg_errorx("couldn't find eng global object"); return false; } void *func; - if (!(func = find_HostState_Frame((*(void ***)eng)[vtidx_Frame]))) { + if_cold (!(func = find_HostState_Frame((*(void ***)eng)[vtidx_Frame]))) { errmsg_errorx("couldn't find HostState_Frame function"); return false; } - if (!(func = find_FrameUpdate(func))) { + if_cold (!(func = find_FrameUpdate(func))) { errmsg_errorx("couldn't find FrameUpdate function"); return false; } - if (!(func = find_floatcall(func, GAMETYPE_MATCHES(L4D2_2147plus) ? 2 : 1, - "CHostState::State_Run"))) { + if_cold (!(func = find_floatcall(func, GAMETYPE_MATCHES(L4D2_2147plus) ? + 2 : 1, "CHostState::State_Run"))) { errmsg_errorx("couldn't find State_Run function"); return false; } - if (!(func = find_floatcall(func, 1, "Host_RunFrame"))) { + if_cold (!(func = find_floatcall(func, 1, "Host_RunFrame"))) { errmsg_errorx("couldn't find Host_RunFrame function"); return false; } - if (!(func = find_floatcall(func, 1, "_Host_RunFrame"))) { + if_cold (!(func = find_floatcall(func, 1, "_Host_RunFrame"))) { errmsg_errorx("couldn't find _Host_RunFrame"); return false; } - if (!find_Host_AccumulateTime(func)) { + if_cold (!find_Host_AccumulateTime(func)) { errmsg_errorx("couldn't find Host_AccumulateTime"); return false; } orig_Host_AccumulateTime = (Host_AccumulateTime_func)hook_inline( (void *)orig_Host_AccumulateTime, (void *)hook_Host_AccumulateTime); - if (!orig_Host_AccumulateTime) { + if_cold (!orig_Host_AccumulateTime) { errmsg_errorsys("couldn't hook Host_AccumulateTime function"); } return true; } END { - if (!sst_userunloaded) return; + if_hot (!sst_userunloaded) return; unhook_inline((void *)orig_Host_AccumulateTime); } diff --git a/src/fixes.c b/src/fixes.c index 79f4e3d..226c5a9 100644 --- a/src/fixes.c +++ b/src/fixes.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * Copyright © 2023 Hayden K <imaciidz@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any @@ -24,6 +24,7 @@ #include "con_.h" #include "gametype.h" +#include "langext.h" static void chflags(const char *name, int unset, int set) { struct con_var *v = con_findvar(name); @@ -118,7 +119,7 @@ static void l4d2specific(void) { // possible on these earlier versions (who knows if that breaks // something...). struct con_var *v = con_findvar("mat_queue_mode"); - if (v && !(v->parent->base.flags & CON_ARCHIVE)) { // not already fixed + if_hot (v && !(v->parent->base.flags & CON_ARCHIVE)) { // not already fixed v->parent->base.flags = v->parent->base.flags & ~(CON_HIDDEN | CON_DEVONLY) | CON_ARCHIVE; v->parent->hasmin = true; v->parent->minval = -1; @@ -132,15 +133,14 @@ static void l4d2specific(void) { // so just blanket enable it if the primary adapter is Intel, since it // doesn't seem to break anything else anyway. v = con_findvar("mat_tonemapping_occlusion_use_stencil"); - if (!v || con_getvari(v)) goto e; + if_cold (!v || con_getvari(v)) goto e; // considered getting d3d9 object from actual game, but it's way easier // to just create another one IDirect3D9 *d3d9 = Direct3DCreate9(D3D_SDK_VERSION); - if (!d3d9) goto e; + if_cold (!d3d9) goto e; D3DADAPTER_IDENTIFIER9 ident; - if (IDirect3D9_GetAdapterIdentifier(d3d9, 0, 0, &ident) == D3D_OK && - ident.VendorId == 0x8086) { // neat vendor id, btw! - con_setvari(v, 1); + if_hot (IDirect3D9_GetAdapterIdentifier(d3d9, 0, 0, &ident) == D3D_OK) { + if (ident.VendorId == 0x8086) con_setvari(v, 1); // neat vendor id, btw! } IDirect3D9_Release(d3d9); e:; @@ -27,6 +27,7 @@ #include "gametype.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "sst.h" #include "vcall.h" @@ -67,7 +68,7 @@ static bool find_SetDefaultFOV(struct con_cmd *fov) { // replacement cvar needs to actively set player fov if in a map static void fovcb(struct con_var *v) { void *player = ent_get(1); // NOTE: singleplayer only! - if (player) orig_SetDefaultFOV(player, con_getvari(v)); + if_hot (player) orig_SetDefaultFOV(player, con_getvari(v)); } // ensure FOV is applied on load, if the engine wouldn't do that itself @@ -87,7 +88,7 @@ PREINIT { INIT { cmd_fov = con_findcmd("fov"); - if (!cmd_fov) return false; // shouldn't really happen but just in case! + if_cold (!cmd_fov) return false; // shouldn't happen but just in case! if (real_fov_desired = con_findvar("fov_desired")) { // latest steampipe already goes up to 120 fov if (real_fov_desired->parent->maxval == 120) return false; @@ -98,13 +99,13 @@ INIT { con_reg(fov_desired); real_fov_desired = fov_desired; } - if (!find_SetDefaultFOV(cmd_fov)) { + if_cold (!find_SetDefaultFOV(cmd_fov)) { errmsg_errorx("couldn't find SetDefaultFOV function"); return false; } orig_SetDefaultFOV = (SetDefaultFOV_func)hook_inline( (void *)orig_SetDefaultFOV, (void *)&hook_SetDefaultFOV); - if (!orig_SetDefaultFOV) { + if_cold (!orig_SetDefaultFOV) { errmsg_errorsys("couldn't hook SetDefaultFOV function"); return false; } @@ -118,7 +119,7 @@ INIT { } END { - if (!sst_userunloaded) return; + if_hot (!sst_userunloaded) return; if (real_fov_desired && real_fov_desired != fov_desired) { real_fov_desired->parent->maxval = 90; if (con_getvarf(real_fov_desired) > 90) { diff --git a/src/gameinfo.c b/src/gameinfo.c index d535a44..21205b7 100644 --- a/src/gameinfo.c +++ b/src/gameinfo.c @@ -23,6 +23,7 @@ #include "errmsg.h" #include "gamedata.h" #include "gametype.h" +#include "langext.h" #include "os.h" #include "vcall.h" @@ -41,7 +42,7 @@ const char *gameinfo_title = title; DECL_VFUNC_DYN(const char *, GetGameDirectory) bool gameinfo_init(void) { - if (!has_vtidx_GetGameDirectory) { + if_cold (!has_vtidx_GetGameDirectory) { errmsg_errorx("unsupported VEngineClient interface"); return false; } @@ -53,8 +54,8 @@ bool gameinfo_init(void) { // using e.g. Cyrillic folder names and successfully loading their // speedgames won't be able to load SST. Thanks Windows! const char *lcpgamedir = GetGameDirectory(engclient); - if (!MultiByteToWideChar(CP_ACP, 0, lcpgamedir, strlen(lcpgamedir), gamedir, - sizeof(gamedir) / sizeof(*gamedir))) { + if_cold (!MultiByteToWideChar(CP_ACP, 0, lcpgamedir, strlen(lcpgamedir), + gamedir, countof(gamedir))) { errmsg_errorsys("couldn't convert game directory path character set"); return false; } @@ -73,7 +74,7 @@ bool gameinfo_init(void) { // XXX: this same FindWindow call happens in ac.c - maybe factor out? void *gamewin = FindWindowW(L"Valve001", 0); // assuming: all games/mods use narrow chars only; this won't fail. - int len = GetWindowTextA(gamewin, title, sizeof(title)); + int len = GetWindowTextA(gamewin, title, ssizeof(title)); // argh, why did they start doing this, it's so pointless! // hopefully nobody included these suffixes in their mod names, lol if (len > 13 && !memcmp(title + len - 13, " - Direct3D 9", 13)) { diff --git a/src/gameserver.c b/src/gameserver.c index 5a76027..9e046ee 100644 --- a/src/gameserver.c +++ b/src/gameserver.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,7 @@ #include "feature.h" #include "gamedata.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "x86.h" #include "vcall.h" @@ -66,7 +67,7 @@ static bool find_sv(con_cmdcb pause_cb) { INIT { struct con_cmd *pause = con_findcmd("pause"); - if (!find_sv(pause->cb)) { + if_cold (!find_sv(pause->cb)) { errmsg_errorx("couldn't find game server object\n"); return false; } @@ -19,6 +19,7 @@ #include "con_.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "x86.h" @@ -67,7 +68,7 @@ void *hook_inline(void *func_, void *target) { // dumb hack: if we hit some thunk that immediately jumps elsewhere (which // seems common for win32 API functions), hook the underlying thing instead. while (*func == X86_JMPIW) func += mem_loads32(func + 1) + 5; - if (!os_mprot(func, 5, PAGE_EXECUTE_READWRITE)) return 0; + if_cold (!os_mprot(func, 5, PAGE_EXECUTE_READWRITE)) return 0; int len = 0; for (;;) { // FIXME: these cases may result in somewhat dodgy error messaging. They @@ -91,7 +92,7 @@ void *hook_inline(void *func_, void *target) { } } // for simplicity, just bump alloc the trampoline. no need to free anyway - if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) { + if_cold (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) { con_warn("hook_inline: out of trampoline space\n"); return 0; } @@ -1,5 +1,6 @@ /* * Copyright © 2022 Matthew Wozniak <sirtomato999@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +24,7 @@ #include "hook.h" #include "hud.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "sst.h" @@ -92,7 +94,10 @@ static void *matsurf, *toolspanel, *scheme; typedef void (*VCALLCONV Paint_func)(void *); static Paint_func orig_Paint; void VCALLCONV hook_Paint(void *this) { - if (this == toolspanel) EMIT_HudPaint(); + // hopefully a smart branch predictor can figure this out - but we still + // want it to be the "slow" path, so that every other path does *not* need a + // prediction record. or.. I dunno, in theory that does make sense. shrug. + if_cold (this == toolspanel) EMIT_HudPaint(); orig_Paint(this); } @@ -156,21 +161,21 @@ static bool find_toolspanel(void *enginevgui) { INIT { matsurf = factory_engine("MatSystemSurface006", 0); - if (!matsurf) { + if_cold (!matsurf) { errmsg_errorx("couldn't get MatSystemSurface006 interface"); return false; } void *schememgr = factory_engine("VGUI_Scheme010", 0); - if (!schememgr) { + if_cold (!schememgr) { errmsg_errorx("couldn't get VGUI_Scheme010 interface"); return false; } - if (!find_toolspanel(vgui)) { + if_cold (!find_toolspanel(vgui)) { errmsg_errorx("couldn't find engine tools panel"); return false; } void **vtable = *(void ***)toolspanel; - if (!os_mprot(vtable + vtidx_Paint, sizeof(void *), + if_cold (!os_mprot(vtable + vtidx_Paint, sizeof(void *), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); return false; @@ -184,8 +189,8 @@ INIT { } END { - // don't unhook toolspanel if exiting, it's already long gone! - if (sst_userunloaded) { + // don't unhook toolspanel if exiting: it's already long gone! + if_cold (sst_userunloaded) { unhook_vtable(*(void ***)toolspanel, vtidx_Paint, (void *)orig_Paint); SetPaintEnabled(toolspanel, false); } @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ #include "intdefs.h" #include "kv.h" -#include "unreachable.h" +#include "langext.h" #define EOF -1 diff --git a/src/kvsys.c b/src/kvsys.c index fc10b93..31652f3 100644 --- a/src/kvsys.c +++ b/src/kvsys.c @@ -24,6 +24,7 @@ #include "gametype.h" #include "hook.h" #include "kvsys.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "vcall.h" @@ -31,7 +32,7 @@ FEATURE() -IMPORT void *KeyValuesSystem(void); // vstlib symbol +void *KeyValuesSystem(void); // vstlib symbol static void *kvs; static int vtidx_GetSymbolForString = 3, vtidx_GetStringForSymbol = 4; static bool iskvv2 = false; @@ -101,7 +102,7 @@ INIT { if (GAMETYPE_MATCHES(L4D2x)) { void **kvsvt = mem_loadptr(kvs); detectabichange(kvsvt); - if (!os_mprot(kvsvt + vtidx_GetStringForSymbol, sizeof(void *), + if_cold (!os_mprot(kvsvt + vtidx_GetStringForSymbol, sizeof(void *), PAGE_READWRITE)) { errmsg_warnx("couldn't make KeyValuesSystem vtable writable"); errmsg_note("won't be able to prevent any nag messages"); diff --git a/src/l4dmm.c b/src/l4dmm.c index 2439d6b..05a03a7 100644 --- a/src/l4dmm.c +++ b/src/l4dmm.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,6 +22,7 @@ #include "gamedata.h" #include "gametype.h" #include "kvsys.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "vcall.h" @@ -62,7 +63,7 @@ const char *l4dmm_curcampaign(void) { if (!matchfwk) { // we must have oldmmiface, then struct contextval *ctxt = unknown_contextlookup(oldmmiface, "CONTEXT_L4D_CAMPAIGN"); - if (!ctxt) return 0; + if_cold (!ctxt) return 0; // HACK: since this context symbol stuff was the best that was found for // this old MM interface, just map things back to their names manually. // bit stupid, but it gets the (rather difficult) job done @@ -76,18 +77,18 @@ const char *l4dmm_curcampaign(void) { #endif void *ctrlr = GetMatchNetworkMsgController(matchfwk); struct KeyValues *kv = GetActiveGameServerDetails(ctrlr, 0); - if (!kv) return 0; // not in server, probably + if_cold (!kv) return 0; // not in server, probably const char *ret = 0; struct KeyValues *subkey = kvsys_getsubkey(kv, sym_game); - if (subkey) subkey = kvsys_getsubkey(subkey, sym_campaign); - if (subkey) ret = kvsys_getstrval(subkey); - if (ret) { + if_hot (subkey) subkey = kvsys_getsubkey(subkey, sym_campaign); + if_hot (subkey) ret = kvsys_getstrval(subkey); + if_hot (ret) { // ugh, we have to free all the memory allocated by the engine, so copy // this glorified global state to a buffer so the caller doesn't have to // deal with freeing. this necessitates a length cap but it's hopefully // reasonable... - int len = strlen(ret); - if (len > sizeof(campaignbuf) - 1) ret = 0; + usize len = strlen(ret); + if_cold (len > sizeof(campaignbuf) - 1) ret = 0; else ret = memcpy(campaignbuf, ret, len + 1); } kvsys_free(kv); @@ -112,8 +113,8 @@ bool l4dmm_firstmap(void) { if (!kv) return false; int chapter = 0; struct KeyValues *subkey = kvsys_getsubkey(kv, sym_game); - if (subkey) subkey = kvsys_getsubkey(subkey, sym_chapter); - if (subkey) chapter = subkey->ival; + if_hot (subkey) subkey = kvsys_getsubkey(subkey, sym_chapter); + if_hot (subkey) chapter = subkey->ival; kvsys_free(kv); return chapter == 1; } @@ -122,12 +123,12 @@ INIT { void *mmlib = os_dlhandle(OS_LIT("matchmaking") OS_LIT(OS_DLSUFFIX)); if (mmlib) { ifacefactory factory = (ifacefactory)os_dlsym(mmlib, "CreateInterface"); - if (!factory) { + if_cold (!factory) { errmsg_errordl("couldn't get matchmaking interface factory"); return false; } matchfwk = factory("MATCHFRAMEWORK_001", 0); - if (!matchfwk) { + if_cold (!matchfwk) { errmsg_errorx("couldn't get IMatchFramework interface"); return false; } @@ -138,7 +139,7 @@ INIT { #ifdef _WIN32 // L4D1 has no Linux build, btw! else { oldmmiface = factory_engine("VENGINE_MATCHMAKING_VERSION001", 0); - if (!oldmmiface) { + if_cold (!oldmmiface) { errmsg_errorx("couldn't get IMatchmaking interface"); return false; } diff --git a/src/l4dreset.c b/src/l4dreset.c index 4a8792e..a53b987 100644 --- a/src/l4dreset.c +++ b/src/l4dreset.c @@ -31,6 +31,7 @@ #include "gameserver.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "l4dmm.h" #include "mem.h" #include "sst.h" @@ -84,14 +85,13 @@ static struct CVoteIssue *getissue(const char *textkey) { static inline void reset(void) { // reset the vote cooldowns if possible (will skip L4D1). only necessary on - // versions >2045 and on map 1, but it's easiest to do unconditionally - if (off_callerrecords != -1) { - // This is equivalent to CUtlVector::RemoveAll() as there's no - // destructors to call. The result as is if nobody had ever voted. - struct CUtlVector *recordvector = mem_offset(*votecontroller, - off_callerrecords); - recordvector->sz = 0; - } + // versions >2045 and on map 1, but it's easiest to do unconditionally. + // the way this is written will *hopefully* produce a nice neat lea+cmovne. + struct CUtlVector *recordvector = mem_offset(*votecontroller, + off_callerrecords); + // This is equivalent to CUtlVector::RemoveAll() as there's no + // destructors to call. The result as is if nobody had ever voted. + if_random (off_callerrecords != -1) recordvector->sz = 0; ExecuteCommand(getissue("#L4D_vote_restart_game")); } @@ -503,24 +503,24 @@ ok: // Director::Update calls UnfreezeTeam after the first jmp instruction INIT { struct con_cmd *cmd_listissues = con_findcmd("listissues"); - if (!cmd_listissues) { + if_cold (!cmd_listissues) { errmsg_errorx("couldn't find \"listissues\" command"); return false; } con_cmdcbv1 listissues_cb = con_getcmdcbv1(cmd_listissues); const uchar *nextinsns = find_votecontroller(listissues_cb); - if (!nextinsns) { + if_cold (!nextinsns) { errmsg_errorx("couldn't find vote controller variable"); return false; } - if (!find_voteissues(nextinsns)) { + if_cold (!find_voteissues(nextinsns)) { errmsg_errorx("couldn't find vote issues list offset\n"); return false; } void **vtable; #ifdef _WIN32 void *GameShutdown = (*(void ***)srvdll)[vtidx_GameShutdown]; - if (!find_TheDirector(GameShutdown)) { + if_cold (!find_TheDirector(GameShutdown)) { errmsg_errorx("couldn't find TheDirector variable"); return false; } @@ -532,7 +532,7 @@ INIT { if (GAMETYPE_MATCHES(L4D2)) { #endif vtable = mem_loadptr(director); - if (!os_mprot(vtable + vtidx_OnGameplayStart, sizeof(*vtable), + if_cold (!os_mprot(vtable + vtidx_OnGameplayStart, sizeof(*vtable), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); return false; @@ -543,7 +543,7 @@ INIT { } else /* L4D1 */ { void *GameFrame = (*(void ***)srvdll)[vtidx_GameFrame]; - if (!find_UnfreezeTeam(GameFrame)) { + if_cold (!find_UnfreezeTeam(GameFrame)) { errmsg_errorx("couldn't find UnfreezeTeam function"); return false; } @@ -556,16 +556,16 @@ INIT { // g_voteController is invalid if not running a server so get the // vtable by inspecting the ent factory code instead const struct CEntityFactory *factory = ent_getfactory("vote_controller"); - if (!factory) { + if_cold (!factory) { errmsg_errorx("couldn't find vote controller entity factory"); goto nocd; } vtable = ent_findvtable(factory, "CVoteController"); - if (!vtable) { + if_cold (!vtable) { errmsg_errorx("couldn't find CVoteController vtable"); goto nocd; } - if (!find_votecallers(vtable[vtidx_Spawn])) { + if_cold (!find_votecallers(vtable[vtidx_Spawn])) { errmsg_errorx("couldn't find vote callers list offset"); nocd: errmsg_note("resetting a first map will not clear vote cooldowns"); } diff --git a/src/l4dwarp.c b/src/l4dwarp.c index 24c3873..d24e39c 100644 --- a/src/l4dwarp.c +++ b/src/l4dwarp.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,6 +25,7 @@ #include "gamedata.h" #include "gametype.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "vcall.h" @@ -41,7 +42,7 @@ DECL_VFUNC_DYN(void, Teleport, const struct vec3f */*pos*/, 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; } + if_cold (!ed) { errmsg_errorx("couldn't access player entity"); return; } 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); diff --git a/src/langext.h b/src/langext.h new file mode 100644 index 0000000..ef0f18d --- /dev/null +++ b/src/langext.h @@ -0,0 +1,64 @@ +/* This file is dedicated to the public domain. */ + +#ifndef INC_LANGEXT_H +#define INC_LANGEXT_H + +#include "intdefs.h" + +#define ssizeof(x) ((ssize)sizeof(x)) +#define countof(x) (ssizeof(x) / ssizeof(*x)) + +#if defined(__GNUC__) || defined(__clang__) +#define if_hot(x) if (__builtin_expect(!!(x), 1)) +#define if_cold(x) if (__builtin_expect(!!(x), 0)) +#define if_random(x) if (__builtin_expect_with_probability(!!(x), 1, 0.5)) +#define unreachable __builtin_unreachable() +#define assume(x) ((void)(!!(x) || (unreachable, 0))) +#define cold __attribute__((__cold__, __noinline__)) +#else +#define if_hot(x) if (x) +#define if_cold(x) if (x) +#define if_random(x) if (x) +#ifdef _MSC_VER +#define unreachable __assume(0) +#define assume(x) ((void)(__assume(x), 0)) +#define cold __declspec(noinline) +#else +#define unreachable ((void)(0)) // might still give some warnings, but too bad +#define assume(x) ((void)0) +#define cold +#endif +#endif + +#define switch_exhaust(x) switch (x) if (0) default: unreachable; else +#if defined(__GNUC__) || defined(__clang__) +#define switch_exhaust_enum(E, x) \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic error \"-Wswitch-enum\"") \ + switch_exhaust ((enum E)(x)) \ + _Pragma("GCC diagnostic pop") +#else +// NOTE: pragma trick doesn't work in MSVC (the pop seems to happen before the +// switch is evaluated, so nothing happens) but you can still get errors using +// -we4061. This doesn't matter for sst but might come in handy elsewhere... +#define switch_exhaust_enum(E, x) switch_exhaust ((enum E)(x)) +#endif + +#define noreturn _Noreturn void + +#ifdef _WIN32 +#define import __declspec(dllimport) // only needed for variables +#define export __declspec(dllexport) +#else +#define import +#ifdef __GNUC__ +// N.B. we assume -fvisibility=hidden +#define export __attribute__((visibility("default")) +#else +#define export int exp[-!!"compiler needs a way to export symbols!"]; +#endif +#endif + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/nomute.c b/src/nomute.c index 6a42d46..4e5bcc4 100644 --- a/src/nomute.c +++ b/src/nomute.c @@ -26,6 +26,7 @@ #include "con_.h" #include "errmsg.h" #include "feature.h" +#include "langext.h" #include "os.h" #include "sst.h" @@ -44,7 +45,7 @@ static con_cmdcbv1 snd_restart_cb = 0; // unless we were loaded later with plugin_load in which case we actually do. static bool skiprestart; static void losefocuscb(struct con_var *v) { - if (!skiprestart) snd_restart_cb(); + if_hot (!skiprestart) snd_restart_cb(); skiprestart = false; } @@ -65,14 +66,15 @@ PREINIT { INIT { skiprestart = sst_earlyloaded; // see above IDirectSound *ds_obj = 0; - if (DirectSoundCreate(0, &ds_obj, 0) != DS_OK) { + if_cold (DirectSoundCreate(0, &ds_obj, 0) != DS_OK) { // XXX: can this error be usefully stringified? errmsg_errorx("couldn't create IDirectSound instance"); return false; } ds_vt = ds_obj->lpVtbl; ds_obj->lpVtbl->Release(ds_obj); - if (!os_mprot(&ds_vt->CreateSoundBuffer, sizeof(void *), PAGE_READWRITE)) { + if_cold (!os_mprot(&ds_vt->CreateSoundBuffer, sizeof(void *), + PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); return false; } @@ -81,7 +83,7 @@ INIT { snd_mute_losefocus->base.flags &= ~CON_HIDDEN; struct con_cmd *snd_restart = con_findcmd("snd_restart"); - if (snd_restart) { + if_hot (snd_restart) { snd_restart_cb = con_getcmdcbv1(snd_restart); snd_mute_losefocus->cb = &losefocuscb; } diff --git a/src/noreturn.h b/src/noreturn.h deleted file mode 100644 index 81b2bae..0000000 --- a/src/noreturn.h +++ /dev/null @@ -1,11 +0,0 @@ -/* This file is dedicated to the public domain. */ - -#ifndef INC_NORETURN_H -#define INC_NORETURN_H - -#undef noreturn -#define noreturn _Noreturn void - -#endif - -// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/nosleep.c b/src/nosleep.c index e75c6e6..52c88a9 100644 --- a/src/nosleep.c +++ b/src/nosleep.c @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include "feature.h" #include "gamedata.h" #include "hook.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "vcall.h" @@ -48,7 +49,7 @@ PREINIT { INIT { vtable = mem_loadptr(inputsystem); - if (!os_mprot(vtable + vtidx_SleepUntilInput, sizeof(void *), + if_cold (!os_mprot(vtable + vtidx_SleepUntilInput, sizeof(void *), PAGE_READWRITE)) { errmsg_errorx("couldn't make virtual table writable"); return false; @@ -31,6 +31,7 @@ #endif #include "intdefs.h" +#include "langext.h" #ifdef _WIN32 @@ -65,7 +66,7 @@ int os_write(int f, const void *buf, int len) { vlong os_fsize(int f) { vlong ret; - if (!GetFileSizeEx((void *)(ssize)f, (LARGE_INTEGER *)&ret)) return -1; + if_cold (!GetFileSizeEx((void *)(ssize)f, (LARGE_INTEGER *)&ret)) return -1; return ret; } @@ -90,13 +91,13 @@ void *os_dlsym(void *restrict lib, const char *restrict s) { } int os_dlfile(void *lib, ushort *buf, int sz) { unsigned int n = GetModuleFileNameW(lib, buf, sz); - if (n == 0 || n == sz) return -1; + if_cold (n == 0 || n == sz) return -1; // get the canonical capitalisation, as for some reason GetModuleFileName() // returns all lowercase. this doesn't really matter except it looks nicer GetLongPathNameW(buf, buf, n + 1); // the drive letter will also be lower case, if it is an actual drive letter // of course. it should be; I'm not gonna lose sleep over UNC paths and such - if (buf[0] >= L'a' && buf[0] <= L'z' && buf[1] == L':') buf[0] &= ~32u; + if_hot (buf[0] >= L'a' && buf[0] <= L'z' && buf[1] == L':') buf[0] &= ~32u; return n; } @@ -124,7 +125,7 @@ void os_close(int f) { close(f); } vlong os_fsize(int f) { struct stat s; - if (stat(f, &s) == -1) return -1; + if_cold (stat(f, &s) == -1) return -1; return s.st_size; } @@ -166,7 +167,7 @@ static struct link_map *lmbase = 0; void *os_dlhandle(const char *name) { extern struct link_map *lmbase; // note: defined in sst.c for now - if (!lmbase) { // IMPORTANT: not thread safe. don't forget later! + if_cold (!lmbase) { // IMPORTANT: not thread safe. don't forget later! lmbase = (struct link_map *)dlopen("libc.so.6", RTLD_LAZY | RTLD_NOLOAD); dlclose(lmbase); // assume success while (lmbase->l_prev) lmbase = lmbase->l_prev; @@ -195,7 +196,7 @@ void *os_dlhandle(const char *name) { int os_dlfile(void *lib, char *buf, int sz) { struct link_map *lm = lib; int ssz = strlen(lm->l_name) + 1; - if (ssz > sz) { errno = ENAMETOOLONG; return -1; } + if_cold (ssz > sz) { errno = ENAMETOOLONG; return -1; } memcpy(buf, lm->l_name, ssz); return ssz; } @@ -24,9 +24,6 @@ #include <wchar.h> // XXX: there's kind of a lot of junk in this header! -#define IMPORT __declspec(dllimport) // only needed for variables -#define EXPORT __declspec(dllexport) - typedef unsigned short os_char; #define _OS_CAT(x, y) x##y #define OS_LIT(x) _OS_CAT(L, x) @@ -75,13 +72,6 @@ typedef unsigned short os_char; #include <errno.h> // meh -#define IMPORT -#ifdef __GNUC__ -#define EXPORT __attribute__((visibility("default")) -#else -#define EXPORT int exp[-!!"compiler needs a way to export symbols!"]; -#endif - // trying to avoid pulling in unnecessary headers as much as possible: define // our own constants for os_mprot() / mprotect() #if defined(__linux__) // apparently linux is pretty much the sole oddball here! diff --git a/src/portalcolours.c b/src/portalcolours.c index 3167c0b..61d9cc7 100644 --- a/src/portalcolours.c +++ b/src/portalcolours.c @@ -24,6 +24,7 @@ #include "hexcolour.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "os.h" #include "ppmagic.h" @@ -102,13 +103,13 @@ PREINIT { INIT { #ifdef _WIN32 - if (!find_UTIL_Portal_Color(clientlib)) { + if_cold (!find_UTIL_Portal_Color(clientlib)) { errmsg_errorx("couldn't find UTIL_Portal_Color"); return false; } orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)hook_inline( (void *)orig_UTIL_Portal_Color, (void *)&hook_UTIL_Portal_Color); - if (!orig_UTIL_Portal_Color) { + if_cold (!orig_UTIL_Portal_Color) { errmsg_errorsys("couldn't hook UTIL_Portal_Color"); return false; } @@ -126,7 +127,7 @@ INIT { } END { - if (!sst_userunloaded) return; + if_hot (!sst_userunloaded) return; unhook_inline((void *)orig_UTIL_Portal_Color); } diff --git a/src/rinput.c b/src/rinput.c index 6b61bff..5464957 100644 --- a/src/rinput.c +++ b/src/rinput.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,6 +28,7 @@ #include "gamedata.h" #include "hook.h" #include "intdefs.h" +#include "langext.h" #include "mem.h" #include "sst.h" #include "vcall.h" @@ -68,12 +69,12 @@ DEF_CVAR_MINMAX(sst_mouse_factor, "Number of hardware mouse counts per step", static ssize __stdcall inproc(void *wnd, uint msg, usize wp, ssize lp) { switch (msg) { case WM_INPUT:; - char buf[sizeof(RAWINPUTHEADER) + sizeof(RAWMOUSE) /* = 40 */]; + char buf[ssizeof(RAWINPUTHEADER) + ssizeof(RAWMOUSE) /* = 40 */]; uint sz = sizeof(buf); - if (GetRawInputData((void *)lp, RID_INPUT, buf, &sz, + if_hot (GetRawInputData((void *)lp, RID_INPUT, buf, &sz, sizeof(RAWINPUTHEADER)) != -1) { RAWINPUT *ri = (RAWINPUT *)buf; - if (ri->header.dwType == RIM_TYPEMOUSE) { + if_hot (ri->header.dwType == RIM_TYPEMOUSE) { int d = con_getvari(sst_mouse_factor); int dx = rx + ri->data.mouse.lLastX; int dy = ry + ri->data.mouse.lLastY; @@ -129,8 +130,8 @@ INIT { if (!inputsystem) return false; vtable_insys = mem_loadptr(inputsystem); // XXX: this is kind of duping nosleep, but that won't always init... - if (!os_mprot(vtable_insys + vtidx_GetRawMouseAccumulators, - sizeof(void *), PAGE_READWRITE)) { + if_cold (!os_mprot(vtable_insys + vtidx_GetRawMouseAccumulators, + ssizeof(void *), PAGE_READWRITE)) { errmsg_errorx("couldn't make virtual table writable"); return false; } @@ -149,7 +150,7 @@ INIT { .lpfnWndProc = (WNDPROC)&inproc, .lpszClassName = L"RInput" }; - if (!RegisterClassExW(&wc)) { + if_cold (!RegisterClassExW(&wc)) { struct rgba gold = {255, 210, 0, 255}; struct rgba blue = {45, 190, 190, 255}; struct rgba white = {200, 200, 200, 255}; @@ -158,7 +159,7 @@ INIT { "Consider launching without that and using "); con_colourmsg(&gold, "m_rawinput 1"); con_colourmsg(&blue, " instead!\n"); - if (has_rawinput) { + if_cold (has_rawinput) { // slow path because this'd be kinda weird! con_colourmsg(&white, "This is built into this version of game, and" " will also get provided by SST in older versions. "); } @@ -181,18 +182,18 @@ INIT { orig_GetCursorPos = (GetCursorPos_func)hook_inline((void *)&GetCursorPos, (void *)&hook_GetCursorPos); - if (!orig_GetCursorPos) { + if_cold (!orig_GetCursorPos) { errmsg_errorsys("couldn't hook %s", "GetCursorPos"); goto e0; } orig_SetCursorPos = (SetCursorPos_func)hook_inline((void *)&SetCursorPos, (void *)&hook_SetCursorPos); - if (!orig_SetCursorPos) { + if_cold (!orig_SetCursorPos) { errmsg_errorsys("couldn't hook %s", "SetCursorPos"); goto e1; } inwin = CreateWindowExW(0, L"RInput", L"RInput", 0, 0, 0, 0, 0, 0, 0, 0, 0); - if (!inwin) { + if_cold (!inwin) { errmsg_errorsys("couldn't create input window"); goto e2; } @@ -201,7 +202,7 @@ INIT { .usUsagePage = USAGEPAGE_MOUSE, .usUsage = USAGE_MOUSE }; - if (!RegisterRawInputDevices(&rd, 1, sizeof(rd))) { + if_cold (!RegisterRawInputDevices(&rd, 1, sizeof(rd))) { errmsg_errorsys("couldn't create raw mouse device"); goto e3; } @@ -218,8 +219,8 @@ e0: UnregisterClassW(L"RInput", 0); } END { - if (!sst_userunloaded) return; - if (orig_SetCursorPos) { // if null, we didn't init our own implementation + if_hot (!sst_userunloaded) return; + if_hot (orig_SetCursorPos) { // we inited our own implementation RAWINPUTDEVICE rd = { .dwFlags = RIDEV_REMOVE, .hwndTarget = 0, @@ -232,7 +233,7 @@ END { unhook_inline((void *)orig_GetCursorPos); unhook_inline((void *)orig_SetCursorPos); } - else { + else { // we must have hooked the *existing* implementation unhook_vtable(vtable_insys, vtidx_GetRawMouseAccumulators, (void *)orig_GetRawMouseAccumulators); } @@ -33,6 +33,7 @@ #include "gameinfo.h" #include "gametype.h" #include "hook.h" +#include "langext.h" #include "os.h" #include "sst.h" #include "vcall.h" @@ -70,7 +71,7 @@ int dladdr1(const void *addr, Dl_info *info, void **extra_info, int flags); static void *ownhandle(void) { static void *cached = 0; Dl_info dontcare; - if (!cached) { + if_cold (!cached) { dladdr1((void *)&ownhandle, &dontcare, &cached, /*RTLD_DL_LINKMAP*/ 2); } return cached; @@ -82,14 +83,14 @@ static void *ownhandle(void) { static inline bool checksamedrive(const ushort *restrict path1, const ushort *restrict path2) { bool ret = (path1[0] | 32) == (path2[0] | 32); - if (!ret) errmsg_errorx("game and plugin must be on the same drive\n"); + if_cold (!ret) errmsg_errorx("game and plugin must be on the same drive\n"); return ret; } #endif DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { os_char path[PATH_MAX]; - if (os_dlfile(ownhandle(), path, sizeof(path) / sizeof(*path)) == -1) { + if_cold (os_dlfile(ownhandle(), path, countof(path)) == -1) { // hopefully by this point this won't happen, but, like, never know errmsg_errordl("failed to get path to plugin"); return; @@ -104,10 +105,10 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { // since old builds allow absolute plugin_load paths but since it's less // reliable if e.g. a disk is removed, and also doesn't work for all // games, just rule it out entirely to keep things simple. - if (!checksamedrive(path, startdir)) return; + if_cold (!checksamedrive(path, startdir)) return; #endif int len = os_strlen(startdir); - if (len + sizeof("/bin") >= PATH_MAX) { + if_cold (len + ssizeof("/bin") >= PATH_MAX) { errmsg_errorx("path to game is too long"); return; } @@ -128,7 +129,7 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { // obscure gameinfo.txt arrangement could technically allow that to work startdir = gameinfo_gamedir; #ifdef _WIN32 - if (!checksamedrive(path, startdir)) return; + if_cold (!checksamedrive(path, startdir)) return; #endif } os_char relpath[PATH_MAX]; @@ -136,7 +137,7 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { // note: dll isn't actually in gamedir if it's in a base mod directory // note: gamedir doesn't account for if the dll is in a base mod's // directory, although it will yield a valid/working relative path anyway. - if (!PathRelativePathToW(relpath, startdir, FILE_ATTRIBUTE_DIRECTORY, + if_cold (!PathRelativePathToW(relpath, startdir, FILE_ATTRIBUTE_DIRECTORY, path, 0)) { errmsg_errorsys("couldn't compute a relative path"); return; @@ -145,12 +146,12 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { // also make sure there's no unicode in there, just in case... int rellen = 0; for (ushort *p = relpath; *p; ++p, ++rellen) { - if (*p > 127) { + if_cold (*p > 127) { errmsg_errorx("mod dir contains Unicode characters which Source " "doesn't handle well - autoload file not created"); return; } - if (*p == L'\\') *p = L'/'; + if_random (*p == L'\\') *p = L'/'; } #else const char *p = path, *q = startdir; @@ -179,35 +180,34 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { c: memcpy(r, p + slash + 1, rellen); #endif int len = os_strlen(gameinfo_gamedir); - if (len + sizeof("/addons/" VDFBASENAME ".vdf") > - sizeof(path) / sizeof(*path)) { + if (len + ssizeof("/addons/" VDFBASENAME ".vdf") > countof(path)) { errmsg_errorx("path to VDF is too long"); return; } os_spancopy(path, gameinfo_gamedir, len); os_spancopy(path + len, OS_LIT("/addons"), 8); - if (!os_mkdir(path) && os_lasterror() != OS_EEXIST) { + if (!os_mkdir(path)) if_cold (os_lasterror() != OS_EEXIST) { errmsg_errorsys("couldn't create %" fS, path); return; } - os_spancopy(path + len + sizeof("/addons") - 1, + os_spancopy(path + len + ssizeof("/addons") - 1, OS_LIT("/") OS_LIT(VDFBASENAME) OS_LIT(".vdf"), - sizeof("/" VDFBASENAME ".vdf")); + ssizeof("/" VDFBASENAME ".vdf")); int f = os_open_write(path); - if (f == -1) { errmsg_errorsys("couldn't open %" fS, path); return; } + if_cold (f == -1) { errmsg_errorsys("couldn't open %" fS, path); return; } #ifdef _WIN32 char buf[19 + PATH_MAX]; memcpy(buf, "Plugin { file \"", 15); for (int i = 0; i < rellen; ++i) buf[i + 15] = relpath[i]; memcpy(buf + 15 + rellen, "\" }\n", 4); - if (os_write(f, buf, rellen + 19) == -1) { // blegh + if_cold (os_write(f, buf, rellen + 19) == -1) { // blegh #else struct iovec iov[3] = { {"Plugin { file \"", 15}, {relpath, rellen}, {"\" }\n", 4} }; - if (writev(fd, &iov, 3) == -1) { + if_cold (writev(fd, &iov, 3) == -1) { #endif errmsg_errorsys("couldn't write to %" fS, path); } @@ -217,15 +217,14 @@ c: memcpy(r, p + slash + 1, rellen); DEF_CCMD_HERE(sst_autoload_disable, "Stop loading SST on game startup", 0) { os_char path[PATH_MAX]; int len = os_strlen(gameinfo_gamedir); - if (len + sizeof("/addons/" VDFBASENAME ".vdf") > - sizeof(path) / sizeof(*path)) { + if_cold (len + ssizeof("/addons/" VDFBASENAME ".vdf") > countof(path)) { errmsg_errorx("path to VDF is too long"); return; } os_spancopy(path, gameinfo_gamedir, len); os_spancopy(path + len, OS_LIT("/addons/") OS_LIT(VDFBASENAME) OS_LIT(".vdf"), - sizeof("/addons/" VDFBASENAME ".vdf")); - if (!os_unlink(path) && os_lasterror() != OS_ENOENT) { + ssizeof("/addons/" VDFBASENAME ".vdf")); + if (!os_unlink(path)) if_cold (os_lasterror() != OS_ENOENT) { errmsg_warnsys("couldn't delete %" fS, path); } } @@ -277,23 +276,24 @@ static void do_featureinit(void) { engineapi_lateinit(); // load libs that might not be there early (...at least on Linux???) clientlib = os_dlhandle(OS_LIT("client") OS_LIT(OS_DLSUFFIX)); - if (!clientlib) { + if_cold (!clientlib) { errmsg_warndl("couldn't get the game's client library"); } - else if (!(factory_client = (ifacefactory)os_dlsym(clientlib, + else if_cold (!(factory_client = (ifacefactory)os_dlsym(clientlib, "CreateInterface"))) { errmsg_warndl("couldn't get client's CreateInterface"); } void *inputsystemlib = os_dlhandle(OS_LIT("bin/") OS_LIT("inputsystem") OS_LIT(OS_DLSUFFIX)); - if (!inputsystemlib) { + if_cold (!inputsystemlib) { errmsg_warndl("couldn't get the input system library"); } - else if (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, + else if_cold (!(factory_inputsystem = (ifacefactory)os_dlsym(inputsystemlib, "CreateInterface"))) { errmsg_warndl("couldn't get input system's CreateInterface"); } - else if (!(inputsystem = factory_inputsystem("InputSystemVersion001", 0))) { + else if_cold (!(inputsystem = factory_inputsystem( + "InputSystemVersion001", 0))) { errmsg_warnx("missing input system interface"); } // ... and now for the real magic! @@ -301,7 +301,7 @@ static void do_featureinit(void) { // if we're autoloaded and the external autoupdate script downloaded a new // version, let the user know about the cool new stuff! - if (getenv("SST_UPDATED")) { + if_cold (getenv("SST_UPDATED")) { // avoid displaying again if we're unloaded and reloaded in one session #ifdef _WIN32 SetEnvironmentVariableA("SST_UPDATED", 0); @@ -338,7 +338,7 @@ DECL_VFUNC_DYN(bool, VGuiIsInitialized) // // Route credit to bill for helping figure a lot of this out - mike static bool deferinit(void) { - if (!vgui) { + if_cold (!vgui) { errmsg_warnx("can't use VEngineVGui for deferred feature setup"); goto e; } @@ -348,7 +348,7 @@ static bool deferinit(void) { // CEngineVGui::IsInitialized() which works everywhere. if (VGuiIsInitialized(vgui)) return false; sst_earlyloaded = true; // let other code know - if (!os_mprot(*(void ***)vgui + vtidx_VGuiConnect, sizeof(void *), + if_cold (!os_mprot(*(void ***)vgui + vtidx_VGuiConnect, ssizeof(void *), PAGE_READWRITE)) { errmsg_warnsys("couldn't make CEngineVGui vtable writable for deferred " "feature setup"); @@ -392,7 +392,7 @@ static void hook_plugin_unload_cb(const struct con_cmdargs *args) { if (!CHECK_AllowPluginLoading(false)) return; int idx = atoi(args->argv[1]); struct CPlugin **plugins = pluginhandler->plugins.m.mem; - if (idx >= 0 && idx < pluginhandler->plugins.sz) { + if_hot (idx >= 0 && idx < pluginhandler->plugins.sz) { const struct CPlugin *plugin = plugins[idx]; // XXX: *could* memoise the ispluginv1 call, but... meh. effort. const struct CPlugin_common *common = ispluginv1(plugin) ? @@ -417,12 +417,12 @@ static void hook_plugin_unload_cb(const struct con_cmdargs *args) { } static bool do_load(ifacefactory enginef, ifacefactory serverf) { - if (!hook_init()) { + if_cold (!hook_init()) { errmsg_warnsys("couldn't set up memory for function hooking"); return false; } factory_engine = enginef; factory_server = serverf; - if (!engineapi_init(ifacever)) return false; + if_cold (!engineapi_init(ifacever)) return false; const void **p = vtable_firstdiff; if (GAMETYPE_MATCHES(Portal2)) *p++ = (void *)&nop_p_v; // ClientFullyConnect *p++ = (void *)&nop_p_v; // ClientDisconnect @@ -437,7 +437,7 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) { *p++ = (void *)&nop_p_v; // OnEdictAllocated *p = (void *)&nop_p_v; // OnEdictFreed if (!deferinit()) { do_featureinit(); fixes_apply(); } - if (pluginhandler) { + if_hot (pluginhandler) { cmd_plugin_load = con_findcmd("plugin_load"); orig_plugin_load_cb = cmd_plugin_load->cb; cmd_plugin_load->cb = &hook_plugin_load_cb; @@ -449,7 +449,8 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) { } static void do_unload(void) { - if (sst_userunloaded) { // note: pluginhandler must also be set here + // slow path: reloading shouldn't happen all the time, prioritise fast exit + if_cold (sst_userunloaded) { // note: if we're here, pluginhandler is set cmd_plugin_load->cb = orig_plugin_load_cb; cmd_plugin_unload->cb = orig_plugin_unload_cb; #ifdef _WIN32 // this bit is only relevant in builds that predate linux support @@ -470,7 +471,7 @@ static void do_unload(void) { static bool VCALLCONV Load(void *this, ifacefactory enginef, ifacefactory serverf) { - if (already_loaded) { + if_cold (already_loaded) { con_warn("Already loaded! Doing nothing!\n"); skip_unload = true; return false; @@ -482,7 +483,7 @@ static bool VCALLCONV Load(void *this, ifacefactory enginef, static void VCALLCONV Unload(void *this) { // the game tries to unload on a failed load, for some reason - if (skip_unload) { skip_unload = false; return; } + if_cold (skip_unload) { skip_unload = false; return; } do_unload(); } @@ -535,9 +536,9 @@ static const void **vtable_firstdiff = vtable + 10; // this is equivalent to a class with no members! static const void *const *const plugin_obj = vtable; -EXPORT const void *CreateInterface(const char *name, int *ret) { - if (!strncmp(name, "ISERVERPLUGINCALLBACKS00", 24)) { - if (name[24] >= '1' && name[24] <= '3' && name[25] == '\0') { +export const void *CreateInterface(const char *name, int *ret) { + if_hot (!strncmp(name, "ISERVERPLUGINCALLBACKS00", 24)) { + if_hot (name[24] >= '1' && name[24] <= '3' && name[25] == '\0') { if (ret) *ret = 0; ifacever = name[24] - '0'; return &plugin_obj; diff --git a/src/unreachable.h b/src/unreachable.h deleted file mode 100644 index 99c82b5..0000000 --- a/src/unreachable.h +++ /dev/null @@ -1,14 +0,0 @@ -/* This file is dedicated to the public domain. */ - -#ifndef INC_UNREACHABLE_H -#define INC_UNREACHABLE_H - -#if defined(__GNUC__) || defined(__clang__) -#define unreachable __builtin_unreachable() -#else -#define unreachable do; while (0) -#endif - -#endif - -// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/x86util.h b/src/x86util.h index 85a824e..9fcdfff 100644 --- a/src/x86util.h +++ b/src/x86util.h @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2024 Michael Smith <mikesmiffy128@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,14 +18,15 @@ #define INC_X86UTIL_H #include "errmsg.h" +#include "langext.h" #include "x86.h" // XXX: don't know where else to put this, or how else to design this, so this -// is very much a plonk-it-here-for-now scenario. +// is very much a plonk-it-here-for-now scenario (and has been for years!) #define NEXT_INSN(p, tgt) do { \ int _len = x86_len(p); \ - if (_len == -1) { \ + if_cold (_len == -1) { \ errmsg_errorx("unknown or invalid instruction looking for %s", tgt); \ return false; \ } \ diff --git a/test/kv.test.c b/test/kv.test.c index 7f82b8a..1bd1784 100644 --- a/test/kv.test.c +++ b/test/kv.test.c @@ -9,7 +9,7 @@ #include "../src/kv.c" #include "../src/intdefs.h" -#include "../src/noreturn.h" +#include "../src/langext.h" static noreturn die(const struct kv_parser *kvp) { fprintf(stderr, "parse error: %d:%d: %s\n", kvp->line, kvp->col, |