diff --git a/AMBuildScript b/AMBuildScript index 2ece4a1..d207c37 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -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', ] diff --git a/core/provider/provider_ep2.cpp b/core/provider/provider_ep2.cpp index 8a3a303..41fcfc6 100644 --- a/core/provider/provider_ep2.cpp +++ b/core/provider/provider_ep2.cpp @@ -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() diff --git a/loader/gamedll.cpp b/loader/gamedll.cpp index 1d46c9f..000f900 100644 --- a/loader/gamedll.cpp +++ b/loader/gamedll.cpp @@ -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); diff --git a/loader/loader.cpp b/loader/loader.cpp index f2a696b..906b913 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -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); } } diff --git a/loader/loader.h b/loader/loader.h index 9c11681..ef22666 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -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 }; diff --git a/public/loader_bridge.h b/loader/loader_bridge.h similarity index 100% rename from public/loader_bridge.h rename to loader/loader_bridge.h diff --git a/loader/utility.cpp b/loader/utility.cpp index 5de2716..c2af968 100644 --- a/loader/utility.cpp +++ b/loader/utility.cpp @@ -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__ diff --git a/loader/utility.h b/loader/utility.h index c586d1b..25be3e6 100644 --- a/loader/utility.h +++ b/loader/utility.h @@ -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, ...); diff --git a/loader2/AMBuilder b/loader2/AMBuilder deleted file mode 100644 index a40c986..0000000 --- a/loader2/AMBuilder +++ /dev/null @@ -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"']) diff --git a/loader2/gamedll.cpp b/loader2/gamedll.cpp deleted file mode 100644 index c81e618..0000000 --- a/loader2/gamedll.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include "loader2.h" -#include -#include -#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; -} - diff --git a/loader2/gamedll.h b/loader2/gamedll.h deleted file mode 100644 index 1248ef8..0000000 --- a/loader2/gamedll.h +++ /dev/null @@ -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_ */ - diff --git a/loader2/loader2.cpp b/loader2/loader2.cpp deleted file mode 100644 index e177f38..0000000 --- a/loader2/loader2.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include "loader2.h" -#include "gamedll.h" -#include "utility.h" -#if defined __APPLE__ -#include -#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); - } -} - diff --git a/loader2/loader2.h b/loader2/loader2.h deleted file mode 100644 index ea16fb9..0000000 --- a/loader2/loader2.h +++ /dev/null @@ -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 -#include -#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 -#include -#include -#include -#if SH_SYS == SH_SYS_APPLE -#include -#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 -#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_ */ - diff --git a/loader2/utility.cpp b/loader2/utility.cpp deleted file mode 100644 index ce75c5c..0000000 --- a/loader2/utility.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include "loader2.h" -#include "utility.h" - -#if defined __linux__ -#include - -#define PAGE_SIZE 4096 -#define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) -#elif defined __APPLE__ -#include -#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(info.AllocationBase); - - /* All this is for our insane sanity checks :o */ - dos = reinterpret_cast(baseAddr); - pe = reinterpret_cast(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(info.dli_fbase); - file = reinterpret_cast(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(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(baseAddr); - - return true; -} diff --git a/loader2/utility.h b/loader2/utility.h deleted file mode 100644 index 9d8b40a..0000000 --- a/loader2/utility.h +++ /dev/null @@ -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 - -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_ */ - diff --git a/public/loader_public.h b/public/loader_public.h deleted file mode 100644 index 18c8536..0000000 --- a/public/loader_public.h +++ /dev/null @@ -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_ \ No newline at end of file