faluco@1557: /**
ds@2860: * vim: set ts=4 sw=4 tw=99 noet :
faluco@1557: * =============================================================================
faluco@1557: * SourceMod
ds@3099: * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
faluco@1557: * =============================================================================
faluco@1557: *
faluco@1557: * This program is free software; you can redistribute it and/or modify it under
faluco@1557: * the terms of the GNU General Public License, version 3.0, as published by the
faluco@1557: * Free Software Foundation.
faluco@1557: *
faluco@1557: * This program is distributed in the hope that it will be useful, but WITHOUT
faluco@1557: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
faluco@1557: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
faluco@1557: * details.
faluco@1557: *
faluco@1557: * You should have received a copy of the GNU General Public License along with
faluco@1557: * this program. If not, see .
faluco@1557: *
faluco@1557: * As a special exception, AlliedModders LLC gives you permission to link the
faluco@1557: * code of this program (as well as its derivative works) to "Half-Life 2," the
faluco@1557: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
faluco@1557: * by the Valve Corporation. You must obey the GNU General Public License in
faluco@1557: * all respects for all other code used. Additionally, AlliedModders LLC grants
faluco@1557: * this exception to all derivative works. AlliedModders LLC defines further
faluco@1557: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
faluco@1557: * or .
faluco@1557: */
faluco@1557:
faluco@1557: #include
faluco@1557: #include
faluco@1557: #include
faluco@1557: #include
dvander@2471: #include
faluco@1557:
faluco@1557: #if defined _MSC_VER
faluco@1557: #define DLL_EXPORT extern "C" __declspec(dllexport)
faluco@1557: #define openlib(lib) LoadLibrary(lib)
faluco@1557: #define closelib(lib) FreeLibrary(lib)
faluco@1557: #define findsym(lib, sym) GetProcAddress(lib, sym)
faluco@1557: #define PLATFORM_EXT ".dll"
faluco@1557: #define vsnprintf _vsnprintf
faluco@1557: #define PATH_SEP_CHAR "\\"
faluco@1557: inline bool IsPathSepChar(char c)
faluco@1557: {
faluco@1557: return (c == '/' || c == '\\');
faluco@1557: }
faluco@1557: #include
faluco@1557: #else
faluco@1557: #define DLL_EXPORT extern "C" __attribute__((visibility("default")))
faluco@1557: #define openlib(lib) dlopen(lib, RTLD_NOW)
faluco@1557: #define closelib(lib) dlclose(lib)
faluco@1557: #define findsym(lib, sym) dlsym(lib, sym)
ds@2997: #if defined __linux__
faluco@1557: #define PLATFORM_EXT ".so"
ds@2997: #elif defined __APPLE__
ds@2997: #define PLATFORM_EXT ".dylib"
ds@2997: #endif
faluco@1557: typedef void * HINSTANCE;
faluco@1557: #define PATH_SEP_CHAR "/"
faluco@1557: inline bool IsPathSepChar(char c)
faluco@1557: {
faluco@1557: return (c == '/');
faluco@1557: }
faluco@1557: #include
faluco@1557: #endif
faluco@1557:
faluco@1557: #define METAMOD_API_MAJOR 2
faluco@1557: #define FILENAME_1_4_EP1 "sourcemod.1.ep1" PLATFORM_EXT
faluco@1557: #define FILENAME_1_6_EP2 "sourcemod.2.ep2" PLATFORM_EXT
faluco@1557: #define FILENAME_1_6_EP1 "sourcemod.2.ep1" PLATFORM_EXT
ds@2583: #define FILENAME_1_6_L4D "sourcemod.2.l4d" PLATFORM_EXT
ds@2583: #define FILENAME_1_6_DARKM "sourcemod.2.darkm" PLATFORM_EXT
fyrenmoo@2847: #define FILENAME_1_6_L4D2 "sourcemod.2.l4d2" PLATFORM_EXT
ds@3099: #define FILENAME_1_6_SWARM "sourcemod.2.swarm" PLATFORM_EXT
skamonkey@3180: #define FILENAME_1_6_BGT "sourcemod.2.bgt" PLATFORM_EXT
skamonkey@3373: #define FILENAME_1_6_EYE "sourcemod.2.eye" PLATFORM_EXT
skamonkey@3515: #define FILENAME_1_6_PORTAL2 "sourcemod.2.portal2" PLATFORM_EXT
skamonkey@3515: #define FILENAME_1_6_CSGO "sourcemod.2.csgo" PLATFORM_EXT
skamonkey@3613: #define FILENAME_1_6_CSS "sourcemod.2.css" PLATFORM_EXT
skamonkey@3961: #define FILENAME_1_6_HL2DM "sourcemod.2.hl2dm" PLATFORM_EXT
skamonkey@3961: #define FILENAME_1_6_DODS "sourcemod.2.dods" PLATFORM_EXT
skamonkey@4141: #define FILENAME_1_6_SDK2013 "sourcemod.2.sdk2013" PLATFORM_EXT
skamonkey@3961: #define FILENAME_1_6_TF2 "sourcemod.2.tf2" PLATFORM_EXT
skamonkey@3961: #define FILENAME_1_6_ND "sourcemod.2.nd" PLATFORM_EXT
skamonkey@3853: #define FILENAME_1_6_DOTA "sourcemod.2.dota" PLATFORM_EXT
skamonkey@4142: #define FILENAME_1_6_BLADE "sourcemod.2.blade" PLATFORM_EXT
skamonkey@4144: #define FILENAME_1_6_INSURGENCY "sourcemod.2.insurgency" PLATFORM_EXT
skamonkey@4301: #define FILENAME_1_6_CONTAGION "sourcemod.2.contagion" PLATFORM_EXT
faluco@1557:
faluco@1557: HINSTANCE g_hCore = NULL;
dvander@1561: bool load_attempted = false;
faluco@1557:
dvander@1840: size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
dvander@1840:
dvander@1840: class FailPlugin : public SourceMM::ISmmFailPlugin
dvander@1840: {
dvander@1840: public:
dvander@1840: int GetApiVersion()
dvander@1840: {
dvander@1840: return fail_version;
dvander@1840: }
dvander@1840:
dvander@1840: bool Load(SourceMM::PluginId id, SourceMM::ISmmAPI *ismm, char *error, size_t maxlength, bool late)
dvander@1840: {
dvander@1840: if (error != NULL && maxlength != 0)
dvander@1840: {
dvander@1840: UTIL_Format(error, maxlength, "%s", error_buffer);
dvander@1840: }
dvander@1840: return false;
dvander@1840: }
dvander@1840:
dvander@1840: int fail_version;
dvander@1840: char error_buffer[512];
dvander@1840: } s_FailPlugin;
dvander@1840:
faluco@1557: size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
faluco@1557: {
faluco@1557: va_list ap;
faluco@1557:
faluco@1557: va_start(ap, fmt);
faluco@1557: size_t len = vsnprintf(buffer, maxlength, fmt, ap);
faluco@1557: va_end(ap);
faluco@1557:
faluco@1557: if (len >= maxlength)
faluco@1557: {
faluco@1557: len = maxlength - 1;
faluco@1557: buffer[len] = '\0';
faluco@1557: }
faluco@1557:
faluco@1557: return len;
faluco@1557: }
faluco@1557:
dvander@1840: METAMOD_PLUGIN *_GetPluginPtr(const char *path, int fail_api)
faluco@1557: {
faluco@1557: METAMOD_FN_ORIG_LOAD fn;
faluco@1557: METAMOD_PLUGIN *pl;
faluco@1557: int ret;
faluco@1557:
faluco@1557: if (!(g_hCore=openlib(path)))
faluco@1557: {
dvander@2471: #if defined __linux__ || defined __APPLE__
dvander@1840: UTIL_Format(s_FailPlugin.error_buffer,
dvander@1840: sizeof(s_FailPlugin.error_buffer),
dvander@1840: "%s",
dvander@1840: dlerror());
dvander@1840: #else
dvander@1840: DWORD err = GetLastError();
dvander@1840:
dvander@1840: if (FormatMessageA(
dvander@1840: FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
dvander@1840: NULL,
dvander@1840: err,
dvander@1840: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
dvander@1840: s_FailPlugin.error_buffer,
dvander@1840: sizeof(s_FailPlugin.error_buffer),
dvander@1840: NULL)
dvander@1840: == 0)
dvander@1840: {
dvander@1840: UTIL_Format(s_FailPlugin.error_buffer,
dvander@1840: sizeof(s_FailPlugin.error_buffer),
dvander@1840: "unknown error %x",
dvander@1840: err);
dvander@1840: }
dvander@1840: #endif
dvander@1840:
dvander@1840: s_FailPlugin.fail_version = fail_api;
dvander@1840:
dvander@1840: return (METAMOD_PLUGIN *)&s_FailPlugin;
faluco@1557: }
faluco@1557:
faluco@1557: if (!(fn=(METAMOD_FN_ORIG_LOAD)findsym(g_hCore, "CreateInterface")))
faluco@1557: {
faluco@1557: goto error;
faluco@1557: }
faluco@1557:
faluco@1557: pl = (METAMOD_PLUGIN *)fn(METAMOD_PLAPI_NAME, &ret);
faluco@1557: if (!pl)
faluco@1557: {
faluco@1557: goto error;
faluco@1557: }
faluco@1557:
faluco@1557: return pl;
faluco@1557: error:
faluco@1557: closelib(g_hCore);
faluco@1557: g_hCore = NULL;
faluco@1557: return NULL;
faluco@1557: }
faluco@1557:
faluco@1557: bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)
faluco@1557: {
faluco@1557: #if defined _MSC_VER
faluco@1557: MEMORY_BASIC_INFORMATION mem;
faluco@1557: if (!VirtualQuery(pAddr, &mem, sizeof(mem)))
faluco@1557: return false;
faluco@1557: if (mem.AllocationBase == NULL)
faluco@1557: return false;
faluco@1557: HMODULE dll = (HMODULE)mem.AllocationBase;
faluco@1557: GetModuleFileName(dll, (LPTSTR)buffer, maxlength);
faluco@1557: #else
faluco@1557: Dl_info info;
faluco@1557: if (!dladdr(pAddr, &info))
faluco@1557: return false;
faluco@1557: if (!info.dli_fbase || !info.dli_fname)
faluco@1557: return false;
faluco@1557: const char *dllpath = info.dli_fname;
faluco@1557: snprintf(buffer, maxlength, "%s", dllpath);
faluco@1557: #endif
faluco@1557: return true;
faluco@1557: }
faluco@1557:
faluco@1557: DLL_EXPORT METAMOD_PLUGIN *CreateInterface_MMS(const MetamodVersionInfo *mvi, const MetamodLoaderInfo *mli)
faluco@1557: {
fyrenmoo@2982: const char *filename;
dvander@1561:
dvander@1561: load_attempted = true;
faluco@1557:
faluco@1557: if (mvi->api_major > METAMOD_API_MAJOR)
faluco@1557: {
faluco@1557: return NULL;
faluco@1557: }
faluco@1557:
faluco@1557: switch (mvi->source_engine)
faluco@1557: {
ds@2583: case SOURCE_ENGINE_ORIGINAL:
ds@2583: case SOURCE_ENGINE_EPISODEONE:
ds@2583: {
ds@2583: filename = FILENAME_1_6_EP1;
ds@2583: break;
ds@2583: }
faluco@1557: case SOURCE_ENGINE_ORANGEBOX:
faluco@1557: {
faluco@1557: filename = FILENAME_1_6_EP2;
faluco@1557: break;
faluco@1557: }
ds@2583: case SOURCE_ENGINE_LEFT4DEAD:
faluco@1557: {
ds@2583: filename = FILENAME_1_6_L4D;
faluco@1557: break;
faluco@1557: }
ds@2583: case SOURCE_ENGINE_DARKMESSIAH:
ds@2400: {
ds@2583: filename = FILENAME_1_6_DARKM;
ds@2400: break;
ds@2400: }
fyrenmoo@2847: case SOURCE_ENGINE_LEFT4DEAD2:
fyrenmoo@2847: {
skamonkey@4006: const char *gamedir = mvi->GetGameDir();
skamonkey@4006: if (strcmp(gamedir, "nucleardawn") == 0)
skamonkey@4006: {
skamonkey@4006: filename = FILENAME_1_6_ND;
skamonkey@4006: }
skamonkey@4006: else
skamonkey@4006: {
skamonkey@4006: filename = FILENAME_1_6_L4D2;
skamonkey@4006: }
fyrenmoo@2847: break;
fyrenmoo@2847: }
skamonkey@3961: case SOURCE_ENGINE_NUCLEARDAWN:
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_ND;
skamonkey@3961: break;
skamonkey@3961: }
skamonkey@4301: case SOURCE_ENGINE_CONTAGION:
skamonkey@4301: {
skamonkey@4301: filename = FILENAME_1_6_CONTAGION;
skamonkey@4301: break;
skamonkey@4301: }
ds@3099: case SOURCE_ENGINE_ALIENSWARM:
ds@3099: {
ds@3099: filename = FILENAME_1_6_SWARM;
ds@3099: break;
ds@3099: }
skamonkey@3180: case SOURCE_ENGINE_BLOODYGOODTIME:
skamonkey@3180: {
skamonkey@3180: filename = FILENAME_1_6_BGT;
skamonkey@3180: break;
skamonkey@3180: }
skamonkey@3373: case SOURCE_ENGINE_EYE:
skamonkey@3373: {
skamonkey@3373: filename = FILENAME_1_6_EYE;
skamonkey@3373: break;
skamonkey@3373: }
skamonkey@3515: case SOURCE_ENGINE_PORTAL2:
skamonkey@3515: {
skamonkey@3515: filename = FILENAME_1_6_PORTAL2;
skamonkey@3515: break;
skamonkey@3515: }
skamonkey@3515: case SOURCE_ENGINE_CSGO:
skamonkey@3515: {
skamonkey@3515: filename = FILENAME_1_6_CSGO;
skamonkey@3515: break;
skamonkey@3515: }
skamonkey@3613: case SOURCE_ENGINE_CSS:
skamonkey@3613: {
skamonkey@3613: filename = FILENAME_1_6_CSS;
skamonkey@3613: break;
skamonkey@3613: }
skamonkey@3961: case SOURCE_ENGINE_HL2DM:
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_HL2DM;
skamonkey@3961: break;
skamonkey@3961: }
skamonkey@3961: case SOURCE_ENGINE_DODS:
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_DODS;
skamonkey@3961: break;
skamonkey@3961: }
skamonkey@4141: case SOURCE_ENGINE_SDK2013:
skamonkey@4141: {
skamonkey@4141: filename = FILENAME_1_6_SDK2013;
skamonkey@4141: break;
skamonkey@4141: }
skamonkey@3961: case SOURCE_ENGINE_TF2:
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_TF2;
skamonkey@3961: break;
skamonkey@3961: }
skamonkey@3961: case SOURCE_ENGINE_ORANGEBOXVALVE_DEPRECATED:
skamonkey@3961: {
skamonkey@3961: const char *gamedir = mvi->GetGameDir();
skamonkey@3961: if (strcmp(gamedir, "tf") == 0)
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_TF2;
skamonkey@3961: }
skamonkey@3961: else if (strcmp(gamedir, "dod") == 0)
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_DODS;
skamonkey@3961: }
skamonkey@3961: else if (strcmp(gamedir, "hl2mp") == 0)
skamonkey@3961: {
skamonkey@3961: filename = FILENAME_1_6_HL2DM;
skamonkey@3961: }
skamonkey@3961: else
skamonkey@3961: {
skamonkey@3961: return NULL;
skamonkey@3961: }
skamonkey@3961: break;
skamonkey@3961: }
skamonkey@3853: case SOURCE_ENGINE_DOTA:
skamonkey@3853: {
skamonkey@3853: filename = FILENAME_1_6_DOTA;
skamonkey@3853: break;
skamonkey@3853: }
skamonkey@4142: case SOURCE_ENGINE_BLADE:
skamonkey@4142: {
skamonkey@4142: filename = FILENAME_1_6_BLADE;
skamonkey@4142: break;
skamonkey@4142: }
skamonkey@4144: case SOURCE_ENGINE_INSURGENCY:
skamonkey@4144: {
skamonkey@4144: filename = FILENAME_1_6_INSURGENCY;
skamonkey@4144: break;
skamonkey@4144: }
faluco@1557: default:
faluco@1557: {
faluco@1557: return NULL;
faluco@1557: }
faluco@1557: }
faluco@1557:
faluco@1557: char abspath[256];
faluco@1557: UTIL_Format(abspath, sizeof(abspath), "%s" PATH_SEP_CHAR "%s", mli->pl_path, filename);
faluco@1557:
dvander@1840: return _GetPluginPtr(abspath, METAMOD_FAIL_API_V2);
faluco@1557: }
faluco@1557:
faluco@1557: DLL_EXPORT void UnloadInterface_MMS()
faluco@1557: {
faluco@1557: if (g_hCore)
faluco@1557: {
faluco@1557: closelib(g_hCore);
faluco@1557: g_hCore = NULL;
faluco@1557: }
faluco@1557: }
faluco@1557:
faluco@1557: DLL_EXPORT void *CreateInterface(const char *iface, int *ret)
faluco@1557: {
dvander@1561: /**
dvander@1561: * If a load has already been attempted, bail out immediately.
dvander@1561: */
dvander@1561: if (load_attempted)
dvander@1561: {
dvander@1561: return NULL;
dvander@1561: }
dvander@1561:
dvander@1561: if (strcmp(iface, METAMOD_PLAPI_NAME) == 0)
faluco@1557: {
faluco@1557: char thisfile[256];
faluco@1557: char targetfile[256];
faluco@1557:
faluco@1557: if (!GetFileOfAddress((void *)CreateInterface_MMS, thisfile, sizeof(thisfile)))
faluco@1557: {
faluco@1557: return NULL;
faluco@1557: }
faluco@1557:
faluco@1557: size_t len = strlen(thisfile);
ds@3261: for (size_t iter=len-1; iter