extensions/sdktools/vcaller.cpp
author David Anderson <dvander@alliedmods.net>
Sat Jun 16 15:40:22 2007 +0000 (2007-06-16)
changeset 953 801e27ece340
parent 950 ac855ba24c7d
child 954 3dd58fa4ccdc
permissions -rw-r--r--
added by-address calling
     1 #include "extension.h"
     2 #include "vcallbuilder.h"
     3 
     4 enum SDKLibrary
     5 {
     6 	SDKLibrary_Server,	/**< server.dll/server_i486.so */
     7 	SDKLibrary_Engine,	/**< engine.dll/engine_*.so */
     8 };
     9 
    10 enum SDKPassMethod
    11 {
    12 	SDKPass_Pointer,		/**< Pass as a pointer */
    13 	SDKPass_Plain,			/**< Pass as plain data */
    14 	SDKPass_ByValue,		/**< Pass an object by value */
    15 	SDKPass_ByRef,			/**< Pass an object by reference */
    16 };
    17 
    18 int s_vtbl_index = 0;
    19 void *s_call_addr = NULL;
    20 ValveCallType s_vcalltype = ValveCall_Static;
    21 bool s_has_return = false;
    22 ValvePassInfo s_return;
    23 unsigned int s_numparams = 0;
    24 ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
    25 
    26 inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
    27 {
    28 	if (method == SDKPass_Pointer)
    29 	{
    30 		type = PassType_Basic;
    31 		if (vtype == Valve_POD
    32 			|| vtype == Valve_Float)
    33 		{
    34 			flags = PASSFLAG_BYREF;
    35 		} else {
    36 			flags = PASSFLAG_BYVAL;
    37 		}
    38 	} else if (method == SDKPass_Plain) {
    39 		type = PassType_Basic;
    40 		flags = PASSFLAG_BYVAL;
    41 	} else if (method == SDKPass_ByValue) {
    42 		if (vtype == Valve_Vector
    43 			|| vtype == Valve_QAngle)
    44 		{
    45 			type = PassType_Object;
    46 		} else {
    47 			type = PassType_Basic;
    48 		}
    49 		flags = PASSFLAG_BYVAL;
    50 	} else if (method == SDKPass_ByRef) {
    51 		if (vtype == Valve_Vector
    52 			|| vtype == Valve_QAngle)
    53 		{
    54 			type = PassType_Object;
    55 		} else {
    56 			type = PassType_Basic;
    57 		}
    58 		flags = PASSFLAG_BYREF;
    59 	}
    60 }
    61 
    62 static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
    63 {
    64 	s_numparams = 0;
    65 	s_vtbl_index = 0;
    66 	s_call_addr = NULL;
    67 	s_has_return = false;
    68 	s_vcalltype = (ValveCallType)params[1];
    69 
    70 	return 1;
    71 }
    72 
    73 static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
    74 {
    75 	s_vtbl_index = params[1];
    76 
    77 	return 1;
    78 }
    79 
    80 static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
    81 {
    82 	void *addrInBase = NULL;
    83 	if (params[1] == SDKLibrary_Server)
    84 	{
    85 		addrInBase = g_SMAPI->serverFactory(false);
    86 	} else if (params[1] == SDKLibrary_Engine) {
    87 		addrInBase = g_SMAPI->engineFactory(false);
    88 	}
    89 	if (addrInBase == NULL)
    90 	{
    91 		return 0;
    92 	}
    93 
    94 	char *sig;
    95 	pContext->LocalToString(params[2], &sig);
    96 
    97 #if defined PLATFORM_LINUX
    98 	if (sig[0] == '@')
    99 	{
   100 		Dl_info info;
   101 		if (dladdr(addrInBase, &info) == 0)
   102 		{
   103 			return 0;
   104 		}
   105 		void *handle = dlopen(info.dli_fname, RTLD_NOW);
   106 		if (!handle)
   107 		{
   108 			return 0;
   109 		}
   110 		s_call_addr = dlsym(handle, &sig[1]);
   111 		dlclose(handle);
   112 
   113 		return (s_call_addr != NULL) ? 1 : 0;
   114 	}
   115 #endif
   116 
   117 	s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
   118 
   119 	return (s_call_addr != NULL) ? 1 : 0;
   120 }
   121 
   122 static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
   123 {
   124 	IGameConfig *conf;
   125 
   126 	if (params[1] == BAD_HANDLE)
   127 	{
   128 		conf = g_pGameConf;
   129 	} else {
   130 		HandleError err;
   131 		if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
   132 		{
   133 			return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   134 		}
   135 	}
   136 	
   137 	char *key;
   138 	pContext->LocalToString(params[3], &key);
   139 
   140 	if (params[2] == 0)
   141 	{
   142 		return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
   143 	} else if (params[2] == 1) {
   144 		bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
   145 		return (result && s_call_addr != NULL) ? 1 : 0;
   146 	}
   147 
   148 	return 0;
   149 }
   150 
   151 static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
   152 {
   153 	s_has_return = true;
   154 	s_return.vtype = (ValveType)params[1];
   155 	DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
   156 	s_return.decflags = params[3];
   157 	s_return.encflags = params[4];
   158 
   159 	return 1;
   160 }
   161 
   162 static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
   163 {
   164 	if (s_numparams >= SP_MAX_EXEC_PARAMS)
   165 	{
   166 		return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
   167 	}
   168 
   169 	ValvePassInfo *info = &s_params[s_numparams++];
   170 	info->vtype = (ValveType)params[1];
   171 	DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
   172 	info->decflags = params[3];
   173 	info->encflags = params[4];
   174 
   175 	return 1;
   176 }
   177 
   178 static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
   179 {
   180 	ValveCall *vc = NULL;
   181 	if (s_vtbl_index)
   182 	{
   183 		vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   184 	} else if (s_call_addr) {
   185 		vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
   186 	}
   187 
   188 	if (!vc)
   189 	{
   190 		return BAD_HANDLE;
   191 	}
   192 
   193 	Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
   194 	if (!hndl)
   195 	{
   196 		delete vc;
   197 	}
   198 
   199 	return hndl;
   200 }
   201 
   202 static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
   203 {
   204 	ValveCall *vc;
   205 	HandleError err;
   206 	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   207 
   208 	if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
   209 	{
   210 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
   211 	}
   212 
   213 	unsigned char *ptr = vc->stk_get();
   214 
   215 	unsigned int numparams = (unsigned)params[0];
   216 	unsigned int startparam = 2;
   217 	/* Do we need to write a thispointer?  */
   218 
   219 	if (vc->thisinfo && 
   220 		(vc->thisinfo->vtype == Valve_CBaseEntity
   221 		 || vc->thisinfo->vtype == Valve_CBasePlayer))
   222 	{
   223 		if (startparam > numparams)
   224 		{
   225 			vc->stk_put(ptr);
   226 			return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
   227 		}
   228 		if (DecodeValveParam(pContext, 
   229 			params[startparam], 
   230 			vc->thisinfo->vtype, 
   231 			vc->thisinfo->decflags | VDECODE_FLAG_BYREF,
   232 			vc->thisinfo->type,
   233 			ptr) == Data_Fail)
   234 		{
   235 			vc->stk_put(ptr);
   236 			return 0;
   237 		}
   238 		startparam++;
   239 	}
   240 
   241 	/* See if we need to skip any more parameters */
   242 	unsigned int retparam = startparam;
   243 	if (vc->retinfo)
   244 	{
   245 		if (vc->retinfo->vtype == Valve_String)
   246 		{
   247 			startparam += 2;
   248 		} else if (vc->retinfo->vtype == Valve_Vector
   249 					|| vc->retinfo->vtype == Valve_QAngle)
   250 		{
   251 			startparam += 1;
   252 		}
   253 	}
   254 
   255 	unsigned int callparams = vc->call->GetParamCount();
   256 	bool will_copyback = false;
   257 	for (unsigned int i=0; i<callparams; i++)
   258 	{
   259 		unsigned int p = startparam + i;
   260 		if (p > numparams)
   261 		{
   262 			vc->stk_put(ptr);
   263 			return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
   264 		}
   265 		if (DecodeValveParam(pContext,
   266 			params[p],
   267 			vc->vparams[i].vtype,
   268 			vc->vparams[i].decflags | VDECODE_FLAG_BYREF,
   269 			vc->vparams[i].type,
   270 			ptr + vc->vparams[i].offset) == Data_Fail)
   271 		{
   272 			vc->stk_put(ptr);
   273 			return 0;
   274 		}
   275 		if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   276 		{
   277 			will_copyback = true;
   278 		}
   279 	}
   280 
   281 	/* Make the actual call */
   282 	vc->call->Execute(ptr, vc->retbuf);
   283 
   284 	/* Do we need to copy anything back? */
   285 	if (will_copyback)
   286 	{
   287 		for (unsigned int i=0; i<callparams; i++)
   288 		{
   289 			if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
   290 			{
   291 				if (EncodeValveParam(pContext, 
   292 					startparam + i, 
   293 					vc->vparams[i].vtype, 
   294 					vc->vparams[i].type, 
   295 					ptr + vc->vparams[i].offset) == Data_Fail)
   296 				{
   297 					vc->stk_put(ptr);
   298 					return 0;
   299 				}
   300 			}
   301 		}
   302 	}
   303 
   304 	/* Save stack once and for all */
   305 	vc->stk_put(ptr);
   306 
   307 	/* Figure out how to decode the return information */
   308 	if (vc->retinfo)
   309 	{
   310 		if (vc->retinfo->vtype == Valve_String)
   311 		{
   312 			if (numparams < 3)
   313 			{
   314 				return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
   315 			}
   316 			cell_t *addr;
   317 			size_t written;
   318 			pContext->LocalToPhysAddr(params[retparam+1], &addr);
   319 			pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
   320 			return (cell_t)written;
   321 		} else if (vc->retinfo->vtype == Valve_Vector
   322 					|| vc->retinfo->vtype == Valve_QAngle)
   323 		{
   324 			if (numparams < 2)
   325 			{
   326 				return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
   327 			}
   328 			if (EncodeValveParam(pContext, params[retparam], vc->retinfo->vtype, vc->retinfo->type, vc->retbuf)
   329 				== Data_Fail)
   330 			{
   331 				return 0;
   332 			}
   333 		} else if (vc->retinfo->vtype == Valve_CBaseEntity
   334 					|| vc->retinfo->vtype == Valve_CBasePlayer)
   335 		{
   336 			CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
   337 			if (!pEntity)
   338 			{
   339 				return -1;
   340 			}
   341 			edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   342 			if (!pEdict || pEdict->IsFree())
   343 			{
   344 				return -1;
   345 			}
   346 			return engine->IndexOfEdict(pEdict);
   347 		} else if (vc->retinfo->vtype == Valve_Edict) {
   348 			edict_t *pEdict = *(edict_t **)(vc->retbuf);
   349 			if (!pEdict || pEdict->IsFree())
   350 			{
   351 				return -1;
   352 			}
   353 			return engine->IndexOfEdict(pEdict);
   354 		} else {
   355 			cell_t *addr = (cell_t *)vc->retbuf;
   356 			if (vc->retinfo->flags & PASSFLAG_BYREF)
   357 			{
   358 				addr = *(cell_t **)addr;
   359 			}
   360 			return *addr;
   361 		}
   362 	}
   363 
   364 	return 0;
   365 }
   366 
   367 sp_nativeinfo_t g_CallNatives[] = 
   368 {
   369 	{"StartPrepSDKCall",			StartPrepSDKCall},
   370 	{"PrepSDKCall_SetVirtual",		PrepSDKCall_SetVirtual},
   371 	{"PrepSDKCall_SetSignature",	PrepSDKCall_SetSignature},
   372 	{"PrepSDKCall_SetFromConf",		PrepSDKCall_SetFromConf},
   373 	{"PrepSDKCall_SetReturnInfo",	PrepSDKCall_SetReturnInfo},
   374 	{"PrepSDKCall_AddParameter",	PrepSDKCall_AddParameter},
   375 	{"EndPrepSDKCall",				EndPrepSDKCall},
   376 	{"SDKCall",						SDKCall},
   377 	{NULL,							NULL},
   378 };