core/vm/sp_vm_basecontext.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1367 0253af5c3bee
child 1382 5399d6f24417
permissions -rw-r--r--
fixed amb834 - command callbacks could potentially occur on invalid clients in very rare cases
     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::BindNativeToAny(SPVM_NATIVE_FUNC native)
   646 {
   647 	uint32_t nativesnum, i;
   648 
   649 	nativesnum = ctx->plugin->info.natives_num;
   650 
   651 	for (i=0; i<nativesnum; i++)
   652 	{
   653 		if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
   654 		{
   655 			ctx->natives[i].pfn = native;
   656 			ctx->natives[i].status = SP_NATIVE_BOUND;
   657 		}
   658 	}
   659 
   660 	return SP_ERROR_NONE;
   661 }
   662 
   663 int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
   664 {
   665 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   666 	{
   667 		return SP_ERROR_INVALID_ADDRESS;
   668 	}
   669 
   670 	if (phys_addr)
   671 	{
   672 		*phys_addr = (cell_t *)(ctx->memory + local_addr);
   673 	}
   674 
   675 	return SP_ERROR_NONE;
   676 }
   677 
   678 int BaseContext::PushCell(cell_t value)
   679 {
   680 	if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
   681 	{
   682 		return SP_ERROR_STACKLOW;
   683 	}
   684 
   685 	ctx->sp -= sizeof(cell_t);
   686 	*(cell_t *)(ctx->memory + ctx->sp) = value;
   687 	ctx->pushcount++;
   688 
   689 	return SP_ERROR_NONE;
   690 }
   691 
   692 int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
   693 {
   694 	unsigned int i;
   695 	int err;
   696 
   697 	for (i=0; i<numcells; i++)
   698 	{
   699 		if ((err = PushCell(array[i])) != SP_ERROR_NONE)
   700 		{
   701 			ctx->sp += (cell_t)(i * sizeof(cell_t));
   702 			ctx->pushcount -= i;
   703 			return err;
   704 		}
   705 	}
   706 
   707 	return SP_ERROR_NONE;
   708 }
   709 
   710 int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
   711 {
   712 	cell_t *ph_addr;
   713 	int err;
   714 
   715 	if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
   716 	{
   717 		return err;
   718 	}
   719 
   720 	memcpy(ph_addr, array, numcells * sizeof(cell_t));
   721 
   722 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   723 	{
   724 		HeapRelease(*local_addr);
   725 		return err;
   726 	}
   727 
   728 	if (phys_addr)
   729 	{
   730 		*phys_addr = ph_addr;
   731 	}
   732 
   733 	return SP_ERROR_NONE;
   734 }
   735 
   736 int BaseContext::LocalToString(cell_t local_addr, char **addr)
   737 {
   738 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   739 	{
   740 		return SP_ERROR_INVALID_ADDRESS;
   741 	}
   742 	*addr = (char *)(ctx->memory + local_addr);
   743 
   744 	return SP_ERROR_NONE;
   745 }
   746 
   747 int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
   748 {
   749 	char *ph_addr;
   750 	int err;
   751 	unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
   752 
   753 	if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
   754 	{
   755 		return err;
   756 	}
   757 
   758 	memcpy(ph_addr, string, len);
   759 	ph_addr[len] = '\0';
   760 
   761 	if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
   762 	{
   763 		HeapRelease(*local_addr);
   764 		return err;
   765 	}
   766 
   767 	if (phys_addr)
   768 	{
   769 		*phys_addr = ph_addr;
   770 	}
   771 
   772 	return SP_ERROR_NONE;
   773 }
   774 
   775 int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
   776 {
   777 	char *dest;
   778 	size_t len;
   779 
   780 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   781 	{
   782 		return SP_ERROR_INVALID_ADDRESS;
   783 	}
   784 
   785 	if (bytes == 0)
   786 	{
   787 		return SP_ERROR_NONE;
   788 	}
   789 
   790 	len = strlen(source);
   791 	dest = (char *)(ctx->memory + local_addr);
   792 
   793 	if (len >= bytes)
   794 	{
   795 		len = bytes - 1;
   796 	}
   797 
   798 	memcpy(dest, source, len);
   799 	dest[len] = '\0';
   800 
   801 	return SP_ERROR_NONE;
   802 }
   803 
   804 inline int __CheckValidChar(char *c)
   805 {
   806 	int count;
   807 	int bytecount = 0;
   808 
   809 	for (count=1; (*c & 0xC0) == 0x80; count++)
   810 	{
   811 		c--;
   812 	}
   813 
   814 	switch (*c & 0xF0)
   815 	{
   816 	case 0xC0:
   817 	case 0xD0:
   818 		{
   819 			bytecount = 2;
   820 			break;
   821 		}
   822 	case 0xE0:
   823 		{
   824 			bytecount = 3;
   825 			break;
   826 		}
   827 	case 0xF0:
   828 		{
   829 			bytecount = 4;
   830 			break;
   831 		}
   832 	}
   833 
   834 	if (bytecount != count)
   835 	{
   836 		return count;
   837 	}
   838 
   839 	return 0;
   840 }
   841 
   842 int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
   843 {
   844 	char *dest;
   845 	size_t len;
   846 	bool needtocheck = false;
   847 
   848 	if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
   849 	{
   850 		return SP_ERROR_INVALID_ADDRESS;
   851 	}
   852 	
   853 	if (maxbytes == 0)
   854 	{
   855 		return SP_ERROR_NONE;
   856 	}
   857 
   858 	len = strlen(source);
   859 	dest = (char *)(ctx->memory + local_addr);
   860 
   861 	if ((size_t)len >= maxbytes)
   862 	{
   863 		len = maxbytes - 1;
   864 		needtocheck = true;
   865 	}
   866 
   867 	memcpy(dest, source, len);
   868 	if ((dest[len-1] & 1<<7) && needtocheck)
   869 	{
   870 		len -= __CheckValidChar(dest+len-1);
   871 	}
   872 	dest[len] = '\0';
   873 
   874 	if (wrtnbytes)
   875 	{
   876 		*wrtnbytes = len;
   877 	}
   878 
   879 	return SP_ERROR_NONE;
   880 }
   881 
   882 #define USHR(x) ((unsigned int)(x)>>1)
   883 
   884 int BaseContext::LookupFile(ucell_t addr, const char **filename)
   885 {
   886 	int high, low, mid;
   887 
   888 	high = ctx->plugin->debug.files_num;
   889 	low = -1;
   890 
   891 	while (high - low > 1)
   892 	{
   893 		mid = USHR(low + high);
   894 		if (ctx->files[mid].addr <= addr)
   895 		{
   896 			low = mid;
   897 		} else {
   898 			high = mid;
   899 		}
   900 	}
   901 
   902 	if (low == -1)
   903 	{
   904 		return SP_ERROR_NOT_FOUND;
   905 	}
   906 
   907 	*filename = ctx->files[low].name;
   908 
   909 	return SP_ERROR_NONE;
   910 }
   911 
   912 int BaseContext::LookupFunction(ucell_t addr, const char **name)
   913 {
   914 	uint32_t iter, max = ctx->plugin->debug.syms_num;
   915 
   916 	for (iter=0; iter<max; iter++)
   917 	{
   918 		if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION) 
   919 			&& (ctx->symbols[iter].codestart <= addr) 
   920 			&& (ctx->symbols[iter].codeend > addr))
   921 		{
   922 			break;
   923 		}
   924 	}
   925 
   926 	if (iter >= max)
   927 	{
   928 		return SP_ERROR_NOT_FOUND;
   929 	}
   930 
   931 	*name = ctx->symbols[iter].name;
   932 
   933 	return SP_ERROR_NONE;
   934 }
   935 
   936 int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
   937 {
   938 	int high, low, mid;
   939 
   940 	high = ctx->plugin->debug.lines_num;
   941 	low = -1;
   942 
   943 	while (high - low > 1)
   944 	{
   945 		mid = USHR(low + high);
   946 		if (ctx->lines[mid].addr <= addr)
   947 		{
   948 			low = mid;
   949 		} else {
   950 			high = mid;
   951 		}
   952 	}
   953 
   954 	if (low == -1)
   955 	{
   956 		return SP_ERROR_NOT_FOUND;
   957 	}
   958 
   959 	/* Since the CIP occurs BEFORE the line, we have to add one */
   960 	*line = ctx->lines[low].line + 1;
   961 
   962 	return SP_ERROR_NONE;
   963 }
   964 
   965 IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
   966 {
   967 	CFunction *pFunc = NULL;
   968 
   969 	if (func_id & 1)
   970 	{
   971 		func_id >>= 1;
   972 		if (func_id >= ctx->plugin->info.publics_num)
   973 		{
   974 			return NULL;
   975 		}
   976 		pFunc = m_pub_funcs[func_id];
   977 		if (!pFunc)
   978 		{
   979 			m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid);
   980 			pFunc = m_pub_funcs[func_id];
   981 		} else if (pFunc->IsInvalidated()) {
   982 			pFunc->Set(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid);
   983 		}
   984 	} else {
   985 		/* :TODO: currently not used */
   986 #if 0
   987 		func_id >>= 1;
   988 		unsigned int index;
   989 		if (!g_pVM->FunctionLookup(ctx, func_id, &index))
   990 		{
   991 			return NULL;
   992 		}
   993 		pFunc = m_priv_funcs[func_id];
   994 		if (!pFunc)
   995 		{
   996 			m_priv_funcs[func_id] = new CFunction(save, this);
   997 			pFunc = m_priv_funcs[func_id];
   998 		}
   999 #endif
  1000 	}
  1001 
  1002 	return pFunc;
  1003 }
  1004 
  1005 IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
  1006 {
  1007 	uint32_t index;
  1008 
  1009 	if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
  1010 	{
  1011 		return NULL;
  1012 	}
  1013 
  1014 	CFunction *pFunc = m_pub_funcs[index];
  1015 	if (!pFunc)
  1016 	{
  1017 		sp_public_t *pub = NULL;
  1018 		GetPublicByIndex(index, &pub);
  1019 		if (pub)
  1020 		{
  1021 			m_pub_funcs[index] = new CFunction(pub->code_offs, this, pub->funcid);
  1022 		}
  1023 		pFunc = m_pub_funcs[index];
  1024 	} else if (pFunc->IsInvalidated()) {
  1025 		sp_public_t *pub = NULL;
  1026 		GetPublicByIndex(index, &pub);
  1027 		if (pub)
  1028 		{
  1029 			pFunc->Set(pub->code_offs, this, pub->funcid);
  1030 		} else {
  1031 			pFunc = NULL;
  1032 		}
  1033 	}
  1034 
  1035 	return pFunc;
  1036 }
  1037 
  1038 int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
  1039 {
  1040 	int err;
  1041 	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
  1042 	{
  1043 		return err;
  1044 	}
  1045 
  1046 	if ((cell_t *)*addr == m_pNullString)
  1047 	{
  1048 		*addr = NULL;
  1049 	}
  1050 
  1051 	return SP_ERROR_NONE;
  1052 }
  1053 
  1054 #if defined SOURCEMOD_BUILD
  1055 SourceMod::IdentityToken_t *BaseContext::GetIdentity()
  1056 {
  1057 	return m_pToken;
  1058 }
  1059 
  1060 void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)
  1061 {
  1062 	m_pToken = token;
  1063 }
  1064 
  1065 cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
  1066 {
  1067 	if (type == SP_NULL_VECTOR)
  1068 	{
  1069 		return m_pNullVec;
  1070 	}
  1071 
  1072 	return NULL;
  1073 }
  1074 #endif