8e5a1d58be
Fixed amb647 - Returning objects by reference wasn't working Various fixes for byref/pointer params and return values (including working copyback) --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401141
605 lines
15 KiB
C++
605 lines
15 KiB
C++
/**
|
|
* vim: set ts=4 :
|
|
* ===============================================================
|
|
* SourceMod SDK Tools Extension
|
|
* Copyright (C) 2004-2007 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
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* 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)
|
|
{
|
|
edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
|
|
*addr = engine->IndexOfEdict(pEdict);
|
|
} 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 = engine->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:
|
|
{
|
|
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 = engine->PEntityOfEntIndex(0);
|
|
} else {
|
|
pContext->ThrowNativeError("World not allowed");
|
|
return Data_Fail;
|
|
}
|
|
} else {
|
|
pContext->ThrowNativeError("Entity index %d is not a valid client", param);
|
|
return Data_Fail;
|
|
}
|
|
CBaseEntity *pEntity = NULL;
|
|
if (pEdict)
|
|
{
|
|
IServerUnknown *pUnknown = pEdict->GetUnknown();
|
|
if (!pUnknown)
|
|
{
|
|
pContext->ThrowNativeError("Entity %d is a not an IServerUnknown");
|
|
return Data_Fail;
|
|
}
|
|
pEntity = pUnknown->GetBaseEntity();
|
|
if (!pEntity)
|
|
{
|
|
pContext->ThrowNativeError("Entity %d is not a CBaseEntity");
|
|
return Data_Fail;
|
|
}
|
|
}
|
|
|
|
CBaseEntity **ebuf = (CBaseEntity **)buffer;
|
|
*ebuf = pEntity;
|
|
|
|
return Data_Okay;
|
|
}
|
|
case Valve_CBaseEntity:
|
|
{
|
|
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 = engine->PEntityOfEntIndex(0);
|
|
} else {
|
|
pContext->ThrowNativeError("World not allowed");
|
|
return Data_Fail;
|
|
}
|
|
} else {
|
|
pEdict = engine->PEntityOfEntIndex(param);
|
|
if (!pEdict || pEdict->IsFree())
|
|
{
|
|
pContext->ThrowNativeError("Entity %d is not valid or is freed", param);
|
|
return Data_Fail;
|
|
}
|
|
}
|
|
CBaseEntity *pEntity = NULL;
|
|
if (pEdict)
|
|
{
|
|
IServerUnknown *pUnknown = pEdict->GetUnknown();
|
|
if (!pUnknown)
|
|
{
|
|
pContext->ThrowNativeError("Entity %d is a not an IServerUnknown");
|
|
return Data_Fail;
|
|
}
|
|
pEntity = pUnknown->GetBaseEntity();
|
|
if (!pEntity)
|
|
{
|
|
pContext->ThrowNativeError("Entity %d is not a CBaseEntity");
|
|
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 = engine->PEntityOfEntIndex(0);
|
|
} else {
|
|
pContext->ThrowNativeError("World not allowed");
|
|
return Data_Fail;
|
|
}
|
|
} else {
|
|
pEdict = engine->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;
|
|
}
|