core/sm_stringutil.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Wed Jul 31 22:03:59 2013 -0400 (2013-07-31)
changeset 3963 39fc1ce67130
parent 3913 a73cf1f26984
child 4327 60e4042f2589
permissions -rw-r--r--
Fixed crash in SDKHooks when throwing bad ent type error on logical ent (r=psychonic).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 <ITextParsers.h>
    36 #include "sm_stringutil.h"
    37 #include "Logger.h"
    38 #include "PlayerManager.h"
    39 #include "logic_bridge.h"
    40 #include "sourcemod.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 inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
    55 {
    56 	cell_t new_params[MAX_TRANSLATE_PARAMS];
    57 	for (unsigned int i = 0; i < pTrans->fmt_count; i++)
    58 	{
    59 		new_params[i] = params[pTrans->fmt_order[i]];
    60 	}
    61 	memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
    62 }
    63 
    64 size_t Translate(char *buffer,
    65 				 size_t maxlen,
    66 				 IPluginContext *pCtx,
    67 				 const char *key,
    68 				 cell_t target,
    69 				 const cell_t *params,
    70 				 int *arg,
    71 				 bool *error)
    72 {
    73 	unsigned int langid;
    74 	*error = false;
    75 	Translation pTrans;
    76 	IPlugin *pl = scripts->FindPluginByContext(pCtx->GetContext());
    77 	unsigned int max_params = 0;
    78 	IPhraseCollection *pPhrases;
    79 
    80 	pPhrases = pl->GetPhrases();
    81 
    82 try_serverlang:
    83 	if (target == SOURCEMOD_SERVER_LANGUAGE)
    84 	{
    85 		langid = translator->GetServerLanguage();
    86  	}
    87 	else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
    88 	{
    89 		langid = translator->GetClientLanguage(target);
    90 	}
    91 	else
    92 	{
    93 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
    94 		goto error_out;
    95 	}
    96 
    97 	if (pPhrases->FindTranslation(key, langid, &pTrans) != Trans_Okay)
    98 	{
    99 		if (target != SOURCEMOD_SERVER_LANGUAGE && langid != translator->GetServerLanguage())
   100 		{
   101 			target = SOURCEMOD_SERVER_LANGUAGE;
   102 			goto try_serverlang;
   103 		}
   104 		else if (langid != SOURCEMOD_LANGUAGE_ENGLISH)
   105 		{
   106 			if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &pTrans) != Trans_Okay)
   107 			{
   108 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   109 				goto error_out;
   110 			}
   111 		}
   112 		else
   113 		{
   114 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   115 			goto error_out;
   116 		}
   117 	}
   118 
   119 	max_params = pTrans.fmt_count;
   120 
   121 	if (max_params)
   122 	{
   123 		cell_t new_params[MAX_TRANSLATE_PARAMS];
   124 
   125 		/* Check if we're going to over the limit */
   126 		if ((*arg) + (max_params - 1) > (size_t)params[0])
   127 		{
   128 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
   129 				"Translation string formatted incorrectly - missing at least %d parameters", 
   130 				((*arg + (max_params - 1)) - params[0])
   131 				);
   132 			goto error_out;
   133 		}
   134 
   135 		/* If we need to re-order the parameters, do so with a temporary array.
   136 		 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
   137 		 */
   138 		memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
   139 		ReorderTranslationParams(&pTrans, &new_params[*arg]);
   140 
   141 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
   142 	}
   143 	else
   144 	{
   145 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
   146 	}
   147 	
   148 error_out:
   149 	*error = true;
   150 	return 0;
   151 }
   152 
   153 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
   154 {
   155 	int size = 0;
   156 	char *buf;
   157 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
   158 
   159 	buf = *buf_p;
   160 
   161 	if (string == NULL)
   162 	{
   163 		string = nlstr;
   164 		prec = -1;
   165 	}
   166 
   167 	if (prec >= 0)
   168 	{
   169 		for (size = 0; size < prec; size++) 
   170 		{
   171 			if (string[size] == '\0')
   172 			{
   173 				break;
   174 			}
   175 		}
   176 	}
   177 	else
   178 	{
   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, int flags)
   206 {
   207 	int digits;					// non-fraction part digits
   208 	double tmp;					// temporary
   209 	char *buf = *buf_p;			// output buffer pointer
   210 	int val;					// temporary
   211 	int sign = 0;				// 0: positive, 1: negative
   212 	int fieldlength;			// for padding
   213 	int significant_digits = 0;	// number of significant digits written
   214 	const int MAX_SIGNIFICANT_DIGITS = 16;
   215 
   216 	// default precision
   217 	if (prec < 0)
   218 	{
   219 		prec = 6;
   220 	}
   221 
   222 	// get the sign
   223 	if (fval < 0)
   224 	{
   225 		fval = -fval;
   226 		sign = 1;
   227 	}
   228 
   229 	// compute whole-part digits count
   230 	digits = (int)log10(fval) + 1;
   231 
   232 	// Only print 0.something if 0 < fval < 1
   233 	if (digits < 1)
   234 	{
   235 		digits = 1;
   236 	}
   237 
   238 	// compute the field length
   239 	fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
   240 
   241 	// minus sign BEFORE left padding if padding with zeros
   242 	if (sign && maxlen && (flags & ZEROPAD))
   243 	{
   244 		*buf++ = '-';
   245 		maxlen--;
   246 	}
   247 
   248 	// right justify if required
   249 	if ((flags & LADJUST) == 0)
   250 	{
   251 		while ((fieldlength < width) && maxlen)
   252 		{
   253 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   254 			width--;
   255 			maxlen--;
   256 		}
   257 	}
   258 
   259 	// minus sign AFTER left padding if padding with spaces
   260 	if (sign && maxlen && !(flags & ZEROPAD))
   261 	{
   262 		*buf++ = '-';
   263 		maxlen--;
   264 	}
   265 
   266 	// write the whole part
   267 	tmp = pow(10.0, digits-1);
   268 	while ((digits--) && maxlen)
   269 	{
   270 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
   271 		{
   272 			*buf++ = '0';
   273 		}
   274 		else
   275 		{
   276 			val = (int)(fval / tmp);
   277 			*buf++ = '0' + val;
   278 			fval -= val * tmp;
   279 			tmp *= 0.1;
   280 		}
   281 		maxlen--;
   282 	}
   283 
   284 	// write the fraction part
   285 	if (maxlen && prec)
   286 	{
   287 		*buf++ = '.';
   288 		maxlen--;
   289 	}
   290 
   291 	tmp = pow(10.0, prec);
   292 
   293 	fval *= tmp;
   294 	while (prec-- && maxlen)
   295 	{
   296 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
   297 		{
   298 			*buf++ = '0';
   299 		}
   300 		else
   301 		{
   302 			tmp *= 0.1;
   303 			val = (int)(fval / tmp);
   304 			*buf++ = '0' + val;
   305 			fval -= val * tmp;
   306 		}
   307 		maxlen--;
   308 	}
   309 
   310 	// left justify if required
   311 	if (flags & LADJUST)
   312 	{
   313 		while ((fieldlength < width) && maxlen)
   314 		{
   315 			// right-padding only with spaces, ZEROPAD is ignored
   316 			*buf++ = ' ';
   317 			width--;
   318 			maxlen--;
   319 		}
   320 	}
   321 
   322 	// update parent's buffer pointer
   323 	*buf_p = buf;
   324 }
   325 
   326 void AddBinary(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   327 {
   328 	char text[32];
   329 	int digits;
   330 	char *buf;
   331 
   332 	digits = 0;
   333 	do
   334 	{
   335 		if (val & 1)
   336 		{
   337 			text[digits++] = '1';
   338 		}
   339 		else
   340 		{
   341 			text[digits++] = '0';
   342 		}
   343 		val >>= 1;
   344 	} while (val);
   345 
   346 	buf = *buf_p;
   347 
   348 	if (!(flags & LADJUST))
   349 	{
   350 		while (digits < width && maxlen)
   351 		{
   352 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   353 			width--;
   354 			maxlen--;
   355 		}
   356 	}
   357 
   358 	while (digits-- && maxlen)
   359 	{
   360 		*buf++ = text[digits];
   361 		width--;
   362 		maxlen--;
   363 	}
   364 
   365 	if (flags & LADJUST)
   366 	{
   367 		while (width-- && maxlen)
   368 		{
   369 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   370 			maxlen--;
   371 		}
   372 	}
   373 
   374 	*buf_p = buf;
   375 }
   376 
   377 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   378 {
   379 	char text[32];
   380 	int digits;
   381 	char *buf;
   382 
   383 	digits = 0;
   384 	do
   385 	{
   386 		text[digits++] = '0' + val % 10;
   387 		val /= 10;
   388 	} while (val);
   389 
   390 	buf = *buf_p;
   391 
   392 	if (!(flags & LADJUST))
   393 	{
   394 		while (digits < width && maxlen)
   395 		{
   396 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   397 			width--;
   398 			maxlen--;
   399 		}
   400 	}
   401 
   402 	while (digits-- && maxlen)
   403 	{
   404 		*buf++ = text[digits];
   405 		width--;
   406 		maxlen--;
   407 	}
   408 
   409 	if (flags & LADJUST)
   410 	{
   411 		while (width-- && maxlen)
   412 		{
   413 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   414 			maxlen--;
   415 		}
   416 	}
   417 
   418 	*buf_p = buf;
   419 }
   420 
   421 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   422 {
   423 	char text[32];
   424 	int digits;
   425 	int signedVal;
   426 	char *buf;
   427 	unsigned int unsignedVal;
   428 
   429 	digits = 0;
   430 	signedVal = val;
   431 	if (val < 0)
   432 	{
   433 		/* we want the unsigned version */
   434 		unsignedVal = abs(val);
   435 	}
   436 	else
   437 	{
   438 		unsignedVal = val;
   439 	}
   440 
   441 	do
   442 	{
   443 		text[digits++] = '0' + unsignedVal % 10;
   444 		unsignedVal /= 10;
   445 	} while (unsignedVal);
   446 
   447 	if (signedVal < 0)
   448 	{
   449 		text[digits++] = '-';
   450 	}
   451 
   452 	buf = *buf_p;
   453 
   454 	if (!(flags & LADJUST))
   455 	{
   456 		while ((digits < width) && maxlen)
   457 		{
   458 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   459 			width--;
   460 			maxlen--;
   461 		}
   462 	}
   463 
   464 	while (digits-- && maxlen)
   465 	{
   466 		*buf++ = text[digits];
   467 		width--;
   468 		maxlen--;
   469 	}
   470 
   471 	if (flags & LADJUST)
   472 	{
   473 		while (width-- && maxlen)
   474 		{
   475 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   476 			maxlen--;
   477 		}
   478 	}
   479 
   480 	*buf_p = buf;
   481 }
   482 
   483 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   484 {
   485 	char text[32];
   486 	int digits;
   487 	char *buf;
   488 	char digit;
   489 	int hexadjust;
   490 
   491 	if (flags & UPPERDIGITS)
   492 	{
   493 		hexadjust = 'A' - '9' - 1;
   494 	}
   495 	else
   496 	{
   497 		hexadjust = 'a' - '9' - 1;
   498 	}
   499 
   500 	digits = 0;
   501 	do 
   502 	{
   503 		digit = ('0' + val % 16);
   504 		if (digit > '9')
   505 		{
   506 			digit += hexadjust;
   507 		}
   508 
   509 		text[digits++] = digit;
   510 		val /= 16;
   511 	} while(val);
   512 
   513 	buf = *buf_p;
   514 
   515 	if (!(flags & LADJUST))
   516 	{
   517 		while (digits < width && maxlen)
   518 		{
   519 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   520 			width--;
   521 			maxlen--;
   522 		}
   523 	}
   524 
   525 	while (digits-- && maxlen)
   526 	{
   527 		*buf++ = text[digits];
   528 		width--;
   529 		maxlen--;
   530 	}
   531 
   532 	if (flags & LADJUST)
   533 	{
   534 		while (width-- && maxlen)
   535 		{
   536 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   537 			maxlen--;
   538 		}
   539 	}
   540 
   541 	*buf_p = buf;
   542 }
   543 
   544 bool gnprintf(char *buffer,
   545 			  size_t maxlen,
   546 			  const char *format,
   547 			  IPhraseCollection *pPhrases,
   548 			  void **params,
   549 			  unsigned int numparams,
   550 			  unsigned int &curparam,
   551 			  size_t *pOutLength,
   552 			  const char **pFailPhrase)
   553 {
   554 	if (!buffer || !maxlen)
   555 	{
   556 		if (pOutLength != NULL)
   557 		{
   558 			*pOutLength = 0;
   559 		}
   560 		return true;
   561 	}
   562 
   563 	if (numparams > MAX_TRANSLATE_PARAMS)
   564 	{
   565 		if (pFailPhrase != NULL)
   566 		{
   567 			*pFailPhrase = NULL;
   568 		}
   569 		return false;
   570 	}
   571 
   572 	int arg = 0;
   573 	char *buf_p;
   574 	char ch;
   575 	int flags;
   576 	int width;
   577 	int prec;
   578 	int n;
   579 	char sign;
   580 	const char *fmt;
   581 	size_t llen = maxlen - 1;
   582 
   583 	buf_p = buffer;
   584 	fmt = format;
   585 
   586 	while (true)
   587 	{
   588 		// run through the format string until we hit a '%' or '\0'
   589 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   590 		{
   591 			*buf_p++ = ch;
   592 			llen--;
   593 		}
   594 		if ((ch == '\0') || (llen <= 0))
   595 		{
   596 			goto done;
   597 		}
   598 
   599 		// skip over the '%'
   600 		fmt++;
   601 
   602 		// reset formatting state
   603 		flags = 0;
   604 		width = 0;
   605 		prec = -1;
   606 		sign = '\0';
   607 
   608 rflag:
   609 		ch = *fmt++;
   610 reswitch:
   611 		switch(ch)
   612 		{
   613 		case '-':
   614 			{
   615 				flags |= LADJUST;
   616 				goto rflag;
   617 			}
   618 		case '.':
   619 			{
   620 				n = 0;
   621 				while(is_digit((ch = *fmt++)))
   622 				{
   623 					n = 10 * n + (ch - '0');
   624 				}
   625 				prec = (n < 0) ? -1 : n;
   626 				goto reswitch;
   627 			}
   628 		case '0':
   629 			{
   630 				flags |= ZEROPAD;
   631 				goto rflag;
   632 			}
   633 		case '1':
   634 		case '2':
   635 		case '3':
   636 		case '4':
   637 		case '5':
   638 		case '6':
   639 		case '7':
   640 		case '8':
   641 		case '9':
   642 			{
   643 				n = 0;
   644 				do
   645 				{
   646 					n = 10 * n + (ch - '0');
   647 					ch = *fmt++;
   648 				} while(is_digit(ch));
   649 				width = n;
   650 				goto reswitch;
   651 			}
   652 		case 'c':
   653 			{
   654 				if (!llen)
   655 				{
   656 					goto done;
   657 				}
   658 				if (curparam >= numparams)
   659 				{
   660 					if (pFailPhrase != NULL)
   661 					{
   662 						*pFailPhrase = NULL;
   663 					}
   664 					return false;
   665 				}
   666 				char *c = (char *)params[curparam];
   667 				curparam++;
   668 				*buf_p++ = *c;
   669 				llen--;
   670 				arg++;
   671 				break;
   672 			}
   673 		case 'b':
   674 			{
   675 				if (curparam >= numparams)
   676 				{
   677 					if (pFailPhrase != NULL)
   678 					{
   679 						*pFailPhrase = NULL;
   680 					}
   681 					return false;
   682 				}
   683 				int *value = (int *)params[curparam];
   684 				curparam++;
   685 				AddBinary(&buf_p, llen, *value, width, flags);
   686 				arg++;
   687 				break;
   688 			}
   689 		case 'd':
   690 		case 'i':
   691 			{
   692 				if (curparam >= numparams)
   693 				{
   694 					if (pFailPhrase != NULL)
   695 					{
   696 						*pFailPhrase = NULL;
   697 					}
   698 					return false;
   699 				}
   700 				int *value = (int *)params[curparam];
   701 				curparam++;
   702 				AddInt(&buf_p, llen, *value, width, flags);
   703 				arg++;
   704 				break;
   705 			}
   706 		case 'u':
   707 			{
   708 				if (curparam >= numparams)
   709 				{
   710 					if (pFailPhrase != NULL)
   711 					{
   712 						*pFailPhrase = NULL;
   713 					}
   714 					return false;
   715 				}
   716 				unsigned int *value = (unsigned int *)params[curparam];
   717 				curparam++;
   718 				AddUInt(&buf_p, llen, *value, width, flags);
   719 				arg++;
   720 				break;
   721 			}
   722 		case 'f':
   723 			{
   724 				if (curparam >= numparams)
   725 				{
   726 					if (pFailPhrase != NULL)
   727 					{
   728 						*pFailPhrase = NULL;
   729 					}
   730 					return false;
   731 				}
   732 				float *value = (float *)params[curparam];
   733 				curparam++;
   734 				AddFloat(&buf_p, llen, *value, width, prec, flags);
   735 				arg++;
   736 				break;
   737 			}
   738 		case 's':
   739 			{
   740 				if (curparam >= numparams)
   741 				{
   742 					if (pFailPhrase != NULL)
   743 					{
   744 						*pFailPhrase = NULL;
   745 					}
   746 					return false;
   747 				}
   748 				const char *str = (const char *)params[curparam];
   749 				curparam++;
   750 				AddString(&buf_p, llen, str, width, prec);
   751 				arg++;
   752 				break;
   753 			}
   754 		case 'T':
   755 		case 't':
   756 			{
   757 				int target;
   758 				const char *key;
   759 				size_t out_length;
   760 				Translation trans;
   761 				unsigned int lang_id;
   762 
   763 				if (curparam >= numparams)
   764 				{
   765 					if (pFailPhrase != NULL)
   766 					{
   767 						*pFailPhrase = NULL;
   768 					}
   769 					return false;
   770 				}
   771 				key = (const char *)(params[curparam]);
   772 				curparam++;
   773 
   774 				if (ch == 'T')
   775 				{
   776 					if (curparam >= numparams)
   777 					{
   778 						if (pFailPhrase != NULL)
   779 						{
   780 							*pFailPhrase = NULL;
   781 						}
   782 						return false;
   783 					}
   784 					target = *((int *)(params[curparam]));
   785 					curparam++;
   786 				}
   787 				else
   788 				{
   789 					target = translator->GetGlobalTarget();
   790 				}
   791 
   792 try_again:
   793 				if (target == SOURCEMOD_SERVER_LANGUAGE)
   794 				{
   795 					lang_id = translator->GetServerLanguage();
   796 				}
   797 				else if (target >= 1 && target <= g_Players.GetMaxClients())
   798 				{
   799 					lang_id = translator->GetClientLanguage(target);
   800 				}
   801 				else
   802 				{
   803 					lang_id = translator->GetServerLanguage();
   804 				}
   805 
   806 				if (pPhrases == NULL)
   807 				{
   808 					if (pFailPhrase != NULL)
   809 					{
   810 						*pFailPhrase = key;
   811 					}
   812 					return false;
   813 				}
   814 
   815 				if (pPhrases->FindTranslation(key, lang_id, &trans) != Trans_Okay)
   816 				{
   817 					if (target != SOURCEMOD_SERVER_LANGUAGE && lang_id != translator->GetServerLanguage())
   818 					{
   819 						target = SOURCEMOD_SERVER_LANGUAGE;
   820 						goto try_again;
   821 					}
   822 					else if (lang_id != SOURCEMOD_LANGUAGE_ENGLISH)
   823 					{
   824 						if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &trans) != Trans_Okay)
   825 						{
   826 							if (pFailPhrase != NULL)
   827 							{
   828 								*pFailPhrase = key;
   829 							}
   830 							return false;
   831 						}
   832 					}
   833 					else
   834 					{
   835 						if (pFailPhrase != NULL)
   836 						{
   837 							*pFailPhrase = key;
   838 						}
   839 						return false;
   840 					}
   841 				}
   842 
   843 				if (trans.fmt_count)
   844 				{
   845 					unsigned int i;
   846 					void *new_params[MAX_TRANSLATE_PARAMS];
   847 
   848 					if (curparam + trans.fmt_count > numparams)
   849 					{
   850 						if (pFailPhrase != NULL)
   851 						{
   852 							*pFailPhrase = NULL;
   853 						}
   854 						return false;
   855 					}
   856 
   857 					/* Copy the array and re-order the stack */
   858 					memcpy(new_params, params, sizeof(void *) * numparams);
   859 					for (i = 0; i < trans.fmt_count; i++)
   860 					{
   861 						new_params[curparam + i] = const_cast<void *>(params[curparam + trans.fmt_order[i]]);
   862 					}
   863 
   864 					if (!gnprintf(buf_p,
   865 							llen,
   866 							trans.szPhrase,
   867 							pPhrases,
   868 							new_params,
   869 							numparams,
   870 							curparam,
   871 							&out_length,
   872 							pFailPhrase))
   873 					{
   874 						return false;
   875 					}
   876 				}
   877 				else
   878 				{
   879 					if (!gnprintf(buf_p,
   880 						llen,
   881 						trans.szPhrase,
   882 						pPhrases,
   883 						params,
   884 						numparams,
   885 						curparam,
   886 						&out_length,
   887 						pFailPhrase))
   888 					{
   889 						return false;
   890 					}
   891 				}
   892 
   893 				buf_p += out_length;
   894 				llen -= out_length;
   895 
   896 				break;
   897 			}
   898 		case 'X':
   899 			{
   900 				if (curparam >= numparams)
   901 				{
   902 					if (pFailPhrase != NULL)
   903 					{
   904 						*pFailPhrase = NULL;
   905 					}
   906 					return false;
   907 				}
   908 				unsigned int *value = (unsigned int *)params[curparam];
   909 				curparam++;
   910 				flags |= UPPERDIGITS;
   911 				AddHex(&buf_p, llen, *value, width, flags);
   912 				arg++;
   913 				break;
   914 			}
   915 		case 'x':
   916 			{
   917 				if (curparam >= numparams)
   918 				{
   919 					if (pFailPhrase != NULL)
   920 					{
   921 						*pFailPhrase = NULL;
   922 					}
   923 					return false;
   924 				}
   925 				unsigned int *value = (unsigned int *)params[curparam];
   926 				curparam++;
   927 				AddHex(&buf_p, llen, *value, width, flags);
   928 				arg++;
   929 				break;
   930 			}
   931 		case '%':
   932 			{
   933 				if (!llen)
   934 				{
   935 					goto done;
   936 				}
   937 				*buf_p++ = ch;
   938 				llen--;
   939 				break;
   940 			}
   941 		case '\0':
   942 			{
   943 				if (!llen)
   944 				{
   945 					goto done;
   946 				}
   947 				*buf_p++ = '%';
   948 				llen--;
   949 				goto done;
   950 			}
   951 		default:
   952 			{
   953 				if (!llen)
   954 				{
   955 					goto done;
   956 				}
   957 				*buf_p++ = ch;
   958 				llen--;
   959 				break;
   960 			}
   961 		}
   962 	}
   963 
   964 done:
   965 	*buf_p = '\0';
   966 
   967 	if (pOutLength != NULL)
   968 	{
   969 		*pOutLength = (maxlen - llen - 1);
   970 	}
   971 
   972 	return true;
   973 }
   974 
   975 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   976 {
   977 	if (!buffer || !maxlen)
   978 	{
   979 		return 0;
   980 	}
   981 
   982 	int arg;
   983 	int args = params[0];
   984 	char *buf_p;
   985 	char ch;
   986 	int flags;
   987 	int width;
   988 	int prec;
   989 	int n;
   990 	char sign;
   991 	const char *fmt;
   992 	size_t llen = maxlen - 1;
   993 
   994 	buf_p = buffer;
   995 	arg = *param;
   996 	fmt = format;
   997 
   998 	while (true)
   999 	{
  1000 		// run through the format string until we hit a '%' or '\0'
  1001 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
  1002 		{
  1003 			*buf_p++ = ch;
  1004 			llen--;
  1005 		}
  1006 		if ((ch == '\0') || (llen <= 0))
  1007 		{
  1008 			goto done;
  1009 		}
  1010 
  1011 		// skip over the '%'
  1012 		fmt++;
  1013 
  1014 		// reset formatting state
  1015 		flags = 0;
  1016 		width = 0;
  1017 		prec = -1;
  1018 		sign = '\0';
  1019 
  1020 rflag:
  1021 		ch = *fmt++;
  1022 reswitch:
  1023 		switch(ch)
  1024 		{
  1025 		case '-':
  1026 			{
  1027 				flags |= LADJUST;
  1028 				goto rflag;
  1029 			}
  1030 		case '.':
  1031 			{
  1032 				n = 0;
  1033 				while(is_digit((ch = *fmt++)))
  1034 				{
  1035 					n = 10 * n + (ch - '0');
  1036 				}
  1037 				prec = (n < 0) ? -1 : n;
  1038 				goto reswitch;
  1039 			}
  1040 		case '0':
  1041 			{
  1042 				flags |= ZEROPAD;
  1043 				goto rflag;
  1044 			}
  1045 		case '1':
  1046 		case '2':
  1047 		case '3':
  1048 		case '4':
  1049 		case '5':
  1050 		case '6':
  1051 		case '7':
  1052 		case '8':
  1053 		case '9':
  1054 			{
  1055 				n = 0;
  1056 				do
  1057 				{
  1058 					n = 10 * n + (ch - '0');
  1059 					ch = *fmt++;
  1060 				} while(is_digit(ch));
  1061 				width = n;
  1062 				goto reswitch;
  1063 			}
  1064 		case 'c':
  1065 			{
  1066 				CHECK_ARGS(0);
  1067 				if (!llen)
  1068 				{
  1069 					goto done;
  1070 				}
  1071 				char *c;
  1072 				pCtx->LocalToString(params[arg], &c);
  1073 				*buf_p++ = *c;
  1074 				llen--;
  1075 				arg++;
  1076 				break;
  1077 			}
  1078 		case 'b':
  1079 			{
  1080 				CHECK_ARGS(0);
  1081 				cell_t *value;
  1082 				pCtx->LocalToPhysAddr(params[arg], &value);
  1083 				AddBinary(&buf_p, llen, *value, width, flags);
  1084 				arg++;
  1085 				break;
  1086 			}
  1087 		case 'd':
  1088 		case 'i':
  1089 			{
  1090 				CHECK_ARGS(0);
  1091 				cell_t *value;
  1092 				pCtx->LocalToPhysAddr(params[arg], &value);
  1093 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
  1094 				arg++;
  1095 				break;
  1096 			}
  1097 		case 'u':
  1098 			{
  1099 				CHECK_ARGS(0);
  1100 				cell_t *value;
  1101 				pCtx->LocalToPhysAddr(params[arg], &value);
  1102 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
  1103 				arg++;
  1104 				break;
  1105 			}
  1106 		case 'f':
  1107 			{
  1108 				CHECK_ARGS(0);
  1109 				cell_t *value;
  1110 				pCtx->LocalToPhysAddr(params[arg], &value);
  1111 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
  1112 				arg++;
  1113 				break;
  1114 			}
  1115 		case 'L':
  1116 			{
  1117 				CHECK_ARGS(0);
  1118 				cell_t *value;
  1119 				pCtx->LocalToPhysAddr(params[arg], &value);
  1120 				char buffer[255];
  1121 				if (*value)
  1122 				{
  1123 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
  1124 					if (!player || !player->IsConnected())
  1125 					{
  1126 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
  1127 					}
  1128 					const char *auth = player->GetAuthString();
  1129 					if (!auth || auth[0] == '\0')
  1130 					{
  1131 						auth = "STEAM_ID_PENDING";
  1132 					}
  1133 					int userid = GetPlayerUserId(player->GetEdict());
  1134 					UTIL_Format(buffer, 
  1135 						sizeof(buffer), 
  1136 						"%s<%d><%s><>", 
  1137 						player->GetName(),
  1138 						userid,
  1139 						auth);
  1140 				}
  1141 				else
  1142 				{
  1143 					UTIL_Format(buffer,
  1144 						sizeof(buffer),
  1145 						"Console<0><Console><Console>");
  1146 				}
  1147 				AddString(&buf_p, llen, buffer, width, prec);
  1148 				arg++;
  1149 				break;
  1150 			}
  1151 		case 'N':
  1152 			{
  1153 				CHECK_ARGS(0);
  1154 				cell_t *value;
  1155 				pCtx->LocalToPhysAddr(params[arg], &value);
  1156 
  1157 				const char *name = "Console";
  1158 				if (*value)
  1159 				{
  1160 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
  1161 					if (!player || !player->IsConnected())
  1162 					{
  1163 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
  1164 					}
  1165 					name = player->GetName();
  1166 				}
  1167 				AddString(&buf_p, llen, name, width, prec);
  1168 				arg++;
  1169 				break;
  1170 			}
  1171 		case 's':
  1172 			{
  1173 				CHECK_ARGS(0);
  1174 				char *str;
  1175 				int err;
  1176 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
  1177 				{
  1178 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
  1179 					return 0;
  1180 				}
  1181 				AddString(&buf_p, llen, str, width, prec);
  1182 				arg++;
  1183 				break;
  1184 			}
  1185 		case 'T':
  1186 			{
  1187 				CHECK_ARGS(1);
  1188 				char *key;
  1189 				bool error;
  1190 				size_t res;
  1191 				cell_t *target;
  1192 				pCtx->LocalToString(params[arg++], &key);
  1193 				pCtx->LocalToPhysAddr(params[arg++], &target);
  1194 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
  1195 				if (error)
  1196 				{
  1197 					return 0;
  1198 				}
  1199 				buf_p += res;
  1200 				llen -= res;
  1201 				break;
  1202 			}
  1203 		case 't':
  1204 			{
  1205 				CHECK_ARGS(0);
  1206 				char *key;
  1207 				bool error;
  1208 				size_t res;
  1209 				cell_t target = g_SourceMod.GetGlobalTarget();
  1210 				pCtx->LocalToString(params[arg++], &key);
  1211 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
  1212 				if (error)
  1213 				{
  1214 					return 0;
  1215 				}
  1216 				buf_p += res;
  1217 				llen -= res;
  1218 				break;
  1219 			}
  1220 		case 'X':
  1221 			{
  1222 				CHECK_ARGS(0);
  1223 				cell_t *value;
  1224 				pCtx->LocalToPhysAddr(params[arg], &value);
  1225 				flags |= UPPERDIGITS;
  1226 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
  1227 				arg++;
  1228 				break;
  1229 			}
  1230 		case 'x':
  1231 			{
  1232 				CHECK_ARGS(0);
  1233 				cell_t *value;
  1234 				pCtx->LocalToPhysAddr(params[arg], &value);
  1235 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
  1236 				arg++;
  1237 				break;
  1238 			}
  1239 		case '%':
  1240 			{
  1241 				if (!llen)
  1242 				{
  1243 					goto done;
  1244 				}
  1245 				*buf_p++ = ch;
  1246 				llen--;
  1247 				break;
  1248 			}
  1249 		case '\0':
  1250 			{
  1251 				if (!llen)
  1252 				{
  1253 					goto done;
  1254 				}
  1255 				*buf_p++ = '%';
  1256 				llen--;
  1257 				goto done;
  1258 			}
  1259 		default:
  1260 			{
  1261 				if (!llen)
  1262 				{
  1263 					goto done;
  1264 				}
  1265 				*buf_p++ = ch;
  1266 				llen--;
  1267 				break;
  1268 			}
  1269 		}
  1270 	}
  1271 
  1272 done:
  1273 	*buf_p = '\0';
  1274 	*param = arg;
  1275 	return (maxlen - llen - 1);
  1276 }
  1277 
  1278 unsigned int strncopy(char *dest, const char *src, size_t count)
  1279 {
  1280 	if (!count)
  1281 	{
  1282 		return 0;
  1283 	}
  1284 
  1285 	char *start = dest;
  1286 	while ((*src) && (--count))
  1287 	{
  1288 		*dest++ = *src++;
  1289 	}
  1290 	*dest = '\0';
  1291 
  1292 	return (dest - start);
  1293 }
  1294 
  1295 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
  1296 {
  1297 	va_list ap;
  1298 	va_start(ap, fmt);
  1299 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1300 	va_end(ap);
  1301 
  1302 	if (len >= maxlength)
  1303 	{
  1304 		buffer[maxlength - 1] = '\0';
  1305 		return (maxlength - 1);
  1306 	}
  1307 	else
  1308 	{
  1309 		return len;
  1310 	}
  1311 }
  1312 
  1313 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
  1314 {
  1315 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1316 
  1317 	if (len >= maxlength)
  1318 	{
  1319 		buffer[maxlength - 1] = '\0';
  1320 		return (maxlength - 1);
  1321 	}
  1322 	else
  1323 	{
  1324 		return len;
  1325 	}
  1326 }
  1327 
  1328 char *sm_strdup(const char *str)
  1329 {
  1330 	char *ptr = new char[strlen(str)+1];
  1331 	strcpy(ptr, str);
  1332 	return ptr;
  1333 }
  1334 
  1335 char *UTIL_TrimWhitespace(char *str, size_t &len)
  1336 {
  1337 	char *end = str + len - 1;
  1338 
  1339 	if (!len)
  1340 	{
  1341 		return str;
  1342 	}
  1343 
  1344 	/* Iterate backwards through string until we reach first non-whitespace char */
  1345 	while (end >= str && textparsers->IsWhitespace(end))
  1346 	{
  1347 		end--;
  1348 		len--;
  1349 	}
  1350 
  1351 	/* Replace first whitespace char (at the end) with null terminator.
  1352 	 * If there is none, we're just replacing the null terminator. 
  1353 	 */
  1354 	*(end + 1) = '\0';
  1355 
  1356 	while (*str != '\0' && textparsers->IsWhitespace(str))
  1357 	{
  1358 		str++;
  1359 		len--;
  1360 	}
  1361 
  1362 	return str;
  1363 }
  1364 
  1365 char *UTIL_ToLowerCase(const char *str)
  1366 {
  1367 	size_t len = strlen(str);
  1368 	char *buffer = new char[len + 1];
  1369 	for (size_t i = 0; i < len; i++)
  1370 	{
  1371 		if (str[i] >= 'A' && str[i] <= 'Z')
  1372 			buffer[i] = tolower(str[i]);
  1373 		else
  1374 			buffer[i] = str[i];
  1375 	}
  1376 	buffer[len] = '\0';
  1377 	return buffer;
  1378 }
  1379