dvander@2168: /** dvander@2168: * vim: set ts=4 : dvander@2168: * ============================================================================= dvander@2168: * SourceMod dvander@2168: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. dvander@2168: * ============================================================================= dvander@2168: * dvander@2168: * This program is free software; you can redistribute it and/or modify it under dvander@2168: * the terms of the GNU General Public License, version 3.0, as published by the dvander@2168: * Free Software Foundation. dvander@2168: * dvander@2168: * This program is distributed in the hope that it will be useful, but WITHOUT dvander@2168: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@2168: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more dvander@2168: * details. dvander@2168: * dvander@2168: * You should have received a copy of the GNU General Public License along with dvander@2168: * this program. If not, see . dvander@2168: * dvander@2168: * As a special exception, AlliedModders LLC gives you permission to link the dvander@2168: * code of this program (as well as its derivative works) to "Half-Life 2," the dvander@2168: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software dvander@2168: * by the Valve Corporation. You must obey the GNU General Public License in dvander@2168: * all respects for all other code used. Additionally, AlliedModders LLC grants dvander@2168: * this exception to all derivative works. AlliedModders LLC defines further dvander@2168: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), dvander@2168: * or . dvander@2168: * dvander@2168: * Version: $Id$ dvander@2168: */ dvander@2168: dvander@2168: #include dvander@2168: #include dvander@2168: #include dvander@2168: #include dvander@2168: #include dvander@2168: dvander@2168: #if defined _MSC_VER dvander@2168: #define DLL_EXPORT extern "C" __declspec(dllexport) dvander@2168: #define openlib(lib) LoadLibrary(lib) dvander@2168: #define closelib(lib) FreeLibrary(lib) dvander@2168: #define findsym(lib, sym) GetProcAddress(lib, sym) dvander@2168: #define PLATFORM_EXT ".dll" dvander@2168: #define vsnprintf _vsnprintf dvander@2168: #define PATH_SEP_CHAR "\\" dvander@2168: inline bool IsPathSepChar(char c) dvander@2168: { dvander@2168: return (c == '/' || c == '\\'); dvander@2168: } dvander@2168: #include dvander@2168: #else dvander@2168: #define DLL_EXPORT extern "C" __attribute__((visibility("default"))) dvander@2168: #define openlib(lib) dlopen(lib, RTLD_NOW) dvander@2168: #define closelib(lib) dlclose(lib) dvander@2168: #define findsym(lib, sym) dlsym(lib, sym) dvander@2168: #define PLATFORM_EXT ".so" dvander@2168: typedef void * HINSTANCE; dvander@2168: #define PATH_SEP_CHAR "/" dvander@2168: inline bool IsPathSepChar(char c) dvander@2168: { dvander@2168: return (c == '/'); dvander@2168: } dvander@2168: #include dvander@2168: #endif dvander@2168: dvander@2168: #define METAMOD_API_MAJOR 2 dvander@2168: #define FILENAME_1_4_EP1 "sourcemod.1.ep1" PLATFORM_EXT dvander@2168: #define FILENAME_1_6_EP2 "sourcemod.2.ep2" PLATFORM_EXT dvander@2168: #define FILENAME_1_6_EP1 "sourcemod.2.ep1" PLATFORM_EXT dvander@2168: dvander@2168: HINSTANCE g_hCore = NULL; dvander@2168: bool load_attempted = false; dvander@2168: dvander@2168: size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); dvander@2168: dvander@2168: class FailPlugin : public SourceMM::ISmmFailPlugin dvander@2168: { dvander@2168: public: dvander@2168: int GetApiVersion() dvander@2168: { dvander@2168: return fail_version; dvander@2168: } dvander@2168: dvander@2168: bool Load(SourceMM::PluginId id, SourceMM::ISmmAPI *ismm, char *error, size_t maxlength, bool late) dvander@2168: { dvander@2168: if (error != NULL && maxlength != 0) dvander@2168: { dvander@2168: UTIL_Format(error, maxlength, "%s", error_buffer); dvander@2168: } dvander@2168: return false; dvander@2168: } dvander@2168: dvander@2168: int fail_version; dvander@2168: char error_buffer[512]; dvander@2168: } s_FailPlugin; dvander@2168: dvander@2168: size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...) dvander@2168: { dvander@2168: va_list ap; dvander@2168: dvander@2168: va_start(ap, fmt); dvander@2168: size_t len = vsnprintf(buffer, maxlength, fmt, ap); dvander@2168: va_end(ap); dvander@2168: dvander@2168: if (len >= maxlength) dvander@2168: { dvander@2168: len = maxlength - 1; dvander@2168: buffer[len] = '\0'; dvander@2168: } dvander@2168: dvander@2168: return len; dvander@2168: } dvander@2168: dvander@2168: METAMOD_PLUGIN *_GetPluginPtr(const char *path, int fail_api) dvander@2168: { dvander@2168: METAMOD_FN_ORIG_LOAD fn; dvander@2168: METAMOD_PLUGIN *pl; dvander@2168: int ret; dvander@2168: dvander@2168: if (!(g_hCore=openlib(path))) dvander@2168: { dvander@2168: #if defined __linux__ dvander@2168: UTIL_Format(s_FailPlugin.error_buffer, dvander@2168: sizeof(s_FailPlugin.error_buffer), dvander@2168: "%s", dvander@2168: dlerror()); dvander@2168: #else dvander@2168: DWORD err = GetLastError(); dvander@2168: dvander@2168: if (FormatMessageA( dvander@2168: FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, dvander@2168: NULL, dvander@2168: err, dvander@2168: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), dvander@2168: s_FailPlugin.error_buffer, dvander@2168: sizeof(s_FailPlugin.error_buffer), dvander@2168: NULL) dvander@2168: == 0) dvander@2168: { dvander@2168: UTIL_Format(s_FailPlugin.error_buffer, dvander@2168: sizeof(s_FailPlugin.error_buffer), dvander@2168: "unknown error %x", dvander@2168: err); dvander@2168: } dvander@2168: #endif dvander@2168: dvander@2168: s_FailPlugin.fail_version = fail_api; dvander@2168: dvander@2168: return (METAMOD_PLUGIN *)&s_FailPlugin; dvander@2168: } dvander@2168: dvander@2168: if (!(fn=(METAMOD_FN_ORIG_LOAD)findsym(g_hCore, "CreateInterface"))) dvander@2168: { dvander@2168: goto error; dvander@2168: } dvander@2168: dvander@2168: pl = (METAMOD_PLUGIN *)fn(METAMOD_PLAPI_NAME, &ret); dvander@2168: if (!pl) dvander@2168: { dvander@2168: goto error; dvander@2168: } dvander@2168: dvander@2168: return pl; dvander@2168: error: dvander@2168: closelib(g_hCore); dvander@2168: g_hCore = NULL; dvander@2168: return NULL; dvander@2168: } dvander@2168: dvander@2168: bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength) dvander@2168: { dvander@2168: #if defined _MSC_VER dvander@2168: MEMORY_BASIC_INFORMATION mem; dvander@2168: if (!VirtualQuery(pAddr, &mem, sizeof(mem))) dvander@2168: return false; dvander@2168: if (mem.AllocationBase == NULL) dvander@2168: return false; dvander@2168: HMODULE dll = (HMODULE)mem.AllocationBase; dvander@2168: GetModuleFileName(dll, (LPTSTR)buffer, maxlength); dvander@2168: #else dvander@2168: Dl_info info; dvander@2168: if (!dladdr(pAddr, &info)) dvander@2168: return false; dvander@2168: if (!info.dli_fbase || !info.dli_fname) dvander@2168: return false; dvander@2168: const char *dllpath = info.dli_fname; dvander@2168: snprintf(buffer, maxlength, "%s", dllpath); dvander@2168: #endif dvander@2168: return true; dvander@2168: } dvander@2168: dvander@2168: DLL_EXPORT METAMOD_PLUGIN *CreateInterface_MMS(const MetamodVersionInfo *mvi, const MetamodLoaderInfo *mli) dvander@2168: { dvander@2168: char *filename; dvander@2168: dvander@2168: load_attempted = true; dvander@2168: dvander@2168: if (mvi->api_major > METAMOD_API_MAJOR) dvander@2168: { dvander@2168: return NULL; dvander@2168: } dvander@2168: dvander@2168: switch (mvi->source_engine) dvander@2168: { dvander@2168: case SOURCE_ENGINE_ORANGEBOX: dvander@2168: { dvander@2168: filename = FILENAME_1_6_EP2; dvander@2168: break; dvander@2168: } dvander@2168: case SOURCE_ENGINE_EPISODEONE: dvander@2168: case SOURCE_ENGINE_ORIGINAL: dvander@2168: { dvander@2168: filename = FILENAME_1_6_EP1; dvander@2168: break; dvander@2168: } dvander@2168: default: dvander@2168: { dvander@2168: return NULL; dvander@2168: } dvander@2168: } dvander@2168: dvander@2168: char abspath[256]; dvander@2168: UTIL_Format(abspath, sizeof(abspath), "%s" PATH_SEP_CHAR "%s", mli->pl_path, filename); dvander@2168: dvander@2168: return _GetPluginPtr(abspath, METAMOD_FAIL_API_V2); dvander@2168: } dvander@2168: dvander@2168: DLL_EXPORT void UnloadInterface_MMS() dvander@2168: { dvander@2168: if (g_hCore) dvander@2168: { dvander@2168: closelib(g_hCore); dvander@2168: g_hCore = NULL; dvander@2168: } dvander@2168: } dvander@2168: dvander@2168: DLL_EXPORT void *CreateInterface(const char *iface, int *ret) dvander@2168: { dvander@2168: /** dvander@2168: * If a load has already been attempted, bail out immediately. dvander@2168: */ dvander@2168: if (load_attempted) dvander@2168: { dvander@2168: return NULL; dvander@2168: } dvander@2168: dvander@2168: if (strcmp(iface, METAMOD_PLAPI_NAME) == 0) dvander@2168: { dvander@2168: char thisfile[256]; dvander@2168: char targetfile[256]; dvander@2168: dvander@2168: if (!GetFileOfAddress((void *)CreateInterface_MMS, thisfile, sizeof(thisfile))) dvander@2168: { dvander@2168: return NULL; dvander@2168: } dvander@2168: dvander@2168: size_t len = strlen(thisfile); dvander@2168: for (size_t iter=len-1; iter>=0 && iter