core/logic/NativeInvoker.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Feb 09 22:50:20 2014 -0600 (2014-02-09)
changeset 4241 46fdb8cd9883
parent 4109 10eb68d668af
permissions -rw-r--r--
Use == instead of is operator for checking string equality in AMBuild scripts (bug 6043, r=dvander).
dvander@2545
     1
/**
dvander@4106
     2
 * vim: set ts=4 sw=4 tw=99 noet :
dvander@2545
     3
 * =============================================================================
dvander@2545
     4
 * SourcePawn
dvander@2545
     5
 * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
dvander@2545
     6
 * =============================================================================
dvander@2545
     7
 *
dvander@2545
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2545
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2545
    10
 * Free Software Foundation.
dvander@2545
    11
 * 
dvander@2545
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2545
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2545
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2545
    15
 * details.
dvander@2545
    16
 *
dvander@2545
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2545
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2545
    19
 *
dvander@2545
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2545
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2545
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2545
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2545
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2545
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2545
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2545
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2545
    28
 *
dvander@2545
    29
 * Version: $Id$
dvander@2545
    30
 */
dvander@2545
    31
dvander@2545
    32
#include "NativeInvoker.h"
dvander@2545
    33
#include "ShareSys.h"
dvander@2545
    34
dvander@2545
    35
NativeInterface g_NInvoke;
dvander@2545
    36
dvander@2545
    37
NativeInvoker::NativeInvoker()
dvander@2545
    38
{
dvander@2545
    39
}
dvander@2545
    40
dvander@2545
    41
NativeInvoker::~NativeInvoker()
dvander@2545
    42
{
dvander@2545
    43
}
dvander@2545
    44
dvander@2545
    45
const char *NativeInterface::GetInterfaceName()
dvander@2545
    46
{
dvander@2545
    47
	return SMINTERFACE_NINVOKE_NAME;
dvander@2545
    48
}
dvander@2545
    49
dvander@2545
    50
unsigned int NativeInterface::GetInterfaceVersion()
dvander@2545
    51
{
dvander@2545
    52
	return SMINTERFACE_NINVOKE_VERSION;
dvander@2545
    53
}
dvander@2545
    54
dvander@2545
    55
void NativeInterface::OnSourceModAllInitialized()
dvander@2545
    56
{
dvander@3872
    57
	sharesys->AddInterface(NULL, &g_NInvoke);
dvander@2545
    58
}
dvander@2545
    59
dvander@2545
    60
IPluginRuntime *NativeInterface::CreateRuntime(const char *name, size_t bytes)
dvander@2545
    61
{
dvander@2545
    62
	return g_pSourcePawn2->CreateEmptyRuntime(name, bytes);
dvander@2545
    63
}
dvander@2545
    64
dvander@2545
    65
INativeInvoker *NativeInterface::CreateInvoker()
dvander@2545
    66
{
dvander@2545
    67
	return new NativeInvoker();
dvander@2545
    68
}
dvander@2545
    69
dvander@2545
    70
bool NativeInvoker::Start(IPluginContext *pContext, const char *name)
dvander@2545
    71
{
dvander@4109
    72
	ke::Ref<Native> entry = g_ShareSys.FindNative(name);
dvander@4106
    73
	if (!entry)
dvander@2545
    74
		return false;
dvander@2545
    75
dvander@4109
    76
	if (!entry->owner)
dvander@4106
    77
		return false;
dvander@2545
    78
dvander@4108
    79
	native_ = entry->func();
dvander@4106
    80
	context_ = pContext;
dvander@2545
    81
dvander@2545
    82
	m_curparam = 0;
dvander@2545
    83
	m_errorstate = SP_ERROR_NONE;
dvander@2545
    84
dvander@2545
    85
	return true;
dvander@2545
    86
}
dvander@2545
    87
dvander@2545
    88
cell_t NativeInvoker::PushCell(cell_t cell)
dvander@2545
    89
{
dvander@2545
    90
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
    91
	{
dvander@2545
    92
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
    93
	}
dvander@2545
    94
dvander@2545
    95
	m_info[m_curparam].marked = false;
dvander@2545
    96
	m_params[m_curparam] = cell;
dvander@2545
    97
	m_curparam++;
dvander@2545
    98
dvander@2545
    99
	return SP_ERROR_NONE;
dvander@2545
   100
}
dvander@2545
   101
dvander@2545
   102
int NativeInvoker::PushCellByRef(cell_t *cell, int flags)
dvander@2545
   103
{
dvander@2545
   104
	return PushArray(cell, 1, flags);
dvander@2545
   105
}
dvander@2545
   106
dvander@2545
   107
int NativeInvoker::PushFloat(float number)
dvander@2545
   108
{
dvander@2545
   109
	cell_t val = *(cell_t *)&number;
dvander@2545
   110
dvander@2545
   111
	return PushCell(val);
dvander@2545
   112
}
dvander@2545
   113
dvander@2545
   114
int NativeInvoker::PushFloatByRef(float *number, int flags)
dvander@2545
   115
{
dvander@2545
   116
	return PushCellByRef((cell_t *)number, flags);
dvander@2545
   117
}
dvander@2545
   118
dvander@2545
   119
int NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
dvander@2545
   120
{
dvander@2545
   121
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
   122
	{
dvander@2545
   123
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
   124
	}
dvander@2545
   125
dvander@2545
   126
	ParamInfo *info = &m_info[m_curparam];
dvander@2545
   127
dvander@2545
   128
	info->flags = inarray ? copyback : 0;
dvander@2545
   129
	info->marked = true;
dvander@2545
   130
	info->size = cells;
dvander@2545
   131
	info->str.is_sz = false;
dvander@2545
   132
	info->orig_addr = inarray;
dvander@2545
   133
dvander@2545
   134
	m_curparam++;
dvander@2545
   135
dvander@2545
   136
	return SP_ERROR_NONE;
dvander@2545
   137
}
dvander@2545
   138
dvander@2545
   139
int NativeInvoker::PushString(const char *string)
dvander@2545
   140
{
dvander@2545
   141
	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
dvander@2545
   142
}
dvander@2545
   143
dvander@2545
   144
int NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
dvander@2545
   145
{
dvander@2545
   146
	return _PushString(buffer, sz_flags, cp_flags, length);
dvander@2545
   147
}
dvander@2545
   148
dvander@2545
   149
int NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
dvander@2545
   150
{
dvander@2545
   151
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
   152
	{
dvander@2545
   153
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
   154
	}
dvander@2545
   155
dvander@2545
   156
	ParamInfo *info = &m_info[m_curparam];
dvander@2545
   157
dvander@2545
   158
	info->marked = true;
dvander@2545
   159
	info->orig_addr = (cell_t *)string;
dvander@2545
   160
	info->flags = cp_flags;
dvander@2545
   161
	info->size = len;
dvander@2545
   162
	info->str.sz_flags = sz_flags;
dvander@2545
   163
	info->str.is_sz = true;
dvander@2545
   164
dvander@2545
   165
	m_curparam++;
dvander@2545
   166
dvander@2545
   167
	return SP_ERROR_NONE;
dvander@2545
   168
}
dvander@2545
   169
dvander@2545
   170
void NativeInvoker::Cancel()
dvander@2545
   171
{
dvander@4106
   172
	if (context_ == NULL)
dvander@2545
   173
		return;
dvander@2545
   174
dvander@2545
   175
	m_errorstate = SP_ERROR_NONE;
dvander@2545
   176
	m_curparam = 0;
dvander@4106
   177
	context_ = NULL;
dvander@4106
   178
	native_ = NULL;
dvander@2545
   179
}
dvander@2545
   180
dvander@2545
   181
int NativeInvoker::SetError(int err)
dvander@2545
   182
{
dvander@2545
   183
	m_errorstate = err;
dvander@2545
   184
	return err;
dvander@2545
   185
}
dvander@2545
   186
dvander@2545
   187
int NativeInvoker::Invoke(cell_t *result)
dvander@2545
   188
{
dvander@2545
   189
	int err = SP_ERROR_NONE;
dvander@2545
   190
dvander@4106
   191
	if (context_ == NULL)
dvander@2545
   192
		return SP_ERROR_INVALID_NATIVE;
dvander@2545
   193
dvander@2545
   194
	if (m_errorstate != SP_ERROR_NONE)
dvander@2545
   195
	{
dvander@2545
   196
		err = m_errorstate;
dvander@2545
   197
		Cancel();
dvander@2545
   198
		return err;
dvander@2545
   199
	}
dvander@2545
   200
dvander@2545
   201
	cell_t tresult;
dvander@2545
   202
dvander@2545
   203
	if (result == NULL)
dvander@2545
   204
	{
dvander@2545
   205
		result = &tresult;
dvander@2545
   206
	}
dvander@2545
   207
dvander@2545
   208
	//This is for re-entrancy!
dvander@4106
   209
	IPluginContext *ctx = context_;
dvander@2545
   210
	cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
dvander@2545
   211
	cell_t *temp_params = &_temp_params[1];
dvander@2545
   212
	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
dvander@2545
   213
	unsigned int numparams = m_curparam;
dvander@2545
   214
	unsigned int i;
dvander@2545
   215
	bool docopies = true;
dvander@2545
   216
dvander@2545
   217
	if (numparams)
dvander@2545
   218
	{
dvander@2545
   219
		//Save the info locally, then reset it for re-entrant calls.
dvander@2545
   220
		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
dvander@2545
   221
	}
dvander@2545
   222
	m_curparam = 0;
dvander@4106
   223
	context_ = NULL;
dvander@2545
   224
dvander@2545
   225
	/* Initialize 0th parameter */
dvander@2545
   226
	_temp_params[0] = numparams;
dvander@2545
   227
dvander@2545
   228
	/* Browse the parameters and build arrays */
dvander@2545
   229
	for (i = 0; i < numparams; i++)
dvander@2545
   230
	{
dvander@2545
   231
		/* Is this marked as an array? */
dvander@2545
   232
		if (temp_info[i].marked)
dvander@2545
   233
		{
dvander@2545
   234
			if (!temp_info[i].str.is_sz)
dvander@2545
   235
			{
dvander@2545
   236
				/* Allocate a normal/generic array */
dvander@2545
   237
				if ((err = ctx->HeapAlloc(temp_info[i].size, 
dvander@2545
   238
										  &temp_info[i].local_addr,
dvander@2545
   239
										  &temp_info[i].phys_addr))
dvander@2545
   240
					!= SP_ERROR_NONE)
dvander@2545
   241
				{
dvander@2545
   242
					break;
dvander@2545
   243
				}
dvander@2545
   244
				if (temp_info[i].orig_addr)
dvander@2545
   245
				{
dvander@2545
   246
					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
dvander@2545
   247
				}
dvander@2545
   248
			}
dvander@2545
   249
			else
dvander@2545
   250
			{
dvander@2545
   251
				/* Calculate cells required for the string */
dvander@2545
   252
				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
dvander@2545
   253
dvander@2545
   254
				/* Allocate the buffer */
dvander@2545
   255
				if ((err = ctx->HeapAlloc(cells,
dvander@2545
   256
										  &temp_info[i].local_addr,
dvander@2545
   257
										  &temp_info[i].phys_addr))
dvander@2545
   258
					!= SP_ERROR_NONE)
dvander@2545
   259
				{
dvander@2545
   260
					break;
dvander@2545
   261
				}
dvander@2545
   262
				/* Copy original string if necessary */
dvander@2545
   263
				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
dvander@2545
   264
				{
dvander@2545
   265
					/* Cut off UTF-8 properly */
dvander@2545
   266
					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
dvander@2545
   267
					{
dvander@2545
   268
						if ((err = ctx->StringToLocalUTF8(temp_info[i].local_addr, 
dvander@2545
   269
														  temp_info[i].size, 
dvander@2545
   270
														  (const char *)temp_info[i].orig_addr,
dvander@2545
   271
														  NULL))
dvander@2545
   272
							!= SP_ERROR_NONE)
dvander@2545
   273
						{
dvander@2545
   274
							break;
dvander@2545
   275
						}
dvander@2545
   276
					}
dvander@2545
   277
					/* Copy a binary blob */
dvander@2545
   278
					else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
dvander@2545
   279
					{
dvander@2545
   280
						memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
dvander@2545
   281
					}
dvander@2545
   282
					/* Copy ASCII characters */
dvander@2545
   283
					else
dvander@2545
   284
					{
dvander@2545
   285
						if ((err = ctx->StringToLocal(temp_info[i].local_addr,
dvander@2545
   286
													  temp_info[i].size,
dvander@2545
   287
													  (const char *)temp_info[i].orig_addr))
dvander@2545
   288
							!= SP_ERROR_NONE)
dvander@2545
   289
						{
dvander@2545
   290
							break;
dvander@2545
   291
						}
dvander@2545
   292
					}
dvander@2545
   293
				}
dvander@2545
   294
			} /* End array/string calculation */
dvander@2545
   295
			/* Update the pushed parameter with the byref local address */
dvander@2545
   296
			temp_params[i] = temp_info[i].local_addr;
dvander@2545
   297
		}
dvander@2545
   298
		else
dvander@2545
   299
		{
dvander@2545
   300
			/* Just copy the value normally */
dvander@2545
   301
			temp_params[i] = m_params[i];
dvander@2545
   302
		}
dvander@2545
   303
	}
dvander@2545
   304
dvander@2545
   305
	/* Make the call if we can */
dvander@2545
   306
	if (err == SP_ERROR_NONE)
dvander@2545
   307
	{
dvander@4106
   308
		*result = native_(ctx, _temp_params);
dvander@2545
   309
		if (ctx->GetLastNativeError() != SP_ERROR_NONE)
dvander@2545
   310
		{
dvander@2545
   311
			docopies = false;
dvander@2545
   312
			ctx->ClearLastNativeError();
dvander@2545
   313
		}
dvander@2545
   314
	}
dvander@2545
   315
	else
dvander@2545
   316
	{
dvander@2545
   317
		docopies = false;
dvander@2545
   318
	}
dvander@2545
   319
dvander@2545
   320
	/* i should be equal to the last valid parameter + 1 */
dvander@2545
   321
	while (i--)
dvander@2545
   322
	{
dvander@2545
   323
		if (!temp_info[i].marked)
dvander@2545
   324
		{
dvander@2545
   325
			continue;
dvander@2545
   326
		}
dvander@2545
   327
dvander@2545
   328
		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
dvander@2545
   329
		{
dvander@2545
   330
			if (temp_info[i].orig_addr)
dvander@2545
   331
			{
dvander@2545
   332
				if (temp_info[i].str.is_sz)
dvander@2545
   333
				{
dvander@2545
   334
					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
dvander@2545
   335
dvander@2545
   336
				}
dvander@2545
   337
				else
dvander@2545
   338
				{
dvander@2545
   339
					if (temp_info[i].size == 1)
dvander@2545
   340
					{
dvander@2545
   341
						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
dvander@2545
   342
					}
dvander@2545
   343
					else
dvander@2545
   344
					{
dvander@2545
   345
						memcpy(temp_info[i].orig_addr, 
dvander@2545
   346
							temp_info[i].phys_addr, 
dvander@2545
   347
							temp_info[i].size * sizeof(cell_t));
dvander@2545
   348
					}
dvander@2545
   349
				}
dvander@2545
   350
			}
dvander@2545
   351
		}
dvander@2545
   352
dvander@2545
   353
		if ((err = ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
dvander@2545
   354
		{
dvander@2545
   355
			return err;
dvander@2545
   356
		}
dvander@2545
   357
	}
dvander@2545
   358
dvander@2545
   359
	return err;
dvander@2545
   360
}