1
0
mirror of https://github.com/alliedmodders/metamod-source.git synced 2024-11-29 11:24:19 +01:00
This commit is contained in:
Nicholas Hastings 2015-07-08 21:39:28 -04:00
parent d735476d69
commit fcb4849b3c
20 changed files with 1864 additions and 31 deletions

View File

@ -38,6 +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'),
's2': SDK('HL2SDKS2', '2.s2', '22', 'SOURCE2', WinOnly, 's2'),
}
def ResolveEnvPath(env, folder):
@ -112,6 +113,9 @@ class MMSConfig(object):
cfg = builder.DetectCompilers()
cxx = cfg.cxx
if builder.options.source2 == '1':
cfg.defines += ['SOURCE2_BUILD=1']
if cxx.behavior == 'gcc':
cfg.defines += [
'stricmp=strcasecmp',
@ -246,7 +250,7 @@ class MMSConfig(object):
compiler.cxxincludes += [
os.path.join(context.currentSourcePath),
os.path.join(context.currentSourcePath, 'sourcehook'),
os.path.join(context.sourcePath, 'loader'),
os.path.join(context.sourcePath, 'public', 'loader'),
]
defines = ['SE_' + self.sdks[i].define + '=' + self.sdks[i].code for i in self.sdks]
@ -278,13 +282,13 @@ class MMSConfig(object):
else:
compiler.defines += ['COMPILER_GCC']
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota']:
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota', 's2']:
if builder.target_platform in ['linux', 'mac']:
compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE']
if sdk.name == 'csgo' and builder.target_platform == 'linux':
compiler.linkflags += ['-lstdc++']
for path in paths:
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
@ -335,7 +339,7 @@ class MMSConfig(object):
else:
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a'))]
if sdk.name in ['blade', 'insurgency', 'csgo', 'dota']:
if sdk.name in ['blade', 'insurgency', 'csgo', 'dota', 's2']:
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))]
binary = self.LibraryBuilder(compiler, name)
@ -345,7 +349,7 @@ class MMSConfig(object):
compiler.linkflags[0:0] = ['-lm']
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2']:
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
elif sdk.name in ['l4d', 'blade', 'insurgency', 'csgo', 'dota']:
elif sdk.name in ['l4d', 'blade', 'insurgency', 'csgo', 'dota', 's2']:
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
else:
dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so']
@ -354,7 +358,7 @@ class MMSConfig(object):
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
elif builder.target_platform == 'windows':
libs = ['tier0', 'tier1', 'vstdlib']
if sdk.name in ['swarm', 'blade', 'insurgency', 'csgo', 'dota']:
if sdk.name in ['swarm', 'blade', 'insurgency', 'csgo', 'dota', 's2']:
libs.append('interfaces')
for lib in libs:
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
@ -391,9 +395,10 @@ if MMS.use_auto_versioning():
)
BuildScripts = [
'loader/AMBuilder',
'core-legacy/AMBuilder',
'core/AMBuilder',
# 'loader/AMBuilder',
'loader2/AMBuilder',
'core-legacy/AMBuilder',
'core/AMBuilder',
]
if builder.backend == 'amb2':

View File

@ -23,4 +23,6 @@ run.options.add_option('--enable-optimize', action='store_const', const='1', des
run.options.add_option('-s', '--sdks', default='all', dest='sdks',
help='Build against specified SDKs; valid args are "all", "present", or '
'comma-delimited list of engine names (default: %default)')
run.options.add_option('--source2-build', action='store_const', const='1', dest='source2',
help='Build loader for Source2')
run.Configure()

View File

@ -59,6 +59,7 @@
#define SOURCE_ENGINE_INSURGENCY 21 /**< Insurgency */
#define SOURCE_ENGINE_CONTAGION 22 /**< Contagion */
#define SOURCE_ENGINE_BMS 23 /**< Black Mesa Multiplayer */
#define SOURCE_ENGINE_SOURCE2 24
#define METAMOD_PLAPI_VERSION 15 /**< Version of this header file */
#define METAMOD_PLAPI_NAME "ISmmPlugin" /**< Name of the plugin interface */

View File

@ -63,7 +63,11 @@ public:
SourceHook::MemFuncInfo mfi;
mfi.isVirtual = false;
#if SOURCE_ENGINE == SE_SOURCE2
SourceHook::GetFuncInfo(&IServerGameDLL::Shutdown, mfi);
#else
SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfi);
#endif
assert(mfi.isVirtual);
assert(mfi.vtbloffs == 0);
assert(mfi.thisptroffs == 0);

View File

@ -98,7 +98,6 @@ static ConVar *mm_basedir = NULL;
static CreateInterfaceFn engine_factory = NULL;
static CreateInterfaceFn physics_factory = NULL;
static CreateInterfaceFn filesystem_factory = NULL;
static CGlobalVars *gpGlobals = NULL;
static CHookManagerAutoGen g_SH_HookManagerAutoGen(&g_SourceHook);
static META_RES last_meta_res;
static IServerPluginCallbacks *vsp_callbacks = NULL;
@ -143,6 +142,21 @@ SourceMM::ISmmAPI *g_pMetamod = &g_Metamod;
} \
}
#if SOURCE_ENGINE == SE_SOURCE2
void meta_game_init(const CCommand &args)
{
Handler_GameInit();
}
void meta_level_init(const CCommand &args)
{
Handler_LevelInit("dummy_level", "", "", "", false, false);
}
void meta_level_shutdown(const CCommand &args)
{
Handler_LevelShutdown();
}
#endif
/* Initialize everything here */
void
mm_InitializeForLoad()
@ -157,6 +171,7 @@ mm_InitializeForLoad()
*/
in_first_level = true;
#if SOURCE_ENGINE != SE_SOURCE2
SourceHook::MemFuncInfo info;
if (!provider->GetHookInfo(ProvidedHook_GameInit, &info))
@ -179,6 +194,7 @@ mm_InitializeForLoad()
}
SH_MANUALHOOK_RECONFIGURE(SGD_LevelShutdown, info.vtblindex, info.vtbloffs, info.thisptroffs);
SH_ADD_MANUALHOOK_STATICFUNC(SGD_LevelShutdown, server, Handler_LevelShutdown, true);
#endif
}
bool
@ -411,6 +427,15 @@ DoInitialPluginLoads()
mm_LoadPlugins(filepath, vdfpath);
}
ConVar *net_maxroutable;
ConCommand *map;
extern ConCommand meta_local_cmd;
CON_COMMAND(meta_test, "")
{
Msg("hi\n");
}
void
mm_StartupMetamod(bool is_vsp_load)
{
@ -422,6 +447,17 @@ mm_StartupMetamod(bool is_vsp_load)
METAMOD_VERSION,
is_vsp_load ? "V" : "");
net_maxroutable = g_pCVar->FindVar("net_maxroutable");
map = g_pCVar->FindCommand("logaddress_add");
meta_local_cmd.AddFlags(0);
meta_test_command.AddFlags(0);
CCommand cmd;
cmd.Tokenize("logaddress_add 192.168.5.9:33333");
map->Dispatch(CCommandContext(0), cmd);
Msg("logaddress_add is at 0x%p\n", map);
Msg("meta_local_cmd is at 0x%p\n", &meta_local_cmd);
metamod_version = provider->CreateConVar("metamod_version",
METAMOD_VERSION,
"Metamod:Source Version",
@ -773,7 +809,7 @@ size_t MetamodSource::PathFormat(char *buffer, size_t len, const char *fmt, ...)
return mylen;
}
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void MetamodSource::ClientConPrintf(int clientIndex, const char *fmt, ...)
{
va_list ap;

View File

@ -76,7 +76,7 @@ public:
void *InterfaceSearch(CreateInterfaceFn fn, const char *iface, int max, int *ret);
const char *GetBaseDir();
size_t PathFormat(char *buffer, size_t len, const char *fmt, ...);
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
// Shim
void ClientConPrintf(int clientIndex, const char *fmt, ...);
#endif

View File

@ -44,7 +44,7 @@ using namespace SourceHook;
#define CLIENT_CONMSG g_Metamod.ClientConPrintf
template <typename ... Ts>
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void CMDMSG(int client, const char *pMsg, Ts ... ts)
#else
void CMDMSG(edict_t *client, const char *pMsg, Ts ... ts)
@ -100,11 +100,13 @@ void CMDMSG(edict_t *client, const char *pMsg, Ts ... ts)
#define MMS_ENGINE_NAME "Counter-Strike: Global Offensive (2012)"
#elif SOURCE_ENGINE == SE_DOTA
#define MMS_ENGINE_NAME "Dota 2 (2013)"
#elif SOURCE_ENGINE == SE_SOURCE2
#define MMS_ENGINE_NAME "Source 2"
#else
#error "SOURCE_ENGINE not defined to a known value"
#endif
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
static void ReplyCredits(int client = 0)
#else
static void ReplyCredits(edict_t *client = nullptr)
@ -118,7 +120,7 @@ static void ReplyCredits(edict_t *client = nullptr)
CMDMSG(client, "http://www.metamodsource.net/\n");
}
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
static void ReplyVersion(int client = 0)
#else
static void ReplyVersion(edict_t *client = nullptr)
@ -698,7 +700,7 @@ bool Command_Meta(IMetamodSourceCommandInfo *info)
return true;
}
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
bool Command_ClientMeta(int client, IMetamodSourceCommandInfo *info)
#else
bool Command_ClientMeta(edict_t *client, IMetamodSourceCommandInfo *info)

View File

@ -31,7 +31,7 @@
#include "metamod_provider.h"
bool Command_Meta(IMetamodSourceCommandInfo *info);
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
bool Command_ClientMeta(int client, IMetamodSourceCommandInfo *info);
#else
bool Command_ClientMeta(edict_t *client, IMetamodSourceCommandInfo *info);

View File

@ -62,9 +62,18 @@ DLL_IMPORT ICommandLine *CommandLine();
/* Functions */
void CacheUserMessages();
void Detour_Error(const tchar *pMsg, ...);
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void ClientCommand(CEntityIndex index, const CCommand &args);
#if SOURCE_ENGINE == SE_DOTA
void LocalCommand_Meta(const CCommandContext &context, const CCommand &args);
#else
void LocalCommand_Meta(const CCommand &args);
#endif
#if SOURCE_ENGINE == SE_SOURCE2
void meta_game_init(const CCommand &args);
void meta_level_init(const CCommand &args);
void meta_level_shutdown(const CCommand &args);
#endif
#elif SOURCE_ENGINE >= SE_ORANGEBOX
void ClientCommand(edict_t *pEdict, const CCommand &args);
void LocalCommand_Meta(const CCommand &args);
@ -83,12 +92,21 @@ static jmp_buf usermsg_end;
ICvar *icvar = NULL;
IFileSystem *baseFs = NULL;
IServerGameDLL *server = NULL;
#if SOURCE_ENGINE == SE_SOURCE2
static ISource2ServerConfig *serverconfig = NULL;
#endif
IVEngineServer *engine = NULL;
IServerGameClients *gameclients = NULL;
CGlobalVars *gpGlobals = NULL;
IMetamodSourceProvider *provider = &g_Ep1Provider;
ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options");
#if SOURCE_ENGINE == SE_SOURCE2
ConCommand _meta_game_init("meta_game_init", meta_game_init);
ConCommand _meta_level_init("meta_level_init", meta_level_init);
ConCommand _meta_level_shutdown("meta_level_shutdown", meta_level_shutdown);
#endif
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CEntityIndex, const CCommand &);
#elif SOURCE_ENGINE >= SE_ORANGEBOX
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &);
@ -114,6 +132,10 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
DisplayError("Could not find IVEngineServer! Metamod cannot load.");
return;
}
#if SOURCE_ENGINE == SE_SOURCE2
gpGlobals = engine->GetServerGlobals();
serverconfig = (ISource2ServerConfig *) ((serverFactory) (INTERFACEVERSION_SERVERCONFIG, NULL));
#endif
#if SOURCE_ENGINE >= SE_ORANGEBOX
icvar = (ICvar *)((engineFactory)(CVAR_INTERFACE_VERSION, NULL));
#else
@ -125,12 +147,15 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
return;
}
#if SOURCE_ENGINE == SE_SOURCE2
gameclients = (IServerGameClients *)(serverFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL));
#else
if ((gameclients = (IServerGameClients *)(serverFactory("ServerGameClients003", NULL)))
== NULL)
{
gameclients = (IServerGameClients *)(serverFactory("ServerGameClients004", NULL));
}
#endif
baseFs = (IFileSystem *)((engineFactory)(FILESYSTEM_INTERFACE_VERSION, NULL));
if (baseFs == NULL)
@ -142,7 +167,8 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
g_pCVar = icvar;
#endif
g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd);
ConVar_Register(0, &g_SMConVarAccessor);
//g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd);
CacheUserMessages();
@ -163,6 +189,7 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
void BaseProvider::Notify_DLLShutdown_Pre()
{
g_SMConVarAccessor.RemoveMetamodCommands();
#if SOURCE_ENGINE == SE_DARKMESSIAH
@ -180,8 +207,8 @@ bool BaseProvider::IsRemotePrintingAvailable()
void BaseProvider::ClientConsolePrint(edict_t *pEdict, const char *message)
{
#if SOURCE_ENGINE == SE_DOTA
int client = (int)(pEdict - g_Metamod.GetCGlobals()->pEdicts);
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
int client = (int)(pEdict - gpGlobals->pEdicts);
engine->ClientPrintf(client, message);
#else
engine->ClientPrintf(pEdict, message);
@ -264,6 +291,7 @@ bool BaseProvider::LogMessage(const char *buffer)
bool BaseProvider::GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo)
{
#if SOURCE_ENGINE != SE_SOURCE2
SourceHook::MemFuncInfo mfi = {true, -1, 0, 0};
if (hook == ProvidedHook_LevelInit)
@ -282,6 +310,9 @@ bool BaseProvider::GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInf
*pInfo = mfi;
return (mfi.thisptroffs >= 0);
#else
return false;
#endif
}
void BaseProvider::DisplayError(const char *fmt, ...)
@ -293,7 +324,11 @@ void BaseProvider::DisplayError(const char *fmt, ...)
UTIL_FormatArgs(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
#if SOURCE_ENGINE == SE_SOURCE2
Msg("ERROR: %s", buffer);
#else
Error("%s", buffer);
#endif
}
void BaseProvider::DisplayWarning(const char *fmt, ...)
@ -325,7 +360,7 @@ void BaseProvider::UnregisterConCommandBase(ConCommandBase *pCommand)
int BaseProvider::GetUserMessageCount()
{
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
return -1;
#else
return (int)usermsgs_list.size();
@ -371,7 +406,7 @@ void BaseProvider::GetGamePath(char *pszBuffer, int len)
const char *BaseProvider::GetGameDescription()
{
return server->GetGameDescription();
return serverconfig->GetGameDescription();
}
int BaseProvider::DetermineSourceEngine()
@ -416,6 +451,8 @@ int BaseProvider::DetermineSourceEngine()
return SOURCE_ENGINE_DOTA;
#elif SOURCE_ENGINE == SE_BMS
return SOURCE_ENGINE_BMS;
#elif SOURCE_ENGINE == SE_SOURCE2
return SOURCE_ENGINE_SOURCE2;
#else
#error "SOURCE_ENGINE not defined to a known value"
#endif
@ -529,7 +566,7 @@ public:
};
#endif
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA// || SOURCE_ENGINE == SE_SOURCE2
void LocalCommand_Meta(const CCommandContext &context, const CCommand &args)
{
GlobCommand cmd(&args);
@ -545,7 +582,7 @@ void LocalCommand_Meta()
Command_Meta(&cmd);
}
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void ClientCommand(CEntityIndex index, const CCommand &_cmd)
{
int client = index.Get();
@ -568,7 +605,7 @@ void ClientCommand(edict_t *client)
RETURN_META(MRES_IGNORED);
}
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void CacheUserMessages()
{

View File

@ -83,6 +83,7 @@ extern IVEngineServer *engine;
extern IServerGameDLL *server;
extern IServerGameClients *gameclients;
extern ICvar *icvar;
extern CGlobalVars *gpGlobals;
#endif //_INCLUDE_METAMOD_SOURCE_BASE_PROVIDER_H_

View File

@ -46,7 +46,7 @@ ConCommand *g_plugin_unload = NULL;
bool g_bIsTryingToUnload;
SourceHook::String vsp_desc("Metamod:Source");
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void InterceptPluginUnloads(const CCommandContext &context, const CCommand &args)
#elif SOURCE_ENGINE >= SE_ORANGEBOX
void InterceptPluginUnloads(const CCommand &args)
@ -57,7 +57,7 @@ void InterceptPluginUnloads()
g_bIsTryingToUnload = true;
}
#if SOURCE_ENGINE == SE_DOTA
#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2
void InterceptPluginUnloads_Post(const CCommandContext &context, const CCommand &args)
#elif SOURCE_ENGINE >= SE_ORANGEBOX
void InterceptPluginUnloads_Post(const CCommand &args)
@ -164,11 +164,14 @@ public:
virtual void Unload()
{
// Source2 doesn't have the Error function (nor VSP support).
#if SOURCE_ENGINE != SE_SOURCE2
if (g_bIsTryingToUnload)
{
Error("Metamod:Source cannot be unloaded from VSP mode. Use \"meta unload\" to unload specific plugins.\n");
return;
}
#endif
if (g_plugin_unload != NULL)
{
SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads, false);

20
loader2/AMBuilder Normal file
View File

@ -0,0 +1,20 @@
# 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"'])

607
loader2/gamedll.cpp Normal file
View File

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

39
loader2/gamedll.h Normal file
View File

@ -0,0 +1,39 @@
/**
* 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_ */

232
loader2/loader2.cpp Normal file
View File

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

100
loader2/loader2.h Normal file
View File

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

546
loader2/utility.cpp Normal file
View File

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

70
loader2/utility.h Normal file
View File

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

70
public/loader_bridge.h Normal file
View File

@ -0,0 +1,70 @@
/**
* vim: set ts=4 sw=4 tw=99 noet :
* ======================================================
* Metamod:Source
* Copyright (C) 2004-2009 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_BRIDGE_H_
#define _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_
typedef void* (*QueryValveInterface)(const char *pName, int *pReturnCode);
class IServerPluginCallbacks;
struct vsp_bridge_info
{
QueryValveInterface engineFactory;
QueryValveInterface gsFactory;
IServerPluginCallbacks * vsp_callbacks;
unsigned int vsp_version;
};
class IVspBridge
{
public:
virtual bool Load(const vsp_bridge_info *info, char *buffer, size_t maxlength) = 0;
virtual void Unload() = 0;
virtual const char *GetDescription() = 0;
};
struct gamedll_bridge_info
{
QueryValveInterface engineFactory;
QueryValveInterface fsFactory;
QueryValveInterface physicsFactory;
QueryValveInterface gsFactory;
void * pGlobals;
unsigned int dllVersion;
void * isgd;
const char * vsp_listener_path;
};
class IGameDllBridge
{
public:
virtual bool DLLInit_Pre(const gamedll_bridge_info *info, char *buffer, size_t maxlength) = 0;
virtual void DLLInit_Post(int *isgdUnload) = 0;
virtual void *QueryInterface(const char *name, int *ret) = 0;
virtual void Unload() = 0;
};
#endif /* _INCLUDE_METAMOD_SOURCE_LOADER_BRIDGE_H_ */

58
public/loader_public.h Normal file
View File

@ -0,0 +1,58 @@
/**
* 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_