Backport linkent for hlds (and listenserver) without rehlds

This commit is contained in:
s1lentq 2023-11-28 17:56:52 +07:00
parent 0118e05280
commit 83c9d6e177
13 changed files with 536 additions and 123 deletions

View File

@ -149,8 +149,7 @@ target_sources(metamod PRIVATE
"src/engine_api.cpp"
"src/game_support.cpp"
"src/h_export.cpp"
"src/linkent.cpp"
"src/linkgame.cpp"
"src/osdep_linkent_linux.cpp"
"src/log_meta.cpp"
"src/mdebug.cpp"
"src/mem_utils.cpp"
@ -174,6 +173,7 @@ target_sources(metamod PRIVATE
target_link_libraries(metamod PRIVATE
dl
pthread
$<$<CONFIG:COMPAT_GLIBC>:libc-2.11.1.so>
)

View File

@ -151,8 +151,6 @@
<ClCompile Include="..\src\engine_api.cpp" />
<ClCompile Include="..\src\game_support.cpp" />
<ClCompile Include="..\src\h_export.cpp" />
<ClCompile Include="..\src\linkent.cpp" />
<ClCompile Include="..\src\linkgame.cpp" />
<ClCompile Include="..\src\log_meta.cpp" />
<ClCompile Include="..\src\mdebug.cpp" />
<ClCompile Include="..\src\mem_utils.cpp" />
@ -164,6 +162,11 @@
<ClCompile Include="..\src\mplugin.cpp" />
<ClCompile Include="..\src\mreg.cpp" />
<ClCompile Include="..\src\mutil.cpp" />
<ClCompile Include="..\src\osdep_linkent_linux.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\src\osdep_linkent_win32.cpp" />
<ClCompile Include="..\src\precompiled.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

View File

@ -35,12 +35,6 @@
<ClCompile Include="..\src\h_export.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\linkent.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\linkgame.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\log_meta.cpp">
<Filter>src</Filter>
</ClCompile>
@ -98,6 +92,12 @@
<ClCompile Include="..\src\meta_rehlds_api.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\osdep_linkent_win32.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\src\osdep_linkent_linux.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\api_info.h">

View File

@ -16,6 +16,7 @@ void WINAPI GiveFnptrsToDll(enginefuncs_t* pengfuncsFromEngine, globalvars_t* pG
g_engine.funcs = &g_engfuncs;
g_engine.globals = pGlobals;
g_engfuncs = *pengfuncsFromEngine;
g_metamod_module.load(&g_engfuncs);
g_engine.sys_module.load(pengfuncsFromEngine);
g_engfuncs = *pengfuncsFromEngine;

View File

@ -1,26 +0,0 @@
#include "precompiled.h"
// Function to perform common code of LINK_ENTITY_TO_GAME, rather than
// duplicating the code in ~2000 expanded macros. Based on code from Jussi
// Kivilinna <kijuhe00@students.oamk.fi>.
void NOINLINE do_link_ent(ENTITY_FN* pfnEntity, int* missing, const char* entStr, entvars_t* pev)
{
if (*missing) {
META_DEBUG(9, "Skipping entity '%s'; was previously found missing", entStr);
return;
}
if (!*pfnEntity) {
META_DEBUG(9, "Looking up game entity '%s'", entStr);
*pfnEntity = (ENTITY_FN)g_GameDLL.sys_module.getsym(entStr);
}
if (!*pfnEntity) {
META_ERROR("Couldn't find game entity '%s' in game DLL '%s': %s", entStr, g_GameDLL.name, CSysModule::getloaderror());
*missing = 1;
return;
}
META_DEBUG(8, "Linking game entity '%s'", entStr);
(*pfnEntity)(pev);
}

View File

@ -1,19 +1,49 @@
// vi: set ts=4 sw=4 :
// vim: set tw=75 :
// linkent.h - export entities from mod "games" back to the HL engine
/*
* Copyright (c) 2001-2006 Will Day <willday@hpgx.net>
*
* This file is part of Metamod.
*
* Metamod is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* Metamod 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 Metamod; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#pragma once
// Initializes replacement code
bool meta_init_linkent_replacement(CSysModule *moduleMetamod, CSysModule *moduleGame);
// Comments from SDK dlls/util.h:
//! This is the glue that hooks .MAP entity class names to our CPP classes.
//! The _declspec forces them to be exported by name so we can do a lookup with GetProcAddress().
//! The function is used to intialize / allocate the object for the entity.
// Adapted from LINK_ENTITY_TO_FUNC in adminmod linkfunc.cpp.
typedef void (*ENTITY_FN)(entvars_t *);
// Function to perform common code of LINK_ENTITY_TO_GAME.
void do_link_ent(ENTITY_FN* pfnEntity, int* missing, const char* entStr, entvars_t* pev);
#define LINK_ENTITY_TO_GAME(entityName) \
C_DLLEXPORT void entityName(entvars_t *pev) { \
static ENTITY_FN pfnEntity = nullptr; \
static int missing = 0; \
do_link_ent(&pfnEntity, &missing, STRINGIZE(entityName, 0), pev); \
}
typedef void (*ENTITY_FN) (entvars_t *);

View File

@ -1,9 +0,0 @@
#include "precompiled.h"
// Entity list for gamedlls adapted from adminmod linkfunc.cpp.
// NOTE: This list of entities is automatically generated via the script
// "mklinkgame.sh". See the files in the directory "ents" for the actual
// list of entities.
LINK_ENTITY_TO_GAME(worldspawn);

View File

@ -18,6 +18,8 @@ option_t g_global_options[] =
gamedll_t g_GameDLL;
CSysModule g_metamod_module;
ALIGN16
meta_globals_t g_metaGlobals;
@ -229,10 +231,17 @@ void metamod_startup()
// Exit on failure here? Dunno...
}
meta_init_rehlds_api();
if (!g_meta_extdll.init(&g_engine.sys_module)) {
Sys_Error("Failure to init extension DLL; exiting...");
if (meta_init_rehlds_api()) {
if (!g_meta_extdll.init(&g_engine.sys_module)) {
Sys_Error("Failure to init extension DLL; exiting...");
}
} else {
// activate linkent-replacement after give_engfuncs so that if game dll is
// plugin too and uses same method we get combined export table of plugin
// and game dll
if (!meta_init_linkent_replacement(&g_metamod_module, &g_GameDLL.sys_module)) {
Sys_Error("dll: Couldn't load linkent replacement for game DLL");
}
}
// Allow for commands to metamod plugins at startup. Autoexec.cfg is

View File

@ -15,6 +15,9 @@
// cvar to contain version
extern cvar_t g_meta_version;
// metamod module handle
extern CSysModule g_metamod_module;
// Info about the game dll/mod.
struct gamedll_t
{

View File

@ -3,7 +3,7 @@
CExtDll g_meta_extdll;
CExtDll::CExtDll()
: m_hGameDLL(CSysModule::INVALID_HANDLE), m_dlls(nullptr), m_count(nullptr)
: m_hGameDLL(CSysModule::INVALID_HANDLE)
{
}
@ -17,10 +17,6 @@ void CExtDll::load()
return;
}
auto extdll = &m_dlls[(*m_count)++];
Q_memset(extdll, 0, sizeof(*extdll));
extdll->lDLLHandle = m_hGameDLL;
}
void CExtDll::unload()
@ -29,68 +25,13 @@ void CExtDll::unload()
g_RehldsFuncs->RemoveExtDll(m_hGameDLL);
return;
}
for (auto i = 0u; i < *m_count; i++)
{
if (m_dlls[i].lDLLHandle == m_hGameDLL)
{
--(*m_count);
if (*m_count != i)
{
Q_memmove(&m_dlls[i], &m_dlls[i + 1], (*m_count - i) * sizeof(m_dlls[0]));
i = *m_count;
}
Q_memset(&m_dlls[i], 0, sizeof(m_dlls[0]));
return;
}
}
}
bool CExtDll::init(CSysModule *module)
{
m_hGameDLL = g_GameDLL.sys_module.gethandle();
if (g_RehldsFuncs) {
return true;
}
m_dlls = (extensiondll_t *)module->getsym("g_rgextdll");
m_count = (size_t *)module->getsym("g_iextdllMac");
if (m_dlls && m_count) {
return true;
}
// HACK HACK MORE HACK
// HLDS SHIT
auto pos = module->find_string_push("SV_SaveGameComment");
if (!pos) {
if (!m_hGameDLL)
return false;
}
const char patterns[2][2][14] = {
{ "\x56\x33\xF6\x39\x2A\x2A\x2A\x2A\x2A\x7E\x2A\x53\x8B", "\x5B\xC3\x90\x90\xA1\x2A\x2A\x2A\x2A\x53\x56\x57\x33" },
{ "\x53\x8B\x2A\x2A\x2A\x2A\x2A\x57\xBF\x2A\x2A\x2A\x2A", "\x7E\x2A\x8B\x2A\x2A\x2A\x2A\x2A\xBE\x2A\x2A\x2A\x2A" },
};
for (auto i = 0u; i < ARRAYSIZE(patterns); i++)
{
auto addr = mem_find_pattern(pos - 33, 20, patterns[0][i], sizeof(patterns[0][i]) - 1);
if (!addr) {
continue;
}
m_count = *(size_t **)(addr + 5);
addr = mem_find_pattern(addr, 30, patterns[1][i], sizeof(patterns[1][i]) - 1);
if (!addr) {
return false;
}
m_dlls = *(extensiondll_t **)(addr + 9);
return true;
}
return false;
return true;
}

View File

@ -12,8 +12,6 @@ public:
private:
module_handle_t m_hGameDLL;
extensiondll_t *m_dlls;
size_t *m_count;
};
extern CExtDll g_meta_extdll;

View File

@ -0,0 +1,217 @@
/*
* Copyright (c) 2004-2006 Jussi Kivilinna
*
* This file is part of "Metamod All-Mod-Support"-patch for Metamod.
*
* Metamod is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* Metamod 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 Metamod; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
#define PAGE_SIZE 4096UL
#define PAGE_MASK (~(PAGE_SIZE - 1))
#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK)
//
// Linux code for dynamic linkents
// -- by Jussi Kivilinna
//
// opcode, e9, + sizeof pointer
#define BYTES_SIZE (1 + sizeof(void*))
typedef void *(*dlsym_func)(void *module, const char *funcname);
static void *gamedll_module_handle = 0;
static void *metamod_module_handle = 0;
// pointer to original dlsym
static dlsym_func dlsym_original;
// contains jmp to replacement_dlsym @dlsym_original
static unsigned char dlsym_new_bytes[BYTES_SIZE];
// contains original bytes of dlsym
static unsigned char dlsym_old_bytes[BYTES_SIZE];
// Mutex for our protection
static pthread_mutex_t mutex_replacement_dlsym = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
// constructs new jmp forwarder
inline void construct_jmp_instruction(void *x, void *place, void *target)
{
((unsigned char *)x)[0] = 0xe9;
*(unsigned long *)((char *)x + 1) = (unsigned long)target - ((unsigned long)place + 5);
}
// checks if pointer x points to jump forwarder
inline bool is_code_trampoline_jmp_opcode(void *x)
{
return (((unsigned char *)x)[0] == 0xff || ((unsigned char *)x)[1] == 0x25);
}
// extracts pointer from "jmp dword ptr[pointer]"
inline void *extract_function_pointer_from_trampoline_jmp(void *x)
{
return (**(void ***)((char *)(x) + 2));
}
//
// restores old dlsym
//
inline void restore_original_dlsym()
{
//Copy old dlsym bytes back
memcpy((void *)dlsym_original, dlsym_old_bytes, BYTES_SIZE);
}
//
// resets new dlsym
//
inline void reset_dlsym_hook()
{
//Copy new dlsym bytes back
memcpy((void *)dlsym_original, dlsym_new_bytes, BYTES_SIZE);
}
//
// Replacement dlsym function
//
static void *__replacement_dlsym(void *module, const char *funcname)
{
// these are needed in case dlsym calls dlsym, default one doesn't do
// it but some LD_PRELOADed library that hooks dlsym might actually
// do so.
static int is_original_restored = 0;
int was_original_restored = is_original_restored;
// Lock before modifing original dlsym
pthread_mutex_lock(&mutex_replacement_dlsym);
// restore old dlsym
if (!is_original_restored)
{
restore_original_dlsym();
is_original_restored = 1;
}
// check if we should hook this call
if (module != metamod_module_handle || !metamod_module_handle || !gamedll_module_handle)
{
//no metamod/gamedll module? should we remove hook now?
void *retval = dlsym_original(module, funcname);
if (metamod_module_handle && gamedll_module_handle)
{
if (!was_original_restored)
{
// reset dlsym hook
reset_dlsym_hook();
is_original_restored = 0;
}
}
else
{
// no metamod/gamedll module? should we remove hook now by not reseting it back?
}
// unlock
pthread_mutex_unlock(&mutex_replacement_dlsym);
return retval;
}
// dlsym on metamod module
void *func = dlsym_original(module, funcname);
if (!func)
{
// function not in metamod module, try gamedll
func = dlsym_original(gamedll_module_handle, funcname);
}
if (!was_original_restored)
{
// reset dlsym hook
reset_dlsym_hook();
is_original_restored = 0;
}
// unlock
pthread_mutex_unlock(&mutex_replacement_dlsym);
return func;
}
//
// Initialize
//
bool meta_init_linkent_replacement(CSysModule *moduleMetamod, CSysModule *moduleGame)
{
metamod_module_handle = moduleMetamod->gethandle();
gamedll_module_handle = moduleGame->gethandle();
// dlsym is already known to be pointing to valid function, we loaded gamedll using it earlier!
void *sym_ptr = (void *)&dlsym;
while (is_code_trampoline_jmp_opcode(sym_ptr))
{
sym_ptr = extract_function_pointer_from_trampoline_jmp(sym_ptr);
}
dlsym_original = (dlsym_func)sym_ptr;
// Backup old bytes of "dlsym" function
memcpy(dlsym_old_bytes, (void *)dlsym_original, BYTES_SIZE);
// Construct new bytes: "jmp offset[replacement_sendto] @ sendto_original"
construct_jmp_instruction((void *)&dlsym_new_bytes[0], (void *)dlsym_original, (void *)&__replacement_dlsym);
// Check if bytes overlap page border.
unsigned long start_of_page = PAGE_ALIGN((long)dlsym_original) - PAGE_SIZE;
unsigned long size_of_pages = 0;
if ((unsigned long)dlsym_original + BYTES_SIZE > PAGE_ALIGN((unsigned long)dlsym_original))
{
// bytes are located on two pages
size_of_pages = PAGE_SIZE * 2;
}
else
{
// bytes are located entirely on one page.
size_of_pages = PAGE_SIZE;
}
// Remove PROT_READ restriction
if (mprotect((void *)start_of_page, size_of_pages, PROT_READ | PROT_WRITE | PROT_EXEC))
{
META_ERROR("Couldn't initialize dynamic linkents, mprotect failed: %i. Exiting...", errno);
return false;
}
// Write our own jmp-forwarder on "dlsym"
reset_dlsym_hook();
// done
return true;
}

View File

@ -0,0 +1,246 @@
/*
* Copyright (c) 2004-2006 Jussi Kivilinna
*
* This file is part of "Metamod All-Mod-Support"-patch for Metamod.
*
* Metamod is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* Metamod 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 Metamod; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "precompiled.h"
//
// Win32 code for dynamic linkents
// -- by Jussi Kivilinna
//
//
// Reads metamod.dll and game.dll function export tables and combines theim to
// single table that replaces metamod.dll's original table.
//
typedef struct sort_names_s {
unsigned long name;
unsigned short nameOrdinal;
} sort_names_t;
// relative virtual address to virtual address
#define rva_to_va(base, rva) ((unsigned long)base + (unsigned long)rva)
// virtual address to relative virtual address
#define va_to_rva(base, va) ((unsigned long)va - (unsigned long)base)
//
// Checks module signatures and return ntheaders pointer for valid module
//
static IMAGE_NT_HEADERS *get_ntheaders(HMODULE module)
{
union {
unsigned long mem;
IMAGE_DOS_HEADER * dos;
IMAGE_NT_HEADERS * pe;
} mem;
//Check if valid dos header
mem.mem = (unsigned long)module;
if (IsBadReadPtr(mem.dos, sizeof(*mem.dos)) || mem.dos->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
//Get and check pe header
mem.mem = rva_to_va(module, mem.dos->e_lfanew);
if (IsBadReadPtr(mem.pe, sizeof(*mem.pe)) || mem.pe->Signature != IMAGE_NT_SIGNATURE)
return NULL;
return mem.pe;
}
//
// Returns export table for valid module
//
static IMAGE_EXPORT_DIRECTORY *get_export_table(HMODULE module)
{
union {
unsigned long mem;
void * pvoid;
IMAGE_DOS_HEADER * dos;
IMAGE_NT_HEADERS * pe;
IMAGE_EXPORT_DIRECTORY * export_dir;
} mem;
// Check module
mem.pe = get_ntheaders(module);
if (!mem.pe)
return NULL;
// Check for exports
if (!mem.pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
return NULL;
mem.mem = rva_to_va(module, mem.pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (IsBadReadPtr(mem.export_dir, sizeof(*mem.export_dir)))
return NULL;
return mem.export_dir;
}
//
// Sort function for qsort
//
static int sort_names_list(const sort_names_t * A, const sort_names_t * B)
{
const char *str_A = (const char *)A->name;
const char *str_B = (const char *)B->name;
return Q_strcmp(str_A, str_B);
}
//
// Combines meta_module and game_module export tables and replaces meta_module table with new one
//
static bool combine_module_export_tables(CSysModule *meta_module, CSysModule *game_module)
{
IMAGE_EXPORT_DIRECTORY * exportMM;
IMAGE_EXPORT_DIRECTORY * exportGame;
unsigned long newNumberOfFunctions;
unsigned long newNumberOfNames;
unsigned long * newFunctions;
unsigned long * newNames;
unsigned short * newNameOrdinals;
sort_names_t * newSort;
unsigned long i;
unsigned long u;
unsigned long funcCount;
unsigned long nameCount;
unsigned long listFix;
HMODULE moduleMeta = meta_module->gethandle();
HMODULE moduleGame = game_module->gethandle();
// Get export tables
exportMM = get_export_table(moduleMeta);
exportGame = get_export_table(moduleGame);
if (!exportMM || !exportGame)
{
META_ERROR("Couldn't initialize dynamic linkents, exportMM: %i, exportGame: %i. Exiting...", exportMM, exportGame);
return false;
}
// setup new export table
newNumberOfFunctions = exportMM->NumberOfFunctions + exportGame->NumberOfFunctions;
newNumberOfNames = exportMM->NumberOfNames + exportGame->NumberOfNames;
// alloc lists
*(void**)&newFunctions = calloc(1, newNumberOfFunctions * sizeof(*newFunctions));
*(void**)&newSort = calloc(1, newNumberOfNames * sizeof(*newSort));
// copy moduleMeta to new export
for (funcCount = 0; funcCount < exportMM->NumberOfFunctions; funcCount++)
newFunctions[funcCount] = rva_to_va(moduleMeta, ((unsigned long *)rva_to_va(moduleMeta, exportMM->AddressOfFunctions))[funcCount]);
for (nameCount = 0; nameCount < exportMM->NumberOfNames; nameCount++)
{
// fix name address
newSort[nameCount].name = rva_to_va(moduleMeta, ((unsigned long *)rva_to_va(moduleMeta, exportMM->AddressOfNames))[nameCount]);
// ordinal is index to function list
newSort[nameCount].nameOrdinal = ((unsigned short *)rva_to_va(moduleMeta, exportMM->AddressOfNameOrdinals))[nameCount];
}
// copy moduleGame to new export
for (i = 0; i < exportGame->NumberOfFunctions; i++)
newFunctions[funcCount + i] = rva_to_va(moduleGame, ((unsigned long *)rva_to_va(moduleGame, exportGame->AddressOfFunctions))[i]);
for (i = 0, listFix = 0; i < exportGame->NumberOfNames; i++)
{
const char *name = (const char *)rva_to_va(moduleGame, ((unsigned long *)rva_to_va(moduleGame, exportGame->AddressOfNames))[i]);
//Check if name already in the list
for (u = 0; u < nameCount; u++)
{
if (!Q_stricmp(name, (const char *)newSort[u].name))
{
listFix -= 1;
break;
}
}
if (u < nameCount) //already in the list.. skip
continue;
newSort[nameCount + i + listFix].name = (unsigned long)name;
newSort[nameCount + i + listFix].nameOrdinal = (unsigned short)funcCount + ((unsigned short *)rva_to_va(moduleGame, exportGame->AddressOfNameOrdinals))[i];
}
//set new number
newNumberOfNames = nameCount + i + listFix;
//sort names list
qsort(newSort, newNumberOfNames, sizeof(*newSort), (int(*)(const void*, const void*))&sort_names_list);
//make newNames and newNameOrdinals lists (VirtualAlloc so we dont waste heap memory to stuff that isn't freed)
*(void**)&newNames = VirtualAlloc(0, newNumberOfNames * sizeof(*newNames), MEM_COMMIT, PAGE_READWRITE);
*(void**)&newNameOrdinals = VirtualAlloc(0, newNumberOfNames * sizeof(*newNameOrdinals), MEM_COMMIT, PAGE_READWRITE);
for (i = 0; i < newNumberOfNames; i++)
{
newNames[i] = newSort[i].name;
newNameOrdinals[i] = newSort[i].nameOrdinal;
}
free(newSort);
// translate VAs to RVAs
for (i = 0; i < newNumberOfFunctions; i++)
newFunctions[i] = va_to_rva(moduleMeta, newFunctions[i]);
for (i = 0; i < newNumberOfNames; i++)
{
newNames[i] = va_to_rva(moduleMeta, newNames[i]);
newNameOrdinals[i] = (unsigned short)va_to_rva(moduleMeta, newNameOrdinals[i]);
}
DWORD OldProtect;
if (!VirtualProtect(exportMM, sizeof(*exportMM), PAGE_READWRITE, &OldProtect))
{
META_ERROR("Couldn't initialize dynamic linkents, VirtualProtect failed: %i. Exiting...", GetLastError());
return false;
}
exportMM->Base = 1;
exportMM->NumberOfFunctions = newNumberOfFunctions;
exportMM->NumberOfNames = newNumberOfNames;
*(unsigned long*)&(exportMM->AddressOfFunctions) = va_to_rva(moduleMeta, newFunctions);
*(unsigned long*)&(exportMM->AddressOfNames) = va_to_rva(moduleMeta, newNames);
*(unsigned long*)&(exportMM->AddressOfNameOrdinals) = va_to_rva(moduleMeta, newNameOrdinals);
VirtualProtect(exportMM, sizeof(*exportMM), OldProtect, &OldProtect);
return true;
}
bool meta_init_linkent_replacement(CSysModule *moduleMetamod, CSysModule *moduleGame)
{
return combine_module_export_tables(moduleMetamod, moduleGame);
}