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