summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/democustom.c138
-rw-r--r--src/democustom.h35
-rw-r--r--src/demorec.c114
-rw-r--r--src/demorec.h14
-rw-r--r--src/sst.c6
5 files changed, 180 insertions, 127 deletions
diff --git a/src/democustom.c b/src/democustom.c
new file mode 100644
index 0000000..0a1174f
--- /dev/null
+++ b/src/democustom.c
@@ -0,0 +1,138 @@
+/*
+ * 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 "bitbuf.h"
+#include "con_.h"
+#include "democustom.h"
+#include "demorec.h"
+#include "engineapi.h"
+#include "errmsg.h"
+#include "gamedata.h"
+#include "intdefs.h"
+#include "mem.h"
+#include "ppmagic.h"
+#include "vcall.h"
+
+static int nbits_msgtype, nbits_datalen;
+
+// The engine allows usermessages up to 255 bytes, we add 2 bytes of overhead,
+// and then there's the leading bits before that too (see create_message)
+static char bb_buf[DEMOCUSTOM_MSG_MAX + 4];
+static struct bitbuf bb = {
+ bb_buf, sizeof(bb_buf), sizeof(bb_buf) * 8, 0, false, false, "SST"
+};
+
+static void create_message(struct bitbuf *msg, const void *buf, int len) {
+ // The way we pack our custom demo data is via a user message packet with
+ // type "HudText" - this causes the client to do a text lookup which will
+ // simply silently fail on invalid keys. By making the first byte null
+ // (creating an empty string), we get the rest of the packet to stick in
+ // whatever other data we want.
+ //
+ // Notes from Uncrafted:
+ // > But yeah the data you want to append is as follows:
+ // > - 6 bits (5 bits in older versions) for the message type - should be 23
+ // > for user message
+ bitbuf_appendbits(msg, 23, nbits_msgtype);
+ // > - 1 byte for the user message type - should be 2 for HudText
+ bitbuf_appendbyte(msg, 2);
+ // > - ~~an int~~ 11 or 12 bits for the length of your data in bits,
+ bitbuf_appendbits(msg, len * 8, nbits_datalen); // NOTE: assuming len <= 254
+ // > - your data
+ // [first the aforementioned null byte, plus an arbitrary marker byte to
+ // avoid confusion when parsing the demo later...
+ bitbuf_appendbyte(msg, 0);
+ bitbuf_appendbyte(msg, 0xAC);
+ // ... and then just the data itself]
+ bitbuf_appendbuf(msg, buf, len);
+ // Thanks Uncrafted, very cool!
+}
+
+typedef void (*VCALLCONV WriteMessages_func)(void *this, struct bitbuf *msg);
+static WriteMessages_func WriteMessages = 0;
+
+void democustom_write(const void *buf, int len) {
+ create_message(&bb, buf, len);
+ WriteMessages(demorecorder, &bb);
+ bitbuf_reset(&bb);
+}
+
+// This finds the CDemoRecorder::WriteMessages() function, which takes a raw
+// network packet, wraps it up in the appropriate demo framing format and writes
+// it out to the demo file being recorded.
+static bool find_WriteMessages(void) {
+ // TODO(compat): probably rewrite this to just scan for a call instruction!
+ const uchar *insns = (*(uchar ***)demorecorder)[vtidx_RecordPacket];
+ // RecordPacket calls WriteMessages pretty much right away:
+ // 56 push esi
+ // 57 push edi
+ // 8B F1 mov esi,ecx
+ // 8D BE lea edi,[esi + 0x68c]
+ // 8C 06 00 00
+ // 57 push edi
+ // E8 call CDemoRecorder_WriteMessages
+ // B0 EF FF FF
+ // So we just double check the byte pattern...
+ static const uchar bytes[] =
+#ifdef _WIN32
+ HEXBYTES(56, 57, 8B, F1, 8D, BE, 8C, 06, 00, 00, 57, E8);
+#else
+#warning This is possibly different on Linux too, have a look!
+ {-1, -1, -1, -1, -1, -1};
+#endif
+ if (!memcmp(insns, bytes, sizeof(bytes))) {
+ ssize off = mem_loadoffset(insns + sizeof(bytes));
+ // ... and then offset is relative to the address of whatever is _after_
+ // the call instruction... because x86.
+ WriteMessages = (WriteMessages_func)(insns + sizeof(bytes) + 4 + off);
+ return true;
+ }
+ return false;
+}
+
+DECL_VFUNC_DYN(int, GetEngineBuildNumber)
+
+bool democustom_init(void) {
+ if (!has_vtidx_GetEngineBuildNumber || !has_vtidx_RecordPacket) {
+ errmsg_errorx("missing gamedata entries for this engine");
+ return false;
+ }
+
+ // More UncraftedkNowledge:
+ // > yeah okay so [the usermessage length is] 11 bits if the demo protocol
+ // > is 11 or if the game is l4d2 and the network protocol is 2042.
+ // > otherwise it's 12 bits
+ // > there might be some other l4d2 versions where it's 11 but idk
+ // So here we have to figure out the network protocol version!
+ // NOTE: assuming engclient != null as GEBN index relies on client version
+ int buildnum = VCALL(engclient, GetEngineBuildNumber);
+ // condition is redundant until other GetEngineBuildNumber offsets are added
+ // if (GAMETYPE_MATCHES(L4D2)) {
+ nbits_msgtype = 6;
+ // based on Some Code I Read, buildnum *should* be the protocol version,
+ // however L4D2 returns the actual game version instead, because sure
+ // why not. The only practical difference though is that the network
+ // protocol froze after 2042, so we just have to do a >=. No big deal
+ // really.
+ if (buildnum >= 2042) nbits_datalen = 11; else nbits_datalen = 12;
+ // }
+
+ return find_WriteMessages();
+}
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/democustom.h b/src/democustom.h
new file mode 100644
index 0000000..9f9bbe9
--- /dev/null
+++ b/src/democustom.h
@@ -0,0 +1,35 @@
+/*
+ * 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_DEMOCUSTOM_H
+#define INC_DEMOCUSTOM_H
+
+#include <stdbool.h>
+
+/* maximum length of a custom demo message, in bytes */
+#define DEMOCUSTOM_MSG_MAX 253
+
+/*
+ * Write a block of up to DEMOWRITER_MSG_MAX bytes into the currently recording
+ * demo - NOT bounds checked, caller MUST ensure length is okay!
+ */
+void democustom_write(const void *buf, int len);
+
+bool democustom_init(void);
+
+#endif
+
+// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/demorec.c b/src/demorec.c
index b4a674d..1fb5e8e 100644
--- a/src/demorec.c
+++ b/src/demorec.c
@@ -18,9 +18,7 @@
#include <stdbool.h>
#include <string.h>
-#include "bitbuf.h"
#include "con_.h"
-#include "demorec.h"
#include "engineapi.h"
#include "errmsg.h"
#include "gamedata.h"
@@ -37,7 +35,7 @@
DEF_CVAR(sst_autorecord, "Continuously record demos even after reconnecting", 1,
CON_ARCHIVE | CON_HIDDEN)
-static void *demorecorder;
+void *demorecorder;
static int *demonum;
static bool *recording;
static bool wantstop = false;
@@ -252,114 +250,4 @@ void demorec_end(void) {
cmd_stop->cb = orig_stop_cb;
}
-// custom data writing stuff is a separate feature, defined below. it we can't
-// find WriteMessage, we can still probably do the auto recording stuff above
-
-static int nbits_msgtype, nbits_datalen;
-
-// The engine allows usermessages up to 255 bytes, we add 2 bytes of overhead,
-// and then there's the leading bits before that too (see create_message)
-static char bb_buf[DEMOREC_CUSTOM_MSG_MAX + 4];
-static struct bitbuf bb = {
- bb_buf, sizeof(bb_buf), sizeof(bb_buf) * 8, 0, false, false, "SST"
-};
-
-static void create_message(struct bitbuf *msg, const void *buf, int len) {
- // The way we pack our custom demo data is via a user message packet with
- // type "HudText" - this causes the client to do a text lookup which will
- // simply silently fail on invalid keys. By making the first byte null
- // (creating an empty string), we get the rest of the packet to stick in
- // whatever other data we want.
- //
- // Notes from Uncrafted:
- // > But yeah the data you want to append is as follows:
- // > - 6 bits (5 bits in older versions) for the message type - should be 23
- // > for user message
- bitbuf_appendbits(msg, 23, nbits_msgtype);
- // > - 1 byte for the user message type - should be 2 for HudText
- bitbuf_appendbyte(msg, 2);
- // > - ~~an int~~ 11 or 12 bits for the length of your data in bits,
- bitbuf_appendbits(msg, len * 8, nbits_datalen); // NOTE: assuming len <= 254
- // > - your data
- // [first the aforementioned null byte, plus an arbitrary marker byte to
- // avoid confusion when parsing the demo later...
- bitbuf_appendbyte(msg, 0);
- bitbuf_appendbyte(msg, 0xAC);
- // ... and then just the data itself]
- bitbuf_appendbuf(msg, buf, len);
- // Thanks Uncrafted, very cool!
-}
-
-typedef void (*VCALLCONV WriteMessages_func)(void *this, struct bitbuf *msg);
-static WriteMessages_func WriteMessages = 0;
-
-void demorec_writecustom(void *buf, int len) {
- create_message(&bb, buf, len);
- WriteMessages(demorecorder, &bb);
- bitbuf_reset(&bb);
-}
-
-// This finds the CDemoRecorder::WriteMessages() function, which takes a raw
-// network packet, wraps it up in the appropriate demo framing format and writes
-// it out to the demo file being recorded.
-static bool find_WriteMessages(void) {
- // TODO(compat): probably rewrite this to just scan for a call instruction!
- const uchar *insns = (*(uchar ***)demorecorder)[vtidx_RecordPacket];
- // RecordPacket calls WriteMessages pretty much right away:
- // 56 push esi
- // 57 push edi
- // 8B F1 mov esi,ecx
- // 8D BE lea edi,[esi + 0x68c]
- // 8C 06 00 00
- // 57 push edi
- // E8 call CDemoRecorder_WriteMessages
- // B0 EF FF FF
- // So we just double check the byte pattern...
- static const uchar bytes[] =
-#ifdef _WIN32
- HEXBYTES(56, 57, 8B, F1, 8D, BE, 8C, 06, 00, 00, 57, E8);
-#else
-#warning This is possibly different on Linux too, have a look!
- {-1, -1, -1, -1, -1, -1};
-#endif
- if (!memcmp(insns, bytes, sizeof(bytes))) {
- ssize off = mem_loadoffset(insns + sizeof(bytes));
- // ... and then offset is relative to the address of whatever is _after_
- // the call instruction... because x86.
- WriteMessages = (WriteMessages_func)(insns + sizeof(bytes) + 4 + off);
- return true;
- }
- return false;
-}
-
-DECL_VFUNC_DYN(int, GetEngineBuildNumber)
-
-bool demorec_custom_init(void) {
- if (!has_vtidx_GetEngineBuildNumber || !has_vtidx_RecordPacket) {
- errmsg_errorx("custom: missing gamedata entries for this engine");
- return false;
- }
-
- // More UncraftedkNowledge:
- // > yeah okay so [the usermessage length is] 11 bits if the demo protocol
- // > is 11 or if the game is l4d2 and the network protocol is 2042.
- // > otherwise it's 12 bits
- // > there might be some other l4d2 versions where it's 11 but idk
- // So here we have to figure out the network protocol version!
- // NOTE: assuming engclient != null as GEBN index relies on client version
- int buildnum = VCALL(engclient, GetEngineBuildNumber);
- // condition is redundant until other GetEngineBuildNumber offsets are added
- // if (GAMETYPE_MATCHES(L4D2)) {
- nbits_msgtype = 6;
- // based on Some Code I Read, buildnum *should* be the protocol version,
- // however L4D2 returns the actual game version instead, because sure
- // why not. The only practical difference though is that the network
- // protocol froze after 2042, so we just have to do a >=. No big deal
- // really.
- if (buildnum >= 2042) nbits_datalen = 11; else nbits_datalen = 12;
- // }
-
- return find_WriteMessages();
-}
-
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/demorec.h b/src/demorec.h
index a18bea4..7306c0a 100644
--- a/src/demorec.h
+++ b/src/demorec.h
@@ -20,20 +20,12 @@
#include <stdbool.h>
+/* For internal use by democustom */
+extern void *demorecorder;
+
bool demorec_init(void);
void demorec_end(void);
-bool demorec_custom_init(void);
-
-/* maximum length of a custom demo message, in bytes */
-#define DEMOREC_CUSTOM_MSG_MAX 253
-
-/*
- * Write a block of up to DEMOWRITER_MSG_MAX bytes into the currently recording
- * demo - NOT bounds checked, caller MUST ensure length is okay!
- */
-void demorec_writecustom(void *buf, int len);
-
#endif
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.c b/src/sst.c
index 7246e89..4182b09 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -26,6 +26,7 @@
#include "alias.h"
#include "autojump.h"
#include "con_.h"
+#include "democustom.h"
#include "demorec.h"
#include "engineapi.h"
#include "errmsg.h"
@@ -209,11 +210,10 @@ static const char *updatenotes = "\
static void do_featureinit(void) {
bool has_bind = bind_init();
if (has_bind) has_ac = ac_init();
- bool has_alias = alias_init();
+ alias_init();
has_autojump = autojump_init();
has_demorec = demorec_init();
- // not enabling demorec_custom yet - kind of incomplete and currently unused
- //if (has_demorec) demorec_custom_init();
+ if (has_demorec) democustom_init();
bool has_ent = ent_init();
has_fov = fov_init(has_ent);
if (has_ent) l4dwarp_init();