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: { dvander@950: edict_t *pEdict = gameents->BaseEntityToEdict(pEntity); 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_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: { 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 { dvander@950: pContext->ThrowNativeError("Entity index %d is not a valid client", param); dvander@950: return Data_Fail; dvander@950: } dvander@956: CBaseEntity *pEntity = NULL; dvander@950: if (pEdict) dvander@950: { dvander@950: IServerUnknown *pUnknown = pEdict->GetUnknown(); dvander@950: if (!pUnknown) dvander@950: { faluco@1498: pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param); dvander@950: return Data_Fail; dvander@950: } dvander@950: pEntity = pUnknown->GetBaseEntity(); dvander@950: if (!pEntity) dvander@950: { faluco@1498: pContext->ThrowNativeError("Entity %d is not a CBaseEntity", 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_CBaseEntity: 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@956: CBaseEntity *pEntity = NULL; dvander@950: if (pEdict) dvander@950: { dvander@950: IServerUnknown *pUnknown = pEdict->GetUnknown(); dvander@950: if (!pUnknown) dvander@950: { faluco@1498: pContext->ThrowNativeError("Entity %d is a not an IServerUnknown", param); dvander@950: return Data_Fail; dvander@950: } dvander@950: pEntity = pUnknown->GetBaseEntity(); dvander@950: if (!pEntity) dvander@950: { faluco@1498: pContext->ThrowNativeError("Entity %d is not a CBaseEntity", 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: }