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