diff --git a/EDFModLoader/EDFModLoader.vcxproj b/EDFModLoader/EDFModLoader.vcxproj
index 2de580a..7e10881 100644
--- a/EDFModLoader/EDFModLoader.vcxproj
+++ b/EDFModLoader/EDFModLoader.vcxproj
@@ -194,6 +194,7 @@
+
@@ -202,6 +203,7 @@
+
diff --git a/EDFModLoader/EDFModLoader.vcxproj.filters b/EDFModLoader/EDFModLoader.vcxproj.filters
index 6af423d..6c71457 100644
--- a/EDFModLoader/EDFModLoader.vcxproj.filters
+++ b/EDFModLoader/EDFModLoader.vcxproj.filters
@@ -21,6 +21,9 @@
Source Files
+
+ Source Files
+
@@ -40,5 +43,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/EDFModLoader/OnlineLimit.cpp b/EDFModLoader/OnlineLimit.cpp
new file mode 100644
index 0000000..d8a4de4
--- /dev/null
+++ b/EDFModLoader/OnlineLimit.cpp
@@ -0,0 +1,53 @@
+#include
+#include
+
+#include "OnlineLimit.h"
+
+// Injects hook into game process
+void __fastcall WriteHookToProcess(void *addr, void *data, size_t len) {
+ DWORD oldProtect;
+ VirtualProtect(addr, len, PAGE_EXECUTE_READWRITE, &oldProtect);
+ memcpy(addr, data, len);
+ VirtualProtect(addr, len, oldProtect, &oldProtect);
+ // Refresh cpu instruction?
+ // FlushInstructionCache(handleEXE, addr, len);
+}
+
+// update game's original functions with interruption, need 15 bytes
+void __fastcall hookGameBlockWithInt3(void *targetAddr, uintptr_t dataAddr) {
+ uint8_t hookFunction[] = {
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr
+ 0xCC // int3
+ };
+ memcpy(&hookFunction[6], &dataAddr, sizeof(dataAddr));
+
+ WriteHookToProcess(targetAddr, hookFunction, sizeof(hookFunction));
+}
+
+// Blocking EDF5 creates public rooms wherever possible.
+void __fastcall EDF5OnlineRoomLimit(PBYTE hmodEXE) {
+ // forced private room creation
+ // everyone, offset is 0x57F88F
+ unsigned char everyone1[] = {0x82, 0x94};
+ WriteHookToProcess((void *)(hmodEXE + 0x58048F + 3), &everyone1, 2U);
+ unsigned char everyone2[] = {0x3C, 0x78};
+ WriteHookToProcess((void *)(hmodEXE + 0x5820D5 + 3), &everyone2, 2U);
+ // need password, offset is 0x57FAD0
+ unsigned char password1[] = {0x41};
+ WriteHookToProcess((void *)(hmodEXE + 0x5806D0 + 3), &password1, 1U);
+ unsigned char password2[] = {0xDC, 0x78};
+ WriteHookToProcess((void *)(hmodEXE + 0x582035 + 3), &password2, 2U);
+}
+
+extern "C" {
+void __fastcall ASMreadMissionSavaData();
+uintptr_t readMissionSavaDataRetAddr;
+}
+
+// Disable online when mod folder is enabled.
+void __fastcall EDF6OnlineRoomLimit(PBYTE hmodEXE, uintptr_t OlOfs) {
+ // edf.dll+E2970, Determine mission savedata location, R8 is 1 when it is online.
+ hookGameBlockWithInt3((void *)(hmodEXE + OlOfs), (uintptr_t)ASMreadMissionSavaData);
+ readMissionSavaDataRetAddr = (uintptr_t)(hmodEXE + OlOfs+0x10);
+}
diff --git a/EDFModLoader/OnlineLimit.h b/EDFModLoader/OnlineLimit.h
new file mode 100644
index 0000000..7320d1d
--- /dev/null
+++ b/EDFModLoader/OnlineLimit.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// Injects hook into game process
+void __fastcall WriteHookToProcess(void *addr, void *data, size_t len);
+// update game's original functions with interruption, need 15 bytes
+void __fastcall hookGameBlockWithInt3(void *targetAddr, uintptr_t dataAddr);
+
+// Blocking EDF5 creates public rooms wherever possible.
+void __fastcall EDF5OnlineRoomLimit(PBYTE hmodEXE);
+
+// Disable online when mod folder is enabled.
+void __fastcall EDF6OnlineRoomLimit(PBYTE hmodEXE, uintptr_t OlOfs);
diff --git a/EDFModLoader/dllmain.cpp b/EDFModLoader/dllmain.cpp
index 7c969a5..5aa6ce2 100644
--- a/EDFModLoader/dllmain.cpp
+++ b/EDFModLoader/dllmain.cpp
@@ -17,6 +17,7 @@
#include "proxy.h"
#include "PluginAPI.h"
#include "LoggerTweaks.h"
+#include "OnlineLimit.h"
#include
#include
@@ -127,6 +128,7 @@ static BOOL LoadPluginsB = TRUE;
static BOOL LoadASI = TRUE;
static BOOL Redirect = TRUE;
static BOOL GameLog = FALSE;
+extern "C" int ActiveModFolder = 0;
// Pointer sets
typedef struct {
@@ -284,6 +286,7 @@ static void *__fastcall crifsio_hook(void *unk1, oddstr *str, void *unk2) {
if (FileExistsW(modpath)) {
PLOG_DEBUG << "Redirecting access to " << modpath;
wstrassign_orig(str, modpath, newlen);
+ ActiveModFolder = 1;
}
delete[] modpath;
}
@@ -472,10 +475,12 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
}
}
FuncOffsets pointers = {};
+ int EDFversion = 0;
if (pointerSet != -1) {
PLOG_INFO << "Running under " << psets[pointerSet].ident;
pointers = psets[pointerSet].pointers;
plugFunc = psets[pointerSet].plugfunc;
+ EDFversion = psets[pointerSet].version;
} else if (lstrcmpiA(hmodName, "EDF6.exe") == 0) {
PLOG_INFO << "Running under EDF6";
plugFunc = "EML6_Load";
@@ -528,6 +533,15 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
if (pointers.gamelog == NULL) {
PLOG_ERROR << "Failed to locate game debug logging function";
}
+
+ scanner = LightningScanner::Scanner("4833C44889451F44894DBF4D63F04C8BFA48895597488BF1488D4DE7");
+ uintptr_t checkModeType = ScanPtr(scanner.Find(hmodEXE, ScanRange));
+ if (pointers.gamelog == NULL) {
+ pointers.redirect = NULL;
+ PLOG_ERROR << "Disallow mod folders";
+ } else {
+ EDF6OnlineRoomLimit(hmodEXE, (checkModeType - (0xE2993 - 0xE2970)));
+ }
} else {
PLOG_ERROR << "Failed to determine what exe is running";
break;
@@ -550,6 +564,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
wstrassign_orig = (wstrassign_func)((PBYTE)hmodEXE + pointers.wstrassign);
if (pointers.wstrassign) {
SetupHook(pointers.redirect, (PVOID*)&crifsio_orig, crifsio_hook, "Mods folder redirector", Redirect);
+ // Limit the creation of public rooms.
+ if (EDFversion == 5) {
+ EDF5OnlineRoomLimit(hmodEXE);
+ }
} else {
PLOG_INFO << "Skipping unsupported " << hmodName << " hook (Mods folder redirector, no std::wstring::assign)";
}
diff --git a/EDFModLoader/winmm.asm b/EDFModLoader/winmm.asm
index 44c175c..5b2cfe4 100644
--- a/EDFModLoader/winmm.asm
+++ b/EDFModLoader/winmm.asm
@@ -1,5 +1,7 @@
.data
extern PA : qword
+extern ActiveModFolder : dword
+extern readMissionSavaDataRetAddr : qword
extern gamelog_hook_main : proto
@@ -22,9 +24,40 @@ extern save_xmm5 : OWORD
.code
runASM proc
-jmp qword ptr [PA]
+ jmp qword ptr [PA]
+ int 3
runASM endp
+align 16
+
+ASMreadMissionSavaData proc
+
+ checkOnline:
+ cmp r8d, 1
+ je toCrash
+ cmp r8d, 3
+ je toCrash
+ cmp r8d, 5
+ je toCrash ; If is offline mode, allow access to game
+ ofsE1D70:
+ mov [rsp+18h], rbx
+ push rbp
+ push rsi
+ push rdi
+ push r12
+ push r13
+ push r14
+ push r15
+ jmp readMissionSavaDataRetAddr
+ toCrash:
+ ; Otherwise crashes game
+ mov dword ptr [0], 0
+ int 3
+
+ASMreadMissionSavaData ENDP
+
+align 16
+
; The original function normally doesn't touch any registers, or do anything.
; The code that calls this function is optimized for that.
; So we must preserve all volatile registers ourself.