core/vm/sp_vm_basecontext.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 29 00:47:05 2007 +0000 (2007-08-29)
changeset 1382 5399d6f24417
parent 1367 0253af5c3bee
child 1383 9e05176c1e92
permissions -rw-r--r--
greatly optimized the process of mapping plugin natives from Core
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2007 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 <string.h>
    33 #include <stdarg.h>
    34 #include <assert.h>
    35 #include <limits.h>
    36 #include "sp_vm_api.h"
    37 #include "sp_vm_basecontext.h"
    38 #include "sp_vm_engine.h"
    39 
    40 #ifdef SOURCEMOD_BUILD
    41 #include "Logger.h"
    42 #include "DebugReporter.h"
    43 #endif
    44 
    45 using namespace SourcePawn;
    46 
    47 extern SourcePawnEngine g_SourcePawn;
    48 
    49 #define CELLBOUNDMAX	(INT_MAX/sizeof(cell_t))
    50 #define STACKMARGIN		((cell_t)(16*sizeof(cell_t)))
    51 
    52 int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
    53 {
    54 	g_SourcePawn.RunTracer(ctx, frm, cip);
    55 
    56 	return SP_ERROR_NONE;
    57 }
    58 
    59 BaseContext::BaseContext(sp_context_t *_ctx)
    60 {
    61 	ctx = _ctx;
    62 	ctx->context = this;
    63 	ctx->dbreak = GlobalDebugBreak;
    64 	m_InExec = false;
    65 	m_CustomMsg = false;
    66 	m_funcsnum = ctx->vmbase->FunctionCount(ctx);
    67 #if 0
    68 	m_priv_funcs = NULL;
    69 #endif
    70 	m_pub_funcs = NULL;
    71 
    72 #if 0
    73 	/**
    74 	 * Note: Since the m_plugin member will never change,
    75 	 * it is safe to assume the function count will never change
    76 	 */
    77 	if (m_funcsnum && m_priv_funcs == NULL)
    78 	{
    79 		m_priv_funcs = new CFunction *[m_funcsnum];
    80 		memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
    81 	} else {
    82 		m_priv_funcs = NULL;
    83 	}
    84 #endif
    85 
    86 	if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
    87 	{
    88 		m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
    89 		memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
    90 	} else {
    91 		m_pub_funcs = NULL;
    92 	}
    93 
    94 	/* Initialize the null references */
    95 	uint32_t index;
    96 	if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
    97 	{
    98 		sp_pubvar_t *pubvar;
    99 		GetPubvarByIndex(index, &pubvar);
   100 		m_pNullVec = pubvar->offs;
   101 	} else {
   102 		m_pNullVec = NULL;
   103 	}
   104 
   105 	if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
   106 	{
   107 		sp_pubvar_t *pubvar;
   108 		GetPubvarByIndex(index, &pubvar);
   109 		m_pNullString = pubvar->offs;
   110 	} else {
   111 		m_pNullString = NULL;
   112 	}
   113 }
   114 
   115 void BaseContext::FlushFunctionCache()
   116 {
   117 	if (m_pub_funcs)
   118 	{
   119 		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
   120 		{
   121 			delete m_pub_funcs[i];
   122 			m_pub_funcs[i] = NULL;
   123 		}
   124 	}
   125 
   126 #if 0
   127 	if (m_priv_funcs)
   128 	{
   129 		for (unsigned int i=0; i<m_funcsnum; i++)
   130 		{
   131 			delete m_priv_funcs[i];
   132 			m_priv_funcs[i] = NULL;
   133 		}
   134 	}
   135 #endif
   136 }
   137 
   138 void BaseContext::RefreshFunctionCache()
   139 {
   140 	if (m_pub_funcs)
   141 	{
   142 		sp_public_t *pub;
   143 		for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
   144 		{
   145 			if (!m_pub_funcs[i])
   146 			{
   147 				continue;
   148 			}
   149 			if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
   150 			{
   151 				continue;
   152 			}
   153 			m_pub_funcs[i]->Set(pub->code_offs, this, pub->funcid);
   154 		}
   155 	}
   156 
   157 #if 0
   158 	if (m_priv_funcs)
   159 	{
   160 		for (unsigned int i=0; i<m_funcsnum; i++)
   161 		{
   162 			if (!m_priv_funcs[i])
   163 			{
   164 				continue;
   165 			}
   166 			g_pVM->
   167 		}
   168 	}
   169 #endif
   170 }
   171 
   172 BaseContext::~BaseContext()
   173 {
   174 	FlushFunctionCache();
   175 	delete [] m_pub_funcs;
   176 	m_pub_funcs = NULL;
   177 #if 0
   178 	delete [] m_priv_funcs;
   179 	m_priv_funcs = NULL;
   180 #endif
   181 }
   182 
   183 void BaseContext::SetContext(sp_context_t *_ctx)
   184 {
   185 	if (!_ctx)
   186 	{
   187 		return;
   188 	}
   189 	ctx = _ctx;
   190 	ctx->context = this;
   191 	ctx->dbreak = GlobalDebugBreak;
   192 	RefreshFunctionCache();
   193 }
   194 
   195 IVirtualMachine *BaseContext::GetVirtualMachine()
   196 {
   197 	return (IVirtualMachine *)ctx->vmbase;
   198 }
   199 
   200 sp_context_t *BaseContext::GetContext()
   201 {
   202 	return ctx;
   203 }
   204 
   205 bool BaseContext::IsDebugging()
   206 {
   207 	return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
   208 }
   209 
   210 int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
   211 {
   212 	if (!IsDebugging())
   213 	{
   214 		return SP_ERROR_NOTDEBUGGING;
   215 	}
   216 
   217 	*oldpfn = ctx->dbreak;
   218 	ctx->dbreak = newpfn;
   219 
   220 	return SP_ERROR_NONE;
   221 }
   222 
   223 IPluginDebugInfo *BaseContext::GetDebugInfo()
   224 {
   225 	if (!IsDebugging())
   226 	{
   227 		return NULL;
   228 	}
   229 	return this;
   230 }
   231 
   232 int BaseContext::Execute(uint32_t code_addr, cell_t *result)
   233 {
   234 	if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
   235 	{
   236 		return SP_ERROR_NOT_RUNNABLE;
   237 	}
   238 
   239 	/* tada, prevent a crash */
   240 	cell_t _ignore_result;
   241 	if (!result)
   242 	{
   243 		result = &_ignore_result;
   244 	}
   245 
   246 	IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
   247 
   248 	uint32_t pushcount = ctx->pushcount;
   249 	int err;
   250 
   251 	if ((err = PushCell(pushcount++)) != SP_ERROR_NONE)
   252 	{
   253 #if defined SOURCEMOD_BUILD
   254 		g_DbgReporter.GenerateCodeError(this, code_addr, err, "Stack error; cannot complete execution!");
   255 #endif
   256 		return SP_ERROR_NOT_RUNNABLE;
   257 	}
   258 	ctx->pushcount = 0;
   259 
   260 	cell_t save_sp = ctx->sp + (pushcount * sizeof(cell_t));
   261 	cell_t save_hp = ctx->hp;
   262 	uint32_t n_idx = ctx->n_idx;
   263 
   264 	bool wasExec = m_InExec;
   265 
   266 	/* Clear the error state, if any */
   267 	ctx->n_err = SP_ERROR_NONE;
   268 	ctx->n_idx = 0;
   269 	m_InExec = true;
   270 	m_MsgCache[0] = '\0';
   271 	m_CustomMsg = false;
   272 
   273 	g_SourcePawn.PushTracer(ctx);
   274 
   275 	err = vm->ContextExecute(ctx, code_addr, result);
   276 
   277 	m_InExec = wasExec;
   278 
   279 	/**
   280 	 * :TODO: Calling from a plugin in here will erase the cached message...
   281 	 * Should that be documented?
   282 	 */
   283 	g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
   284 
   285 #if defined SOURCEMOD_BUILD
   286 	if (err == SP_ERROR_NONE)
   287 	{
   288 		if (ctx->sp != save_sp)
   289 		{
   290 			g_DbgReporter.GenerateCodeError(this,
   291 				code_addr,
   292 				SP_ERROR_STACKLEAK,
   293 				"Stack leak detected: sp:%d should be %d!",
   294 				ctx->sp,
   295 				save_sp);
   296 		}
   297 		if (ctx->hp != save_hp)
   298 		{
   299 			g_DbgReporter.GenerateCodeError(this,
   300 				code_addr,
   301 				SP_ERROR_HEAPLEAK,
   302 				"Heap leak detected: sp:%d should be %d!",
   303 				ctx->hp,
   304 				save_hp);
   305 		}
   306 	}
   307 #endif
   308 
   309 	if (err != SP_ERROR_NONE)
   310 	{
   311 		ctx->sp = save_sp;
   312 		ctx->hp = save_hp;
   313 	}
   314 
   315 	ctx->n_idx = n_idx;
   316 	ctx->n_err = SP_ERROR_NONE;
   317 	m_MsgCache[0] = '\0';
   318 	m_CustomMsg = false;
   319 
   320 	return err;
   321 }
   322 
   323 void BaseContext::SetErrorMessage(const char *msg, va_list ap)
   324 {
   325 	m_CustomMsg = true;
   326 
   327 	vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
   328 }
   329 
   330 cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
   331 {
   332 	if (!m_InExec)
   333 	{
   334 		return 0;
   335 	}
   336 
   337 	ctx->n_err = error;
   338 	
   339 	if (msg)
   340 	{
   341 		va_list ap;
   342 		va_start(ap, msg);
   343 		SetErrorMessage(msg, ap);
   344 		va_end(ap);
   345 	}
   346 
   347 	return 0;
   348 }
   349 
   350 cell_t BaseContext::ThrowNativeError(const char *msg, ...)
   351 {
   352 	if (!m_InExec)
   353 	{
   354 		return 0;
   355 	}
   356 
   357 	ctx->n_err = SP_ERROR_NATIVE;
   358 
   359 	if (msg)
   360 	{
   361 		va_list ap;
   362 		va_start(ap, msg);
   363 		SetErrorMessage(msg, ap);
   364 		va_end(ap);
   365 	}
   366 
   367 	return 0;
   368 }
   369 
   370 int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
   371 {
   372 	cell_t *addr;
   373 	ucell_t realmem;
   374 
   375 #if 0
   376 	if (cells > CELLBOUNDMAX)
   377 	{
   378 		return SP_ERROR_ARAM;
   379 	}
   380 #else
   381 	assert(cells < CELLBOUNDMAX);
   382 #endif
   383 
   384 	realmem = cells * sizeof(cell_t);
   385 
   386 	/**
   387 	 * Check if the space between the heap and stack is sufficient.
   388 	 */
   389 	if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
   390 	{
   391 		return SP_ERROR_HEAPLOW;
   392 	}
   393 
   394 	addr = (cell_t *)(ctx->memory + ctx->hp);
   395 	/* store size of allocation in cells */
   396 	*addr = (cell_t)cells;
   397 	addr++;
   398 	ctx->hp += sizeof(cell_t);
   399 
   400 	*local_addr = ctx->hp;
   401 
   402 	if (phys_addr)
   403 	{
   404 		*phys_addr = addr;
   405 	}
   406 
   407 	ctx->hp += realmem;
   408 
   409 	return SP_ERROR_NONE;
   410 }
   411 
   412 int BaseContext::HeapPop(cell_t local_addr)
   413 {
   414 	cell_t cellcount;
   415 	cell_t *addr;
   416 
   417 	/* check the bounds of this address */
   418 	local_addr -= sizeof(cell_t);
   419 	if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
   420 	{
   421 		return SP_ERROR_INVALID_ADDRESS;
   422 	}
   423 
   424 	addr = (cell_t *)(ctx->memory + local_addr);
   425 	cellcount = (*addr) * sizeof(cell_t);
   426 	/* check if this memory count looks valid */
   427 	if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
   428 	{
   429 		return SP_ERROR_INVALID_ADDRESS;
   430 	}
   431 
   432 	ctx->hp = local_addr;
   433 
   434 	return SP_ERROR_NONE;
   435 }
   436 
   437 
   438 int BaseContext::HeapRelease(cell_t local_addr)
   439 {
   440 	if (local_addr < ctx->heap_base)
   441 	{
   442 		return SP_ERROR_INVALID_ADDRESS;
   443 	}
   444 
   445 	ctx->hp = local_addr - sizeof(cell_t);
   446 
   447 	return SP_ERROR_NONE;
   448 }
   449 
   450 int BaseContext::FindNativeByName(const char *name, uint32_t *index)
   451 {
   452 	int high;
   453 
   454 	high = ctx->plugin->info.natives_num - 1;
   455 
   456 	for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
   457 	{
   458 		if (strcmp(ctx->natives[i].name, name) == 0)
   459 		{
   460 			if (index)
   461 			{
   462 				*index = i;
   463 			}
   464 			return SP_ERROR_NONE;
   465 		}
   466 	}
   467 
   468 	return SP_ERROR_NOT_FOUND;
   469 }
   470 
   471 int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
   472 {
   473 	if (index >= ctx->plugin->info.natives_num)
   474 	{
   475 		return SP_ERROR_INDEX;
   476 	}
   477 
   478 	if (native)
   479 	{
   480 		*native = &(ctx->natives[index]);
   481 	}
   482 
   483 	return SP_ERROR_NONE;
   484 }
   485 
   486 
   487 uint32_t BaseContext::GetNativesNum()
   488 {
   489 	return ctx->plugin->info.natives_num;
   490 }
   491 
   492 int BaseContext::FindPublicByName(const char *name, uint32_t *index)
   493 {
   494 	int diff, high, low;
   495 	uint32_t mid;
   496 
   497 	high = ctx->plugin->info.publics_num - 1;
   498 	low = 0;
   499 
   500 	while (low <= high)
   501 	{
   502 		mid = (low + high) / 2;
   503 		diff = strcmp(ctx->publics[mid].name, name);
   504 		if (diff == 0)
   505 		{
   506 			if (index)
   507 			{
   508 				*index = mid;
   509 			}
   510 			return SP_ERROR_NONE;
   511 		} else if (diff < 0) {
   512 			low = mid + 1;
   513 		} else {
   514 			high = mid - 1;
   515 		}
   516 	}
   517 
   518 	return SP_ERROR_NOT_FOUND;
   519 }
   520 
   521 int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
   522 {
   523 	if (index >= ctx->plugin->info.publics_num)
   524 	{
   525 		return SP_ERROR_INDEX;
   526 	}
   527 
   528 	if (pblic)
   529 	{
   530 		*pblic = &(ctx->publics[index]);
   531 	}
   532 
   533 	return SP_ERROR_NONE;
   534 }
   535 
   536 uint32_t BaseContext::GetPublicsNum()
   537 {
   538 	return ctx->plugin->info.publics_num;
   539 }
   540 
   541 int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
   542 {
   543 	if (index >= ctx->plugin->info.pubvars_num)
   544 	{
   545 		return SP_ERROR_INDEX;
   546 	}
   547 
   548 	if (pubvar)
   549 	{
   550 		*pubvar = &(ctx->pubvars[index]);
   551 	}
   552 
   553 	return SP_ERROR_NONE;
   554 }
   555 
   556 int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
   557 {
   558 	int diff, high, low;
   559 	uint32_t mid;
   560 
   561 	high = ctx->plugin->info.pubvars_num - 1;
   562 	low = 0;
   563 
   564 	while (low <= high)
   565 	{
   566 		mid = (low + high) / 2;
   567 		diff = strcmp(ctx->pubvars[mid].name, name);
   568 		if (diff == 0)
   569 		{
   570 			if (index)
   571 			{
   572 				*index = mid;
   573 			}
   574 			return SP_ERROR_NONE;
   575 		} else if (diff < 0) {
   576 			low = mid + 1;
   577 		} else {
   578 			high = mid - 1;
   579 		}
   580 	}
   581 
   582 	return SP_ERROR_NOT_FOUND;
   583 }
   584 
   585 int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
   586 {
   587 	if (index >= ctx->plugin->info.pubvars_num)
   588 	{
   589 		return SP_ERROR_INDEX;
   590 	}
   591 
   592 	*local_addr = ctx->plugin->info.pubvars[index].address;
   593 	*phys_addr = ctx->pubvars[index].offs;
   594 
   595 	return SP_ERROR_NONE;
   596 }
   597 
   598 uint32_t BaseContext::GetPubVarsNum()
   599 {
   600 	return ctx->plugin->info.pubvars_num;
   601 }
   602 
   603 int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
   604 {
   605 	uint32_t i, j, max;
   606 
   607 	max = ctx->plugin->info.natives_num;
   608 
   609 	for (i=0; i<max; i++)
   610 	{
   611 		if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
   612 		{
   613 			continue;
   614 		}
   615 
   616 		for (j=0; (natives[j].name) && (!num || j<num); j++)
   617 		{
   618 			if (!strcmp(ctx->natives[i].name, natives[j].name))
   619 			{
   620 				ctx->natives[i].pfn = natives[j].func;
   621 				ctx->natives[i].status = SP_NATIVE_BOUND;
   622 			}
   623 		}
   624 	}
   625 
   626 	return SP_ERROR_NONE;
   627 }
   628 
   629 int BaseContext::BindNative(const sp_nativeinfo_t *native)
   630 {
   631 	uint32_t index;
   632 	int err;
   633 
   634 	if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
   635 	{
   636 		return err;
   637 	}
   638 
   639 	ctx->natives[index].pfn = native->func;
   640 	ctx->natives[index].status = SP_NATIVE_BOUND;
   641 
   642 	return SP_ERROR_NONE;
   643 }
   644 
   645 int BaseContext::BindNativeToIndex(uint32_t index, SPVM_NATIVE_FUNC native)
   646 {
   647 	int err;
   648 	sp_native_t *native;
   649 
   650 	if ((err = GetNativeByIndex(index, &native)) != SP_ERROR_NONE)
   651 	{
   652 		return err;
   653 	}
   654 
   655 	ctx->natives[index].pfn = native;
   656 	ctx->natives[index].status = SP_NATIVE_BOUND;
   657 
   658 	return SP_ERROR_NONE;
   659 }
   660 
   661 int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
   662 {
   663 	uint32_t nativesnum, i;
   664 
   665 	nativesnum = ctx->plugin->info.natives_num;
   666 
   667 	for (i=0; i<nativesnum; i++)
   668 	{
   669 		if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
   670 		{
   671 			ctx->natives[i].pfn = native;
   672 			ctx->natives[i].status = SP_NATIVE_BOUND;
   673 		}
   674 	}
   675 
   676 	return SP_ERROR_NONE;
   677 }
   678 
   679 int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
   680 {
   681 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   682 	{
   683 		return SP_ERROR_INVALID_ADDRESS;
   684 	}
   685 
   686 	if (phys_addr)
   687 	{
   688 		*phys_addr = (cell_t *)(ctx->memory + local_addr);
   689 	}
   690 
   691 	return SP_ERROR_NONE;
   692 }
   693 
   694 int BaseContext::PushCell(cell_t value)
   695 {
   696 	if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
   697 	{
   698 		return SP_ERROR_STACKLOW;
   699 	}
   700 
   701 	ctx->sp -= sizeof(cell_t);
   702 	*(cell_t *)(ctx->memory + ctx->sp) = value;
   703 	ctx->pushcount++;
   704 
   705 	return SP_ERROR_NONE;
   706 }
   707 
   708 int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
   709 {
   710 	unsigned int i;
   711 	int err;
   712 
   713 	for (i=0; i<numcells; i++)
   714 	{
   715 		if ((err = PushCell(array[i])) != SP_ERROR_NONE)
   716 		{
   717 			ctx->sp += (cell_t)(i * sizeof(cell_t));
   718 			ctx->pushcount -= i;
   719 			return err;
   720 		}
   721 	}
   722 
   723 	return SP_ERROR_NONE;
   724 }
   725 
   726 int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
   727 {
   728 	cell_t *ph_addr;
   729 	int err;
   730 
   731 	if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
   732 	{
   733 		return err;
   734 	}
   735 
   736 	memcpy(ph_addr, array, numcells * sizeof(cell_t));
   737 
   738 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   739 	{
   740 		HeapRelease(*local_addr);
   741 		return err;
   742 	}
   743 
   744 	if (phys_addr)
   745 	{
   746 		*phys_addr = ph_addr;
   747 	}
   748 
   749 	return SP_ERROR_NONE;
   750 }
   751 
   752 int BaseContext::LocalToString(cell_t local_addr, char **addr)
   753 {
   754 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   755 	{
   756 		return SP_ERROR_INVALID_ADDRESS;
   757 	}
   758 	*addr = (char *)(ctx->memory + local_addr);
   759 
   760 	return SP_ERROR_NONE;
   761 }
   762 
   763 int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
   764 {
   765 	char *ph_addr;
   766 	int err;
   767 	unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
   768 
   769 	if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
   770 	{
   771 		return err;
   772 	}
   773 
   774 	memcpy(ph_addr, string, len);
   775 	ph_addr[len] = '\0';
   776 
   777 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   778 	{
   779 		HeapRelease(*local_addr);
   780 		return err;
   781 	}
   782 
   783 	if (phys_addr)
   784 	{
   785 		*phys_addr = ph_addr;
   786 	}
   787 
   788 	return SP_ERROR_NONE;
   789 }
   790 
   791 int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
   792 {
   793 	char *dest;
   794 	size_t len;
   795 
   796 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   797 	{
   798 		return SP_ERROR_INVALID_ADDRESS;
   799 	}
   800 
   801 	if (bytes == 0)
   802 	{
   803 		return SP_ERROR_NONE;
   804 	}
   805 
   806 	len = strlen(source);
   807 	dest = (char *)(ctx->memory + local_addr);
   808 
   809 	if (len >= bytes)
   810 	{
   811 		len = bytes - 1;
   812 	}
   813 
   814 	memcpy(dest, source, len);
   815 	dest[len] = '\0';
   816 
   817 	return SP_ERROR_NONE;
   818 }
   819 
   820 inline int __CheckValidChar(char *c)
   821 {
   822 	int count;
   823 	int bytecount = 0;
   824 
   825 	for (count=1; (*c & 0xC0) == 0x80; count++)
   826 	{
   827 		c--;
   828 	}
   829 
   830 	switch (*c & 0xF0)
   831 	{
   832 	case 0xC0:
   833 	case 0xD0:
   834 		{
   835 			bytecount = 2;
   836 			break;
   837 		}
   838 	case 0xE0:
   839 		{
   840 			bytecount = 3;
   841 			break;
   842 		}
   843 	case 0xF0:
   844 		{
   845 			bytecount = 4;
   846 			break;
   847 		}
   848 	}
   849 
   850 	if (bytecount != count)
   851 	{
   852 		return count;
   853 	}
   854 
   855 	return 0;
   856 }
   857 
   858 int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
   859 {
   860 	char *dest;
   861 	size_t len;
   862 	bool needtocheck = false;
   863 
   864 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   865 	{
   866 		return SP_ERROR_INVALID_ADDRESS;
   867 	}
   868 	
   869 	if (maxbytes == 0)
   870 	{
   871 		return SP_ERROR_NONE;
   872 	}
   873 
   874 	len = strlen(source);
   875 	dest = (char *)(ctx->memory + local_addr);
   876 
   877 	if ((size_t)len >= maxbytes)
   878 	{
   879 		len = maxbytes - 1;
   880 		needtocheck = true;
   881 	}
   882 
   883 	memcpy(dest, source, len);
   884 	if ((dest[len-1] & 1<<7) && needtocheck)
   885 	{
   886 		len -= __CheckValidChar(dest+len-1);
   887 	}
   888 	dest[len] = '\0';
   889 
   890 	if (wrtnbytes)
   891 	{
   892 		*wrtnbytes = len;
   893 	}
   894 
   895 	return SP_ERROR_NONE;
   896 }
   897 
   898 #define USHR(x) ((unsigned int)(x)>>1)
   899 
   900 int BaseContext::LookupFile(ucell_t addr, const char **filename)
   901 {
   902 	int high, low, mid;
   903 
   904 	high = ctx->plugin->debug.files_num;
   905 	low = -1;
   906 
   907 	while (high - low > 1)
   908 	{
   909 		mid = USHR(low + high);
   910 		if (ctx->files[mid].addr <= addr)
   911 		{
   912 			low = mid;
   913 		} else {
   914 			high = mid;
   915 		}
   916 	}
   917 
   918 	if (low == -1)
   919 	{
   920 		return SP_ERROR_NOT_FOUND;
   921 	}
   922 
   923 	*filename = ctx->files[low].name;
   924 
   925 	return SP_ERROR_NONE;
   926 }
   927 
   928 int BaseContext::LookupFunction(ucell_t addr, const char **name)
   929 {
   930 	uint32_t iter, max = ctx->plugin->debug.syms_num;
   931 
   932 	for (iter=0; iter<max; iter++)
   933 	{
   934 		if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) 
   935 			&& (ctx->symbols[iter].codestart <= addr) 
   936 			&& (ctx->symbols[iter].codeend > addr))
   937 		{
   938 			break;
   939 		}
   940 	}
   941 
   942 	if (iter >= max)
   943 	{
   944 		return SP_ERROR_NOT_FOUND;
   945 	}
   946 
   947 	*name = ctx->symbols[iter].name;
   948 
   949 	return SP_ERROR_NONE;
   950 }
   951 
   952 int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
   953 {
   954 	int high, low, mid;
   955 
   956 	high = ctx->plugin->debug.lines_num;
   957 	low = -1;
   958 
   959 	while (high - low > 1)
   960 	{
   961 		mid = USHR(low + high);
   962 		if (ctx->lines[mid].addr <= addr)
   963 		{
   964 			low = mid;
   965 		} else {
   966 			high = mid;
   967 		}
   968 	}
   969 
   970 	if (low == -1)
   971 	{
   972 		return SP_ERROR_NOT_FOUND;
   973 	}
   974 
   975 	/* Since the CIP occurs BEFORE the line, we have to add one */
   976 	*line = ctx->lines[low].line + 1;
   977 
   978 	return SP_ERROR_NONE;
   979 }
   980 
   981 IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
   982 {
   983 	CFunction *pFunc = NULL;
   984 
   985 	if (func_id & 1)
   986 	{
   987 		func_id >>= 1;
   988 		if (func_id >= ctx->plugin->info.publics_num)
   989 		{
   990 			return NULL;
   991 		}
   992 		pFunc = m_pub_funcs[func_id];
   993 		if (!pFunc)
   994 		{
   995 			m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid);
   996 			pFunc = m_pub_funcs[func_id];
   997 		} else if (pFunc->IsInvalidated()) {
   998 			pFunc->Set(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid);
   999 		}
  1000 	} else {
  1001 		/* :TODO: currently not used */
  1002 #if 0
  1003 		func_id >>= 1;
  1004 		unsigned int index;
  1005 		if (!g_pVM->FunctionLookup(ctx, func_id, &index))
  1006 		{
  1007 			return NULL;
  1008 		}
  1009 		pFunc = m_priv_funcs[func_id];
  1010 		if (!pFunc)
  1011 		{
  1012 			m_priv_funcs[func_id] = new CFunction(save, this);
  1013 			pFunc = m_priv_funcs[func_id];
  1014 		}
  1015 #endif
  1016 	}
  1017 
  1018 	return pFunc;
  1019 }
  1020 
  1021 IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
  1022 {
  1023 	uint32_t index;
  1024 
  1025 	if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
  1026 	{
  1027 		return NULL;
  1028 	}
  1029 
  1030 	CFunction *pFunc = m_pub_funcs[index];
  1031 	if (!pFunc)
  1032 	{
  1033 		sp_public_t *pub = NULL;
  1034 		GetPublicByIndex(index, &pub);
  1035 		if (pub)
  1036 		{
  1037 			m_pub_funcs[index] = new CFunction(pub->code_offs, this, pub->funcid);
  1038 		}
  1039 		pFunc = m_pub_funcs[index];
  1040 	} else if (pFunc->IsInvalidated()) {
  1041 		sp_public_t *pub = NULL;
  1042 		GetPublicByIndex(index, &pub);
  1043 		if (pub)
  1044 		{
  1045 			pFunc->Set(pub->code_offs, this, pub->funcid);
  1046 		} else {
  1047 			pFunc = NULL;
  1048 		}
  1049 	}
  1050 
  1051 	return pFunc;
  1052 }
  1053 
  1054 int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
  1055 {
  1056 	int err;
  1057 	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
  1058 	{
  1059 		return err;
  1060 	}
  1061 
  1062 	if ((cell_t *)*addr == m_pNullString)
  1063 	{
  1064 		*addr = NULL;
  1065 	}
  1066 
  1067 	return SP_ERROR_NONE;
  1068 }
  1069 
  1070 #if defined SOURCEMOD_BUILD
  1071 SourceMod::IdentityToken_t *BaseContext::GetIdentity()
  1072 {
  1073 	return m_pToken;
  1074 }
  1075 
  1076 void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
  1077 {
  1078 	m_pToken = token;
  1079 }
  1080 
  1081 cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
  1082 {
  1083 	if (type == SP_NULL_VECTOR)
  1084 	{
  1085 		return m_pNullVec;
  1086 	}
  1087 
  1088 	return NULL;
  1089 }
  1090 #endif