-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnamefunc.cpp
355 lines (313 loc) · 13.5 KB
/
namefunc.cpp
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
347
348
349
350
351
352
353
354
355
/*
* RACC - AI development project for first-person shooter games derivated
* from Valve's Half-Life
* (http://racc.bots-united.com)
*
* Rational Autonomous Cybernetic Commandos AI
*
* Copyright (c) 2004, Pierre-Marie Baty
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the RACC nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
//
// namefunc.cpp
//
// Since there is a known issue on Windows platforms that prevent
// hook DLLs (such as our bot DLL) to be used in single player
// games (Training Room) because they don't export all the stuff
// they should, we may need to build our own array of exported
// symbols from the actual game DLL in order to use it as such
// if necessary.
//
#include "main.h"
#ifdef WINDOWSNT
// file-related structure definitions
typedef struct tagDOS_HEADER {
WORD e_magic; // magic number
WORD e_cblp; // bytes on last page of file
WORD e_cp; // pages in file
WORD e_crlc; // relocations
WORD e_cparhdr; // size of header in paragraphs
WORD e_minalloc; // minimum extra paragraphs needed
WORD e_maxalloc; // maximum extra paragraphs needed
WORD e_ss; // initial (relative) SS value
WORD e_sp; // initial SP value
WORD e_csum; // checksum
WORD e_ip; // initial IP value
WORD e_cs; // initial (relative) CS value
WORD e_lfarlc; // file address of relocation table
WORD e_ovno; // overlay number
WORD e_res[4]; // reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // reserved words
LONG e_lfanew; // file address of new exe header
} DOS_HEADER, *P_DOS_HEADER; // DOS .EXE header
typedef struct tagPE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} PE_HEADER, *P_PE_HEADER;
typedef struct tagSECTION_HEADER {
BYTE Name[8];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} SECTION_HEADER, *P_SECTION_HEADER;
typedef struct tagDATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} DATA_DIRECTORY, *P_DATA_DIRECTORY;
typedef struct tagOPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD W32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
DATA_DIRECTORY DataDirectory[16];
} OPTIONAL_HEADER, *P_OPTIONAL_HEADER;
typedef struct tagEXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} EXPORT_DIRECTORY, *P_EXPORT_DIRECTORY;
WORD *p_Ordinals = NULL;
DWORD *p_Functions = NULL;
DWORD *p_Names = NULL;
char **p_FunctionNames = NULL;
int num_ordinals = 0;
unsigned long base_offset;
void FreeNameFuncGlobals(void)
{
// free our table of exported symbols (only on Windows platforms)
if (p_Ordinals)
free(p_Ordinals);
p_Ordinals = NULL;
if (p_Functions)
free(p_Functions);
p_Functions = NULL;
if (p_Names)
free(p_Names);
p_Names = NULL;
if (p_FunctionNames) {
for (int index = 0; index < num_ordinals; index++) {
if (p_FunctionNames[index])
free(p_FunctionNames[index]);
p_FunctionNames[index] = NULL;
}
free(p_FunctionNames);
}
p_FunctionNames = NULL;
}
/**
* The purpose of this function is to perfect the bot DLL interfacing. Having all the
* MOD entities listed and linked to their proper function with LINK_ENTITY_TO_FUNC is
* not enough, procs are missing, and that's the reason why most bot DLLs don't allow
* to run single player games. This function loads the symbols in the game DLL by hand,
* strips their MSVC-style case mangling, and builds an exports array which supercedes
* the one the engine would get afterwards from the MOD DLL, which can't pass through
* the bot DLL. This way we are sure that *nothing is missing* in the interfacing. Note
* this is a fix for Windows systems only. But since UNIX systems only host dedicated
* servers, there's no need to run single-player games on them.
*/
void LoadSymbols(const char *filename)
{
FILE *fp;
DOS_HEADER dos_header;
LONG nt_signature;
PE_HEADER pe_header;
SECTION_HEADER section_header;
OPTIONAL_HEADER optional_header;
LONG edata_offset;
LONG edata_delta;
EXPORT_DIRECTORY export_directory;
LONG name_offset;
LONG ordinal_offset;
LONG function_offset;
char function_name[256], ch;
int i, j;
void *game_GiveFnptrsToDll;
// open MOD DLL file in binary read mode
fp = fopen(filename, "rb"); // can't fail to do this, since we LoadLibrary()'ed it before
fread(&dos_header, sizeof(dos_header), 1, fp); // get the DOS header
fseek(fp, dos_header.e_lfanew, SEEK_SET);
fread(&nt_signature, sizeof(nt_signature), 1, fp); // get the NT signature
fread(&pe_header, sizeof(pe_header), 1, fp); // get the PE header
fread(&optional_header, sizeof(optional_header), 1, fp); // get the optional header
edata_offset = optional_header.DataDirectory[0].VirtualAddress; // no edata by default
edata_delta = 0;
// cycle through all sections of the PE header to look for edata
for (i = 0; i < pe_header.NumberOfSections; i++) {
if (strcmp((char *)section_header.Name, ".edata") == 0) {
edata_offset = section_header.PointerToRawData; // if found, save its offset
edata_delta = section_header.VirtualAddress - section_header.PointerToRawData;
}
}
fseek(fp, edata_offset, SEEK_SET);
fread(&export_directory, sizeof(export_directory), 1, fp); // get the export directory
num_ordinals = export_directory.NumberOfNames; // save number of ordinals
ordinal_offset = export_directory.AddressOfNameOrdinals - edata_delta; // save ordinals offset
fseek(fp, ordinal_offset, SEEK_SET);
p_Ordinals = (WORD *)malloc(num_ordinals * sizeof(WORD)); // allocate space for ordinals
fread(p_Ordinals, num_ordinals * sizeof(WORD), 1, fp); // get the list of ordinals
function_offset = export_directory.AddressOfFunctions - edata_delta; // save functions offset
fseek(fp, function_offset, SEEK_SET);
p_Functions = (DWORD *)malloc(num_ordinals * sizeof(DWORD)); // allocate space for functions
fread(p_Functions, num_ordinals * sizeof(DWORD), 1, fp); // get the list of functions
name_offset = export_directory.AddressOfNames - edata_delta; // save names offset
fseek(fp, name_offset, SEEK_SET);
p_Names = (DWORD *)malloc(num_ordinals * sizeof(DWORD)); // allocate space for names
fread(p_Names, num_ordinals * sizeof(DWORD), 1, fp); // get the list of names
p_FunctionNames = (char **)malloc(sizeof(char *) * num_ordinals);
// reset function names array first
for (i = 0; i < num_ordinals; i++)
p_FunctionNames[i] = NULL;
// cycle through all function names and fill in the exports array
for (i = 0; i < num_ordinals; i++) {
if (fseek(fp, p_Names[i] - edata_delta, SEEK_SET) != -1) {
j = 0; // start at beginning of string
// while end of file is not reached
while ((ch = fgetc (fp)) != EOF) {
function_name[j] = ch; // store what is read in the name variable
if (ch == 0)
break; // return the name with the trailing \0
j++;
}
// allocate space
p_FunctionNames[i] = (char *)malloc(strlen(function_name) + 1);
// is this a MSVC mangled name?
if (function_name[0] == '?') {
j = 1; // skip the leading '?'
// while the first @@ is not reached
while (!((function_name[j] == '@') && (function_name[j + 1] == '@'))) {
p_FunctionNames[i][j - 1] = function_name[j]; // store what is read in the name variable
if (function_name[j + 1] == 0)
break; // return the name
j++;
}
p_FunctionNames[i][j] = 0; // terminate string at the "@@"
}
else // else no change needed
strcpy (p_FunctionNames[i], function_name);
}
}
fclose (fp); // close MOD DLL file
// cycle through all function names to find the GiveFnptrsToDll function
for (i = 0; i < num_ordinals; i++) {
if (strcmp("GiveFnptrsToDll", p_FunctionNames[i]) == 0) {
game_GiveFnptrsToDll = (void *)GetProcAddress(h_Library, "GiveFnptrsToDll");
base_offset = (unsigned long)(game_GiveFnptrsToDll) - p_Functions[p_Ordinals[i]];
break; // base offset has been saved
}
}
}
/**
* This function returns the address of a certain function in the exports array. We don't call
* the engine function for this, because since our bot DLL doesn't exports ALL the functions
* the game DLL is exporting (functions beginning by "@@" are missing), we had to build our
* own exports array by reading the game DLL file to complete the one of our bot DLL. That's
* the purpose of the LoadSymbols() function, which is called as soon as LoadLibrary() is
* called in GameDLLInit(). Note this fix is only enabled on Windows systems, since UNIXes
* don't need it (only hosting dedicated servers and thus never single player games).
*/
uint32 FunctionFromName(const char *pName)
{
// cycle through our exports array and find the entry containing the function name we want
for (int index = 0; index < num_ordinals; index++)
if (strcmp(pName, p_FunctionNames[index]) == 0)
return (p_Functions[p_Ordinals[index]] + base_offset); // return the address of that function
// either no custom exports or couldn't find entry, fallback on asking the engine for it
return g_engfuncs.pfnFunctionFromName(pName);
}
/**
* This function returns the name of the function at a certain address in the exports array.
* We don't call the engine function for this, because since our bot DLL doesn't exports ALL
* the functions the game DLL is exporting (functions beginning by "@@" are missing), we had
* to build our own exports array by reading the game DLL file to complete the one of our bot
* DLL. That's the purpose of the LoadSymbols() function, which is called as soon as
* LoadLibrary() is called in GameDLLInit(). Note this fix is only enabled on Windows systems,
* since UNIXes don't need it (only hosting dedicated servers and thus never single player
* games).
*/
const char *NameForFunction(uint32 function)
{
unsigned long address_to_look_for = function - base_offset;
// cycle through our exports array and stop at the function offset index we want
for (int index = 0; index < num_ordinals; index++)
if (address_to_look_for == p_Functions[p_Ordinals[index]])
return p_FunctionNames[index]; // return the name of that function
// either no custom exports or couldn't find entry, fallback on asking the engine for it
return g_engfuncs.pfnNameForFunction(function);
}
#endif