loader/loader.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 05:14:32 2008 +0000 (2008-07-11)
branchrefac-jit
changeset 2190 880e81f30b5e
parent 2168 c24a6fa957db
child 2289 44045ead61cd
permissions -rw-r--r--
GetContext() now returns 'this' as a hack (shouldn't+can't be accessed anyway)
dvander@2168
     1
/**
dvander@2168
     2
 * vim: set ts=4 :
dvander@2168
     3
 * =============================================================================
dvander@2168
     4
 * SourceMod
dvander@2168
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2168
     6
 * =============================================================================
dvander@2168
     7
 *
dvander@2168
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2168
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2168
    10
 * Free Software Foundation.
dvander@2168
    11
 * 
dvander@2168
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2168
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2168
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2168
    15
 * details.
dvander@2168
    16
 *
dvander@2168
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2168
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2168
    19
 *
dvander@2168
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2168
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2168
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2168
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2168
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2168
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2168
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2168
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2168
    28
 *
dvander@2168
    29
 * Version: $Id$
dvander@2168
    30
 */
dvander@2168
    31
dvander@2168
    32
#include <ISmmPluginExt.h>
dvander@2168
    33
#include <stdarg.h>
dvander@2168
    34
#include <string.h>
dvander@2168
    35
#include <stdio.h>
dvander@2168
    36
#include <malloc.h>
dvander@2168
    37
dvander@2168
    38
#if defined _MSC_VER
dvander@2168
    39
	#define DLL_EXPORT				extern "C" __declspec(dllexport)
dvander@2168
    40
	#define openlib(lib)			LoadLibrary(lib)
dvander@2168
    41
	#define closelib(lib)			FreeLibrary(lib)
dvander@2168
    42
	#define findsym(lib, sym)		GetProcAddress(lib, sym)
dvander@2168
    43
	#define PLATFORM_EXT			".dll"
dvander@2168
    44
	#define vsnprintf				_vsnprintf
dvander@2168
    45
	#define PATH_SEP_CHAR			"\\"
dvander@2168
    46
	inline bool IsPathSepChar(char c) 
dvander@2168
    47
	{
dvander@2168
    48
		return (c == '/' || c == '\\');
dvander@2168
    49
	}
dvander@2168
    50
	#include <Windows.h>
dvander@2168
    51
#else
dvander@2168
    52
	#define DLL_EXPORT				extern "C" __attribute__((visibility("default")))
dvander@2168
    53
	#define openlib(lib)			dlopen(lib, RTLD_NOW)
dvander@2168
    54
	#define closelib(lib)			dlclose(lib)
dvander@2168
    55
	#define findsym(lib, sym)		dlsym(lib, sym)
dvander@2168
    56
	#define PLATFORM_EXT			".so"
dvander@2168
    57
	typedef void *					HINSTANCE;
dvander@2168
    58
	#define PATH_SEP_CHAR			"/"
dvander@2168
    59
	inline bool IsPathSepChar(char c) 
dvander@2168
    60
	{
dvander@2168
    61
		return (c == '/');
dvander@2168
    62
	}
dvander@2168
    63
	#include <dlfcn.h>
dvander@2168
    64
#endif
dvander@2168
    65
dvander@2168
    66
#define METAMOD_API_MAJOR			2
dvander@2168
    67
#define FILENAME_1_4_EP1			"sourcemod.1.ep1" PLATFORM_EXT
dvander@2168
    68
#define FILENAME_1_6_EP2			"sourcemod.2.ep2" PLATFORM_EXT
dvander@2168
    69
#define FILENAME_1_6_EP1			"sourcemod.2.ep1" PLATFORM_EXT
dvander@2168
    70
dvander@2168
    71
HINSTANCE g_hCore = NULL;
dvander@2168
    72
bool load_attempted = false;
dvander@2168
    73
dvander@2168
    74
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
dvander@2168
    75
dvander@2168
    76
class FailPlugin : public SourceMM::ISmmFailPlugin
dvander@2168
    77
{
dvander@2168
    78
public:
dvander@2168
    79
	int GetApiVersion()
dvander@2168
    80
	{
dvander@2168
    81
		return fail_version;
dvander@2168
    82
	}
dvander@2168
    83
dvander@2168
    84
	bool Load(SourceMM::PluginId id, SourceMM::ISmmAPI *ismm, char *error, size_t maxlength, bool late)
dvander@2168
    85
	{
dvander@2168
    86
		if (error != NULL && maxlength != 0)
dvander@2168
    87
		{
dvander@2168
    88
			UTIL_Format(error, maxlength, "%s", error_buffer);
dvander@2168
    89
		}
dvander@2168
    90
		return false;
dvander@2168
    91
	}
dvander@2168
    92
dvander@2168
    93
	int fail_version;
dvander@2168
    94
	char error_buffer[512];
dvander@2168
    95
} s_FailPlugin;
dvander@2168
    96
dvander@2168
    97
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
dvander@2168
    98
{
dvander@2168
    99
	va_list ap;
dvander@2168
   100
dvander@2168
   101
	va_start(ap, fmt);
dvander@2168
   102
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
dvander@2168
   103
	va_end(ap);
dvander@2168
   104
dvander@2168
   105
	if (len >= maxlength)
dvander@2168
   106
	{
dvander@2168
   107
		len = maxlength - 1;
dvander@2168
   108
		buffer[len] = '\0';
dvander@2168
   109
	}
dvander@2168
   110
dvander@2168
   111
	return len;
dvander@2168
   112
}
dvander@2168
   113
dvander@2168
   114
METAMOD_PLUGIN *_GetPluginPtr(const char *path, int fail_api)
dvander@2168
   115
{
dvander@2168
   116
	METAMOD_FN_ORIG_LOAD fn;
dvander@2168
   117
	METAMOD_PLUGIN *pl;
dvander@2168
   118
	int ret;
dvander@2168
   119
dvander@2168
   120
	if (!(g_hCore=openlib(path)))
dvander@2168
   121
	{
dvander@2168
   122
#if defined __linux__
dvander@2168
   123
		UTIL_Format(s_FailPlugin.error_buffer, 
dvander@2168
   124
			sizeof(s_FailPlugin.error_buffer),
dvander@2168
   125
			"%s",
dvander@2168
   126
			dlerror());
dvander@2168
   127
#else
dvander@2168
   128
		DWORD err = GetLastError();
dvander@2168
   129
dvander@2168
   130
		if (FormatMessageA(
dvander@2168
   131
				FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
dvander@2168
   132
				NULL,
dvander@2168
   133
				err,
dvander@2168
   134
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
dvander@2168
   135
				s_FailPlugin.error_buffer,
dvander@2168
   136
				sizeof(s_FailPlugin.error_buffer),
dvander@2168
   137
				NULL)
dvander@2168
   138
			== 0)
dvander@2168
   139
		{
dvander@2168
   140
			UTIL_Format(s_FailPlugin.error_buffer, 
dvander@2168
   141
				sizeof(s_FailPlugin.error_buffer),
dvander@2168
   142
				"unknown error %x",
dvander@2168
   143
				err);
dvander@2168
   144
		}
dvander@2168
   145
#endif
dvander@2168
   146
dvander@2168
   147
		s_FailPlugin.fail_version = fail_api;
dvander@2168
   148
dvander@2168
   149
		return (METAMOD_PLUGIN *)&s_FailPlugin;
dvander@2168
   150
	}
dvander@2168
   151
dvander@2168
   152
	if (!(fn=(METAMOD_FN_ORIG_LOAD)findsym(g_hCore, "CreateInterface")))
dvander@2168
   153
	{
dvander@2168
   154
		goto error;
dvander@2168
   155
	}
dvander@2168
   156
dvander@2168
   157
	pl = (METAMOD_PLUGIN *)fn(METAMOD_PLAPI_NAME, &ret);
dvander@2168
   158
	if (!pl)
dvander@2168
   159
	{
dvander@2168
   160
		goto error;
dvander@2168
   161
	}
dvander@2168
   162
dvander@2168
   163
	return pl;
dvander@2168
   164
error:
dvander@2168
   165
	closelib(g_hCore);
dvander@2168
   166
	g_hCore = NULL;
dvander@2168
   167
	return NULL;
dvander@2168
   168
}
dvander@2168
   169
dvander@2168
   170
bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength)
dvander@2168
   171
{
dvander@2168
   172
#if defined _MSC_VER
dvander@2168
   173
	MEMORY_BASIC_INFORMATION mem;
dvander@2168
   174
	if (!VirtualQuery(pAddr, &mem, sizeof(mem)))
dvander@2168
   175
		return false;
dvander@2168
   176
	if (mem.AllocationBase == NULL)
dvander@2168
   177
		return false;
dvander@2168
   178
	HMODULE dll = (HMODULE)mem.AllocationBase;
dvander@2168
   179
	GetModuleFileName(dll, (LPTSTR)buffer, maxlength);
dvander@2168
   180
#else
dvander@2168
   181
	Dl_info info;
dvander@2168
   182
	if (!dladdr(pAddr, &info))
dvander@2168
   183
		return false;
dvander@2168
   184
	if (!info.dli_fbase || !info.dli_fname)
dvander@2168
   185
		return false;
dvander@2168
   186
	const char *dllpath = info.dli_fname;
dvander@2168
   187
	snprintf(buffer, maxlength, "%s", dllpath);
dvander@2168
   188
#endif
dvander@2168
   189
	return true;
dvander@2168
   190
}
dvander@2168
   191
dvander@2168
   192
DLL_EXPORT METAMOD_PLUGIN *CreateInterface_MMS(const MetamodVersionInfo *mvi, const MetamodLoaderInfo *mli)
dvander@2168
   193
{
dvander@2168
   194
	char *filename;
dvander@2168
   195
dvander@2168
   196
	load_attempted = true;
dvander@2168
   197
	
dvander@2168
   198
	if (mvi->api_major > METAMOD_API_MAJOR)
dvander@2168
   199
	{
dvander@2168
   200
		return NULL;
dvander@2168
   201
	}
dvander@2168
   202
dvander@2168
   203
	switch (mvi->source_engine)
dvander@2168
   204
	{
dvander@2168
   205
	case SOURCE_ENGINE_ORANGEBOX:
dvander@2168
   206
		{
dvander@2168
   207
			filename = FILENAME_1_6_EP2;
dvander@2168
   208
			break;
dvander@2168
   209
		}
dvander@2168
   210
	case SOURCE_ENGINE_EPISODEONE:
dvander@2168
   211
	case SOURCE_ENGINE_ORIGINAL:
dvander@2168
   212
		{
dvander@2168
   213
			filename = FILENAME_1_6_EP1;
dvander@2168
   214
			break;
dvander@2168
   215
		}
dvander@2168
   216
	default:
dvander@2168
   217
		{
dvander@2168
   218
			return NULL;
dvander@2168
   219
		}
dvander@2168
   220
	}
dvander@2168
   221
dvander@2168
   222
	char abspath[256];
dvander@2168
   223
	UTIL_Format(abspath, sizeof(abspath), "%s" PATH_SEP_CHAR "%s", mli->pl_path, filename);
dvander@2168
   224
dvander@2168
   225
	return _GetPluginPtr(abspath, METAMOD_FAIL_API_V2);
dvander@2168
   226
}
dvander@2168
   227
dvander@2168
   228
DLL_EXPORT void UnloadInterface_MMS()
dvander@2168
   229
{
dvander@2168
   230
	if (g_hCore)
dvander@2168
   231
	{
dvander@2168
   232
		closelib(g_hCore);
dvander@2168
   233
		g_hCore = NULL;
dvander@2168
   234
	}
dvander@2168
   235
}
dvander@2168
   236
dvander@2168
   237
DLL_EXPORT void *CreateInterface(const char *iface, int *ret)
dvander@2168
   238
{
dvander@2168
   239
	/**
dvander@2168
   240
	 * If a load has already been attempted, bail out immediately.
dvander@2168
   241
	 */
dvander@2168
   242
	if (load_attempted)
dvander@2168
   243
	{
dvander@2168
   244
		return NULL;
dvander@2168
   245
	}
dvander@2168
   246
dvander@2168
   247
	if (strcmp(iface, METAMOD_PLAPI_NAME) == 0)
dvander@2168
   248
	{
dvander@2168
   249
		char thisfile[256];
dvander@2168
   250
		char targetfile[256];
dvander@2168
   251
dvander@2168
   252
		if (!GetFileOfAddress((void *)CreateInterface_MMS, thisfile, sizeof(thisfile)))
dvander@2168
   253
		{
dvander@2168
   254
			return NULL;
dvander@2168
   255
		}
dvander@2168
   256
dvander@2168
   257
		size_t len = strlen(thisfile);
dvander@2168
   258
		for (size_t iter=len-1; iter>=0 && iter<len; iter--)
dvander@2168
   259
		{
dvander@2168
   260
			if (IsPathSepChar(thisfile[iter]))
dvander@2168
   261
			{
dvander@2168
   262
				thisfile[iter] = '\0';
dvander@2168
   263
				break;
dvander@2168
   264
			}
dvander@2168
   265
		}
dvander@2168
   266
dvander@2168
   267
		UTIL_Format(targetfile, sizeof(targetfile), "%s" PATH_SEP_CHAR FILENAME_1_4_EP1, thisfile);
dvander@2168
   268
dvander@2168
   269
		return _GetPluginPtr(targetfile, METAMOD_FAIL_API_V1);
dvander@2168
   270
	}
dvander@2168
   271
dvander@2168
   272
	return NULL;
dvander@2168
   273
}
dvander@2168
   274
dvander@2168
   275
#if defined _MSC_VER
dvander@2168
   276
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
dvander@2168
   277
{
dvander@2168
   278
	if (fdwReason == DLL_PROCESS_DETACH)
dvander@2168
   279
	{
dvander@2168
   280
		UnloadInterface_MMS();
dvander@2168
   281
	}
dvander@2168
   282
	return TRUE;
dvander@2168
   283
}
dvander@2168
   284
#else
dvander@2168
   285
__attribute__((destructor)) static void gcc_fini()
dvander@2168
   286
{
dvander@2168
   287
	UnloadInterface_MMS();
dvander@2168
   288
}
dvander@2168
   289
#endif
dvander@2168
   290
dvander@2168
   291
/* Overload a few things to prevent libstdc++ linking */
dvander@2168
   292
#if defined __linux__
dvander@2168
   293
extern "C" void __cxa_pure_virtual(void)
dvander@2168
   294
{
dvander@2168
   295
}
dvander@2168
   296
dvander@2168
   297
void *operator new(size_t size)
dvander@2168
   298
{
dvander@2168
   299
	return malloc(size);
dvander@2168
   300
}
dvander@2168
   301
dvander@2168
   302
void *operator new[](size_t size)
dvander@2168
   303
{
dvander@2168
   304
	return malloc(size);
dvander@2168
   305
}
dvander@2168
   306
dvander@2168
   307
void operator delete(void *ptr)
dvander@2168
   308
{
dvander@2168
   309
	free(ptr);
dvander@2168
   310
}
dvander@2168
   311
dvander@2168
   312
void operator delete[](void * ptr)
dvander@2168
   313
{
dvander@2168
   314
	free(ptr);
dvander@2168
   315
}
dvander@2168
   316
#endif
dvander@2168
   317
dvander@2168
   318