extensions/sdktools/vcaller.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Mon Aug 27 19:31:38 2012 -0400 (2012-08-27)
changeset 3637 868db34cf3e6
parent 3613 b0740093756d
child 3961 0dc477c29792
permissions -rw-r--r--
Updated SDKCall SetSignature to use same hidden-symbols engine list as core (bug 5440, r=me).
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 noet :
     3  * =============================================================================
     4  * SourceMod SDKTools Extension
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  */
    29 
    30 #include "extension.h"
    31 #include "vcallbuilder.h"
    32 #include "vglobals.h"
    33 
    34 enum SDKLibrary
    35 {
    36 	SDKLibrary_Server,	/**< server.dll/server_i486.so */
    37 	SDKLibrary_Engine,	/**< engine.dll/engine_*.so */
    38 };
    39 
    40 enum SDKPassMethod
    41 {
    42 	SDKPass_Pointer,		/**< Pass as a pointer */
    43 	SDKPass_Plain,			/**< Pass as plain data */
    44 	SDKPass_ByValue,		/**< Pass an object by value */
    45 	SDKPass_ByRef,			/**< Pass an object by reference */
    46 };
    47 
    48 int s_vtbl_index = -1;
    49 void *s_call_addr = NULL;
    50 ValveCallType s_vcalltype = ValveCall_Static;
    51 bool s_has_return = false;
    52 ValvePassInfo s_return;
    53 unsigned int s_numparams = 0;
    54 ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
    55 
    56 inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
    57 {
    58 	if (method == SDKPass_Pointer || method == SDKPass_ByRef)
    59 	{
    60 		type = PassType_Basic;
    61 		if (vtype == Valve_POD
    62 			|| vtype == Valve_Float
    63 			|| vtype == Valve_Bool)
    64 		{
    65 			flags = PASSFLAG_BYVAL | PASSFLAG_ASPOINTER;
    66 		} else {
    67 			flags = PASSFLAG_BYVAL;
    68 		}
    69 	} else if (method == SDKPass_Plain) {
    70 		type = PassType_Basic;
    71 		flags = PASSFLAG_BYVAL;
    72 	} else if (method == SDKPass_ByValue) {
    73 		if (vtype == Valve_Vector
    74 			|| vtype == Valve_QAngle)
    75 		{
    76 			type = PassType_Object;
    77 		} else {
    78 			type = PassType_Basic;
    79 		}
    80 		flags = PASSFLAG_BYVAL;
    81 	}
    82 }
    83 
    84 static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
    85 {
    86 	s_numparams = 0;
    87 	s_vtbl_index = -1;
    88 	s_call_addr = NULL;
    89 	s_has_return = false;
    90 	s_vcalltype = (ValveCallType)params[1];
    91 
    92 	return 1;
    93 }
    94 
    95 static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
    96 {
    97 	s_vtbl_index = params[1];
    98 
    99 	return 1;
   100 }
   101 
   102 static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
   103 {
   104 	void *addrInBase = NULL;
   105 	if (params[1] == SDKLibrary_Server)
   106 	{
   107 		addrInBase = (void *)g_SMAPI->GetServerFactory(false);
   108 	} else if (params[1] == SDKLibrary_Engine) {
   109 		addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
   110 	}
   111 	if (addrInBase == NULL)
   112 	{
   113 		return 0;
   114 	}
   115 
   116 	char *sig;
   117 	pContext->LocalToString(params[2], &sig);
   118 
   119 #if defined PLATFORM_POSIX
   120 	if (sig[0] == '@')
   121 	{
   122 		Dl_info info;
   123 		if (dladdr(addrInBase, &info) == 0)
   124 		{
   125 			return 0;
   126 		}
   127 		void *handle = dlopen(info.dli_fname, RTLD_NOW);
   128 		if (!handle)
   129 		{
   130 			return 0;
   131 		}
   132 #if (SOURCE_ENGINE == SE_CSS) || (SOURCE_ENGINE == SE_ORANGEBOXVALVE) || (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2) || (SOURCE_ENGINE == SE_CSGO)
   133 		s_call_addr = memutils->ResolveSymbol(handle, &sig[1]);
   134 #else
   135 		s_call_addr = dlsym(handle, &sig[1]);
   136 #endif
   137 		dlclose(handle);
   138 
   139 		return (s_call_addr != NULL) ? 1 : 0;
   140 	}
   141 #endif
   142 
   143 	s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
   144 
   145 	return (s_call_addr != NULL) ? 1 : 0;
   146 }
   147 
   148 static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
   149 {
   150 	IGameConfig *conf;
   151 
   152 	if (params[1] == BAD_HANDLE)
   153 	{
   154 		conf = g_pGameConf;
   155 	} else {
   156 		HandleError err;
   157 		if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
   158 		{
   159 			return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   160 		}
   161 	}
   162 	
   163 	char *key;
   164 	pContext->LocalToString(params[3], &key);
   165 
   166 	if (params[2] == 0)
   167 	{
   168 		return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
   169 	} else if (params[2] == 1) {
   170 		bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
   171 		return (result && s_call_addr != NULL) ? 1 : 0;
   172 	}
   173 
   174 	return 0;
   175 }
   176 
   177 static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
   178 {
   179 	s_has_return = true;
   180 	s_return.vtype = (ValveType)params[1];
   181 	DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
   182 	s_return.decflags = params[3];
   183 	s_return.encflags = params[4];
   184 
   185 	return 1;
   186 }
   187 
   188 static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
   189 {
   190 	if (s_numparams >= SP_MAX_EXEC_PARAMS)
   191 	{
   192 		return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
   193 	}
   194 
   195 	ValvePassInfo *info = &s_params[s_numparams++];
   196 	info->vtype = (ValveType)params[1];
   197 	SDKPassMethod method = (SDKPassMethod)params[2];
   198 	DecodePassMethod(info->vtype, method, info->type, info->flags);
   199 	info->decflags = params[3] | VDECODE_FLAG_BYREF;
   200 	info->encflags = params[4];
   201 
   202 	/* Since SDKPass_ByRef acts like SDKPass_Pointer we can't allow NULL, just in case */
   203 	if (method == SDKPass_ByRef)
   204 	{
   205 		info->decflags &= ~VDECODE_FLAG_ALLOWNULL;
   206 	}
   207 
   208 	return 1;
   209 }
   210 
   211 static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
   212 {
   213 	ValveCall *vc = NULL;
   214 	if (s_vtbl_index > -1)
   215 	{
   216 		vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   217 	} else if (s_call_addr) {
   218 		vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   219 	}
   220 
   221 	if (!vc)
   222 	{
   223 		return BAD_HANDLE;
   224 	}
   225 
   226 	if (vc->thisinfo)
   227 	{
   228 		vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
   229 	}
   230 
   231 	Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
   232 	if (!hndl)
   233 	{
   234 		delete vc;
   235 	}
   236 
   237 	return hndl;
   238 }
   239 
   240 static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
   241 {
   242 	ValveCall *vc;
   243 	HandleError err;
   244 	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   245 
   246 	if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
   247 	{
   248 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   249 	}
   250 
   251 	unsigned char *ptr = vc->stk_get();
   252 
   253 	const unsigned int numparams = (unsigned)params[0];
   254 	unsigned int startparam = 2;
   255 	/* Do we need to write a thispointer?  */
   256 
   257 	if (vc->thisinfo)
   258 	{
   259 		switch (vc->type)
   260 		{
   261 		case ValveCall_Entity:
   262 		case ValveCall_Player:
   263 			{
   264 				if (startparam > numparams)
   265 				{
   266 					vc->stk_put(ptr);
   267 					return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
   268 				}
   269 
   270 				if (DecodeValveParam(pContext, 
   271 					params[startparam],
   272 					vc,
   273 					vc->thisinfo,
   274 					ptr) == Data_Fail)
   275 				{
   276 					vc->stk_put(ptr);
   277 					return 0;
   278 				}
   279 				startparam++;
   280 			}
   281 			break;
   282 		case ValveCall_GameRules:
   283 			{
   284 				if (g_pGameRules == NULL)
   285 				{
   286 					vc->stk_put(ptr);
   287 					return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
   288 				}
   289 
   290 				void *gamerules = *g_pGameRules;
   291 
   292 				if (gamerules == NULL)
   293 				{
   294 					vc->stk_put(ptr);
   295 					return pContext->ThrowNativeError("GameRules not available before map is loaded");
   296 				}
   297 				*(void **)ptr = gamerules;
   298 			}
   299 			break;
   300 		case ValveCall_EntityList:
   301 			{
   302 				if (g_EntList == NULL)
   303 				{
   304 					vc->stk_put(ptr);
   305 					return pContext->ThrowNativeError("EntityList unsupported or not available; file a bug report");
   306 				}
   307 
   308 				*(void **)ptr = g_EntList;
   309 			}
   310 			break;
   311 		case ValveCall_Raw:
   312 			{
   313 				//params[startparam] is an address to a pointer to THIS
   314 				//params following this are params to the method we will invoke later
   315 				if (startparam > numparams)
   316 				{
   317 					vc->stk_put(ptr);
   318 					return pContext->ThrowNativeError("Expected a ThisPtr address, it wasn't found");
   319 				}
   320 
   321 				//note: varargs pawn args are passed by-ref
   322 				cell_t *cell;
   323 				pContext->LocalToPhysAddr(params[startparam], &cell);
   324 				void *thisptr = reinterpret_cast<void*>(*cell);
   325 
   326 				*(void **)ptr = thisptr;
   327 				startparam++;
   328 			}
   329 			break;
   330 		}
   331 	}
   332 
   333 	/* See if we need to skip any more parameters */
   334 	unsigned int retparam = startparam;
   335 	if (vc->retinfo)
   336 	{
   337 		if (vc->retinfo->vtype == Valve_String)
   338 		{
   339 			startparam += 2;
   340 		} else if (vc->retinfo->vtype == Valve_Vector
   341 					|| vc->retinfo->vtype == Valve_QAngle)
   342 		{
   343 			startparam += 1;
   344 		}
   345 	}
   346 
   347 	unsigned int callparams = vc->call->GetParamCount();
   348 	bool will_copyback = false;
   349 	for (unsigned int i=0; i<callparams; i++)
   350 	{
   351 		unsigned int p = startparam + i;
   352 		if (p > numparams)
   353 		{
   354 			vc->stk_put(ptr);
   355 			return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
   356 		}
   357 		if (DecodeValveParam(pContext,
   358 			params[p],
   359 			vc,
   360 			&(vc->vparams[i]),
   361 			ptr) == Data_Fail)
   362 		{
   363 			vc->stk_put(ptr);
   364 			return 0;
   365 		}
   366 		if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   367 		{
   368 			will_copyback = true;
   369 		}
   370 	}
   371 
   372 	/* Make the actual call */
   373 	vc->call->Execute(ptr, vc->retbuf);
   374 
   375 	/* Do we need to copy anything back? */
   376 	if (will_copyback)
   377 	{
   378 		for (unsigned int i=0; i<callparams; i++)
   379 		{
   380 			if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   381 			{
   382 				if (EncodeValveParam(pContext, 
   383 					params[startparam + i], 
   384 					vc,
   385 					&vc->vparams[i],
   386 					ptr) == Data_Fail)
   387 				{
   388 					vc->stk_put(ptr);
   389 					return 0;
   390 				}
   391 			}
   392 		}
   393 	}
   394 
   395 	/* Save stack once and for all */
   396 	vc->stk_put(ptr);
   397 
   398 	/* Figure out how to decode the return information */
   399 	if (vc->retinfo)
   400 	{
   401 		if (vc->retinfo->vtype == Valve_String)
   402 		{
   403 			if (numparams < 3)
   404 			{
   405 				return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
   406 			}
   407 			cell_t *addr;
   408 			size_t written;
   409 			pContext->LocalToPhysAddr(params[retparam+1], &addr);
   410 			pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
   411 			return (cell_t)written;
   412 		} else if (vc->retinfo->vtype == Valve_Vector
   413 					|| vc->retinfo->vtype == Valve_QAngle)
   414 		{
   415 			if (numparams < 2)
   416 			{
   417 				return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
   418 			}
   419 			if (EncodeValveParam(pContext, params[retparam], vc, vc->retinfo, vc->retbuf)
   420 				== Data_Fail)
   421 			{
   422 				return 0;
   423 			}
   424 		} else if (vc->retinfo->vtype == Valve_CBaseEntity
   425 					|| vc->retinfo->vtype == Valve_CBasePlayer)
   426 		{
   427 			CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
   428 			if (!pEntity)
   429 			{
   430 				return -1;
   431 			}
   432 			edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   433 			if (!pEdict || pEdict->IsFree())
   434 			{
   435 				return -1;
   436 			}
   437 			return IndexOfEdict(pEdict);
   438 		} else if (vc->retinfo->vtype == Valve_Edict) {
   439 			edict_t *pEdict = *(edict_t **)(vc->retbuf);
   440 			if (!pEdict || pEdict->IsFree())
   441 			{
   442 				return -1;
   443 			}
   444 			return IndexOfEdict(pEdict);
   445 		} else if (vc->retinfo->vtype == Valve_Bool) {
   446 			bool *addr = (bool  *)vc->retbuf;
   447 			if (vc->retinfo->flags & PASSFLAG_ASPOINTER)
   448 			{
   449 				addr = *(bool **)addr;
   450 			}
   451 			return *addr ? 1 : 0;
   452 		} else {
   453 			cell_t *addr = (cell_t *)vc->retbuf;
   454 			if (vc->retinfo->flags & PASSFLAG_ASPOINTER)
   455 			{
   456 				addr = *(cell_t **)addr;
   457 			}
   458 			return *addr;
   459 		}
   460 	}
   461 
   462 	return 0;
   463 }
   464 
   465 sp_nativeinfo_t g_CallNatives[] = 
   466 {
   467 	{"StartPrepSDKCall",			StartPrepSDKCall},
   468 	{"PrepSDKCall_SetVirtual",		PrepSDKCall_SetVirtual},
   469 	{"PrepSDKCall_SetSignature",	PrepSDKCall_SetSignature},
   470 	{"PrepSDKCall_SetFromConf",		PrepSDKCall_SetFromConf},
   471 	{"PrepSDKCall_SetReturnInfo",	PrepSDKCall_SetReturnInfo},
   472 	{"PrepSDKCall_AddParameter",	PrepSDKCall_AddParameter},
   473 	{"EndPrepSDKCall",				EndPrepSDKCall},
   474 	{"SDKCall",						SDKCall},
   475 	{NULL,							NULL},
   476 };