2 Commitit 823c712b27 ... eb9237ae49

Tekijä SHA1 Viesti Päivämäärä
  Hayden K eb9237ae49 addon wip v4 4 kuukautta sitten
  Hayden K 5424690309 addon wip v3 4 kuukautta sitten
1 muutettua tiedostoa jossa 75 lisäystä ja 67 poistoa
  1. 75 67
      src/l4daddon.c

+ 75 - 67
src/l4daddon.c

@@ -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;