summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcompile2
-rw-r--r--compile.bat3
-rw-r--r--gamedata/vphysics.txt10
-rw-r--r--src/portalisg.c152
4 files changed, 165 insertions, 2 deletions
diff --git a/compile b/compile
index 2ff2ae2..a15605b 100755
--- a/compile
+++ b/compile
@@ -101,7 +101,7 @@ $HOSTCC -O2 -fuse-ld=lld $warnings $stdflags \
-o .build/mkentprops src/build/mkentprops.c src/os.c
.build/gluegen `for s in $src; do echo "src/$s"; done`
.build/mkgamedata gamedata/engine.txt gamedata/gamelib.txt gamedata/inputsystem.txt \
-gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt
+gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt gamedata/vphysics.txt
.build/mkentprops gamedata/entprops.txt
for s in $src; do cc "$s"; done
$CC -shared -fpic -fuse-ld=lld -O0 -w -o .build/libtier0.so src/stubs/tier0.c
diff --git a/compile.bat b/compile.bat
index 543e50b..a9ccf44 100644
--- a/compile.bat
+++ b/compile.bat
@@ -94,6 +94,7 @@ setlocal DisableDelayedExpansion
:+ nosleep.c
:+ os.c
:+ portalcolours.c
+:+ portalisg.c
:+ rinput.c
:+ sst.c
:+ trace.c
@@ -122,7 +123,7 @@ if %host64%==1 (
-L.build %lbcryptprimitives_host% -o .build/mkentprops.exe src/build/mkentprops.c src/os.c || goto :end
.build\gluegen.exe%src% || goto :end
.build\mkgamedata.exe gamedata/engine.txt gamedata/gamelib.txt gamedata/inputsystem.txt ^
-gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt || goto :end
+gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt gamedata/vphysics.txt || goto :end
.build\mkentprops.exe gamedata/entprops.txt || goto :end
llvm-rc /FO .build\dll.res src\dll.rc || goto :end
for %%b in (%src%) do ( call :cc %%b || goto :end )
diff --git a/gamedata/vphysics.txt b/gamedata/vphysics.txt
new file mode 100644
index 0000000..9c8bfb6
--- /dev/null
+++ b/gamedata/vphysics.txt
@@ -0,0 +1,10 @@
+# IPhysics
+vtidx_CreateEnvironment 5
+
+# IPhysicsEnvironment
+vtidx_CreatePolyObject 7
+
+# IPhysicsObject
+vtidx_RecheckCollisionFilter 26
+
+# vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/portalisg.c b/src/portalisg.c
new file mode 100644
index 0000000..ffa80f2
--- /dev/null
+++ b/src/portalisg.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright © Willian Henrique <wsimanbrazil@yahoo.com.br>
+ *
+ * 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 "con_.h"
+#include "engineapi.h"
+#include "errmsg.h"
+#include "feature.h"
+#include "gamedata.h"
+#include "intdefs.h"
+#include "langext.h"
+#include "mem.h"
+#include "x86.h"
+#include "x86util.h"
+
+FEATURE("Portal \"ISG\" state reset (experimental)")
+GAMESPECIFIC(Portal1)
+REQUIRE_GAMEDATA(vtidx_CreateEnvironment)
+REQUIRE_GAMEDATA(vtidx_CreatePolyObject)
+REQUIRE_GAMEDATA(vtidx_RecheckCollisionFilter)
+
+static bool *isg_flag;
+static con_cmdcbv2 disconnect_cb;
+
+DEF_FEAT_CCMD_HERE(sst_portal_resetisg,
+ "Remove \"ISG\" state and disconnect from the server", 0) {
+ struct con_cmdargs disconnect_args = {0};
+ disconnect_cb(&disconnect_args);
+ *isg_flag = false;
+}
+
+static void **find_physenv_vtable(void *CreateEnvironment) {
+ const uchar *insns = (uchar *)CreateEnvironment;
+ for (const uchar *p = insns; p - insns < 16;) {
+ if (*p == X86_CALL) { p = insns = p + 5 + mem_loads32(p + 1); goto _1; }
+ NEXT_INSN(p, "call to CreateEnvironment");
+ }
+ return 0;
+_1: for (const uchar *p = insns; p - insns < 32;) {
+ // tail call to the constructor
+ if (*p == X86_JMPIW) { insns = p + 5 + mem_loads32(p + 1); goto _2; }
+ NEXT_INSN(p, "call to CPhysicsEnvironment constructor");
+ }
+ return 0;
+_2: for (const uchar *p = insns; p - insns < 16;) {
+ // the vtable is loaded pretty early on:
+ // mov dword ptr [reg], <vtable address>
+ if (*p == X86_MOVMIW && (p[1] & 0xF8) == 0) return mem_loadptr(p + 2);
+ NEXT_INSN(p, "CPhysicsEnvironment vtable");
+ }
+ return 0;
+}
+
+static void **find_physobj_vtable(void *CreatePolyObject) {
+ const uchar *insns = (uchar *)CreatePolyObject;
+ for (const uchar *p = insns; p - insns < 64;) {
+ // first thing in the method is a call (after pushing a million params)
+ if (*p == X86_CALL) {
+ insns = p + 5 + mem_loads32(p + 1);
+ goto _1;
+ }
+ NEXT_INSN(p, "call to CreatePhysicsObject");
+ }
+ return 0;
+_1: for (const uchar *p = insns; p - insns < 768;) {
+ // there's a call to "new CPhysicsObject" somewhere down the line.
+ // the (always inlined) constructor calls memset on the obj to init it.
+ // the obj's vtable being loaded in is interleaved with pushing args
+ // for memset and the order for all the instructions varies between
+ // versions. the consistent bit is that `push 72` always happens shortly
+ // before the vtable is loaded.
+ if (*p == X86_PUSHI8 && p[1] == 72) { insns = p + 2; goto _2; }
+ NEXT_INSN(p, "push before CPhysicsObject vtable load");
+ }
+ return 0;
+_2: for (const uchar *p = insns; p - insns < 16;) {
+ // mov dword ptr [reg], <vtable address>
+ if (*p == X86_MOVMIW && (p[1] & 0xF8) == 0) return mem_loadptr(p + 2);
+ NEXT_INSN(p, "CPhysicsObject vtable");
+ }
+ return 0;
+}
+
+static bool find_isg_flag(void *RecheckCollisionFilter) {
+ const uchar *insns = (uchar *)RecheckCollisionFilter, *p = insns;
+ while (p - insns < 32) {
+ // besides some flag handling, the only thing this function does is
+ // call m_pObject->recheck_collision_filter()
+ if (*p == X86_CALL) {
+ p = p + 5 + mem_loads32(p + 1);
+ goto _1;
+ }
+ NEXT_INSN(p, "call to RecheckCollisionFilter");
+ }
+ return false;
+_1: for (insns = p; p - insns < 32;) {
+ // recheck_collision_filter pretty much just calls a function
+ if (*p == X86_CALL) {
+ p = p + 5 + mem_loads32(p + 1);
+ goto _2;
+ }
+ NEXT_INSN(p, "call to recheck_ov_element");
+ }
+ return false;
+_2: for (insns = p; p - insns < 0x300;) {
+ // mov byte ptr [g_fDeferDeleteMindist]
+ if (*p == X86_MOVMI8 && p[1] == X86_MODRM(0, 0, 5) && p[6] == 1) {
+ isg_flag = mem_loadptr(p + 2);
+ return true;
+ }
+ NEXT_INSN(p, "g_fDeferDeleteMindist");
+ }
+ return false;
+}
+
+INIT {
+ disconnect_cb = con_getcmdcbv2(con_findcmd("disconnect"));
+ if_cold(!disconnect_cb) return FEAT_INCOMPAT;
+ void *phys = factory_engine("VPhysics031", 0);
+ if_cold (phys == 0) {
+ errmsg_errorx("couldn't get IPhysics interface");
+ return FEAT_INCOMPAT;
+ }
+ void **vtable = mem_loadptr(phys);
+ vtable = find_physenv_vtable(vtable[vtidx_CreateEnvironment]);
+ if_cold (!vtable) {
+ errmsg_errorx("couldn't find CPhysicsEnvironment vtable");
+ return FEAT_INCOMPAT;
+ }
+ vtable = find_physobj_vtable(vtable[vtidx_CreatePolyObject]);
+ if_cold (!vtable) {
+ errmsg_errorx("couldn't find CPhysicsObject vtable");
+ return FEAT_INCOMPAT;
+ }
+ if_cold (!find_isg_flag(vtable[vtidx_RecheckCollisionFilter])) {
+ errmsg_errorx("couldn't find ISG flag");
+ return FEAT_INCOMPAT;
+ }
+ return FEAT_OK;
+}