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