extensions/sdktools/vcaller.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jun 17 05:19:55 2007 +0000 (2007-06-17)
changeset 967 1e48c64a4e9d
parent 956 8df901c7c51d
child 968 6c7ff786a1de
permissions -rw-r--r--
fixed a bug where this parameter was not marked VDECODE_FLAG_BYREF for SDKCall()
dvander@950
     1
#include "extension.h"
dvander@950
     2
#include "vcallbuilder.h"
dvander@950
     3
dvander@950
     4
enum SDKLibrary
dvander@950
     5
{
dvander@950
     6
	SDKLibrary_Server,	/**< server.dll/server_i486.so */
dvander@950
     7
	SDKLibrary_Engine,	/**< engine.dll/engine_*.so */
dvander@950
     8
};
dvander@950
     9
dvander@950
    10
enum SDKPassMethod
dvander@950
    11
{
dvander@950
    12
	SDKPass_Pointer,		/**< Pass as a pointer */
dvander@950
    13
	SDKPass_Plain,			/**< Pass as plain data */
dvander@950
    14
	SDKPass_ByValue,		/**< Pass an object by value */
dvander@950
    15
	SDKPass_ByRef,			/**< Pass an object by reference */
dvander@950
    16
};
dvander@950
    17
dvander@950
    18
int s_vtbl_index = 0;
dvander@950
    19
void *s_call_addr = NULL;
dvander@950
    20
ValveCallType s_vcalltype = ValveCall_Static;
dvander@950
    21
bool s_has_return = false;
dvander@950
    22
ValvePassInfo s_return;
dvander@950
    23
unsigned int s_numparams = 0;
dvander@950
    24
ValvePassInfo s_params[SP_MAX_EXEC_PARAMS];
dvander@950
    25
dvander@950
    26
inline void DecodePassMethod(ValveType vtype, SDKPassMethod method, PassType &type, unsigned int &flags)
dvander@950
    27
{
dvander@950
    28
	if (method == SDKPass_Pointer)
dvander@950
    29
	{
dvander@950
    30
		type = PassType_Basic;
dvander@950
    31
		if (vtype == Valve_POD
dvander@954
    32
			|| vtype == Valve_Float
dvander@954
    33
			|| vtype == Valve_Bool)
dvander@950
    34
		{
dvander@954
    35
			flags = PASSFLAG_ASPOINTER;
dvander@950
    36
		} else {
dvander@950
    37
			flags = PASSFLAG_BYVAL;
dvander@950
    38
		}
dvander@950
    39
	} else if (method == SDKPass_Plain) {
dvander@950
    40
		type = PassType_Basic;
dvander@950
    41
		flags = PASSFLAG_BYVAL;
dvander@950
    42
	} else if (method == SDKPass_ByValue) {
dvander@950
    43
		if (vtype == Valve_Vector
dvander@950
    44
			|| vtype == Valve_QAngle)
dvander@950
    45
		{
dvander@950
    46
			type = PassType_Object;
dvander@950
    47
		} else {
dvander@950
    48
			type = PassType_Basic;
dvander@950
    49
		}
dvander@950
    50
		flags = PASSFLAG_BYVAL;
dvander@950
    51
	} else if (method == SDKPass_ByRef) {
dvander@950
    52
		if (vtype == Valve_Vector
dvander@950
    53
			|| vtype == Valve_QAngle)
dvander@950
    54
		{
dvander@950
    55
			type = PassType_Object;
dvander@950
    56
		} else {
dvander@950
    57
			type = PassType_Basic;
dvander@950
    58
		}
dvander@950
    59
		flags = PASSFLAG_BYREF;
dvander@950
    60
	}
dvander@950
    61
}
dvander@950
    62
dvander@950
    63
static cell_t StartPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
    64
{
dvander@950
    65
	s_numparams = 0;
dvander@950
    66
	s_vtbl_index = 0;
dvander@950
    67
	s_call_addr = NULL;
dvander@950
    68
	s_has_return = false;
dvander@950
    69
	s_vcalltype = (ValveCallType)params[1];
dvander@950
    70
dvander@950
    71
	return 1;
dvander@950
    72
}
dvander@950
    73
dvander@950
    74
static cell_t PrepSDKCall_SetVirtual(IPluginContext *pContext, const cell_t *params)
dvander@950
    75
{
dvander@950
    76
	s_vtbl_index = params[1];
dvander@950
    77
dvander@950
    78
	return 1;
dvander@950
    79
}
dvander@950
    80
dvander@950
    81
static cell_t PrepSDKCall_SetSignature(IPluginContext *pContext, const cell_t *params)
dvander@950
    82
{
dvander@950
    83
	void *addrInBase = NULL;
dvander@950
    84
	if (params[1] == SDKLibrary_Server)
dvander@950
    85
	{
dvander@956
    86
		addrInBase = (void *)g_SMAPI->serverFactory(false);
dvander@950
    87
	} else if (params[1] == SDKLibrary_Engine) {
dvander@956
    88
		addrInBase = (void *)g_SMAPI->engineFactory(false);
dvander@950
    89
	}
dvander@950
    90
	if (addrInBase == NULL)
dvander@950
    91
	{
dvander@950
    92
		return 0;
dvander@950
    93
	}
dvander@950
    94
dvander@950
    95
	char *sig;
dvander@950
    96
	pContext->LocalToString(params[2], &sig);
dvander@950
    97
dvander@950
    98
#if defined PLATFORM_LINUX
dvander@950
    99
	if (sig[0] == '@')
dvander@950
   100
	{
dvander@950
   101
		Dl_info info;
dvander@950
   102
		if (dladdr(addrInBase, &info) == 0)
dvander@950
   103
		{
dvander@950
   104
			return 0;
dvander@950
   105
		}
dvander@950
   106
		void *handle = dlopen(info.dli_fname, RTLD_NOW);
dvander@950
   107
		if (!handle)
dvander@950
   108
		{
dvander@950
   109
			return 0;
dvander@950
   110
		}
dvander@950
   111
		s_call_addr = dlsym(handle, &sig[1]);
dvander@950
   112
		dlclose(handle);
dvander@950
   113
dvander@950
   114
		return (s_call_addr != NULL) ? 1 : 0;
dvander@950
   115
	}
dvander@950
   116
#endif
dvander@950
   117
dvander@950
   118
	s_call_addr = memutils->FindPattern(addrInBase, sig, params[3]);
dvander@950
   119
dvander@950
   120
	return (s_call_addr != NULL) ? 1 : 0;
dvander@950
   121
}
dvander@950
   122
dvander@950
   123
static cell_t PrepSDKCall_SetFromConf(IPluginContext *pContext, const cell_t *params)
dvander@950
   124
{
dvander@950
   125
	IGameConfig *conf;
dvander@950
   126
dvander@950
   127
	if (params[1] == BAD_HANDLE)
dvander@950
   128
	{
dvander@950
   129
		conf = g_pGameConf;
dvander@950
   130
	} else {
dvander@950
   131
		HandleError err;
dvander@950
   132
		if ((conf = gameconfs->ReadHandle(params[1], pContext->GetIdentity(), &err)) == NULL)
dvander@950
   133
		{
dvander@950
   134
			return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950
   135
		}
dvander@950
   136
	}
dvander@950
   137
	
dvander@950
   138
	char *key;
dvander@950
   139
	pContext->LocalToString(params[3], &key);
dvander@950
   140
dvander@950
   141
	if (params[2] == 0)
dvander@950
   142
	{
dvander@950
   143
		return conf->GetOffset(key, &s_vtbl_index) ? 1 : 0;
dvander@950
   144
	} else if (params[2] == 1) {
dvander@950
   145
		bool result = conf->GetMemSig(key, &s_call_addr) ? 1 : 0;
dvander@950
   146
		return (result && s_call_addr != NULL) ? 1 : 0;
dvander@950
   147
	}
dvander@950
   148
dvander@950
   149
	return 0;
dvander@950
   150
}
dvander@950
   151
dvander@950
   152
static cell_t PrepSDKCall_SetReturnInfo(IPluginContext *pContext, const cell_t *params)
dvander@950
   153
{
dvander@950
   154
	s_has_return = true;
dvander@950
   155
	s_return.vtype = (ValveType)params[1];
dvander@950
   156
	DecodePassMethod(s_return.vtype, (SDKPassMethod)params[2], s_return.type, s_return.flags);
dvander@950
   157
	s_return.decflags = params[3];
dvander@950
   158
	s_return.encflags = params[4];
dvander@950
   159
dvander@950
   160
	return 1;
dvander@950
   161
}
dvander@950
   162
dvander@950
   163
static cell_t PrepSDKCall_AddParameter(IPluginContext *pContext, const cell_t *params)
dvander@950
   164
{
dvander@950
   165
	if (s_numparams >= SP_MAX_EXEC_PARAMS)
dvander@950
   166
	{
dvander@950
   167
		return pContext->ThrowNativeError("Parameter limit for SDK calls reached");
dvander@950
   168
	}
dvander@950
   169
dvander@950
   170
	ValvePassInfo *info = &s_params[s_numparams++];
dvander@950
   171
	info->vtype = (ValveType)params[1];
dvander@950
   172
	DecodePassMethod(info->vtype, (SDKPassMethod)params[2], info->type, info->flags);
dvander@954
   173
	info->decflags = params[3] | VDECODE_FLAG_BYREF;
dvander@950
   174
	info->encflags = params[4];
dvander@950
   175
dvander@950
   176
	return 1;
dvander@950
   177
}
dvander@950
   178
dvander@950
   179
static cell_t EndPrepSDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
   180
{
dvander@950
   181
	ValveCall *vc = NULL;
dvander@950
   182
	if (s_vtbl_index)
dvander@950
   183
	{
dvander@950
   184
		vc = CreateValveVCall(s_vtbl_index, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@953
   185
	} else if (s_call_addr) {
dvander@953
   186
		vc = CreateValveCall(s_call_addr, s_vcalltype, s_has_return ? &s_return : NULL, s_params, s_numparams);
dvander@950
   187
	}
dvander@950
   188
dvander@967
   189
	vc->thisinfo->decflags |= VDECODE_FLAG_BYREF;
dvander@967
   190
dvander@950
   191
	if (!vc)
dvander@950
   192
	{
dvander@950
   193
		return BAD_HANDLE;
dvander@950
   194
	}
dvander@950
   195
dvander@950
   196
	Handle_t hndl = handlesys->CreateHandle(g_CallHandle, vc, pContext->GetIdentity(), myself->GetIdentity(), NULL);
dvander@950
   197
	if (!hndl)
dvander@950
   198
	{
dvander@950
   199
		delete vc;
dvander@950
   200
	}
dvander@950
   201
dvander@950
   202
	return hndl;
dvander@950
   203
}
dvander@950
   204
dvander@950
   205
static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
dvander@950
   206
{
dvander@950
   207
	ValveCall *vc;
dvander@950
   208
	HandleError err;
dvander@950
   209
	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
dvander@950
   210
dvander@950
   211
	if ((err = handlesys->ReadHandle(params[1], g_CallHandle, &sec, (void **)&vc)) != HandleError_None)
dvander@950
   212
	{
dvander@950
   213
		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
dvander@950
   214
	}
dvander@950
   215
dvander@950
   216
	unsigned char *ptr = vc->stk_get();
dvander@950
   217
dvander@950
   218
	unsigned int numparams = (unsigned)params[0];
dvander@950
   219
	unsigned int startparam = 2;
dvander@950
   220
	/* Do we need to write a thispointer?  */
dvander@950
   221
dvander@950
   222
	if (vc->thisinfo && 
dvander@950
   223
		(vc->thisinfo->vtype == Valve_CBaseEntity
dvander@950
   224
		 || vc->thisinfo->vtype == Valve_CBasePlayer))
dvander@950
   225
	{
dvander@950
   226
		if (startparam > numparams)
dvander@950
   227
		{
dvander@950
   228
			vc->stk_put(ptr);
dvander@950
   229
			return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
dvander@950
   230
		}
dvander@950
   231
		if (DecodeValveParam(pContext, 
dvander@950
   232
			params[startparam], 
dvander@954
   233
			vc->thisinfo,
dvander@950
   234
			ptr) == Data_Fail)
dvander@950
   235
		{
dvander@950
   236
			vc->stk_put(ptr);
dvander@950
   237
			return 0;
dvander@950
   238
		}
dvander@950
   239
		startparam++;
dvander@950
   240
	}
dvander@950
   241
dvander@950
   242
	/* See if we need to skip any more parameters */
dvander@950
   243
	unsigned int retparam = startparam;
dvander@950
   244
	if (vc->retinfo)
dvander@950
   245
	{
dvander@950
   246
		if (vc->retinfo->vtype == Valve_String)
dvander@950
   247
		{
dvander@950
   248
			startparam += 2;
dvander@950
   249
		} else if (vc->retinfo->vtype == Valve_Vector
dvander@950
   250
					|| vc->retinfo->vtype == Valve_QAngle)
dvander@950
   251
		{
dvander@950
   252
			startparam += 1;
dvander@950
   253
		}
dvander@950
   254
	}
dvander@950
   255
dvander@950
   256
	unsigned int callparams = vc->call->GetParamCount();
dvander@950
   257
	bool will_copyback = false;
dvander@950
   258
	for (unsigned int i=0; i<callparams; i++)
dvander@950
   259
	{
dvander@950
   260
		unsigned int p = startparam + i;
dvander@950
   261
		if (p > numparams)
dvander@950
   262
		{
dvander@950
   263
			vc->stk_put(ptr);
dvander@950
   264
			return pContext->ThrowNativeError("Expected %dth parameter, found none", p);
dvander@950
   265
		}
dvander@950
   266
		if (DecodeValveParam(pContext,
dvander@950
   267
			params[p],
dvander@954
   268
			&(vc->vparams[i]),
dvander@950
   269
			ptr + vc->vparams[i].offset) == Data_Fail)
dvander@950
   270
		{
dvander@950
   271
			vc->stk_put(ptr);
dvander@950
   272
			return 0;
dvander@950
   273
		}
dvander@950
   274
		if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950
   275
		{
dvander@950
   276
			will_copyback = true;
dvander@950
   277
		}
dvander@950
   278
	}
dvander@950
   279
dvander@950
   280
	/* Make the actual call */
dvander@950
   281
	vc->call->Execute(ptr, vc->retbuf);
dvander@950
   282
dvander@950
   283
	/* Do we need to copy anything back? */
dvander@950
   284
	if (will_copyback)
dvander@950
   285
	{
dvander@950
   286
		for (unsigned int i=0; i<callparams; i++)
dvander@950
   287
		{
dvander@950
   288
			if (vc->vparams[i].encflags & VENCODE_FLAG_COPYBACK)
dvander@950
   289
			{
dvander@950
   290
				if (EncodeValveParam(pContext, 
dvander@950
   291
					startparam + i, 
dvander@954
   292
					&vc->vparams[i],
dvander@950
   293
					ptr + vc->vparams[i].offset) == Data_Fail)
dvander@950
   294
				{
dvander@950
   295
					vc->stk_put(ptr);
dvander@950
   296
					return 0;
dvander@950
   297
				}
dvander@950
   298
			}
dvander@950
   299
		}
dvander@950
   300
	}
dvander@950
   301
dvander@950
   302
	/* Save stack once and for all */
dvander@950
   303
	vc->stk_put(ptr);
dvander@950
   304
dvander@950
   305
	/* Figure out how to decode the return information */
dvander@950
   306
	if (vc->retinfo)
dvander@950
   307
	{
dvander@950
   308
		if (vc->retinfo->vtype == Valve_String)
dvander@950
   309
		{
dvander@950
   310
			if (numparams < 3)
dvander@950
   311
			{
dvander@950
   312
				return pContext->ThrowNativeError("Expected arguments (2,3) for string storage");
dvander@950
   313
			}
dvander@950
   314
			cell_t *addr;
dvander@950
   315
			size_t written;
dvander@950
   316
			pContext->LocalToPhysAddr(params[retparam+1], &addr);
dvander@950
   317
			pContext->StringToLocalUTF8(params[retparam], *addr, *(char **)vc->retbuf, &written);
dvander@950
   318
			return (cell_t)written;
dvander@950
   319
		} else if (vc->retinfo->vtype == Valve_Vector
dvander@950
   320
					|| vc->retinfo->vtype == Valve_QAngle)
dvander@950
   321
		{
dvander@950
   322
			if (numparams < 2)
dvander@950
   323
			{
dvander@950
   324
				return pContext->ThrowNativeError("Expected argument (2) for Float[3] storage");
dvander@950
   325
			}
dvander@954
   326
			if (EncodeValveParam(pContext, params[retparam], vc->retinfo, vc->retbuf)
dvander@950
   327
				== Data_Fail)
dvander@950
   328
			{
dvander@950
   329
				return 0;
dvander@950
   330
			}
dvander@950
   331
		} else if (vc->retinfo->vtype == Valve_CBaseEntity
dvander@950
   332
					|| vc->retinfo->vtype == Valve_CBasePlayer)
dvander@950
   333
		{
dvander@950
   334
			CBaseEntity *pEntity = *(CBaseEntity **)(vc->retbuf);
dvander@950
   335
			if (!pEntity)
dvander@950
   336
			{
dvander@950
   337
				return -1;
dvander@950
   338
			}
dvander@950
   339
			edict_t *pEdict = gameents->BaseEntityToEdict(pEntity);
dvander@950
   340
			if (!pEdict || pEdict->IsFree())
dvander@950
   341
			{
dvander@950
   342
				return -1;
dvander@950
   343
			}
dvander@950
   344
			return engine->IndexOfEdict(pEdict);
dvander@950
   345
		} else if (vc->retinfo->vtype == Valve_Edict) {
dvander@950
   346
			edict_t *pEdict = *(edict_t **)(vc->retbuf);
dvander@950
   347
			if (!pEdict || pEdict->IsFree())
dvander@950
   348
			{
dvander@950
   349
				return -1;
dvander@950
   350
			}
dvander@950
   351
			return engine->IndexOfEdict(pEdict);
dvander@954
   352
		} else if (vc->retinfo->vtype == Valve_Bool) {
dvander@954
   353
			bool *addr = (bool  *)vc->retbuf;
dvander@954
   354
			if (vc->retinfo->flags & PASSFLAG_BYREF)
dvander@954
   355
			{
dvander@954
   356
				addr = *(bool **)addr;
dvander@954
   357
			}
dvander@954
   358
			return *addr ? 1 : 0;
dvander@950
   359
		} else {
dvander@950
   360
			cell_t *addr = (cell_t *)vc->retbuf;
dvander@950
   361
			if (vc->retinfo->flags & PASSFLAG_BYREF)
dvander@950
   362
			{
dvander@950
   363
				addr = *(cell_t **)addr;
dvander@950
   364
			}
dvander@950
   365
			return *addr;
dvander@950
   366
		}
dvander@950
   367
	}
dvander@950
   368
dvander@950
   369
	return 0;
dvander@950
   370
}
dvander@950
   371
dvander@950
   372
sp_nativeinfo_t g_CallNatives[] = 
dvander@950
   373
{
dvander@950
   374
	{"StartPrepSDKCall",			StartPrepSDKCall},
dvander@950
   375
	{"PrepSDKCall_SetVirtual",		PrepSDKCall_SetVirtual},
dvander@950
   376
	{"PrepSDKCall_SetSignature",	PrepSDKCall_SetSignature},
dvander@950
   377
	{"PrepSDKCall_SetFromConf",		PrepSDKCall_SetFromConf},
dvander@950
   378
	{"PrepSDKCall_SetReturnInfo",	PrepSDKCall_SetReturnInfo},
dvander@950
   379
	{"PrepSDKCall_AddParameter",	PrepSDKCall_AddParameter},
dvander@950
   380
	{"EndPrepSDKCall",				EndPrepSDKCall},
dvander@950
   381
	{"SDKCall",						SDKCall},
dvander@950
   382
	{NULL,							NULL},
dvander@950
   383
};