extensions/sdktools/vcaller.cpp
author Scott Ehlert <ds@alliedmods.net>
Tue Jul 17 13:57:03 2007 +0000 (2007-07-17)
changeset 1130 ff50b695a042
parent 1065 11756fc9fb63
child 1135 1a04a48f0788
permissions -rw-r--r--
Fixed VENCODE_FLAG_COPYBACK flag (SDKTools)
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod SDK Tools Extension
     5  * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
     6  * ===============================================================
     7  *
     8  * This program is free software; you can redistribute it and/or
     9  * modify it under the terms of the GNU General Public License
    10  * as published by the Free Software Foundation; either version 2
    11  * of the License, or (at your option) any later version.
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  * 
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program; if not, write to the Free Software
    19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    20  *
    21  * Version: $Id$
    22  */
    23 
    24 #include "extension.h"
    25 #include "vcallbuilder.h"
    26 #include "gamerules.h"
    27 
    28 enum SDKLibrary
    29 {
    30 	SDKLibrary_Server,	/**< server.dll/server_i486.so */
    31 	SDKLibrary_Engine,	/**< engine.dll/engine_*.so */
    32 };
    33 
    34 enum SDKPassMethod
    35 {
    36 	SDKPass_Pointer,		/**< Pass as a pointer */
    37 	SDKPass_Plain,			/**< Pass as plain data */
    38 	SDKPass_ByValue,		/**< Pass an object by value */
    39 	SDKPass_ByRef,			/**< Pass an object by reference */
    40 };
    41 
    42 int s_vtbl_index = -1;
    43 void *s_call_addr = NULL;
    44 ValveCallType s_vcalltype = ValveCall_Static;
    45 bool s_has_return = false;
    46 ValvePassInfo s_return;
    47 unsigned int s_numparams = 0;
    48 ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
    49 
    50 inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
    51 {
    52 	if (method == SDKPass_Pointer)
    53 	{
    54 		type = PassType_Basic;
    55 		if (vtype == Valve_POD
    56 			|| vtype == Valve_Float
    57 			|| vtype == Valve_Bool)
    58 		{
    59 			flags = PASSFLAG_ASPOINTER;
    60 		} else {
    61 			flags = PASSFLAG_BYVAL;
    62 		}
    63 	} else if (method == SDKPass_Plain) {
    64 		type = PassType_Basic;
    65 		flags = PASSFLAG_BYVAL;
    66 	} else if (method == SDKPass_ByValue) {
    67 		if (vtype == Valve_Vector
    68 			|| vtype == Valve_QAngle)
    69 		{
    70 			type = PassType_Object;
    71 		} else {
    72 			type = PassType_Basic;
    73 		}
    74 		flags = PASSFLAG_BYVAL;
    75 	} else if (method == SDKPass_ByRef) {
    76 		if (vtype == Valve_Vector
    77 			|| vtype == Valve_QAngle)
    78 		{
    79 			type = PassType_Object;
    80 		} else {
    81 			type = PassType_Basic;
    82 		}
    83 		flags = PASSFLAG_BYREF;
    84 	}
    85 }
    86 
    87 static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
    88 {
    89 	s_numparams = 0;
    90 	s_vtbl_index = -1;
    91 	s_call_addr = NULL;
    92 	s_has_return = false;
    93 	s_vcalltype = (ValveCallType)params[1];
    94 
    95 	return 1;
    96 }
    97 
    98 static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
    99 {
   100 	s_vtbl_index = params[1];
   101 
   102 	return 1;
   103 }
   104 
   105 static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
   106 {
   107 	void *addrInBase = NULL;
   108 	if (params[1] == SDKLibrary_Server)
   109 	{
   110 		addrInBase = (void *)g_SMAPI->serverFactory(false);
   111 	} else if (params[1] == SDKLibrary_Engine) {
   112 		addrInBase = (void *)g_SMAPI->engineFactory(false);
   113 	}
   114 	if (addrInBase == NULL)
   115 	{
   116 		return 0;
   117 	}
   118 
   119 	char *sig;
   120 	pContext->LocalToString(params[2], &sig);
   121 
   122 #if defined PLATFORM_LINUX
   123 	if (sig[0] == '@')
   124 	{
   125 		Dl_info info;
   126 		if (dladdr(addrInBase, &info) == 0)
   127 		{
   128 			return 0;
   129 		}
   130 		void *handle = dlopen(info.dli_fname, RTLD_NOW);
   131 		if (!handle)
   132 		{
   133 			return 0;
   134 		}
   135 		s_call_addr = dlsym(handle, &sig[1]);
   136 		dlclose(handle);
   137 
   138 		return (s_call_addr != NULL) ? 1 : 0;
   139 	}
   140 #endif
   141 
   142 	s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
   143 
   144 	return (s_call_addr != NULL) ? 1 : 0;
   145 }
   146 
   147 static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
   148 {
   149 	IGameConfig *conf;
   150 
   151 	if (params[1] == BAD_HANDLE)
   152 	{
   153 		conf = g_pGameConf;
   154 	} else {
   155 		HandleError err;
   156 		if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
   157 		{
   158 			return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   159 		}
   160 	}
   161 	
   162 	char *key;
   163 	pContext->LocalToString(params[3], &key);
   164 
   165 	if (params[2] == 0)
   166 	{
   167 		return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
   168 	} else if (params[2] == 1) {
   169 		bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
   170 		return (result && s_call_addr != NULL) ? 1 : 0;
   171 	}
   172 
   173 	return 0;
   174 }
   175 
   176 static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
   177 {
   178 	s_has_return = true;
   179 	s_return.vtype = (ValveType)params[1];
   180 	DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
   181 	s_return.decflags = params[3];
   182 	s_return.encflags = params[4];
   183 
   184 	return 1;
   185 }
   186 
   187 static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
   188 {
   189 	if (s_numparams >= SP_MAX_EXEC_PARAMS)
   190 	{
   191 		return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
   192 	}
   193 
   194 	ValvePassInfo *info = &s_params[s_numparams++];
   195 	info->vtype = (ValveType)params[1];
   196 	DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
   197 	info->decflags = params[3] | VDECODE_FLAG_BYREF;
   198 	info->encflags = params[4];
   199 
   200 	return 1;
   201 }
   202 
   203 static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
   204 {
   205 	ValveCall *vc = NULL;
   206 	if (s_vtbl_index > -1)
   207 	{
   208 		vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   209 	} else if (s_call_addr) {
   210 		vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   211 	}
   212 
   213 	if (!vc)
   214 	{
   215 		return BAD_HANDLE;
   216 	}
   217 
   218 	if (vc->thisinfo)
   219 	{
   220 		vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
   221 	}
   222 
   223 	Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
   224 	if (!hndl)
   225 	{
   226 		delete vc;
   227 	}
   228 
   229 	return hndl;
   230 }
   231 
   232 static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
   233 {
   234 	ValveCall *vc;
   235 	HandleError err;
   236 	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   237 
   238 	if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
   239 	{
   240 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   241 	}
   242 
   243 	unsigned char *ptr = vc->stk_get();
   244 
   245 	unsigned int numparams = (unsigned)params[0];
   246 	unsigned int startparam = 2;
   247 	/* Do we need to write a thispointer?  */
   248 
   249 	if (vc->thisinfo)
   250 	{
   251 		switch (vc->type)
   252 		{
   253 		case ValveCall_Entity:
   254 		case ValveCall_Player:
   255 			{
   256 				if (startparam > numparams)
   257 				{
   258 					vc->stk_put(ptr);
   259 					return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
   260 				}
   261 
   262 				if (DecodeValveParam(pContext, 
   263 					params[startparam],
   264 					vc,
   265 					vc->thisinfo,
   266 					ptr) == Data_Fail)
   267 				{
   268 					vc->stk_put(ptr);
   269 					return 0;
   270 				}
   271 				startparam++;
   272 			}
   273 			break;
   274 		case ValveCall_GameRules:
   275 			{
   276 				if (g_pGameRules == NULL)
   277 				{
   278 					vc->stk_put(ptr);
   279 					return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
   280 				}
   281 
   282 				void *gamerules = *g_pGameRules;
   283 
   284 				if (gamerules == NULL)
   285 				{
   286 					vc->stk_put(ptr);
   287 					return pContext->ThrowNativeError("GameRules not available before map is loaded");
   288 				}
   289 				*(void **)ptr = gamerules;
   290 			}
   291 			break;
   292 		}
   293 	}
   294 
   295 	/* See if we need to skip any more parameters */
   296 	unsigned int retparam = startparam;
   297 	if (vc->retinfo)
   298 	{
   299 		if (vc->retinfo->vtype == Valve_String)
   300 		{
   301 			startparam += 2;
   302 		} else if (vc->retinfo->vtype == Valve_Vector
   303 					|| vc->retinfo->vtype == Valve_QAngle)
   304 		{
   305 			startparam += 1;
   306 		}
   307 	}
   308 
   309 	unsigned int callparams = vc->call->GetParamCount();
   310 	bool will_copyback = false;
   311 	for (unsigned int i=0; i<callparams; i++)
   312 	{
   313 		unsigned int p = startparam + i;
   314 		if (p > numparams)
   315 		{
   316 			vc->stk_put(ptr);
   317 			return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
   318 		}
   319 		if (DecodeValveParam(pContext,
   320 			params[p],
   321 			vc,
   322 			&(vc->vparams[i]),
   323 			ptr) == Data_Fail)
   324 		{
   325 			vc->stk_put(ptr);
   326 			return 0;
   327 		}
   328 		if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   329 		{
   330 			will_copyback = true;
   331 		}
   332 	}
   333 
   334 	/* Make the actual call */
   335 	vc->call->Execute(ptr, vc->retbuf);
   336 
   337 	/* Do we need to copy anything back? */
   338 	if (will_copyback)
   339 	{
   340 		for (unsigned int i=0; i<callparams; i++)
   341 		{
   342 			if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   343 			{
   344 				if (EncodeValveParam(pContext, 
   345 					params[startparam + i], 
   346 					vc,
   347 					&vc->vparams[i],
   348 					ptr) == Data_Fail)
   349 				{
   350 					vc->stk_put(ptr);
   351 					return 0;
   352 				}
   353 			}
   354 		}
   355 	}
   356 
   357 	/* Save stack once and for all */
   358 	vc->stk_put(ptr);
   359 
   360 	/* Figure out how to decode the return information */
   361 	if (vc->retinfo)
   362 	{
   363 		if (vc->retinfo->vtype == Valve_String)
   364 		{
   365 			if (numparams < 3)
   366 			{
   367 				return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
   368 			}
   369 			cell_t *addr;
   370 			size_t written;
   371 			pContext->LocalToPhysAddr(params[retparam+1], &addr);
   372 			pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
   373 			return (cell_t)written;
   374 		} else if (vc->retinfo->vtype == Valve_Vector
   375 					|| vc->retinfo->vtype == Valve_QAngle)
   376 		{
   377 			if (numparams < 2)
   378 			{
   379 				return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
   380 			}
   381 			if (EncodeValveParam(pContext, params[retparam], vc, vc->retinfo, vc->retbuf)
   382 				== Data_Fail)
   383 			{
   384 				return 0;
   385 			}
   386 		} else if (vc->retinfo->vtype == Valve_CBaseEntity
   387 					|| vc->retinfo->vtype == Valve_CBasePlayer)
   388 		{
   389 			CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
   390 			if (!pEntity)
   391 			{
   392 				return -1;
   393 			}
   394 			edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   395 			if (!pEdict || pEdict->IsFree())
   396 			{
   397 				return -1;
   398 			}
   399 			return engine->IndexOfEdict(pEdict);
   400 		} else if (vc->retinfo->vtype == Valve_Edict) {
   401 			edict_t *pEdict = *(edict_t **)(vc->retbuf);
   402 			if (!pEdict || pEdict->IsFree())
   403 			{
   404 				return -1;
   405 			}
   406 			return engine->IndexOfEdict(pEdict);
   407 		} else if (vc->retinfo->vtype == Valve_Bool) {
   408 			bool *addr = (bool  *)vc->retbuf;
   409 			if (vc->retinfo->flags & PASSFLAG_BYREF)
   410 			{
   411 				addr = *(bool **)addr;
   412 			}
   413 			return *addr ? 1 : 0;
   414 		} else {
   415 			cell_t *addr = (cell_t *)vc->retbuf;
   416 			if (vc->retinfo->flags & PASSFLAG_BYREF)
   417 			{
   418 				addr = *(cell_t **)addr;
   419 			}
   420 			return *addr;
   421 		}
   422 	}
   423 
   424 	return 0;
   425 }
   426 
   427 sp_nativeinfo_t g_CallNatives[] = 
   428 {
   429 	{"StartPrepSDKCall",			StartPrepSDKCall},
   430 	{"PrepSDKCall_SetVirtual",		PrepSDKCall_SetVirtual},
   431 	{"PrepSDKCall_SetSignature",	PrepSDKCall_SetSignature},
   432 	{"PrepSDKCall_SetFromConf",		PrepSDKCall_SetFromConf},
   433 	{"PrepSDKCall_SetReturnInfo",	PrepSDKCall_SetReturnInfo},
   434 	{"PrepSDKCall_AddParameter",	PrepSDKCall_AddParameter},
   435 	{"EndPrepSDKCall",				EndPrepSDKCall},
   436 	{"SDKCall",						SDKCall},
   437 	{NULL,							NULL},
   438 };