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