faluco@1031: /**
ds@2863: * vim: set ts=4 sw=4 tw=99 noet :
ds@1330: * =============================================================================
dvander@1233: * SourceMod SDKTools Extension
ds@2863: * Copyright (C) 2004-2009 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:
dvander@950: #include "extension.h"
dvander@950: #include "vcallbuilder.h"
ds@1240: #include "vglobals.h"
dvander@950:
dvander@950: enum SDKLibrary
dvander@950: {
dvander@950: SDKLibrary_Server, /**< server.dll/server_i486.so */
dvander@950: SDKLibrary_Engine, /**< engine.dll/engine_*.so */
dvander@950: };
dvander@950:
dvander@950: enum SDKPassMethod
dvander@950: {
dvander@950: SDKPass_Pointer, /**< Pass as a pointer */
dvander@950: SDKPass_Plain, /**< Pass as plain data */
dvander@950: SDKPass_ByValue, /**< Pass an object by value */
dvander@950: SDKPass_ByRef, /**< Pass an object by reference */
dvander@950: };
dvander@950:
ds@1042: int s_vtbl_index = -1;
dvander@950: void *s_call_addr = NULL;
dvander@950: ValveCallType s_vcalltype = ValveCall_Static;
dvander@950: bool s_has_return = false;
dvander@950: ValvePassInfo s_return;
dvander@950: unsigned int s_numparams = 0;
dvander@950: ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
dvander@950:
dvander@950: inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
dvander@950: {
ds@1135: if (method == SDKPass_Pointer || method == SDKPass_ByRef)
dvander@950: {
dvander@950: type = PassType_Basic;
dvander@950: if (vtype == Valve_POD
dvander@954: || vtype == Valve_Float
dvander@954: || vtype == Valve_Bool)
dvander@950: {
ds@1135: flags = PASSFLAG_BYVAL | PASSFLAG_ASPOINTER;
dvander@950: } else {
dvander@950: flags = PASSFLAG_BYVAL;
dvander@950: }
dvander@950: } else if (method == SDKPass_Plain) {
dvander@950: type = PassType_Basic;
dvander@950: flags = PASSFLAG_BYVAL;
dvander@950: } else if (method == SDKPass_ByValue) {
dvander@950: if (vtype == Valve_Vector
dvander@950: || vtype == Valve_QAngle)
dvander@950: {
dvander@950: type = PassType_Object;
dvander@950: } else {
dvander@950: type = PassType_Basic;
dvander@950: }
dvander@950: flags = PASSFLAG_BYVAL;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: s_numparams = 0;
ds@1042: s_vtbl_index = -1;
dvander@950: s_call_addr = NULL;
dvander@950: s_has_return = false;
dvander@950: s_vcalltype = (ValveCallType)params[1];
dvander@950:
dvander@950: return 1;
dvander@950: }
dvander@950:
dvander@950: static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: s_vtbl_index = params[1];
dvander@950:
dvander@950: return 1;
dvander@950: }
dvander@950:
dvander@950: static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: void *addrInBase = NULL;
dvander@950: if (params[1] == SDKLibrary_Server)
dvander@950: {
faluco@1560: addrInBase = (void *)g_SMAPI->GetServerFactory(false);
dvander@950: } else if (params[1] == SDKLibrary_Engine) {
faluco@1560: addrInBase = (void *)g_SMAPI->GetEngineFactory(false);
dvander@950: }
dvander@950: if (addrInBase == NULL)
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950:
dvander@950: char *sig;
dvander@950: pContext->LocalToString(params[2], &sig);
dvander@950:
ds@3027: #if defined PLATFORM_POSIX
dvander@950: if (sig[0] == '@')
dvander@950: {
dvander@950: Dl_info info;
dvander@950: if (dladdr(addrInBase, &info) == 0)
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950: void *handle = dlopen(info.dli_fname, RTLD_NOW);
dvander@950: if (!handle)
dvander@950: {
dvander@950: return 0;
dvander@950: }
skamonkey@4141: #if SOURCE_ENGINE == SE_CSS \
skamonkey@4141: || SOURCE_ENGINE == SE_HL2DM \
skamonkey@4141: || SOURCE_ENGINE == SE_DODS \
skamonkey@4141: || SOURCE_ENGINE == SE_SDK2013 \
skamonkey@4141: || SOURCE_ENGINE == SE_TF2 \
skamonkey@4141: || SOURCE_ENGINE == SE_LEFT4DEAD \
skamonkey@4141: || SOURCE_ENGINE == SE_LEFT4DEAD2 \
skamonkey@4141: || SOURCE_ENGINE == SE_NUCLEARDAWN \
skamonkey@4142: || SOURCE_ENGINE == SE_BLADE \
skamonkey@4144: || SOURCE_ENGINE == SE_INSURGENCY \
skamonkey@4141: || SOURCE_ENGINE == SE_CSGO
ds@2863: s_call_addr = memutils->ResolveSymbol(handle, &sig[1]);
ds@2863: #else
dvander@950: s_call_addr = dlsym(handle, &sig[1]);
ds@2863: #endif
dvander@950: dlclose(handle);
dvander@950:
dvander@950: return (s_call_addr != NULL) ? 1 : 0;
dvander@950: }
dvander@950: #endif
dvander@950:
dvander@950: s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
dvander@950:
dvander@950: return (s_call_addr != NULL) ? 1 : 0;
dvander@950: }
dvander@950:
dvander@950: static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: IGameConfig *conf;
dvander@950:
dvander@950: if (params[1] == BAD_HANDLE)
dvander@950: {
dvander@950: conf = g_pGameConf;
dvander@950: } else {
dvander@950: HandleError err;
dvander@950: if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
dvander@950: {
dvander@950: return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: char *key;
dvander@950: pContext->LocalToString(params[3], &key);
dvander@950:
dvander@950: if (params[2] == 0)
dvander@950: {
dvander@950: return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
dvander@950: } else if (params[2] == 1) {
dvander@950: bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
dvander@950: return (result && s_call_addr != NULL) ? 1 : 0;
dvander@950: }
dvander@950:
dvander@950: return 0;
dvander@950: }
dvander@950:
dvander@950: static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: s_has_return = true;
dvander@950: s_return.vtype = (ValveType)params[1];
dvander@950: DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
dvander@950: s_return.decflags = params[3];
dvander@950: s_return.encflags = params[4];
dvander@950:
dvander@950: return 1;
dvander@950: }
dvander@950:
dvander@950: static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: if (s_numparams >= SP_MAX_EXEC_PARAMS)
dvander@950: {
dvander@950: return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
dvander@950: }
dvander@950:
dvander@950: ValvePassInfo *info = &s_params[s_numparams++];
dvander@950: info->vtype = (ValveType)params[1];
ds@1135: SDKPassMethod method = (SDKPassMethod)params[2];
ds@1135: DecodePassMethod(info->vtype, method, info->type, info->flags);
dvander@954: info->decflags = params[3] | VDECODE_FLAG_BYREF;
dvander@950: info->encflags = params[4];
dvander@950:
ds@1135: /* Since SDKPass_ByRef acts like SDKPass_Pointer we can't allow NULL, just in case */
ds@1135: if (method == SDKPass_ByRef)
ds@1135: {
ds@1135: info->decflags &= ~VDECODE_FLAG_ALLOWNULL;
ds@1135: }
ds@1135:
dvander@950: return 1;
dvander@950: }
dvander@950:
dvander@950: static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: ValveCall *vc = NULL;
ds@1042: if (s_vtbl_index > -1)
dvander@950: {
dvander@950: vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@953: } else if (s_call_addr) {
dvander@953: vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@950: }
dvander@950:
dvander@950: if (!vc)
dvander@950: {
dvander@950: return BAD_HANDLE;
dvander@950: }
dvander@950:
dvander@968: if (vc->thisinfo)
dvander@968: {
dvander@968: vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
dvander@968: }
dvander@968:
dvander@950: Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
dvander@950: if (!hndl)
dvander@950: {
dvander@950: delete vc;
dvander@950: }
dvander@950:
dvander@950: return hndl;
dvander@950: }
dvander@950:
dvander@950: static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950: {
dvander@950: ValveCall *vc;
dvander@950: HandleError err;
dvander@950: HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
dvander@950:
dvander@950: if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
dvander@950: {
dvander@950: return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950: }
dvander@950:
dvander@950: unsigned char *ptr = vc->stk_get();
dvander@950:
downtown1@2901: const unsigned int numparams = (unsigned)params[0];
dvander@950: unsigned int startparam = 2;
dvander@950: /* Do we need to write a thispointer? */
dvander@950:
ds@1065: if (vc->thisinfo)
dvander@950: {
ds@1065: switch (vc->type)
dvander@950: {
ds@1065: case ValveCall_Entity:
ds@1065: case ValveCall_Player:
ds@1065: {
ds@1065: if (startparam > numparams)
ds@1065: {
ds@1065: vc->stk_put(ptr);
ds@1065: return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
ds@1065: }
ds@1065:
ds@1065: if (DecodeValveParam(pContext,
ds@1065: params[startparam],
ds@1065: vc,
ds@1065: vc->thisinfo,
ds@1065: ptr) == Data_Fail)
ds@1065: {
ds@1065: vc->stk_put(ptr);
ds@1065: return 0;
ds@1065: }
ds@1065: startparam++;
ds@1065: }
ds@1065: break;
ds@1065: case ValveCall_GameRules:
ds@1065: {
ds@1065: if (g_pGameRules == NULL)
ds@1065: {
ds@1065: vc->stk_put(ptr);
ds@1065: return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
ds@1065: }
ds@1065:
ds@1065: void *gamerules = *g_pGameRules;
ds@1065:
ds@1065: if (gamerules == NULL)
ds@1065: {
ds@1065: vc->stk_put(ptr);
ds@1065: return pContext->ThrowNativeError("GameRules not available before map is loaded");
ds@1065: }
ds@1065: *(void **)ptr = gamerules;
ds@1065: }
ds@1065: break;
ds@1240: case ValveCall_EntityList:
ds@1240: {
ds@1240: if (g_EntList == NULL)
ds@1240: {
ds@1240: vc->stk_put(ptr);
ds@1240: return pContext->ThrowNativeError("EntityList unsupported or not available; file a bug report");
ds@1240: }
ds@1240:
ds@1240: *(void **)ptr = g_EntList;
ds@1240: }
ds@1240: break;
downtown1@2901: case ValveCall_Raw:
downtown1@2901: {
downtown1@2901: //params[startparam] is an address to a pointer to THIS
downtown1@2901: //params following this are params to the method we will invoke later
downtown1@2901: if (startparam > numparams)
downtown1@2901: {
downtown1@2901: vc->stk_put(ptr);
downtown1@2901: return pContext->ThrowNativeError("Expected a ThisPtr address, it wasn't found");
downtown1@2901: }
downtown1@2901:
downtown1@2901: //note: varargs pawn args are passed by-ref
downtown1@2901: cell_t *cell;
downtown1@2901: pContext->LocalToPhysAddr(params[startparam], &cell);
downtown1@2901: void *thisptr = reinterpret_cast(*cell);
downtown1@2901:
downtown1@2901: *(void **)ptr = thisptr;
downtown1@2901: startparam++;
downtown1@2901: }
downtown1@2901: break;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: /* See if we need to skip any more parameters */
dvander@950: unsigned int retparam = startparam;
dvander@950: if (vc->retinfo)
dvander@950: {
dvander@950: if (vc->retinfo->vtype == Valve_String)
dvander@950: {
dvander@950: startparam += 2;
dvander@950: } else if (vc->retinfo->vtype == Valve_Vector
dvander@950: || vc->retinfo->vtype == Valve_QAngle)
dvander@950: {
dvander@950: startparam += 1;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: unsigned int callparams = vc->call->GetParamCount();
dvander@950: bool will_copyback = false;
dvander@950: for (unsigned int i=0; i numparams)
dvander@950: {
dvander@950: vc->stk_put(ptr);
dvander@950: return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
dvander@950: }
dvander@950: if (DecodeValveParam(pContext,
dvander@950: params[p],
dvander@987: vc,
dvander@954: &(vc->vparams[i]),
dvander@987: ptr) == Data_Fail)
dvander@950: {
dvander@950: vc->stk_put(ptr);
dvander@950: return 0;
dvander@950: }
dvander@950: if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950: {
dvander@950: will_copyback = true;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: /* Make the actual call */
dvander@950: vc->call->Execute(ptr, vc->retbuf);
dvander@950:
dvander@950: /* Do we need to copy anything back? */
dvander@950: if (will_copyback)
dvander@950: {
dvander@950: for (unsigned int i=0; ivparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950: {
dvander@950: if (EncodeValveParam(pContext,
ds@1130: params[startparam + i],
dvander@987: vc,
dvander@954: &vc->vparams[i],
dvander@987: ptr) == Data_Fail)
dvander@950: {
dvander@950: vc->stk_put(ptr);
dvander@950: return 0;
dvander@950: }
dvander@950: }
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: /* Save stack once and for all */
dvander@950: vc->stk_put(ptr);
dvander@950:
dvander@950: /* Figure out how to decode the return information */
dvander@950: if (vc->retinfo)
dvander@950: {
dvander@950: if (vc->retinfo->vtype == Valve_String)
dvander@950: {
dvander@950: if (numparams < 3)
dvander@950: {
dvander@950: return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
dvander@950: }
dvander@950: cell_t *addr;
dvander@950: size_t written;
dvander@950: pContext->LocalToPhysAddr(params[retparam+1], &addr);
dvander@950: pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
dvander@950: return (cell_t)written;
dvander@950: } else if (vc->retinfo->vtype == Valve_Vector
dvander@950: || vc->retinfo->vtype == Valve_QAngle)
dvander@950: {
dvander@950: if (numparams < 2)
dvander@950: {
dvander@950: return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
dvander@950: }
dvander@987: if (EncodeValveParam(pContext, params[retparam], vc, vc->retinfo, vc->retbuf)
dvander@950: == Data_Fail)
dvander@950: {
dvander@950: return 0;
dvander@950: }
dvander@950: } else if (vc->retinfo->vtype == Valve_CBaseEntity
dvander@950: || vc->retinfo->vtype == Valve_CBasePlayer)
dvander@950: {
dvander@950: CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
dvander@950: if (!pEntity)
dvander@950: {
dvander@950: return -1;
dvander@950: }
dvander@950: edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
dvander@950: if (!pEdict || pEdict->IsFree())
dvander@950: {
dvander@950: return -1;
dvander@950: }
ds@2400: return IndexOfEdict(pEdict);
dvander@950: } else if (vc->retinfo->vtype == Valve_Edict) {
dvander@950: edict_t *pEdict = *(edict_t **)(vc->retbuf);
dvander@950: if (!pEdict || pEdict->IsFree())
dvander@950: {
dvander@950: return -1;
dvander@950: }
ds@2400: return IndexOfEdict(pEdict);
dvander@954: } else if (vc->retinfo->vtype == Valve_Bool) {
dvander@954: bool *addr = (bool *)vc->retbuf;
ds@1135: if (vc->retinfo->flags & PASSFLAG_ASPOINTER)
dvander@954: {
dvander@954: addr = *(bool **)addr;
dvander@954: }
dvander@954: return *addr ? 1 : 0;
dvander@950: } else {
dvander@950: cell_t *addr = (cell_t *)vc->retbuf;
ds@1135: if (vc->retinfo->flags & PASSFLAG_ASPOINTER)
dvander@950: {
dvander@950: addr = *(cell_t **)addr;
dvander@950: }
dvander@950: return *addr;
dvander@950: }
dvander@950: }
dvander@950:
dvander@950: return 0;
dvander@950: }
dvander@950:
dvander@950: sp_nativeinfo_t g_CallNatives[] =
dvander@950: {
dvander@950: {"StartPrepSDKCall", StartPrepSDKCall},
dvander@950: {"PrepSDKCall_SetVirtual", PrepSDKCall_SetVirtual},
dvander@950: {"PrepSDKCall_SetSignature", PrepSDKCall_SetSignature},
dvander@950: {"PrepSDKCall_SetFromConf", PrepSDKCall_SetFromConf},
dvander@950: {"PrepSDKCall_SetReturnInfo", PrepSDKCall_SetReturnInfo},
dvander@950: {"PrepSDKCall_AddParameter", PrepSDKCall_AddParameter},
dvander@950: {"EndPrepSDKCall", EndPrepSDKCall},
dvander@950: {"SDKCall", SDKCall},
dvander@950: {NULL, NULL},
dvander@950: };