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