faluco@1031: /**
faluco@1031: * vim: set ts=4 :
ds@1330: * =============================================================================
dvander@1233: * SourceMod SDKTools Extension
ds@1947: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
ds@1330: * =============================================================================
faluco@1031: *
ds@1330: * This program is free software; you can redistribute it and/or modify it under
ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330: * Free Software Foundation.
dvander@1233: *
ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
ds@1330: * details.
dvander@1233: *
ds@1330: * You should have received a copy of the GNU General Public License along with
ds@1330: * this program. If not, see .
dvander@1233: *
ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in
ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants
ds@1330: * this exception to all derivative works. AlliedModders LLC defines further
ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330: * or .
faluco@1031: *
faluco@1031: * Version: $Id$
faluco@1031: */
faluco@1031:
dvander@950: #include "smsdk_ext.h"
dvander@950: #include "extension.h"
dvander@950: #include "vdecoder.h"
dvander@987: #include "vcallbuilder.h"
dvander@950:
dvander@950: using namespace SourceMod;
dvander@950: using namespace SourcePawn;
dvander@950:
dvander@950: /**
dvander@950: * For object pointers, the data looks like this instead:
dvander@987: * 4 bytes: POINTER TO LATER
dvander@950: * + bytes: Object internal data
dvander@950: *
dvander@950: * We use the virtual stack as extra fake stack space and create a temp object.
dvander@950: * If these objects had destructors, we'd need to fake destroy toom of course.
dvander@950: * Of course, BinTools only reads the first four bytes and passes the pointer.
dvander@950: */
dvander@950:
dvander@950: size_t ValveParamToBinParam(ValveType type,
dvander@950: PassType pass,
dvander@950: unsigned int flags,
dvander@987: PassInfo *info,
dvander@987: bool &needs_extra)
dvander@950: {
dvander@987: needs_extra = false;
dvander@950: switch (type)
dvander@950: {
dvander@950: case Valve_Vector:
dvander@950: {
dvander@950: size_t mySize = sizeof(Vector *);
dvander@950: if (pass == PassType_Basic)
dvander@950: {
dvander@976: if (flags & PASSFLAG_BYREF)
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950: info->type = PassType_Basic;
dvander@950: info->flags = flags;
dvander@950: info->size = sizeof(Vector *);
dvander@987: mySize = sizeof(Vector);
dvander@987: needs_extra = true;
dvander@950: } else if (pass == PassType_Object) {
dvander@950: info->type = PassType_Object;
dvander@950: info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
dvander@950: info->size = sizeof(Vector);
dvander@950: } else {
dvander@950: return 0;
dvander@950: }
dvander@950: return mySize;
dvander@950: }
dvander@950: case Valve_QAngle:
dvander@950: {
dvander@950: size_t mySize = sizeof(QAngle *);
dvander@950: if (pass == PassType_Basic)
dvander@950: {
dvander@976: if (flags & PASSFLAG_BYREF)
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950: info->type = PassType_Basic;
dvander@950: info->flags = flags;
dvander@950: info->size = sizeof(QAngle *);
dvander@987: mySize = sizeof(QAngle);
dvander@987: needs_extra = true;
dvander@950: } else if (pass == PassType_Object) {
dvander@950: info->type = PassType_Object;
dvander@950: info->flags = flags | PASSFLAG_OASSIGNOP | PASSFLAG_OCTOR;
dvander@950: info->size = sizeof(QAngle);
dvander@950: } else {
dvander@950: return 0;
dvander@950: }
dvander@950: return mySize;
dvander@950: }
dvander@950: case Valve_CBaseEntity:
dvander@950: case Valve_CBasePlayer:
dvander@950: case Valve_Edict:
dvander@950: case Valve_String:
dvander@950: {
dvander@976: if (pass != PassType_Basic || (flags & PASSFLAG_BYREF))
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950: info->type = PassType_Basic;
dvander@950: info->flags = flags;
dvander@950: info->size = sizeof(void *);
dvander@950: return sizeof(void *);
dvander@950: }
dvander@954: case Valve_POD:
dvander@954: {
dvander@954: info->type = PassType_Basic;
dvander@954: info->flags = flags;
dvander@954: if (flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: needs_extra = true;
dvander@954: info->size = sizeof(int *);
dvander@954: return sizeof(int *) + sizeof(int);
dvander@954: } else {
dvander@954: info->size = sizeof(int);
dvander@954: return sizeof(int);
dvander@954: }
dvander@954: }
dvander@954: case Valve_Bool:
dvander@954: {
dvander@954: info->type = PassType_Basic;
dvander@954: info->flags = flags;
dvander@954: if (flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: needs_extra = true;
dvander@954: info->size = sizeof(bool *);
dvander@954: return sizeof(bool *) + sizeof(bool);
dvander@954: } else {
dvander@954: info->size = sizeof(bool);
dvander@954: return sizeof(bool);
dvander@954: }
dvander@954: }
dvander@950: case Valve_Float:
dvander@950: {
dvander@950: info->flags = flags;
dvander@954: if (flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: needs_extra = true;
ds@1135: info->type = PassType_Basic;
dvander@954: info->size = sizeof(float *);
dvander@954: return sizeof(float *) + sizeof(float);
dvander@954: } else {
ds@1135: info->type = PassType_Float;
dvander@954: info->size = sizeof(float);
dvander@954: return sizeof(float);
dvander@954: }
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: return 0;
dvander@950: }
dvander@950:
dvander@950: DataStatus EncodeValveParam(IPluginContext *pContext,
dvander@950: cell_t param,
dvander@987: const ValveCall *pCall,
dvander@954: const ValvePassInfo *data,
dvander@987: const void *_buffer)
dvander@950: {
dvander@987: const void *buffer = (const unsigned char *)_buffer + data->offset;
dvander@954: switch (data->vtype)
dvander@950: {
dvander@950: case Valve_Vector:
dvander@950: {
dvander@950: Vector *v = NULL;
dvander@950:
dvander@954: if (data->type == PassType_Basic)
dvander@950: {
ds@1135: v = *(Vector **)buffer;
dvander@954: } else if (data->type == PassType_Object) {
dvander@950: v = (Vector *)buffer;
dvander@950: }
dvander@950:
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@956: addr[0] = sp_ftoc(v->x);
dvander@956: addr[1] = sp_ftoc(v->y);
dvander@956: addr[2] = sp_ftoc(v->z);
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_QAngle:
dvander@950: {
dvander@950: QAngle *q = NULL;
dvander@950:
dvander@954: if (data->type == PassType_Basic)
dvander@950: {
ds@1135: q = *(QAngle **)buffer;
dvander@954: } else if (data->type == PassType_Object) {
dvander@950: q = (QAngle *)buffer;
dvander@950: }
dvander@950:
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@956: addr[0] = sp_ftoc(q->x);
dvander@956: addr[1] = sp_ftoc(q->y);
dvander@956: addr[2] = sp_ftoc(q->z);
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_CBaseEntity:
dvander@950: case Valve_CBasePlayer:
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@950: CBaseEntity *pEntity = *(CBaseEntity **)buffer;
dvander@950: if (pEntity)
dvander@950: {
pred@2746: *addr = gamehelpers->EntityToBCompatRef(pEntity);
dvander@950: } else {
dvander@950: *addr = -1;
dvander@950: }
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_Edict:
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@950: edict_t *pEdict = *(edict_t **)buffer;
dvander@950: if (pEdict)
dvander@950: {
ds@2400: *addr = IndexOfEdict(pEdict);
dvander@950: } else {
dvander@950: *addr = -1;
dvander@950: }
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_POD:
dvander@950: case Valve_Float:
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@954: if (data->flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: buffer = *(cell_t **)buffer;
dvander@954: }
dvander@954:
dvander@950: *addr = *(cell_t *)buffer;
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@954: case Valve_Bool:
dvander@954: {
dvander@954: cell_t *addr;
dvander@954: pContext->LocalToPhysAddr(param, &addr);
dvander@954:
dvander@954: if (data->flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: buffer = *(bool **)buffer;
dvander@954: }
dvander@954:
dvander@954: *addr = *(bool *)buffer ? 1 : 0;
dvander@954:
dvander@954: return Data_Okay;
dvander@954: }
dvander@950: }
dvander@950:
dvander@950: return Data_Fail;
dvander@950: }
dvander@950:
dvander@950: DataStatus DecodeValveParam(IPluginContext *pContext,
dvander@950: cell_t param,
dvander@987: const ValveCall *pCall,
dvander@954: const ValvePassInfo *data,
dvander@987: void *_buffer)
dvander@950: {
dvander@987: void *buffer = (unsigned char *)_buffer + data->offset;
dvander@954: switch (data->vtype)
dvander@950: {
dvander@950: case Valve_Vector:
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: int err;
dvander@950: err = pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@950: unsigned char *mem = (unsigned char *)buffer;
dvander@954: if (data->type == PassType_Basic)
dvander@950: {
dvander@950: /* Store the object in the next N bytes, and store
dvander@950: * a pointer to that object right beforehand.
dvander@950: */
dvander@950: Vector **realPtr = (Vector **)buffer;
dvander@950:
dvander@950: if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
dvander@950: {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWNULL)
dvander@950: {
dvander@950: *realPtr = NULL;
dvander@950: return Data_Okay;
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("NULL not allowed");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: } else {
dvander@987: mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
dvander@950: *realPtr = (Vector *)mem;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: if (err != SP_ERROR_NONE)
dvander@950: {
dvander@950: pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950:
dvander@950: /* Use placement new to initialize the object cleanly
dvander@950: * This has no destructor so we don't need to do
dvander@950: * DestroyValveParam() or something :]
dvander@950: */
dvander@950: Vector *v = new (mem) Vector(
dvander@950: sp_ctof(addr[0]),
dvander@950: sp_ctof(addr[1]),
dvander@950: sp_ctof(addr[2]));
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_QAngle:
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: int err;
dvander@950: err = pContext->LocalToPhysAddr(param, &addr);
dvander@950:
dvander@950: unsigned char *mem = (unsigned char *)buffer;
dvander@954: if (data->type == PassType_Basic)
dvander@950: {
dvander@950: /* Store the object in the next N bytes, and store
dvander@953: * a pointer to that object right beforehand.
dvander@953: */
dvander@950: QAngle **realPtr = (QAngle **)buffer;
dvander@950:
dvander@950: if (addr == pContext->GetNullRef(SP_NULL_VECTOR))
dvander@950: {
dvander@954: if (!(data->decflags & VDECODE_FLAG_ALLOWNULL))
dvander@950: {
dvander@950: pContext->ThrowNativeError("NULL not allowed");
dvander@950: return Data_Fail;
dvander@950: } else {
dvander@950: *realPtr = NULL;
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: } else {
dvander@987: mem = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
dvander@950: *realPtr = (QAngle *)mem;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: if (err != SP_ERROR_NONE)
dvander@950: {
dvander@950: pContext->ThrowNativeErrorEx(err, "Could not read plugin data");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950:
dvander@950: /* Use placement new to initialize the object cleanly
dvander@953: * This has no destructor so we don't need to do
dvander@953: * DestroyValveParam() or something :]
dvander@953: */
dvander@950: QAngle *v = new (mem) QAngle(
dvander@950: sp_ctof(addr[0]),
dvander@950: sp_ctof(addr[1]),
dvander@950: sp_ctof(addr[2]));
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_CBasePlayer:
dvander@950: {
pred@2746: CBaseEntity *pEntity = NULL;
dvander@954: if (data->decflags & VDECODE_FLAG_BYREF)
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950: param = *addr;
dvander@950: }
pred@2746: int index = gamehelpers->ReferenceToIndex(param);
pred@2750: if ((unsigned)index == INVALID_EHANDLE_INDEX && param != -1)
dvander@950: {
pred@2746: return Data_Fail;
pred@2746: }
pred@2746: if (index >= 1 && index <= playerhelpers->GetMaxClients())
pred@2746: {
pred@2746: IGamePlayer *player = playerhelpers->GetGamePlayer(index);
dvander@954: if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
dvander@950: && !player->IsConnected())
dvander@950: {
dvander@950: pContext->ThrowNativeError("Client %d is not connected", param);
dvander@950: return Data_Fail;
dvander@950: } else if (!player->IsInGame()) {
dvander@950: pContext->ThrowNativeError("Client %d is not in game", param);
dvander@950: return Data_Fail;
dvander@950: }
pred@2746: pEntity = gamehelpers->ReferenceToEntity(param);
dvander@950: } else if (param == -1) {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWNULL)
dvander@950: {
pred@2746: pEntity = NULL;
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("NULL not allowed");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("Entity index %d is not a valid client", param);
dvander@950: return Data_Fail;
dvander@950: }
pred@2746:
dvander@950: CBaseEntity **ebuf = (CBaseEntity **)buffer;
dvander@950: *ebuf = pEntity;
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_CBaseEntity:
dvander@950: {
pred@2746: CBaseEntity *pEntity = NULL;
dvander@954: if (data->decflags & VDECODE_FLAG_BYREF)
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950: param = *addr;
dvander@950: }
pred@2746: int index = gamehelpers->ReferenceToIndex(param);
pred@2750: if ((unsigned)index == INVALID_EHANDLE_INDEX && param != -1)
dvander@950: {
pred@2746: return Data_Fail;
pred@2746: }
pred@2746: if (index >= 1 && index <= playerhelpers->GetMaxClients())
pred@2746: {
pred@2746: IGamePlayer *player = playerhelpers->GetGamePlayer(index);
dvander@954: if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
dvander@950: && !player->IsConnected())
dvander@950: {
dvander@950: pContext->ThrowNativeError("Client %d is not connected", param);
dvander@950: return Data_Fail;
dvander@950: } else if (!player->IsInGame()) {
dvander@950: pContext->ThrowNativeError("Client %d is not in game", param);
dvander@950: return Data_Fail;
dvander@950: }
pred@2746: pEntity = gamehelpers->ReferenceToEntity(param);
dvander@950: } else if (param == -1) {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWNULL)
dvander@950: {
pred@2746: pEntity = NULL;
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("NULL not allowed");
dvander@950: return Data_Fail;
dvander@950: }
pred@2746: } else if (index == 0) {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
dvander@950: {
pred@2746: pEntity = gamehelpers->ReferenceToEntity(0);
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("World not allowed");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: } else {
pred@2746: pEntity = gamehelpers->ReferenceToEntity(param);
dvander@950: if (!pEntity)
dvander@950: {
pred@2746: pContext->ThrowNativeError("Entity %d is not valid", param);
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: CBaseEntity **ebuf = (CBaseEntity **)buffer;
dvander@950: *ebuf = pEntity;
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_Edict:
dvander@950: {
dvander@950: edict_t *pEdict;
dvander@954: if (data->decflags & VDECODE_FLAG_BYREF)
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950: param = *addr;
dvander@950: }
dvander@950: if (param >= 1 && param <= playerhelpers->GetMaxClients())
dvander@950: {
dvander@950: IGamePlayer *player = playerhelpers->GetGamePlayer(param);
dvander@954: if ((data->decflags & VDECODE_FLAG_ALLOWNOTINGAME)
dvander@950: && !player->IsConnected())
dvander@950: {
dvander@950: pContext->ThrowNativeError("Client %d is not connected", param);
dvander@950: return Data_Fail;
dvander@950: } else if (!player->IsInGame()) {
dvander@950: pContext->ThrowNativeError("Client %d is not in game", param);
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: pEdict = player->GetEdict();
dvander@950: } else if (param == -1) {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWNULL)
dvander@950: {
dvander@950: pEdict = NULL;
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("NULL not allowed");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: } else if (param == 0) {
dvander@954: if (data->decflags & VDECODE_FLAG_ALLOWWORLD)
dvander@950: {
ds@2400: pEdict = PEntityOfEntIndex(0);
dvander@950: } else {
dvander@950: pContext->ThrowNativeError("World not allowed");
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: } else {
ds@2400: pEdict = PEntityOfEntIndex(param);
dvander@950: if (!pEdict || pEdict->IsFree())
dvander@950: {
dvander@950: pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
dvander@950: return Data_Fail;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: edict_t **ebuf = (edict_t **)buffer;
dvander@950: *ebuf = pEdict;
dvander@950:
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: case Valve_POD:
dvander@950: case Valve_Float:
dvander@950: {
dvander@954: if (data->decflags & VDECODE_FLAG_BYREF)
dvander@950: {
dvander@950: cell_t *addr;
dvander@950: pContext->LocalToPhysAddr(param, &addr);
dvander@950: param = *addr;
dvander@950: }
dvander@954: if (data->flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: *(void **)buffer = (unsigned char *)_buffer + pCall->stackEnd + data->obj_offset;
dvander@954: buffer = *(void **)buffer;
dvander@954: }
dvander@950: *(cell_t *)buffer = param;
dvander@950: return Data_Okay;
dvander@950: }
dvander@954: case Valve_Bool:
dvander@954: {
dvander@954: if (data->decflags & VDECODE_FLAG_BYREF)
dvander@954: {
dvander@954: cell_t *addr;
dvander@954: pContext->LocalToPhysAddr(param, &addr);
dvander@954: param = *addr;
dvander@954: }
dvander@954: if (data->flags & PASSFLAG_ASPOINTER)
dvander@954: {
ds@1135: *(bool **)buffer = (bool *)((unsigned char *)_buffer + pCall->stackEnd + data->obj_offset);
dvander@954: buffer = *(bool **)buffer;
dvander@954: }
dvander@954: *(bool *)buffer = param ? true : false;
dvander@954: return Data_Okay;
dvander@954: }
dvander@950: case Valve_String:
dvander@950: {
dvander@950: char *addr;
dvander@950: pContext->LocalToString(param, &addr);
dvander@950: *(char **)buffer = addr;
dvander@950: return Data_Okay;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: return Data_Fail;
dvander@950: }