Skip to content

Commit 75618a0

Browse files
committed
patch lan mode to play online
1 parent 8d56baa commit 75618a0

File tree

8 files changed

+289
-9
lines changed

8 files changed

+289
-9
lines changed

EmperorHooks/EmperorHooks.vcxproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
<SubSystem>Windows</SubSystem>
6666
<GenerateDebugInformation>true</GenerateDebugInformation>
6767
<EnableUAC>false</EnableUAC>
68-
<AdditionalDependencies>$(SolutionDir)Detours-4.0.1\lib.X86\detours.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
68+
<AdditionalDependencies>$(SolutionDir)Detours-4.0.1\lib.X86\detours.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
6969
</Link>
7070
<PreBuildEvent>
7171
<Command>cd $(SolutionDir)Detours-4.0.1
@@ -91,7 +91,7 @@ nmake</Command>
9191
<OptimizeReferences>true</OptimizeReferences>
9292
<GenerateDebugInformation>true</GenerateDebugInformation>
9393
<EnableUAC>false</EnableUAC>
94-
<AdditionalDependencies>$(SolutionDir)Detours-4.0.1\lib.X86\detours.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
94+
<AdditionalDependencies>$(SolutionDir)Detours-4.0.1\lib.X86\detours.lib;winmm.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
9595
</Link>
9696
<PreBuildEvent>
9797
<Command>cd $(SolutionDir)Detours-4.0.1
@@ -106,6 +106,7 @@ nmake</Command>
106106
<ClInclude Include="DoCdCheck.hpp" />
107107
<ClInclude Include="PatchD3D7ResolutionLimit.hpp" />
108108
<ClInclude Include="PatchDebugLog.hpp" />
109+
<ClInclude Include="PatchLan.hpp" />
109110
<ClInclude Include="PatchSettings.hpp" />
110111
<ClInclude Include="pch.h" />
111112
</ItemGroup>
@@ -116,6 +117,7 @@ nmake</Command>
116117
<ClCompile Include="DoCdCheck.cpp" />
117118
<ClCompile Include="PatchD3D7ResolutionLimit.cpp" />
118119
<ClCompile Include="PatchDebugLog.cpp" />
120+
<ClCompile Include="PatchLan.cpp" />
119121
<ClCompile Include="PatchSettings.cpp" />
120122
<ClCompile Include="pch.cpp">
121123
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>

EmperorHooks/EmperorHooks.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
<ClInclude Include="PatchSettings.hpp">
3939
<Filter>Source Files</Filter>
4040
</ClInclude>
41+
<ClInclude Include="PatchLan.hpp">
42+
<Filter>Source Files</Filter>
43+
</ClInclude>
4144
</ItemGroup>
4245
<ItemGroup>
4346
<ClCompile Include="dllmain.cpp">
@@ -64,5 +67,8 @@
6467
<ClCompile Include="PatchSettings.cpp">
6568
<Filter>Source Files</Filter>
6669
</ClCompile>
70+
<ClCompile Include="PatchLan.cpp">
71+
<Filter>Source Files</Filter>
72+
</ClCompile>
6773
</ItemGroup>
6874
</Project>

EmperorHooks/GameExeImports.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ char* (__thiscall* ResourceManager_GetResourceStringOrig)(int* resourceManager,
88
size_t(__cdecl* setupSoundCdPathsOrig)(const char* driveLetterPath, int cdIndex) = (size_t(__cdecl*)(const char* driveLetterPath, int cdIndex))0x00477220;
99
char(__cdecl* doCdCheckOrig)(int cdIndex, char a2) = (char(__cdecl*)(int cdIndex, char a2))0x00496580;
1010
char(__cdecl* regSettingsOpenHkeyOrig)(char* fullPath, int createKey) = (char(__cdecl*)(char* fullPath, int createKey))0x0051E0E0;
11+
int(__thiscall* SomeNetworkManager_SendLobbyMessageOrig)(SomeNetworkManager* This, __int16 a2, int a3, wchar_t* message, char a5) = (int(__thiscall *)(SomeNetworkManager * This, __int16 a2, int a3, wchar_t* message, char a5))0x004D96A0;
1112

1213
DWORD* dword_B7D098P = (DWORD*)0xB7D098;
1314

EmperorHooks/GameExeImports.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
#include <cstdint>
33
#include <Windows.h>
44

5+
struct SomeNetworkManager;
6+
57
extern void (*sub_477250)();
68
extern int (__thiscall* sub_5473C0)(DWORD* This, const char* a2, const char* a3);
79

810
extern char* (__thiscall *ResourceManager_GetResourceStringOrig)(int* This, const char* name);
911
extern size_t (__cdecl* setupSoundCdPathsOrig)(const char* driveLetterPath, int cdIndex);
1012
extern char (__cdecl* doCdCheckOrig)(int cdIndex, char a2);
1113
extern char (__cdecl* regSettingsOpenHkeyOrig)(char* fullPath, int createKey);
14+
extern int(__thiscall* SomeNetworkManager_SendLobbyMessageOrig)(SomeNetworkManager* This, __int16 a2, int a3, wchar_t* message, char a5);
1215

1316
extern DWORD* dword_B7D098P;
1417

EmperorHooks/PatchLan.cpp

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#include "pch.h"
2+
#include <winsock.h>
3+
#include <Windows.h>
4+
#include <string>
5+
#include <vector>
6+
#include <detours.h>
7+
#include "GameExeImports.hpp"
8+
9+
std::string sockaddrToString(const sockaddr_in* addr)
10+
{
11+
return "family: " + std::to_string(addr->sin_family) + " " + std::string(inet_ntoa(addr->sin_addr)) + ":" + std::to_string(ntohs(addr->sin_port));
12+
}
13+
14+
std::string sockaddrToString(const sockaddr* addr)
15+
{
16+
if (addr->sa_family != AF_INET)
17+
return "ERR: NOT IPV4";
18+
return sockaddrToString((const sockaddr_in*)addr);
19+
}
20+
21+
std::string socketToString(SOCKET s)
22+
{
23+
if (s == INVALID_SOCKET)
24+
return "INVALID_SOCKET";
25+
26+
sockaddr sa = {};
27+
int len = sizeof(sockaddr);
28+
29+
if (getsockname(s, &sa, &len))
30+
return std::string("getsockname error ") + std::to_string(WSAGetLastError());
31+
32+
int type = 0;
33+
int length = sizeof(int);
34+
getsockopt(s, SOL_SOCKET, SO_TYPE, (char*)&type, &length);
35+
36+
return sockaddrToString(&sa) + " type: " + std::to_string(type);
37+
}
38+
39+
std::vector<std::pair<in_addr, sockaddr_in>> connectionsMapping;
40+
sockaddr_in serverAddress = {};
41+
42+
43+
enum class ConnectionType
44+
{
45+
Server,
46+
Client,
47+
};
48+
ConnectionType connectionType = ConnectionType::Server;
49+
50+
#define netprintf(...) printf(__VA_ARGS__)
51+
//#define netprintf(...) do {} while (false)
52+
53+
54+
int (PASCAL FAR* sendtoReal)(
55+
_In_ SOCKET s,
56+
_In_reads_bytes_(len) const char FAR* buf,
57+
_In_ int len,
58+
_In_ int flags,
59+
_In_reads_bytes_opt_(tolen) const struct sockaddr FAR* to,
60+
_In_ int tolen) = nullptr;
61+
62+
63+
int PASCAL FAR sendtoWrap(
64+
_In_ SOCKET s,
65+
_In_reads_bytes_(len) const char FAR* buf,
66+
_In_ int len,
67+
_In_ int flags,
68+
_In_reads_bytes_opt_(tolen) const struct sockaddr FAR* to,
69+
_In_ int tolen)
70+
{
71+
netprintf("send packet to %s\n", sockaddrToString(to).c_str());
72+
netprintf(" SOCKET %s\n", socketToString(s).c_str());
73+
74+
75+
if (connectionType == ConnectionType::Server && to)
76+
{
77+
const sockaddr_in* addr = (const sockaddr_in*)to;
78+
auto it = std::find_if(connectionsMapping.begin(), connectionsMapping.end(), [&](const std::pair<in_addr, sockaddr_in>& pair) { return pair.first.S_un.S_addr == addr->sin_addr.S_un.S_addr; });
79+
if (it != connectionsMapping.end())
80+
{
81+
sockaddr_in* realAddr = &it->second;
82+
83+
netprintf(" REDIR to %s\n", sockaddrToString(realAddr).c_str());
84+
85+
int ret = sendtoReal(s, buf, len, flags, (struct sockaddr*)realAddr, sizeof(sockaddr_in));
86+
if (ret == SOCKET_ERROR)
87+
netprintf("socket error: %d\n", WSAGetLastError());
88+
89+
return ret;
90+
}
91+
}
92+
93+
if (connectionType == ConnectionType::Client)
94+
{
95+
netprintf(" CLIENT REDIR to %s\n", sockaddrToString(&serverAddress).c_str());
96+
97+
int ret = sendtoReal(s, buf, len, flags, (struct sockaddr*)&serverAddress, sizeof(sockaddr_in));
98+
if (ret == SOCKET_ERROR)
99+
netprintf("socket error: %d\n", WSAGetLastError());
100+
101+
return ret;
102+
}
103+
104+
105+
return sendtoReal(s, buf, len, flags, to, tolen);
106+
}
107+
108+
109+
int (PASCAL FAR* recvfromReal)(
110+
_In_ SOCKET s,
111+
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR* buf,
112+
_In_ int len,
113+
_In_ int flags,
114+
_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR* from,
115+
_Inout_opt_ int FAR* fromlen) = nullptr;
116+
117+
int PASCAL FAR recvfromWrap(
118+
_In_ SOCKET s,
119+
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR* buf,
120+
_In_ int len,
121+
_In_ int flags,
122+
_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR* from,
123+
_Inout_opt_ int FAR* fromlen)
124+
{
125+
int bytesReceived = recvfromReal(s, buf, len, flags, from, fromlen);
126+
netprintf("recv from real: %s\n", sockaddrToString(from).c_str());
127+
netprintf(" SOCKET %s\n", socketToString(s).c_str());
128+
129+
if (connectionType == ConnectionType::Server)
130+
{
131+
sockaddr_in* from_in = (sockaddr_in*)from;
132+
133+
auto it = std::find_if(connectionsMapping.begin(), connectionsMapping.end(), [&](const std::pair<in_addr, sockaddr_in>& pair)
134+
{
135+
return pair.second.sin_family == from_in->sin_family &&
136+
pair.second.sin_addr.S_un.S_addr == from_in->sin_addr.S_un.S_addr &&
137+
pair.second.sin_port == from_in->sin_port;
138+
});
139+
140+
if (it != connectionsMapping.end())
141+
{
142+
sockaddr_in* realAddr = &it->second;
143+
netprintf(" REDIR to fake: %s\n", sockaddrToString(realAddr).c_str());
144+
*from_in = *realAddr;
145+
}
146+
else if (bytesReceived != SOCKET_ERROR && bytesReceived == 128)
147+
{
148+
in_addr* ip = (in_addr*)(buf + 0x2C);
149+
150+
char ipString[32] = {};
151+
strcpy(ipString, inet_ntoa(*ip));
152+
153+
netprintf("MAGIC PACKET!!! ip: %s, real ip: %s\n", ipString, sockaddrToString(from_in).c_str());
154+
155+
connectionsMapping.emplace_back(*ip, *from_in);
156+
from_in->sin_addr = *ip;
157+
from_in->sin_port = htons(4927);
158+
}
159+
}
160+
161+
return bytesReceived;
162+
}
163+
164+
int (PASCAL FAR * bindReal)(
165+
_In_ SOCKET s,
166+
_In_reads_bytes_(namelen) const struct sockaddr FAR * addr,
167+
_In_ int namelen) = nullptr;
168+
169+
int PASCAL FAR bindWrap(
170+
_In_ SOCKET s,
171+
_In_reads_bytes_(namelen) const struct sockaddr FAR * addr,
172+
_In_ int namelen)
173+
{
174+
175+
netprintf("-------------------------------------------BIND %s\n", sockaddrToString(addr).c_str());
176+
int ret = bindReal(s, addr, namelen);
177+
return ret;
178+
}
179+
180+
181+
int (PASCAL FAR * setsockoptReal)(
182+
_In_ SOCKET s,
183+
_In_ int level,
184+
_In_ int optname,
185+
_In_reads_bytes_opt_(optlen) const char FAR * optval,
186+
_In_ int optlen) = nullptr;
187+
188+
int PASCAL FAR setsockoptWrap(
189+
_In_ SOCKET s,
190+
_In_ int level,
191+
_In_ int optname,
192+
_In_reads_bytes_opt_(optlen) const char FAR * optval,
193+
_In_ int optlen)
194+
{
195+
netprintf("setsockopt %d %d\n", level, optname);
196+
return setsockoptReal(s, level, optname, optval, optlen);
197+
}
198+
199+
// original is __thiscall, but we can't declare a thiscall func outside a class, so we fake it with fastcall and an unused edx param
200+
int __fastcall SomeNetworkManager_SendLobbyMessageWrap(SomeNetworkManager * This, DWORD edx, __int16 a2, int a3, wchar_t* message_, char a5)
201+
{
202+
printf("GOT CHAT MESSAGE %S\n", message_);
203+
204+
std::wstring commandReply;
205+
206+
std::wstring_view message(message_);
207+
if (message == L"/connect")
208+
{
209+
connectionType = ConnectionType::Client;
210+
connectionsMapping.clear();
211+
commandReply = L"connecting!";
212+
213+
serverAddress.sin_family = AF_INET;
214+
serverAddress.sin_addr.S_un.S_addr = inet_addr("86.246.78.252");
215+
serverAddress.sin_port = htons(4927);
216+
}
217+
else if (message == L"/disconnect")
218+
{
219+
connectionType = ConnectionType::Server;
220+
connectionsMapping.clear();
221+
commandReply = L"disconnected!";
222+
}
223+
224+
if (!commandReply.empty())
225+
return SomeNetworkManager_SendLobbyMessageOrig(This, a2, a3, (std::wstring(message_) + L": " + commandReply).data(), a5);
226+
227+
return SomeNetworkManager_SendLobbyMessageOrig(This, a2, a3, message_, a5);
228+
}
229+
230+
void patchLan()
231+
{
232+
HMODULE wsock = LoadLibraryA("wsock32.dll");
233+
234+
sendtoReal = (int (PASCAL FAR*)(
235+
_In_ SOCKET s,
236+
_In_reads_bytes_(len) const char FAR * buf,
237+
_In_ int len,
238+
_In_ int flags,
239+
_In_reads_bytes_opt_(tolen) const struct sockaddr FAR * to,
240+
_In_ int tolen)) GetProcAddress(wsock, "sendto");
241+
242+
recvfromReal = (int (PASCAL FAR*)(_In_ SOCKET s,
243+
_Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR* buf,
244+
_In_ int len,
245+
_In_ int flags,
246+
_Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR* from,
247+
_Inout_opt_ int FAR* fromlen)) GetProcAddress(wsock, "recvfrom");
248+
249+
bindReal = (int (PASCAL FAR*)(
250+
_In_ SOCKET s,
251+
_In_reads_bytes_(namelen) const struct sockaddr FAR * addr,
252+
_In_ int namelen)) GetProcAddress(wsock, "bind");
253+
254+
setsockoptReal = (int (PASCAL FAR*)(
255+
_In_ SOCKET s,
256+
_In_ int level,
257+
_In_ int optname,
258+
_In_reads_bytes_opt_(optlen) const char FAR * optval,
259+
_In_ int optlen)) GetProcAddress(wsock, "setsockopt");
260+
261+
262+
DetourAttach(&(PVOID&)sendtoReal, sendtoWrap);
263+
DetourAttach(&(PVOID&)recvfromReal, recvfromWrap);
264+
DetourAttach(&(PVOID&)bindReal, bindWrap);
265+
DetourAttach(&(PVOID&)setsockoptReal, setsockoptWrap);
266+
DetourAttach(&(PVOID&)SomeNetworkManager_SendLobbyMessageOrig, SomeNetworkManager_SendLobbyMessageWrap);
267+
}

EmperorHooks/PatchLan.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
void patchLan();

EmperorHooks/dllmain.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "DoCdCheck.hpp"
1414
#include "PatchDebugLog.hpp"
1515
#include "PatchSettings.hpp"
16+
#include "PatchLan.hpp"
1617

1718

1819
int (WINAPI* TrueShowCursor)(BOOL bShow) = ShowCursor;
@@ -257,6 +258,7 @@ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOI
257258
DetourAttach(&(PVOID&)regSettingsOpenHkeyOrig, regSettingsOpenHkeyPatched);
258259
HookD3D7();
259260
patchDebugLog();
261+
patchLan();
260262
DetourTransactionCommit();
261263

262264
patchD3D7ResolutionLimit();

EmperorLauncher/EmperorLauncher.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,25 +203,21 @@ void writeGraphicsSettings(int screenWidth, int screenHeight)
203203
RegCloseKey(key);
204204
}
205205

206-
void getMainMonitorDimensions(int& screenWidth, int& screenHeight)
206+
int getMainMonitorHeight()
207207
{
208208
const POINT ptZero = { 0, 0 };
209209
HMONITOR monitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
210210
MONITORINFO info;
211211
info.cbSize = sizeof(MONITORINFO);
212212
GetMonitorInfo(monitor, &info);
213-
screenWidth = info.rcMonitor.right - info.rcMonitor.left;
214-
screenHeight = info.rcMonitor.bottom - info.rcMonitor.top;
213+
return info.rcMonitor.bottom - info.rcMonitor.top;
215214
}
216215

217-
218216
int main(int argc, char** argv)
219217
{
220218
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
221219

222-
int screenWidth = 0;
223-
int screenHeight = 0;
224-
getMainMonitorDimensions(screenWidth, screenHeight);
220+
int screenHeight = getMainMonitorHeight();
225221

226222
// Lots of stuff in the game assumes a 4:3 screen ratio - I tried to patch this out but I couldn't figure it out for now :(
227223
int adjustedScreenWidth = int(float(screenHeight) * 4.0f / 3.0f);

0 commit comments

Comments
 (0)