core/logic/NativeInvoker.cpp
author David Anderson <dvander@alliedmods.net>
Thu Aug 08 22:02:42 2013 -0700 (2013-08-08)
changeset 3980 dc7dfcd855dc
parent 3872 4c93f74cae51
child 4106 a39557bde4ac
permissions -rw-r--r--
More Windows build fix.
dvander@2545
     1
/**
dvander@2545
     2
 * vim: set ts=4 :
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@2545
    72
	NativeEntry *entry;
dvander@2545
    73
	
dvander@2545
    74
	entry = g_ShareSys.FindNative(name);
dvander@2545
    75
	if (entry == NULL)
dvander@2545
    76
	{
dvander@2545
    77
		return false;
dvander@2545
    78
	}
dvander@2545
    79
dvander@2545
    80
	native = NULL;
dvander@2545
    81
	if (entry->replacement.owner != NULL)
dvander@2545
    82
	{
dvander@2545
    83
		native = entry->replacement.func;
dvander@2545
    84
	}
dvander@2545
    85
	else if (entry->owner != NULL)
dvander@2545
    86
	{
dvander@2545
    87
		native = entry->func;
dvander@2545
    88
	}
dvander@2545
    89
dvander@2545
    90
	if (native == NULL)
dvander@2545
    91
	{
dvander@2545
    92
		return false;
dvander@2545
    93
	}
dvander@2545
    94
dvander@2545
    95
	this->pContext = pContext;
dvander@2545
    96
dvander@2545
    97
	m_curparam = 0;
dvander@2545
    98
	m_errorstate = SP_ERROR_NONE;
dvander@2545
    99
dvander@2545
   100
	return true;
dvander@2545
   101
}
dvander@2545
   102
dvander@2545
   103
cell_t NativeInvoker::PushCell(cell_t cell)
dvander@2545
   104
{
dvander@2545
   105
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
   106
	{
dvander@2545
   107
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
   108
	}
dvander@2545
   109
dvander@2545
   110
	m_info[m_curparam].marked = false;
dvander@2545
   111
	m_params[m_curparam] = cell;
dvander@2545
   112
	m_curparam++;
dvander@2545
   113
dvander@2545
   114
	return SP_ERROR_NONE;
dvander@2545
   115
}
dvander@2545
   116
dvander@2545
   117
int NativeInvoker::PushCellByRef(cell_t *cell, int flags)
dvander@2545
   118
{
dvander@2545
   119
	return PushArray(cell, 1, flags);
dvander@2545
   120
}
dvander@2545
   121
dvander@2545
   122
int NativeInvoker::PushFloat(float number)
dvander@2545
   123
{
dvander@2545
   124
	cell_t val = *(cell_t *)&number;
dvander@2545
   125
dvander@2545
   126
	return PushCell(val);
dvander@2545
   127
}
dvander@2545
   128
dvander@2545
   129
int NativeInvoker::PushFloatByRef(float *number, int flags)
dvander@2545
   130
{
dvander@2545
   131
	return PushCellByRef((cell_t *)number, flags);
dvander@2545
   132
}
dvander@2545
   133
dvander@2545
   134
int NativeInvoker::PushArray(cell_t *inarray, unsigned int cells, int copyback)
dvander@2545
   135
{
dvander@2545
   136
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
   137
	{
dvander@2545
   138
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
   139
	}
dvander@2545
   140
dvander@2545
   141
	ParamInfo *info = &m_info[m_curparam];
dvander@2545
   142
dvander@2545
   143
	info->flags = inarray ? copyback : 0;
dvander@2545
   144
	info->marked = true;
dvander@2545
   145
	info->size = cells;
dvander@2545
   146
	info->str.is_sz = false;
dvander@2545
   147
	info->orig_addr = inarray;
dvander@2545
   148
dvander@2545
   149
	m_curparam++;
dvander@2545
   150
dvander@2545
   151
	return SP_ERROR_NONE;
dvander@2545
   152
}
dvander@2545
   153
dvander@2545
   154
int NativeInvoker::PushString(const char *string)
dvander@2545
   155
{
dvander@2545
   156
	return _PushString(string, SM_PARAM_STRING_COPY, 0, strlen(string)+1);
dvander@2545
   157
}
dvander@2545
   158
dvander@2545
   159
int NativeInvoker::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags)
dvander@2545
   160
{
dvander@2545
   161
	return _PushString(buffer, sz_flags, cp_flags, length);
dvander@2545
   162
}
dvander@2545
   163
dvander@2545
   164
int NativeInvoker::_PushString(const char *string, int sz_flags, int cp_flags, size_t len)
dvander@2545
   165
{
dvander@2545
   166
	if (m_curparam >= SP_MAX_EXEC_PARAMS)
dvander@2545
   167
	{
dvander@2545
   168
		return SetError(SP_ERROR_PARAMS_MAX);
dvander@2545
   169
	}
dvander@2545
   170
dvander@2545
   171
	ParamInfo *info = &m_info[m_curparam];
dvander@2545
   172
dvander@2545
   173
	info->marked = true;
dvander@2545
   174
	info->orig_addr = (cell_t *)string;
dvander@2545
   175
	info->flags = cp_flags;
dvander@2545
   176
	info->size = len;
dvander@2545
   177
	info->str.sz_flags = sz_flags;
dvander@2545
   178
	info->str.is_sz = true;
dvander@2545
   179
dvander@2545
   180
	m_curparam++;
dvander@2545
   181
dvander@2545
   182
	return SP_ERROR_NONE;
dvander@2545
   183
}
dvander@2545
   184
dvander@2545
   185
void NativeInvoker::Cancel()
dvander@2545
   186
{
dvander@2545
   187
	if (pContext == NULL)
dvander@2545
   188
	{
dvander@2545
   189
		return;
dvander@2545
   190
	}
dvander@2545
   191
dvander@2545
   192
	m_errorstate = SP_ERROR_NONE;
dvander@2545
   193
	m_curparam = 0;
dvander@2545
   194
	pContext = NULL;
dvander@2545
   195
	native = NULL;
dvander@2545
   196
}
dvander@2545
   197
dvander@2545
   198
int NativeInvoker::SetError(int err)
dvander@2545
   199
{
dvander@2545
   200
	m_errorstate = err;
dvander@2545
   201
	return err;
dvander@2545
   202
}
dvander@2545
   203
dvander@2545
   204
int NativeInvoker::Invoke(cell_t *result)
dvander@2545
   205
{
dvander@2545
   206
	int err = SP_ERROR_NONE;
dvander@2545
   207
dvander@2545
   208
	if (pContext == NULL)
dvander@2545
   209
	{
dvander@2545
   210
		return SP_ERROR_INVALID_NATIVE;
dvander@2545
   211
	}
dvander@2545
   212
dvander@2545
   213
	if (m_errorstate != SP_ERROR_NONE)
dvander@2545
   214
	{
dvander@2545
   215
		err = m_errorstate;
dvander@2545
   216
		Cancel();
dvander@2545
   217
		return err;
dvander@2545
   218
	}
dvander@2545
   219
dvander@2545
   220
	cell_t tresult;
dvander@2545
   221
dvander@2545
   222
	if (result == NULL)
dvander@2545
   223
	{
dvander@2545
   224
		result = &tresult;
dvander@2545
   225
	}
dvander@2545
   226
dvander@2545
   227
	//This is for re-entrancy!
dvander@2545
   228
	IPluginContext *ctx = pContext;
dvander@2545
   229
	cell_t _temp_params[SP_MAX_EXEC_PARAMS + 1];
dvander@2545
   230
	cell_t *temp_params = &_temp_params[1];
dvander@2545
   231
	ParamInfo temp_info[SP_MAX_EXEC_PARAMS];
dvander@2545
   232
	unsigned int numparams = m_curparam;
dvander@2545
   233
	unsigned int i;
dvander@2545
   234
	bool docopies = true;
dvander@2545
   235
dvander@2545
   236
	if (numparams)
dvander@2545
   237
	{
dvander@2545
   238
		//Save the info locally, then reset it for re-entrant calls.
dvander@2545
   239
		memcpy(temp_info, m_info, numparams * sizeof(ParamInfo));
dvander@2545
   240
	}
dvander@2545
   241
	m_curparam = 0;
dvander@2545
   242
	pContext = NULL;
dvander@2545
   243
dvander@2545
   244
	/* Initialize 0th parameter */
dvander@2545
   245
	_temp_params[0] = numparams;
dvander@2545
   246
dvander@2545
   247
	/* Browse the parameters and build arrays */
dvander@2545
   248
	for (i = 0; i < numparams; i++)
dvander@2545
   249
	{
dvander@2545
   250
		/* Is this marked as an array? */
dvander@2545
   251
		if (temp_info[i].marked)
dvander@2545
   252
		{
dvander@2545
   253
			if (!temp_info[i].str.is_sz)
dvander@2545
   254
			{
dvander@2545
   255
				/* Allocate a normal/generic array */
dvander@2545
   256
				if ((err = ctx->HeapAlloc(temp_info[i].size, 
dvander@2545
   257
										  &temp_info[i].local_addr,
dvander@2545
   258
										  &temp_info[i].phys_addr))
dvander@2545
   259
					!= SP_ERROR_NONE)
dvander@2545
   260
				{
dvander@2545
   261
					break;
dvander@2545
   262
				}
dvander@2545
   263
				if (temp_info[i].orig_addr)
dvander@2545
   264
				{
dvander@2545
   265
					memcpy(temp_info[i].phys_addr, temp_info[i].orig_addr, sizeof(cell_t) * temp_info[i].size);
dvander@2545
   266
				}
dvander@2545
   267
			}
dvander@2545
   268
			else
dvander@2545
   269
			{
dvander@2545
   270
				/* Calculate cells required for the string */
dvander@2545
   271
				size_t cells = (temp_info[i].size + sizeof(cell_t) - 1) / sizeof(cell_t);
dvander@2545
   272
dvander@2545
   273
				/* Allocate the buffer */
dvander@2545
   274
				if ((err = ctx->HeapAlloc(cells,
dvander@2545
   275
										  &temp_info[i].local_addr,
dvander@2545
   276
										  &temp_info[i].phys_addr))
dvander@2545
   277
					!= SP_ERROR_NONE)
dvander@2545
   278
				{
dvander@2545
   279
					break;
dvander@2545
   280
				}
dvander@2545
   281
				/* Copy original string if necessary */
dvander@2545
   282
				if ((temp_info[i].str.sz_flags & SM_PARAM_STRING_COPY) && (temp_info[i].orig_addr != NULL))
dvander@2545
   283
				{
dvander@2545
   284
					/* Cut off UTF-8 properly */
dvander@2545
   285
					if (temp_info[i].str.sz_flags & SM_PARAM_STRING_UTF8)
dvander@2545
   286
					{
dvander@2545
   287
						if ((err = ctx->StringToLocalUTF8(temp_info[i].local_addr, 
dvander@2545
   288
														  temp_info[i].size, 
dvander@2545
   289
														  (const char *)temp_info[i].orig_addr,
dvander@2545
   290
														  NULL))
dvander@2545
   291
							!= SP_ERROR_NONE)
dvander@2545
   292
						{
dvander@2545
   293
							break;
dvander@2545
   294
						}
dvander@2545
   295
					}
dvander@2545
   296
					/* Copy a binary blob */
dvander@2545
   297
					else if (temp_info[i].str.sz_flags & SM_PARAM_STRING_BINARY)
dvander@2545
   298
					{
dvander@2545
   299
						memmove(temp_info[i].phys_addr, temp_info[i].orig_addr, temp_info[i].size);
dvander@2545
   300
					}
dvander@2545
   301
					/* Copy ASCII characters */
dvander@2545
   302
					else
dvander@2545
   303
					{
dvander@2545
   304
						if ((err = ctx->StringToLocal(temp_info[i].local_addr,
dvander@2545
   305
													  temp_info[i].size,
dvander@2545
   306
													  (const char *)temp_info[i].orig_addr))
dvander@2545
   307
							!= SP_ERROR_NONE)
dvander@2545
   308
						{
dvander@2545
   309
							break;
dvander@2545
   310
						}
dvander@2545
   311
					}
dvander@2545
   312
				}
dvander@2545
   313
			} /* End array/string calculation */
dvander@2545
   314
			/* Update the pushed parameter with the byref local address */
dvander@2545
   315
			temp_params[i] = temp_info[i].local_addr;
dvander@2545
   316
		}
dvander@2545
   317
		else
dvander@2545
   318
		{
dvander@2545
   319
			/* Just copy the value normally */
dvander@2545
   320
			temp_params[i] = m_params[i];
dvander@2545
   321
		}
dvander@2545
   322
	}
dvander@2545
   323
dvander@2545
   324
	/* Make the call if we can */
dvander@2545
   325
	if (err == SP_ERROR_NONE)
dvander@2545
   326
	{
dvander@2545
   327
		*result = native(ctx, _temp_params);
dvander@2545
   328
		if (ctx->GetLastNativeError() != SP_ERROR_NONE)
dvander@2545
   329
		{
dvander@2545
   330
			docopies = false;
dvander@2545
   331
			ctx->ClearLastNativeError();
dvander@2545
   332
		}
dvander@2545
   333
	}
dvander@2545
   334
	else
dvander@2545
   335
	{
dvander@2545
   336
		docopies = false;
dvander@2545
   337
	}
dvander@2545
   338
dvander@2545
   339
	/* i should be equal to the last valid parameter + 1 */
dvander@2545
   340
	while (i--)
dvander@2545
   341
	{
dvander@2545
   342
		if (!temp_info[i].marked)
dvander@2545
   343
		{
dvander@2545
   344
			continue;
dvander@2545
   345
		}
dvander@2545
   346
dvander@2545
   347
		if (docopies && (temp_info[i].flags & SM_PARAM_COPYBACK))
dvander@2545
   348
		{
dvander@2545
   349
			if (temp_info[i].orig_addr)
dvander@2545
   350
			{
dvander@2545
   351
				if (temp_info[i].str.is_sz)
dvander@2545
   352
				{
dvander@2545
   353
					memcpy(temp_info[i].orig_addr, temp_info[i].phys_addr, temp_info[i].size);
dvander@2545
   354
dvander@2545
   355
				}
dvander@2545
   356
				else
dvander@2545
   357
				{
dvander@2545
   358
					if (temp_info[i].size == 1)
dvander@2545
   359
					{
dvander@2545
   360
						*temp_info[i].orig_addr = *(temp_info[i].phys_addr);
dvander@2545
   361
					}
dvander@2545
   362
					else
dvander@2545
   363
					{
dvander@2545
   364
						memcpy(temp_info[i].orig_addr, 
dvander@2545
   365
							temp_info[i].phys_addr, 
dvander@2545
   366
							temp_info[i].size * sizeof(cell_t));
dvander@2545
   367
					}
dvander@2545
   368
				}
dvander@2545
   369
			}
dvander@2545
   370
		}
dvander@2545
   371
dvander@2545
   372
		if ((err = ctx->HeapPop(temp_info[i].local_addr)) != SP_ERROR_NONE)
dvander@2545
   373
		{
dvander@2545
   374
			return err;
dvander@2545
   375
		}
dvander@2545
   376
	}
dvander@2545
   377
dvander@2545
   378
	return err;
dvander@2545
   379
}