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