--HG-- rename : core/GameConfigs.cpp => core/logic/GameConfigs.cpp rename : core/GameConfigs.h => core/logic/GameConfigs.h rename : core/smn_gameconfigs.cpp => core/logic/smn_gameconfigs.cpp
		
			
				
	
	
		
			1379 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1379 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * vim: set ts=4 :
 | |
|  * =============================================================================
 | |
|  * SourceMod
 | |
|  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
 | |
|  * =============================================================================
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it under
 | |
|  * the terms of the GNU General Public License, version 3.0, as published by the
 | |
|  * Free Software Foundation.
 | |
|  * 
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * As a special exception, AlliedModders LLC gives you permission to link the
 | |
|  * code of this program (as well as its derivative works) to "Half-Life 2," the
 | |
|  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | |
|  * by the Valve Corporation.  You must obey the GNU General Public License in
 | |
|  * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | |
|  * this exception to all derivative works.  AlliedModders LLC defines further
 | |
|  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | |
|  * or <http://www.sourcemod.net/license.php>.
 | |
|  *
 | |
|  * Version: $Id$
 | |
|  */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <stdarg.h>
 | |
| #include "sm_stringutil.h"
 | |
| #include "Logger.h"
 | |
| #include "PluginSys.h"
 | |
| #include "PlayerManager.h"
 | |
| #include "logic_bridge.h"
 | |
| 
 | |
| #define LADJUST			0x00000004		/* left adjustment */
 | |
| #define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
 | |
| #define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
 | |
| #define to_digit(c)		((c) - '0')
 | |
| #define is_digit(c)		((unsigned)to_digit(c) <= 9)
 | |
| 
 | |
| #define CHECK_ARGS(x) \
 | |
| 	if ((arg+x) > args) { \
 | |
| 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
 | |
| 		return 0; \
 | |
| 	}
 | |
| 
 | |
| inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
 | |
| {
 | |
| 	cell_t new_params[MAX_TRANSLATE_PARAMS];
 | |
| 	for (unsigned int i = 0; i < pTrans->fmt_count; i++)
 | |
| 	{
 | |
| 		new_params[i] = params[pTrans->fmt_order[i]];
 | |
| 	}
 | |
| 	memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
 | |
| }
 | |
| 
 | |
| size_t Translate(char *buffer,
 | |
| 				 size_t maxlen,
 | |
| 				 IPluginContext *pCtx,
 | |
| 				 const char *key,
 | |
| 				 cell_t target,
 | |
| 				 const cell_t *params,
 | |
| 				 int *arg,
 | |
| 				 bool *error)
 | |
| {
 | |
| 	unsigned int langid;
 | |
| 	*error = false;
 | |
| 	Translation pTrans;
 | |
| 	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
 | |
| 	unsigned int max_params = 0;
 | |
| 	IPhraseCollection *pPhrases;
 | |
| 
 | |
| 	pPhrases = pl->GetPhrases();
 | |
| 
 | |
| try_serverlang:
 | |
| 	if (target == SOURCEMOD_SERVER_LANGUAGE)
 | |
| 	{
 | |
| 		langid = translator->GetServerLanguage();
 | |
|  	}
 | |
| 	else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
 | |
| 	{
 | |
| 		langid = translator->GetClientLanguage(target);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
 | |
| 		goto error_out;
 | |
| 	}
 | |
| 
 | |
| 	if (pPhrases->FindTranslation(key, langid, &pTrans) != Trans_Okay)
 | |
| 	{
 | |
| 		if (target != SOURCEMOD_SERVER_LANGUAGE && langid != translator->GetServerLanguage())
 | |
| 		{
 | |
| 			target = SOURCEMOD_SERVER_LANGUAGE;
 | |
| 			goto try_serverlang;
 | |
| 		}
 | |
| 		else if (langid != SOURCEMOD_LANGUAGE_ENGLISH)
 | |
| 		{
 | |
| 			if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &pTrans) != Trans_Okay)
 | |
| 			{
 | |
| 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
 | |
| 				goto error_out;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
 | |
| 			goto error_out;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	max_params = pTrans.fmt_count;
 | |
| 
 | |
| 	if (max_params)
 | |
| 	{
 | |
| 		cell_t new_params[MAX_TRANSLATE_PARAMS];
 | |
| 
 | |
| 		/* Check if we're going to over the limit */
 | |
| 		if ((*arg) + (max_params - 1) > (size_t)params[0])
 | |
| 		{
 | |
| 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
 | |
| 				"Translation string formatted incorrectly - missing at least %d parameters", 
 | |
| 				((*arg + (max_params - 1)) - params[0])
 | |
| 				);
 | |
| 			goto error_out;
 | |
| 		}
 | |
| 
 | |
| 		/* If we need to re-order the parameters, do so with a temporary array.
 | |
| 		 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
 | |
| 		 */
 | |
| 		memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
 | |
| 		ReorderTranslationParams(&pTrans, &new_params[*arg]);
 | |
| 
 | |
| 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
 | |
| 	}
 | |
| 	
 | |
| error_out:
 | |
| 	*error = true;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
 | |
| {
 | |
| 	int size = 0;
 | |
| 	char *buf;
 | |
| 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
 | |
| 
 | |
| 	buf = *buf_p;
 | |
| 
 | |
| 	if (string == NULL)
 | |
| 	{
 | |
| 		string = nlstr;
 | |
| 		prec = -1;
 | |
| 	}
 | |
| 
 | |
| 	if (prec >= 0)
 | |
| 	{
 | |
| 		for (size = 0; size < prec; size++) 
 | |
| 		{
 | |
| 			if (string[size] == '\0')
 | |
| 			{
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		while (string[size++]);
 | |
| 		size--;
 | |
| 	}
 | |
| 
 | |
| 	if (size > (int)maxlen)
 | |
| 	{
 | |
| 		size = maxlen;
 | |
| 	}
 | |
| 
 | |
| 	maxlen -= size;
 | |
| 	width -= size;
 | |
| 
 | |
| 	while (size--)
 | |
| 	{
 | |
| 		*buf++ = *string++;
 | |
| 	}
 | |
| 
 | |
| 	while ((width-- > 0) && maxlen)
 | |
| 	{
 | |
| 		*buf++ = ' ';
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
 | |
| {
 | |
| 	int digits;					// non-fraction part digits
 | |
| 	double tmp;					// temporary
 | |
| 	char *buf = *buf_p;			// output buffer pointer
 | |
| 	int val;					// temporary
 | |
| 	int sign = 0;				// 0: positive, 1: negative
 | |
| 	int fieldlength;			// for padding
 | |
| 	int significant_digits = 0;	// number of significant digits written
 | |
| 	const int MAX_SIGNIFICANT_DIGITS = 16;
 | |
| 
 | |
| 	// default precision
 | |
| 	if (prec < 0)
 | |
| 	{
 | |
| 		prec = 6;
 | |
| 	}
 | |
| 
 | |
| 	// get the sign
 | |
| 	if (fval < 0)
 | |
| 	{
 | |
| 		fval = -fval;
 | |
| 		sign = 1;
 | |
| 	}
 | |
| 
 | |
| 	// compute whole-part digits count
 | |
| 	digits = (int)log10(fval) + 1;
 | |
| 
 | |
| 	// Only print 0.something if 0 < fval < 1
 | |
| 	if (digits < 1)
 | |
| 	{
 | |
| 		digits = 1;
 | |
| 	}
 | |
| 
 | |
| 	// compute the field length
 | |
| 	fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
 | |
| 
 | |
| 	// minus sign BEFORE left padding if padding with zeros
 | |
| 	if (sign && maxlen && (flags & ZEROPAD))
 | |
| 	{
 | |
| 		*buf++ = '-';
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	// right justify if required
 | |
| 	if ((flags & LADJUST) == 0)
 | |
| 	{
 | |
| 		while ((fieldlength < width) && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// minus sign AFTER left padding if padding with spaces
 | |
| 	if (sign && maxlen && !(flags & ZEROPAD))
 | |
| 	{
 | |
| 		*buf++ = '-';
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	// write the whole part
 | |
| 	tmp = pow(10.0, digits-1);
 | |
| 	while ((digits--) && maxlen)
 | |
| 	{
 | |
| 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
 | |
| 		{
 | |
| 			*buf++ = '0';
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			val = (int)(fval / tmp);
 | |
| 			*buf++ = '0' + val;
 | |
| 			fval -= val * tmp;
 | |
| 			tmp *= 0.1;
 | |
| 		}
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	// write the fraction part
 | |
| 	if (maxlen && prec)
 | |
| 	{
 | |
| 		*buf++ = '.';
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	tmp = pow(10.0, prec);
 | |
| 
 | |
| 	fval *= tmp;
 | |
| 	while (prec-- && maxlen)
 | |
| 	{
 | |
| 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
 | |
| 		{
 | |
| 			*buf++ = '0';
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			tmp *= 0.1;
 | |
| 			val = (int)(fval / tmp);
 | |
| 			*buf++ = '0' + val;
 | |
| 			fval -= val * tmp;
 | |
| 		}
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	// left justify if required
 | |
| 	if (flags & LADJUST)
 | |
| 	{
 | |
| 		while ((fieldlength < width) && maxlen)
 | |
| 		{
 | |
| 			// right-padding only with spaces, ZEROPAD is ignored
 | |
| 			*buf++ = ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// update parent's buffer pointer
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| void AddBinary(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
 | |
| {
 | |
| 	char text[32];
 | |
| 	int digits;
 | |
| 	char *buf;
 | |
| 
 | |
| 	digits = 0;
 | |
| 	do
 | |
| 	{
 | |
| 		if (val & 1)
 | |
| 		{
 | |
| 			text[digits++] = '1';
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			text[digits++] = '0';
 | |
| 		}
 | |
| 		val >>= 1;
 | |
| 	} while (val);
 | |
| 
 | |
| 	buf = *buf_p;
 | |
| 
 | |
| 	if (!(flags & LADJUST))
 | |
| 	{
 | |
| 		while (digits < width && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	while (digits-- && maxlen)
 | |
| 	{
 | |
| 		*buf++ = text[digits];
 | |
| 		width--;
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & LADJUST)
 | |
| 	{
 | |
| 		while (width-- && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
 | |
| {
 | |
| 	char text[32];
 | |
| 	int digits;
 | |
| 	char *buf;
 | |
| 
 | |
| 	digits = 0;
 | |
| 	do
 | |
| 	{
 | |
| 		text[digits++] = '0' + val % 10;
 | |
| 		val /= 10;
 | |
| 	} while (val);
 | |
| 
 | |
| 	buf = *buf_p;
 | |
| 
 | |
| 	if (!(flags & LADJUST))
 | |
| 	{
 | |
| 		while (digits < width && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	while (digits-- && maxlen)
 | |
| 	{
 | |
| 		*buf++ = text[digits];
 | |
| 		width--;
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & LADJUST)
 | |
| 	{
 | |
| 		while (width-- && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
 | |
| {
 | |
| 	char text[32];
 | |
| 	int digits;
 | |
| 	int signedVal;
 | |
| 	char *buf;
 | |
| 	unsigned int unsignedVal;
 | |
| 
 | |
| 	digits = 0;
 | |
| 	signedVal = val;
 | |
| 	if (val < 0)
 | |
| 	{
 | |
| 		/* we want the unsigned version */
 | |
| 		unsignedVal = abs(val);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		unsignedVal = val;
 | |
| 	}
 | |
| 
 | |
| 	do
 | |
| 	{
 | |
| 		text[digits++] = '0' + unsignedVal % 10;
 | |
| 		unsignedVal /= 10;
 | |
| 	} while (unsignedVal);
 | |
| 
 | |
| 	if (signedVal < 0)
 | |
| 	{
 | |
| 		text[digits++] = '-';
 | |
| 	}
 | |
| 
 | |
| 	buf = *buf_p;
 | |
| 
 | |
| 	if (!(flags & LADJUST))
 | |
| 	{
 | |
| 		while ((digits < width) && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	while (digits-- && maxlen)
 | |
| 	{
 | |
| 		*buf++ = text[digits];
 | |
| 		width--;
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & LADJUST)
 | |
| 	{
 | |
| 		while (width-- && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
 | |
| {
 | |
| 	char text[32];
 | |
| 	int digits;
 | |
| 	char *buf;
 | |
| 	char digit;
 | |
| 	int hexadjust;
 | |
| 
 | |
| 	if (flags & UPPERDIGITS)
 | |
| 	{
 | |
| 		hexadjust = 'A' - '9' - 1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		hexadjust = 'a' - '9' - 1;
 | |
| 	}
 | |
| 
 | |
| 	digits = 0;
 | |
| 	do 
 | |
| 	{
 | |
| 		digit = ('0' + val % 16);
 | |
| 		if (digit > '9')
 | |
| 		{
 | |
| 			digit += hexadjust;
 | |
| 		}
 | |
| 
 | |
| 		text[digits++] = digit;
 | |
| 		val /= 16;
 | |
| 	} while(val);
 | |
| 
 | |
| 	buf = *buf_p;
 | |
| 
 | |
| 	if (!(flags & LADJUST))
 | |
| 	{
 | |
| 		while (digits < width && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			width--;
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	while (digits-- && maxlen)
 | |
| 	{
 | |
| 		*buf++ = text[digits];
 | |
| 		width--;
 | |
| 		maxlen--;
 | |
| 	}
 | |
| 
 | |
| 	if (flags & LADJUST)
 | |
| 	{
 | |
| 		while (width-- && maxlen)
 | |
| 		{
 | |
| 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
 | |
| 			maxlen--;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*buf_p = buf;
 | |
| }
 | |
| 
 | |
| bool gnprintf(char *buffer,
 | |
| 			  size_t maxlen,
 | |
| 			  const char *format,
 | |
| 			  IPhraseCollection *pPhrases,
 | |
| 			  void **params,
 | |
| 			  unsigned int numparams,
 | |
| 			  unsigned int &curparam,
 | |
| 			  size_t *pOutLength,
 | |
| 			  const char **pFailPhrase)
 | |
| {
 | |
| 	if (!buffer || !maxlen)
 | |
| 	{
 | |
| 		if (pOutLength != NULL)
 | |
| 		{
 | |
| 			*pOutLength = 0;
 | |
| 		}
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	if (numparams > MAX_TRANSLATE_PARAMS)
 | |
| 	{
 | |
| 		if (pFailPhrase != NULL)
 | |
| 		{
 | |
| 			*pFailPhrase = NULL;
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	int arg = 0;
 | |
| 	char *buf_p;
 | |
| 	char ch;
 | |
| 	int flags;
 | |
| 	int width;
 | |
| 	int prec;
 | |
| 	int n;
 | |
| 	char sign;
 | |
| 	const char *fmt;
 | |
| 	size_t llen = maxlen - 1;
 | |
| 
 | |
| 	buf_p = buffer;
 | |
| 	fmt = format;
 | |
| 
 | |
| 	while (true)
 | |
| 	{
 | |
| 		// run through the format string until we hit a '%' or '\0'
 | |
| 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
 | |
| 		{
 | |
| 			*buf_p++ = ch;
 | |
| 			llen--;
 | |
| 		}
 | |
| 		if ((ch == '\0') || (llen <= 0))
 | |
| 		{
 | |
| 			goto done;
 | |
| 		}
 | |
| 
 | |
| 		// skip over the '%'
 | |
| 		fmt++;
 | |
| 
 | |
| 		// reset formatting state
 | |
| 		flags = 0;
 | |
| 		width = 0;
 | |
| 		prec = -1;
 | |
| 		sign = '\0';
 | |
| 
 | |
| rflag:
 | |
| 		ch = *fmt++;
 | |
| reswitch:
 | |
| 		switch(ch)
 | |
| 		{
 | |
| 		case '-':
 | |
| 			{
 | |
| 				flags |= LADJUST;
 | |
| 				goto rflag;
 | |
| 			}
 | |
| 		case '.':
 | |
| 			{
 | |
| 				n = 0;
 | |
| 				while(is_digit((ch = *fmt++)))
 | |
| 				{
 | |
| 					n = 10 * n + (ch - '0');
 | |
| 				}
 | |
| 				prec = (n < 0) ? -1 : n;
 | |
| 				goto reswitch;
 | |
| 			}
 | |
| 		case '0':
 | |
| 			{
 | |
| 				flags |= ZEROPAD;
 | |
| 				goto rflag;
 | |
| 			}
 | |
| 		case '1':
 | |
| 		case '2':
 | |
| 		case '3':
 | |
| 		case '4':
 | |
| 		case '5':
 | |
| 		case '6':
 | |
| 		case '7':
 | |
| 		case '8':
 | |
| 		case '9':
 | |
| 			{
 | |
| 				n = 0;
 | |
| 				do
 | |
| 				{
 | |
| 					n = 10 * n + (ch - '0');
 | |
| 					ch = *fmt++;
 | |
| 				} while(is_digit(ch));
 | |
| 				width = n;
 | |
| 				goto reswitch;
 | |
| 			}
 | |
| 		case 'c':
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				char *c = (char *)params[curparam];
 | |
| 				curparam++;
 | |
| 				*buf_p++ = *c;
 | |
| 				llen--;
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'b':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				int *value = (int *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddBinary(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'd':
 | |
| 		case 'i':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				int *value = (int *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddInt(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'u':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				unsigned int *value = (unsigned int *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddUInt(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'f':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				float *value = (float *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddFloat(&buf_p, llen, *value, width, prec, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 's':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				const char *str = (const char *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddString(&buf_p, llen, str, width, prec);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'T':
 | |
| 		case 't':
 | |
| 			{
 | |
| 				int target;
 | |
| 				const char *key;
 | |
| 				size_t out_length;
 | |
| 				Translation trans;
 | |
| 				unsigned int lang_id;
 | |
| 
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				key = (const char *)(params[curparam]);
 | |
| 				curparam++;
 | |
| 
 | |
| 				if (ch == 'T')
 | |
| 				{
 | |
| 					if (curparam >= numparams)
 | |
| 					{
 | |
| 						if (pFailPhrase != NULL)
 | |
| 						{
 | |
| 							*pFailPhrase = NULL;
 | |
| 						}
 | |
| 						return false;
 | |
| 					}
 | |
| 					target = *((int *)(params[curparam]));
 | |
| 					curparam++;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					target = translator->GetGlobalTarget();
 | |
| 				}
 | |
| 
 | |
| try_again:
 | |
| 				if (target == SOURCEMOD_SERVER_LANGUAGE)
 | |
| 				{
 | |
| 					lang_id = translator->GetServerLanguage();
 | |
| 				}
 | |
| 				else if (target >= 1 && target <= g_Players.GetMaxClients())
 | |
| 				{
 | |
| 					lang_id = translator->GetClientLanguage(target);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					lang_id = translator->GetServerLanguage();
 | |
| 				}
 | |
| 
 | |
| 				if (pPhrases == NULL)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = key;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				if (pPhrases->FindTranslation(key, lang_id, &trans) != Trans_Okay)
 | |
| 				{
 | |
| 					if (target != SOURCEMOD_SERVER_LANGUAGE && lang_id != translator->GetServerLanguage())
 | |
| 					{
 | |
| 						target = SOURCEMOD_SERVER_LANGUAGE;
 | |
| 						goto try_again;
 | |
| 					}
 | |
| 					else if (lang_id != SOURCEMOD_LANGUAGE_ENGLISH)
 | |
| 					{
 | |
| 						if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &trans) != Trans_Okay)
 | |
| 						{
 | |
| 							if (pFailPhrase != NULL)
 | |
| 							{
 | |
| 								*pFailPhrase = key;
 | |
| 							}
 | |
| 							return false;
 | |
| 						}
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						if (pFailPhrase != NULL)
 | |
| 						{
 | |
| 							*pFailPhrase = key;
 | |
| 						}
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (trans.fmt_count)
 | |
| 				{
 | |
| 					unsigned int i;
 | |
| 					void *new_params[MAX_TRANSLATE_PARAMS];
 | |
| 
 | |
| 					if (curparam + trans.fmt_count > numparams)
 | |
| 					{
 | |
| 						if (pFailPhrase != NULL)
 | |
| 						{
 | |
| 							*pFailPhrase = NULL;
 | |
| 						}
 | |
| 						return false;
 | |
| 					}
 | |
| 
 | |
| 					/* Copy the array and re-order the stack */
 | |
| 					memcpy(new_params, params, sizeof(void *) * numparams);
 | |
| 					for (i = 0; i < trans.fmt_count; i++)
 | |
| 					{
 | |
| 						new_params[curparam + i] = const_cast<void *>(params[curparam + trans.fmt_order[i]]);
 | |
| 					}
 | |
| 
 | |
| 					if (!gnprintf(buf_p,
 | |
| 							llen,
 | |
| 							trans.szPhrase,
 | |
| 							pPhrases,
 | |
| 							new_params,
 | |
| 							numparams,
 | |
| 							curparam,
 | |
| 							&out_length,
 | |
| 							pFailPhrase))
 | |
| 					{
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if (!gnprintf(buf_p,
 | |
| 						llen,
 | |
| 						trans.szPhrase,
 | |
| 						pPhrases,
 | |
| 						params,
 | |
| 						numparams,
 | |
| 						curparam,
 | |
| 						&out_length,
 | |
| 						pFailPhrase))
 | |
| 					{
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				buf_p += out_length;
 | |
| 				llen -= out_length;
 | |
| 
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'X':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				unsigned int *value = (unsigned int *)params[curparam];
 | |
| 				curparam++;
 | |
| 				flags |= UPPERDIGITS;
 | |
| 				AddHex(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'x':
 | |
| 			{
 | |
| 				if (curparam >= numparams)
 | |
| 				{
 | |
| 					if (pFailPhrase != NULL)
 | |
| 					{
 | |
| 						*pFailPhrase = NULL;
 | |
| 					}
 | |
| 					return false;
 | |
| 				}
 | |
| 				unsigned int *value = (unsigned int *)params[curparam];
 | |
| 				curparam++;
 | |
| 				AddHex(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case '%':
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = ch;
 | |
| 				llen--;
 | |
| 				break;
 | |
| 			}
 | |
| 		case '\0':
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = '%';
 | |
| 				llen--;
 | |
| 				goto done;
 | |
| 			}
 | |
| 		default:
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = ch;
 | |
| 				llen--;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	*buf_p = '\0';
 | |
| 
 | |
| 	if (pOutLength != NULL)
 | |
| 	{
 | |
| 		*pOutLength = (maxlen - llen - 1);
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
 | |
| {
 | |
| 	if (!buffer || !maxlen)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	int arg;
 | |
| 	int args = params[0];
 | |
| 	char *buf_p;
 | |
| 	char ch;
 | |
| 	int flags;
 | |
| 	int width;
 | |
| 	int prec;
 | |
| 	int n;
 | |
| 	char sign;
 | |
| 	const char *fmt;
 | |
| 	size_t llen = maxlen - 1;
 | |
| 
 | |
| 	buf_p = buffer;
 | |
| 	arg = *param;
 | |
| 	fmt = format;
 | |
| 
 | |
| 	while (true)
 | |
| 	{
 | |
| 		// run through the format string until we hit a '%' or '\0'
 | |
| 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
 | |
| 		{
 | |
| 			*buf_p++ = ch;
 | |
| 			llen--;
 | |
| 		}
 | |
| 		if ((ch == '\0') || (llen <= 0))
 | |
| 		{
 | |
| 			goto done;
 | |
| 		}
 | |
| 
 | |
| 		// skip over the '%'
 | |
| 		fmt++;
 | |
| 
 | |
| 		// reset formatting state
 | |
| 		flags = 0;
 | |
| 		width = 0;
 | |
| 		prec = -1;
 | |
| 		sign = '\0';
 | |
| 
 | |
| rflag:
 | |
| 		ch = *fmt++;
 | |
| reswitch:
 | |
| 		switch(ch)
 | |
| 		{
 | |
| 		case '-':
 | |
| 			{
 | |
| 				flags |= LADJUST;
 | |
| 				goto rflag;
 | |
| 			}
 | |
| 		case '.':
 | |
| 			{
 | |
| 				n = 0;
 | |
| 				while(is_digit((ch = *fmt++)))
 | |
| 				{
 | |
| 					n = 10 * n + (ch - '0');
 | |
| 				}
 | |
| 				prec = (n < 0) ? -1 : n;
 | |
| 				goto reswitch;
 | |
| 			}
 | |
| 		case '0':
 | |
| 			{
 | |
| 				flags |= ZEROPAD;
 | |
| 				goto rflag;
 | |
| 			}
 | |
| 		case '1':
 | |
| 		case '2':
 | |
| 		case '3':
 | |
| 		case '4':
 | |
| 		case '5':
 | |
| 		case '6':
 | |
| 		case '7':
 | |
| 		case '8':
 | |
| 		case '9':
 | |
| 			{
 | |
| 				n = 0;
 | |
| 				do
 | |
| 				{
 | |
| 					n = 10 * n + (ch - '0');
 | |
| 					ch = *fmt++;
 | |
| 				} while(is_digit(ch));
 | |
| 				width = n;
 | |
| 				goto reswitch;
 | |
| 			}
 | |
| 		case 'c':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				char *c;
 | |
| 				pCtx->LocalToString(params[arg], &c);
 | |
| 				*buf_p++ = *c;
 | |
| 				llen--;
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'b':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				AddBinary(&buf_p, llen, *value, width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'd':
 | |
| 		case 'i':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'u':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'f':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'L':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				char buffer[255];
 | |
| 				if (*value)
 | |
| 				{
 | |
| 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
 | |
| 					if (!player || !player->IsConnected())
 | |
| 					{
 | |
| 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
 | |
| 					}
 | |
| 					const char *auth = player->GetAuthString();
 | |
| 					if (!auth || auth[0] == '\0')
 | |
| 					{
 | |
| 						auth = "STEAM_ID_PENDING";
 | |
| 					}
 | |
| 					int userid = engine->GetPlayerUserId(player->GetEdict());
 | |
| 					UTIL_Format(buffer, 
 | |
| 						sizeof(buffer), 
 | |
| 						"%s<%d><%s><>", 
 | |
| 						player->GetName(),
 | |
| 						userid,
 | |
| 						auth);
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					UTIL_Format(buffer,
 | |
| 						sizeof(buffer),
 | |
| 						"Console<0><Console><Console>");
 | |
| 				}
 | |
| 				AddString(&buf_p, llen, buffer, width, prec);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'N':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 
 | |
| 				const char *name = "Console";
 | |
| 				if (*value)
 | |
| 				{
 | |
| 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
 | |
| 					if (!player || !player->IsConnected())
 | |
| 					{
 | |
| 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
 | |
| 					}
 | |
| 					name = player->GetName();
 | |
| 				}
 | |
| 				AddString(&buf_p, llen, name, width, prec);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 's':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				char *str;
 | |
| 				int err;
 | |
| 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
 | |
| 				{
 | |
| 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
 | |
| 					return 0;
 | |
| 				}
 | |
| 				AddString(&buf_p, llen, str, width, prec);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'T':
 | |
| 			{
 | |
| 				CHECK_ARGS(1);
 | |
| 				char *key;
 | |
| 				bool error;
 | |
| 				size_t res;
 | |
| 				cell_t *target;
 | |
| 				pCtx->LocalToString(params[arg++], &key);
 | |
| 				pCtx->LocalToPhysAddr(params[arg++], &target);
 | |
| 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
 | |
| 				if (error)
 | |
| 				{
 | |
| 					return 0;
 | |
| 				}
 | |
| 				buf_p += res;
 | |
| 				llen -= res;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 't':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				char *key;
 | |
| 				bool error;
 | |
| 				size_t res;
 | |
| 				cell_t target = g_SourceMod.GetGlobalTarget();
 | |
| 				pCtx->LocalToString(params[arg++], &key);
 | |
| 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
 | |
| 				if (error)
 | |
| 				{
 | |
| 					return 0;
 | |
| 				}
 | |
| 				buf_p += res;
 | |
| 				llen -= res;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'X':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				flags |= UPPERDIGITS;
 | |
| 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case 'x':
 | |
| 			{
 | |
| 				CHECK_ARGS(0);
 | |
| 				cell_t *value;
 | |
| 				pCtx->LocalToPhysAddr(params[arg], &value);
 | |
| 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
 | |
| 				arg++;
 | |
| 				break;
 | |
| 			}
 | |
| 		case '%':
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = ch;
 | |
| 				llen--;
 | |
| 				break;
 | |
| 			}
 | |
| 		case '\0':
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = '%';
 | |
| 				llen--;
 | |
| 				goto done;
 | |
| 			}
 | |
| 		default:
 | |
| 			{
 | |
| 				if (!llen)
 | |
| 				{
 | |
| 					goto done;
 | |
| 				}
 | |
| 				*buf_p++ = ch;
 | |
| 				llen--;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	*buf_p = '\0';
 | |
| 	*param = arg;
 | |
| 	return (maxlen - llen - 1);
 | |
| }
 | |
| 
 | |
| unsigned int strncopy(char *dest, const char *src, size_t count)
 | |
| {
 | |
| 	if (!count)
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	char *start = dest;
 | |
| 	while ((*src) && (--count))
 | |
| 	{
 | |
| 		*dest++ = *src++;
 | |
| 	}
 | |
| 	*dest = '\0';
 | |
| 
 | |
| 	return (dest - start);
 | |
| }
 | |
| 
 | |
| size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
 | |
| {
 | |
| 	va_list ap;
 | |
| 	va_start(ap, fmt);
 | |
| 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
 | |
| 	va_end(ap);
 | |
| 
 | |
| 	if (len >= maxlength)
 | |
| 	{
 | |
| 		buffer[maxlength - 1] = '\0';
 | |
| 		return (maxlength - 1);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return len;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
 | |
| {
 | |
| 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
 | |
| 
 | |
| 	if (len >= maxlength)
 | |
| 	{
 | |
| 		buffer[maxlength - 1] = '\0';
 | |
| 		return (maxlength - 1);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return len;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| char *sm_strdup(const char *str)
 | |
| {
 | |
| 	char *ptr = new char[strlen(str)+1];
 | |
| 	strcpy(ptr, str);
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| char *UTIL_TrimWhitespace(char *str, size_t &len)
 | |
| {
 | |
| 	char *end = str + len - 1;
 | |
| 
 | |
| 	if (!len)
 | |
| 	{
 | |
| 		return str;
 | |
| 	}
 | |
| 
 | |
| 	/* Iterate backwards through string until we reach first non-whitespace char */
 | |
| 	while (end >= str && textparsers->IsWhitespace(end))
 | |
| 	{
 | |
| 		end--;
 | |
| 		len--;
 | |
| 	}
 | |
| 
 | |
| 	/* Replace first whitespace char (at the end) with null terminator.
 | |
| 	 * If there is none, we're just replacing the null terminator. 
 | |
| 	 */
 | |
| 	*(end + 1) = '\0';
 | |
| 
 | |
| 	while (*str != '\0' && textparsers->IsWhitespace(str))
 | |
| 	{
 | |
| 		str++;
 | |
| 		len--;
 | |
| 	}
 | |
| 
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| char *UTIL_ToLowerCase(const char *str)
 | |
| {
 | |
| 	size_t len = strlen(str);
 | |
| 	char *buffer = new char[len + 1];
 | |
| 	for (size_t i = 0; i < len; i++)
 | |
| 	{
 | |
| 		if (str[i] >= 'A' && str[i] <= 'Z')
 | |
| 			buffer[i] = tolower(str[i]);
 | |
| 		else
 | |
| 			buffer[i] = str[i];
 | |
| 	}
 | |
| 	buffer[len] = '\0';
 | |
| 	return buffer;
 | |
| }
 | |
| 
 |