extensions/sdktools/vdecoder.cpp
author Borja Ferrer <faluco@alliedmods.net>
Sun May 04 15:52:34 2008 +0000 (2008-05-04)
changeset 2038 a317edffcfa7
parent 1952 b7aa2b626807
child 2289 44045ead61cd
permissions -rw-r--r--
removed these functions in tf2 for now to avoid more crashes
     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 "smsdk_ext.h"
    33 #include "extension.h"
    34 #include "vdecoder.h"
    35 #include "vcallbuilder.h"
    36 
    37 using namespace SourceMod;
    38 using namespace SourcePawn;
    39 
    40 /**
    41  * For object pointers, the data looks like this instead:
    42  * 4 bytes: POINTER TO LATER
    43  * + bytes: Object internal data
    44  *
    45  * We use the virtual stack as extra fake stack space and create a temp object.
    46  * If these objects had destructors, we'd need to fake destroy toom of course.
    47  * Of course, BinTools only reads the first four bytes and passes the pointer.
    48  */
    49 
    50 size_t ValveParamToBinParam(ValveType type, 
    51 						  PassType pass,
    52 						  unsigned int flags,
    53 						  PassInfo *info,
    54 						  bool &needs_extra)
    55 {
    56 	needs_extra = false;
    57 	switch (type)
    58 	{
    59 	case Valve_Vector:
    60 		{
    61 			size_t mySize = sizeof(Vector *);
    62 			if (pass == PassType_Basic)
    63 			{
    64 				if (flags & PASSFLAG_BYREF)
    65 				{
    66 					return 0;
    67 				}
    68 				info->type = PassType_Basic;
    69 				info->flags = flags;
    70 				info->size = sizeof(Vector *);
    71 				mySize = sizeof(Vector);
    72 				needs_extra = true;
    73 			} else if (pass == PassType_Object) {
    74 				info->type = PassType_Object;
    75 				info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
    76 				info->size = sizeof(Vector);
    77 			} else {
    78 				return 0;
    79 			}
    80 			return mySize;
    81 		}
    82 	case Valve_QAngle:
    83 		{
    84 			size_t mySize = sizeof(QAngle *);
    85 			if (pass == PassType_Basic)
    86 			{
    87 				if (flags & PASSFLAG_BYREF)
    88 				{
    89 					return 0;
    90 				}
    91 				info->type = PassType_Basic;
    92 				info->flags = flags;
    93 				info->size = sizeof(QAngle *);
    94 				mySize = sizeof(QAngle);
    95 				needs_extra = true;
    96 			} else if (pass == PassType_Object) {
    97 				info->type = PassType_Object;
    98 				info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
    99 				info->size = sizeof(QAngle);
   100 			} else {
   101 				return 0;
   102 			}
   103 			return mySize;
   104 		}
   105 	case Valve_CBaseEntity:
   106 	case Valve_CBasePlayer:
   107 	case Valve_Edict:
   108 	case Valve_String:
   109 		{
   110 			if (pass != PassType_Basic || (flags & PASSFLAG_BYREF))
   111 			{
   112 				return 0;
   113 			}
   114 			info->type = PassType_Basic;
   115 			info->flags = flags;
   116 			info->size = sizeof(void *);
   117 			return sizeof(void *);
   118 		}
   119 	case Valve_POD:
   120 		{
   121 			info->type = PassType_Basic;
   122 			info->flags = flags;
   123 			if (flags & PASSFLAG_ASPOINTER)
   124 			{
   125 				needs_extra = true;
   126 				info->size = sizeof(int *);
   127 				return sizeof(int *) + sizeof(int);
   128 			} else {
   129 				info->size = sizeof(int);
   130 				return sizeof(int);
   131 			}
   132 		}
   133 	case Valve_Bool:
   134 		{
   135 			info->type = PassType_Basic;
   136 			info->flags = flags;
   137 			if (flags & PASSFLAG_ASPOINTER)
   138 			{
   139 				needs_extra = true;
   140 				info->size = sizeof(bool *);
   141 				return sizeof(bool *) + sizeof(bool);
   142 			} else {
   143 				info->size = sizeof(bool);
   144 				return sizeof(bool);
   145 			}
   146 		}
   147 	case Valve_Float:
   148 		{
   149 			info->flags = flags;
   150 			if (flags & PASSFLAG_ASPOINTER)
   151 			{
   152 				needs_extra = true;
   153 				info->type = PassType_Basic;
   154 				info->size = sizeof(float *);
   155 				return sizeof(float *) + sizeof(float);
   156 			} else {
   157 				info->type = PassType_Float;
   158 				info->size = sizeof(float);
   159 				return sizeof(float);
   160 			}
   161 		}
   162 	}
   163 
   164 	return 0;
   165 }
   166 
   167 DataStatus EncodeValveParam(IPluginContext *pContext,
   168 							cell_t param, 
   169 							const ValveCall *pCall,
   170 							const ValvePassInfo *data,
   171 							const void *_buffer)
   172 {
   173 	const void *buffer = (const unsigned char *)_buffer + data->offset;
   174 	switch (data->vtype)
   175 	{
   176 	case Valve_Vector:
   177 		{
   178 			Vector *v = NULL;
   179 
   180 			if (data->type == PassType_Basic)
   181 			{
   182 				v = *(Vector **)buffer;
   183 			} else if (data->type == PassType_Object) {
   184 				v = (Vector *)buffer;
   185 			}
   186 
   187 			cell_t *addr;
   188 			pContext->LocalToPhysAddr(param, &addr);
   189 
   190 			addr[0] = sp_ftoc(v->x);
   191 			addr[1] = sp_ftoc(v->y);
   192 			addr[2] = sp_ftoc(v->z);
   193 
   194 			return Data_Okay;
   195 		}
   196 	case Valve_QAngle:
   197 		{
   198 			QAngle *q = NULL;
   199 
   200 			if (data->type == PassType_Basic)
   201 			{
   202 				q = *(QAngle **)buffer;
   203 			} else if (data->type == PassType_Object) {
   204 				q = (QAngle *)buffer;
   205 			}
   206 
   207 			cell_t *addr;
   208 			pContext->LocalToPhysAddr(param, &addr);
   209 
   210 			addr[0] = sp_ftoc(q->x);
   211 			addr[1] = sp_ftoc(q->y);
   212 			addr[2] = sp_ftoc(q->z);
   213 
   214 			return Data_Okay;
   215 		}
   216 	case Valve_CBaseEntity:
   217 	case Valve_CBasePlayer:
   218 		{
   219 			cell_t *addr;
   220 			pContext->LocalToPhysAddr(param, &addr);
   221 
   222 			CBaseEntity *pEntity = *(CBaseEntity **)buffer;
   223 			if (pEntity)
   224 			{
   225 				edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
   226 				*addr = engine->IndexOfEdict(pEdict);
   227 			} else {
   228 				*addr = -1;
   229 			}
   230 
   231 			return Data_Okay;
   232 		}
   233 	case Valve_Edict:
   234 		{
   235 			cell_t *addr;
   236 			pContext->LocalToPhysAddr(param, &addr);
   237 
   238 			edict_t *pEdict = *(edict_t **)buffer;
   239 			if (pEdict)
   240 			{
   241 				*addr = engine->IndexOfEdict(pEdict);
   242 			} else {
   243 				*addr = -1;
   244 			}
   245 
   246 			return Data_Okay;
   247 		}
   248 	case Valve_POD:
   249 	case Valve_Float:
   250 		{
   251 			cell_t *addr;
   252 			pContext->LocalToPhysAddr(param, &addr);
   253 
   254 			if (data->flags & PASSFLAG_ASPOINTER)
   255 			{
   256 				buffer = *(cell_t **)buffer;
   257 			}
   258 
   259 			*addr = *(cell_t *)buffer;
   260 
   261 			return Data_Okay;
   262 		}
   263 	case Valve_Bool:
   264 		{
   265 			cell_t *addr;
   266 			pContext->LocalToPhysAddr(param, &addr);
   267 
   268 			if (data->flags & PASSFLAG_ASPOINTER)
   269 			{
   270 				buffer = *(bool **)buffer;
   271 			}
   272 
   273 			*addr = *(bool *)buffer ? 1 : 0;
   274 
   275 			return Data_Okay;
   276 		}
   277 	}
   278 
   279 	return Data_Fail;
   280 }
   281 
   282 DataStatus DecodeValveParam(IPluginContext *pContext,
   283 					  cell_t param,
   284 					  const ValveCall *pCall,
   285 					  const ValvePassInfo *data,
   286 					  void *_buffer)
   287 {
   288 	void *buffer = (unsigned char *)_buffer + data->offset;
   289 	switch (data->vtype)
   290 	{
   291 	case Valve_Vector:
   292 		{
   293 			cell_t *addr;
   294 			int err;
   295 			err = pContext->LocalToPhysAddr(param, &addr);
   296 
   297 			unsigned char *mem = (unsigned char *)buffer;
   298 			if (data->type == PassType_Basic)
   299 			{
   300 				/* Store the object in the next N bytes, and store
   301 				 * a pointer to that object right beforehand.
   302 				 */
   303 				Vector **realPtr = (Vector **)buffer;
   304 
   305 				if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
   306 				{
   307 					if (data->decflags & VDECODE_FLAG_ALLOWNULL)
   308 					{
   309 						*realPtr = NULL;
   310 						return Data_Okay;
   311 					} else {
   312 						pContext->ThrowNativeError("NULL not allowed");
   313 						return Data_Fail;
   314 					}
   315 				} else {
   316 					mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
   317 					*realPtr = (Vector *)mem;
   318 				}
   319 			}
   320 
   321 			if (err != SP_ERROR_NONE)
   322 			{
   323 				pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
   324 				return Data_Fail;
   325 			}
   326 
   327 			/* Use placement new to initialize the object cleanly
   328 			 * This has no destructor so we don't need to do 
   329 			 * DestroyValveParam() or something :]
   330 			 */
   331 			Vector *v = new (mem) Vector(
   332 				sp_ctof(addr[0]),
   333 				sp_ctof(addr[1]),
   334 				sp_ctof(addr[2]));
   335 
   336 			return Data_Okay;
   337 		}
   338 	case Valve_QAngle:
   339 		{
   340 			cell_t *addr;
   341 			int err;
   342 			err = pContext->LocalToPhysAddr(param, &addr);
   343 
   344 			unsigned char *mem = (unsigned char *)buffer;
   345 			if (data->type == PassType_Basic)
   346 			{
   347 				/* Store the object in the next N bytes, and store
   348 				 * a pointer to that object right beforehand.
   349 				 */
   350 				QAngle **realPtr = (QAngle **)buffer;
   351 
   352 				if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
   353 				{
   354 					if (!(data->decflags & VDECODE_FLAG_ALLOWNULL))
   355 					{
   356 						pContext->ThrowNativeError("NULL not allowed");
   357 						return Data_Fail;
   358 					} else {
   359 						*realPtr = NULL;
   360 						return Data_Okay;
   361 					}
   362 				} else {
   363 					mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
   364 					*realPtr = (QAngle *)mem;
   365 				}
   366 			}
   367 
   368 			if (err != SP_ERROR_NONE)
   369 			{
   370 				pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
   371 				return Data_Fail;
   372 			}
   373 
   374 			/* Use placement new to initialize the object cleanly
   375 			 * This has no destructor so we don't need to do 
   376 			 * DestroyValveParam() or something :]
   377 			 */
   378 			QAngle *v = new (mem) QAngle(
   379 				sp_ctof(addr[0]),
   380 				sp_ctof(addr[1]),
   381 				sp_ctof(addr[2]));
   382 
   383 			return Data_Okay;
   384 		}
   385 	case Valve_CBasePlayer:
   386 		{
   387 			edict_t *pEdict;
   388 			if (data->decflags & VDECODE_FLAG_BYREF)
   389 			{
   390 				cell_t *addr;
   391 				pContext->LocalToPhysAddr(param, &addr);
   392 				param = *addr;
   393 			}
   394 			if (param >= 1 && param <= playerhelpers->GetMaxClients())
   395 			{
   396 				IGamePlayer *player = playerhelpers->GetGamePlayer(param);
   397 				if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
   398 					&& !player->IsConnected())
   399 				{
   400 					pContext->ThrowNativeError("Client %d is not connected", param);
   401 					return Data_Fail;
   402 				} else if (!player->IsInGame()) {
   403 					pContext->ThrowNativeError("Client %d is not in game", param);
   404 					return Data_Fail;
   405 				}
   406 				pEdict = player->GetEdict();
   407 			} else if (param == -1) {
   408 				if (data->decflags & VDECODE_FLAG_ALLOWNULL)
   409 				{
   410 					pEdict = NULL;
   411 				} else {
   412 					pContext->ThrowNativeError("NULL not allowed");
   413 					return Data_Fail;
   414 				}
   415 			} else if (param == 0) {
   416 				if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
   417 				{
   418 					pEdict = engine->PEntityOfEntIndex(0);
   419 				} else {
   420 					pContext->ThrowNativeError("World not allowed");
   421 					return Data_Fail;
   422 				}
   423 			} else {
   424 				pContext->ThrowNativeError("Entity index %d is not a valid client", param);
   425 				return Data_Fail;
   426 			}
   427 			CBaseEntity *pEntity = NULL;
   428 			if (pEdict)
   429 			{
   430 				IServerUnknown *pUnknown = pEdict->GetUnknown();
   431 				if (!pUnknown)
   432 				{
   433 					pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param);
   434 					return Data_Fail;
   435 				}
   436 				pEntity = pUnknown->GetBaseEntity();
   437 				if (!pEntity)
   438 				{
   439 					pContext->ThrowNativeError("Entity %d is not a CBaseEntity", param);
   440 					return Data_Fail;
   441 				}
   442 			}
   443 
   444 			CBaseEntity **ebuf = (CBaseEntity **)buffer;
   445 			*ebuf = pEntity;
   446 
   447 			return Data_Okay;
   448 		}
   449 	case Valve_CBaseEntity:
   450 		{
   451 			edict_t *pEdict;
   452 			if (data->decflags & VDECODE_FLAG_BYREF)
   453 			{
   454 				cell_t *addr;
   455 				pContext->LocalToPhysAddr(param, &addr);
   456 				param = *addr;
   457 			}
   458 			if (param >= 1 && param <= playerhelpers->GetMaxClients())
   459 			{
   460 				IGamePlayer *player = playerhelpers->GetGamePlayer(param);
   461 				if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
   462 					&& !player->IsConnected())
   463 				{
   464 					pContext->ThrowNativeError("Client %d is not connected", param);
   465 					return Data_Fail;
   466 				} else if (!player->IsInGame()) {
   467 					pContext->ThrowNativeError("Client %d is not in game", param);
   468 					return Data_Fail;
   469 				}
   470 				pEdict = player->GetEdict();
   471 			} else if (param == -1) {
   472 				if (data->decflags & VDECODE_FLAG_ALLOWNULL)
   473 				{
   474 					pEdict = NULL;
   475 				} else {
   476 					pContext->ThrowNativeError("NULL not allowed");
   477 					return Data_Fail;
   478 				}
   479 			} else if (param == 0) {
   480 				if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
   481 				{
   482 					pEdict = engine->PEntityOfEntIndex(0);
   483 				} else {
   484 					pContext->ThrowNativeError("World not allowed");
   485 					return Data_Fail;
   486 				}
   487 			} else {
   488 				pEdict = engine->PEntityOfEntIndex(param);
   489 				if (!pEdict || pEdict->IsFree())
   490 				{
   491 					pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
   492 					return Data_Fail;
   493 				}
   494 			}
   495 			CBaseEntity *pEntity = NULL;
   496 			if (pEdict)
   497 			{
   498 				IServerUnknown *pUnknown = pEdict->GetUnknown();
   499 				if (!pUnknown)
   500 				{
   501 					pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param);
   502 					return Data_Fail;
   503 				}
   504 				pEntity = pUnknown->GetBaseEntity();
   505 				if (!pEntity)
   506 				{
   507 					pContext->ThrowNativeError("Entity %d is not a CBaseEntity", param);
   508 					return Data_Fail;
   509 				}
   510 			}
   511 
   512 			CBaseEntity **ebuf = (CBaseEntity **)buffer;
   513 			*ebuf = pEntity;
   514 
   515 			return Data_Okay;
   516 		}
   517 	case Valve_Edict:
   518 		{
   519 			edict_t *pEdict;
   520 			if (data->decflags & VDECODE_FLAG_BYREF)
   521 			{
   522 				cell_t *addr;
   523 				pContext->LocalToPhysAddr(param, &addr);
   524 				param = *addr;
   525 			}
   526 			if (param >= 1 && param <= playerhelpers->GetMaxClients())
   527 			{
   528 				IGamePlayer *player = playerhelpers->GetGamePlayer(param);
   529 				if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
   530 					&& !player->IsConnected())
   531 				{
   532 					pContext->ThrowNativeError("Client %d is not connected", param);
   533 					return Data_Fail;
   534 				} else if (!player->IsInGame()) {
   535 					pContext->ThrowNativeError("Client %d is not in game", param);
   536 					return Data_Fail;
   537 				}
   538 				pEdict = player->GetEdict();
   539 			} else if (param == -1) {
   540 				if (data->decflags & VDECODE_FLAG_ALLOWNULL)
   541 				{
   542 					pEdict = NULL;
   543 				} else {
   544 					pContext->ThrowNativeError("NULL not allowed");
   545 					return Data_Fail;
   546 				}
   547 			} else if (param == 0) {
   548 				if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
   549 				{
   550 					pEdict = engine->PEntityOfEntIndex(0);
   551 				} else {
   552 					pContext->ThrowNativeError("World not allowed");
   553 					return Data_Fail;
   554 				}
   555 			} else {
   556 				pEdict = engine->PEntityOfEntIndex(param);
   557 				if (!pEdict || pEdict->IsFree())
   558 				{
   559 					pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
   560 					return Data_Fail;
   561 				}
   562 			}
   563 
   564 			edict_t **ebuf = (edict_t **)buffer;
   565 			*ebuf = pEdict;
   566 
   567 			return Data_Okay;
   568 		}
   569 	case Valve_POD:
   570 	case Valve_Float:
   571 		{
   572 			if (data->decflags & VDECODE_FLAG_BYREF)
   573 			{
   574 				cell_t *addr;
   575 				pContext->LocalToPhysAddr(param, &addr);
   576 				param = *addr;
   577 			}
   578 			if (data->flags & PASSFLAG_ASPOINTER)
   579 			{
   580 				*(void **)buffer = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
   581 				buffer = *(void **)buffer;
   582 			}
   583 			*(cell_t *)buffer = param;
   584 			return Data_Okay;
   585 		}
   586 	case Valve_Bool:
   587 		{
   588 			if (data->decflags & VDECODE_FLAG_BYREF)
   589 			{
   590 				cell_t *addr;
   591 				pContext->LocalToPhysAddr(param, &addr);
   592 				param = *addr;
   593 			}
   594 			if (data->flags & PASSFLAG_ASPOINTER)
   595 			{
   596 				*(bool **)buffer = (bool *)((unsigned char *)_buffer + pCall->stackEnd + data->obj_offset);
   597 				buffer = *(bool **)buffer;
   598 			}
   599 			*(bool *)buffer = param ? true : false;
   600 			return Data_Okay;
   601 		}
   602 	case Valve_String:
   603 		{
   604 			char *addr;
   605 			pContext->LocalToString(param, &addr);
   606 			*(char **)buffer = addr;
   607 			return Data_Okay;
   608 		}
   609 	}
   610 
   611 	return Data_Fail;
   612 }