1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2025-02-21 14:54:14 +01:00

Merge loader, loader2. Rename env var HL2SDKS2 -> HL2SDKSOURCE2.

This commit is contained in:
Nicholas Hastings 2015-07-09 14:09:56 -04:00
parent 4b75bcb431
commit 30b7be46a8
16 changed files with 422 additions and 1713 deletions

View File

@ -38,7 +38,7 @@ PossibleSDKs = {
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'),
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'),
'source2': SDK('HL2SDKS2', '2.s2', '22', 'SOURCE2', WinOnly, 'source2'),
'source2': SDK('HL2SDKSOURCE2', '2.s2', '22', 'SOURCE2', WinOnly, 'source2'),
}
def ResolveEnvPath(env, folder):
@ -250,7 +250,7 @@ class MMSConfig(object):
compiler.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(context.currentSourcePath, 'sourcehook'),
os.path.join(context.sourcePath, 'public', 'loader'),
os.path.join(context.sourcePath, 'loader'),
]
defines = ['SE_' + self.sdks[i].define + '=' + self.sdks[i].code for i in self.sdks]
@ -395,8 +395,7 @@ if MMS.use_auto_versioning():
)
BuildScripts = [
# 'loader/AMBuilder',
'loader2/AMBuilder',
'loader/AMBuilder',
'core-legacy/AMBuilder',
'core/AMBuilder',
]

View File

@ -405,7 +405,11 @@ void BaseProvider::GetGamePath(char *pszBuffer, int len)
const char *BaseProvider::GetGameDescription()
{
#if SOURCE_ENGINE == SE_SOURCE2
return serverconfig->GetGameDescription();
#else
return server->GetGameDescription();
#endif
}
int BaseProvider::DetermineSourceEngine()

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2009 AlliedModders LLC and authors.
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
@ -36,6 +36,7 @@
#include "gamedll.h"
class IServerGameDLL;
class ISource2ServerConfig;
#define MAX_GAMEDLL_PATHS 10
@ -47,10 +48,13 @@ static void *gamedll_libs[MAX_GAMEDLL_PATHS];
static unsigned int gamedll_path_count = 0;
static void *gamedll_lib = NULL;
static IServerGameDLL *gamedll_iface = NULL;
static ISource2ServerConfig *config_iface = NULL;
static QueryValveInterface gamedll_qvi = NULL;
static int gamedll_version = 0;
static int isgd_shutdown_index = -1;
static int is2sc_allowdedi_index = 20;
static char mm_path[PLATFORM_MAX_PATH];
static bool g_is_source2 = false;
#if defined _WIN32
#define SERVER_NAME "server.dll"
@ -78,7 +82,7 @@ mm_DetectGameInformation()
return false;
}
if (!mm_ResolvePath(game_name, game_path, sizeof(game_path)))
if (!mm_ResolvePath(game_name, game_path, sizeof(game_path), g_is_source2))
{
mm_LogFatal("Could not resolve path: %s", game_name);
return false;
@ -87,11 +91,21 @@ mm_DetectGameInformation()
FILE *fp;
char gameinfo_path[PLATFORM_MAX_PATH];
bool is_source2 = false;
mm_PathFormat(gameinfo_path, sizeof(gameinfo_path), "%s/gameinfo.txt", game_path);
if ((fp = fopen(gameinfo_path, "rt")) == NULL)
{
mm_LogFatal("Could not read file: %s", gameinfo_path);
return false;
// Try Source2 gameinfo
mm_PathFormat(gameinfo_path, sizeof(gameinfo_path), "%s/gameinfo.gi", game_path);
if ((fp = fopen(gameinfo_path, "rt")) == NULL)
{
mm_LogFatal("Could not read file: %s", gameinfo_path);
return false;
}
else
{
is_source2 = true;
}
}
char temp_path[PLATFORM_MAX_PATH];
@ -131,12 +145,14 @@ mm_DetectGameInformation()
lptr = cur_path;
}
char *pRelPath = is_source2 ? "../../" : "";
char *pOSDir = is_source2 ? "win32/" : "";
if (stricmp(key, "GameBin") == 0)
mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s/" SERVER_NAME, lptr, ptr);
mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s%s/%s" SERVER_NAME, lptr, pRelPath, ptr, pOSDir);
else if (!ptr[0])
mm_PathFormat(temp_path, sizeof(temp_path), "%s/bin/" SERVER_NAME, lptr);
mm_PathFormat(temp_path, sizeof(temp_path), "%s/%sbin/%s" SERVER_NAME, lptr, pRelPath, pOSDir);
else
mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s/bin/" SERVER_NAME, lptr, ptr);
mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s%s/bin/%s" SERVER_NAME, lptr, pRelPath, ptr, pOSDir);
if (mm_PathCmp(mm_path, temp_path))
continue;
@ -174,7 +190,7 @@ mm_DetectGameInformation()
if (gamedll_path_count == 0)
{
mm_LogFatal("Could not detect any valid game paths in gameinfo.txt");
mm_LogFatal("Could not detect any valid game paths in gameinfo file");
return false;
}
@ -198,13 +214,215 @@ mm_PatchDllInit(bool patch);
static void
mm_PatchDllShutdown();
static void
mm_PatchAllowDedicated(bool patch);
static void
mm_PatchConnect(bool patch);
static void *isgd_orig_init = NULL;
static void *isgd_orig_shutdown = NULL;
static void *is2sc_orig_allowdedi = NULL;
static void *is2sc_orig_connect = NULL;
class VEmptyClass
{
};
gamedll_bridge_info g_bridge_info;
// Source2 - Rough start order
// CreateInterfaceFn (IS2SC) - hook Connect and AllowDedicatedServer
// IS2SC::Connect - save factory pointer. return orig. remove hook.
// IS2SC::AllowDedicatedServer - return true. remove hook.
// CreateInterfaceFn (IS2S) - hook Init and Shutdown
// IS2S::Init - do same as old ISGD::DLLInit, including core load. return orig. remove hook.
// IS2S::Shutdown - <-- this
enum InitReturnVal_t
{
INIT_FAILED = 0,
INIT_OK,
INIT_LAST_VAL,
};
class ISource2ServerConfig
{
public:
virtual bool Connect(QueryValveInterface factory)
{
g_bridge_info.engineFactory = factory;
g_bridge_info.fsFactory = factory;
g_bridge_info.physicsFactory = factory;
/* Call the original */
bool result;
{
union
{
bool(VEmptyClass::*mfpnew)(QueryValveInterface factory);
#if defined _WIN32
void *addr;
} u;
u.addr = is2sc_orig_connect;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = is2sc_orig_connect;
u.s.adjustor = 0;
#endif
result = (((VEmptyClass *) config_iface)->*u.mfpnew)(factory);
}
mm_PatchConnect(false);
return result;
}
virtual bool AllowDedicatedServers(int universe) const
{
mm_PatchAllowDedicated(false);
return true;
}
};
class ISource2Server
{
public:
virtual bool Connect(QueryValveInterface factory) { return true; }
virtual void Disconnect() {}
virtual void *QueryInterface(const char *pInterfaceName) { return nullptr; }
virtual InitReturnVal_t Init()
{
char error[255];
if (mm_backend == MMBackend_UNKNOWN)
{
mm_LogFatal("Could not detect engine version");
}
else
{
if (!mm_LoadMetamodLibrary(mm_backend, error, sizeof(error)))
{
mm_LogFatal("Detected engine %d but could not load: %s", mm_backend, error);
}
else
{
typedef IGameDllBridge *(*GetGameDllBridge)();
GetGameDllBridge get_bridge = (GetGameDllBridge)mm_GetProcAddress("GetGameDllBridge");
if (get_bridge == NULL)
{
mm_UnloadMetamodLibrary();
mm_LogFatal("Detected engine %d but could not find GetGameDllBridge callback", mm_backend);
}
else
{
gamedll_bridge = get_bridge();
}
}
}
if (gamedll_bridge)
{
g_bridge_info.pGlobals = nullptr;// pGlobals;
g_bridge_info.dllVersion = gamedll_version;
g_bridge_info.isgd = gamedll_iface;
g_bridge_info.gsFactory = gamedll_qvi;
g_bridge_info.vsp_listener_path = mm_path;
strcpy(error, "Unknown error");
if (!gamedll_bridge->DLLInit_Pre(&g_bridge_info, error, sizeof(error)))
{
gamedll_bridge = NULL;
mm_UnloadMetamodLibrary();
mm_LogFatal("Unknown error loading Metamod for engine %d: %s", mm_backend, error);
}
}
/* Call the original */
InitReturnVal_t result;
{
union
{
InitReturnVal_t(VEmptyClass::*mfpnew)();
#if defined _WIN32
void *addr;
} u;
u.addr = isgd_orig_init;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = isgd_orig_init;
u.s.adjustor = 0;
#endif
result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)();
}
/**
* :TODO: possible logic hole here, what happens if the gamedll REALLY returns false?
* I'm pretty sure we'll die horribly.
*/
if (!result)
{
gamedll_bridge->Unload();
mm_UnloadMetamodLibrary();
gamedll_bridge = NULL;
}
else if (gamedll_bridge != NULL)
{
gamedll_bridge->DLLInit_Post(&isgd_shutdown_index);
assert(isgd_shutdown_index != -1);
mm_PatchDllShutdown();
}
mm_PatchDllInit(false);
return result;
}
virtual void Shutdown()
{
gamedll_bridge->Unload();
gamedll_bridge = NULL;
mm_UnloadMetamodLibrary();
/* Call original function */
{
union
{
void (VEmptyClass::*mfpnew)();
#if defined _WIN32
void *addr;
} u;
u.addr = isgd_orig_shutdown;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = isgd_orig_shutdown;
u.s.adjustor = 0;
#endif
(((VEmptyClass *)gamedll_iface)->*u.mfpnew)();
}
mm_UnloadLibrary(gamedll_lib);
gamedll_lib = NULL;
}
};
class IServerGameDLL
{
public:
@ -244,19 +462,17 @@ public:
if (gamedll_bridge)
{
gamedll_bridge_info info;
info.engineFactory = (QueryValveInterface)engineFactory;
info.physicsFactory = (QueryValveInterface)physicsFactory;
info.fsFactory = (QueryValveInterface)fileSystemFactory;
info.pGlobals = pGlobals;
info.dllVersion = gamedll_version;
info.isgd = gamedll_iface;
info.gsFactory = gamedll_qvi;
info.vsp_listener_path = mm_path;
g_bridge_info.engineFactory = (QueryValveInterface)engineFactory;
g_bridge_info.physicsFactory = (QueryValveInterface)physicsFactory;
g_bridge_info.fsFactory = (QueryValveInterface)fileSystemFactory;
g_bridge_info.pGlobals = pGlobals;
g_bridge_info.dllVersion = gamedll_version;
g_bridge_info.isgd = gamedll_iface;
g_bridge_info.gsFactory = gamedll_qvi;
g_bridge_info.vsp_listener_path = mm_path;
strcpy(error, "Unknown error");
if (!gamedll_bridge->DLLInit_Pre(&info, error, sizeof(error)))
if (!gamedll_bridge->DLLInit_Pre(&g_bridge_info, error, sizeof(error)))
{
gamedll_bridge = NULL;
mm_UnloadMetamodLibrary();
@ -350,6 +566,8 @@ public:
};
static IServerGameDLL isgd_thunk;
static ISource2Server is2s_thunk;
static ISource2ServerConfig is2sc_thunk;
static void
mm_PatchDllInit(bool patch)
@ -358,13 +576,27 @@ mm_PatchDllInit(bool patch)
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&IServerGameDLL::DLLInit, mfp);
if (g_is_source2)
{
SourceHook::GetFuncInfo(&ISource2Server::Init, mfp);
}
else
{
SourceHook::GetFuncInfo(&IServerGameDLL::DLLInit, mfp);
}
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **)*(void **)&isgd_thunk;
if (g_is_source2)
{
vtable_src = (void **)*(void **)&is2s_thunk;
}
else
{
vtable_src = (void **)*(void **)&isgd_thunk;
}
vtable_dest = (void **)*(void **)gamedll_iface;
SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex],
@ -393,12 +625,26 @@ mm_PatchDllShutdown()
SourceHook::MemFuncInfo mfp;
mfp.isVirtual = false;
SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfp);
if (g_is_source2)
{
SourceHook::GetFuncInfo(&ISource2Server::Shutdown, mfp);
}
else
{
SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfp);
}
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **)*(void **)&isgd_thunk;
if (g_is_source2)
{
vtable_src = (void **)*(void **)&is2s_thunk;
}
else
{
vtable_src = (void **)*(void **)&isgd_thunk;
}
vtable_dest = (void **)*(void **)gamedll_iface;
isgd_orig_shutdown = vtable_dest[isgd_shutdown_index];
@ -406,20 +652,138 @@ mm_PatchDllShutdown()
}
static void
mm_PrepForGameLoad()
mm_PatchAllowDedicated(bool patch)
{
mm_PatchDllInit(true);
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&ISource2ServerConfig::AllowDedicatedServers, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **) *(void **) &is2sc_thunk;
vtable_dest = (void **) *(void **) config_iface;
SourceHook::SetMemAccess(&vtable_dest[is2sc_allowdedi_index],
sizeof(void*),
SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
if (patch)
{
assert(is2sc_orig_allowdedi == NULL);
is2sc_orig_allowdedi = vtable_dest[is2sc_allowdedi_index];
vtable_dest[is2sc_allowdedi_index] = vtable_src[mfp.vtblindex];
}
else
{
assert(is2sc_orig_allowdedi != NULL);
vtable_dest[is2sc_allowdedi_index] = is2sc_orig_allowdedi;
is2sc_orig_allowdedi = NULL;
}
}
static void
mm_PatchConnect(bool patch)
{
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&ISource2ServerConfig::Connect, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **) *(void **) &is2sc_thunk;
vtable_dest = (void **) *(void **) config_iface;
SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex],
sizeof(void*),
SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
if (patch)
{
assert(is2sc_orig_connect == NULL);
is2sc_orig_connect = vtable_dest[mfp.vtblindex];
vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex];
}
else
{
assert(is2sc_orig_connect != NULL);
vtable_dest[mfp.vtblindex] = is2sc_orig_connect;
is2sc_orig_connect = NULL;
}
}
void *
mm_GameDllRequest(const char *name, int *ret)
{
if (gamedll_lib != NULL && gamedll_bridge == NULL)
if (strncmp(name, "Source2ServerConfig", 19) == 0)
{
return gamedll_qvi(name, ret);
}
g_is_source2 = true;
if (!mm_DetectGameInformation())
{
if (ret != NULL)
*ret = 1;
return NULL;
}
if (strncmp(name, "ServerGameDLL", 13) == 0)
void *lib;
char error[255];
void *ptr = NULL;
QueryValveInterface qvi;
for (unsigned int i = 0; i < gamedll_path_count; i++)
{
if (gamedll_libs[i] == NULL)
{
lib = mm_LoadLibrary(gamedll_paths[i], error, sizeof(error));
if (lib == NULL)
continue;
gamedll_libs[i] = lib;
}
lib = gamedll_libs[i];
qvi = (QueryValveInterface)mm_GetLibAddress(lib, "CreateInterface");
if (qvi == NULL)
continue;
ptr = qvi(name, ret);
if (ptr != NULL)
{
gamedll_libs[i] = NULL;
break;
}
}
if (ptr != NULL)
{
mm_FreeCachedLibraries();
gamedll_lib = lib;
config_iface = (ISource2ServerConfig *) ptr;
gamedll_qvi = qvi;
mm_PatchConnect(true);
mm_PatchAllowDedicated(true);
if (ret != NULL)
*ret = 0;
return ptr;
}
}
else if (strncmp(name, "Source2Server0", 14) == 0)
{
gamedll_iface = (IServerGameDLL *)gamedll_qvi(name, ret);
gamedll_version = atoi(&name[13]);
mm_PatchDllInit(true);
if (ret != NULL)
*ret = 0;
return gamedll_iface;
}
else if (strncmp(name, "ServerGameDLL", 13) == 0)
{
if (!mm_DetectGameInformation())
{
@ -460,13 +824,17 @@ mm_GameDllRequest(const char *name, int *ret)
gamedll_iface = (IServerGameDLL *)ptr;
gamedll_qvi = qvi;
gamedll_version = atoi(&name[13]);
mm_PrepForGameLoad();
mm_PatchDllInit(true);
if (ret != NULL)
*ret = 0;
return ptr;
}
}
else if (gamedll_lib != NULL && gamedll_bridge == NULL)
{
return gamedll_qvi(name, ret);
}
else if (game_info_detected == 0)
{
mm_LogFatal("Received interface request too early: %s", name);

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2010 AlliedModders LLC and authors.
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
@ -90,6 +90,7 @@ static const char *backend_names[] =
"2.insurgency",
"2.contagion",
"2.bms",
"2.source2"
};
#if defined _WIN32
@ -267,7 +268,9 @@ mm_GetGameName(char *buffer, size_t size)
if (buffer[0] == 0)
{
strncpy(buffer, ".", size);
// FIXME: this was "." and is now "dota" for Source2.
// That breaks Dark Messiah compatibility.
strncpy(buffer, "dota", size);
}
}

View File

@ -2,7 +2,7 @@
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2009 AlliedModders LLC and authors.
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
@ -102,6 +102,7 @@ enum MetamodBackend
MMBackend_Insurgency,
MMBackend_Contagion,
MMBackend_BMS,
MMBackend_Source2,
MMBackend_UNKNOWN
};

View File

@ -2,7 +2,7 @@
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2008 AlliedModders LLC and authors.
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
@ -275,8 +275,14 @@ mm_PathCmp(const char *path1, const char *path2)
}
bool
mm_ResolvePath(const char *path, char *buffer, size_t maxlength)
mm_ResolvePath(const char *path, char *buffer, size_t maxlength, bool bSource2)
{
char tmp[PLATFORM_MAX_PATH];
if (bSource2)
{
mm_Format(tmp, sizeof(tmp), "../../%s", path);
path = tmp;
}
#if defined _WIN32
return _fullpath(buffer, path, maxlength) != NULL;
#elif defined __linux__ || defined __APPLE__

View File

@ -2,7 +2,7 @@
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2008 AlliedModders LLC and authors.
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
@ -43,7 +43,7 @@ extern void
mm_UnloadLibrary(void *lib);
extern bool
mm_ResolvePath(const char *path, char *buffer, size_t maxlength);
mm_ResolvePath(const char *path, char *buffer, size_t maxlength, bool bSource2);
extern size_t
mm_PathFormat(char *buffer, size_t len, const char *fmt, ...);

View File

@ -1,20 +0,0 @@
# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python:
import os.path
def configure_library(name, linux_defines):
binary = MMS.Library(builder, name)
binary.compiler.cxxincludes += [os.path.join(builder.sourcePath, 'core', 'sourcehook')]
binary.sources += [
'loader2.cpp',
'gamedll.cpp',
#'serverplugin.cpp',
'utility.cpp',
]
if builder.target_platform == 'linux':
binary.compiler.defines += linux_defines
nodes = builder.Add(binary)
MMS.binaries += [nodes]
configure_library('server', ['LIB_PREFIX="lib"', 'LIB_SUFFIX=".so"'])

View File

@ -1,607 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include "loader2.h"
#include <sh_memfuncinfo.h>
#include <sh_memory.h>
#include "utility.h"
#include "gamedll.h"
class ISource2Server;
class ISource2ServerConfig;
#define MAX_GAMEDLL_PATHS 10
IGameDllBridge* gamedll_bridge = NULL;
static int game_info_detected = 0;
static char game_name[128];
static char gamedll_paths[MAX_GAMEDLL_PATHS][PLATFORM_MAX_PATH];
static void *gamedll_libs[MAX_GAMEDLL_PATHS];
static unsigned int gamedll_path_count = 0;
static void *gamedll_lib = NULL;
static ISource2Server *gamedll_iface = NULL;
static ISource2ServerConfig *config_iface = NULL;
static QueryValveInterface gamedll_qvi = NULL;
static int gamedll_version = 0;
static int is2s_shutdown_index = -1;
static int is2sc_allowdedi_index = 20;
static char mm_path[PLATFORM_MAX_PATH];
#if defined _WIN32
#define SERVER_NAME "server.dll"
#elif defined __APPLE__
#define SERVER_NAME "server.dylib"
#elif defined __linux__
#define SERVER_NAME "server" LIB_SUFFIX
#endif
static bool mm_DetectGameInformation()
{
char game_path[PLATFORM_MAX_PATH];
if (game_info_detected)
return game_info_detected == 1 ? true : false;
game_info_detected = -1;
mm_GetGameName(game_name, sizeof(game_name));
if (!mm_GetFileOfAddress((void*)mm_DetectGameInformation, mm_path, sizeof(mm_path)))
{
mm_LogFatal("Could not locate Metamod loader library path");
return false;
}
if (!mm_ResolvePath(game_name, game_path, sizeof(game_path)))
{
mm_LogFatal("Could not resolve path: %s", game_name);
return false;
}
FILE *fp;
char gameinfo_path[PLATFORM_MAX_PATH];
mm_PathFormat(gameinfo_path, sizeof(gameinfo_path), "%s/gameinfo.gi", game_path);
if ((fp = fopen(gameinfo_path, "rt")) == NULL)
{
mm_LogFatal("Could not read file: %s", gameinfo_path);
return false;
}
char temp_path[PLATFORM_MAX_PATH];
char cur_path[PLATFORM_MAX_PATH];
getcwd(cur_path, sizeof(cur_path));
char *ptr;
const char *lptr;
bool search = false;
char buffer[255], key[128], val[128];
while (!feof(fp) && fgets(buffer, sizeof(buffer), fp) != NULL)
{
mm_TrimComments(buffer);
mm_TrimLeft(buffer);
mm_TrimRight(buffer);
if (stricmp(buffer, "SearchPaths") == 0)
search = true;
if (!search)
continue;
mm_KeySplit(buffer, key, sizeof(key) - 1, val, sizeof(val) - 1);
if (stricmp(key, "Game") != 0 && stricmp(key, "GameBin") != 0)
continue;
if (strncmp(val, "|gameinfo_path|", sizeof("|gameinfo_path|") - 1) == 0)
{
ptr = &val[sizeof("|gameinfo_path|") - 1];
if (ptr[0] == '.')
ptr++;
lptr = game_path;
}
else
{
ptr = val;
lptr = cur_path;
}
if (stricmp(key, "GameBin") == 0)
mm_PathFormat(temp_path, sizeof(temp_path), "%s/../../%s/" SERVER_NAME, lptr, ptr);
else if (!ptr[0])
mm_PathFormat(temp_path, sizeof(temp_path), "%s/../../bin/win32/" SERVER_NAME, lptr);
else
mm_PathFormat(temp_path, sizeof(temp_path), "%s/../../%s/bin/win32/" SERVER_NAME, lptr, ptr);
if (mm_PathCmp(mm_path, temp_path))
continue;
FILE *exists = fopen(temp_path, "rb");
if (!exists)
continue;
fclose(exists);
/* exists is still non-NULL... use this as a flag */
for (unsigned int i = 0; i < gamedll_path_count; i++)
{
if (mm_PathCmp(gamedll_paths[i], temp_path))
{
exists = NULL;
break;
}
}
if (!exists)
continue;
mm_Format(gamedll_paths[gamedll_path_count],
PLATFORM_MAX_PATH,
"%s",
temp_path);
gamedll_path_count++;
if (gamedll_path_count == MAX_GAMEDLL_PATHS)
break;
}
fclose(fp);
game_info_detected = 1;
if (gamedll_path_count == 0)
{
mm_LogFatal("Could not detect any valid game paths in gameinfo.gi");
return false;
}
return true;
}
static void mm_FreeCachedLibraries()
{
for (unsigned int i = 0; i < gamedll_path_count; i++)
{
if (gamedll_libs[i] == NULL)
continue;
mm_UnloadLibrary(gamedll_libs[i]);
}
}
static void mm_PatchDllInit(bool patch);
static void mm_PatchDllShutdown();
static void mm_PatchAllowDedicated(bool patch);
static void mm_PatchConnect(bool patch);
static void *is2s_orig_init = NULL;
static void *is2s_orig_shutdown = NULL;
static void *is2sc_orig_allowdedi = NULL;
static void *is2sc_orig_connect = NULL;
class VEmptyClass
{
};
gamedll_bridge_info g_bridge_info;
// Rough start order
// CreateInterfaceFn (IS2SC) - hook Connect and AllowDedicatedServer
// IS2SC::Connect - save factory pointer. return orig. remove hook.
// IS2SC::AllowDedicatedServer - return true. remove hook.
// CreateInterfaceFn (IS2S) - hook Init and Shutdown
// IS2S::Init - do same as old ISGD::DLLInit, including core load. return orig. remove hook.
// IS2S::Shutdown - <-- this
enum InitReturnVal_t
{
INIT_FAILED = 0,
INIT_OK,
INIT_LAST_VAL,
};
class ISource2ServerConfig
{
public:
virtual bool Connect(QueryValveInterface factory)
{
g_bridge_info.engineFactory = factory;
g_bridge_info.fsFactory = factory;
g_bridge_info.physicsFactory = factory;
/* Call the original */
bool result;
{
union
{
bool(VEmptyClass::*mfpnew)(QueryValveInterface factory);
#if defined _WIN32
void *addr;
} u;
u.addr = is2sc_orig_connect;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = is2sc_orig_connect;
u.s.adjustor = 0;
#endif
result = (((VEmptyClass *) config_iface)->*u.mfpnew)(factory);
}
mm_PatchConnect(false);
return result;
}
virtual bool AllowDedicatedServers(int universe) const
{
mm_PatchAllowDedicated(false);
return true;
}
};
class ISource2Server
{
public:
virtual bool Connect(QueryValveInterface factory) { return true; }
virtual void Disconnect() {}
virtual void *QueryInterface(const char *pInterfaceName) { return nullptr; }
virtual InitReturnVal_t Init()
{
char error[255];
if (mm_backend == MMBackend_UNKNOWN)
{
mm_LogFatal("Could not detect engine version");
}
else
{
if (!mm_LoadMetamodLibrary(mm_backend, error, sizeof(error)))
{
mm_LogFatal("Detected engine %d but could not load: %s", mm_backend, error);
}
else
{
typedef IGameDllBridge *(*GetGameDllBridge)();
GetGameDllBridge get_bridge = (GetGameDllBridge)mm_GetProcAddress("GetGameDllBridge");
if (get_bridge == NULL)
{
mm_UnloadMetamodLibrary();
mm_LogFatal("Detected engine %d but could not find GetGameDllBridge callback", mm_backend);
}
else
{
gamedll_bridge = get_bridge();
}
}
}
if (gamedll_bridge)
{
g_bridge_info.pGlobals = nullptr;// pGlobals;
g_bridge_info.dllVersion = gamedll_version;
g_bridge_info.isgd = gamedll_iface;
g_bridge_info.gsFactory = gamedll_qvi;
g_bridge_info.vsp_listener_path = mm_path;
strcpy(error, "Unknown error");
if (!gamedll_bridge->DLLInit_Pre(&g_bridge_info, error, sizeof(error)))
{
gamedll_bridge = NULL;
mm_UnloadMetamodLibrary();
mm_LogFatal("Unknown error loading Metamod for engine %d: %s", mm_backend, error);
}
}
/* Call the original */
InitReturnVal_t result;
{
union
{
InitReturnVal_t(VEmptyClass::*mfpnew)();
#if defined _WIN32
void *addr;
} u;
u.addr = is2s_orig_init;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = is2s_orig_init;
u.s.adjustor = 0;
#endif
result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)();
}
/**
* :TODO: possible logic hole here, what happens if the gamedll REALLY returns false?
* I'm pretty sure we'll die horribly.
*/
if (!result)
{
gamedll_bridge->Unload();
mm_UnloadMetamodLibrary();
gamedll_bridge = NULL;
}
else if (gamedll_bridge != NULL)
{
gamedll_bridge->DLLInit_Post(&is2s_shutdown_index);
assert(is2s_shutdown_index != -1);
mm_PatchDllShutdown();
}
mm_PatchDllInit(false);
return result;
}
virtual void Shutdown()
{
gamedll_bridge->Unload();
gamedll_bridge = NULL;
mm_UnloadMetamodLibrary();
/* Call original function */
{
union
{
void (VEmptyClass::*mfpnew)();
#if defined _WIN32
void *addr;
} u;
u.addr = is2s_orig_shutdown;
#else
struct
{
void *addr;
intptr_t adjustor;
} s;
} u;
u.s.addr = is2s_orig_shutdown;
u.s.adjustor = 0;
#endif
(((VEmptyClass *)gamedll_iface)->*u.mfpnew)();
}
mm_UnloadLibrary(gamedll_lib);
gamedll_lib = NULL;
}
};
static ISource2Server is2s_thunk;
static ISource2ServerConfig is2sc_thunk;
static void mm_PatchDllInit(bool patch)
{
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&ISource2Server::Init, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **)*(void **)&is2s_thunk;
vtable_dest = (void **)*(void **)gamedll_iface;
SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex],
sizeof(void*),
SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
if (patch)
{
assert(is2s_orig_init == NULL);
is2s_orig_init = vtable_dest[mfp.vtblindex];
vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex];
}
else
{
assert(is2s_orig_init != NULL);
vtable_dest[mfp.vtblindex] = is2s_orig_init;
is2s_orig_init = NULL;
}
}
static void mm_PatchDllShutdown()
{
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
mfp.isVirtual = false;
SourceHook::GetFuncInfo(&ISource2Server::Shutdown, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **)*(void **)&is2s_thunk;
vtable_dest = (void **)*(void **)gamedll_iface;
is2s_orig_shutdown = vtable_dest[is2s_shutdown_index];
vtable_dest[is2s_shutdown_index] = vtable_src[mfp.vtblindex];
}
static void mm_PatchAllowDedicated(bool patch)
{
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&ISource2ServerConfig::AllowDedicatedServers, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **) *(void **) &is2sc_thunk;
vtable_dest = (void **) *(void **) config_iface;
SourceHook::SetMemAccess(&vtable_dest[is2sc_allowdedi_index],
sizeof(void*),
SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
if (patch)
{
assert(is2sc_orig_allowdedi == NULL);
is2sc_orig_allowdedi = vtable_dest[is2sc_allowdedi_index];
vtable_dest[is2sc_allowdedi_index] = vtable_src[mfp.vtblindex];
}
else
{
assert(is2sc_orig_allowdedi != NULL);
vtable_dest[is2sc_allowdedi_index] = is2sc_orig_allowdedi;
is2sc_orig_allowdedi = NULL;
}
}
static void mm_PatchConnect(bool patch)
{
void **vtable_src;
void **vtable_dest;
SourceHook::MemFuncInfo mfp;
SourceHook::GetFuncInfo(&ISource2ServerConfig::Connect, mfp);
assert(mfp.isVirtual);
assert(mfp.thisptroffs == 0);
assert(mfp.vtbloffs == 0);
vtable_src = (void **) *(void **) &is2sc_thunk;
vtable_dest = (void **) *(void **) config_iface;
SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex],
sizeof(void*),
SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC);
if (patch)
{
assert(is2sc_orig_connect == NULL);
is2sc_orig_connect = vtable_dest[mfp.vtblindex];
vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex];
}
else
{
assert(is2sc_orig_connect != NULL);
vtable_dest[mfp.vtblindex] = is2sc_orig_connect;
is2sc_orig_connect = NULL;
}
}
enum ServerIface
{
Other,
ServerConfig,
Server
};
void *
mm_GameDllRequest(const char *name, int *ret)
{
if (strncmp(name, "Source2ServerConfig", 19) == 0)
{
if (!mm_DetectGameInformation())
{
if (ret != NULL)
*ret = 1;
return NULL;
}
void *lib;
char error[255];
void *ptr = NULL;
QueryValveInterface qvi;
for (unsigned int i = 0; i < gamedll_path_count; i++)
{
if (gamedll_libs[i] == NULL)
{
lib = mm_LoadLibrary(gamedll_paths[i], error, sizeof(error));
if (lib == NULL)
continue;
gamedll_libs[i] = lib;
}
lib = gamedll_libs[i];
qvi = (QueryValveInterface)mm_GetLibAddress(lib, "CreateInterface");
if (qvi == NULL)
continue;
ptr = qvi(name, ret);
if (ptr != NULL)
{
gamedll_libs[i] = NULL;
break;
}
}
if (ptr != NULL)
{
mm_FreeCachedLibraries();
gamedll_lib = lib;
config_iface = (ISource2ServerConfig *) ptr;
gamedll_qvi = qvi;
mm_PatchConnect(true);
mm_PatchAllowDedicated(true);
if (ret != NULL)
*ret = 0;
return ptr;
}
}
else if (strncmp(name, "Source2Server0", 14) == 0)
{
gamedll_iface = (ISource2Server *)gamedll_qvi(name, ret);
gamedll_version = atoi(&name[13]);
mm_PatchDllInit(true);
if (ret != NULL)
*ret = 0;
return gamedll_iface;
}
else if (gamedll_lib != NULL && gamedll_bridge == NULL)
{
return gamedll_qvi(name, ret);
}
else if (game_info_detected == 0)
{
mm_LogFatal("Received interface request too early: %s", name);
}
if (ret != NULL)
*ret = 1;
return NULL;
}

View File

@ -1,39 +0,0 @@
/**
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id$
*/
#ifndef _INCLUDE_METAMOD_SOURCE_GAMEDLLS_H_
#define _INCLUDE_METAMOD_SOURCE_GAMEDLLS_H_
#include "loader_bridge.h"
extern void *
mm_GameDllRequest(const char *name, int *ret);
extern IGameDllBridge* gamedll_bridge;
#endif /* _INCLUDE_METAMOD_SOURCE_GAMEDLLS_H_ */

View File

@ -1,232 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "loader2.h"
#include "gamedll.h"
#include "utility.h"
#if defined __APPLE__
#include <crt_externs.h>
#endif
static HMODULE mm_library = NULL;
static char mm_fatal_logfile[PLATFORM_MAX_PATH] = "metamod-fatal.log";
MetamodBackend mm_backend = MMBackend_Source2;
extern void
mm_LogFatal(const char *message, ...)
{
FILE *fp;
time_t t;
va_list ap;
char header[256];
fp = fopen(mm_fatal_logfile, "at");
if (!fp && (fp = fopen("metamod-fatal.log", "at")) == NULL)
return;
t = time(NULL);
strftime(header, sizeof(header), "%m/%d/%Y - %H:%M:%S", localtime(&t));
fprintf(fp, "L %s: ", header);
va_start(ap, message);
vfprintf(fp, message, ap);
va_end(ap);
fprintf(fp, "\n");
fclose(fp);
}
#if defined _WIN32
#define LIBRARY_EXT ".dll"
#define LIBRARY_MINEXT ".dll"
#elif defined __APPLE__
#define LIBRARY_EXT ".dylib"
#define LIBRARY_MINEXT ".dylib"
#elif defined __linux__
#define LIBRARY_EXT LIB_SUFFIX
#define LIBRARY_MINEXT ".so"
#endif
bool
mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength)
{
size_t len, temp_len;
char mm_path[PLATFORM_MAX_PATH * 2];
/* Get our path */
if (!mm_GetFileOfAddress((void*)mm_GetFileOfAddress, mm_path, sizeof(mm_path)))
return false;
len = strlen(mm_path);
temp_len = strlen("server" LIBRARY_EXT);
if (len < temp_len)
return false;
/* Build log file name */
mm_path[len - temp_len] = '\0';
mm_Format(mm_fatal_logfile,
sizeof(mm_fatal_logfile),
"%smetamod-fatal.log",
mm_path);
/* Replace server2.dll with the new binary we want */
mm_Format(&mm_path[len - temp_len],
sizeof(mm_path) - (len - temp_len),
"metamod.2.s2" LIBRARY_MINEXT);
mm_library = (HMODULE)mm_LoadLibrary(mm_path, buffer, maxlength);
return (mm_library != NULL);
}
void
mm_UnloadMetamodLibrary()
{
mm_UnloadLibrary(mm_library);
mm_library = NULL;
}
#if defined _WIN32
#define EXPORT extern "C" __declspec(dllexport)
#elif defined __GNUC__
#if __GNUC__ == 4
#define EXPORT extern "C" __attribute__ ((visibility("default")))
#else
#define EXPORT extern "C"
#endif
#endif
EXPORT void *
CreateInterface(const char *name, int *ret)
{
void *ptr;
if (gamedll_bridge == NULL)
{
/* Load as gamedll */
ptr = mm_GameDllRequest(name, ret);
}
else
{
/* If we've got a gamedll bridge, forward the request. */
return gamedll_bridge->QueryInterface(name, ret);
}
if (ret != NULL)
*ret = (ptr != NULL) ? 0 : 1;
return ptr;
}
void *
mm_GetProcAddress(const char *name)
{
return mm_GetLibAddress(mm_library, name);
}
void
mm_GetGameName(char *buffer, size_t size)
{
buffer[0] = '\0';
#if defined _WIN32
static char game[128];
LPWSTR pCmdLine = GetCommandLineW();
int argc;
LPWSTR *wargv = CommandLineToArgvW(pCmdLine, &argc);
for (int i = 0; i < argc; ++i)
{
if (wcscmp(wargv[i], L"-game") != 0)
continue;
if (++i >= argc)
break;
wcstombs(buffer, wargv[i], size);
buffer[size-1] = '\0';
break;
}
LocalFree(wargv);
#elif defined __APPLE__
int argc = *_NSGetArgc();
char **argv = *_NSGetArgv();
for (int i = 0; i < argc; ++i)
{
if (strcmp(argv[i], "-game") != 0)
continue;
if (++i >= argc)
break;
strncpy(buffer, argv[i], size);
buffer[size-1] = '\0';
break;
}
#elif defined __linux__
FILE *pFile = fopen("/proc/self/cmdline", "rb");
if (pFile)
{
char *arg = NULL;
size_t argsize = 0;
bool bNextIsGame = false;
while (getdelim(&arg, &argsize, 0, pFile) != -1)
{
if (bNextIsGame)
{
strncpy(buffer, arg, size);
buffer[size-1] = '\0';
break;
}
if (strcmp(arg, "-game") == 0)
{
bNextIsGame = true;
}
}
free(arg);
fclose(pFile);
}
#else
#error unsupported platform
#endif
if (buffer[0] == 0)
{
strncpy(buffer, "dota", size);
}
}

View File

@ -1,100 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _INCLUDE_METAMOD_SOURCE_LOADER_H_
#define _INCLUDE_METAMOD_SOURCE_LOADER_H_
// System
#define SH_SYS_WIN32 1
#define SH_SYS_LINUX 2
#define SH_SYS_APPLE 3
// Platform
#define SH_XP_POSIX 10
#define SH_XP_WINAPI 20
// Compiler
#define SH_COMP_GCC 1
#define SH_COMP_MSVC 2
#if defined WIN32
#define SH_SYS SH_SYS_WIN32
#define SH_XP SH_XP_WINAPI
#define SH_COMP SH_COMP_MSVC
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <direct.h>
#define PLATFORM_MAX_PATH MAX_PATH
#define PATH_SEP_STR "\\"
#define PATH_SEP_CHAR '\\'
#define ALT_SEP_CHAR '/'
#elif defined __linux__ || defined __APPLE__
#if defined __linux__
#define SH_SYS SH_SYS_LINUX
#elif defined __APPLE__
#define SH_SYS SH_SYS_APPLE
#endif
#define SH_XP SH_XP_POSIX
#define SH_COMP SH_COMP_GCC
#include <dlfcn.h>
#include <dirent.h>
#include <stdint.h>
#include <unistd.h>
#if SH_SYS == SH_SYS_APPLE
#include <sys/syslimits.h>
#endif
typedef void * HMODULE;
#define PLATFORM_MAX_PATH PATH_MAX
#define PATH_SEP_STR "/"
#define PATH_SEP_CHAR '/'
#define ALT_SEP_CHAR '\\'
#else
#error "OS detection failed"
#endif
#include <loader_public.h>
#include "loader_bridge.h"
#define SH_PTRSIZE sizeof(void*)
extern bool
mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength);
extern void *
mm_GetProcAddress(const char *name);
extern void
mm_UnloadMetamodLibrary();
extern void
mm_LogFatal(const char *message, ...);
extern void
mm_GetGameName(char *buffer, size_t size);
extern MetamodBackend mm_backend;
#endif /* _INCLUDE_METAMOD_SOURCE_LOADER_H_ */

View File

@ -1,546 +0,0 @@
/**
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id$
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include "loader2.h"
#include "utility.h"
#if defined __linux__
#include <link.h>
#define PAGE_SIZE 4096
#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
#elif defined __APPLE__
#include <mach-o/loader.h>
#endif
#if defined _WIN32
static void
mm_GetPlatformError(char *buffer, size_t maxlength)
{
DWORD dw = GetLastError();
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)buffer,
maxlength,
NULL);
}
#endif
size_t
mm_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list params)
{
size_t len = vsnprintf(buffer, maxlength, fmt, params);
if (len >= maxlength)
{
len = maxlength - 1;
buffer[len] = '\0';
}
return len;
}
size_t
mm_Format(char *buffer, size_t maxlength, const char *fmt, ...)
{
size_t len;
va_list ap;
va_start(ap, fmt);
len = mm_FormatArgs(buffer, maxlength, fmt, ap);
va_end(ap);
return len;
}
size_t
mm_PathFormat(char *buffer, size_t maxlen, const char *fmt, ...)
{
size_t len;
va_list ap;
va_start(ap, fmt);
len = mm_FormatArgs(buffer, maxlen, fmt, ap);
va_end(ap);
for (size_t i = 0; i < len; i++)
{
if (buffer[i] == ALT_SEP_CHAR)
buffer[i] = PATH_SEP_CHAR;
}
return len;
}
void
mm_TrimLeft(char *buffer)
{
/* Let's think of this as our iterator */
char *i = buffer;
/* Make sure the buffer isn't null */
if (i && *i)
{
/* Add up number of whitespace characters */
while(isspace((unsigned char) *i))
i++;
/* If whitespace chars in buffer then adjust string so first non-whitespace char is at start of buffer */
if (i != buffer)
memmove(buffer, i, (strlen(i) + 1) * sizeof(char));
}
}
void
mm_TrimRight(char *buffer)
{
/* Make sure buffer isn't null */
if (buffer)
{
size_t len = strlen(buffer);
/* Loop through buffer backwards while replacing whitespace chars with null chars */
for (size_t i = len - 1; i < len; i--)
{
if (isspace((unsigned char) buffer[i]))
buffer[i] = '\0';
else
break;
}
}
}
/* :TODO: this should skip string literals */
void
mm_TrimComments(char *buffer)
{
int num_sc = 0;
size_t len = strlen(buffer);
if (buffer)
{
for (int i = len - 1; i >= 0; i--)
{
if (buffer[i] == '/')
{
if (++num_sc >= 2 && i==0)
{
buffer[i] = '\0';
return;
}
}
else
{
if (num_sc >= 2)
{
buffer[i] = '\0';
return;
}
num_sc = 0;
}
/* size_t won't go below 0, manually break out */
if (i == 0)
break;
}
}
}
void
mm_KeySplit(const char *str, char *buf1, size_t len1, char *buf2, size_t len2)
{
size_t start;
size_t len = strlen(str);
for (start = 0; start < len; start++)
{
if (!isspace(str[start]))
break;
}
size_t end;
for (end = start; end < len; end++)
{
if (isspace(str[end]))
break;
}
size_t i, c = 0;
for (i = start; i < end; i++, c++)
{
if (c >= len1)
break;
buf1[c] = str[i];
}
buf1[c] = '\0';
for (start = end; start < len; start++)
{
if (!isspace(str[start]))
break;
}
for (c = 0; start < len; start++, c++)
{
if (c >= len2)
break;
buf2[c] = str[start];
}
buf2[c] = '\0';
}
bool
mm_PathCmp(const char *path1, const char *path2)
{
size_t pos1 = 0, pos2 = 0;
while (true)
{
if (path1[pos1] == '\0' || path2[pos2] == '\0')
return (path1[pos1] == path2[pos2]);
if (path1[pos1] == PATH_SEP_CHAR)
{
if (path2[pos2] != PATH_SEP_CHAR)
return false;
/* Look for extra path chars */
while (path1[++pos1])
{
if (path1[pos1] != PATH_SEP_CHAR)
break;
}
while (path2[++pos2])
{
if (path2[pos2] != PATH_SEP_CHAR)
break;
}
continue;
}
/* If we're at a different non-alphanumeric, the next character MUST match */
if ((((unsigned)path1[pos1] & 0x80) && path1[pos1] != path2[pos2])
||
(!isalpha(path1[pos1]) && (path1[pos1] != path2[pos2]))
)
{
return false;
}
#ifdef WIN32
if (toupper(path1[pos1]) != toupper(path2[pos2]))
#else
if (path1[pos1] != path2[pos2])
#endif
{
return false;
}
pos1++;
pos2++;
}
}
bool
mm_ResolvePath(const char *path, char *buffer, size_t maxlength)
{
char tmp[PLATFORM_MAX_PATH];
mm_Format(tmp, sizeof(tmp), "../../%s", path);
#if defined _WIN32
return _fullpath(buffer, tmp, maxlength) != NULL;
#elif defined __linux__ || defined __APPLE__
assert(maxlength >= PATH_MAX);
return realpath(tmp, buffer) != NULL;
#endif
}
void *
mm_LoadLibrary(const char *path, char *buffer, size_t maxlength)
{
void *lib;
#if defined _WIN32
lib = (void*)LoadLibrary(path);
if (lib == NULL)
{
mm_GetPlatformError(buffer, maxlength);
return NULL;
}
#elif defined __linux__ || defined __APPLE__
lib = dlopen(path, RTLD_NOW);
if (lib == NULL)
{
mm_Format(buffer, maxlength, "%s", dlerror());
return NULL;
}
#endif
return lib;
}
void *
mm_GetLibAddress(void *lib, const char *name)
{
#if defined _WIN32
return GetProcAddress((HMODULE)lib, name);
#elif defined __linux__ || defined __APPLE__
return dlsym(lib, name);
#endif
}
void
mm_UnloadLibrary(void *lib)
{
#if defined _WIN32
FreeLibrary((HMODULE)lib);
#elif defined __linux__ || defined __APPLE__
dlclose(lib);
#endif
}
bool
mm_GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)
{
#if defined _WIN32
MEMORY_BASIC_INFORMATION mem;
if (!VirtualQuery(pAddr, &mem, sizeof(mem)))
return false;
if (mem.AllocationBase == NULL)
return false;
HMODULE dll = (HMODULE)mem.AllocationBase;
GetModuleFileName(dll, (LPTSTR)buffer, maxlength);
#elif defined __linux__ || defined __APPLE__
Dl_info info;
if (!dladdr(pAddr, &info))
return false;
if (!info.dli_fbase || !info.dli_fname)
return false;
const char *dllpath = info.dli_fname;
snprintf(buffer, maxlength, "%s", dllpath);
#endif
return true;
}
struct DynLibInfo
{
void *baseAddress;
size_t memorySize;
};
static bool
mm_GetLibraryInfo(const void *libPtr, DynLibInfo &lib)
{
uintptr_t baseAddr;
if (libPtr == NULL)
{
return false;
}
#ifdef _WIN32
MEMORY_BASIC_INFORMATION info;
IMAGE_DOS_HEADER *dos;
IMAGE_NT_HEADERS *pe;
IMAGE_FILE_HEADER *file;
IMAGE_OPTIONAL_HEADER *opt;
if (!VirtualQuery(libPtr, &info, sizeof(MEMORY_BASIC_INFORMATION)))
{
return false;
}
baseAddr = reinterpret_cast<uintptr_t>(info.AllocationBase);
/* All this is for our insane sanity checks :o */
dos = reinterpret_cast<IMAGE_DOS_HEADER *>(baseAddr);
pe = reinterpret_cast<IMAGE_NT_HEADERS *>(baseAddr + dos->e_lfanew);
file = &pe->FileHeader;
opt = &pe->OptionalHeader;
/* Check PE magic and signature */
if (dos->e_magic != IMAGE_DOS_SIGNATURE || pe->Signature != IMAGE_NT_SIGNATURE || opt->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
return false;
}
/* Check architecture, which is 32-bit/x86 right now
* Should change this for 64-bit if Valve gets their act together
*/
if (file->Machine != IMAGE_FILE_MACHINE_I386)
{
return false;
}
/* For our purposes, this must be a dynamic library */
if ((file->Characteristics & IMAGE_FILE_DLL) == 0)
{
return false;
}
/* Finally, we can do this */
lib.memorySize = opt->SizeOfImage;
#elif defined __linux__
Dl_info info;
Elf32_Ehdr *file;
Elf32_Phdr *phdr;
uint16_t phdrCount;
if (!dladdr(libPtr, &info))
{
return false;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
}
/* This is for our insane sanity checks :o */
baseAddr = reinterpret_cast<uintptr_t>(info.dli_fbase);
file = reinterpret_cast<Elf32_Ehdr *>(baseAddr);
/* Check ELF magic */
if (memcmp(ELFMAG, file->e_ident, SELFMAG) != 0)
{
return false;
}
/* Check ELF version */
if (file->e_ident[EI_VERSION] != EV_CURRENT)
{
return false;
}
/* Check ELF architecture, which is 32-bit/x86 right now
* Should change this for 64-bit if Valve gets their act together
*/
if (file->e_ident[EI_CLASS] != ELFCLASS32 || file->e_machine != EM_386 || file->e_ident[EI_DATA] != ELFDATA2LSB)
{
return false;
}
/* For our purposes, this must be a dynamic library/shared object */
if (file->e_type != ET_DYN)
{
return false;
}
phdrCount = file->e_phnum;
phdr = reinterpret_cast<Elf32_Phdr *>(baseAddr + file->e_phoff);
for (uint16_t i = 0; i < phdrCount; i++)
{
Elf32_Phdr &hdr = phdr[i];
/* We only really care about the segment with executable code */
if (hdr.p_type == PT_LOAD && hdr.p_flags == (PF_X|PF_R))
{
/* From glibc, elf/dl-load.c:
* c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
* & ~(GLRO(dl_pagesize) - 1));
*
* In glibc, the segment file size is aligned up to the nearest page size and
* added to the virtual address of the segment. We just want the size here.
*/
lib.memorySize = PAGE_ALIGN_UP(hdr.p_filesz);
break;
}
}
#elif defined __APPLE__
Dl_info info;
struct mach_header *file;
struct segment_command *seg;
uint32_t cmd_count;
if (!dladdr(libPtr, &info))
{
return false;
}
if (!info.dli_fbase || !info.dli_fname)
{
return false;
}
/* This is for our insane sanity checks :o */
baseAddr = (uintptr_t)info.dli_fbase;
file = (struct mach_header *)baseAddr;
/* Check Mach-O magic */
if (file->magic != MH_MAGIC)
{
return false;
}
/* Check architecture (32-bit/x86) */
if (file->cputype != CPU_TYPE_I386 || file->cpusubtype != CPU_SUBTYPE_I386_ALL)
{
return false;
}
/* For our purposes, this must be a dynamic library */
if (file->filetype != MH_DYLIB)
{
return false;
}
cmd_count = file->ncmds;
seg = (struct segment_command *)(baseAddr + sizeof(struct mach_header));
/* Add up memory sizes of mapped segments */
for (uint32_t i = 0; i < cmd_count; i++)
{
if (seg->cmd == LC_SEGMENT)
{
lib.memorySize += seg->vmsize;
}
seg = (struct segment_command *)((uintptr_t)seg + seg->cmdsize);
}
#endif
lib.baseAddress = reinterpret_cast<void *>(baseAddr);
return true;
}

View File

@ -1,70 +0,0 @@
/**
* vim: set ts=4 :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2015 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Version: $Id$
*/
#ifndef _INCLUDE_METAMOD_SOURCE_LOADER_UTILITY_H_
#define _INCLUDE_METAMOD_SOURCE_LOADER_UTILITY_H_
#include <stddef.h>
extern size_t
mm_Format(char *buffer, size_t maxlength, const char *fmt, ...);
extern void *
mm_LoadLibrary(const char *path, char *buffer, size_t maxlength);
extern void *
mm_GetLibAddress(void *lib, const char *name);
extern void
mm_UnloadLibrary(void *lib);
extern bool
mm_ResolvePath(const char *path, char *buffer, size_t maxlength);
extern size_t
mm_PathFormat(char *buffer, size_t len, const char *fmt, ...);
extern void
mm_TrimLeft(char *buffer);
extern void
mm_TrimRight(char *buffer);
extern void
mm_TrimComments(char *buffer);
extern void
mm_KeySplit(const char *str, char *buf1, size_t len1, char *buf2, size_t len2);
extern bool
mm_PathCmp(const char *path1, const char *path2);
extern bool
mm_GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength);
#endif /* _INCLUDE_METAMOD_SOURCE_LOADER_UTILITY_H_ */

View File

@ -1,58 +0,0 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2010 AlliedModders LLC and authors.
* All rights reserved.
* ======================================================
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef _INCLUDE_METAMOD_LOADER_PUBLIC_H_
#define _INCLUDE_METAMOD_LOADER_PUBLIC_H_
enum MetamodBackend
{
MMBackend_Episode1 = 0,
MMBackend_DarkMessiah,
MMBackend_Episode2,
MMBackend_BloodyGoodTime,
MMBackend_EYE,
MMBackend_CSS,
MMBackend_Episode2Valve_OBSOLETE,
MMBackend_Left4Dead,
MMBackend_Left4Dead2,
MMBackend_AlienSwarm,
MMBackend_Portal2,
MMBackend_CSGO,
MMBackend_DOTA,
MMBackend_HL2DM,
MMBackend_DODS,
MMBackend_TF2,
MMBackend_NuclearDawn,
MMBackend_SDK2013,
MMBackend_Blade,
MMBackend_Insurgency,
MMBackend_Contagion,
MMBackend_BMS,
MMBackend_Source2,
MMBackend_UNKNOWN
};
#endif // _INCLUDE_METAMOD_LOADER_PUBLIC_H_