diff --git a/loader/Makefile b/loader/Makefile index 77b71f0..f3ee494 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -8,7 +8,9 @@ BINARY = server_i486.so OBJECTS = loader.cpp \ - serverplugin.cpp + utility.cpp \ + serverplugin.cpp \ + gamedll.cpp ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### diff --git a/loader/gamedll.cpp b/loader/gamedll.cpp new file mode 100644 index 0000000..a411794 --- /dev/null +++ b/loader/gamedll.cpp @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include +#include "loader.h" +#include +#include +#include "utility.h" +#include "gamedll.h" +#include "valve_commandline.h" + +class IServerGameDLL; +typedef ICommandLine *(*GetCommandLine)(); + +#define MAX_GAMEDLL_PATHS 10 + +IGameDllBridge* gamedll_bridge = NULL; +static int game_info_detected = 0; +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 IServerGameDLL *gamedll_iface = NULL; +static QueryValveInterface gamedll_qvi = NULL; +static int gamedll_version = 0; + +#if defined _WIN32 +#define TIER0_NAME "bin\\tier0.dll" +#define VSTDLIB_NAME "bin\\vstdlib.dll" +#define SERVER_NAME "server.dll" +#elif defined __linux__ +#define TIER0_NAME "bin/tier0_i486.so" +#define VSTDLIB_NAME "bin/vstdlib_i486.so" +#define SERVER_NAME "server_i486.so" +#endif + +static bool +mm_DetectGameInformation() +{ + void *lib; + char error[255]; + GetCommandLine valve_cmdline; + char mm_path[PLATFORM_MAX_PATH]; + char lib_path[PLATFORM_MAX_PATH]; + char game_name[PLATFORM_MAX_PATH]; + char game_path[PLATFORM_MAX_PATH]; + + if (game_info_detected) + return game_info_detected == 1 ? true : false; + + game_info_detected = -1; + + if (!mm_ResolvePath(TIER0_NAME, lib_path, sizeof(lib_path))) + { + mm_LogFatal("Could not find path for: " TIER0_NAME); + return false; + } + + if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) + { + mm_LogFatal("Could not load %s: %s", lib_path, error); + return false; + } + + valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine_Tier0"); + if (valve_cmdline == NULL) + { + /* We probably have a Ship engine. */ + mm_UnloadLibrary(lib); + if (!mm_ResolvePath(VSTDLIB_NAME, lib_path, sizeof(lib_path))) + { + mm_LogFatal("Could not find path for: " VSTDLIB_NAME); + return false; + } + + if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) + { + mm_LogFatal("Could not load %s: %s", lib_path, error); + return false; + } + + valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine"); + } + + mm_UnloadLibrary(lib); + + if (valve_cmdline == NULL) + { + mm_LogFatal("Could not locate any command line functionality"); + return false; + } + + if (!mm_GetFileOfAddress((void*)mm_DetectGameInformation, mm_path, sizeof(mm_path))) + { + mm_LogFatal("Could not locate metamod loader library path"); + return false; + } + + mm_Format(game_name, + sizeof(game_name), + "%s", + valve_cmdline()->ParmValue("-game", "hl2")); + 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.txt", 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/" SERVER_NAME, lptr); + else + mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s/bin/" 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; + + 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 *isgd_orig_call = NULL; + +class VEmptyClass +{ +}; + +class IServerGameDLL +{ +public: + virtual bool DLLInit(QueryValveInterface engineFactory, + QueryValveInterface physicsFactory, + QueryValveInterface fileSystemFactory, + void *pGlobals) + { + MetamodBackend backend = mm_DetermineBackend(engineFactory); + + char error[255]; + if (backend == MMBackend_UNKNOWN) + { + mm_LogFatal("Could not detect engine version"); + } + else + { + if (!mm_LoadMetamodLibrary(backend, error, sizeof(error))) + { + mm_LogFatal("Detected engine %d but could not load: %s", 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", backend); + } + gamedll_bridge = get_bridge(); + } + } + + 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; + + strcpy(error, "Unknown error"); + if (!gamedll_bridge->DLLInit_Pre(&info, error, sizeof(error))) + { + gamedll_bridge = NULL; + mm_UnloadMetamodLibrary(); + mm_LogFatal("Unknown error loading Metamod for engine %d: %s", backend, error); + } + } + + /* Call the original */ + bool result; + { + union + { + bool (VEmptyClass::*mfpnew)(QueryValveInterface engineFactory, + QueryValveInterface physicsFactory, + QueryValveInterface fileSystemFactory, + void *pGlobals); +#if defined _WIN32 + void *addr; + } u; + u.addr = isgd_orig_call; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = isgd_orig_call; + u.s.adjustor = 0; +#endif + result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(engineFactory, + physicsFactory, + fileSystemFactory, + pGlobals); + } + + /** + * :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(); + } + + mm_PatchDllInit(false); + + return result; + } +}; + +static IServerGameDLL isgd_thunk; + +static void +mm_PatchDllInit(bool patch) +{ + void **vtable_src; + void **vtable_dest; + SourceHook::MemFuncInfo mfp; + + SourceHook::GetFuncInfo(&IServerGameDLL::DLLInit, mfp); + + assert(mfp.isVirtual); + assert(mfp.thisptroffs == 0); + assert(mfp.vtbloffs == 0); + + vtable_src = (void **)*(void **)&isgd_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(isgd_orig_call == NULL); + isgd_orig_call = vtable_dest[mfp.vtblindex]; + vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex]; + } + else + { + assert(isgd_orig_call != NULL); + vtable_dest[mfp.vtblindex] = isgd_orig_call; + isgd_orig_call = NULL; + } +} + +static void +mm_PrepForGameLoad() +{ + mm_PatchDllInit(true); +} + +void * +mm_GameDllRequest(const char *name, int *ret) +{ + if (gamedll_lib != NULL && gamedll_bridge == NULL) + { + mm_LogFatal("Requested unknown interface before game load: %s", name); + return gamedll_qvi(name, ret); + } + + if (strncmp(name, "ServerGameDLL", 13) == 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; + gamedll_iface = (IServerGameDLL *)ptr; + gamedll_qvi = qvi; + gamedll_version = atoi(&name[13]); + mm_PrepForGameLoad(); + + if (ret != NULL) + *ret = 0; + return ptr; + } + } + 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/loader/gamedll.h b/loader/gamedll.h new file mode 100644 index 0000000..ad8bd67 --- /dev/null +++ b/loader/gamedll.h @@ -0,0 +1,12 @@ +#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/loader/loader.cpp b/loader/loader.cpp index b8ab5c9..6d0e618 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -1,10 +1,13 @@ #include -#include +#include #include +#include #include #include #include "loader.h" #include "serverplugin.h" +#include "gamedll.h" +#include "utility.h" static HMODULE mm_library = NULL; static char mm_fatal_logfile[PLATFORM_MAX_PATH] = "metamod-fatal.log"; @@ -34,56 +37,6 @@ mm_LogFatal(const char *message, ...) fclose(fp); } -static 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; -} - -static 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; -} - -static 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); -#else - 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; -} - static const char *backend_names[3] = { "1.ep1", @@ -100,7 +53,7 @@ static const char *backend_names[3] = #endif #if defined _WIN32 -static void +void mm_GetPlatformError(char *buffer, size_t maxlength) { DWORD dw = GetLastError(); @@ -143,35 +96,16 @@ mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength) "metamod.%s" LIBRARY_MINEXT, backend_names[backend]); -#if defined _WIN32 - mm_library = LoadLibrary(mm_path); + mm_library = mm_LoadLibrary(mm_path, buffer, maxlength); - if (mm_library == NULL) - { - mm_GetPlatformError(buffer, maxlength); - return false; - } -#elif defined __linux__ - mm_library = dlopen(mm_path, RTLD_NOW); - - if (mm_library == NULL) - { - mm_Format(buffer, maxlength, "%s", dlerror()); - return false; - } -#endif - - return true; + return (mm_library != NULL); } void mm_UnloadMetamodLibrary() { -#if defined _WIN32 - FreeLibrary(mm_library); -#else - dlclose(mm_library); -#endif + mm_UnloadLibrary(mm_library); + mm_library = NULL; } #if defined _WIN32 @@ -187,35 +121,63 @@ mm_UnloadMetamodLibrary() EXPORT void * CreateInterface(const char *name, int *ret) { - if (mm_library != NULL) + /* If we've got a VSP bridge, do nothing. */ + if (vsp_bridge != NULL) { if (ret != NULL) *ret = 1; return NULL; } + /* If we've got a gamedll bridge, forward the request. */ + if (gamedll_bridge != NULL) + return gamedll_bridge->QueryInterface(name, ret); + + /* Otherwise, we're probably trying to load Metamod. */ void *ptr; if (strncmp(name, "ISERVERPLUGINCALLBACKS", 22) == 0) - { ptr = mm_GetVspCallbacks(atoi(&name[22])); - if (ret != NULL) - *ret = ptr != NULL ? 0 : 1; - return ptr; - } + else + ptr = mm_GameDllRequest(name, ret); if (ret != NULL) - *ret = 1; + *ret = (ptr != NULL) ? 0 : 1; - return NULL; + return ptr; } -extern void * +void * mm_GetProcAddress(const char *name) { -#if defined _WIN32 - return GetProcAddress(mm_library, name); -#elif defined __linux__ - return dlsym(mm_library, name); -#endif + return mm_GetLibAddress(mm_library, name); +} + +MetamodBackend +mm_DetermineBackend(QueryValveInterface engineFactory) +{ + /* Check for L4D */ + if (engineFactory("VEngineServer022", NULL) != NULL && + engineFactory("VEngineCvar007", NULL) != NULL) + { + return MMBackend_Left4Dead; + } + else if (engineFactory("VEngineServer021", NULL) != NULL) + { + /* Check for OB */ + if (engineFactory("VEngineCvar004", NULL) != NULL && + engineFactory("VModelInfoServer002", NULL) != NULL) + { + return MMBackend_Episode2; + } + /* Check for EP1 */ + else if (engineFactory("VModelInfoServer001", NULL) != NULL && + (engineFactory("VEngineCvar003", NULL) != NULL || + engineFactory("VEngineCvar002", NULL) != NULL)) + { + return MMBackend_Episode1; + } + } + + return MMBackend_UNKNOWN; } diff --git a/loader/loader.h b/loader/loader.h index 67446f6..c31a740 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -9,17 +9,26 @@ #include #define PLATFORM_MAX_PATH MAX_PATH #define SH_COMP SH_COMP_MSVC +#define PATH_SEP_STR "\\" +#define PATH_SEP_CHAR '\\' +#define ALT_SEP_CHAR '/' #elif defined __linux__ #include #include #include +#include typedef void * HMODULE; #define PLATFORM_MAX_PATH PATH_MAX #define SH_COMP SH_COMP_GCC +#define PATH_SEP_STR "/" +#define PATH_SEP_CHAR '/' +#define ALT_SEP_CHAR '\\' #else #error "OS detection failed" #endif +#include "loader_bridge.h" + #define SH_PTRSIZE sizeof(void*) enum MetamodBackend @@ -42,5 +51,8 @@ mm_UnloadMetamodLibrary(); extern void mm_LogFatal(const char *message, ...); +extern MetamodBackend +mm_DetermineBackend(QueryValveInterface qvi); + #endif /* _INCLUDE_METAMOD_SOURCE_LOADER_H_ */ diff --git a/loader/loader_bridge.h b/loader/loader_bridge.h index 9368afd..f15a301 100644 --- a/loader/loader_bridge.h +++ b/loader/loader_bridge.h @@ -20,5 +20,25 @@ public: virtual const char *GetDescription() = 0; }; +struct gamedll_bridge_info +{ + QueryValveInterface engineFactory; + QueryValveInterface fsFactory; + QueryValveInterface physicsFactory; + QueryValveInterface gsFactory; + void * pGlobals; + unsigned int dllVersion; + void * isgd; +}; + +class IGameDllBridge +{ +public: + virtual bool DLLInit_Pre(const gamedll_bridge_info *info, char *buffer, size_t maxlength) = 0; + virtual void DLLInit_Post() = 0; + virtual void *QueryInterface(const char *name, int *ret) = 0; + virtual void Unload() = 0; +}; + #endif /* _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_ */ diff --git a/loader/serverplugin.cpp b/loader/serverplugin.cpp index c6d62c4..e52d0f1 100644 --- a/loader/serverplugin.cpp +++ b/loader/serverplugin.cpp @@ -42,13 +42,14 @@ public: } }; +IVspBridge *vsp_bridge = NULL; + /** * The vtable must match the general layout for ISPC. We modify the vtable * based on what we get back. */ class ServerPlugin { - IVspBridge* bridge; unsigned int vsp_version; bool load_allowed; public: @@ -58,39 +59,16 @@ public: } virtual bool Load(QueryValveInterface engineFactory, QueryValveInterface gsFactory) { - MetamodBackend backend = MMBackend_UNKNOWN; - if (!load_allowed) return false; load_allowed = false; - /* Check for L4D */ - if (engineFactory("VEngineServer022", NULL) != NULL && - engineFactory("VEngineCvar007", NULL) != NULL) - { - backend = MMBackend_Left4Dead; - } - else if (engineFactory("VEngineServer021", NULL) != NULL) - { - /* Check for OB */ - if (engineFactory("VEngineCvar004", NULL) != NULL && - engineFactory("VModelInfoServer002", NULL) != NULL) - { - backend = MMBackend_Episode2; - } - /* Check for EP1 */ - else if (engineFactory("VModelInfoServer001", NULL) != NULL && - (engineFactory("VEngineCvar003", NULL) != NULL || - engineFactory("VEngineCvar002", NULL) != NULL)) - { - backend = MMBackend_Episode1; - } - } + MetamodBackend backend = mm_DetermineBackend(engineFactory); if (backend == MMBackend_UNKNOWN) { - mm_LogFatal("Could not detect engine version, spewing stats:"); + mm_LogFatal("Could not detect engine version"); return false; } else if (backend >= MMBackend_Episode2) @@ -134,11 +112,11 @@ public: if (get_bridge == NULL) { mm_UnloadMetamodLibrary(); - mm_LogFatal("Detected engine %d but could not find GetVspBridge callback.", backend); + mm_LogFatal("Detected engine %d but could not find GetVspBridge callback", backend); return false; } - bridge = get_bridge(); + vsp_bridge = get_bridge(); vsp_bridge_info info; @@ -148,8 +126,9 @@ public: info.vsp_version = vsp_version; strcpy(error, "Unknown error"); - if (!bridge->Load(&info, error, sizeof(error))) + if (!vsp_bridge->Load(&info, error, sizeof(error))) { + vsp_bridge = NULL; mm_UnloadMetamodLibrary(); mm_LogFatal("Unknown error loading Metamod for engine %d: %s", backend, error); return false; @@ -159,9 +138,9 @@ public: } virtual void Unload() { - if (bridge == NULL) + if (vsp_bridge == NULL) return; - bridge->Unload(); + vsp_bridge->Unload(); mm_UnloadMetamodLibrary(); } virtual void Pause() @@ -172,9 +151,9 @@ public: } virtual const char *GetPluginDescription() { - if (bridge == NULL) + if (vsp_bridge == NULL) return "Metamod:Source Loader Shim"; - return bridge->GetDescription(); + return vsp_bridge->GetDescription(); } virtual void LevelInit(char const *pMapName) { @@ -232,17 +211,13 @@ public: vsp_version = version; load_allowed = true; } - bool IsLoaded() - { - return bridge != NULL; - } }; ServerPlugin mm_vsp_callbacks; void *mm_GetVspCallbacks(unsigned int version) { - if (mm_vsp_callbacks.IsLoaded()) + if (vsp_bridge != NULL) return NULL; /* Only support versions 1 or 2 right now */ diff --git a/loader/serverplugin.h b/loader/serverplugin.h index 757ad60..45801dd 100644 --- a/loader/serverplugin.h +++ b/loader/serverplugin.h @@ -6,5 +6,7 @@ extern void * mm_GetVspCallbacks(unsigned int version); +extern IVspBridge *vsp_bridge; + #endif /* _INCLUDE_METAMOD_SOURCE_SERVERPLUGINS_H_ */ diff --git a/loader/utility.cpp b/loader/utility.cpp new file mode 100644 index 0000000..95d2553 --- /dev/null +++ b/loader/utility.cpp @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include +#include "loader.h" +#include "utility.h" + +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 >= 0; 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) +{ +#if defined _WIN32 + return _fullpath(buffer, path, maxlength) != NULL; +#elif defined __linux__ + assert(maxlength >= PATH_MAX); + return realpath(path, buffer) != NULL; +#endif +} + +void * +mm_LoadLibrary(const char *path, char *buffer, size_t maxlength) +{ + void *lib; + +#if defined _WIN32 + lib = LoadLibrary(path); + + if (lib == NULL) + { + mm_GetPlatformError(buffer, maxlength); + return NULL; + } +#elif defined __linux__ + 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(lib, name); +#elif defined __linux__ + return dlsym(lib, name); +#endif +} + +void +mm_UnloadLibrary(void *lib) +{ +#if defined _WIN32 + FreeLibrary(lib); +#else + 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__ + 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; +} + diff --git a/loader/utility.h b/loader/utility.h new file mode 100644 index 0000000..e358cdf --- /dev/null +++ b/loader/utility.h @@ -0,0 +1,43 @@ +#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/loader/valve_commandline.h b/loader/valve_commandline.h new file mode 100644 index 0000000..e4e8319 --- /dev/null +++ b/loader/valve_commandline.h @@ -0,0 +1,28 @@ +#ifndef _INCLUDE_VALVE_COMMAND_LINE_H_ +#define _INCLUDE_VALVE_COMMAND_LINE_H_ + +class ICommandLine +{ +public: + virtual void CreateCmdLine( const char *commandline ) = 0; + virtual void CreateCmdLine( int argc, char **argv ) = 0; + virtual const char *GetCmdLine( void ) const = 0; + + // Check whether a particular parameter exists + virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const = 0; + virtual void RemoveParm( const char *parm ) = 0; + virtual void AppendParm( const char *pszParm, const char *pszValues ) = 0; + + // Returns the argument after the one specified, or the default if not found + virtual const char *ParmValue( const char *psz, const char *pDefaultVal = 0 ) const = 0; + virtual int ParmValue( const char *psz, int nDefaultVal ) const = 0; + virtual float ParmValue( const char *psz, float flDefaultVal ) const = 0; + + // Gets at particular parameters + virtual int ParmCount() const = 0; + virtual int FindParm( const char *psz ) const = 0; // Returns 0 if not found. + virtual const char* GetParm( int nIndex ) const = 0; +}; + +#endif /* _INCLUDE_VALVE_COMMAND_LINE_H_ */ +