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