extensions/sdktools/vcaller.cpp
author Scott Ehlert <ds@alliedmods.net>
Tue Jul 17 13:57:03 2007 +0000 (2007-07-17)
changeset 1130 ff50b695a042
parent 1065 11756fc9fb63
child 1135 1a04a48f0788
permissions -rw-r--r--
Fixed VENCODE_FLAG_COPYBACK flag (SDKTools)
faluco@1031
     1
/**
faluco@1031
     2
 * vim: set ts=4 :
faluco@1031
     3
 * ===============================================================
faluco@1031
     4
 * SourceMod SDK Tools Extension
faluco@1031
     5
 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
faluco@1031
     6
 * ===============================================================
faluco@1031
     7
 *
faluco@1031
     8
 * This program is free software; you can redistribute it and/or
faluco@1031
     9
 * modify it under the terms of the GNU General Public License
faluco@1031
    10
 * as published by the Free Software Foundation; either version 2
faluco@1031
    11
 * of the License, or (at your option) any later version.
faluco@1031
    12
 * This program is distributed in the hope that it will be useful,
faluco@1031
    13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
faluco@1031
    14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
faluco@1031
    15
 * GNU General Public License for more details.
faluco@1031
    16
 * 
faluco@1031
    17
 * You should have received a copy of the GNU General Public License
faluco@1031
    18
 * along with this program; if not, write to the Free Software
faluco@1031
    19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
faluco@1031
    20
 *
faluco@1031
    21
 * Version: $Id$
faluco@1031
    22
 */
faluco@1031
    23
dvander@950
    24
#include "extension.h"
dvander@950
    25
#include "vcallbuilder.h"
ds@1065
    26
#include "gamerules.h"
dvander@950
    27
dvander@950
    28
enum SDKLibrary
dvander@950
    29
{
dvander@950
    30
	SDKLibrary_Server,	/**< server.dll/server_i486.so */
dvander@950
    31
	SDKLibrary_Engine,	/**< engine.dll/engine_*.so */
dvander@950
    32
};
dvander@950
    33
dvander@950
    34
enum SDKPassMethod
dvander@950
    35
{
dvander@950
    36
	SDKPass_Pointer,		/**< Pass as a pointer */
dvander@950
    37
	SDKPass_Plain,			/**< Pass as plain data */
dvander@950
    38
	SDKPass_ByValue,		/**< Pass an object by value */
dvander@950
    39
	SDKPass_ByRef,			/**< Pass an object by reference */
dvander@950
    40
};
dvander@950
    41
ds@1042
    42
int s_vtbl_index = -1;
dvander@950
    43
void *s_call_addr = NULL;
dvander@950
    44
ValveCallType s_vcalltype = ValveCall_Static;
dvander@950
    45
bool s_has_return = false;
dvander@950
    46
ValvePassInfo s_return;
dvander@950
    47
unsigned int s_numparams = 0;
dvander@950
    48
ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
dvander@950
    49
dvander@950
    50
inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
dvander@950
    51
{
dvander@950
    52
	if (method == SDKPass_Pointer)
dvander@950
    53
	{
dvander@950
    54
		type = PassType_Basic;
dvander@950
    55
		if (vtype == Valve_POD
dvander@954
    56
			|| vtype == Valve_Float
dvander@954
    57
			|| vtype == Valve_Bool)
dvander@950
    58
		{
dvander@954
    59
			flags = PASSFLAG_ASPOINTER;
dvander@950
    60
		} else {
dvander@950
    61
			flags = PASSFLAG_BYVAL;
dvander@950
    62
		}
dvander@950
    63
	} else if (method == SDKPass_Plain) {
dvander@950
    64
		type = PassType_Basic;
dvander@950
    65
		flags = PASSFLAG_BYVAL;
dvander@950
    66
	} else if (method == SDKPass_ByValue) {
dvander@950
    67
		if (vtype == Valve_Vector
dvander@950
    68
			|| vtype == Valve_QAngle)
dvander@950
    69
		{
dvander@950
    70
			type = PassType_Object;
dvander@950
    71
		} else {
dvander@950
    72
			type = PassType_Basic;
dvander@950
    73
		}
dvander@950
    74
		flags = PASSFLAG_BYVAL;
dvander@950
    75
	} else if (method == SDKPass_ByRef) {
dvander@950
    76
		if (vtype == Valve_Vector
dvander@950
    77
			|| vtype == Valve_QAngle)
dvander@950
    78
		{
dvander@950
    79
			type = PassType_Object;
dvander@950
    80
		} else {
dvander@950
    81
			type = PassType_Basic;
dvander@950
    82
		}
dvander@950
    83
		flags = PASSFLAG_BYREF;
dvander@950
    84
	}
dvander@950
    85
}
dvander@950
    86
dvander@950
    87
static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
    88
{
dvander@950
    89
	s_numparams = 0;
ds@1042
    90
	s_vtbl_index = -1;
dvander@950
    91
	s_call_addr = NULL;
dvander@950
    92
	s_has_return = false;
dvander@950
    93
	s_vcalltype = (ValveCallType)params[1];
dvander@950
    94
dvander@950
    95
	return 1;
dvander@950
    96
}
dvander@950
    97
dvander@950
    98
static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
dvander@950
    99
{
dvander@950
   100
	s_vtbl_index = params[1];
dvander@950
   101
dvander@950
   102
	return 1;
dvander@950
   103
}
dvander@950
   104
dvander@950
   105
static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
dvander@950
   106
{
dvander@950
   107
	void *addrInBase = NULL;
dvander@950
   108
	if (params[1] == SDKLibrary_Server)
dvander@950
   109
	{
dvander@956
   110
		addrInBase = (void *)g_SMAPI->serverFactory(false);
dvander@950
   111
	} else if (params[1] == SDKLibrary_Engine) {
dvander@956
   112
		addrInBase = (void *)g_SMAPI->engineFactory(false);
dvander@950
   113
	}
dvander@950
   114
	if (addrInBase == NULL)
dvander@950
   115
	{
dvander@950
   116
		return 0;
dvander@950
   117
	}
dvander@950
   118
dvander@950
   119
	char *sig;
dvander@950
   120
	pContext->LocalToString(params[2], &sig);
dvander@950
   121
dvander@950
   122
#if defined PLATFORM_LINUX
dvander@950
   123
	if (sig[0] == '@')
dvander@950
   124
	{
dvander@950
   125
		Dl_info info;
dvander@950
   126
		if (dladdr(addrInBase, &info) == 0)
dvander@950
   127
		{
dvander@950
   128
			return 0;
dvander@950
   129
		}
dvander@950
   130
		void *handle = dlopen(info.dli_fname, RTLD_NOW);
dvander@950
   131
		if (!handle)
dvander@950
   132
		{
dvander@950
   133
			return 0;
dvander@950
   134
		}
dvander@950
   135
		s_call_addr = dlsym(handle, &sig[1]);
dvander@950
   136
		dlclose(handle);
dvander@950
   137
dvander@950
   138
		return (s_call_addr != NULL) ? 1 : 0;
dvander@950
   139
	}
dvander@950
   140
#endif
dvander@950
   141
dvander@950
   142
	s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
dvander@950
   143
dvander@950
   144
	return (s_call_addr != NULL) ? 1 : 0;
dvander@950
   145
}
dvander@950
   146
dvander@950
   147
static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
dvander@950
   148
{
dvander@950
   149
	IGameConfig *conf;
dvander@950
   150
dvander@950
   151
	if (params[1] == BAD_HANDLE)
dvander@950
   152
	{
dvander@950
   153
		conf = g_pGameConf;
dvander@950
   154
	} else {
dvander@950
   155
		HandleError err;
dvander@950
   156
		if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
dvander@950
   157
		{
dvander@950
   158
			return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950
   159
		}
dvander@950
   160
	}
dvander@950
   161
	
dvander@950
   162
	char *key;
dvander@950
   163
	pContext->LocalToString(params[3], &key);
dvander@950
   164
dvander@950
   165
	if (params[2] == 0)
dvander@950
   166
	{
dvander@950
   167
		return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
dvander@950
   168
	} else if (params[2] == 1) {
dvander@950
   169
		bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
dvander@950
   170
		return (result && s_call_addr != NULL) ? 1 : 0;
dvander@950
   171
	}
dvander@950
   172
dvander@950
   173
	return 0;
dvander@950
   174
}
dvander@950
   175
dvander@950
   176
static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
dvander@950
   177
{
dvander@950
   178
	s_has_return = true;
dvander@950
   179
	s_return.vtype = (ValveType)params[1];
dvander@950
   180
	DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
dvander@950
   181
	s_return.decflags = params[3];
dvander@950
   182
	s_return.encflags = params[4];
dvander@950
   183
dvander@950
   184
	return 1;
dvander@950
   185
}
dvander@950
   186
dvander@950
   187
static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
dvander@950
   188
{
dvander@950
   189
	if (s_numparams >= SP_MAX_EXEC_PARAMS)
dvander@950
   190
	{
dvander@950
   191
		return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
dvander@950
   192
	}
dvander@950
   193
dvander@950
   194
	ValvePassInfo *info = &s_params[s_numparams++];
dvander@950
   195
	info->vtype = (ValveType)params[1];
dvander@950
   196
	DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
dvander@954
   197
	info->decflags = params[3] | VDECODE_FLAG_BYREF;
dvander@950
   198
	info->encflags = params[4];
dvander@950
   199
dvander@950
   200
	return 1;
dvander@950
   201
}
dvander@950
   202
dvander@950
   203
static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
   204
{
dvander@950
   205
	ValveCall *vc = NULL;
ds@1042
   206
	if (s_vtbl_index > -1)
dvander@950
   207
	{
dvander@950
   208
		vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@953
   209
	} else if (s_call_addr) {
dvander@953
   210
		vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@950
   211
	}
dvander@950
   212
dvander@950
   213
	if (!vc)
dvander@950
   214
	{
dvander@950
   215
		return BAD_HANDLE;
dvander@950
   216
	}
dvander@950
   217
dvander@968
   218
	if (vc->thisinfo)
dvander@968
   219
	{
dvander@968
   220
		vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
dvander@968
   221
	}
dvander@968
   222
dvander@950
   223
	Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
dvander@950
   224
	if (!hndl)
dvander@950
   225
	{
dvander@950
   226
		delete vc;
dvander@950
   227
	}
dvander@950
   228
dvander@950
   229
	return hndl;
dvander@950
   230
}
dvander@950
   231
dvander@950
   232
static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
   233
{
dvander@950
   234
	ValveCall *vc;
dvander@950
   235
	HandleError err;
dvander@950
   236
	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
dvander@950
   237
dvander@950
   238
	if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
dvander@950
   239
	{
dvander@950
   240
		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950
   241
	}
dvander@950
   242
dvander@950
   243
	unsigned char *ptr = vc->stk_get();
dvander@950
   244
dvander@950
   245
	unsigned int numparams = (unsigned)params[0];
dvander@950
   246
	unsigned int startparam = 2;
dvander@950
   247
	/* Do we need to write a thispointer?  */
dvander@950
   248
ds@1065
   249
	if (vc->thisinfo)
dvander@950
   250
	{
ds@1065
   251
		switch (vc->type)
dvander@950
   252
		{
ds@1065
   253
		case ValveCall_Entity:
ds@1065
   254
		case ValveCall_Player:
ds@1065
   255
			{
ds@1065
   256
				if (startparam > numparams)
ds@1065
   257
				{
ds@1065
   258
					vc->stk_put(ptr);
ds@1065
   259
					return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
ds@1065
   260
				}
ds@1065
   261
ds@1065
   262
				if (DecodeValveParam(pContext, 
ds@1065
   263
					params[startparam],
ds@1065
   264
					vc,
ds@1065
   265
					vc->thisinfo,
ds@1065
   266
					ptr) == Data_Fail)
ds@1065
   267
				{
ds@1065
   268
					vc->stk_put(ptr);
ds@1065
   269
					return 0;
ds@1065
   270
				}
ds@1065
   271
				startparam++;
ds@1065
   272
			}
ds@1065
   273
			break;
ds@1065
   274
		case ValveCall_GameRules:
ds@1065
   275
			{
ds@1065
   276
				if (g_pGameRules == NULL)
ds@1065
   277
				{
ds@1065
   278
					vc->stk_put(ptr);
ds@1065
   279
					return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
ds@1065
   280
				}
ds@1065
   281
ds@1065
   282
				void *gamerules = *g_pGameRules;
ds@1065
   283
ds@1065
   284
				if (gamerules == NULL)
ds@1065
   285
				{
ds@1065
   286
					vc->stk_put(ptr);
ds@1065
   287
					return pContext->ThrowNativeError("GameRules not available before map is loaded");
ds@1065
   288
				}
ds@1065
   289
				*(void **)ptr = gamerules;
ds@1065
   290
			}
ds@1065
   291
			break;
dvander@950
   292
		}
dvander@950
   293
	}
dvander@950
   294
dvander@950
   295
	/* See if we need to skip any more parameters */
dvander@950
   296
	unsigned int retparam = startparam;
dvander@950
   297
	if (vc->retinfo)
dvander@950
   298
	{
dvander@950
   299
		if (vc->retinfo->vtype == Valve_String)
dvander@950
   300
		{
dvander@950
   301
			startparam += 2;
dvander@950
   302
		} else if (vc->retinfo->vtype == Valve_Vector
dvander@950
   303
					|| vc->retinfo->vtype == Valve_QAngle)
dvander@950
   304
		{
dvander@950
   305
			startparam += 1;
dvander@950
   306
		}
dvander@950
   307
	}
dvander@950
   308
dvander@950
   309
	unsigned int callparams = vc->call->GetParamCount();
dvander@950
   310
	bool will_copyback = false;
dvander@950
   311
	for (unsigned int i=0; i<callparams; i++)
dvander@950
   312
	{
dvander@950
   313
		unsigned int p = startparam + i;
dvander@950
   314
		if (p > numparams)
dvander@950
   315
		{
dvander@950
   316
			vc->stk_put(ptr);
dvander@950
   317
			return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
dvander@950
   318
		}
dvander@950
   319
		if (DecodeValveParam(pContext,
dvander@950
   320
			params[p],
dvander@987
   321
			vc,
dvander@954
   322
			&(vc->vparams[i]),
dvander@987
   323
			ptr) == Data_Fail)
dvander@950
   324
		{
dvander@950
   325
			vc->stk_put(ptr);
dvander@950
   326
			return 0;
dvander@950
   327
		}
dvander@950
   328
		if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950
   329
		{
dvander@950
   330
			will_copyback = true;
dvander@950
   331
		}
dvander@950
   332
	}
dvander@950
   333
dvander@950
   334
	/* Make the actual call */
dvander@950
   335
	vc->call->Execute(ptr, vc->retbuf);
dvander@950
   336
dvander@950
   337
	/* Do we need to copy anything back? */
dvander@950
   338
	if (will_copyback)
dvander@950
   339
	{
dvander@950
   340
		for (unsigned int i=0; i<callparams; i++)
dvander@950
   341
		{
dvander@950
   342
			if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950
   343
			{
dvander@950
   344
				if (EncodeValveParam(pContext, 
ds@1130
   345
					params[startparam + i], 
dvander@987
   346
					vc,
dvander@954
   347
					&vc->vparams[i],
dvander@987
   348
					ptr) == Data_Fail)
dvander@950
   349
				{
dvander@950
   350
					vc->stk_put(ptr);
dvander@950
   351
					return 0;
dvander@950
   352
				}
dvander@950
   353
			}
dvander@950
   354
		}
dvander@950
   355
	}
dvander@950
   356
dvander@950
   357
	/* Save stack once and for all */
dvander@950
   358
	vc->stk_put(ptr);
dvander@950
   359
dvander@950
   360
	/* Figure out how to decode the return information */
dvander@950
   361
	if (vc->retinfo)
dvander@950
   362
	{
dvander@950
   363
		if (vc->retinfo->vtype == Valve_String)
dvander@950
   364
		{
dvander@950
   365
			if (numparams < 3)
dvander@950
   366
			{
dvander@950
   367
				return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
dvander@950
   368
			}
dvander@950
   369
			cell_t *addr;
dvander@950
   370
			size_t written;
dvander@950
   371
			pContext->LocalToPhysAddr(params[retparam+1], &addr);
dvander@950
   372
			pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
dvander@950
   373
			return (cell_t)written;
dvander@950
   374
		} else if (vc->retinfo->vtype == Valve_Vector
dvander@950
   375
					|| vc->retinfo->vtype == Valve_QAngle)
dvander@950
   376
		{
dvander@950
   377
			if (numparams < 2)
dvander@950
   378
			{
dvander@950
   379
				return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
dvander@950
   380
			}
dvander@987
   381
			if (EncodeValveParam(pContext, params[retparam], vc, vc->retinfo, vc->retbuf)
dvander@950
   382
				== Data_Fail)
dvander@950
   383
			{
dvander@950
   384
				return 0;
dvander@950
   385
			}
dvander@950
   386
		} else if (vc->retinfo->vtype == Valve_CBaseEntity
dvander@950
   387
					|| vc->retinfo->vtype == Valve_CBasePlayer)
dvander@950
   388
		{
dvander@950
   389
			CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
dvander@950
   390
			if (!pEntity)
dvander@950
   391
			{
dvander@950
   392
				return -1;
dvander@950
   393
			}
dvander@950
   394
			edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
dvander@950
   395
			if (!pEdict || pEdict->IsFree())
dvander@950
   396
			{
dvander@950
   397
				return -1;
dvander@950
   398
			}
dvander@950
   399
			return engine->IndexOfEdict(pEdict);
dvander@950
   400
		} else if (vc->retinfo->vtype == Valve_Edict) {
dvander@950
   401
			edict_t *pEdict = *(edict_t **)(vc->retbuf);
dvander@950
   402
			if (!pEdict || pEdict->IsFree())
dvander@950
   403
			{
dvander@950
   404
				return -1;
dvander@950
   405
			}
dvander@950
   406
			return engine->IndexOfEdict(pEdict);
dvander@954
   407
		} else if (vc->retinfo->vtype == Valve_Bool) {
dvander@954
   408
			bool *addr = (bool  *)vc->retbuf;
dvander@954
   409
			if (vc->retinfo->flags & PASSFLAG_BYREF)
dvander@954
   410
			{
dvander@954
   411
				addr = *(bool **)addr;
dvander@954
   412
			}
dvander@954
   413
			return *addr ? 1 : 0;
dvander@950
   414
		} else {
dvander@950
   415
			cell_t *addr = (cell_t *)vc->retbuf;
dvander@950
   416
			if (vc->retinfo->flags & PASSFLAG_BYREF)
dvander@950
   417
			{
dvander@950
   418
				addr = *(cell_t **)addr;
dvander@950
   419
			}
dvander@950
   420
			return *addr;
dvander@950
   421
		}
dvander@950
   422
	}
dvander@950
   423
dvander@950
   424
	return 0;
dvander@950
   425
}
dvander@950
   426
dvander@950
   427
sp_nativeinfo_t g_CallNatives[] = 
dvander@950
   428
{
dvander@950
   429
	{"StartPrepSDKCall",			StartPrepSDKCall},
dvander@950
   430
	{"PrepSDKCall_SetVirtual",		PrepSDKCall_SetVirtual},
dvander@950
   431
	{"PrepSDKCall_SetSignature",	PrepSDKCall_SetSignature},
dvander@950
   432
	{"PrepSDKCall_SetFromConf",		PrepSDKCall_SetFromConf},
dvander@950
   433
	{"PrepSDKCall_SetReturnInfo",	PrepSDKCall_SetReturnInfo},
dvander@950
   434
	{"PrepSDKCall_AddParameter",	PrepSDKCall_AddParameter},
dvander@950
   435
	{"EndPrepSDKCall",				EndPrepSDKCall},
dvander@950
   436
	{"SDKCall",						SDKCall},
dvander@950
   437
	{NULL,							NULL},
dvander@950
   438
};