mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2025-02-20 13:54:14 +01:00
Initial import of gameinfo loader.
This commit is contained in:
parent
ce7a3beab0
commit
ad6f271a71
@ -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 ###
|
||||
|
436
loader/gamedll.cpp
Normal file
436
loader/gamedll.cpp
Normal file
@ -0,0 +1,436 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "loader.h"
|
||||
#include <sh_memfuncinfo.h>
|
||||
#include <sh_memory.h>
|
||||
#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;
|
||||
}
|
||||
|
12
loader/gamedll.h
Normal file
12
loader/gamedll.h
Normal file
@ -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_ */
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
@ -9,17 +9,26 @@
|
||||
#include <windows.h>
|
||||
#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 <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
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_ */
|
||||
|
||||
|
@ -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_ */
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -6,5 +6,7 @@
|
||||
extern void *
|
||||
mm_GetVspCallbacks(unsigned int version);
|
||||
|
||||
extern IVspBridge *vsp_bridge;
|
||||
|
||||
#endif /* _INCLUDE_METAMOD_SOURCE_SERVERPLUGINS_H_ */
|
||||
|
||||
|
303
loader/utility.cpp
Normal file
303
loader/utility.cpp
Normal file
@ -0,0 +1,303 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
||||
|
43
loader/utility.h
Normal file
43
loader/utility.h
Normal file
@ -0,0 +1,43 @@
|
||||
#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_ */
|
||||
|
28
loader/valve_commandline.h
Normal file
28
loader/valve_commandline.h
Normal file
@ -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_ */
|
||||
|
Loading…
x
Reference in New Issue
Block a user