core/sm_stringutil.cpp
author Phong Dang <teame06@gmail.com>
Wed Aug 01 07:29:40 2007 +0000 (2007-08-01)
changeset 1241 2e8046793bc9
parent 1231 17badaf66ce4
child 1330 ab94316bb52b
permissions -rw-r--r--
Added check to make sure player are alive to perform sm_slap, sm_burn, sm_slay.
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or 
     9  * modify it under the terms of the GNU General Public License, 
    10  * version 3.0, as published by the Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful,
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  * GNU General Public License for more details.
    16  *
    17  * You should have received a copy of the GNU General Public License
    18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to 
    21  * link the code of this program (as well as its derivative works) to 
    22  * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
    23  * Game MODs that run on software by the Valve Corporation.  You must 
    24  * obey the GNU General Public License in all respects for all other 
    25  * code used. Additionally, AlliedModders LLC grants this exception 
    26  * to all derivative works. AlliedModders LLC defines further 
    27  * exceptions, found in LICENSE.txt (as of this writing, version 
    28  * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include <stdlib.h>
    34 #include <ctype.h>
    35 #include <stdarg.h>
    36 #include "sm_stringutil.h"
    37 #include "Logger.h"
    38 #include "PluginSys.h"
    39 #include "Translator.h"
    40 #include "PlayerManager.h"
    41 
    42 #define LADJUST			0x00000004		/* left adjustment */
    43 #define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
    44 #define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
    45 #define to_digit(c)		((c) - '0')
    46 #define is_digit(c)		((unsigned)to_digit(c) <= 9)
    47 
    48 #define CHECK_ARGS(x) \
    49 	if ((arg+x) > args) { \
    50 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
    51 		return 0; \
    52 	}
    53 
    54 size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
    55 {
    56 	Translation pTrans;
    57 	TransError err;
    58 
    59 	err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
    60 	if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
    61 	{
    62 		err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
    63 	}
    64 
    65 	if (err != Trans_Okay)
    66 	{
    67 		return UTIL_Format(buffer, maxlength, "%s", phrase);
    68 	}
    69 
    70 	return g_Translator.Translate(buffer, maxlength, params, &pTrans);
    71 }
    72 
    73 inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
    74 {
    75 	TransError err = Trans_BadLanguage;
    76 	CPhraseFile *phrfl;
    77 
    78 	for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
    79 	{
    80 		phrfl = g_Translator.GetFileByIndex(pl->GetLangFileByIndex(i));
    81 		err = phrfl->GetTranslation(key, langid, pTrans);
    82 	}
    83 
    84 	return (err == Trans_Okay) ? true : false;
    85 }
    86 
    87 size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error)
    88 {
    89 	unsigned int langid;
    90 	*error = false;
    91 	Translation pTrans;
    92 	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
    93 	size_t langcount = pl->GetLangFileCount();
    94 	void *new_params[MAX_TRANSLATE_PARAMS];
    95 	unsigned int max_params = 0;
    96 
    97 try_serverlang:
    98 	if (target == LANG_SERVER)
    99 	{
   100 		langid = g_Translator.GetServerLanguage();
   101  	} else if ((target >= 1) && (target <= g_Players.GetMaxClients())) {
   102 		langid = g_Translator.GetClientLanguage(target);
   103 	} else {
   104 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
   105 		goto error_out;
   106 	}
   107 
   108 	if (!TryTranslation(pl, key, langid, langcount, &pTrans))
   109 	{
   110 		if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
   111 		{
   112 			target = LANG_SERVER;
   113 			goto try_serverlang;
   114 		} else if (langid != LANGUAGE_ENGLISH) {
   115 			if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
   116 			{
   117 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   118 				goto error_out;
   119 			}
   120 		} else {
   121 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   122 			goto error_out;
   123 		}
   124 	}
   125 
   126 	max_params = pTrans.fmt_count;
   127 	
   128 	if (max_params)
   129 	{
   130 		/* Check if we're going to over the limit */
   131 		if ((*arg) + (max_params - 1) > (size_t)params[0])
   132 		{
   133 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
   134 				"Translation string formatted incorrectly - missing at least %d parameters", 
   135 				((*arg + (max_params - 1)) - params[0])
   136 				);
   137 			goto error_out;
   138 		}
   139 
   140 		/* Translate the parameters to raw pointers */
   141 		for (size_t i=0; i<max_params; i++)
   142 		{
   143 			pCtx->LocalToPhysAddr(params[*arg], reinterpret_cast<cell_t **>(&new_params[i]));
   144 			(*arg)++;
   145 		}
   146 	}
   147 
   148 	return g_Translator.Translate(buffer, maxlen, new_params, &pTrans);
   149 error_out:
   150 	*error = true;
   151 	return 0;
   152 }
   153 
   154 //:TODO: review this code before we choose a license
   155 
   156 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
   157 {
   158 	int size = 0;
   159 	char *buf;
   160 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
   161 
   162 	buf = *buf_p;
   163 
   164 	if (string == NULL)
   165 	{
   166 		string = nlstr;
   167 		prec = -1;
   168 	}
   169 
   170 	if (prec >= 0)
   171 	{
   172 		for (size = 0; size < prec; size++) 
   173 		{
   174 			if (string[size] == '\0')
   175 			{
   176 				break;
   177 			}
   178 		}
   179 	} else {
   180 		while (string[size++]);
   181 		size--;
   182 	}
   183 
   184 	if (size > (int)maxlen)
   185 	{
   186 		size = maxlen;
   187 	}
   188 
   189 	maxlen -= size;
   190 	width -= size;
   191 
   192 	while (size--)
   193 	{
   194 		*buf++ = *string++;
   195 	}
   196 
   197 	while ((width-- > 0) && maxlen)
   198 	{
   199 		*buf++ = ' ';
   200 		maxlen--;
   201 	}
   202 
   203 	*buf_p = buf;
   204 }
   205 
   206 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec)
   207 {
   208 	char text[32];
   209 	int digits;
   210 	double signedVal;
   211 	char *buf;
   212 	int val;
   213 
   214 	// get the sign
   215 	signedVal = fval;
   216 	if (fval < 0)
   217 	{
   218 		fval = -fval;
   219 	}
   220 
   221 	// write the float number
   222 	digits = 0;
   223 	val = (int)fval;
   224 	do
   225 	{
   226 		text[digits++] = '0' + val % 10;
   227 		val /= 10;
   228 	} while (val);
   229 
   230 	if (signedVal < 0)
   231 	{
   232 		text[digits++] = '-';
   233 	}
   234 
   235 	buf = *buf_p;
   236 
   237 	while ((digits < width) && maxlen)
   238 	{
   239 		*buf++ = ' ';
   240 		width--;
   241 		maxlen--;
   242 	}
   243 
   244 	while ((digits--) && maxlen)
   245 	{
   246 		*buf++ = text[digits];
   247 		maxlen--;
   248 	}
   249 
   250 	*buf_p = buf;
   251 
   252 	if (prec < 0)
   253 	{
   254 		prec = 6;
   255 	}
   256 	// write the fraction
   257 	digits = 0;
   258 	while (digits < prec)
   259 	{
   260 		fval -= (int)fval;
   261 		fval *= 10.0;
   262 		val = (int)fval;
   263 		text[digits++] = '0' + val % 10;
   264 	}
   265 
   266 	if ((digits > 0) && maxlen)
   267 	{
   268 		buf = *buf_p;
   269 		*buf++ = '.';
   270 		maxlen--;
   271 		for (prec = 0; maxlen && (prec < digits); prec++)
   272 		{
   273 			*buf++ = text[prec];
   274 			maxlen--;
   275 		}
   276 		*buf_p = buf;
   277 	}
   278 }
   279 
   280 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   281 {
   282 	char text[32];
   283 	int digits;
   284 	char *buf;
   285 
   286 	digits = 0;
   287 	do
   288 	{
   289 		text[digits++] = '0' + val % 10;
   290 		val /= 10;
   291 	} while (val);
   292 
   293 	buf = *buf_p;
   294 
   295 	if (!(flags & LADJUST))
   296 	{
   297 		while (digits < width && maxlen)
   298 		{
   299 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   300 			width--;
   301 			maxlen--;
   302 		}
   303 	}
   304 
   305 	while (digits-- && maxlen)
   306 	{
   307 		*buf++ = text[digits];
   308 		width--;
   309 		maxlen--;
   310 	}
   311 
   312 	if (flags & LADJUST)
   313 	{
   314 		while (width-- && maxlen)
   315 		{
   316 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   317 			maxlen--;
   318 		}
   319 	}
   320 
   321 	*buf_p = buf;
   322 }
   323 
   324 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   325 {
   326 	char text[32];
   327 	int digits;
   328 	int signedVal;
   329 	char *buf;
   330 	unsigned int unsignedVal;
   331 
   332 	digits = 0;
   333 	signedVal = val;
   334 	if (val < 0)
   335 	{
   336 		/* we want the unsigned version */
   337 		unsignedVal = abs(val);
   338 	} else {
   339 		unsignedVal = val;
   340 	}
   341 	do
   342 	{
   343 		text[digits++] = '0' + unsignedVal % 10;
   344 		unsignedVal /= 10;
   345 	} while (unsignedVal);
   346 
   347 	if (signedVal < 0)
   348 	{
   349 		text[digits++] = '-';
   350 	}
   351 
   352 	buf = *buf_p;
   353 
   354 	if (!(flags & LADJUST))
   355 	{
   356 		while ((digits < width) && maxlen)
   357 		{
   358 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   359 			width--;
   360 			maxlen--;
   361 		}
   362 	}
   363 
   364 	while (digits-- && maxlen)
   365 	{
   366 		*buf++ = text[digits];
   367 		width--;
   368 		maxlen--;
   369 	}
   370 
   371 	if (flags & LADJUST)
   372 	{
   373 		while (width-- && maxlen)
   374 		{
   375 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   376 			maxlen--;
   377 		}
   378 	}
   379 
   380 	*buf_p = buf;
   381 }
   382 
   383 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   384 {
   385 	char text[32];
   386 	int digits;
   387 	char *buf;
   388 	char digit;
   389 	int hexadjust;
   390 
   391 	if (flags & UPPERDIGITS)
   392 	{
   393 		hexadjust = 'A' - '9' - 1;
   394 	} else {
   395 		hexadjust = 'a' - '9' - 1;
   396 	}
   397 
   398 	digits = 0;
   399 	do 
   400 	{
   401 		digit = ('0' + val % 16);
   402 		if (digit > '9')
   403 		{
   404 			digit += hexadjust;
   405 		}
   406 
   407 		text[digits++] = digit;
   408 		val /= 16;
   409 	} while(val);
   410 
   411 	buf = *buf_p;
   412 
   413 	if (!(flags & LADJUST))
   414 	{
   415 		while (digits < width && maxlen)
   416 		{
   417 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   418 			width--;
   419 			maxlen--;
   420 		}
   421 	}
   422 
   423 	while (digits-- && maxlen)
   424 	{
   425 		*buf++ = text[digits];
   426 		width--;
   427 		maxlen--;
   428 	}
   429 
   430 	if (flags & LADJUST)
   431 	{
   432 		while (width-- && maxlen)
   433 		{
   434 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   435 			maxlen--;
   436 		}
   437 	}
   438 
   439 	*buf_p = buf;
   440 }
   441 
   442 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
   443 {
   444 	if (!buffer || !maxlen)
   445 	{
   446 		return 0;
   447 	}
   448 
   449 	int arg = 0;
   450 	char *buf_p;
   451 	char ch;
   452 	int flags;
   453 	int width;
   454 	int prec;
   455 	int n;
   456 	char sign;
   457 	const char *fmt;
   458 	size_t llen = maxlen - 1;
   459 
   460 	buf_p = buffer;
   461 	fmt = format;
   462 
   463 	while (true)
   464 	{
   465 		// run through the format string until we hit a '%' or '\0'
   466 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   467 		{
   468 			*buf_p++ = ch;
   469 			llen--;
   470 		}
   471 		if ((ch == '\0') || (llen <= 0))
   472 		{
   473 			goto done;
   474 		}
   475 
   476 		// skip over the '%'
   477 		fmt++;
   478 
   479 		// reset formatting state
   480 		flags = 0;
   481 		width = 0;
   482 		prec = -1;
   483 		sign = '\0';
   484 
   485 rflag:
   486 		ch = *fmt++;
   487 reswitch:
   488 		switch(ch)
   489 		{
   490 		case '-':
   491 			{
   492 				flags |= LADJUST;
   493 				goto rflag;
   494 			}
   495 		case '.':
   496 			{
   497 				n = 0;
   498 				while(is_digit((ch = *fmt++)))
   499 				{
   500 					n = 10 * n + (ch - '0');
   501 				}
   502 				prec = (n < 0) ? -1 : n;
   503 				goto reswitch;
   504 			}
   505 		case '0':
   506 			{
   507 				flags |= ZEROPAD;
   508 				goto rflag;
   509 			}
   510 		case '1':
   511 		case '2':
   512 		case '3':
   513 		case '4':
   514 		case '5':
   515 		case '6':
   516 		case '7':
   517 		case '8':
   518 		case '9':
   519 			{
   520 				n = 0;
   521 				do
   522 				{
   523 					n = 10 * n + (ch - '0');
   524 					ch = *fmt++;
   525 				} while(is_digit(ch));
   526 				width = n;
   527 				goto reswitch;
   528 			}
   529 		case 'c':
   530 			{
   531 				if (!llen)
   532 				{
   533 					goto done;
   534 				}
   535 				char *c = (char *)args[arg];
   536 				*buf_p++ = *c;
   537 				llen--;
   538 				arg++;
   539 				break;
   540 			}
   541 		case 'd':
   542 		case 'i':
   543 			{
   544 				int *value = (int *)args[arg];
   545 				AddInt(&buf_p, llen, *value, width, flags);
   546 				arg++;
   547 				break;
   548 			}
   549 		case 'u':
   550 			{
   551 				unsigned int *value = (unsigned int *)args[arg];
   552 				AddUInt(&buf_p, llen, *value, width, flags);
   553 				arg++;
   554 				break;
   555 			}
   556 		case 'f':
   557 			{
   558 				float *value = (float *)args[arg];
   559 				AddFloat(&buf_p, llen, *value, width, prec);
   560 				arg++;
   561 				break;
   562 			}
   563 		case 's':
   564 			{
   565 				const char *str = (const char *)args[arg];
   566 				AddString(&buf_p, llen, str, width, prec);
   567 				arg++;
   568 				break;
   569 			}
   570 		case 'X':
   571 			{
   572 				unsigned int *value = (unsigned int *)args[arg];
   573 				flags |= UPPERDIGITS;
   574 				AddHex(&buf_p, llen, *value, width, flags);
   575 				arg++;
   576 				break;
   577 			}
   578 		case 'x':
   579 			{
   580 				unsigned int *value = (unsigned int *)args[arg];
   581 				AddHex(&buf_p, llen, *value, width, flags);
   582 				arg++;
   583 				break;
   584 			}
   585 		case '%':
   586 			{
   587 				if (!llen)
   588 				{
   589 					goto done;
   590 				}
   591 				*buf_p++ = ch;
   592 				llen--;
   593 				break;
   594 			}
   595 		case '\0':
   596 			{
   597 				if (!llen)
   598 				{
   599 					goto done;
   600 				}
   601 				*buf_p++ = '%';
   602 				llen--;
   603 				goto done;
   604 			}
   605 		default:
   606 			{
   607 				if (!llen)
   608 				{
   609 					goto done;
   610 				}
   611 				*buf_p++ = ch;
   612 				llen--;
   613 				break;
   614 			}
   615 		}
   616 	}
   617 
   618 done:
   619 	*buf_p = '\0';
   620 
   621 	return (maxlen - llen - 1);
   622 }
   623 
   624 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   625 {
   626 	if (!buffer || !maxlen)
   627 	{
   628 		return 0;
   629 	}
   630 
   631 	int arg;
   632 	int args = params[0];
   633 	char *buf_p;
   634 	char ch;
   635 	int flags;
   636 	int width;
   637 	int prec;
   638 	int n;
   639 	char sign;
   640 	const char *fmt;
   641 	size_t llen = maxlen - 1;
   642 
   643 	buf_p = buffer;
   644 	arg = *param;
   645 	fmt = format;
   646 
   647 	while (true)
   648 	{
   649 		// run through the format string until we hit a '%' or '\0'
   650 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   651 		{
   652 			*buf_p++ = ch;
   653 			llen--;
   654 		}
   655 		if ((ch == '\0') || (llen <= 0))
   656 		{
   657 			goto done;
   658 		}
   659 
   660 		// skip over the '%'
   661 		fmt++;
   662 
   663 		// reset formatting state
   664 		flags = 0;
   665 		width = 0;
   666 		prec = -1;
   667 		sign = '\0';
   668 
   669 rflag:
   670 		ch = *fmt++;
   671 reswitch:
   672 		switch(ch)
   673 		{
   674 		case '-':
   675 			{
   676 				flags |= LADJUST;
   677 				goto rflag;
   678 			}
   679 		case '.':
   680 			{
   681 				n = 0;
   682 				while(is_digit((ch = *fmt++)))
   683 				{
   684 					n = 10 * n + (ch - '0');
   685 				}
   686 				prec = (n < 0) ? -1 : n;
   687 				goto reswitch;
   688 			}
   689 		case '0':
   690 			{
   691 				flags |= ZEROPAD;
   692 				goto rflag;
   693 			}
   694 		case '1':
   695 		case '2':
   696 		case '3':
   697 		case '4':
   698 		case '5':
   699 		case '6':
   700 		case '7':
   701 		case '8':
   702 		case '9':
   703 			{
   704 				n = 0;
   705 				do
   706 				{
   707 					n = 10 * n + (ch - '0');
   708 					ch = *fmt++;
   709 				} while(is_digit(ch));
   710 				width = n;
   711 				goto reswitch;
   712 			}
   713 		case 'c':
   714 			{
   715 				CHECK_ARGS(0);
   716 				if (!llen)
   717 				{
   718 					goto done;
   719 				}
   720 				char *c;
   721 				pCtx->LocalToString(params[arg], &c);
   722 				*buf_p++ = *c;
   723 				llen--;
   724 				arg++;
   725 				break;
   726 			}
   727 		case 'd':
   728 		case 'i':
   729 			{
   730 				CHECK_ARGS(0);
   731 				cell_t *value;
   732 				pCtx->LocalToPhysAddr(params[arg], &value);
   733 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
   734 				arg++;
   735 				break;
   736 			}
   737 		case 'u':
   738 			{
   739 				CHECK_ARGS(0);
   740 				cell_t *value;
   741 				pCtx->LocalToPhysAddr(params[arg], &value);
   742 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   743 				arg++;
   744 				break;
   745 			}
   746 		case 'f':
   747 			{
   748 				CHECK_ARGS(0);
   749 				cell_t *value;
   750 				pCtx->LocalToPhysAddr(params[arg], &value);
   751 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec);
   752 				arg++;
   753 				break;
   754 			}
   755 		case 'L':
   756 			{
   757 				CHECK_ARGS(0);
   758 				cell_t *value;
   759 				pCtx->LocalToPhysAddr(params[arg], &value);
   760 				char buffer[255];
   761 				if (*value)
   762 				{
   763 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   764 					if (!player || !player->IsConnected())
   765 					{
   766 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   767 					}
   768 					const char *auth = player->GetAuthString();
   769 					if (!auth || auth[0] == '\0')
   770 					{
   771 						auth = "STEAM_ID_PENDING";
   772 					}
   773 					int userid = engine->GetPlayerUserId(player->GetEdict());
   774 					UTIL_Format(buffer, 
   775 						sizeof(buffer), 
   776 						"%s<%d><%s><>", 
   777 						player->GetName(),
   778 						userid,
   779 						auth);
   780 				} else {
   781 					UTIL_Format(buffer,
   782 						sizeof(buffer),
   783 						"Console<0><Console><Console>");
   784 				}
   785 				AddString(&buf_p, llen, buffer, width, prec);
   786 				arg++;
   787 				break;
   788 			}
   789 		case 's':
   790 			{
   791 				CHECK_ARGS(0);
   792 				char *str;
   793 				int err;
   794 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
   795 				{
   796 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
   797 					return 0;
   798 				}
   799 				AddString(&buf_p, llen, str, width, prec);
   800 				arg++;
   801 				break;
   802 			}
   803 		case 'T':
   804 			{
   805 				CHECK_ARGS(1);
   806 				char *key;
   807 				bool error;
   808 				size_t res;
   809 				cell_t *target;
   810 				pCtx->LocalToString(params[arg++], &key);
   811 				pCtx->LocalToPhysAddr(params[arg++], &target);
   812 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
   813 				if (error)
   814 				{
   815 					return 0;
   816 				}
   817 				buf_p += res;
   818 				llen -= res;
   819 				break;
   820 			}
   821 		case 't':
   822 			{
   823 				CHECK_ARGS(0);
   824 				char *key;
   825 				bool error;
   826 				size_t res;
   827 				cell_t target = g_SourceMod.GetGlobalTarget();
   828 				pCtx->LocalToString(params[arg++], &key);
   829 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   830 				if (error)
   831 				{
   832 					return 0;
   833 				}
   834 				buf_p += res;
   835 				llen -= res;
   836 				break;
   837 			}
   838 		case 'X':
   839 			{
   840 				CHECK_ARGS(0);
   841 				cell_t *value;
   842 				pCtx->LocalToPhysAddr(params[arg], &value);
   843 				flags |= UPPERDIGITS;
   844 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   845 				arg++;
   846 				break;
   847 			}
   848 		case 'x':
   849 			{
   850 				CHECK_ARGS(0);
   851 				cell_t *value;
   852 				pCtx->LocalToPhysAddr(params[arg], &value);
   853 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   854 				arg++;
   855 				break;
   856 			}
   857 		case '%':
   858 			{
   859 				if (!llen)
   860 				{
   861 					goto done;
   862 				}
   863 				*buf_p++ = ch;
   864 				llen--;
   865 				break;
   866 			}
   867 		case '\0':
   868 			{
   869 				if (!llen)
   870 				{
   871 					goto done;
   872 				}
   873 				*buf_p++ = '%';
   874 				llen--;
   875 				goto done;
   876 			}
   877 		default:
   878 			{
   879 				if (!llen)
   880 				{
   881 					goto done;
   882 				}
   883 				*buf_p++ = ch;
   884 				llen--;
   885 				break;
   886 			}
   887 		}
   888 	}
   889 
   890 done:
   891 	*buf_p = '\0';
   892 	*param = arg;
   893 	return (maxlen - llen - 1);
   894 }
   895 
   896 const char *stristr(const char *str, const char *substr)
   897 {
   898 	if (!*substr)
   899 	{
   900 		return ((char *)str);
   901 	}
   902 
   903 	char *needle = (char *)substr;
   904 	char *prevloc = (char *)str;
   905 	char *haystack = (char *)str;
   906 
   907 	while (*haystack)
   908 	{
   909 		if (tolower(*haystack) == tolower(*needle))
   910 		{
   911 			haystack++;
   912 			if (!*++needle)
   913 			{
   914 				return prevloc;
   915 			}
   916 		} else {
   917 			haystack = ++prevloc;
   918 			needle = (char *)substr;
   919 		}
   920 	}
   921 
   922 	return NULL;
   923 }
   924 
   925 unsigned int strncopy(char *dest, const char *src, size_t count)
   926 {
   927 	if (!count)
   928 	{
   929 		return 0;
   930 	}
   931 
   932 	char *start = dest;
   933 	while ((*src) && (--count))
   934 	{
   935 		*dest++ = *src++;
   936 	}
   937 	*dest = '\0';
   938 
   939 	return (dest - start);
   940 }
   941 
   942 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
   943 {
   944 	va_list ap;
   945 	va_start(ap, fmt);
   946 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   947 	va_end(ap);
   948 
   949 	if (len >= maxlength)
   950 	{
   951 		buffer[maxlength - 1] = '\0';
   952 		return (maxlength - 1);
   953 	} else {
   954 		return len;
   955 	}
   956 }
   957 
   958 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
   959 {
   960 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   961 
   962 	if (len >= maxlength)
   963 	{
   964 		buffer[maxlength - 1] = '\0';
   965 		return (maxlength - 1);
   966 	} else {
   967 		return len;
   968 	}
   969 }
   970 
   971 char *sm_strdup(const char *str)
   972 {
   973 	char *ptr = new char[strlen(str)+1];
   974 	strcpy(ptr, str);
   975 	return ptr;
   976 }
   977 
   978 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
   979 {
   980 	size_t searchLen = strlen(search);
   981 	size_t replaceLen = strlen(replace);
   982 
   983 	char *ptr = subject;
   984 	unsigned int total = 0;
   985 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
   986 	{
   987 		total++;
   988 		if (*ptr == '\0')
   989 		{
   990 			break;
   991 		}
   992 	}
   993 
   994 	return total;
   995 }
   996 
   997 /**
   998  * NOTE: Do not edit this for the love of god unless you have 
   999  * read the test cases and understand the code behind each one.
  1000  * While I don't guarantee there aren't mistakes, I do guarantee
  1001  * that plugins will end up relying on tiny idiosyncracies of this
  1002  * function, just like they did with AMX Mod X.
  1003  *
  1004  * There are explicitly more cases than the AMX Mod X version because
  1005  * we're not doing a blind copy.  Each case is specifically optimized
  1006  * for what needs to be done.  Even better, we don't have to error on
  1007  * bad buffer sizes.  Instead, this function will smartly cut off the 
  1008  * string in a way that pushes old data out.
  1009  */
  1010 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
  1011 {
  1012 	char *ptr = subject;
  1013 	size_t browsed = 0;
  1014 	size_t textLen = strlen(subject);
  1015 
  1016 	/* It's not possible to search or replace */
  1017 	if (searchLen > textLen || maxLen <= 1)
  1018 	{
  1019 		return NULL;
  1020 	}
  1021 
  1022 	/* Subtract one off the maxlength so we can include the null terminator */
  1023 	maxLen--;
  1024 
  1025 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
  1026 	{
  1027 		/* See if we get a comparison */
  1028 		if (strncmp(ptr, search, searchLen) == 0)
  1029 		{
  1030 			if (replaceLen > searchLen)
  1031 			{
  1032 				/* First, see if we have enough space to do this operation */
  1033 				if (maxLen - textLen < replaceLen - searchLen)
  1034 				{
  1035 					/* First, see if the replacement length goes out of bounds. */
  1036 					if (browsed + replaceLen >= maxLen)
  1037 					{
  1038 						/* EXAMPLE CASE:
  1039 						 * Subject: AABBBCCC
  1040 						 * Buffer : 12 bytes
  1041 						 * Search : BBB
  1042 						 * Replace: DDDDDDDDDD
  1043 						 * OUTPUT : AADDDDDDDDD
  1044 						 * POSITION:           ^
  1045 						 */
  1046 						/* If it does, we'll just bound the length and do a strcpy. */
  1047 						replaceLen = maxLen - browsed;
  1048 						/* Note, we add one to the final result for the null terminator */
  1049 						strncopy(ptr, replace, replaceLen+1);
  1050 					} else {
  1051 						/* EXAMPLE CASE:
  1052 						 * Subject: AABBBCCC
  1053 						 * Buffer : 12 bytes
  1054 						 * Search : BBB
  1055 						 * Replace: DDDDDDD
  1056 						 * OUTPUT : AADDDDDDDCC
  1057 						 * POSITION:         ^
  1058 						 */
  1059 						/* We're going to have some bytes left over... */
  1060 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1061 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
  1062 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
  1063 						char *moveTo = ptr + replaceLen;
  1064 
  1065 						/* First, move our old data out of the way. */
  1066 						memmove(moveTo, moveFrom, realBytesToCopy);
  1067 
  1068 						/* Now, do our replacement. */
  1069 						memcpy(ptr, replace, replaceLen);
  1070 					}
  1071 				} else {
  1072 					/* EXAMPLE CASE:
  1073 					 * Subject: AABBBCCC
  1074 					 * Buffer : 12 bytes
  1075 					 * Search : BBB
  1076 					 * Replace: DDDD
  1077 					 * OUTPUT : AADDDDCCC
  1078 					 * POSITION:      ^
  1079 					 */
  1080 					/* Yes, we have enough space.  Do a normal move operation. */
  1081 					char *moveFrom = ptr + searchLen;
  1082 					char *moveTo = ptr + replaceLen;
  1083 
  1084 					/* First move our old data out of the way. */
  1085 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1086 					memmove(moveTo, moveFrom, bytesToCopy);
  1087 
  1088 					/* Now do our replacement. */
  1089 					memcpy(ptr, replace, replaceLen);
  1090 				}
  1091 			} else if (replaceLen < searchLen) {
  1092 				/* EXAMPLE CASE:
  1093 				 * Subject: AABBBCCC
  1094 				 * Buffer : 12 bytes
  1095 				 * Search : BBB
  1096 				 * Replace: D
  1097 				 * OUTPUT : AADCCC
  1098 				 * POSOTION:   ^
  1099 				 */
  1100 				/* If the replacement does not grow the string length, we do not
  1101 				 * need to do any fancy checking at all.  Yay!
  1102 				 */
  1103 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
  1104 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
  1105 
  1106 				/* Copy our replacement in, if any */
  1107 				if (replaceLen)
  1108 				{
  1109 					memcpy(ptr, replace, replaceLen);
  1110 				}
  1111 
  1112 				/* Figure out how many bytes to move down, including null terminator */
  1113 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1114 
  1115 				/* Move the rest of the string down */
  1116 				memmove(moveTo, moveFrom, bytesToCopy);
  1117 			} else {
  1118 				/* EXAMPLE CASE:
  1119 				 * Subject: AABBBCCC
  1120 				 * Buffer : 12 bytes
  1121 				 * Search : BBB
  1122 				 * Replace: DDD
  1123 				 * OUTPUT : AADDDCCC
  1124 				 * POSITION:     ^
  1125 				 */
  1126 				/* We don't have to move anything around, just do a straight copy */
  1127 				memcpy(ptr, replace, replaceLen);
  1128 			}
  1129 
  1130 			return ptr + replaceLen;
  1131 		}
  1132 		ptr++;
  1133 		browsed++;
  1134 	}
  1135 
  1136 	return NULL;
  1137 }