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