core/sm_stringutil.cpp
author Matt Woodrow <pred@alliedmods.net>
Fri Aug 08 00:17:37 2008 +0000 (2008-08-08)
changeset 2224 676c3b826f13
parent 2112 2a83a521287c
child 2289 44045ead61cd
permissions -rw-r--r--
Initial Import of beta structs extension.
ds@1952
     1
/**
ds@1952
     2
 * vim: set ts=4 :
ds@1952
     3
 * =============================================================================
ds@1952
     4
 * SourceMod
ds@1952
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1952
     6
 * =============================================================================
ds@1952
     7
 *
ds@1952
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1952
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1952
    10
 * Free Software Foundation.
ds@1952
    11
 * 
ds@1952
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1952
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1952
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1952
    15
 * details.
ds@1952
    16
 *
ds@1952
    17
 * You should have received a copy of the GNU General Public License along with
ds@1952
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
ds@1952
    19
 *
ds@1952
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1952
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1952
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1952
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1952
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1952
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1952
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1952
    27
 * or <http://www.sourcemod.net/license.php>.
ds@1952
    28
 *
ds@1952
    29
 * Version: $Id$
ds@1952
    30
 */
ds@1952
    31
ds@1952
    32
#include <stdlib.h>
ds@1952
    33
#include <ctype.h>
ds@1952
    34
#include <stdarg.h>
ds@1952
    35
#include "sm_stringutil.h"
ds@1952
    36
#include "Logger.h"
ds@1952
    37
#include "PluginSys.h"
ds@1952
    38
#include "Translator.h"
ds@1952
    39
#include "PlayerManager.h"
ds@1952
    40
ds@1952
    41
#define LADJUST			0x00000004		/* left adjustment */
ds@1952
    42
#define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
ds@1952
    43
#define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
ds@1952
    44
#define to_digit(c)		((c) - '0')
ds@1952
    45
#define is_digit(c)		((unsigned)to_digit(c) <= 9)
ds@1952
    46
ds@1952
    47
#define CHECK_ARGS(x) \
ds@1952
    48
	if ((arg+x) > args) { \
ds@1952
    49
		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
ds@1952
    50
		return 0; \
ds@1952
    51
	}
ds@1952
    52
ds@1952
    53
inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
ds@1952
    54
{
ds@1952
    55
	cell_t new_params[MAX_TRANSLATE_PARAMS];
ds@1952
    56
	for (unsigned int i = 0; i < pTrans->fmt_count; i++)
ds@1952
    57
	{
ds@1952
    58
		new_params[i] = params[pTrans->fmt_order[i]];
ds@1952
    59
	}
ds@1952
    60
	memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
ds@1952
    61
}
ds@1952
    62
ds@1952
    63
size_t Translate(char *buffer,
ds@1952
    64
				 size_t maxlen,
ds@1952
    65
				 IPluginContext *pCtx,
ds@1952
    66
				 const char *key,
ds@1952
    67
				 cell_t target,
ds@1952
    68
				 const cell_t *params,
ds@1952
    69
				 int *arg,
ds@1952
    70
				 bool *error)
ds@1952
    71
{
ds@1952
    72
	unsigned int langid;
ds@1952
    73
	*error = false;
ds@1952
    74
	Translation pTrans;
ds@1952
    75
	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
ds@1952
    76
	unsigned int max_params = 0;
dvander@2042
    77
	IPhraseCollection *pPhrases;
dvander@2042
    78
dvander@2042
    79
	pPhrases = pl->GetPhrases();
ds@1952
    80
ds@1952
    81
try_serverlang:
dvander@2042
    82
	if (target == SOURCEMOD_SERVER_LANGUAGE)
ds@1952
    83
	{
ds@1952
    84
		langid = g_Translator.GetServerLanguage();
ds@1952
    85
 	}
ds@1952
    86
	else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
ds@1952
    87
	{
ds@1952
    88
		langid = g_Translator.GetClientLanguage(target);
ds@1952
    89
	}
ds@1952
    90
	else
ds@1952
    91
	{
ds@1952
    92
		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
ds@1952
    93
		goto error_out;
ds@1952
    94
	}
ds@1952
    95
dvander@2042
    96
	if (pPhrases->FindTranslation(key, langid, &pTrans) != Trans_Okay)
ds@1952
    97
	{
dvander@2042
    98
		if (target != SOURCEMOD_SERVER_LANGUAGE && langid != g_Translator.GetServerLanguage())
ds@1952
    99
		{
dvander@2042
   100
			target = SOURCEMOD_SERVER_LANGUAGE;
ds@1952
   101
			goto try_serverlang;
ds@1952
   102
		}
dvander@2042
   103
		else if (langid != SOURCEMOD_LANGUAGE_ENGLISH)
ds@1952
   104
		{
dvander@2042
   105
			if (!pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &pTrans))
ds@1952
   106
			{
ds@1952
   107
				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
ds@1952
   108
				goto error_out;
ds@1952
   109
			}
ds@1952
   110
		}
ds@1952
   111
		else
ds@1952
   112
		{
ds@1952
   113
			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
ds@1952
   114
			goto error_out;
ds@1952
   115
		}
ds@1952
   116
	}
ds@1952
   117
ds@1952
   118
	max_params = pTrans.fmt_count;
ds@1952
   119
ds@1952
   120
	if (max_params)
ds@1952
   121
	{
ds@1952
   122
		cell_t new_params[MAX_TRANSLATE_PARAMS];
ds@1952
   123
ds@1952
   124
		/* Check if we're going to over the limit */
ds@1952
   125
		if ((*arg) + (max_params - 1) > (size_t)params[0])
ds@1952
   126
		{
ds@1952
   127
			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
ds@1952
   128
				"Translation string formatted incorrectly - missing at least %d parameters", 
ds@1952
   129
				((*arg + (max_params - 1)) - params[0])
ds@1952
   130
				);
ds@1952
   131
			goto error_out;
ds@1952
   132
		}
ds@1952
   133
ds@1952
   134
		/* If we need to re-order the parameters, do so with a temporary array.
ds@1952
   135
		 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
ds@1952
   136
		 */
ds@1952
   137
		memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
ds@1952
   138
		ReorderTranslationParams(&pTrans, &new_params[*arg]);
ds@1952
   139
ds@1952
   140
		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
ds@1952
   141
	}
ds@1952
   142
	else
ds@1952
   143
	{
ds@1952
   144
		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
ds@1952
   145
	}
ds@1952
   146
	
ds@1952
   147
error_out:
ds@1952
   148
	*error = true;
ds@1952
   149
	return 0;
ds@1952
   150
}
ds@1952
   151
ds@1952
   152
void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
ds@1952
   153
{
ds@1952
   154
	int size = 0;
ds@1952
   155
	char *buf;
ds@1952
   156
	static char nlstr[] = {'(','n','u','l','l',')','\0'};
ds@1952
   157
ds@1952
   158
	buf = *buf_p;
ds@1952
   159
ds@1952
   160
	if (string == NULL)
ds@1952
   161
	{
ds@1952
   162
		string = nlstr;
ds@1952
   163
		prec = -1;
ds@1952
   164
	}
ds@1952
   165
ds@1952
   166
	if (prec >= 0)
ds@1952
   167
	{
ds@1952
   168
		for (size = 0; size < prec; size++) 
ds@1952
   169
		{
ds@1952
   170
			if (string[size] == '\0')
ds@1952
   171
			{
ds@1952
   172
				break;
ds@1952
   173
			}
ds@1952
   174
		}
ds@1952
   175
	}
ds@1952
   176
	else
ds@1952
   177
	{
ds@1952
   178
		while (string[size++]);
ds@1952
   179
		size--;
ds@1952
   180
	}
ds@1952
   181
ds@1952
   182
	if (size > (int)maxlen)
ds@1952
   183
	{
ds@1952
   184
		size = maxlen;
ds@1952
   185
	}
ds@1952
   186
ds@1952
   187
	maxlen -= size;
ds@1952
   188
	width -= size;
ds@1952
   189
ds@1952
   190
	while (size--)
ds@1952
   191
	{
ds@1952
   192
		*buf++ = *string++;
ds@1952
   193
	}
ds@1952
   194
ds@1952
   195
	while ((width-- > 0) && maxlen)
ds@1952
   196
	{
ds@1952
   197
		*buf++ = ' ';
ds@1952
   198
		maxlen--;
ds@1952
   199
	}
ds@1952
   200
ds@1952
   201
	*buf_p = buf;
ds@1952
   202
}
ds@1952
   203
ds@1952
   204
void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
ds@1952
   205
{
ds@1952
   206
	int digits;					// non-fraction part digits
ds@1952
   207
	double tmp;					// temporary
ds@1952
   208
	char *buf = *buf_p;			// output buffer pointer
ds@1952
   209
	int val;					// temporary
ds@1952
   210
	int sign = 0;				// 0: positive, 1: negative
ds@1952
   211
	int fieldlength;			// for padding
ds@1952
   212
	int significant_digits = 0;	// number of significant digits written
ds@1952
   213
	const int MAX_SIGNIFICANT_DIGITS = 16;
ds@1952
   214
ds@1952
   215
	// default precision
ds@1952
   216
	if (prec < 0)
ds@1952
   217
	{
ds@1952
   218
		prec = 6;
ds@1952
   219
	}
ds@1952
   220
ds@1952
   221
	// get the sign
ds@1952
   222
	if (fval < 0)
ds@1952
   223
	{
ds@1952
   224
		fval = -fval;
ds@1952
   225
		sign = 1;
ds@1952
   226
	}
ds@1952
   227
ds@1952
   228
	// compute whole-part digits count
ds@1952
   229
	digits = (int)log10(fval) + 1;
ds@1952
   230
ds@1952
   231
	// Only print 0.something if 0 < fval < 1
ds@1952
   232
	if (digits < 1)
ds@1952
   233
	{
ds@1952
   234
		digits = 1;
ds@1952
   235
	}
ds@1952
   236
ds@1952
   237
	// compute the field length
ds@1952
   238
	fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
ds@1952
   239
ds@1952
   240
	// minus sign BEFORE left padding if padding with zeros
ds@1952
   241
	if (sign && maxlen && (flags & ZEROPAD))
ds@1952
   242
	{
ds@1952
   243
		*buf++ = '-';
ds@1952
   244
		maxlen--;
ds@1952
   245
	}
ds@1952
   246
ds@1952
   247
	// right justify if required
ds@1952
   248
	if ((flags & LADJUST) == 0)
ds@1952
   249
	{
ds@1952
   250
		while ((fieldlength < width) && maxlen)
ds@1952
   251
		{
ds@1952
   252
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   253
			width--;
ds@1952
   254
			maxlen--;
ds@1952
   255
		}
ds@1952
   256
	}
ds@1952
   257
ds@1952
   258
	// minus sign AFTER left padding if padding with spaces
ds@1952
   259
	if (sign && maxlen && !(flags & ZEROPAD))
ds@1952
   260
	{
ds@1952
   261
		*buf++ = '-';
ds@1952
   262
		maxlen--;
ds@1952
   263
	}
ds@1952
   264
ds@1952
   265
	// write the whole part
ds@1952
   266
	tmp = pow(10.0, digits-1);
ds@1952
   267
	while ((digits--) && maxlen)
ds@1952
   268
	{
ds@1952
   269
		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
ds@1952
   270
		{
ds@1952
   271
			*buf++ = '0';
ds@1952
   272
		}
ds@1952
   273
		else
ds@1952
   274
		{
ds@1952
   275
			val = (int)(fval / tmp);
ds@1952
   276
			*buf++ = '0' + val;
ds@1952
   277
			fval -= val * tmp;
ds@1952
   278
			tmp *= 0.1;
ds@1952
   279
		}
ds@1952
   280
		maxlen--;
ds@1952
   281
	}
ds@1952
   282
ds@1952
   283
	// write the fraction part
ds@1952
   284
	if (maxlen)
ds@1952
   285
	{
ds@1952
   286
		*buf++ = '.';
ds@1952
   287
		maxlen--;
ds@1952
   288
	}
ds@1952
   289
ds@1952
   290
	tmp = pow(10.0, prec);
ds@1952
   291
ds@1952
   292
	fval *= tmp;
ds@1952
   293
	while (prec-- && maxlen)
ds@1952
   294
	{
ds@1952
   295
		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
ds@1952
   296
		{
ds@1952
   297
			*buf++ = '0';
ds@1952
   298
		}
ds@1952
   299
		else
ds@1952
   300
		{
ds@1952
   301
			tmp *= 0.1;
ds@1952
   302
			val = (int)(fval / tmp);
ds@1952
   303
			*buf++ = '0' + val;
ds@1952
   304
			fval -= val * tmp;
ds@1952
   305
		}
ds@1952
   306
		maxlen--;
ds@1952
   307
	}
ds@1952
   308
ds@1952
   309
	// left justify if required
ds@1952
   310
	if (flags & LADJUST)
ds@1952
   311
	{
ds@1952
   312
		while ((fieldlength < width) && maxlen)
ds@1952
   313
		{
ds@1952
   314
			// right-padding only with spaces, ZEROPAD is ignored
ds@1952
   315
			*buf++ = ' ';
ds@1952
   316
			width--;
ds@1952
   317
			maxlen--;
ds@1952
   318
		}
ds@1952
   319
	}
ds@1952
   320
ds@1952
   321
	// update parent's buffer pointer
ds@1952
   322
	*buf_p = buf;
ds@1952
   323
}
ds@1952
   324
dvander@1965
   325
void AddBinary(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
dvander@1965
   326
{
dvander@1965
   327
	char text[32];
dvander@1965
   328
	int digits;
dvander@1965
   329
	char *buf;
dvander@1965
   330
dvander@1965
   331
	digits = 0;
dvander@1965
   332
	do
dvander@1965
   333
	{
dvander@1965
   334
		if (val & 1)
dvander@1965
   335
		{
dvander@1965
   336
			text[digits++] = '1';
dvander@1965
   337
		}
dvander@1965
   338
		else
dvander@1965
   339
		{
dvander@1965
   340
			text[digits++] = '0';
dvander@1965
   341
		}
dvander@1965
   342
		val >>= 1;
dvander@1965
   343
	} while (val);
dvander@1965
   344
dvander@1965
   345
	buf = *buf_p;
dvander@1965
   346
dvander@1965
   347
	if (!(flags & LADJUST))
dvander@1965
   348
	{
dvander@1965
   349
		while (digits < width && maxlen)
dvander@1965
   350
		{
dvander@1965
   351
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
dvander@1965
   352
			width--;
dvander@1965
   353
			maxlen--;
dvander@1965
   354
		}
dvander@1965
   355
	}
dvander@1965
   356
dvander@1965
   357
	while (digits-- && maxlen)
dvander@1965
   358
	{
dvander@1965
   359
		*buf++ = text[digits];
dvander@1965
   360
		width--;
dvander@1965
   361
		maxlen--;
dvander@1965
   362
	}
dvander@1965
   363
dvander@1965
   364
	if (flags & LADJUST)
dvander@1965
   365
	{
dvander@1965
   366
		while (width-- && maxlen)
dvander@1965
   367
		{
dvander@1965
   368
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
dvander@1965
   369
			maxlen--;
dvander@1965
   370
		}
dvander@1965
   371
	}
dvander@1965
   372
dvander@1965
   373
	*buf_p = buf;
dvander@1965
   374
}
dvander@1965
   375
ds@1952
   376
void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   377
{
ds@1952
   378
	char text[32];
ds@1952
   379
	int digits;
ds@1952
   380
	char *buf;
ds@1952
   381
ds@1952
   382
	digits = 0;
ds@1952
   383
	do
ds@1952
   384
	{
ds@1952
   385
		text[digits++] = '0' + val % 10;
ds@1952
   386
		val /= 10;
ds@1952
   387
	} while (val);
ds@1952
   388
ds@1952
   389
	buf = *buf_p;
ds@1952
   390
ds@1952
   391
	if (!(flags & LADJUST))
ds@1952
   392
	{
ds@1952
   393
		while (digits < width && maxlen)
ds@1952
   394
		{
ds@1952
   395
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   396
			width--;
ds@1952
   397
			maxlen--;
ds@1952
   398
		}
ds@1952
   399
	}
ds@1952
   400
ds@1952
   401
	while (digits-- && maxlen)
ds@1952
   402
	{
ds@1952
   403
		*buf++ = text[digits];
ds@1952
   404
		width--;
ds@1952
   405
		maxlen--;
ds@1952
   406
	}
ds@1952
   407
ds@1952
   408
	if (flags & LADJUST)
ds@1952
   409
	{
ds@1952
   410
		while (width-- && maxlen)
ds@1952
   411
		{
ds@1952
   412
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   413
			maxlen--;
ds@1952
   414
		}
ds@1952
   415
	}
ds@1952
   416
ds@1952
   417
	*buf_p = buf;
ds@1952
   418
}
ds@1952
   419
ds@1952
   420
void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
ds@1952
   421
{
ds@1952
   422
	char text[32];
ds@1952
   423
	int digits;
ds@1952
   424
	int signedVal;
ds@1952
   425
	char *buf;
ds@1952
   426
	unsigned int unsignedVal;
ds@1952
   427
ds@1952
   428
	digits = 0;
ds@1952
   429
	signedVal = val;
ds@1952
   430
	if (val < 0)
ds@1952
   431
	{
ds@1952
   432
		/* we want the unsigned version */
ds@1952
   433
		unsignedVal = abs(val);
ds@1952
   434
	}
ds@1952
   435
	else
ds@1952
   436
	{
ds@1952
   437
		unsignedVal = val;
ds@1952
   438
	}
ds@1952
   439
ds@1952
   440
	do
ds@1952
   441
	{
ds@1952
   442
		text[digits++] = '0' + unsignedVal % 10;
ds@1952
   443
		unsignedVal /= 10;
ds@1952
   444
	} while (unsignedVal);
ds@1952
   445
ds@1952
   446
	if (signedVal < 0)
ds@1952
   447
	{
ds@1952
   448
		text[digits++] = '-';
ds@1952
   449
	}
ds@1952
   450
ds@1952
   451
	buf = *buf_p;
ds@1952
   452
ds@1952
   453
	if (!(flags & LADJUST))
ds@1952
   454
	{
ds@1952
   455
		while ((digits < width) && maxlen)
ds@1952
   456
		{
ds@1952
   457
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   458
			width--;
ds@1952
   459
			maxlen--;
ds@1952
   460
		}
ds@1952
   461
	}
ds@1952
   462
ds@1952
   463
	while (digits-- && maxlen)
ds@1952
   464
	{
ds@1952
   465
		*buf++ = text[digits];
ds@1952
   466
		width--;
ds@1952
   467
		maxlen--;
ds@1952
   468
	}
ds@1952
   469
ds@1952
   470
	if (flags & LADJUST)
ds@1952
   471
	{
ds@1952
   472
		while (width-- && maxlen)
ds@1952
   473
		{
ds@1952
   474
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   475
			maxlen--;
ds@1952
   476
		}
ds@1952
   477
	}
ds@1952
   478
ds@1952
   479
	*buf_p = buf;
ds@1952
   480
}
ds@1952
   481
ds@1952
   482
void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@1952
   483
{
ds@1952
   484
	char text[32];
ds@1952
   485
	int digits;
ds@1952
   486
	char *buf;
ds@1952
   487
	char digit;
ds@1952
   488
	int hexadjust;
ds@1952
   489
ds@1952
   490
	if (flags & UPPERDIGITS)
ds@1952
   491
	{
ds@1952
   492
		hexadjust = 'A' - '9' - 1;
ds@1952
   493
	}
ds@1952
   494
	else
ds@1952
   495
	{
ds@1952
   496
		hexadjust = 'a' - '9' - 1;
ds@1952
   497
	}
ds@1952
   498
ds@1952
   499
	digits = 0;
ds@1952
   500
	do 
ds@1952
   501
	{
ds@1952
   502
		digit = ('0' + val % 16);
ds@1952
   503
		if (digit > '9')
ds@1952
   504
		{
ds@1952
   505
			digit += hexadjust;
ds@1952
   506
		}
ds@1952
   507
ds@1952
   508
		text[digits++] = digit;
ds@1952
   509
		val /= 16;
ds@1952
   510
	} while(val);
ds@1952
   511
ds@1952
   512
	buf = *buf_p;
ds@1952
   513
ds@1952
   514
	if (!(flags & LADJUST))
ds@1952
   515
	{
ds@1952
   516
		while (digits < width && maxlen)
ds@1952
   517
		{
ds@1952
   518
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   519
			width--;
ds@1952
   520
			maxlen--;
ds@1952
   521
		}
ds@1952
   522
	}
ds@1952
   523
ds@1952
   524
	while (digits-- && maxlen)
ds@1952
   525
	{
ds@1952
   526
		*buf++ = text[digits];
ds@1952
   527
		width--;
ds@1952
   528
		maxlen--;
ds@1952
   529
	}
ds@1952
   530
ds@1952
   531
	if (flags & LADJUST)
ds@1952
   532
	{
ds@1952
   533
		while (width-- && maxlen)
ds@1952
   534
		{
ds@1952
   535
			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@1952
   536
			maxlen--;
ds@1952
   537
		}
ds@1952
   538
	}
ds@1952
   539
ds@1952
   540
	*buf_p = buf;
ds@1952
   541
}
ds@1952
   542
dvander@2042
   543
bool gnprintf(char *buffer,
dvander@2042
   544
			  size_t maxlen,
dvander@2042
   545
			  const char *format,
dvander@2042
   546
			  IPhraseCollection *pPhrases,
dvander@2042
   547
			  void **params,
dvander@2042
   548
			  unsigned int numparams,
dvander@2042
   549
			  unsigned int &curparam,
dvander@2042
   550
			  size_t *pOutLength,
dvander@2042
   551
			  const char **pFailPhrase)
ds@1952
   552
{
ds@1952
   553
	if (!buffer || !maxlen)
ds@1952
   554
	{
dvander@2042
   555
		if (pOutLength != NULL)
dvander@2042
   556
		{
dvander@2042
   557
			*pOutLength = 0;
dvander@2042
   558
		}
dvander@2042
   559
		return true;
dvander@2042
   560
	}
dvander@2042
   561
dvander@2042
   562
	if (numparams > MAX_TRANSLATE_PARAMS)
dvander@2042
   563
	{
dvander@2042
   564
		if (pFailPhrase != NULL)
dvander@2042
   565
		{
dvander@2042
   566
			*pFailPhrase = NULL;
dvander@2042
   567
		}
dvander@2042
   568
		return false;
ds@1952
   569
	}
ds@1952
   570
ds@1952
   571
	int arg = 0;
ds@1952
   572
	char *buf_p;
ds@1952
   573
	char ch;
ds@1952
   574
	int flags;
ds@1952
   575
	int width;
ds@1952
   576
	int prec;
ds@1952
   577
	int n;
ds@1952
   578
	char sign;
ds@1952
   579
	const char *fmt;
ds@1952
   580
	size_t llen = maxlen - 1;
ds@1952
   581
ds@1952
   582
	buf_p = buffer;
ds@1952
   583
	fmt = format;
ds@1952
   584
ds@1952
   585
	while (true)
ds@1952
   586
	{
ds@1952
   587
		// run through the format string until we hit a '%' or '\0'
ds@1952
   588
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
   589
		{
ds@1952
   590
			*buf_p++ = ch;
ds@1952
   591
			llen--;
ds@1952
   592
		}
ds@1952
   593
		if ((ch == '\0') || (llen <= 0))
ds@1952
   594
		{
ds@1952
   595
			goto done;
ds@1952
   596
		}
ds@1952
   597
ds@1952
   598
		// skip over the '%'
ds@1952
   599
		fmt++;
ds@1952
   600
ds@1952
   601
		// reset formatting state
ds@1952
   602
		flags = 0;
ds@1952
   603
		width = 0;
ds@1952
   604
		prec = -1;
ds@1952
   605
		sign = '\0';
ds@1952
   606
ds@1952
   607
rflag:
ds@1952
   608
		ch = *fmt++;
ds@1952
   609
reswitch:
ds@1952
   610
		switch(ch)
ds@1952
   611
		{
ds@1952
   612
		case '-':
ds@1952
   613
			{
ds@1952
   614
				flags |= LADJUST;
ds@1952
   615
				goto rflag;
ds@1952
   616
			}
ds@1952
   617
		case '.':
ds@1952
   618
			{
ds@1952
   619
				n = 0;
ds@1952
   620
				while(is_digit((ch = *fmt++)))
ds@1952
   621
				{
ds@1952
   622
					n = 10 * n + (ch - '0');
ds@1952
   623
				}
ds@1952
   624
				prec = (n < 0) ? -1 : n;
ds@1952
   625
				goto reswitch;
ds@1952
   626
			}
ds@1952
   627
		case '0':
ds@1952
   628
			{
ds@1952
   629
				flags |= ZEROPAD;
ds@1952
   630
				goto rflag;
ds@1952
   631
			}
ds@1952
   632
		case '1':
ds@1952
   633
		case '2':
ds@1952
   634
		case '3':
ds@1952
   635
		case '4':
ds@1952
   636
		case '5':
ds@1952
   637
		case '6':
ds@1952
   638
		case '7':
ds@1952
   639
		case '8':
ds@1952
   640
		case '9':
ds@1952
   641
			{
ds@1952
   642
				n = 0;
ds@1952
   643
				do
ds@1952
   644
				{
ds@1952
   645
					n = 10 * n + (ch - '0');
ds@1952
   646
					ch = *fmt++;
ds@1952
   647
				} while(is_digit(ch));
ds@1952
   648
				width = n;
ds@1952
   649
				goto reswitch;
ds@1952
   650
			}
ds@1952
   651
		case 'c':
ds@1952
   652
			{
ds@1952
   653
				if (!llen)
ds@1952
   654
				{
ds@1952
   655
					goto done;
ds@1952
   656
				}
dvander@2042
   657
				if (curparam >= numparams)
dvander@2042
   658
				{
dvander@2042
   659
					if (pFailPhrase != NULL)
dvander@2042
   660
					{
dvander@2042
   661
						*pFailPhrase = NULL;
dvander@2042
   662
					}
dvander@2042
   663
					return false;
dvander@2042
   664
				}
dvander@2042
   665
				char *c = (char *)params[curparam];
dvander@2042
   666
				curparam++;
ds@1952
   667
				*buf_p++ = *c;
ds@1952
   668
				llen--;
ds@1952
   669
				arg++;
ds@1952
   670
				break;
ds@1952
   671
			}
dvander@1965
   672
		case 'b':
dvander@1965
   673
			{
dvander@2042
   674
				if (curparam >= numparams)
dvander@2042
   675
				{
dvander@2042
   676
					if (pFailPhrase != NULL)
dvander@2042
   677
					{
dvander@2042
   678
						*pFailPhrase = NULL;
dvander@2042
   679
					}
dvander@2042
   680
					return false;
dvander@2042
   681
				}
dvander@2042
   682
				int *value = (int *)params[curparam];
dvander@2042
   683
				curparam++;
dvander@1965
   684
				AddBinary(&buf_p, llen, *value, width, flags);
dvander@1965
   685
				arg++;
dvander@1965
   686
				break;
dvander@1965
   687
			}
ds@1952
   688
		case 'd':
ds@1952
   689
		case 'i':
ds@1952
   690
			{
dvander@2042
   691
				if (curparam >= numparams)
dvander@2042
   692
				{
dvander@2042
   693
					if (pFailPhrase != NULL)
dvander@2042
   694
					{
dvander@2042
   695
						*pFailPhrase = NULL;
dvander@2042
   696
					}
dvander@2042
   697
					return false;
dvander@2042
   698
				}
dvander@2042
   699
				int *value = (int *)params[curparam];
dvander@2042
   700
				curparam++;
ds@1952
   701
				AddInt(&buf_p, llen, *value, width, flags);
ds@1952
   702
				arg++;
ds@1952
   703
				break;
ds@1952
   704
			}
ds@1952
   705
		case 'u':
ds@1952
   706
			{
dvander@2042
   707
				if (curparam >= numparams)
dvander@2042
   708
				{
dvander@2042
   709
					if (pFailPhrase != NULL)
dvander@2042
   710
					{
dvander@2042
   711
						*pFailPhrase = NULL;
dvander@2042
   712
					}
dvander@2042
   713
					return false;
dvander@2042
   714
				}
dvander@2042
   715
				unsigned int *value = (unsigned int *)params[curparam];
dvander@2042
   716
				curparam++;
ds@1952
   717
				AddUInt(&buf_p, llen, *value, width, flags);
ds@1952
   718
				arg++;
ds@1952
   719
				break;
ds@1952
   720
			}
ds@1952
   721
		case 'f':
ds@1952
   722
			{
dvander@2042
   723
				if (curparam >= numparams)
dvander@2042
   724
				{
dvander@2042
   725
					if (pFailPhrase != NULL)
dvander@2042
   726
					{
dvander@2042
   727
						*pFailPhrase = NULL;
dvander@2042
   728
					}
dvander@2042
   729
					return false;
dvander@2042
   730
				}
dvander@2042
   731
				float *value = (float *)params[curparam];
dvander@2042
   732
				curparam++;
ds@1952
   733
				AddFloat(&buf_p, llen, *value, width, prec, flags);
ds@1952
   734
				arg++;
ds@1952
   735
				break;
ds@1952
   736
			}
ds@1952
   737
		case 's':
ds@1952
   738
			{
dvander@2042
   739
				if (curparam >= numparams)
dvander@2042
   740
				{
dvander@2042
   741
					if (pFailPhrase != NULL)
dvander@2042
   742
					{
dvander@2042
   743
						*pFailPhrase = NULL;
dvander@2042
   744
					}
dvander@2042
   745
					return false;
dvander@2042
   746
				}
dvander@2042
   747
				const char *str = (const char *)params[curparam];
dvander@2042
   748
				curparam++;
ds@1952
   749
				AddString(&buf_p, llen, str, width, prec);
ds@1952
   750
				arg++;
ds@1952
   751
				break;
ds@1952
   752
			}
dvander@2042
   753
		case 'T':
dvander@2042
   754
		case 't':
dvander@2042
   755
			{
dvander@2042
   756
				int target;
dvander@2042
   757
				const char *key;
dvander@2042
   758
				size_t out_length;
dvander@2042
   759
				Translation trans;
dvander@2042
   760
				unsigned int lang_id;
dvander@2042
   761
dvander@2042
   762
				if (curparam >= numparams)
dvander@2042
   763
				{
dvander@2042
   764
					if (pFailPhrase != NULL)
dvander@2042
   765
					{
dvander@2042
   766
						*pFailPhrase = NULL;
dvander@2042
   767
					}
dvander@2042
   768
					return false;
dvander@2042
   769
				}
dvander@2042
   770
				key = (const char *)(params[curparam]);
dvander@2042
   771
				curparam++;
dvander@2042
   772
dvander@2042
   773
				if (ch == 'T')
dvander@2042
   774
				{
dvander@2042
   775
					if (curparam >= numparams)
dvander@2042
   776
					{
dvander@2042
   777
						if (pFailPhrase != NULL)
dvander@2042
   778
						{
dvander@2042
   779
							*pFailPhrase = NULL;
dvander@2042
   780
						}
dvander@2042
   781
						return false;
dvander@2042
   782
					}
dvander@2042
   783
					target = *((int *)(params[curparam]));
dvander@2042
   784
					curparam++;
dvander@2042
   785
				}
dvander@2042
   786
				else
dvander@2042
   787
				{
dvander@2042
   788
					target = g_Translator.GetGlobalTarget();
dvander@2042
   789
				}
dvander@2042
   790
dvander@2042
   791
try_again:
dvander@2042
   792
				if (target == SOURCEMOD_SERVER_LANGUAGE)
dvander@2042
   793
				{
dvander@2042
   794
					lang_id = g_Translator.GetServerLanguage();
dvander@2042
   795
				}
dvander@2042
   796
				else if (target >= 1 && target <= g_Players.GetMaxClients())
dvander@2042
   797
				{
dvander@2042
   798
					lang_id = g_Translator.GetClientLanguage(target);
dvander@2042
   799
				}
dvander@2042
   800
				else
dvander@2042
   801
				{
dvander@2042
   802
					lang_id = g_Translator.GetServerLanguage();
dvander@2042
   803
				}
dvander@2042
   804
dvander@2042
   805
				if (pPhrases == NULL)
dvander@2042
   806
				{
dvander@2042
   807
					if (pFailPhrase != NULL)
dvander@2042
   808
					{
dvander@2042
   809
						*pFailPhrase = key;
dvander@2042
   810
					}
dvander@2042
   811
					return false;
dvander@2042
   812
				}
dvander@2042
   813
dvander@2042
   814
				if (pPhrases->FindTranslation(key, lang_id, &trans) != Trans_Okay)
dvander@2042
   815
				{
dvander@2042
   816
					if (target != SOURCEMOD_SERVER_LANGUAGE && lang_id != g_Translator.GetServerLanguage())
dvander@2042
   817
					{
dvander@2042
   818
						target = SOURCEMOD_SERVER_LANGUAGE;
dvander@2042
   819
						goto try_again;
dvander@2042
   820
					}
dvander@2042
   821
					else if (lang_id != SOURCEMOD_LANGUAGE_ENGLISH)
dvander@2042
   822
					{
dvander@2042
   823
						if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &trans) != Trans_Okay)
dvander@2042
   824
						{
dvander@2042
   825
							if (pFailPhrase != NULL)
dvander@2042
   826
							{
dvander@2042
   827
								*pFailPhrase = key;
dvander@2042
   828
							}
dvander@2042
   829
							return false;
dvander@2042
   830
						}
dvander@2042
   831
					}
dvander@2042
   832
					else
dvander@2042
   833
					{
dvander@2042
   834
						if (pFailPhrase != NULL)
dvander@2042
   835
						{
dvander@2042
   836
							*pFailPhrase = key;
dvander@2042
   837
						}
dvander@2042
   838
						return false;
dvander@2042
   839
					}
dvander@2042
   840
				}
dvander@2042
   841
dvander@2042
   842
				if (trans.fmt_count)
dvander@2042
   843
				{
dvander@2042
   844
					unsigned int i;
dvander@2042
   845
					void *new_params[MAX_TRANSLATE_PARAMS];
dvander@2042
   846
dvander@2042
   847
					if (curparam + trans.fmt_count > numparams)
dvander@2042
   848
					{
dvander@2042
   849
						if (pFailPhrase != NULL)
dvander@2042
   850
						{
dvander@2042
   851
							*pFailPhrase = NULL;
dvander@2042
   852
						}
dvander@2042
   853
						return false;
dvander@2042
   854
					}
dvander@2042
   855
dvander@2042
   856
					/* Copy the array and re-order the stack */
dvander@2042
   857
					memcpy(new_params, params, sizeof(void *) * numparams);
dvander@2042
   858
					for (i = 0; i < trans.fmt_count; i++)
dvander@2042
   859
					{
dvander@2042
   860
						new_params[i] = const_cast<void *>(params[curparam + trans.fmt_order[i]]);
dvander@2042
   861
					}
dvander@2042
   862
dvander@2042
   863
					if (!gnprintf(buf_p,
dvander@2042
   864
							llen,
dvander@2042
   865
							trans.szPhrase,
dvander@2042
   866
							pPhrases,
dvander@2042
   867
							new_params,
dvander@2042
   868
							numparams,
dvander@2042
   869
							curparam,
dvander@2042
   870
							&out_length,
dvander@2042
   871
							pFailPhrase))
dvander@2042
   872
					{
dvander@2042
   873
						return false;
dvander@2042
   874
					}
dvander@2042
   875
				}
dvander@2042
   876
				else
dvander@2042
   877
				{
dvander@2042
   878
					if (!gnprintf(buf_p,
dvander@2042
   879
						llen,
dvander@2042
   880
						trans.szPhrase,
dvander@2042
   881
						pPhrases,
dvander@2042
   882
						params,
dvander@2042
   883
						numparams,
dvander@2042
   884
						curparam,
dvander@2042
   885
						&out_length,
dvander@2042
   886
						pFailPhrase))
dvander@2042
   887
					{
dvander@2042
   888
						return false;
dvander@2042
   889
					}
dvander@2042
   890
				}
dvander@2042
   891
dvander@2042
   892
				buf_p += out_length;
dvander@2042
   893
				llen -= out_length;
dvander@2042
   894
dvander@2042
   895
				break;
dvander@2042
   896
			}
ds@1952
   897
		case 'X':
ds@1952
   898
			{
dvander@2042
   899
				if (curparam >= numparams)
dvander@2042
   900
				{
dvander@2042
   901
					if (pFailPhrase != NULL)
dvander@2042
   902
					{
dvander@2042
   903
						*pFailPhrase = NULL;
dvander@2042
   904
					}
dvander@2042
   905
					return false;
dvander@2042
   906
				}
dvander@2042
   907
				unsigned int *value = (unsigned int *)params[curparam];
dvander@2042
   908
				curparam++;
ds@1952
   909
				flags |= UPPERDIGITS;
ds@1952
   910
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   911
				arg++;
ds@1952
   912
				break;
ds@1952
   913
			}
ds@1952
   914
		case 'x':
ds@1952
   915
			{
dvander@2042
   916
				if (curparam >= numparams)
dvander@2042
   917
				{
dvander@2042
   918
					if (pFailPhrase != NULL)
dvander@2042
   919
					{
dvander@2042
   920
						*pFailPhrase = NULL;
dvander@2042
   921
					}
dvander@2042
   922
					return false;
dvander@2042
   923
				}
dvander@2042
   924
				unsigned int *value = (unsigned int *)params[curparam];
dvander@2042
   925
				curparam++;
ds@1952
   926
				AddHex(&buf_p, llen, *value, width, flags);
ds@1952
   927
				arg++;
ds@1952
   928
				break;
ds@1952
   929
			}
ds@1952
   930
		case '%':
ds@1952
   931
			{
ds@1952
   932
				if (!llen)
ds@1952
   933
				{
ds@1952
   934
					goto done;
ds@1952
   935
				}
ds@1952
   936
				*buf_p++ = ch;
ds@1952
   937
				llen--;
ds@1952
   938
				break;
ds@1952
   939
			}
ds@1952
   940
		case '\0':
ds@1952
   941
			{
ds@1952
   942
				if (!llen)
ds@1952
   943
				{
ds@1952
   944
					goto done;
ds@1952
   945
				}
ds@1952
   946
				*buf_p++ = '%';
ds@1952
   947
				llen--;
ds@1952
   948
				goto done;
ds@1952
   949
			}
ds@1952
   950
		default:
ds@1952
   951
			{
ds@1952
   952
				if (!llen)
ds@1952
   953
				{
ds@1952
   954
					goto done;
ds@1952
   955
				}
ds@1952
   956
				*buf_p++ = ch;
ds@1952
   957
				llen--;
ds@1952
   958
				break;
ds@1952
   959
			}
ds@1952
   960
		}
ds@1952
   961
	}
ds@1952
   962
ds@1952
   963
done:
ds@1952
   964
	*buf_p = '\0';
ds@1952
   965
dvander@2042
   966
	if (pOutLength != NULL)
dvander@2042
   967
	{
dvander@2042
   968
		*pOutLength = (maxlen - llen - 1);
dvander@2042
   969
	}
dvander@2042
   970
dvander@2042
   971
	return true;
ds@1952
   972
}
ds@1952
   973
ds@1952
   974
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
ds@1952
   975
{
ds@1952
   976
	if (!buffer || !maxlen)
ds@1952
   977
	{
ds@1952
   978
		return 0;
ds@1952
   979
	}
ds@1952
   980
ds@1952
   981
	int arg;
ds@1952
   982
	int args = params[0];
ds@1952
   983
	char *buf_p;
ds@1952
   984
	char ch;
ds@1952
   985
	int flags;
ds@1952
   986
	int width;
ds@1952
   987
	int prec;
ds@1952
   988
	int n;
ds@1952
   989
	char sign;
ds@1952
   990
	const char *fmt;
ds@1952
   991
	size_t llen = maxlen - 1;
ds@1952
   992
ds@1952
   993
	buf_p = buffer;
ds@1952
   994
	arg = *param;
ds@1952
   995
	fmt = format;
ds@1952
   996
ds@1952
   997
	while (true)
ds@1952
   998
	{
ds@1952
   999
		// run through the format string until we hit a '%' or '\0'
ds@1952
  1000
		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
ds@1952
  1001
		{
ds@1952
  1002
			*buf_p++ = ch;
ds@1952
  1003
			llen--;
ds@1952
  1004
		}
ds@1952
  1005
		if ((ch == '\0') || (llen <= 0))
ds@1952
  1006
		{
ds@1952
  1007
			goto done;
ds@1952
  1008
		}
ds@1952
  1009
ds@1952
  1010
		// skip over the '%'
ds@1952
  1011
		fmt++;
ds@1952
  1012
ds@1952
  1013
		// reset formatting state
ds@1952
  1014
		flags = 0;
ds@1952
  1015
		width = 0;
ds@1952
  1016
		prec = -1;
ds@1952
  1017
		sign = '\0';
ds@1952
  1018
ds@1952
  1019
rflag:
ds@1952
  1020
		ch = *fmt++;
ds@1952
  1021
reswitch:
ds@1952
  1022
		switch(ch)
ds@1952
  1023
		{
ds@1952
  1024
		case '-':
ds@1952
  1025
			{
ds@1952
  1026
				flags |= LADJUST;
ds@1952
  1027
				goto rflag;
ds@1952
  1028
			}
ds@1952
  1029
		case '.':
ds@1952
  1030
			{
ds@1952
  1031
				n = 0;
ds@1952
  1032
				while(is_digit((ch = *fmt++)))
ds@1952
  1033
				{
ds@1952
  1034
					n = 10 * n + (ch - '0');
ds@1952
  1035
				}
ds@1952
  1036
				prec = (n < 0) ? -1 : n;
ds@1952
  1037
				goto reswitch;
ds@1952
  1038
			}
ds@1952
  1039
		case '0':
ds@1952
  1040
			{
ds@1952
  1041
				flags |= ZEROPAD;
ds@1952
  1042
				goto rflag;
ds@1952
  1043
			}
ds@1952
  1044
		case '1':
ds@1952
  1045
		case '2':
ds@1952
  1046
		case '3':
ds@1952
  1047
		case '4':
ds@1952
  1048
		case '5':
ds@1952
  1049
		case '6':
ds@1952
  1050
		case '7':
ds@1952
  1051
		case '8':
ds@1952
  1052
		case '9':
ds@1952
  1053
			{
ds@1952
  1054
				n = 0;
ds@1952
  1055
				do
ds@1952
  1056
				{
ds@1952
  1057
					n = 10 * n + (ch - '0');
ds@1952
  1058
					ch = *fmt++;
ds@1952
  1059
				} while(is_digit(ch));
ds@1952
  1060
				width = n;
ds@1952
  1061
				goto reswitch;
ds@1952
  1062
			}
ds@1952
  1063
		case 'c':
ds@1952
  1064
			{
ds@1952
  1065
				CHECK_ARGS(0);
ds@1952
  1066
				if (!llen)
ds@1952
  1067
				{
ds@1952
  1068
					goto done;
ds@1952
  1069
				}
ds@1952
  1070
				char *c;
ds@1952
  1071
				pCtx->LocalToString(params[arg], &c);
ds@1952
  1072
				*buf_p++ = *c;
ds@1952
  1073
				llen--;
ds@1952
  1074
				arg++;
ds@1952
  1075
				break;
ds@1952
  1076
			}
dvander@1965
  1077
		case 'b':
dvander@1965
  1078
			{
dvander@1965
  1079
				CHECK_ARGS(0);
dvander@1965
  1080
				cell_t *value;
dvander@1965
  1081
				pCtx->LocalToPhysAddr(params[arg], &value);
dvander@1965
  1082
				AddBinary(&buf_p, llen, *value, width, flags);
dvander@1965
  1083
				arg++;
dvander@1965
  1084
				break;
dvander@1965
  1085
			}
ds@1952
  1086
		case 'd':
ds@1952
  1087
		case 'i':
ds@1952
  1088
			{
ds@1952
  1089
				CHECK_ARGS(0);
ds@1952
  1090
				cell_t *value;
ds@1952
  1091
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1092
				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
ds@1952
  1093
				arg++;
ds@1952
  1094
				break;
ds@1952
  1095
			}
ds@1952
  1096
		case 'u':
ds@1952
  1097
			{
ds@1952
  1098
				CHECK_ARGS(0);
ds@1952
  1099
				cell_t *value;
ds@1952
  1100
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1101
				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
  1102
				arg++;
ds@1952
  1103
				break;
ds@1952
  1104
			}
ds@1952
  1105
		case 'f':
ds@1952
  1106
			{
ds@1952
  1107
				CHECK_ARGS(0);
ds@1952
  1108
				cell_t *value;
ds@1952
  1109
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1110
				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
ds@1952
  1111
				arg++;
ds@1952
  1112
				break;
ds@1952
  1113
			}
ds@1952
  1114
		case 'L':
ds@1952
  1115
			{
ds@1952
  1116
				CHECK_ARGS(0);
ds@1952
  1117
				cell_t *value;
ds@1952
  1118
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1119
				char buffer[255];
ds@1952
  1120
				if (*value)
ds@1952
  1121
				{
ds@1952
  1122
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
  1123
					if (!player || !player->IsConnected())
ds@1952
  1124
					{
ds@1952
  1125
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
  1126
					}
ds@1952
  1127
					const char *auth = player->GetAuthString();
ds@1952
  1128
					if (!auth || auth[0] == '\0')
ds@1952
  1129
					{
ds@1952
  1130
						auth = "STEAM_ID_PENDING";
ds@1952
  1131
					}
ds@1952
  1132
					int userid = engine->GetPlayerUserId(player->GetEdict());
ds@1952
  1133
					UTIL_Format(buffer, 
ds@1952
  1134
						sizeof(buffer), 
ds@1952
  1135
						"%s<%d><%s><>", 
ds@1952
  1136
						player->GetName(),
ds@1952
  1137
						userid,
ds@1952
  1138
						auth);
ds@1952
  1139
				}
ds@1952
  1140
				else
ds@1952
  1141
				{
ds@1952
  1142
					UTIL_Format(buffer,
ds@1952
  1143
						sizeof(buffer),
ds@1952
  1144
						"Console<0><Console><Console>");
ds@1952
  1145
				}
ds@1952
  1146
				AddString(&buf_p, llen, buffer, width, prec);
ds@1952
  1147
				arg++;
ds@1952
  1148
				break;
ds@1952
  1149
			}
ds@1952
  1150
		case 'N':
ds@1952
  1151
			{
ds@1952
  1152
				CHECK_ARGS(0);
ds@1952
  1153
				cell_t *value;
ds@1952
  1154
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1155
ds@1952
  1156
				const char *name = "Console";
ds@1952
  1157
				if (*value)
ds@1952
  1158
				{
ds@1952
  1159
					CPlayer *player = g_Players.GetPlayerByIndex(*value);
ds@1952
  1160
					if (!player || !player->IsConnected())
ds@1952
  1161
					{
ds@1952
  1162
						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
ds@1952
  1163
					}
ds@1952
  1164
					name = player->GetName();
ds@1952
  1165
				}
ds@1952
  1166
				AddString(&buf_p, llen, name, width, prec);
ds@1952
  1167
				arg++;
ds@1952
  1168
				break;
ds@1952
  1169
			}
ds@1952
  1170
		case 's':
ds@1952
  1171
			{
ds@1952
  1172
				CHECK_ARGS(0);
ds@1952
  1173
				char *str;
ds@1952
  1174
				int err;
ds@1952
  1175
				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
ds@1952
  1176
				{
ds@1952
  1177
					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
ds@1952
  1178
					return 0;
ds@1952
  1179
				}
ds@1952
  1180
				AddString(&buf_p, llen, str, width, prec);
ds@1952
  1181
				arg++;
ds@1952
  1182
				break;
ds@1952
  1183
			}
ds@1952
  1184
		case 'T':
ds@1952
  1185
			{
ds@1952
  1186
				CHECK_ARGS(1);
ds@1952
  1187
				char *key;
ds@1952
  1188
				bool error;
ds@1952
  1189
				size_t res;
ds@1952
  1190
				cell_t *target;
ds@1952
  1191
				pCtx->LocalToString(params[arg++], &key);
ds@1952
  1192
				pCtx->LocalToPhysAddr(params[arg++], &target);
ds@1952
  1193
				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
ds@1952
  1194
				if (error)
ds@1952
  1195
				{
ds@1952
  1196
					return 0;
ds@1952
  1197
				}
ds@1952
  1198
				buf_p += res;
ds@1952
  1199
				llen -= res;
ds@1952
  1200
				break;
ds@1952
  1201
			}
ds@1952
  1202
		case 't':
ds@1952
  1203
			{
ds@1952
  1204
				CHECK_ARGS(0);
ds@1952
  1205
				char *key;
ds@1952
  1206
				bool error;
ds@1952
  1207
				size_t res;
ds@1952
  1208
				cell_t target = g_SourceMod.GetGlobalTarget();
ds@1952
  1209
				pCtx->LocalToString(params[arg++], &key);
ds@1952
  1210
				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
ds@1952
  1211
				if (error)
ds@1952
  1212
				{
ds@1952
  1213
					return 0;
ds@1952
  1214
				}
ds@1952
  1215
				buf_p += res;
ds@1952
  1216
				llen -= res;
ds@1952
  1217
				break;
ds@1952
  1218
			}
ds@1952
  1219
		case 'X':
ds@1952
  1220
			{
ds@1952
  1221
				CHECK_ARGS(0);
ds@1952
  1222
				cell_t *value;
ds@1952
  1223
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1224
				flags |= UPPERDIGITS;
ds@1952
  1225
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
  1226
				arg++;
ds@1952
  1227
				break;
ds@1952
  1228
			}
ds@1952
  1229
		case 'x':
ds@1952
  1230
			{
ds@1952
  1231
				CHECK_ARGS(0);
ds@1952
  1232
				cell_t *value;
ds@1952
  1233
				pCtx->LocalToPhysAddr(params[arg], &value);
ds@1952
  1234
				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
ds@1952
  1235
				arg++;
ds@1952
  1236
				break;
ds@1952
  1237
			}
ds@1952
  1238
		case '%':
ds@1952
  1239
			{
ds@1952
  1240
				if (!llen)
ds@1952
  1241
				{
ds@1952
  1242
					goto done;
ds@1952
  1243
				}
ds@1952
  1244
				*buf_p++ = ch;
ds@1952
  1245
				llen--;
ds@1952
  1246
				break;
ds@1952
  1247
			}
ds@1952
  1248
		case '\0':
ds@1952
  1249
			{
ds@1952
  1250
				if (!llen)
ds@1952
  1251
				{
ds@1952
  1252
					goto done;
ds@1952
  1253
				}
ds@1952
  1254
				*buf_p++ = '%';
ds@1952
  1255
				llen--;
ds@1952
  1256
				goto done;
ds@1952
  1257
			}
ds@1952
  1258
		default:
ds@1952
  1259
			{
ds@1952
  1260
				if (!llen)
ds@1952
  1261
				{
ds@1952
  1262
					goto done;
ds@1952
  1263
				}
ds@1952
  1264
				*buf_p++ = ch;
ds@1952
  1265
				llen--;
ds@1952
  1266
				break;
ds@1952
  1267
			}
ds@1952
  1268
		}
ds@1952
  1269
	}
ds@1952
  1270
ds@1952
  1271
done:
ds@1952
  1272
	*buf_p = '\0';
ds@1952
  1273
	*param = arg;
ds@1952
  1274
	return (maxlen - llen - 1);
ds@1952
  1275
}
ds@1952
  1276
ds@1952
  1277
const char *stristr(const char *str, const char *substr)
ds@1952
  1278
{
ds@1952
  1279
	if (!*substr)
ds@1952
  1280
	{
ds@1952
  1281
		return ((char *)str);
ds@1952
  1282
	}
ds@1952
  1283
ds@1952
  1284
	char *needle = (char *)substr;
ds@1952
  1285
	char *prevloc = (char *)str;
ds@1952
  1286
	char *haystack = (char *)str;
ds@1952
  1287
ds@1952
  1288
	while (*haystack)
ds@1952
  1289
	{
ds@1952
  1290
		if (tolower(*haystack) == tolower(*needle))
ds@1952
  1291
		{
ds@1952
  1292
			haystack++;
ds@1952
  1293
			if (!*++needle)
ds@1952
  1294
			{
ds@1952
  1295
				return prevloc;
ds@1952
  1296
			}
ds@1952
  1297
		}
ds@1952
  1298
		else
ds@1952
  1299
		{
ds@1952
  1300
			haystack = ++prevloc;
ds@1952
  1301
			needle = (char *)substr;
ds@1952
  1302
		}
ds@1952
  1303
	}
ds@1952
  1304
ds@1952
  1305
	return NULL;
ds@1952
  1306
}
ds@1952
  1307
ds@1952
  1308
unsigned int strncopy(char *dest, const char *src, size_t count)
ds@1952
  1309
{
ds@1952
  1310
	if (!count)
ds@1952
  1311
	{
ds@1952
  1312
		return 0;
ds@1952
  1313
	}
ds@1952
  1314
ds@1952
  1315
	char *start = dest;
ds@1952
  1316
	while ((*src) && (--count))
ds@1952
  1317
	{
ds@1952
  1318
		*dest++ = *src++;
ds@1952
  1319
	}
ds@1952
  1320
	*dest = '\0';
ds@1952
  1321
ds@1952
  1322
	return (dest - start);
ds@1952
  1323
}
ds@1952
  1324
ds@1952
  1325
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
ds@1952
  1326
{
ds@1952
  1327
	va_list ap;
ds@1952
  1328
	va_start(ap, fmt);
ds@1952
  1329
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1330
	va_end(ap);
ds@1952
  1331
ds@1952
  1332
	if (len >= maxlength)
ds@1952
  1333
	{
ds@1952
  1334
		buffer[maxlength - 1] = '\0';
ds@1952
  1335
		return (maxlength - 1);
ds@1952
  1336
	}
ds@1952
  1337
	else
ds@1952
  1338
	{
ds@1952
  1339
		return len;
ds@1952
  1340
	}
ds@1952
  1341
}
ds@1952
  1342
ds@1952
  1343
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
ds@1952
  1344
{
ds@1952
  1345
	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
ds@1952
  1346
ds@1952
  1347
	if (len >= maxlength)
ds@1952
  1348
	{
ds@1952
  1349
		buffer[maxlength - 1] = '\0';
ds@1952
  1350
		return (maxlength - 1);
ds@1952
  1351
	}
ds@1952
  1352
	else
ds@1952
  1353
	{
ds@1952
  1354
		return len;
ds@1952
  1355
	}
ds@1952
  1356
}
ds@1952
  1357
ds@1952
  1358
char *sm_strdup(const char *str)
ds@1952
  1359
{
ds@1952
  1360
	char *ptr = new char[strlen(str)+1];
ds@1952
  1361
	strcpy(ptr, str);
ds@1952
  1362
	return ptr;
ds@1952
  1363
}
ds@1952
  1364
ds@1952
  1365
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
ds@1952
  1366
{
ds@1952
  1367
	size_t searchLen = strlen(search);
ds@1952
  1368
	size_t replaceLen = strlen(replace);
ds@1952
  1369
ds@1952
  1370
	char *ptr = subject;
ds@1952
  1371
	unsigned int total = 0;
ds@1952
  1372
	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
ds@1952
  1373
	{
ds@1952
  1374
		total++;
ds@1952
  1375
		if (*ptr == '\0')
ds@1952
  1376
		{
ds@1952
  1377
			break;
ds@1952
  1378
		}
ds@1952
  1379
	}
ds@1952
  1380
ds@1952
  1381
	return total;
ds@1952
  1382
}
ds@1952
  1383
ds@1952
  1384
/**
ds@1952
  1385
 * NOTE: Do not edit this for the love of god unless you have 
ds@1952
  1386
 * read the test cases and understand the code behind each one.
ds@1952
  1387
 * While I don't guarantee there aren't mistakes, I do guarantee
ds@2112
  1388
 * that plugins will end up relying on tiny idiosyncrasies of this
ds@1952
  1389
 * function, just like they did with AMX Mod X.
ds@1952
  1390
 *
ds@1952
  1391
 * There are explicitly more cases than the AMX Mod X version because
ds@1952
  1392
 * we're not doing a blind copy.  Each case is specifically optimized
ds@1952
  1393
 * for what needs to be done.  Even better, we don't have to error on
ds@1952
  1394
 * bad buffer sizes.  Instead, this function will smartly cut off the 
ds@1952
  1395
 * string in a way that pushes old data out.
ds@1952
  1396
 */
ds@1952
  1397
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
ds@1952
  1398
{
ds@1952
  1399
	char *ptr = subject;
ds@1952
  1400
	size_t browsed = 0;
ds@1952
  1401
	size_t textLen = strlen(subject);
ds@1952
  1402
ds@1952
  1403
	/* It's not possible to search or replace */
ds@1952
  1404
	if (searchLen > textLen)
ds@1952
  1405
	{
ds@1952
  1406
		return NULL;
ds@1952
  1407
	}
ds@1952
  1408
ds@1952
  1409
	/* Handle the case of one byte replacement.
ds@1952
  1410
	 * It's only valid in one case.
ds@1952
  1411
	 */
ds@1952
  1412
	if (maxLen == 1)
ds@1952
  1413
	{
ds@1952
  1414
		/* If the search matches and the replace length is 0, 
ds@1952
  1415
		 * we can just terminate the string and be done.
ds@1952
  1416
		 */
ds@1952
  1417
		if (strcmp(subject, search) == 0 && replaceLen == 0)
ds@1952
  1418
		{
ds@1952
  1419
			*subject = '\0';
ds@1952
  1420
			return subject;
ds@1952
  1421
		}
ds@1952
  1422
		else
ds@1952
  1423
		{
ds@1952
  1424
			return NULL;
ds@1952
  1425
		}
ds@1952
  1426
	}
ds@1952
  1427
ds@1952
  1428
	/* Subtract one off the maxlength so we can include the null terminator */
ds@1952
  1429
	maxLen--;
ds@1952
  1430
ds@1952
  1431
	while (*ptr != '\0' && (browsed <= textLen - searchLen))
ds@1952
  1432
	{
ds@1952
  1433
		/* See if we get a comparison */
ds@1952
  1434
		if (strncmp(ptr, search, searchLen) == 0)
ds@1952
  1435
		{
ds@1952
  1436
			if (replaceLen > searchLen)
ds@1952
  1437
			{
ds@1952
  1438
				/* First, see if we have enough space to do this operation */
ds@1952
  1439
				if (maxLen - textLen < replaceLen - searchLen)
ds@1952
  1440
				{
ds@1952
  1441
					/* First, see if the replacement length goes out of bounds. */
ds@1952
  1442
					if (browsed + replaceLen >= maxLen)
ds@1952
  1443
					{
ds@1952
  1444
						/* EXAMPLE CASE:
ds@1952
  1445
						 * Subject: AABBBCCC
ds@1952
  1446
						 * Buffer : 12 bytes
ds@1952
  1447
						 * Search : BBB
ds@1952
  1448
						 * Replace: DDDDDDDDDD
ds@1952
  1449
						 * OUTPUT : AADDDDDDDDD
ds@1952
  1450
						 * POSITION:           ^
ds@1952
  1451
						 */
ds@1952
  1452
						/* If it does, we'll just bound the length and do a strcpy. */
ds@1952
  1453
						replaceLen = maxLen - browsed;
ds@1952
  1454
						/* Note, we add one to the final result for the null terminator */
ds@1952
  1455
						strncopy(ptr, replace, replaceLen+1);
ds@1952
  1456
					}
ds@1952
  1457
					else
ds@1952
  1458
					{
ds@1952
  1459
						/* EXAMPLE CASE:
ds@1952
  1460
						 * Subject: AABBBCCC
ds@1952
  1461
						 * Buffer : 12 bytes
ds@1952
  1462
						 * Search : BBB
ds@1952
  1463
						 * Replace: DDDDDDD
ds@1952
  1464
						 * OUTPUT : AADDDDDDDCC
ds@1952
  1465
						 * POSITION:         ^
ds@1952
  1466
						 */
ds@1952
  1467
						/* We're going to have some bytes left over... */
ds@1952
  1468
						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1469
						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
ds@1952
  1470
						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
ds@1952
  1471
						char *moveTo = ptr + replaceLen;
ds@1952
  1472
ds@1952
  1473
						/* First, move our old data out of the way. */
ds@1952
  1474
						memmove(moveTo, moveFrom, realBytesToCopy);
ds@1952
  1475
ds@1952
  1476
						/* Now, do our replacement. */
ds@1952
  1477
						memcpy(ptr, replace, replaceLen);
ds@1952
  1478
					}
ds@1952
  1479
				}
ds@1952
  1480
				else
ds@1952
  1481
				{
ds@1952
  1482
					/* EXAMPLE CASE:
ds@1952
  1483
					 * Subject: AABBBCCC
ds@1952
  1484
					 * Buffer : 12 bytes
ds@1952
  1485
					 * Search : BBB
ds@1952
  1486
					 * Replace: DDDD
ds@1952
  1487
					 * OUTPUT : AADDDDCCC
ds@1952
  1488
					 * POSITION:      ^
ds@1952
  1489
					 */
ds@1952
  1490
					/* Yes, we have enough space.  Do a normal move operation. */
ds@1952
  1491
					char *moveFrom = ptr + searchLen;
ds@1952
  1492
					char *moveTo = ptr + replaceLen;
ds@1952
  1493
ds@1952
  1494
					/* First move our old data out of the way. */
ds@1952
  1495
					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1496
					memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1497
ds@1952
  1498
					/* Now do our replacement. */
ds@1952
  1499
					memcpy(ptr, replace, replaceLen);
ds@1952
  1500
				}
ds@1952
  1501
			}
ds@1952
  1502
			else if (replaceLen < searchLen)
ds@1952
  1503
			{
ds@1952
  1504
				/* EXAMPLE CASE:
ds@1952
  1505
				 * Subject: AABBBCCC
ds@1952
  1506
				 * Buffer : 12 bytes
ds@1952
  1507
				 * Search : BBB
ds@1952
  1508
				 * Replace: D
ds@1952
  1509
				 * OUTPUT : AADCCC
ds@1952
  1510
				 * POSOTION:   ^
ds@1952
  1511
				 */
ds@1952
  1512
				/* If the replacement does not grow the string length, we do not
ds@1952
  1513
				 * need to do any fancy checking at all.  Yay!
ds@1952
  1514
				 */
ds@1952
  1515
				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
ds@1952
  1516
				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
ds@1952
  1517
ds@1952
  1518
				/* Copy our replacement in, if any */
ds@1952
  1519
				if (replaceLen)
ds@1952
  1520
				{
ds@1952
  1521
					memcpy(ptr, replace, replaceLen);
ds@1952
  1522
				}
ds@1952
  1523
ds@1952
  1524
				/* Figure out how many bytes to move down, including null terminator */
ds@1952
  1525
				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
ds@1952
  1526
ds@1952
  1527
				/* Move the rest of the string down */
ds@1952
  1528
				memmove(moveTo, moveFrom, bytesToCopy);
ds@1952
  1529
			}
ds@1952
  1530
			else
ds@1952
  1531
			{
ds@1952
  1532
				/* EXAMPLE CASE:
ds@1952
  1533
				 * Subject: AABBBCCC
ds@1952
  1534
				 * Buffer : 12 bytes
ds@1952
  1535
				 * Search : BBB
ds@1952
  1536
				 * Replace: DDD
ds@1952
  1537
				 * OUTPUT : AADDDCCC
ds@1952
  1538
				 * POSITION:     ^
ds@1952
  1539
				 */
ds@1952
  1540
				/* We don't have to move anything around, just do a straight copy */
ds@1952
  1541
				memcpy(ptr, replace, replaceLen);
ds@1952
  1542
			}
ds@1952
  1543
ds@1952
  1544
			return ptr + replaceLen;
ds@1952
  1545
		}
ds@1952
  1546
		ptr++;
ds@1952
  1547
		browsed++;
ds@1952
  1548
	}
ds@1952
  1549
ds@1952
  1550
	return NULL;
ds@1952
  1551
}
ds@1952
  1552
ds@1952
  1553
char *UTIL_TrimWhitespace(char *str, size_t &len)
ds@1952
  1554
{
ds@1952
  1555
	char *end = str + len - 1;
ds@1952
  1556
ds@1952
  1557
	if (!len)
ds@1952
  1558
	{
ds@1952
  1559
		return str;
ds@1952
  1560
	}
ds@1952
  1561
ds@1952
  1562
	/* Iterate backwards through string until we reach first non-whitespace char */
ds@1952
  1563
	while (end >= str && textparsers->IsWhitespace(end))
ds@1952
  1564
	{
ds@1952
  1565
		end--;
ds@1952
  1566
		len--;
ds@1952
  1567
	}
ds@1952
  1568
ds@1952
  1569
	/* Replace first whitespace char (at the end) with null terminator.
ds@1952
  1570
	 * If there is none, we're just replacing the null terminator. 
ds@1952
  1571
	 */
ds@1952
  1572
	*(end + 1) = '\0';
ds@1952
  1573
ds@1952
  1574
	while (*str != '\0' && textparsers->IsWhitespace(str))
ds@1952
  1575
	{
ds@1952
  1576
		str++;
ds@1952
  1577
		len--;
ds@1952
  1578
	}
ds@1952
  1579
ds@1952
  1580
	return str;
ds@1952
  1581
}
ds@1952
  1582