mirror of
https://github.com/alliedmodders/metamod-source.git
synced 2024-11-29 11:24:19 +01:00
S2 WiP
This commit is contained in:
parent
d735476d69
commit
fcb4849b3c
@ -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':
|
||||
|
@ -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()
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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_
|
||||
|
||||
|
@ -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
20
loader2/AMBuilder
Normal 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
607
loader2/gamedll.cpp
Normal 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
39
loader2/gamedll.h
Normal 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
232
loader2/loader2.cpp
Normal 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
100
loader2/loader2.h
Normal 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
546
loader2/utility.cpp
Normal 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
70
loader2/utility.h
Normal 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
70
public/loader_bridge.h
Normal 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
58
public/loader_public.h
Normal 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_
|
Loading…
Reference in New Issue
Block a user