extensions/sdktools/vcallbuilder.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 09:24:12 2008 +0000 (2008-07-11)
changeset 2195 5b70179559f9
parent 1952 b7aa2b626807
child 2289 44045ead61cd
permissions -rw-r--r--
fixed regressions in debug mode
     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 "vcallbuilder.h"
    33 #include "extension.h"
    34 
    35 ValveCall::ValveCall()
    36 {
    37 	call = NULL;
    38 	vparams = NULL;
    39 	retinfo = NULL;
    40 	thisinfo = NULL;
    41 	retbuf = NULL;
    42 }
    43 
    44 ValveCall::~ValveCall()
    45 {
    46 	while (!stk.empty())
    47 	{
    48 		unsigned char *ptr = stk.front();
    49 		delete [] ptr;
    50 		stk.pop();
    51 	}
    52 	if (call)
    53 	{
    54 		call->Destroy();
    55 	}
    56 	delete [] retbuf;
    57 	delete [] vparams;
    58 }
    59 
    60 unsigned char *ValveCall::stk_get()
    61 {
    62 	unsigned char *ptr;
    63 	if (stk.empty())
    64 	{
    65 		ptr = new unsigned char[stackSize];
    66 	} else {
    67 		ptr = stk.front();
    68 		stk.pop();
    69 	}
    70 	return ptr;
    71 }
    72 
    73 void ValveCall::stk_put(unsigned char *ptr)
    74 {
    75 	stk.push(ptr);
    76 }
    77 
    78 ValveCall *CreateValveCall(void *addr,
    79 						   ValveCallType vcalltype,
    80 						   const ValvePassInfo *retInfo,
    81 						   const ValvePassInfo *params,
    82 						   unsigned int numParams)
    83 {
    84 	if (numParams > 32)
    85 	{
    86 		return NULL;
    87 	}
    88 
    89 	ValveCall *vc = new ValveCall;
    90 
    91 	vc->type = vcalltype;
    92 
    93 	size_t size = 0;
    94 	vc->stackSize = 0;
    95 
    96 	/* Get return information - encode only */
    97 	PassInfo retBuf;
    98 	size_t retBufSize = 0;
    99 	bool retbuf_needs_extra;
   100 	if (retInfo)
   101 	{
   102 		if ((size = ValveParamToBinParam(retInfo->vtype, retInfo->type, retInfo->flags, &retBuf, retbuf_needs_extra)) == 0)
   103 		{
   104 			delete vc;
   105 			return NULL;
   106 		}
   107 		retBufSize = retBuf.size;
   108 	}
   109 
   110 	/* Get parameter info */
   111 	PassInfo paramBuf[32];
   112 	size_t sizes[32];
   113 	size_t normSize = 0;
   114 	size_t extraSize = 0;
   115 	for (unsigned int i=0; i<numParams; i++)
   116 	{
   117 		bool needs_extra;
   118 		if ((size = ValveParamToBinParam(params[i].vtype, 
   119 			params[i].type,
   120 			params[i].flags,
   121 			&paramBuf[i],
   122 			needs_extra)) == 0)
   123 		{
   124 			delete vc;
   125 			return NULL;
   126 		}
   127 		if (needs_extra)
   128 		{
   129 			sizes[i] = size;
   130 		} else {
   131 			sizes[i] = 0;
   132 		}
   133 		normSize += paramBuf[i].size;
   134 		extraSize += sizes[i];
   135 	}
   136 
   137 
   138 	/* Get thisinfo if needed */
   139 	ValvePassInfo thisbuf;
   140 	ValvePassInfo *thisinfo = NULL;
   141 	CallConvention cv = CallConv_Cdecl;
   142 	if (vcalltype != ValveCall_Static)
   143 	{
   144 		thisinfo = &thisbuf;
   145 		thisinfo->type = PassType_Basic;
   146 		switch (vcalltype)
   147 		{
   148 		case ValveCall_Entity:
   149 			thisinfo->vtype = Valve_CBaseEntity;
   150 			thisinfo->flags = PASSFLAG_BYVAL;
   151 			thisinfo->decflags |= VDECODE_FLAG_ALLOWWORLD;
   152 			break;
   153 		case ValveCall_Player:
   154 			thisinfo->vtype = Valve_CBasePlayer;
   155 			thisinfo->flags = PASSFLAG_BYVAL;
   156 			thisinfo->decflags = 0;
   157 			break;
   158 		default:
   159 			thisinfo->vtype = Valve_POD;
   160 			thisinfo->flags = PASSFLAG_ASPOINTER;
   161 			thisinfo->decflags = 0;
   162 			break;
   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 	default:
   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 }