Repository navigation
Expand file tree
/
Copy pathEngine.cpp
More file actions
346 lines (293 loc) · 11 KB
/
Copy pathEngine.cpp
File metadata and controls
346 lines (293 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/* Copyright (c) 2026 SeriousAlexej.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "Engine.h"
#include "Graphics/Shader.h"
static void (__stdcall* pglBlendEquation)(int) = NULL;
#define GL_BLEND_EQUATION_EXT 0x8009
#define GL_FUNC_ADD_EXT 0x8006
#define GL_FUNC_SUBTRACT_EXT 0x800A
#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B
#define GL_MIN_EXT 0x8007
#define GL_MAX_EXT 0x8008
static GfxBlendEquation ogl_GetBlendEquation()
{
int blend_equation = GL_FUNC_ADD_EXT;
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
void (**ppglGetIntegerv)(int, int*) = (void (**)(int, int*))(enginedll_base_address + 0x1F9E60);
(*ppglGetIntegerv)(GL_BLEND_EQUATION_EXT, &blend_equation);
switch (blend_equation)
{
case GL_FUNC_ADD_EXT:
return GFX_BLENDEQ_ADD;
case GL_FUNC_SUBTRACT_EXT:
return GFX_BLENDEQ_SUBTRACT;
case GL_FUNC_REVERSE_SUBTRACT_EXT:
return GFX_BLENDEQ_REVSUBTRACT;
case GL_MIN_EXT:
return GFX_BLENDEQ_MIN;
case GL_MAX_EXT:
return GFX_BLENDEQ_MAX;
default:
return GFX_BLENDEQ_ADD;
}
}
static void ogl_BlendEquation(GfxBlendEquation gfx_blend_equation)
{
if (!pglBlendEquation)
return;
int blend_equation = GL_FUNC_ADD_EXT;
switch (gfx_blend_equation)
{
case GFX_BLENDEQ_ADD:
blend_equation = GL_FUNC_ADD_EXT;
break;
case GFX_BLENDEQ_SUBTRACT:
blend_equation = GL_FUNC_SUBTRACT_EXT;
break;
case GFX_BLENDEQ_REVSUBTRACT:
blend_equation = GL_FUNC_REVERSE_SUBTRACT_EXT;
break;
case GFX_BLENDEQ_MIN:
blend_equation = GL_MIN_EXT;
break;
case GFX_BLENDEQ_MAX:
blend_equation = GL_MAX_EXT;
break;
default:
blend_equation = GL_FUNC_ADD_EXT;
break;
}
pglBlendEquation(blend_equation);
}
static GfxBlendEquation d3d_GetBlendEquation()
{
HRESULT hr;
DWORD blend_equation = D3DBLENDOP_ADD;
hr = _pGfx->gl_pd3dDevice->GetRenderState(D3DRS_BLENDOP, &blend_equation);
D3D_CHECKERROR(hr);
return static_cast<GfxBlendEquation>(blend_equation);
}
static void d3d_BlendEquation(GfxBlendEquation blend_equation)
{
HRESULT hr;
hr = _pGfx->gl_pd3dDevice->SetRenderState(D3DRS_BLENDOP, static_cast<DWORD>(blend_equation));
D3D_CHECKERROR(hr);
}
static GfxBlendEquation none_GetBlendEquation() { return GFX_BLENDEQ_ADD; }
static void none_BlendEquation(GfxBlendEquation) { NOTHING; }
extern GfxBlendEquation(*gfxGetBlendEquation)(void) = &none_GetBlendEquation;
extern void (*gfxBlendEquation)(GfxBlendEquation) = &none_BlendEquation;
static void InitContext_OGL_Extra(void)
{
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
PROC (__stdcall** ppwglGetProcAddress)(LPCSTR) = (PROC (__stdcall**)(LPCSTR))(enginedll_base_address + 0x1FA4D4);
pglBlendEquation = (void (__stdcall*)(int))(*ppwglGetProcAddress)("glBlendEquation");
void (*pReloadTextures)(void) = (void (*)(void))(enginedll_base_address + 0x61AF0);
pReloadTextures();
}
static void PatchInitContext_OGL()
{
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
const int InitContext_OGL_injection_address = enginedll_base_address + 0x58037;
UBYTE call_bytes[5] = { UBYTE(0xE8), UBYTE(0x0), UBYTE(0x0), UBYTE(0x0), UBYTE(0x0) };
const int InitContext_OGL_Extra_offset = reinterpret_cast<int>(&InitContext_OGL_Extra) - InitContext_OGL_injection_address - 5;
memcpy(call_bytes + 1, &InitContext_OGL_Extra_offset, sizeof(int));
DWORD old_page_mode;
if (::VirtualProtect(reinterpret_cast<void*>(InitContext_OGL_injection_address), sizeof(call_bytes), PAGE_EXECUTE_READWRITE, &old_page_mode) != 0)
{
memcpy(reinterpret_cast<void*>(InitContext_OGL_injection_address), call_bytes, sizeof(call_bytes));
DWORD dummy;
::VirtualProtect(reinterpret_cast<void*>(InitContext_OGL_injection_address), sizeof(call_bytes), old_page_mode, &dummy);
}
}
static void GFX_SetFunctionPointers_Extra(INDEX iAPI)
{
if (iAPI == GAT_OGL)
{
gfxGetBlendEquation = &ogl_GetBlendEquation;
gfxBlendEquation = &ogl_BlendEquation;
} else if (iAPI == GAT_D3D)
{
gfxGetBlendEquation = &d3d_GetBlendEquation;
gfxBlendEquation = &d3d_BlendEquation;
} else {
gfxGetBlendEquation = &none_GetBlendEquation;
gfxBlendEquation = &none_BlendEquation;
}
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
void (*pGFX_SetFunctionPointers)(INDEX) = (void (*)(INDEX))(enginedll_base_address + 0x5C0E0);
pGFX_SetFunctionPointers(iAPI);
}
static void PatchGFXFunctionRef(const int patch_addr)
{
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
const int injection_address = enginedll_base_address + patch_addr;
UBYTE call_bytes[5] = { UBYTE(0xE8), UBYTE(0x0), UBYTE(0x0), UBYTE(0x0), UBYTE(0x0) };
const int call_offset = reinterpret_cast<int>(&GFX_SetFunctionPointers_Extra) - injection_address - 5;
memcpy(call_bytes + 1, &call_offset, sizeof(int));
DWORD old_page_mode;
if (::VirtualProtect(reinterpret_cast<void*>(injection_address), sizeof(call_bytes), PAGE_EXECUTE_READWRITE, &old_page_mode) != 0)
{
memcpy(reinterpret_cast<void*>(injection_address), call_bytes, sizeof(call_bytes));
DWORD dummy;
::VirtualProtect(reinterpret_cast<void*>(injection_address), sizeof(call_bytes), old_page_mode, &dummy);
}
}
static void PatchGFX_SetFunctionPointers()
{
PatchGFXFunctionRef(0x6203C);
PatchGFXFunctionRef(0x621D7);
PatchGFXFunctionRef(0x659DD);
PatchGFXFunctionRef(0x65C2E);
PatchGFXFunctionRef(0x65E25);
PatchGFXFunctionRef(0x65FD7);
}
static void sehSE_InitEngine_(CTString_* strGameID)
{
SECAPI_TRY
PatchInitContext_OGL();
PatchGFX_SetFunctionPointers();
SE_InitEngine(*((CTString*)strGameID));
SECAPI_CATCH
}
void SE_InitEngine_(CTString_* strGameID)
{
STREAM_TRY
sehSE_InitEngine_(strGameID);
STREAM_CATCH
}
static void sehSE_InitEngineAtDirectory_(CTString_* strGameID, const CTFileName_* fnAppPath, const CTFileName_* fnExePath)
{
SECAPI_TRY
PatchInitContext_OGL();
PatchGFX_SetFunctionPointers();
_fnmApplicationPath = *((const CTFileName*)fnAppPath);
_fnmApplicationExe = *((const CTFileName*)fnExePath);
if (_strLogFile=="") {
_strLogFile = _fnmApplicationExe.FileName();
}
// skip routine that calculates _fnmApplicationPath and _fnmApplicationExe from GetModuleFileNameA
UBYTE jump_instruction[sizeof(int) + 1] = { UBYTE(0xE9), 0, 0, 0, 0 };
int offset = 0x191 - sizeof(jump_instruction);
memcpy(jump_instruction + 1, &offset, sizeof(int));
void* instruction_addr = reinterpret_cast<void*>(reinterpret_cast<int>(&SE_InitEngine) + 0x44);
UBYTE orig_instruction[sizeof(jump_instruction)] = { 0, 0, 0, 0, 0 };
memcpy(orig_instruction, instruction_addr, sizeof(orig_instruction));
DWORD old_page_mode, dummy;
if (::VirtualProtect(instruction_addr, sizeof(jump_instruction), PAGE_EXECUTE_READWRITE, &old_page_mode) == 0)
throw "Failed to init engine";
memcpy(instruction_addr, jump_instruction, sizeof(jump_instruction));
if (::VirtualProtect(instruction_addr, sizeof(jump_instruction), old_page_mode, &dummy) == 0)
throw "Failed to init engine";
SE_InitEngine(*((CTString*)strGameID));
if (::VirtualProtect(instruction_addr, sizeof(orig_instruction), PAGE_EXECUTE_READWRITE, &old_page_mode) == 0)
throw "Failed to init engine";
memcpy(instruction_addr, orig_instruction, sizeof(orig_instruction));
if (::VirtualProtect(instruction_addr, sizeof(orig_instruction), old_page_mode, &dummy) == 0)
throw "Failed to init engine";
SECAPI_CATCH
}
void SE_InitEngineAtDirectory_(CTString_* strGameID, const CTFileName_* fnAppPath, const CTFileName_* fnExePath)
{
STREAM_TRY
sehSE_InitEngineAtDirectory_(strGameID, fnAppPath, fnExePath);
STREAM_CATCH
}
static void sehSE_EndEngine_()
{
SECAPI_TRY
SE_EndEngine();
SECAPI_CATCH
}
void SE_EndEngine_()
{
STREAM_TRY
sehSE_EndEngine_();
STREAM_CATCH
}
static void sehSE_LoadDefaultFonts_()
{
SECAPI_TRY
SE_LoadDefaultFonts();
SECAPI_CATCH
}
void SE_LoadDefaultFonts_()
{
STREAM_TRY
sehSE_LoadDefaultFonts_();
STREAM_CATCH
}
void SE_UpdateWindowHandle_(HWND hwndWindowed)
{
SECAPI_TRY
SE_UpdateWindowHandle(hwndWindowed);
SECAPI_CATCH
}
static void sehSE_PretouchIfNeeded_()
{
SECAPI_TRY
SE_PretouchIfNeeded();
SECAPI_CATCH
}
void SE_PretouchIfNeeded_()
{
STREAM_TRY
sehSE_PretouchIfNeeded_();
STREAM_CATCH
}
static bool g_replaceFilePatched = false;
static void PatchReplaceFile()
{
if (g_replaceFilePatched)
return;
g_replaceFilePatched = true;
const char this_dll_name[] = "SeriousEngineCAPI.dll";
const int name_len = strlen(this_dll_name) + 1;
const int enginedll_base_address = reinterpret_cast<int>(&CEntity::HandleSentEvents) - 0xFEC80;
const int engine_gui_name_addr = enginedll_base_address + 0x1DB318; // data with "EngineGUI.dll" string
DWORD old_page_mode;
if (::VirtualProtect(reinterpret_cast<void*>(engine_gui_name_addr), name_len, PAGE_READWRITE, &old_page_mode) != 0)
{
// "EngineGUI.dll" string is actually written 2 times, so its okay to overwrite over length of one of them
// as long as "SeriousEngineCAPI.dll0" (len is 22) is shorter than "EngineGUI.dll000EngineGUI.dll0" (len is 30)
// second string is used for error handling printing so its not important
memcpy(reinterpret_cast<void*>(engine_gui_name_addr), this_dll_name, name_len);
DWORD dummy;
::VirtualProtect(reinterpret_cast<void*>(engine_gui_name_addr), name_len, old_page_mode, &dummy);
}
}
static CTFileName_* (*pFileRequesterCallback)(char* title, char* filters, char* registry, char* selectedByDefault) = NULL;
void SetFileRequesterCallback(CTFileName_* (*c)(char* title, char* filters, char* registry, char* selectedByDefault))
{
pFileRequesterCallback = c;
PatchReplaceFile();
}
// not "extern C" to keep weird C++
// mangled name which should be "?FileRequester@@YA?AVCTFileName@@PAD000@Z"
// vanilla Engine.dll looks for it in CallFileRequester func at ReplaceFile.cpp : 70
// following pragma makes sure the symbol is mangled as expected by Engine
#pragma comment(linker, "/export:CAPIFileRequester=?FileRequester@@YA?AVCTFileName@@PAD000@Z")
SECAPI CTFileName FileRequester(
char* pchrTitle,
char* pchrFilters,
char* pchrRegistry,
char* pchrFileSelectedByDefault)
{
if (pFileRequesterCallback)
{
CTFileName* res_tmp = (CTFileName*)pFileRequesterCallback(pchrTitle, pchrFilters, pchrRegistry, pchrFileSelectedByDefault);
CTFileName res = *res_tmp;
delete res_tmp;
return res;
}
return CTString("");
}