3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
35 #include <ITextParsers.h>
36 #include "sm_stringutil.h"
38 #include "PlayerManager.h"
39 #include "logic_bridge.h"
40 #include "sourcemod.h"
42 #define LADJUST 0x00000004 /* left adjustment */
43 #define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
44 #define UPPERDIGITS 0x00000200 /* make alpha digits uppercase */
45 #define to_digit(c) ((c) - '0')
46 #define is_digit(c) ((unsigned)to_digit(c) <= 9)
48 #define CHECK_ARGS(x) \
49 if ((arg+x) > args) { \
50 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
54 inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
56 cell_t new_params[MAX_TRANSLATE_PARAMS];
57 for (unsigned int i = 0; i < pTrans->fmt_count; i++)
59 new_params[i] = params[pTrans->fmt_order[i]];
61 memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
64 size_t Translate(char *buffer,
76 IPlugin *pl = scripts->FindPluginByContext(pCtx->GetContext());
77 unsigned int max_params = 0;
78 IPhraseCollection *pPhrases;
80 pPhrases = pl->GetPhrases();
83 if (target == SOURCEMOD_SERVER_LANGUAGE)
85 langid = translator->GetServerLanguage();
87 else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
89 langid = translator->GetClientLanguage(target);
93 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
97 if (pPhrases->FindTranslation(key, langid, &pTrans) != Trans_Okay)
99 if (target != SOURCEMOD_SERVER_LANGUAGE && langid != translator->GetServerLanguage())
101 target = SOURCEMOD_SERVER_LANGUAGE;
104 else if (langid != SOURCEMOD_LANGUAGE_ENGLISH)
106 if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &pTrans) != Trans_Okay)
108 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
114 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
119 max_params = pTrans.fmt_count;
123 cell_t new_params[MAX_TRANSLATE_PARAMS];
125 /* Check if we're going to over the limit */
126 if ((*arg) + (max_params - 1) > (size_t)params[0])
128 pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX,
129 "Translation string formatted incorrectly - missing at least %d parameters",
130 ((*arg + (max_params - 1)) - params[0])
135 /* If we need to re-order the parameters, do so with a temporary array.
136 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
138 memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
139 ReorderTranslationParams(&pTrans, &new_params[*arg]);
141 return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
145 return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
153 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
157 static char nlstr[] = {'(','n','u','l','l',')','\0'};
169 for (size = 0; size < prec; size++)
171 if (string[size] == '\0')
179 while (string[size++]);
183 if (size > (int)maxlen)
196 while ((width-- > 0) && maxlen)
205 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
207 int digits; // non-fraction part digits
208 double tmp; // temporary
209 char *buf = *buf_p; // output buffer pointer
210 int val; // temporary
211 int sign = 0; // 0: positive, 1: negative
212 int fieldlength; // for padding
213 int significant_digits = 0; // number of significant digits written
214 const int MAX_SIGNIFICANT_DIGITS = 16;
217 AddString(buf_p, maxlen, "NaN", width, prec);
234 // compute whole-part digits count
235 digits = (int)log10(fval) + 1;
237 // Only print 0.something if 0 < fval < 1
243 // compute the field length
244 fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
246 // minus sign BEFORE left padding if padding with zeros
247 if (sign && maxlen && (flags & ZEROPAD))
253 // right justify if required
254 if ((flags & LADJUST) == 0)
256 while ((fieldlength < width) && maxlen)
258 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
264 // minus sign AFTER left padding if padding with spaces
265 if (sign && maxlen && !(flags & ZEROPAD))
271 // write the whole part
272 tmp = pow(10.0, digits-1);
273 while ((digits--) && maxlen)
275 if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
281 val = (int)(fval / tmp);
289 // write the fraction part
296 tmp = pow(10.0, prec);
299 while (prec-- && maxlen)
301 if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
308 val = (int)(fval / tmp);
315 // left justify if required
318 while ((fieldlength < width) && maxlen)
320 // right-padding only with spaces, ZEROPAD is ignored
327 // update parent's buffer pointer
331 void AddBinary(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
342 text[digits++] = '1';
346 text[digits++] = '0';
353 if (!(flags & LADJUST))
355 while (digits < width && maxlen)
357 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
363 while (digits-- && maxlen)
365 *buf++ = text[digits];
372 while (width-- && maxlen)
374 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
382 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
391 text[digits++] = '0' + val % 10;
397 if (!(flags & LADJUST))
399 while (digits < width && maxlen)
401 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
407 while (digits-- && maxlen)
409 *buf++ = text[digits];
416 while (width-- && maxlen)
418 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
426 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
432 unsigned int unsignedVal;
438 /* we want the unsigned version */
439 unsignedVal = abs(val);
448 text[digits++] = '0' + unsignedVal % 10;
450 } while (unsignedVal);
454 text[digits++] = '-';
459 if (!(flags & LADJUST))
461 while ((digits < width) && maxlen)
463 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
469 while (digits-- && maxlen)
471 *buf++ = text[digits];
478 while (width-- && maxlen)
480 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
488 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
496 if (flags & UPPERDIGITS)
498 hexadjust = 'A' - '9' - 1;
502 hexadjust = 'a' - '9' - 1;
508 digit = ('0' + val % 16);
514 text[digits++] = digit;
520 if (!(flags & LADJUST))
522 while (digits < width && maxlen)
524 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
530 while (digits-- && maxlen)
532 *buf++ = text[digits];
539 while (width-- && maxlen)
541 *buf++ = (flags & ZEROPAD) ? '0' : ' ';
549 bool gnprintf(char *buffer,
552 IPhraseCollection *pPhrases,
554 unsigned int numparams,
555 unsigned int &curparam,
557 const char **pFailPhrase)
559 if (!buffer || !maxlen)
561 if (pOutLength != NULL)
568 if (numparams > MAX_TRANSLATE_PARAMS)
570 if (pFailPhrase != NULL)
586 size_t llen = maxlen - 1;
593 // run through the format string until we hit a '%' or '\0'
594 for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
599 if ((ch == '\0') || (llen <= 0))
607 // reset formatting state
626 while(is_digit((ch = *fmt++)))
628 n = 10 * n + (ch - '0');
630 prec = (n < 0) ? -1 : n;
651 n = 10 * n + (ch - '0');
653 } while(is_digit(ch));
663 if (curparam >= numparams)
665 if (pFailPhrase != NULL)
671 char *c = (char *)params[curparam];
680 if (curparam >= numparams)
682 if (pFailPhrase != NULL)
688 int *value = (int *)params[curparam];
690 AddBinary(&buf_p, llen, *value, width, flags);
697 if (curparam >= numparams)
699 if (pFailPhrase != NULL)
705 int *value = (int *)params[curparam];
707 AddInt(&buf_p, llen, *value, width, flags);
713 if (curparam >= numparams)
715 if (pFailPhrase != NULL)
721 unsigned int *value = (unsigned int *)params[curparam];
723 AddUInt(&buf_p, llen, *value, width, flags);
729 if (curparam >= numparams)
731 if (pFailPhrase != NULL)
737 float *value = (float *)params[curparam];
739 AddFloat(&buf_p, llen, *value, width, prec, flags);
745 if (curparam >= numparams)
747 if (pFailPhrase != NULL)
753 const char *str = (const char *)params[curparam];
755 AddString(&buf_p, llen, str, width, prec);
766 unsigned int lang_id;
768 if (curparam >= numparams)
770 if (pFailPhrase != NULL)
776 key = (const char *)(params[curparam]);
781 if (curparam >= numparams)
783 if (pFailPhrase != NULL)
789 target = *((int *)(params[curparam]));
794 target = translator->GetGlobalTarget();
798 if (target == SOURCEMOD_SERVER_LANGUAGE)
800 lang_id = translator->GetServerLanguage();
802 else if (target >= 1 && target <= g_Players.GetMaxClients())
804 lang_id = translator->GetClientLanguage(target);
808 lang_id = translator->GetServerLanguage();
811 if (pPhrases == NULL)
813 if (pFailPhrase != NULL)
820 if (pPhrases->FindTranslation(key, lang_id, &trans) != Trans_Okay)
822 if (target != SOURCEMOD_SERVER_LANGUAGE && lang_id != translator->GetServerLanguage())
824 target = SOURCEMOD_SERVER_LANGUAGE;
827 else if (lang_id != SOURCEMOD_LANGUAGE_ENGLISH)
829 if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &trans) != Trans_Okay)
831 if (pFailPhrase != NULL)
840 if (pFailPhrase != NULL)
851 void *new_params[MAX_TRANSLATE_PARAMS];
853 if (curparam + trans.fmt_count > numparams)
855 if (pFailPhrase != NULL)
862 /* Copy the array and re-order the stack */
863 memcpy(new_params, params, sizeof(void *) * numparams);
864 for (i = 0; i < trans.fmt_count; i++)
866 new_params[curparam + i] = const_cast<void *>(params[curparam + trans.fmt_order[i]]);
905 if (curparam >= numparams)
907 if (pFailPhrase != NULL)
913 unsigned int *value = (unsigned int *)params[curparam];
915 flags |= UPPERDIGITS;
916 AddHex(&buf_p, llen, *value, width, flags);
922 if (curparam >= numparams)
924 if (pFailPhrase != NULL)
930 unsigned int *value = (unsigned int *)params[curparam];
932 AddHex(&buf_p, llen, *value, width, flags);
972 if (pOutLength != NULL)
974 *pOutLength = (maxlen - llen - 1);
980 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
982 if (!buffer || !maxlen)
988 int args = params[0];
997 size_t llen = maxlen - 1;
1005 // run through the format string until we hit a '%' or '\0'
1006 for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
1011 if ((ch == '\0') || (llen <= 0))
1016 // skip over the '%'
1019 // reset formatting state
1038 while(is_digit((ch = *fmt++)))
1040 n = 10 * n + (ch - '0');
1042 prec = (n < 0) ? -1 : n;
1063 n = 10 * n + (ch - '0');
1065 } while(is_digit(ch));
1077 pCtx->LocalToString(params[arg], &c);
1087 pCtx->LocalToPhysAddr(params[arg], &value);
1088 AddBinary(&buf_p, llen, *value, width, flags);
1097 pCtx->LocalToPhysAddr(params[arg], &value);
1098 AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
1106 pCtx->LocalToPhysAddr(params[arg], &value);
1107 AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
1115 pCtx->LocalToPhysAddr(params[arg], &value);
1116 AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
1124 pCtx->LocalToPhysAddr(params[arg], &value);
1128 CPlayer *player = g_Players.GetPlayerByIndex(*value);
1129 if (!player || !player->IsConnected())
1131 return pCtx->ThrowNativeError("Client index %d is invalid", *value);
1133 const char *auth = player->GetAuthString();
1134 if (!auth || auth[0] == '\0')
1136 auth = "STEAM_ID_PENDING";
1138 int userid = GetPlayerUserId(player->GetEdict());
1150 "Console<0><Console><Console>");
1152 AddString(&buf_p, llen, buffer, width, prec);
1160 pCtx->LocalToPhysAddr(params[arg], &value);
1162 const char *name = "Console";
1165 CPlayer *player = g_Players.GetPlayerByIndex(*value);
1166 if (!player || !player->IsConnected())
1168 return pCtx->ThrowNativeError("Client index %d is invalid", *value);
1170 name = player->GetName();
1172 AddString(&buf_p, llen, name, width, prec);
1181 if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
1183 pCtx->ThrowNativeErrorEx(err, "Could not deference string");
1186 AddString(&buf_p, llen, str, width, prec);
1197 pCtx->LocalToString(params[arg++], &key);
1198 pCtx->LocalToPhysAddr(params[arg++], &target);
1199 res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
1214 cell_t target = g_SourceMod.GetGlobalTarget();
1215 pCtx->LocalToString(params[arg++], &key);
1216 res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
1229 pCtx->LocalToPhysAddr(params[arg], &value);
1230 flags |= UPPERDIGITS;
1231 AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
1239 pCtx->LocalToPhysAddr(params[arg], &value);
1240 AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
1280 return (maxlen - llen - 1);
1283 unsigned int strncopy(char *dest, const char *src, size_t count)
1291 while ((*src) && (--count))
1297 return (dest - start);
1300 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
1304 size_t len = vsnprintf(buffer, maxlength, fmt, ap);
1307 if (len >= maxlength)
1309 buffer[maxlength - 1] = '\0';
1310 return (maxlength - 1);
1318 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
1320 size_t len = vsnprintf(buffer, maxlength, fmt, ap);
1322 if (len >= maxlength)
1324 buffer[maxlength - 1] = '\0';
1325 return (maxlength - 1);
1333 char *sm_strdup(const char *str)
1335 char *ptr = new char[strlen(str)+1];
1340 char *UTIL_TrimWhitespace(char *str, size_t &len)
1342 char *end = str + len - 1;
1349 /* Iterate backwards through string until we reach first non-whitespace char */
1350 while (end >= str && textparsers->IsWhitespace(end))
1356 /* Replace first whitespace char (at the end) with null terminator.
1357 * If there is none, we're just replacing the null terminator.
1361 while (*str != '\0' && textparsers->IsWhitespace(str))
1370 char *UTIL_ToLowerCase(const char *str)
1372 size_t len = strlen(str);
1373 char *buffer = new char[len + 1];
1374 for (size_t i = 0; i < len; i++)
1376 if (str[i] >= 'A' && str[i] <= 'Z')
1377 buffer[i] = tolower(str[i]);