1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/extensions/sdktools/vdecoder.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,612 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod SDKTools Extension
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "smsdk_ext.h"
1.36 +#include "extension.h"
1.37 +#include "vdecoder.h"
1.38 +#include "vcallbuilder.h"
1.39 +
1.40 +using namespace SourceMod;
1.41 +using namespace SourcePawn;
1.42 +
1.43 +/**
1.44 + * For object pointers, the data looks like this instead:
1.45 + * 4 bytes: POINTER TO LATER
1.46 + * + bytes: Object internal data
1.47 + *
1.48 + * We use the virtual stack as extra fake stack space and create a temp object.
1.49 + * If these objects had destructors, we'd need to fake destroy toom of course.
1.50 + * Of course, BinTools only reads the first four bytes and passes the pointer.
1.51 + */
1.52 +
1.53 +size_t ValveParamToBinParam(ValveType type,
1.54 + PassType pass,
1.55 + unsigned int flags,
1.56 + PassInfo *info,
1.57 + bool &needs_extra)
1.58 +{
1.59 + needs_extra = false;
1.60 + switch (type)
1.61 + {
1.62 + case Valve_Vector:
1.63 + {
1.64 + size_t mySize = sizeof(Vector *);
1.65 + if (pass == PassType_Basic)
1.66 + {
1.67 + if (flags & PASSFLAG_BYREF)
1.68 + {
1.69 + return 0;
1.70 + }
1.71 + info->type = PassType_Basic;
1.72 + info->flags = flags;
1.73 + info->size = sizeof(Vector *);
1.74 + mySize = sizeof(Vector);
1.75 + needs_extra = true;
1.76 + } else if (pass == PassType_Object) {
1.77 + info->type = PassType_Object;
1.78 + info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
1.79 + info->size = sizeof(Vector);
1.80 + } else {
1.81 + return 0;
1.82 + }
1.83 + return mySize;
1.84 + }
1.85 + case Valve_QAngle:
1.86 + {
1.87 + size_t mySize = sizeof(QAngle *);
1.88 + if (pass == PassType_Basic)
1.89 + {
1.90 + if (flags & PASSFLAG_BYREF)
1.91 + {
1.92 + return 0;
1.93 + }
1.94 + info->type = PassType_Basic;
1.95 + info->flags = flags;
1.96 + info->size = sizeof(QAngle *);
1.97 + mySize = sizeof(QAngle);
1.98 + needs_extra = true;
1.99 + } else if (pass == PassType_Object) {
1.100 + info->type = PassType_Object;
1.101 + info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
1.102 + info->size = sizeof(QAngle);
1.103 + } else {
1.104 + return 0;
1.105 + }
1.106 + return mySize;
1.107 + }
1.108 + case Valve_CBaseEntity:
1.109 + case Valve_CBasePlayer:
1.110 + case Valve_Edict:
1.111 + case Valve_String:
1.112 + {
1.113 + if (pass != PassType_Basic || (flags & PASSFLAG_BYREF))
1.114 + {
1.115 + return 0;
1.116 + }
1.117 + info->type = PassType_Basic;
1.118 + info->flags = flags;
1.119 + info->size = sizeof(void *);
1.120 + return sizeof(void *);
1.121 + }
1.122 + case Valve_POD:
1.123 + {
1.124 + info->type = PassType_Basic;
1.125 + info->flags = flags;
1.126 + if (flags & PASSFLAG_ASPOINTER)
1.127 + {
1.128 + needs_extra = true;
1.129 + info->size = sizeof(int *);
1.130 + return sizeof(int *) + sizeof(int);
1.131 + } else {
1.132 + info->size = sizeof(int);
1.133 + return sizeof(int);
1.134 + }
1.135 + }
1.136 + case Valve_Bool:
1.137 + {
1.138 + info->type = PassType_Basic;
1.139 + info->flags = flags;
1.140 + if (flags & PASSFLAG_ASPOINTER)
1.141 + {
1.142 + needs_extra = true;
1.143 + info->size = sizeof(bool *);
1.144 + return sizeof(bool *) + sizeof(bool);
1.145 + } else {
1.146 + info->size = sizeof(bool);
1.147 + return sizeof(bool);
1.148 + }
1.149 + }
1.150 + case Valve_Float:
1.151 + {
1.152 + info->flags = flags;
1.153 + if (flags & PASSFLAG_ASPOINTER)
1.154 + {
1.155 + needs_extra = true;
1.156 + info->type = PassType_Basic;
1.157 + info->size = sizeof(float *);
1.158 + return sizeof(float *) + sizeof(float);
1.159 + } else {
1.160 + info->type = PassType_Float;
1.161 + info->size = sizeof(float);
1.162 + return sizeof(float);
1.163 + }
1.164 + }
1.165 + }
1.166 +
1.167 + return 0;
1.168 +}
1.169 +
1.170 +DataStatus EncodeValveParam(IPluginContext *pContext,
1.171 + cell_t param,
1.172 + const ValveCall *pCall,
1.173 + const ValvePassInfo *data,
1.174 + const void *_buffer)
1.175 +{
1.176 + const void *buffer = (const unsigned char *)_buffer + data->offset;
1.177 + switch (data->vtype)
1.178 + {
1.179 + case Valve_Vector:
1.180 + {
1.181 + Vector *v = NULL;
1.182 +
1.183 + if (data->type == PassType_Basic)
1.184 + {
1.185 + v = *(Vector **)buffer;
1.186 + } else if (data->type == PassType_Object) {
1.187 + v = (Vector *)buffer;
1.188 + }
1.189 +
1.190 + cell_t *addr;
1.191 + pContext->LocalToPhysAddr(param, &addr);
1.192 +
1.193 + addr[0] = sp_ftoc(v->x);
1.194 + addr[1] = sp_ftoc(v->y);
1.195 + addr[2] = sp_ftoc(v->z);
1.196 +
1.197 + return Data_Okay;
1.198 + }
1.199 + case Valve_QAngle:
1.200 + {
1.201 + QAngle *q = NULL;
1.202 +
1.203 + if (data->type == PassType_Basic)
1.204 + {
1.205 + q = *(QAngle **)buffer;
1.206 + } else if (data->type == PassType_Object) {
1.207 + q = (QAngle *)buffer;
1.208 + }
1.209 +
1.210 + cell_t *addr;
1.211 + pContext->LocalToPhysAddr(param, &addr);
1.212 +
1.213 + addr[0] = sp_ftoc(q->x);
1.214 + addr[1] = sp_ftoc(q->y);
1.215 + addr[2] = sp_ftoc(q->z);
1.216 +
1.217 + return Data_Okay;
1.218 + }
1.219 + case Valve_CBaseEntity:
1.220 + case Valve_CBasePlayer:
1.221 + {
1.222 + cell_t *addr;
1.223 + pContext->LocalToPhysAddr(param, &addr);
1.224 +
1.225 + CBaseEntity *pEntity = *(CBaseEntity **)buffer;
1.226 + if (pEntity)
1.227 + {
1.228 + edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
1.229 + *addr = engine->IndexOfEdict(pEdict);
1.230 + } else {
1.231 + *addr = -1;
1.232 + }
1.233 +
1.234 + return Data_Okay;
1.235 + }
1.236 + case Valve_Edict:
1.237 + {
1.238 + cell_t *addr;
1.239 + pContext->LocalToPhysAddr(param, &addr);
1.240 +
1.241 + edict_t *pEdict = *(edict_t **)buffer;
1.242 + if (pEdict)
1.243 + {
1.244 + *addr = engine->IndexOfEdict(pEdict);
1.245 + } else {
1.246 + *addr = -1;
1.247 + }
1.248 +
1.249 + return Data_Okay;
1.250 + }
1.251 + case Valve_POD:
1.252 + case Valve_Float:
1.253 + {
1.254 + cell_t *addr;
1.255 + pContext->LocalToPhysAddr(param, &addr);
1.256 +
1.257 + if (data->flags & PASSFLAG_ASPOINTER)
1.258 + {
1.259 + buffer = *(cell_t **)buffer;
1.260 + }
1.261 +
1.262 + *addr = *(cell_t *)buffer;
1.263 +
1.264 + return Data_Okay;
1.265 + }
1.266 + case Valve_Bool:
1.267 + {
1.268 + cell_t *addr;
1.269 + pContext->LocalToPhysAddr(param, &addr);
1.270 +
1.271 + if (data->flags & PASSFLAG_ASPOINTER)
1.272 + {
1.273 + buffer = *(bool **)buffer;
1.274 + }
1.275 +
1.276 + *addr = *(bool *)buffer ? 1 : 0;
1.277 +
1.278 + return Data_Okay;
1.279 + }
1.280 + }
1.281 +
1.282 + return Data_Fail;
1.283 +}
1.284 +
1.285 +DataStatus DecodeValveParam(IPluginContext *pContext,
1.286 + cell_t param,
1.287 + const ValveCall *pCall,
1.288 + const ValvePassInfo *data,
1.289 + void *_buffer)
1.290 +{
1.291 + void *buffer = (unsigned char *)_buffer + data->offset;
1.292 + switch (data->vtype)
1.293 + {
1.294 + case Valve_Vector:
1.295 + {
1.296 + cell_t *addr;
1.297 + int err;
1.298 + err = pContext->LocalToPhysAddr(param, &addr);
1.299 +
1.300 + unsigned char *mem = (unsigned char *)buffer;
1.301 + if (data->type == PassType_Basic)
1.302 + {
1.303 + /* Store the object in the next N bytes, and store
1.304 + * a pointer to that object right beforehand.
1.305 + */
1.306 + Vector **realPtr = (Vector **)buffer;
1.307 +
1.308 + if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
1.309 + {
1.310 + if (data->decflags & VDECODE_FLAG_ALLOWNULL)
1.311 + {
1.312 + *realPtr = NULL;
1.313 + return Data_Okay;
1.314 + } else {
1.315 + pContext->ThrowNativeError("NULL not allowed");
1.316 + return Data_Fail;
1.317 + }
1.318 + } else {
1.319 + mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
1.320 + *realPtr = (Vector *)mem;
1.321 + }
1.322 + }
1.323 +
1.324 + if (err != SP_ERROR_NONE)
1.325 + {
1.326 + pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
1.327 + return Data_Fail;
1.328 + }
1.329 +
1.330 + /* Use placement new to initialize the object cleanly
1.331 + * This has no destructor so we don't need to do
1.332 + * DestroyValveParam() or something :]
1.333 + */
1.334 + Vector *v = new (mem) Vector(
1.335 + sp_ctof(addr[0]),
1.336 + sp_ctof(addr[1]),
1.337 + sp_ctof(addr[2]));
1.338 +
1.339 + return Data_Okay;
1.340 + }
1.341 + case Valve_QAngle:
1.342 + {
1.343 + cell_t *addr;
1.344 + int err;
1.345 + err = pContext->LocalToPhysAddr(param, &addr);
1.346 +
1.347 + unsigned char *mem = (unsigned char *)buffer;
1.348 + if (data->type == PassType_Basic)
1.349 + {
1.350 + /* Store the object in the next N bytes, and store
1.351 + * a pointer to that object right beforehand.
1.352 + */
1.353 + QAngle **realPtr = (QAngle **)buffer;
1.354 +
1.355 + if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
1.356 + {
1.357 + if (!(data->decflags & VDECODE_FLAG_ALLOWNULL))
1.358 + {
1.359 + pContext->ThrowNativeError("NULL not allowed");
1.360 + return Data_Fail;
1.361 + } else {
1.362 + *realPtr = NULL;
1.363 + return Data_Okay;
1.364 + }
1.365 + } else {
1.366 + mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
1.367 + *realPtr = (QAngle *)mem;
1.368 + }
1.369 + }
1.370 +
1.371 + if (err != SP_ERROR_NONE)
1.372 + {
1.373 + pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
1.374 + return Data_Fail;
1.375 + }
1.376 +
1.377 + /* Use placement new to initialize the object cleanly
1.378 + * This has no destructor so we don't need to do
1.379 + * DestroyValveParam() or something :]
1.380 + */
1.381 + QAngle *v = new (mem) QAngle(
1.382 + sp_ctof(addr[0]),
1.383 + sp_ctof(addr[1]),
1.384 + sp_ctof(addr[2]));
1.385 +
1.386 + return Data_Okay;
1.387 + }
1.388 + case Valve_CBasePlayer:
1.389 + {
1.390 + edict_t *pEdict;
1.391 + if (data->decflags & VDECODE_FLAG_BYREF)
1.392 + {
1.393 + cell_t *addr;
1.394 + pContext->LocalToPhysAddr(param, &addr);
1.395 + param = *addr;
1.396 + }
1.397 + if (param >= 1 && param <= playerhelpers->GetMaxClients())
1.398 + {
1.399 + IGamePlayer *player = playerhelpers->GetGamePlayer(param);
1.400 + if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
1.401 + && !player->IsConnected())
1.402 + {
1.403 + pContext->ThrowNativeError("Client %d is not connected", param);
1.404 + return Data_Fail;
1.405 + } else if (!player->IsInGame()) {
1.406 + pContext->ThrowNativeError("Client %d is not in game", param);
1.407 + return Data_Fail;
1.408 + }
1.409 + pEdict = player->GetEdict();
1.410 + } else if (param == -1) {
1.411 + if (data->decflags & VDECODE_FLAG_ALLOWNULL)
1.412 + {
1.413 + pEdict = NULL;
1.414 + } else {
1.415 + pContext->ThrowNativeError("NULL not allowed");
1.416 + return Data_Fail;
1.417 + }
1.418 + } else if (param == 0) {
1.419 + if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
1.420 + {
1.421 + pEdict = engine->PEntityOfEntIndex(0);
1.422 + } else {
1.423 + pContext->ThrowNativeError("World not allowed");
1.424 + return Data_Fail;
1.425 + }
1.426 + } else {
1.427 + pContext->ThrowNativeError("Entity index %d is not a valid client", param);
1.428 + return Data_Fail;
1.429 + }
1.430 + CBaseEntity *pEntity = NULL;
1.431 + if (pEdict)
1.432 + {
1.433 + IServerUnknown *pUnknown = pEdict->GetUnknown();
1.434 + if (!pUnknown)
1.435 + {
1.436 + pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param);
1.437 + return Data_Fail;
1.438 + }
1.439 + pEntity = pUnknown->GetBaseEntity();
1.440 + if (!pEntity)
1.441 + {
1.442 + pContext->ThrowNativeError("Entity %d is not a CBaseEntity", param);
1.443 + return Data_Fail;
1.444 + }
1.445 + }
1.446 +
1.447 + CBaseEntity **ebuf = (CBaseEntity **)buffer;
1.448 + *ebuf = pEntity;
1.449 +
1.450 + return Data_Okay;
1.451 + }
1.452 + case Valve_CBaseEntity:
1.453 + {
1.454 + edict_t *pEdict;
1.455 + if (data->decflags & VDECODE_FLAG_BYREF)
1.456 + {
1.457 + cell_t *addr;
1.458 + pContext->LocalToPhysAddr(param, &addr);
1.459 + param = *addr;
1.460 + }
1.461 + if (param >= 1 && param <= playerhelpers->GetMaxClients())
1.462 + {
1.463 + IGamePlayer *player = playerhelpers->GetGamePlayer(param);
1.464 + if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
1.465 + && !player->IsConnected())
1.466 + {
1.467 + pContext->ThrowNativeError("Client %d is not connected", param);
1.468 + return Data_Fail;
1.469 + } else if (!player->IsInGame()) {
1.470 + pContext->ThrowNativeError("Client %d is not in game", param);
1.471 + return Data_Fail;
1.472 + }
1.473 + pEdict = player->GetEdict();
1.474 + } else if (param == -1) {
1.475 + if (data->decflags & VDECODE_FLAG_ALLOWNULL)
1.476 + {
1.477 + pEdict = NULL;
1.478 + } else {
1.479 + pContext->ThrowNativeError("NULL not allowed");
1.480 + return Data_Fail;
1.481 + }
1.482 + } else if (param == 0) {
1.483 + if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
1.484 + {
1.485 + pEdict = engine->PEntityOfEntIndex(0);
1.486 + } else {
1.487 + pContext->ThrowNativeError("World not allowed");
1.488 + return Data_Fail;
1.489 + }
1.490 + } else {
1.491 + pEdict = engine->PEntityOfEntIndex(param);
1.492 + if (!pEdict || pEdict->IsFree())
1.493 + {
1.494 + pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
1.495 + return Data_Fail;
1.496 + }
1.497 + }
1.498 + CBaseEntity *pEntity = NULL;
1.499 + if (pEdict)
1.500 + {
1.501 + IServerUnknown *pUnknown = pEdict->GetUnknown();
1.502 + if (!pUnknown)
1.503 + {
1.504 + pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param);
1.505 + return Data_Fail;
1.506 + }
1.507 + pEntity = pUnknown->GetBaseEntity();
1.508 + if (!pEntity)
1.509 + {
1.510 + pContext->ThrowNativeError("Entity %d is not a CBaseEntity", param);
1.511 + return Data_Fail;
1.512 + }
1.513 + }
1.514 +
1.515 + CBaseEntity **ebuf = (CBaseEntity **)buffer;
1.516 + *ebuf = pEntity;
1.517 +
1.518 + return Data_Okay;
1.519 + }
1.520 + case Valve_Edict:
1.521 + {
1.522 + edict_t *pEdict;
1.523 + if (data->decflags & VDECODE_FLAG_BYREF)
1.524 + {
1.525 + cell_t *addr;
1.526 + pContext->LocalToPhysAddr(param, &addr);
1.527 + param = *addr;
1.528 + }
1.529 + if (param >= 1 && param <= playerhelpers->GetMaxClients())
1.530 + {
1.531 + IGamePlayer *player = playerhelpers->GetGamePlayer(param);
1.532 + if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
1.533 + && !player->IsConnected())
1.534 + {
1.535 + pContext->ThrowNativeError("Client %d is not connected", param);
1.536 + return Data_Fail;
1.537 + } else if (!player->IsInGame()) {
1.538 + pContext->ThrowNativeError("Client %d is not in game", param);
1.539 + return Data_Fail;
1.540 + }
1.541 + pEdict = player->GetEdict();
1.542 + } else if (param == -1) {
1.543 + if (data->decflags & VDECODE_FLAG_ALLOWNULL)
1.544 + {
1.545 + pEdict = NULL;
1.546 + } else {
1.547 + pContext->ThrowNativeError("NULL not allowed");
1.548 + return Data_Fail;
1.549 + }
1.550 + } else if (param == 0) {
1.551 + if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
1.552 + {
1.553 + pEdict = engine->PEntityOfEntIndex(0);
1.554 + } else {
1.555 + pContext->ThrowNativeError("World not allowed");
1.556 + return Data_Fail;
1.557 + }
1.558 + } else {
1.559 + pEdict = engine->PEntityOfEntIndex(param);
1.560 + if (!pEdict || pEdict->IsFree())
1.561 + {
1.562 + pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
1.563 + return Data_Fail;
1.564 + }
1.565 + }
1.566 +
1.567 + edict_t **ebuf = (edict_t **)buffer;
1.568 + *ebuf = pEdict;
1.569 +
1.570 + return Data_Okay;
1.571 + }
1.572 + case Valve_POD:
1.573 + case Valve_Float:
1.574 + {
1.575 + if (data->decflags & VDECODE_FLAG_BYREF)
1.576 + {
1.577 + cell_t *addr;
1.578 + pContext->LocalToPhysAddr(param, &addr);
1.579 + param = *addr;
1.580 + }
1.581 + if (data->flags & PASSFLAG_ASPOINTER)
1.582 + {
1.583 + *(void **)buffer = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
1.584 + buffer = *(void **)buffer;
1.585 + }
1.586 + *(cell_t *)buffer = param;
1.587 + return Data_Okay;
1.588 + }
1.589 + case Valve_Bool:
1.590 + {
1.591 + if (data->decflags & VDECODE_FLAG_BYREF)
1.592 + {
1.593 + cell_t *addr;
1.594 + pContext->LocalToPhysAddr(param, &addr);
1.595 + param = *addr;
1.596 + }
1.597 + if (data->flags & PASSFLAG_ASPOINTER)
1.598 + {
1.599 + *(bool **)buffer = (bool *)((unsigned char *)_buffer + pCall->stackEnd + data->obj_offset);
1.600 + buffer = *(bool **)buffer;
1.601 + }
1.602 + *(bool *)buffer = param ? true : false;
1.603 + return Data_Okay;
1.604 + }
1.605 + case Valve_String:
1.606 + {
1.607 + char *addr;
1.608 + pContext->LocalToString(param, &addr);
1.609 + *(char **)buffer = addr;
1.610 + return Data_Okay;
1.611 + }
1.612 + }
1.613 +
1.614 + return Data_Fail;
1.615 +}