|
@@ -42,51 +42,49 @@ static int old_addonvecsz;
|
|
|
|
|
|
typedef void (*FSMAFAS_func)(bool, char *, char *, bool);
|
|
|
static FSMAFAS_func orig_FSMAFAS;
|
|
|
-static void hook_FSMAFAS(bool param1, char *param2, char *param3, bool param4) {
|
|
|
- // param1: if addons are disallowed in this mode (versus, scav, etc)
|
|
|
- // param2: campaign/mission
|
|
|
- // param3: gamemode
|
|
|
- // param4: is cur mode a mutation
|
|
|
- con_msg("======================\n");
|
|
|
- con_msg("testicles\n");
|
|
|
- con_msg("last_mission %s\n", last_mission);
|
|
|
- con_msg("last_gamemode %s\n", last_gamemode);
|
|
|
- con_msg("the old number of addons: %u\n", old_addonvecsz);
|
|
|
- if (*addonvecsz > 0) {
|
|
|
- old_addonvecsz = *addonvecsz;
|
|
|
- if (param2 && param3) {
|
|
|
- if ((strcmp(param2, last_mission)) || strcmp(param3, last_gamemode)) {
|
|
|
- con_msg("\ncalling original function (p2/p3 are not null, one of them changed)\n\n");
|
|
|
- orig_FSMAFAS(param1, param2, param3, param4);
|
|
|
- }
|
|
|
- strcpy(last_mission, param2);
|
|
|
- strcpy(last_gamemode, param3);
|
|
|
- } else {
|
|
|
- con_msg("\ncalling FSMAFAS (p2 and/or p3 are null)\n\n");
|
|
|
- strcpy(last_mission, "");
|
|
|
- strcpy(last_gamemode, "");
|
|
|
- orig_FSMAFAS(param1, param2, param3, param4);
|
|
|
- }
|
|
|
- } else if (old_addonvecsz > 0) {
|
|
|
- old_addonvecsz = *addonvecsz;
|
|
|
- con_msg("\ncalling FSMAFAS\n\n");
|
|
|
- orig_FSMAFAS(param1, param2, param3, param4);
|
|
|
- } else {
|
|
|
- con_msg("\ndid not call FSMAFAS, addons are disabled and they were not just disabled a moment ago\n\n");
|
|
|
- }
|
|
|
-
|
|
|
- con_msg("isAddonsDisallowedInMode: %u\n", param1);
|
|
|
- con_msg("mission: %s\n", param2);
|
|
|
- con_msg("mode: %s\n", param3);
|
|
|
- con_msg("isMutation: %u\n", param4);
|
|
|
- con_msg("the number of addons thing idk: %u\n", *addonvecsz);
|
|
|
- con_msg("======================\n");
|
|
|
+static void hook_FSMAFAS(bool p1, char *p2, char *p3, bool p4) {
|
|
|
+ // p1: if addons are disallowed in this mode (versus, scav, etc)
|
|
|
+ // p2: campaign/mission
|
|
|
+ // p3: gamemode
|
|
|
+ // p4: is cur mode a mutation
|
|
|
+ // note: the 4th parameter was first added in 2204 (Oct 21st 2020), but we
|
|
|
+ // don't have to worry about that since it's cdecl
|
|
|
+ // assumptions: addons and mode config for addon blocking (e.g. versus)
|
|
|
+ // aren't being changed mid-campaign
|
|
|
+
|
|
|
+ int curaddonvecsz = *addonvecsz;
|
|
|
+ // addons changed, which means we are in the main menu, another call to FSMAFAS
|
|
|
+ // with null p2 and p3 already happened and our "last_" variables have been
|
|
|
+ // cleared already. update the addon count and run original function
|
|
|
+ if (curaddonvecsz != old_addonvecsz) {
|
|
|
+ old_addonvecsz = curaddonvecsz;
|
|
|
+ goto hook_end;
|
|
|
+ }
|
|
|
+
|
|
|
+ // addons didn't change and no addons are enabled: do nothing
|
|
|
+ if (!curaddonvecsz) return;
|
|
|
+
|
|
|
+ // cache campaign and mode names and, if we were given a campaign and a mode
|
|
|
+ // name, try to early exit
|
|
|
+ if (p2 && p3) {
|
|
|
+ bool earlyret = !strcmp(p2, last_mission) && !strcmp(p3, last_gamemode);
|
|
|
+ strcpy(last_mission, p2);
|
|
|
+ strcpy(last_gamemode, p3);
|
|
|
+ if (earlyret) return;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ last_mission[0] = '\0';
|
|
|
+ last_gamemode[0] = '\0';
|
|
|
+ }
|
|
|
+hook_end:
|
|
|
+ orig_FSMAFAS(p1, p2, p3, p4);
|
|
|
}
|
|
|
|
|
|
-DECL_VFUNC(void, CEC_FSMAFAS, 179)
|
|
|
+DECL_VFUNC(void, CEC_MAFAS, 179) // CEngineClient::ManageAddonsForActiveSession
|
|
|
|
|
|
static inline bool find_FSMAFAS(void) {
|
|
|
- const uchar *insns = (const uchar*)VFUNC(engclient, CEC_FSMAFAS);
|
|
|
+ const uchar *insns = (const uchar*)VFUNC(engclient, CEC_MAFAS);
|
|
|
+ // CEngineClient::ManageAddonsForActiveSession just calls FSMAFAS
|
|
|
for (const uchar *p = insns; p - insns < 32;) {
|
|
|
if (p[0] == X86_CALL) {
|
|
|
orig_FSMAFAS = (FSMAFAS_func)(p + 5 + mem_loads32(p + 1));
|
|
@@ -101,7 +99,8 @@ static con_cmdcb orig_show_addon_metadata_cb;
|
|
|
|
|
|
static inline bool find_addonvecsz(void) {
|
|
|
const uchar *insns = (const uchar*)orig_show_addon_metadata_cb;
|
|
|
- // The show_addon_metadata command does...
|
|
|
+ // show_addon_metadata immediately checks if s_vecAddonMetadata is 0,
|
|
|
+ // so we can just grab it from the CMP instruction
|
|
|
for (const uchar *p = insns; p - insns < 32;) {
|
|
|
if (p[0] == X86_ALUMI8S && p[1] == X86_MODRM(0, 7, 5) && p[6] == 0) {
|
|
|
addonvecsz = mem_loadptr(p + 2);
|
|
@@ -116,44 +115,53 @@ static void *broken_addon_check;
|
|
|
static u8 orig_broken_addon_check_bytes[13];
|
|
|
static bool has_broken_addon_check = false;
|
|
|
|
|
|
-static inline void fix_broken_addon_check(bool larger_jmp_insn) {
|
|
|
+static inline void nop_addon_check(bool larger_jmp_insn) {
|
|
|
+ // In versions prior to 2.2.0.4 (Oct 21st 2020), FSMAFAS checks if any
|
|
|
+ // addons are enabled before doing anything else. If no addons are enabled,
|
|
|
+ // then the function just returns immediately. FSMAFAS gets called by
|
|
|
+ // update_addon_paths, and that gets called when you click 'Done' in the
|
|
|
+ // addons menu. This is problematic because whatever addons you last
|
|
|
+ // disabled won't get properly disabled since the rest of FSMAFAS doesn't
|
|
|
+ // execute. If, for example, you only had a common infected retexture addon
|
|
|
+ // enabled, and you decided to disable it, your commons would be invisible
|
|
|
+ // until you either restarted the game or enabled another addon.
|
|
|
+ // so, we simply NOP the relevant CMP and JZ instructions to get rid of this
|
|
|
+ // check, so that FSMAFAS always executes. Depending on the size of the JZ
|
|
|
+ // instruction, we need to NOP either 9 bytes (e.g. 2.2.0.3) or 13 bytes
|
|
|
+ // (e.g. 2.1.4.7). So, we always write a 9-byte NOP, and sometimes also
|
|
|
+ // write a 4-byte NOP afterwards.
|
|
|
const u8 nop[] =
|
|
|
HEXBYTES(66, 0F, 1F, 84, 00, 00, 00, 00, 00, 0F, 1F, 40, 00);
|
|
|
memcpy(orig_broken_addon_check_bytes, broken_addon_check, 13);
|
|
|
- if (larger_jmp_insn) {
|
|
|
- if_hot(os_mprot(broken_addon_check, 13, PAGE_EXECUTE_READWRITE)) {
|
|
|
- memcpy(broken_addon_check, nop, 13);
|
|
|
- } else {
|
|
|
- errmsg_warnsys("unable to fix broken addon check: "
|
|
|
- "couldn't make make memory writable");
|
|
|
- }
|
|
|
- } else {
|
|
|
- if_hot(os_mprot(broken_addon_check, 9, PAGE_EXECUTE_READWRITE)) {
|
|
|
- memcpy(broken_addon_check, nop, 9);
|
|
|
- } else {
|
|
|
- errmsg_warnsys("unable to fix broken addon check: "
|
|
|
- "couldn't make make memory writable");
|
|
|
- }
|
|
|
+ int nop_size = larger_jmp_insn ? 13 : 9;
|
|
|
+ if_hot(os_mprot(broken_addon_check, nop_size, PAGE_EXECUTE_READWRITE)) {
|
|
|
+ memcpy(broken_addon_check, nop, nop_size);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ errmsg_warnsys("unable to fix broken addon check: "
|
|
|
+ "couldn't make make memory writable");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static inline bool find_broken_addon_check(void) {
|
|
|
+static inline void try_fix_broken_addon_check(void) {
|
|
|
uchar *insns = (uchar *)orig_FSMAFAS;
|
|
|
for (uchar *p = insns; p - insns < 32;) {
|
|
|
if (p[0] == X86_ALUMI8S && p[1] == X86_MODRM(0, 7, 5) &&
|
|
|
mem_loadptr(p + 2) == addonvecsz) {
|
|
|
broken_addon_check = p;
|
|
|
has_broken_addon_check = true;
|
|
|
- if (p[7] == X86_2BYTE) {
|
|
|
- fix_broken_addon_check(true);
|
|
|
- } else {
|
|
|
- fix_broken_addon_check(false);
|
|
|
- }
|
|
|
- return true;
|
|
|
+ nop_addon_check(p[7] == X86_2BYTE);
|
|
|
+ return;
|
|
|
}
|
|
|
- NEXT_INSN(p, "broken addon check");
|
|
|
+ int _len = x86_len(p);
|
|
|
+ if_cold(_len == -1) {
|
|
|
+ errmsg_errorx("unknown or invalid instruction looking for broken "
|
|
|
+ "addon check");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ p += _len;
|
|
|
}
|
|
|
- return false;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
PREINIT {
|
|
@@ -172,7 +180,7 @@ INIT {
|
|
|
errmsg_errorx("couldn't find addon metadata counter");
|
|
|
return false;
|
|
|
}
|
|
|
- find_broken_addon_check();
|
|
|
+ try_fix_broken_addon_check();
|
|
|
orig_FSMAFAS = (FSMAFAS_func)hook_inline( (void *)orig_FSMAFAS,
|
|
|
(void *)&hook_FSMAFAS);
|
|
|
return true;
|