dvander@370: /**
ds@653: * vim: set ts=4 :
ds@1330: * =============================================================================
dvander@1231: * SourceMod
dvander@1231: * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
ds@1330: * =============================================================================
dvander@370: *
ds@1330: * This program is free software; you can redistribute it and/or modify it under
ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330: * Free Software Foundation.
dvander@1231: *
ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
ds@1330: * details.
dvander@1231: *
ds@1330: * You should have received a copy of the GNU General Public License along with
ds@1330: * this program. If not, see .
dvander@1231: *
ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in
ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants
ds@1330: * this exception to all derivative works. AlliedModders LLC defines further
ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330: * or .
dvander@370: *
dvander@370: * Version: $Id$
dvander@370: */
dvander@370:
faluco@230: #include
faluco@230: #include
faluco@274: #include
faluco@227: #include "sm_stringutil.h"
dvander@592: #include "Logger.h"
faluco@389: #include "PluginSys.h"
dvander@591: #include "Translator.h"
dvander@591: #include "PlayerManager.h"
faluco@227:
faluco@227: #define LADJUST 0x00000004 /* left adjustment */
faluco@227: #define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
ds@609: #define UPPERDIGITS 0x00000200 /* make alpha digits uppercase */
faluco@227: #define to_digit(c) ((c) - '0')
faluco@227: #define is_digit(c) ((unsigned)to_digit(c) <= 9)
faluco@227:
faluco@288: #define CHECK_ARGS(x) \
faluco@288: if ((arg+x) > args) { \
dvander@599: pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
faluco@288: return 0; \
faluco@227: }
faluco@227:
dvander@728: size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
faluco@389: {
dvander@728: Translation pTrans;
dvander@728: TransError err;
dvander@728:
faluco@1352: err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetClientLanguage(client), &pTrans);
faluco@1352: if (err != Trans_Okay)
faluco@422: {
faluco@1352: err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
faluco@1352: if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
faluco@1352: {
faluco@1352: err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
faluco@1352: }
faluco@422: }
dvander@728:
dvander@728: if (err != Trans_Okay)
dvander@728: {
dvander@728: return UTIL_Format(buffer, maxlength, "%s", phrase);
dvander@728: }
dvander@728:
dvander@728: return g_Translator.Translate(buffer, maxlength, params, &pTrans);
faluco@422: }
faluco@389:
faluco@422: inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
faluco@422: {
faluco@422: TransError err = Trans_BadLanguage;
faluco@389: CPhraseFile *phrfl;
faluco@389:
faluco@389: for (size_t i=0; iGetLangFileByIndex(i));
faluco@422: err = phrfl->GetTranslation(key, langid, pTrans);
faluco@389: }
faluco@389:
faluco@422: return (err == Trans_Okay) ? true : false;
faluco@422: }
faluco@422:
faluco@422: size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error)
faluco@422: {
faluco@422: unsigned int langid;
faluco@422: *error = false;
dvander@464: Translation pTrans;
dvander@464: CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
dvander@464: size_t langcount = pl->GetLangFileCount();
dvander@464: void *new_params[MAX_TRANSLATE_PARAMS];
dvander@464: unsigned int max_params = 0;
faluco@422:
faluco@422: try_serverlang:
faluco@422: if (target == LANG_SERVER)
faluco@389: {
ds@669: langid = g_Translator.GetServerLanguage();
dvander@663: } else if ((target >= 1) && (target <= g_Players.GetMaxClients())) {
dvander@1105: langid = g_Translator.GetClientLanguage(target);
faluco@422: } else {
faluco@422: pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
faluco@422: goto error_out;
faluco@422: }
faluco@422:
faluco@422: if (!TryTranslation(pl, key, langid, langcount, &pTrans))
faluco@422: {
dvander@909: if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
faluco@422: {
faluco@422: target = LANG_SERVER;
faluco@422: goto try_serverlang;
dvander@663: } else if (langid != LANGUAGE_ENGLISH) {
dvander@663: if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
faluco@422: {
faluco@422: pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
faluco@422: goto error_out;
faluco@422: }
dvander@909: } else {
dvander@919: pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
dvander@909: goto error_out;
faluco@422: }
faluco@389: }
faluco@389:
dvander@474: max_params = pTrans.fmt_count;
faluco@1352:
dvander@909: if (max_params)
faluco@389: {
dvander@909: /* Check if we're going to over the limit */
dvander@909: if ((*arg) + (max_params - 1) > (size_t)params[0])
faluco@389: {
dvander@909: pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX,
dvander@909: "Translation string formatted incorrectly - missing at least %d parameters",
dvander@909: ((*arg + (max_params - 1)) - params[0])
dvander@909: );
faluco@422: goto error_out;
faluco@389: }
dvander@909:
dvander@909: /* Translate the parameters to raw pointers */
dvander@909: for (size_t i=0; iLocalToPhysAddr(params[*arg], reinterpret_cast(&new_params[i]));
dvander@909: (*arg)++;
dvander@909: }
faluco@389: }
faluco@389:
faluco@389: return g_Translator.Translate(buffer, maxlen, new_params, &pTrans);
faluco@422: error_out:
faluco@422: *error = true;
faluco@422: return 0;
faluco@389: }
faluco@389:
dvander@276: //:TODO: review this code before we choose a license
dvander@276:
faluco@227: void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
faluco@227: {
faluco@227: int size = 0;
faluco@227: char *buf;
faluco@227: static char nlstr[] = {'(','n','u','l','l',')','\0'};
faluco@227:
faluco@227: buf = *buf_p;
faluco@227:
faluco@227: if (string == NULL)
faluco@227: {
faluco@227: string = nlstr;
faluco@227: prec = -1;
faluco@227: }
faluco@227:
faluco@227: if (prec >= 0)
faluco@227: {
faluco@227: for (size = 0; size < prec; size++)
faluco@227: {
faluco@227: if (string[size] == '\0')
faluco@227: {
faluco@227: break;
faluco@227: }
faluco@227: }
faluco@227: } else {
faluco@227: while (string[size++]);
faluco@227: size--;
faluco@227: }
faluco@227:
faluco@227: if (size > (int)maxlen)
faluco@227: {
faluco@227: size = maxlen;
faluco@227: }
faluco@227:
faluco@227: maxlen -= size;
faluco@227: width -= size;
faluco@227:
faluco@227: while (size--)
faluco@227: {
faluco@227: *buf++ = *string++;
faluco@227: }
faluco@227:
faluco@227: while ((width-- > 0) && maxlen)
faluco@227: {
faluco@227: *buf++ = ' ';
faluco@227: maxlen--;
faluco@227: }
faluco@227:
faluco@227: *buf_p = buf;
faluco@227: }
faluco@227:
faluco@227: void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec)
faluco@227: {
faluco@227: char text[32];
faluco@227: int digits;
faluco@227: double signedVal;
faluco@227: char *buf;
faluco@227: int val;
faluco@227:
faluco@227: // get the sign
faluco@227: signedVal = fval;
faluco@227: if (fval < 0)
faluco@227: {
faluco@227: fval = -fval;
faluco@227: }
faluco@227:
faluco@227: // write the float number
faluco@227: digits = 0;
faluco@227: val = (int)fval;
faluco@227: do
faluco@227: {
faluco@227: text[digits++] = '0' + val % 10;
faluco@227: val /= 10;
faluco@227: } while (val);
faluco@227:
faluco@227: if (signedVal < 0)
faluco@227: {
faluco@227: text[digits++] = '-';
faluco@227: }
faluco@227:
faluco@227: buf = *buf_p;
faluco@227:
faluco@227: while ((digits < width) && maxlen)
faluco@227: {
faluco@227: *buf++ = ' ';
faluco@227: width--;
faluco@227: maxlen--;
faluco@227: }
faluco@227:
faluco@227: while ((digits--) && maxlen)
faluco@227: {
faluco@227: *buf++ = text[digits];
faluco@227: maxlen--;
faluco@227: }
faluco@227:
faluco@227: *buf_p = buf;
faluco@227:
faluco@227: if (prec < 0)
faluco@227: {
faluco@227: prec = 6;
faluco@227: }
faluco@227: // write the fraction
faluco@227: digits = 0;
faluco@227: while (digits < prec)
faluco@227: {
faluco@227: fval -= (int)fval;
faluco@227: fval *= 10.0;
faluco@227: val = (int)fval;
faluco@227: text[digits++] = '0' + val % 10;
faluco@227: }
faluco@227:
faluco@227: if ((digits > 0) && maxlen)
faluco@227: {
faluco@227: buf = *buf_p;
faluco@227: *buf++ = '.';
faluco@227: maxlen--;
faluco@227: for (prec = 0; maxlen && (prec < digits); prec++)
faluco@227: {
faluco@227: *buf++ = text[prec];
faluco@227: maxlen--;
faluco@227: }
faluco@227: *buf_p = buf;
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
faluco@227: {
faluco@227: char text[32];
faluco@227: int digits;
faluco@227: char *buf;
faluco@227:
faluco@227: digits = 0;
faluco@227: do
faluco@227: {
faluco@227: text[digits++] = '0' + val % 10;
faluco@227: val /= 10;
faluco@227: } while (val);
faluco@227:
faluco@227: buf = *buf_p;
faluco@227:
faluco@227: if (!(flags & LADJUST))
faluco@227: {
faluco@227: while (digits < width && maxlen)
faluco@227: {
faluco@227: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
faluco@227: width--;
faluco@227: maxlen--;
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: while (digits-- && maxlen)
faluco@227: {
faluco@227: *buf++ = text[digits];
faluco@227: width--;
faluco@227: maxlen--;
faluco@227: }
faluco@227:
faluco@227: if (flags & LADJUST)
faluco@227: {
faluco@227: while (width-- && maxlen)
faluco@227: {
faluco@227: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
faluco@227: maxlen--;
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: *buf_p = buf;
faluco@227: }
faluco@227:
faluco@227: void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
faluco@227: {
faluco@227: char text[32];
faluco@227: int digits;
faluco@227: int signedVal;
faluco@227: char *buf;
faluco@227: unsigned int unsignedVal;
faluco@227:
faluco@227: digits = 0;
faluco@227: signedVal = val;
faluco@227: if (val < 0)
faluco@227: {
faluco@227: /* we want the unsigned version */
faluco@227: unsignedVal = abs(val);
faluco@227: } else {
faluco@227: unsignedVal = val;
faluco@227: }
faluco@227: do
faluco@227: {
faluco@227: text[digits++] = '0' + unsignedVal % 10;
faluco@227: unsignedVal /= 10;
faluco@227: } while (unsignedVal);
faluco@227:
faluco@227: if (signedVal < 0)
faluco@227: {
faluco@227: text[digits++] = '-';
faluco@227: }
faluco@227:
faluco@227: buf = *buf_p;
faluco@227:
faluco@227: if (!(flags & LADJUST))
faluco@227: {
faluco@227: while ((digits < width) && maxlen)
faluco@227: {
faluco@227: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
faluco@227: width--;
faluco@227: maxlen--;
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: while (digits-- && maxlen)
faluco@227: {
faluco@227: *buf++ = text[digits];
faluco@227: width--;
faluco@227: maxlen--;
faluco@227: }
faluco@227:
faluco@227: if (flags & LADJUST)
faluco@227: {
faluco@227: while (width-- && maxlen)
faluco@227: {
faluco@227: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
faluco@227: maxlen--;
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: *buf_p = buf;
faluco@227: }
faluco@227:
ds@544: void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
ds@544: {
ds@544: char text[32];
ds@544: int digits;
ds@544: char *buf;
ds@544: char digit;
ds@544: int hexadjust;
ds@544:
ds@544: if (flags & UPPERDIGITS)
ds@544: {
ds@544: hexadjust = 'A' - '9' - 1;
ds@544: } else {
ds@544: hexadjust = 'a' - '9' - 1;
ds@544: }
ds@544:
ds@544: digits = 0;
ds@544: do
ds@544: {
ds@544: digit = ('0' + val % 16);
ds@544: if (digit > '9')
ds@544: {
ds@544: digit += hexadjust;
ds@544: }
ds@544:
ds@544: text[digits++] = digit;
ds@544: val /= 16;
ds@544: } while(val);
ds@544:
ds@544: buf = *buf_p;
ds@544:
ds@544: if (!(flags & LADJUST))
ds@544: {
ds@544: while (digits < width && maxlen)
ds@544: {
ds@544: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@544: width--;
ds@544: maxlen--;
ds@544: }
ds@544: }
ds@544:
ds@544: while (digits-- && maxlen)
ds@544: {
ds@544: *buf++ = text[digits];
ds@544: width--;
ds@544: maxlen--;
ds@544: }
ds@544:
ds@544: if (flags & LADJUST)
ds@544: {
ds@544: while (width-- && maxlen)
ds@544: {
ds@544: *buf++ = (flags & ZEROPAD) ? '0' : ' ';
ds@544: maxlen--;
ds@544: }
ds@544: }
ds@544:
ds@544: *buf_p = buf;
ds@544: }
ds@544:
dvander@276: size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
dvander@276: {
dvander@276: if (!buffer || !maxlen)
dvander@276: {
dvander@276: return 0;
dvander@276: }
dvander@276:
dvander@276: int arg = 0;
dvander@276: char *buf_p;
dvander@276: char ch;
dvander@276: int flags;
dvander@276: int width;
dvander@276: int prec;
dvander@276: int n;
dvander@276: char sign;
dvander@276: const char *fmt;
dvander@276: size_t llen = maxlen - 1;
dvander@276:
dvander@276: buf_p = buffer;
dvander@276: fmt = format;
dvander@276:
dvander@276: while (true)
dvander@276: {
dvander@276: // run through the format string until we hit a '%' or '\0'
dvander@276: for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
dvander@276: {
dvander@276: *buf_p++ = ch;
dvander@276: llen--;
dvander@276: }
dvander@276: if ((ch == '\0') || (llen <= 0))
dvander@276: {
dvander@276: goto done;
dvander@276: }
dvander@276:
dvander@276: // skip over the '%'
dvander@276: fmt++;
dvander@276:
dvander@276: // reset formatting state
dvander@276: flags = 0;
dvander@276: width = 0;
dvander@276: prec = -1;
dvander@276: sign = '\0';
dvander@276:
dvander@276: rflag:
dvander@276: ch = *fmt++;
dvander@276: reswitch:
dvander@276: switch(ch)
dvander@276: {
dvander@276: case '-':
dvander@276: {
dvander@276: flags |= LADJUST;
dvander@276: goto rflag;
dvander@276: }
dvander@276: case '.':
dvander@276: {
dvander@276: n = 0;
dvander@276: while(is_digit((ch = *fmt++)))
dvander@276: {
dvander@276: n = 10 * n + (ch - '0');
dvander@276: }
dvander@276: prec = (n < 0) ? -1 : n;
dvander@276: goto reswitch;
dvander@276: }
dvander@276: case '0':
dvander@276: {
dvander@276: flags |= ZEROPAD;
dvander@276: goto rflag;
dvander@276: }
dvander@276: case '1':
dvander@276: case '2':
dvander@276: case '3':
dvander@276: case '4':
dvander@276: case '5':
dvander@276: case '6':
dvander@276: case '7':
dvander@276: case '8':
dvander@276: case '9':
dvander@276: {
dvander@276: n = 0;
dvander@276: do
dvander@276: {
dvander@276: n = 10 * n + (ch - '0');
dvander@276: ch = *fmt++;
dvander@276: } while(is_digit(ch));
dvander@276: width = n;
dvander@276: goto reswitch;
dvander@276: }
dvander@276: case 'c':
dvander@276: {
dvander@276: if (!llen)
dvander@276: {
dvander@276: goto done;
dvander@276: }
dvander@276: char *c = (char *)args[arg];
dvander@276: *buf_p++ = *c;
dvander@276: llen--;
dvander@276: arg++;
dvander@276: break;
dvander@276: }
dvander@276: case 'd':
dvander@276: case 'i':
dvander@276: {
dvander@276: int *value = (int *)args[arg];
dvander@276: AddInt(&buf_p, llen, *value, width, flags);
dvander@276: arg++;
dvander@276: break;
dvander@276: }
dvander@276: case 'u':
dvander@276: {
dvander@276: unsigned int *value = (unsigned int *)args[arg];
dvander@276: AddUInt(&buf_p, llen, *value, width, flags);
dvander@276: arg++;
dvander@276: break;
dvander@276: }
dvander@276: case 'f':
dvander@276: {
dvander@276: float *value = (float *)args[arg];
dvander@276: AddFloat(&buf_p, llen, *value, width, prec);
dvander@276: arg++;
dvander@276: break;
dvander@276: }
dvander@276: case 's':
dvander@276: {
dvander@276: const char *str = (const char *)args[arg];
dvander@276: AddString(&buf_p, llen, str, width, prec);
dvander@276: arg++;
dvander@276: break;
dvander@276: }
ds@544: case 'X':
ds@544: {
ds@544: unsigned int *value = (unsigned int *)args[arg];
ds@544: flags |= UPPERDIGITS;
ds@544: AddHex(&buf_p, llen, *value, width, flags);
ds@544: arg++;
ds@544: break;
ds@544: }
ds@544: case 'x':
ds@544: {
ds@544: unsigned int *value = (unsigned int *)args[arg];
ds@544: AddHex(&buf_p, llen, *value, width, flags);
ds@544: arg++;
ds@544: break;
ds@544: }
dvander@276: case '%':
dvander@276: {
dvander@276: if (!llen)
dvander@276: {
dvander@276: goto done;
dvander@276: }
dvander@276: *buf_p++ = ch;
dvander@276: llen--;
dvander@276: break;
dvander@276: }
dvander@276: case '\0':
dvander@276: {
dvander@276: if (!llen)
dvander@276: {
dvander@276: goto done;
dvander@276: }
dvander@276: *buf_p++ = '%';
dvander@276: llen--;
dvander@276: goto done;
dvander@276: }
dvander@276: default:
dvander@276: {
dvander@276: if (!llen)
dvander@276: {
dvander@276: goto done;
dvander@276: }
dvander@276: *buf_p++ = ch;
dvander@276: llen--;
dvander@276: break;
dvander@276: }
dvander@276: }
dvander@276: }
dvander@276:
dvander@276: done:
dvander@276: *buf_p = '\0';
dvander@276:
dvander@276: return (maxlen - llen - 1);
dvander@276: }
dvander@276:
faluco@227: size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
faluco@227: {
dvander@276: if (!buffer || !maxlen)
dvander@276: {
dvander@276: return 0;
dvander@276: }
dvander@276:
faluco@227: int arg;
faluco@288: int args = params[0];
faluco@227: char *buf_p;
faluco@227: char ch;
faluco@227: int flags;
faluco@227: int width;
faluco@227: int prec;
faluco@227: int n;
faluco@227: char sign;
faluco@227: const char *fmt;
faluco@230: size_t llen = maxlen - 1;
faluco@227:
faluco@227: buf_p = buffer;
faluco@227: arg = *param;
faluco@227: fmt = format;
faluco@227:
faluco@227: while (true)
faluco@227: {
faluco@227: // run through the format string until we hit a '%' or '\0'
faluco@227: for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
faluco@227: {
faluco@227: *buf_p++ = ch;
faluco@227: llen--;
faluco@227: }
faluco@227: if ((ch == '\0') || (llen <= 0))
faluco@227: {
faluco@227: goto done;
faluco@227: }
faluco@227:
faluco@227: // skip over the '%'
faluco@227: fmt++;
faluco@227:
faluco@227: // reset formatting state
faluco@227: flags = 0;
faluco@227: width = 0;
faluco@227: prec = -1;
faluco@227: sign = '\0';
faluco@227:
faluco@227: rflag:
faluco@227: ch = *fmt++;
faluco@227: reswitch:
faluco@227: switch(ch)
faluco@227: {
faluco@227: case '-':
faluco@227: {
faluco@227: flags |= LADJUST;
faluco@227: goto rflag;
faluco@227: }
faluco@227: case '.':
faluco@227: {
faluco@227: n = 0;
faluco@227: while(is_digit((ch = *fmt++)))
faluco@227: {
faluco@227: n = 10 * n + (ch - '0');
faluco@227: }
faluco@227: prec = (n < 0) ? -1 : n;
faluco@227: goto reswitch;
faluco@227: }
faluco@227: case '0':
faluco@227: {
faluco@227: flags |= ZEROPAD;
faluco@227: goto rflag;
faluco@227: }
faluco@227: case '1':
faluco@227: case '2':
faluco@227: case '3':
faluco@227: case '4':
faluco@227: case '5':
faluco@227: case '6':
faluco@227: case '7':
faluco@227: case '8':
faluco@227: case '9':
faluco@227: {
faluco@227: n = 0;
faluco@227: do
faluco@227: {
faluco@227: n = 10 * n + (ch - '0');
faluco@227: ch = *fmt++;
faluco@227: } while(is_digit(ch));
faluco@227: width = n;
faluco@227: goto reswitch;
faluco@227: }
faluco@227: case 'c':
faluco@227: {
faluco@227: CHECK_ARGS(0);
faluco@230: if (!llen)
faluco@227: {
faluco@227: goto done;
faluco@227: }
faluco@227: char *c;
faluco@227: pCtx->LocalToString(params[arg], &c);
faluco@227: *buf_p++ = *c;
faluco@227: llen--;
faluco@227: arg++;
faluco@227: break;
faluco@227: }
faluco@227: case 'd':
faluco@227: case 'i':
faluco@227: {
faluco@227: CHECK_ARGS(0);
faluco@227: cell_t *value;
faluco@227: pCtx->LocalToPhysAddr(params[arg], &value);
faluco@227: AddInt(&buf_p, llen, static_cast(*value), width, flags);
faluco@227: arg++;
faluco@227: break;
faluco@227: }
faluco@227: case 'u':
faluco@227: {
faluco@227: CHECK_ARGS(0);
faluco@227: cell_t *value;
faluco@227: pCtx->LocalToPhysAddr(params[arg], &value);
faluco@227: AddUInt(&buf_p, llen, static_cast(*value), width, flags);
faluco@227: arg++;
faluco@227: break;
faluco@227: }
faluco@227: case 'f':
faluco@227: {
faluco@227: CHECK_ARGS(0);
faluco@227: cell_t *value;
faluco@227: pCtx->LocalToPhysAddr(params[arg], &value);
dvander@233: AddFloat(&buf_p, llen, sp_ctof(*value), width, prec);
faluco@227: arg++;
faluco@227: break;
faluco@227: }
dvander@905: case 'L':
dvander@905: {
dvander@905: CHECK_ARGS(0);
dvander@905: cell_t *value;
dvander@905: pCtx->LocalToPhysAddr(params[arg], &value);
dvander@905: char buffer[255];
dvander@906: if (*value)
dvander@905: {
dvander@906: CPlayer *player = g_Players.GetPlayerByIndex(*value);
dvander@906: if (!player || !player->IsConnected())
dvander@906: {
dvander@906: return pCtx->ThrowNativeError("Client index %d is invalid", *value);
dvander@906: }
dvander@906: const char *auth = player->GetAuthString();
dvander@906: if (!auth || auth[0] == '\0')
dvander@906: {
dvander@906: auth = "STEAM_ID_PENDING";
dvander@906: }
dvander@906: int userid = engine->GetPlayerUserId(player->GetEdict());
dvander@906: UTIL_Format(buffer,
dvander@906: sizeof(buffer),
dvander@906: "%s<%d><%s><>",
dvander@906: player->GetName(),
dvander@906: userid,
dvander@906: auth);
dvander@906: } else {
dvander@906: UTIL_Format(buffer,
dvander@906: sizeof(buffer),
dvander@906: "Console<0>");
dvander@905: }
dvander@905: AddString(&buf_p, llen, buffer, width, prec);
dvander@905: arg++;
dvander@905: break;
dvander@905: }
dvander@1420: case 'N':
dvander@1420: {
dvander@1420: CHECK_ARGS(0);
dvander@1420: cell_t *value;
dvander@1420: pCtx->LocalToPhysAddr(params[arg], &value);
dvander@1420:
dvander@1420: const char *name = "Console";
dvander@1420: if (*value)
dvander@1420: {
dvander@1420: CPlayer *player = g_Players.GetPlayerByIndex(*value);
dvander@1420: if (!player || !player->IsConnected())
dvander@1420: {
dvander@1420: return pCtx->ThrowNativeError("Client index %d is invalid", *value);
dvander@1420: }
dvander@1420: name = player->GetName();
dvander@1420: }
dvander@1420: AddString(&buf_p, llen, name, width, prec);
dvander@1420: arg++;
dvander@1420: break;
dvander@1420: }
faluco@227: case 's':
faluco@227: {
faluco@227: CHECK_ARGS(0);
faluco@227: char *str;
dvander@535: int err;
dvander@535: if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
dvander@535: {
dvander@535: pCtx->ThrowNativeErrorEx(err, "Could not deference string");
dvander@535: return 0;
dvander@535: }
faluco@227: AddString(&buf_p, llen, str, width, prec);
faluco@227: arg++;
faluco@227: break;
faluco@227: }
faluco@389: case 'T':
faluco@389: {
dvander@888: CHECK_ARGS(1);
faluco@389: char *key;
faluco@422: bool error;
faluco@422: size_t res;
ds@669: cell_t *target;
dvander@888: pCtx->LocalToString(params[arg++], &key);
ds@669: pCtx->LocalToPhysAddr(params[arg++], &target);
ds@669: res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
faluco@422: if (error)
faluco@422: {
faluco@422: return 0;
faluco@422: }
faluco@422: buf_p += res;
faluco@422: llen -= res;
faluco@422: break;
faluco@422: }
faluco@422: case 't':
faluco@422: {
faluco@422: CHECK_ARGS(0);
faluco@422: char *key;
faluco@422: bool error;
faluco@422: size_t res;
faluco@422: cell_t target = g_SourceMod.GetGlobalTarget();
faluco@422: pCtx->LocalToString(params[arg++], &key);
faluco@422: res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
faluco@422: if (error)
faluco@422: {
faluco@422: return 0;
faluco@422: }
faluco@422: buf_p += res;
faluco@422: llen -= res;
faluco@389: break;
faluco@389: }
ds@544: case 'X':
ds@544: {
ds@544: CHECK_ARGS(0);
ds@544: cell_t *value;
ds@544: pCtx->LocalToPhysAddr(params[arg], &value);
ds@544: flags |= UPPERDIGITS;
ds@544: AddHex(&buf_p, llen, static_cast(*value), width, flags);
ds@544: arg++;
ds@544: break;
ds@544: }
ds@544: case 'x':
ds@544: {
ds@544: CHECK_ARGS(0);
ds@544: cell_t *value;
ds@544: pCtx->LocalToPhysAddr(params[arg], &value);
ds@544: AddHex(&buf_p, llen, static_cast(*value), width, flags);
ds@544: arg++;
ds@544: break;
ds@544: }
faluco@227: case '%':
faluco@227: {
faluco@230: if (!llen)
faluco@227: {
faluco@227: goto done;
faluco@227: }
faluco@227: *buf_p++ = ch;
faluco@227: llen--;
faluco@227: break;
faluco@227: }
faluco@227: case '\0':
faluco@227: {
faluco@230: if (!llen)
faluco@227: {
faluco@227: goto done;
faluco@227: }
faluco@227: *buf_p++ = '%';
faluco@227: llen--;
faluco@227: goto done;
faluco@227: }
faluco@227: default:
faluco@227: {
faluco@230: if (!llen)
faluco@227: {
faluco@227: goto done;
faluco@227: }
faluco@227: *buf_p++ = ch;
faluco@227: llen--;
faluco@227: break;
faluco@227: }
faluco@227: }
faluco@227: }
faluco@227:
faluco@227: done:
faluco@227: *buf_p = '\0';
faluco@227: *param = arg;
faluco@236: return (maxlen - llen - 1);
faluco@227: }
faluco@230:
faluco@230: const char *stristr(const char *str, const char *substr)
faluco@230: {
faluco@230: if (!*substr)
faluco@230: {
faluco@230: return ((char *)str);
faluco@230: }
faluco@230:
faluco@230: char *needle = (char *)substr;
faluco@230: char *prevloc = (char *)str;
faluco@230: char *haystack = (char *)str;
faluco@230:
faluco@230: while (*haystack)
faluco@230: {
faluco@230: if (tolower(*haystack) == tolower(*needle))
faluco@230: {
faluco@230: haystack++;
faluco@230: if (!*++needle)
faluco@230: {
faluco@230: return prevloc;
faluco@230: }
faluco@230: } else {
faluco@230: haystack = ++prevloc;
faluco@230: needle = (char *)substr;
faluco@230: }
faluco@230: }
faluco@230:
faluco@230: return NULL;
faluco@230: }
faluco@230:
dvander@233: unsigned int strncopy(char *dest, const char *src, size_t count)
faluco@230: {
faluco@230: if (!count)
faluco@230: {
faluco@230: return 0;
faluco@230: }
faluco@230:
faluco@230: char *start = dest;
faluco@230: while ((*src) && (--count))
faluco@230: {
faluco@230: *dest++ = *src++;
faluco@230: }
faluco@230: *dest = '\0';
faluco@230:
faluco@230: return (dest - start);
faluco@230: }
faluco@274:
faluco@274: size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
faluco@274: {
faluco@274: va_list ap;
faluco@274: va_start(ap, fmt);
faluco@274: size_t len = vsnprintf(buffer, maxlength, fmt, ap);
faluco@274: va_end(ap);
faluco@274:
faluco@651: if (len >= maxlength)
faluco@651: {
faluco@651: buffer[maxlength - 1] = '\0';
faluco@651: return (maxlength - 1);
faluco@651: } else {
faluco@651: return len;
faluco@651: }
dvander@351: }
dvander@351:
dvander@663: size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
dvander@663: {
dvander@663: size_t len = vsnprintf(buffer, maxlength, fmt, ap);
dvander@663:
dvander@663: if (len >= maxlength)
dvander@663: {
dvander@663: buffer[maxlength - 1] = '\0';
dvander@663: return (maxlength - 1);
dvander@663: } else {
dvander@663: return len;
dvander@663: }
dvander@663: }
dvander@663:
ds@504: char *sm_strdup(const char *str)
ds@504: {
ds@504: char *ptr = new char[strlen(str)+1];
ds@504: strcpy(ptr, str);
ds@504: return ptr;
ds@504: }
dvander@833:
dvander@833: unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
dvander@833: {
dvander@833: size_t searchLen = strlen(search);
dvander@833: size_t replaceLen = strlen(replace);
dvander@833:
dvander@833: char *ptr = subject;
dvander@833: unsigned int total = 0;
dvander@833: while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
dvander@833: {
dvander@833: total++;
dvander@833: if (*ptr == '\0')
dvander@833: {
dvander@833: break;
dvander@833: }
dvander@833: }
dvander@833:
dvander@833: return total;
dvander@833: }
dvander@833:
dvander@833: /**
dvander@833: * NOTE: Do not edit this for the love of god unless you have
dvander@833: * read the test cases and understand the code behind each one.
dvander@833: * While I don't guarantee there aren't mistakes, I do guarantee
dvander@833: * that plugins will end up relying on tiny idiosyncracies of this
dvander@833: * function, just like they did with AMX Mod X.
dvander@833: *
dvander@833: * There are explicitly more cases than the AMX Mod X version because
dvander@833: * we're not doing a blind copy. Each case is specifically optimized
dvander@833: * for what needs to be done. Even better, we don't have to error on
dvander@833: * bad buffer sizes. Instead, this function will smartly cut off the
dvander@833: * string in a way that pushes old data out.
dvander@833: */
dvander@833: char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
dvander@833: {
dvander@833: char *ptr = subject;
dvander@833: size_t browsed = 0;
dvander@833: size_t textLen = strlen(subject);
dvander@833:
dvander@833: /* It's not possible to search or replace */
dvander@1505: if (searchLen > textLen)
dvander@833: {
dvander@833: return NULL;
dvander@833: }
dvander@833:
dvander@1505: /* Handle the case of one byte replacement.
dvander@1505: * It's only valid in one case.
dvander@1505: */
dvander@1505: if (maxLen == 1)
dvander@1505: {
dvander@1505: /* If the search matches and the replace length is 0,
dvander@1505: * we can just terminate the string and be done.
dvander@1505: */
dvander@1505: if (strcmp(subject, search) == 0 && replaceLen == 0)
dvander@1505: {
dvander@1505: *subject = '\0';
dvander@1505: return subject;
dvander@1505: }
dvander@1505: else
dvander@1505: {
dvander@1505: return NULL;
dvander@1505: }
dvander@1505: }
dvander@1505:
dvander@833: /* Subtract one off the maxlength so we can include the null terminator */
dvander@833: maxLen--;
dvander@833:
dvander@833: while (*ptr != '\0' && (browsed <= textLen - searchLen))
dvander@833: {
dvander@833: /* See if we get a comparison */
dvander@833: if (strncmp(ptr, search, searchLen) == 0)
dvander@833: {
dvander@833: if (replaceLen > searchLen)
dvander@833: {
dvander@833: /* First, see if we have enough space to do this operation */
dvander@833: if (maxLen - textLen < replaceLen - searchLen)
dvander@833: {
dvander@833: /* First, see if the replacement length goes out of bounds. */
dvander@833: if (browsed + replaceLen >= maxLen)
dvander@833: {
dvander@833: /* EXAMPLE CASE:
dvander@833: * Subject: AABBBCCC
dvander@833: * Buffer : 12 bytes
dvander@833: * Search : BBB
dvander@833: * Replace: DDDDDDDDDD
dvander@833: * OUTPUT : AADDDDDDDDD
dvander@833: * POSITION: ^
dvander@833: */
dvander@833: /* If it does, we'll just bound the length and do a strcpy. */
dvander@833: replaceLen = maxLen - browsed;
dvander@833: /* Note, we add one to the final result for the null terminator */
dvander@833: strncopy(ptr, replace, replaceLen+1);
dvander@833: } else {
dvander@833: /* EXAMPLE CASE:
dvander@833: * Subject: AABBBCCC
dvander@833: * Buffer : 12 bytes
dvander@833: * Search : BBB
dvander@833: * Replace: DDDDDDD
dvander@833: * OUTPUT : AADDDDDDDCC
dvander@833: * POSITION: ^
dvander@833: */
dvander@833: /* We're going to have some bytes left over... */
dvander@833: size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@833: size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
dvander@833: char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
dvander@833: char *moveTo = ptr + replaceLen;
dvander@833:
dvander@833: /* First, move our old data out of the way. */
dvander@833: memmove(moveTo, moveFrom, realBytesToCopy);
dvander@833:
dvander@833: /* Now, do our replacement. */
dvander@833: memcpy(ptr, replace, replaceLen);
dvander@833: }
dvander@833: } else {
dvander@833: /* EXAMPLE CASE:
dvander@833: * Subject: AABBBCCC
dvander@833: * Buffer : 12 bytes
dvander@833: * Search : BBB
dvander@833: * Replace: DDDD
dvander@833: * OUTPUT : AADDDDCCC
dvander@833: * POSITION: ^
dvander@833: */
dvander@833: /* Yes, we have enough space. Do a normal move operation. */
dvander@833: char *moveFrom = ptr + searchLen;
dvander@833: char *moveTo = ptr + replaceLen;
dvander@833:
dvander@833: /* First move our old data out of the way. */
dvander@833: size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@833: memmove(moveTo, moveFrom, bytesToCopy);
dvander@833:
dvander@833: /* Now do our replacement. */
dvander@833: memcpy(ptr, replace, replaceLen);
dvander@833: }
dvander@833: } else if (replaceLen < searchLen) {
dvander@833: /* EXAMPLE CASE:
dvander@833: * Subject: AABBBCCC
dvander@833: * Buffer : 12 bytes
dvander@833: * Search : BBB
dvander@833: * Replace: D
dvander@833: * OUTPUT : AADCCC
dvander@833: * POSOTION: ^
dvander@833: */
dvander@833: /* If the replacement does not grow the string length, we do not
dvander@833: * need to do any fancy checking at all. Yay!
dvander@833: */
dvander@833: char *moveFrom = ptr + searchLen; /* Start after the search pointer */
dvander@833: char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
dvander@833:
dvander@833: /* Copy our replacement in, if any */
dvander@833: if (replaceLen)
dvander@833: {
dvander@833: memcpy(ptr, replace, replaceLen);
dvander@833: }
dvander@833:
dvander@833: /* Figure out how many bytes to move down, including null terminator */
dvander@833: size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@833:
dvander@833: /* Move the rest of the string down */
dvander@833: memmove(moveTo, moveFrom, bytesToCopy);
dvander@833: } else {
dvander@833: /* EXAMPLE CASE:
dvander@833: * Subject: AABBBCCC
dvander@833: * Buffer : 12 bytes
dvander@833: * Search : BBB
dvander@833: * Replace: DDD
dvander@833: * OUTPUT : AADDDCCC
dvander@833: * POSITION: ^
dvander@833: */
dvander@833: /* We don't have to move anything around, just do a straight copy */
dvander@833: memcpy(ptr, replace, replaceLen);
dvander@833: }
dvander@833:
dvander@833: return ptr + replaceLen;
dvander@833: }
dvander@833: ptr++;
dvander@833: browsed++;
dvander@833: }
dvander@833:
dvander@833: return NULL;
dvander@833: }