summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMichael Smith <mikesmiffy128@gmail.com>2022-05-30 00:57:45 +0100
committerMichael Smith <mikesmiffy128@gmail.com>2022-05-30 23:37:45 +0100
commit165aa899bc9d1ea0bebb7b08351582dfeff8bbde (patch)
tree4f966f7d4526224138b8c38bb57dcddae26029aa
parentdacb42286f41065c495b685a73ac6885dba3dcd8 (diff)
Add basic Portal crosshair colour customisation
Currently only works in 3420 and 5135 and uses hardcoded offsets with a byte pattern sanity check. Future work includes making it more widely compatible, and also doing the crazy thing I wanted to do but gave up on wherein the actual textures and stuff get patched in memory to sync up all the colours. Oh also, a couple of vtables were erroneously made executable, so I went ahead and fixed that while I was at it.
-rwxr-xr-xcompile1
-rw-r--r--compile.bat5
-rw-r--r--src/autojump.c2
-rw-r--r--src/demorec.c2
-rw-r--r--src/nosleep.c2
-rw-r--r--src/portalcolours.c147
-rw-r--r--src/portalcolours.h27
-rw-r--r--src/sst.c9
8 files changed, 186 insertions, 9 deletions
diff --git a/compile b/compile
index 02a80d3..d921b78 100755
--- a/compile
+++ b/compile
@@ -53,6 +53,7 @@ src="\
kv.c
l4dwarp.c
nosleep.c
+ portalcolours.c
sst.c
x86.c"
if [ "$dbg" = 1 ]; then src="$src \
diff --git a/compile.bat b/compile.bat
index c218816..60372a1 100644
--- a/compile.bat
+++ b/compile.bat
@@ -42,8 +42,8 @@ goto :eof
-o .build/mkgamedata.exe src/build/mkgamedata.c src/kv.c || exit /b
%HOSTCC% -municode -O2 %warnings% -D_CRT_SECURE_NO_WARNINGS -ladvapi32 ^
-o .build/mkentprops.exe src/build/mkentprops.c src/kv.c || exit /b
-.build\codegen.exe src/autojump.c src/con_.c src/demorec.c src/engineapi.c src/ent.c src/extmalloc.c src/fixes.c ^
-src/fov.c src/gamedata.c src/gameinfo.c src/hook.c src/kv.c src/l4dwarp.c src/nosleep.c src/rinput.c src/sst.c src/x86.c || exit /b
+.build\codegen.exe src/autojump.c src/con_.c src/demorec.c src/engineapi.c src/ent.c src/extmalloc.c src/fixes.c src/fov.c ^
+src/gamedata.c src/gameinfo.c src/hook.c src/kv.c src/l4dwarp.c src/nosleep.c src/portalcolours.c src/rinput.c src/sst.c src/x86.c || exit /b
.build\mkgamedata.exe gamedata/engine.kv gamedata/gamelib.kv gamedata/inputsystem.kv || exit /b
.build\mkentprops.exe gamedata/entprops.kv || exit /b
llvm-rc /FO .build\dll.res src\dll.rc || exit /b
@@ -63,6 +63,7 @@ call :cc src/hook.c || exit /b
call :cc src/kv.c || exit /b
call :cc src/l4dwarp.c || exit /b
call :cc src/nosleep.c || exit /b
+call :cc src/portalcolours.c || exit /b
call :cc src/rinput.c || exit /b
call :cc src/sst.c || exit /b
call :cc src/x86.c || exit /b
diff --git a/src/autojump.c b/src/autojump.c
index 590b916..a8064da 100644
--- a/src/autojump.c
+++ b/src/autojump.c
@@ -62,7 +62,7 @@ static bool VCALLCONV hookcl(void *this) {
static bool unprot(void *gm) {
void **vtable = *(void ***)gm;
bool ret = os_mprot(vtable + vtidx_CheckJumpButton, sizeof(void *),
- PAGE_EXECUTE_READWRITE);
+ PAGE_READWRITE);
if (!ret) con_warn("autojump: couldn't make memory writable\n");
return ret;
}
diff --git a/src/demorec.c b/src/demorec.c
index 448e92d..a7e0486 100644
--- a/src/demorec.c
+++ b/src/demorec.c
@@ -234,7 +234,7 @@ bool demorec_init(void) {
void **vtable = *(void ***)demorecorder;
// XXX: 16 is totally arbitrary here! figure out proper bounds later
- if (!os_mprot(vtable, 16 * sizeof(void *), PAGE_EXECUTE_READWRITE)) {
+ if (!os_mprot(vtable, 16 * sizeof(void *), PAGE_READWRITE)) {
#ifdef _WIN32
char err[128];
OS_WINDOWS_ERROR(err);
diff --git a/src/nosleep.c b/src/nosleep.c
index 4ad02df..c1c5a9b 100644
--- a/src/nosleep.c
+++ b/src/nosleep.c
@@ -55,7 +55,7 @@ bool nosleep_init(void) {
}
vtable = *(void ***)insys;
if (!os_mprot(vtable + vtidx_SleepUntilInput, sizeof(void *),
- PAGE_EXECUTE_READWRITE)) {
+ PAGE_READWRITE)) {
con_warn("nosleep: couldn't make memory writable\n");
return false;
}
diff --git a/src/portalcolours.c b/src/portalcolours.c
new file mode 100644
index 0000000..6030bb8
--- /dev/null
+++ b/src/portalcolours.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2022 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "con_.h"
+#include "engineapi.h"
+#include "extmalloc.h"
+#include "gametype.h"
+#include "hook.h"
+#include "intdefs.h"
+#include "mem.h"
+#include "os.h"
+#include "ppmagic.h"
+#include "vcall.h"
+
+// It's like the thing Portal Tools does, but at runtime!
+
+DEF_CVAR(sst_portal_colour0, "Crosshair colour for gravity beam (hex)",
+ "F2CAA7", CON_ARCHIVE | CON_HIDDEN)
+DEF_CVAR(sst_portal_colour1, "Crosshair colour for left portal (hex)",
+ "40A0FF", CON_ARCHIVE | CON_HIDDEN)
+DEF_CVAR(sst_portal_colour2, "Crosshair colour for right portal (hex)",
+ "FFA020", CON_ARCHIVE | CON_HIDDEN)
+// XXX: bit weird that this is still con_colour. should we move it back out to
+// engineapi as a general colour struct??
+static struct con_colour colours[3] = {
+ {242, 202, 167, 255}, {64, 160, 255, 255}, {255, 160, 32, 255}};
+
+static void hexparse(uchar out[static 4], const char *s) {
+ const char *p = s;
+ for (uchar *q = out; q - out < 3; ++q) {
+ if (*p >= '0' && *p <= '9') {
+ *q = *p++ - '0' << 4;
+ }
+ else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') {
+ *q = 10 + (*p++ | 32) - 'a' << 4;
+ }
+ else {
+ // screw it, just fall back on white, I guess.
+ // note: this also handles *p == '\0' so we don't overrun the string
+ memset(out, 255, 4); // write 4 rather than 3, prolly faster?
+ return;
+ }
+ // repetitive unrolled nonsense
+ if (*p >= '0' && *p <= '9') {
+ *q |= *p++ - '0';
+ }
+ else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') {
+ *q |= 10 + (*p++ | 32) - 'a';
+ }
+ else {
+ memset(out, 255, 4);
+ return;
+ }
+ }
+ //out[3] = 255; // never changes!
+}
+
+static void colourcb(struct con_var *v) {
+ // this is stupid and ugly and has no friends, too bad!
+ if (v == sst_portal_colour0) {
+ hexparse(colours[0].bytes, con_getvarstr(v));
+ }
+ else if (v == sst_portal_colour1) {
+ hexparse(colours[1].bytes, con_getvarstr(v));
+ }
+ else /* sst_portal_colour2 */ {
+ hexparse(colours[2].bytes, con_getvarstr(v));
+ }
+}
+
+// Original sig is the following but we wanna avoid calling convention weirdness
+//typedef struct con_colour (*UTIL_Portal_Color_func)(int);
+typedef void (*UTIL_Portal_Color_func)(struct con_colour *out, int portal);
+static UTIL_Portal_Color_func orig_UTIL_Portal_Color;
+static void hook_UTIL_Portal_Color(struct con_colour *out, int portal) {
+ if (portal < 0 || portal > 2) *out = (struct con_colour){255, 255, 255, 255};
+ else *out = colours[portal];
+}
+
+// TODO(compat): would like to do the usual pointer-chasing business instead of
+// using hardcoded offsets, but that's pretty hard here. Would probably have to
+// do the entprops stuff for ClientClass, get at the portalgun factory, get a
+// vtable, find ViewModelDrawn or something, chase through another 4 or 5 call
+// offsets to find something that calls UTIL_Portal_Color... that or dig through
+// vgui/hud entries, find the crosshair drawing...
+//
+// For now we do this!
+
+static bool find_UTIL_Portal_Color(void *base) {
+ static const uchar x[] = HEXBYTES(8B, 44, 24, 08, 83, E8, 00, 74, 37, 83,
+ E8, 01, B1, FF, 74, 1E, 83, E8, 01, 8B, 44, 24, 04, 88);
+ // 5135
+ orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)mem_offset(base, 0x1BF090);
+ if (!memcmp((void *)orig_UTIL_Portal_Color, x, sizeof(x))) return true;
+ // 3420
+ orig_UTIL_Portal_Color = (UTIL_Portal_Color_func)mem_offset(base, 0x1AA810);
+ if (!memcmp((void *)orig_UTIL_Portal_Color, x, sizeof(x))) return true;
+ return false;
+}
+
+bool portalcolours_init(void *clientlib) { // ... should libs be globals?
+ if (!GAMETYPE_MATCHES(Portal)) return false;
+#ifdef _WIN32
+ if (!find_UTIL_Portal_Color(clientlib)) {
+ con_warn("portalcolours: error: couldn't find UTIL_Portal_Color\n");
+ 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) {
+ con_warn("portalcolours: error: couldn't hook UTIL_Portal_Color\n");
+ return false;
+ }
+ sst_portal_colour0->base.flags &= ~CON_HIDDEN;
+ sst_portal_colour0->cb = &colourcb;
+ sst_portal_colour1->base.flags &= ~CON_HIDDEN;
+ sst_portal_colour1->cb = &colourcb;
+ sst_portal_colour2->base.flags &= ~CON_HIDDEN;
+ sst_portal_colour2->cb = &colourcb;
+ return true;
+#else
+#warning TODO(linux): yet more stuff!
+ return false;
+#endif
+}
+
+void portalcolours_end(void) {
+ unhook_inline((void *)orig_UTIL_Portal_Color);
+}
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/portalcolours.h b/src/portalcolours.h
new file mode 100644
index 0000000..2dd4bb5
--- /dev/null
+++ b/src/portalcolours.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2022 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
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INC_PORTALCOLOURS_H
+#define INC_PORTALCOLOURS_H
+
+#include <stdbool.h>
+
+bool portalcolours_init(void *clientlib);
+void portalcolours_end();
+
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.c b/src/sst.c
index b4a63aa..39996c9 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -33,6 +33,7 @@
#include "hook.h"
#include "l4dwarp.h"
#include "nosleep.h"
+#include "portalcolours.h"
#include "os.h"
#include "rinput.h"
#include "vcall.h"
@@ -46,10 +47,8 @@
static int ifacever;
-#ifdef __linux__
// we need to keep this reference to dlclose() it later - see below
static void *clientlib = 0;
-#endif
#ifdef _WIN32
extern long __ImageBase; // this is actually the PE header struct but don't care
@@ -189,7 +188,7 @@ static const void *const *const plugin_obj;
// but we want to actually release the plugin this decade so for now I'm just
// plonking some bools here and worrying about it later. :^)
static bool has_autojump = false, has_demorec = false, has_fov = false,
- has_nosleep = false;
+ has_nosleep = false, has_portalcolours = false;
#ifdef _WIN32
static bool has_rinput = false;
#endif
@@ -207,6 +206,7 @@ static void do_featureinit(void) {
has_fov = fov_init(has_ent);
if (has_ent) l4dwarp_init();
has_nosleep = nosleep_init();
+ if (clientlib) has_portalcolours = portalcolours_init(clientlib);
#ifdef _WIN32
has_rinput = rinput_init();
#endif
@@ -286,7 +286,7 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {
*p = (void *)&nop_p_v; // OnEdictFreed
#ifdef _WIN32
- void *clientlib = GetModuleHandleW(gameinfo_clientlib);
+ clientlib = GetModuleHandleW(gameinfo_clientlib);
#else
// Apparently on Linux, the client library isn't actually loaded yet here,
// so RTLD_NOLOAD won't actually find it. We have to just dlopen it
@@ -385,6 +385,7 @@ static void do_unload(void) {
if (has_demorec) demorec_end();
if (has_fov) fov_end(); // dep on ent
if (has_nosleep) nosleep_end();
+ if (has_portalcolours) portalcolours_end();
#ifdef _WIN32
if (has_rinput) rinput_end();
#endif