From f03e7c515d285e0efff2a09dacba48120778614a Mon Sep 17 00:00:00 2001 From: Hayden K Date: Tue, 11 Feb 2025 17:48:49 -0500 Subject: Add feature to remove the chat rate limit This probably works for basically every game, but it was only tested for L4D/L4D2/L4D:S/Portal 2. --- src/chatrate.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/chatrate.c (limited to 'src') diff --git a/src/chatrate.c b/src/chatrate.c new file mode 100644 index 0000000..b69b0d5 --- /dev/null +++ b/src/chatrate.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2025 Hayden K + * + * 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 "errmsg.h" +#include "feature.h" +#include "intdefs.h" +#include "langext.h" +#include "os.h" +#include "x86.h" +#include "x86util.h" + +FEATURE("chat rate limit removal") +GAMESPECIFIC(L4Dbased) // Tested/known to work in L4D1/2, L4D:S, and Portal 2. + +static uchar *patchedbyte; + +// Same method as SAR here. Basically, the game only lets you chat every 0.66s. +// So, instead of adding 0.66 to the current time, we subtract it, and that +// means we can always chat immediately. + +static inline bool find_ratelimit_insn(struct con_cmd *cmd_say) { + // Find the add instruction + uchar *insns = (uchar *)cmd_say->cb; + for (uchar *p = insns; p - insns < 128;) { + // find FADD + if (p[0] == X86_FLTBLK5 && p[1] == X86_MODRM(0, 0, 5)) { + patchedbyte = p + 1; + return true; + } + // Portal 2, L4D2 2125-2134, L4D:S all use SSE2, so try finding ADDSD + if (p[0] == X86_PFX_REPN && p[1] == X86_2BYTE & p[2] == X86_2B_ADD) { + patchedbyte = p + 2; + return true; + } + NEXT_INSN(p, "chat rate limit"); + } + return false; +} + +static inline bool patch_ratelimit_insn() { + // if FADD replace with FSUB; otherwise it is ADDSD, replace that with SUBSD + if (!os_mprot(patchedbyte, 1, PAGE_EXECUTE_READWRITE)) { + errmsg_errorsys("failed to patch chat rate limit: " + "couldn't make memory writable"); + return false; + } + if (*patchedbyte == X86_MODRM(0, 0, 5)) *patchedbyte = X86_MODRM(0, 4, 5); + else *patchedbyte = X86_2B_SUB; + return true; +} + +static inline void unpatch_ratelimit_insn() { + // same logic as above but in reverse + if (*patchedbyte == X86_MODRM(0, 4, 5)) *patchedbyte = X86_MODRM(0, 0, 5); + else *patchedbyte = X86_2B_ADD; +} + +INIT { + struct con_cmd *cmd_say = con_findcmd("say"); + if_cold (!cmd_say) return false; + if (!find_ratelimit_insn(cmd_say)) { + errmsg_errorx("couldn't find chat rate limit instruction"); + return FEAT_INCOMPAT; + } + if (!patch_ratelimit_insn()) { + errmsg_errorx("couldn't patch chat rate limit"); + return FEAT_FAIL; + } + return FEAT_OK; +} + +END { + unpatch_ratelimit_insn(); +} -- cgit v1.2.3-54-g00ecf