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