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