extensions/sdktools/vcallbuilder.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 01 02:48:11 2007 +0000 (2007-08-01)
changeset 1233 e9cfc03b9bb8
parent 1135 1a04a48f0788
child 1240 79eebacc9d02
permissions -rw-r--r--
updated sdktools license headers
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod SDKTools 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  * version 3.0, as published by the Free Software Foundation.
    11  * 
    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, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include "vcallbuilder.h"
    34 #include "extension.h"
    35 
    36 ValveCall::ValveCall()
    37 {
    38 	call = NULL;
    39 	vparams = NULL;
    40 	retinfo = NULL;
    41 	thisinfo = NULL;
    42 	retbuf = NULL;
    43 }
    44 
    45 ValveCall::~ValveCall()
    46 {
    47 	while (!stk.empty())
    48 	{
    49 		unsigned char *ptr = stk.front();
    50 		delete [] ptr;
    51 		stk.pop();
    52 	}
    53 	if (call)
    54 	{
    55 		call->Destroy();
    56 	}
    57 	delete [] retbuf;
    58 	delete [] vparams;
    59 }
    60 
    61 unsigned char *ValveCall::stk_get()
    62 {
    63 	unsigned char *ptr;
    64 	if (stk.empty())
    65 	{
    66 		ptr = new unsigned char[stackSize];
    67 	} else {
    68 		ptr = stk.front();
    69 		stk.pop();
    70 	}
    71 	return ptr;
    72 }
    73 
    74 void ValveCall::stk_put(unsigned char *ptr)
    75 {
    76 	stk.push(ptr);
    77 }
    78 
    79 ValveCall *CreateValveCall(void *addr,
    80 						   ValveCallType vcalltype,
    81 						   const ValvePassInfo *retInfo,
    82 						   const ValvePassInfo *params,
    83 						   unsigned int numParams)
    84 {
    85 	if (numParams > 32)
    86 	{
    87 		return NULL;
    88 	}
    89 
    90 	ValveCall *vc = new ValveCall;
    91 
    92 	vc->type = vcalltype;
    93 
    94 	size_t size = 0;
    95 	vc->stackSize = 0;
    96 
    97 	/* Get return information - encode only */
    98 	PassInfo retBuf;
    99 	size_t retBufSize = 0;
   100 	bool retbuf_needs_extra;
   101 	if (retInfo)
   102 	{
   103 		if ((size = ValveParamToBinParam(retInfo->vtype, retInfo->type, retInfo->flags, &retBuf, retbuf_needs_extra)) == 0)
   104 		{
   105 			delete vc;
   106 			return NULL;
   107 		}
   108 		retBufSize = retBuf.size;
   109 	}
   110 
   111 	/* Get parameter info */
   112 	PassInfo paramBuf[32];
   113 	size_t sizes[32];
   114 	size_t normSize = 0;
   115 	size_t extraSize = 0;
   116 	for (unsigned int i=0; i<numParams; i++)
   117 	{
   118 		bool needs_extra;
   119 		if ((size = ValveParamToBinParam(params[i].vtype, 
   120 			params[i].type,
   121 			params[i].flags,
   122 			&paramBuf[i],
   123 			needs_extra)) == 0)
   124 		{
   125 			delete vc;
   126 			return NULL;
   127 		}
   128 		if (needs_extra)
   129 		{
   130 			sizes[i] = size;
   131 		} else {
   132 			sizes[i] = 0;
   133 		}
   134 		normSize += paramBuf[i].size;
   135 		extraSize += sizes[i];
   136 	}
   137 
   138 
   139 	/* Get thisinfo if needed */
   140 	ValvePassInfo thisbuf;
   141 	ValvePassInfo *thisinfo = NULL;
   142 	CallConvention cv = CallConv_Cdecl;
   143 	if (vcalltype != ValveCall_Static)
   144 	{
   145 		thisinfo = &thisbuf;
   146 		thisinfo->type = PassType_Basic;
   147 		switch (vcalltype)
   148 		{
   149 		case ValveCall_Entity:
   150 			thisinfo->vtype = Valve_CBaseEntity;
   151 			thisinfo->flags = PASSFLAG_BYVAL;
   152 			thisinfo->decflags |= VDECODE_FLAG_ALLOWWORLD;
   153 			break;
   154 		case ValveCall_Player:
   155 			thisinfo->vtype = Valve_CBasePlayer;
   156 			thisinfo->flags = PASSFLAG_BYVAL;
   157 			thisinfo->decflags = 0;
   158 			break;
   159 		case ValveCall_GameRules:
   160 			thisinfo->vtype = Valve_POD;
   161 			thisinfo->flags = PASSFLAG_ASPOINTER;
   162 			thisinfo->decflags = 0;
   163 		}
   164 		thisinfo->encflags = 0;
   165 		thisinfo->offset = 0;
   166 		normSize += sizeof(void *);
   167 		cv = CallConv_ThisCall;
   168 	}
   169 
   170 	/* Now we can try creating the call */
   171 	if ((vc->call = g_pBinTools->CreateCall(addr,
   172 		cv,
   173 		(retInfo ? &retBuf : NULL),
   174 		paramBuf,
   175 		numParams))
   176 		== NULL)
   177 	{
   178 		if (!vc->call)
   179 		{
   180 			delete vc;
   181 			return NULL;
   182 		}
   183 	}
   184 
   185 	/* Allocate extra space for thisptr AND ret buffer, even if we don't use it */
   186 	vc->vparams = new ValvePassInfo[numParams + 2];
   187 
   188 	/* We've got the call and everything is encoded.
   189 	 * It's time to save the valve specific information and helper variables.
   190 	 */
   191 	if (retInfo)
   192 	{
   193 		/* Allocate and copy */
   194 		vc->retinfo = &(vc->vparams[numParams]);
   195 		*vc->retinfo = *retInfo;
   196 		vc->retinfo->offset = 0;
   197 		vc->retinfo->obj_offset = retbuf_needs_extra ? sizeof(void *) : 0;
   198 		/* Allocate stack space */
   199 		vc->retbuf = new unsigned char[retBufSize];
   200 	} else {
   201 		vc->retinfo = NULL;
   202 		vc->retbuf = NULL;
   203 	}
   204 
   205 	if (thisinfo)
   206 	{
   207 		/* Allocate and copy */
   208 		vc->thisinfo = &(vc->vparams[numParams + 1]);
   209 		*vc->thisinfo = *thisinfo;
   210 		vc->thisinfo->offset = 0;
   211 		vc->thisinfo->obj_offset = 0;
   212 	} else {
   213 		vc->thisinfo = NULL;
   214 	}
   215 
   216 	/* Now, save info about each parameter. */
   217 	size_t last_extra_offset = 0;
   218 	for (unsigned int i=0; i<numParams; i++)
   219 	{
   220 		/* Copy */
   221 		vc->vparams[i] = params[i];
   222 		vc->vparams[i].offset = vc->call->GetParamInfo(i)->offset;
   223 		vc->vparams[i].obj_offset = last_extra_offset;
   224 		last_extra_offset += sizes[i];
   225 	}
   226 
   227 	vc->stackSize = normSize + extraSize;
   228 	vc->stackEnd = normSize;
   229 
   230 	return vc;
   231 }
   232 
   233 ValveCall *CreateValveVCall(unsigned int vtableIdx,
   234 							ValveCallType vcalltype,
   235 							const ValvePassInfo *retInfo,
   236 							const ValvePassInfo *params,
   237 							unsigned int numParams)
   238 {
   239 	if (numParams > 32)
   240 	{
   241 		return NULL;
   242 	}
   243 
   244 	ValveCall *vc = new ValveCall;
   245 
   246 	vc->type = vcalltype;
   247 
   248 	size_t size = 0;
   249 	vc->stackSize = 0;
   250 
   251 	/* Get return information - encode only */
   252 	PassInfo retBuf;
   253 	size_t retBufSize = 0;
   254 	bool retbuf_needs_extra;
   255 	if (retInfo)
   256 	{
   257 		if ((size = ValveParamToBinParam(retInfo->vtype, retInfo->type, retInfo->flags, &retBuf, retbuf_needs_extra)) == 0)
   258 		{
   259 			delete vc;
   260 			return NULL;
   261 		}
   262 		retBufSize = retBuf.size;
   263 	}
   264 
   265 	/* Get parameter info */
   266 	PassInfo paramBuf[32];
   267 	size_t sizes[32];
   268 	size_t normSize = 0;
   269 	size_t extraSize = 0;
   270 	for (unsigned int i=0; i<numParams; i++)
   271 	{
   272 		bool needs_extra;
   273 		if ((size = ValveParamToBinParam(params[i].vtype, 
   274 										params[i].type,
   275 										params[i].flags,
   276 										&paramBuf[i],
   277 										needs_extra)) == 0)
   278 		{
   279 			delete vc;
   280 			return NULL;
   281 		}
   282 		if (needs_extra)
   283 		{
   284 			sizes[i] = size;
   285 		} else {
   286 			sizes[i] = 0;
   287 		}
   288 		normSize += paramBuf[i].size;
   289 		extraSize += sizes[i];
   290 	}
   291 
   292 	/* Now we can try creating the call */
   293 	if ((vc->call = g_pBinTools->CreateVCall(vtableIdx, 
   294 		0, 
   295 		0, 
   296 		(retInfo ? &retBuf : NULL),
   297 		paramBuf,
   298 		numParams))
   299 		== NULL)
   300 	{
   301 		if (!vc->call)
   302 		{
   303 			delete vc;
   304 			return NULL;
   305 		}
   306 	}
   307 
   308 	/* Allocate extra space for thisptr AND ret buffer, even if we don't use it */
   309 	vc->vparams = new ValvePassInfo[numParams + 2];
   310 
   311 	/* We've got the call and everything is encoded.
   312 	 * It's time to save the valve specific information and helper variables.
   313 	 */
   314 	if (retInfo)
   315 	{
   316 		/* Allocate and copy */
   317 		vc->retinfo = &(vc->vparams[numParams]);
   318 		*vc->retinfo = *retInfo;
   319 		vc->retinfo->offset = 0;
   320 		vc->retinfo->obj_offset = retbuf_needs_extra ? sizeof(void *) : 0;
   321 		/* Allocate stack space */
   322 		vc->retbuf = new unsigned char[retBufSize];
   323 	} else {
   324 		vc->retinfo = NULL;
   325 		vc->retbuf = NULL;
   326 	}
   327 
   328 	/* Save the this info for the dynamic decoder */
   329 	vc->thisinfo = &(vc->vparams[numParams + 1]);
   330 	vc->thisinfo->type = PassType_Basic;
   331 	switch (vcalltype)
   332 	{
   333 	case ValveCall_Entity:
   334 		vc->thisinfo->vtype = Valve_CBaseEntity;
   335 		vc->thisinfo->flags = PASSFLAG_BYVAL;
   336 		vc->thisinfo->decflags = VDECODE_FLAG_ALLOWWORLD;
   337 		break;
   338 	case ValveCall_Player:
   339 		vc->thisinfo->vtype = Valve_CBasePlayer;
   340 		vc->thisinfo->flags = PASSFLAG_BYVAL;
   341 		vc->thisinfo->decflags = 0;
   342 		break;
   343 	case ValveCall_GameRules:
   344 		vc->thisinfo->vtype = Valve_POD;
   345 		vc->thisinfo->flags = PASSFLAG_ASPOINTER;
   346 		vc->thisinfo->decflags = 0;
   347 		break;
   348 	}
   349 	vc->thisinfo->encflags = 0;
   350 	vc->thisinfo->offset = 0;
   351 	vc->thisinfo->obj_offset = 0;
   352 	normSize += sizeof(void *);
   353 
   354 	/* Now, save info about each parameter. */
   355 	size_t last_extra_offset = 0;
   356 	for (unsigned int i=0; i<numParams; i++)
   357 	{
   358 		/* Copy */
   359 		vc->vparams[i] = params[i];
   360 		vc->vparams[i].offset = vc->call->GetParamInfo(i)->offset;
   361 		vc->vparams[i].obj_offset = last_extra_offset;
   362 		last_extra_offset += sizes[i];
   363 	}
   364 
   365 	vc->stackSize = normSize + extraSize;
   366 	vc->stackEnd = normSize;
   367 
   368 	return vc;
   369 }