3 * ===============================================================
4 * SourceMod SDK Tools Extension
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ===============================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "extension.h"
25 #include "vcallbuilder.h"
26 #include "gamerules.h"
30 SDKLibrary_Server, /**< server.dll/server_i486.so */
31 SDKLibrary_Engine, /**< engine.dll/engine_*.so */
36 SDKPass_Pointer, /**< Pass as a pointer */
37 SDKPass_Plain, /**< Pass as plain data */
38 SDKPass_ByValue, /**< Pass an object by value */
39 SDKPass_ByRef, /**< Pass an object by reference */
42 int s_vtbl_index = -1;
43 void *s_call_addr = NULL;
44 ValveCallType s_vcalltype = ValveCall_Static;
45 bool s_has_return = false;
46 ValvePassInfo s_return;
47 unsigned int s_numparams = 0;
48 ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
50 inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
52 if (method == SDKPass_Pointer)
54 type = PassType_Basic;
55 if (vtype == Valve_POD
56 || vtype == Valve_Float
57 || vtype == Valve_Bool)
59 flags = PASSFLAG_ASPOINTER;
61 flags = PASSFLAG_BYVAL;
63 } else if (method == SDKPass_Plain) {
64 type = PassType_Basic;
65 flags = PASSFLAG_BYVAL;
66 } else if (method == SDKPass_ByValue) {
67 if (vtype == Valve_Vector
68 || vtype == Valve_QAngle)
70 type = PassType_Object;
72 type = PassType_Basic;
74 flags = PASSFLAG_BYVAL;
75 } else if (method == SDKPass_ByRef) {
76 if (vtype == Valve_Vector
77 || vtype == Valve_QAngle)
79 type = PassType_Object;
81 type = PassType_Basic;
83 flags = PASSFLAG_BYREF;
87 static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
93 s_vcalltype = (ValveCallType)params[1];
98 static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
100 s_vtbl_index = params[1];
105 static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
107 void *addrInBase = NULL;
108 if (params[1] == SDKLibrary_Server)
110 addrInBase = (void *)g_SMAPI->serverFactory(false);
111 } else if (params[1] == SDKLibrary_Engine) {
112 addrInBase = (void *)g_SMAPI->engineFactory(false);
114 if (addrInBase == NULL)
120 pContext->LocalToString(params[2], &sig);
122 #if defined PLATFORM_LINUX
126 if (dladdr(addrInBase, &info) == 0)
130 void *handle = dlopen(info.dli_fname, RTLD_NOW);
135 s_call_addr = dlsym(handle, &sig[1]);
138 return (s_call_addr != NULL) ? 1 : 0;
142 s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
144 return (s_call_addr != NULL) ? 1 : 0;
147 static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
151 if (params[1] == BAD_HANDLE)
156 if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
158 return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
163 pContext->LocalToString(params[3], &key);
167 return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
168 } else if (params[2] == 1) {
169 bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
170 return (result && s_call_addr != NULL) ? 1 : 0;
176 static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
179 s_return.vtype = (ValveType)params[1];
180 DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
181 s_return.decflags = params[3];
182 s_return.encflags = params[4];
187 static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
189 if (s_numparams >= SP_MAX_EXEC_PARAMS)
191 return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
194 ValvePassInfo *info = &s_params[s_numparams++];
195 info->vtype = (ValveType)params[1];
196 DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
197 info->decflags = params[3] | VDECODE_FLAG_BYREF;
198 info->encflags = params[4];
203 static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
205 ValveCall *vc = NULL;
206 if (s_vtbl_index > -1)
208 vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
209 } else if (s_call_addr) {
210 vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
220 vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
223 Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
232 static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
236 HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
238 if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
240 return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
243 unsigned char *ptr = vc->stk_get();
245 unsigned int numparams = (unsigned)params[0];
246 unsigned int startparam = 2;
247 /* Do we need to write a thispointer? */
253 case ValveCall_Entity:
254 case ValveCall_Player:
256 if (startparam > numparams)
259 return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
262 if (DecodeValveParam(pContext,
274 case ValveCall_GameRules:
276 if (g_pGameRules == NULL)
279 return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
282 void *gamerules = *g_pGameRules;
284 if (gamerules == NULL)
287 return pContext->ThrowNativeError("GameRules not available before map is loaded");
289 *(void **)ptr = gamerules;
295 /* See if we need to skip any more parameters */
296 unsigned int retparam = startparam;
299 if (vc->retinfo->vtype == Valve_String)
302 } else if (vc->retinfo->vtype == Valve_Vector
303 || vc->retinfo->vtype == Valve_QAngle)
309 unsigned int callparams = vc->call->GetParamCount();
310 bool will_copyback = false;
311 for (unsigned int i=0; i<callparams; i++)
313 unsigned int p = startparam + i;
317 return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
319 if (DecodeValveParam(pContext,
328 if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
330 will_copyback = true;
334 /* Make the actual call */
335 vc->call->Execute(ptr, vc->retbuf);
337 /* Do we need to copy anything back? */
340 for (unsigned int i=0; i<callparams; i++)
342 if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
344 if (EncodeValveParam(pContext,
345 params[startparam + i],
357 /* Save stack once and for all */
360 /* Figure out how to decode the return information */
363 if (vc->retinfo->vtype == Valve_String)
367 return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
371 pContext->LocalToPhysAddr(params[retparam+1], &addr);
372 pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
373 return (cell_t)written;
374 } else if (vc->retinfo->vtype == Valve_Vector
375 || vc->retinfo->vtype == Valve_QAngle)
379 return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
381 if (EncodeValveParam(pContext, params[retparam], vc, vc->retinfo, vc->retbuf)
386 } else if (vc->retinfo->vtype == Valve_CBaseEntity
387 || vc->retinfo->vtype == Valve_CBasePlayer)
389 CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
394 edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
395 if (!pEdict || pEdict->IsFree())
399 return engine->IndexOfEdict(pEdict);
400 } else if (vc->retinfo->vtype == Valve_Edict) {
401 edict_t *pEdict = *(edict_t **)(vc->retbuf);
402 if (!pEdict || pEdict->IsFree())
406 return engine->IndexOfEdict(pEdict);
407 } else if (vc->retinfo->vtype == Valve_Bool) {
408 bool *addr = (bool *)vc->retbuf;
409 if (vc->retinfo->flags & PASSFLAG_BYREF)
411 addr = *(bool **)addr;
413 return *addr ? 1 : 0;
415 cell_t *addr = (cell_t *)vc->retbuf;
416 if (vc->retinfo->flags & PASSFLAG_BYREF)
418 addr = *(cell_t **)addr;
427 sp_nativeinfo_t g_CallNatives[] =
429 {"StartPrepSDKCall", StartPrepSDKCall},
430 {"PrepSDKCall_SetVirtual", PrepSDKCall_SetVirtual},
431 {"PrepSDKCall_SetSignature", PrepSDKCall_SetSignature},
432 {"PrepSDKCall_SetFromConf", PrepSDKCall_SetFromConf},
433 {"PrepSDKCall_SetReturnInfo", PrepSDKCall_SetReturnInfo},
434 {"PrepSDKCall_AddParameter", PrepSDKCall_AddParameter},
435 {"EndPrepSDKCall", EndPrepSDKCall},
436 {"SDKCall", SDKCall},