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