From 83da606072ce272eb053d4e1497d77e647cfecae Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 3 Aug 2024 23:40:31 +0100 Subject: Revise syntax macros and add a ton of branch hints My new programming style is branch hints. All non-confusing branches must be hinted when I can be bothered. It's faster, sometimes, maybe. Also, start trying to use more signed sizes in at least some of the places where it makes sense. Unsigned sizes are surprisingly error-prone! --- src/ac.c | 110 ++++++++++++++++++++++++++----------------------- src/alias.c | 6 +-- src/autojump.c | 15 +++---- src/bind.c | 5 ++- src/bitbuf.h | 7 ++-- src/build/codegen.c | 9 ++-- src/build/mkentprops.c | 4 +- src/build/mkgamedata.c | 2 +- src/chunklets/msg.c | 6 +-- src/democustom.c | 3 +- src/demorec.c | 15 +++---- src/engineapi.c | 5 ++- src/ent.c | 16 +++---- src/extmalloc.c | 9 ++-- src/fastfwd.c | 46 ++++++++++----------- src/fixes.c | 14 +++---- src/fov.c | 11 ++--- src/gameinfo.c | 9 ++-- src/gameserver.c | 5 ++- src/hook.c | 5 ++- src/hud.c | 19 +++++---- src/kv.c | 4 +- src/kvsys.c | 5 ++- src/l4dmm.c | 27 ++++++------ src/l4dreset.c | 34 +++++++-------- src/l4dwarp.c | 5 ++- src/langext.h | 64 ++++++++++++++++++++++++++++ src/nomute.c | 10 +++-- src/noreturn.h | 11 ----- src/nosleep.c | 5 ++- src/os.c | 13 +++--- src/os.h | 10 ----- src/portalcolours.c | 7 ++-- src/rinput.c | 31 +++++++------- src/sst.c | 79 +++++++++++++++++------------------ src/unreachable.h | 14 ------- src/x86util.h | 7 ++-- test/kv.test.c | 2 +- 38 files changed, 356 insertions(+), 293 deletions(-) create mode 100644 src/langext.h delete mode 100644 src/noreturn.h delete mode 100644 src/unreachable.h diff --git a/src/ac.c b/src/ac.c index 8c98cfb..7f3157c 100644 --- a/src/ac.c +++ b/src/ac.c @@ -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 + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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, diff --git a/src/bind.c b/src/bind.c index deb943e..327d0c2 100644 --- a/src/bind.c +++ b/src/bind.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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 #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 + * Copyright © 2024 Michael Smith * * 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 // 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; } diff --git a/src/ent.c b/src/ent.c index a1ad08f..1845b1f 100644 --- a/src/ent.c +++ b/src/ent.c @@ -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 + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * Copyright © 2023 Hayden K * * 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:; diff --git a/src/fov.c b/src/fov.c index 73cd17e..f4b8575 100644 --- a/src/fov.c +++ b/src/fov.c @@ -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 + * Copyright © 2024 Michael Smith * * 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; } diff --git a/src/hook.c b/src/hook.c index fe311cc..9e5d694 100644 --- a/src/hook.c +++ b/src/hook.c @@ -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; } diff --git a/src/hud.c b/src/hud.c index 24d8563..b1cbb56 100644 --- a/src/hud.c +++ b/src/hud.c @@ -1,5 +1,6 @@ /* * Copyright © 2022 Matthew Wozniak + * Copyright © 2024 Michael Smith * * 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); } diff --git a/src/kv.c b/src/kv.c index 019d017..698aa8e 100644 --- a/src/kv.c +++ b/src/kv.c @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Michael Smith + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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 + * Copyright © 2024 Michael Smith * * 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; diff --git a/src/os.c b/src/os.c index 49cab63..cf5c262 100644 --- a/src/os.c +++ b/src/os.c @@ -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; } diff --git a/src/os.h b/src/os.h index b599523..cbecf2c 100644 --- a/src/os.h +++ b/src/os.h @@ -24,9 +24,6 @@ #include // 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 // 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 + * Copyright © 2024 Michael Smith * * 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); } diff --git a/src/sst.c b/src/sst.c index ce80032..3f42327 100644 --- a/src/sst.c +++ b/src/sst.c @@ -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 + * Copyright © 2024 Michael Smith * * 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, -- cgit v1.2.3