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: }