diff --git a/amxmodx/CLang.cpp b/amxmodx/CLang.cpp index 2c8adb66..cd81bd8f 100755 --- a/amxmodx/CLang.cpp +++ b/amxmodx/CLang.cpp @@ -324,277 +324,244 @@ int CLangMngr::GetKeyEntry(String &key) return val.index; } -#define CHECK_PTR(ptr, start, bufsize) if ((ptr) - (start) >= (bufsize)) { \ - LogError(amx, AMX_ERR_STACKERR, "Buffer overflow in string formatting"); \ - outbuf[0] = 0; \ - len = 0; \ - return outbuf; } -#define CHECK_OUTPTR(offset) CHECK_PTR(outptr+offset, outbuf, sizeof(outbuf)) -#define ZEROTERM(buf) buf[(sizeof(buf)/sizeof(buf[0]))-1]=0; -#define NEXT_PARAM() \ - if (parm > paramCount) \ - { \ - strcpy(outbuf, ""); \ - len = 0; \ - LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", parm, paramCount); \ - return outbuf; \ - } +/** + * FORMATTING ROUTINES + */ -char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len) + +#define MAX_LEVELS 4 + +size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level); + +size_t do_amx_format_parameter(AMX *amx, cell *params, const char **fmtstr, int *param, char *output, size_t maxlen, int level) { - // number of parameters ( for NEXT_PARAM macro ) - int paramCount = *params / sizeof(cell); - int status; - - // the output buffer - static char outbuf[4096]; - char *outptr = outbuf; - cell *src = get_amxaddr(amx, params[parm++]); - - while (*src) + static char buffer[MAX_LEVELS][2048]; + static char fmt[MAX_LEVELS][32]; + size_t len = 0; + char *fmtptr = fmt[level]; + const char *fmtsrc = *fmtstr; + char ctrl_code; + int numParams = params[0] / sizeof(cell); + if (*param > numParams) { - if (*src == '%') + LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", *param, numParams); + return 0; + } + cell _addr = params[*param]; + cell *addr = get_amxaddr(amx, _addr); + (*param)++; + + if (level >= MAX_LEVELS) + { + output[0] = '\0'; + return 0; + } + + *fmtptr++ = '%'; + while (fmtsrc[len] && !isalpha((char)fmtsrc[len])) + { + if (len >= sizeof(fmt)-2) + break; + *fmtptr++ = static_cast(fmtsrc[len++]); + } + //get the final character + ctrl_code = fmtsrc[len++]; + //inc the source pointer + *fmtstr = &(fmtsrc[len]); + //finalize the string + *fmtptr++ = ctrl_code; + *fmtptr = '\0'; + len = 0; + //reset the format pointer + fmtptr = fmt[level]; + + char *tmp_buf = buffer[level]; + + //we now have the format + switch (ctrl_code) + { + case 's': { - ++src; - if (*src == 'L') + get_amxstring_r(amx, _addr, tmp_buf, 2047); + return _snprintf(output, maxlen, fmtptr, tmp_buf); + break; + } + case 'g': + case 'f': + { + return _snprintf(output, maxlen, fmtptr, *(REAL *)addr); + break; + } + case 'i': + case 'd': + case 'c': + { + return _snprintf(output, maxlen, fmtptr, (int)addr[0]); + break; + } + case 'L': + { + const char *pLangName = NULL; + const char *def = NULL, *key = NULL; + int status; + int tmpLen; + if (addr[0] == LANG_PLAYER) { - cell langName = params[parm]; // "en" case (langName contains the address to the string) - NEXT_PARAM(); - cell *pAmxLangName = get_amxaddr(amx, params[parm++]); // other cases - const char *cpLangName=NULL; - // Handle player ids (1-32) and server language - - if (*pAmxLangName == LANG_PLAYER) // LANG_PLAYER + if ( (int)CVAR_GET_FLOAT("amx_client_languages") == 0 ) { - if ((int)CVAR_GET_FLOAT("amx_client_languages") == 0) - { - cpLangName = g_vault.get("server_language"); - } else { - cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(m_CurGlobId)->pEdict, "lang"); - } + pLangName = g_vault.get("server_language"); + } else { + pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(g_langMngr.GetDefLang())->pEdict, "lang"); } - else if (*pAmxLangName == LANG_SERVER) // LANG_SERVER + } else if (addr[0] == LANG_SERVER) { + pLangName = g_vault.get("server_language"); + } else if (addr[0] >= 1 && addr[0] <= gpGlobals->maxClients) { + if ( (int)CVAR_GET_FLOAT("amx_client_languages") == 0 ) { - cpLangName = g_vault.get("server_language"); - } - else if (*pAmxLangName >= 1 && *pAmxLangName <= 32) // Direct Client Id - { - if ((int)CVAR_GET_FLOAT("amx_client_languages") == 0) - { - cpLangName = g_vault.get("server_language"); - } else { - cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(*pAmxLangName)->pEdict, "lang"); - } - } else { // Language Name - int tmplen = 0; - cpLangName = get_amxstring(amx, langName, 2, tmplen); - } - - if (!cpLangName || strlen(cpLangName) < 1) - cpLangName = "en"; - - int tmplen = 0; - NEXT_PARAM(); - char *key = get_amxstring(amx, params[parm++], 1, tmplen); - const char *def = GetDef(cpLangName, key, status); - - if (def == NULL) - { - bool a = true; - if (status == LANG_STATUS_LNOTFOUND) - { - AMXXLOG_Log("[AMXX] Language \"%s\" not found", cpLangName); - } - else if (status == LANG_STATUS_KLNOTFOUND) - { - a = false; - AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, cpLangName); - } - - if (*pAmxLangName != LANG_SERVER) - { - def = GetDef(g_vault.get("server_language"), key, status); - } - - if (!def && (strcmp(cpLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0)) - { - def = GetDef("en", key, status); - } - - if (!def) - { - static char buf[512]; - CHECK_PTR((char*)(buf + 17 + strlen(key)), buf, sizeof(buf)); - sprintf(buf, "ML_LNOTFOUND: %s", key); - def = buf; - - if (a) - AMXXLOG_Log("[AMXX] Language key \"%s\" not found, check \"%s\"", key, GetFileName(amx)); - } - } - - while (*def) - { - if (*def == '%') - { - ++def; - if (*def == '%' || *def == 0) - { - *outptr++ = '%'; - ++def; - } else { - static char format[32]; - format[0] = '%'; - char *ptr = format + 1; - - while (ptr-format(*tmpCell++); - *tmpPtr = 0; - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, tmpString); - ZEROTERM(outbuf); - break; - } - case 'g': - case 'f': - { - NEXT_PARAM(); - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, *(REAL*)get_amxaddr(amx, params[parm++])); - ZEROTERM(outbuf); - break; - } - case 'i': - case 'd': - case 'c': - { - NEXT_PARAM(); - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, (int)*get_amxaddr(amx, params[parm++])); - ZEROTERM(outbuf); - break; - } - default: - { - CHECK_OUTPTR(strlen(format) + 1); - strcpy(outptr, format); - break; - } - } - - outptr += strlen(outptr); - } - } - else if (*def == '^') - { - ++def; - - switch (*def) - { - case 'n': - CHECK_OUTPTR(1); - *outptr++ = '\n'; - break; - case 't': - CHECK_OUTPTR(1); - *outptr++ = '\t'; - break; - case '^': - CHECK_OUTPTR(1); - *outptr++ = '^'; - break; - default: - CHECK_OUTPTR(2); - *outptr++ = '^'; - *outptr++ = *def; - break; - } - - ++def; - } else { - CHECK_OUTPTR(1); - *outptr++ = *def++; - } + pLangName = g_vault.get("server_language"); + } else { + pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(addr[0])->pEdict, "lang"); } } else { - static char tmpString[4096]; - char *tmpPtr = tmpString; - int tmpLen = 0; - static char format[32] = {'%'}; - char *ptr = format + 1; + pLangName = get_amxstring(amx, _addr, 0, tmpLen); + } + if (!pLangName || !isalpha(pLangName[0])) + pLangName = "en"; + //next parameter! + _addr = params[*param]; + (*param)++; + key = get_amxstring(amx, _addr, 1, tmpLen); + def = g_langMngr.GetDef(pLangName, key, status); - if (*src != '%') + if (def == NULL) + { + bool a = true; + if (status == LANG_STATUS_LNOTFOUND) { - while (*src != 0 && ptr-format(*src++))) - /*nothing*/; - *ptr = 0; - ZEROTERM(format); - --src; - - switch (*(ptr - 1)) + AMXXLOG_Log("[AMXX] Language \"%s\" not found", pLangName); + } else if (status == LANG_STATUS_KLNOTFOUND) { + a = false; + AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, pLangName); + } + + if (addr[0] != LANG_SERVER) + def = g_langMngr.GetDef(g_vault.get("server_language"), key, status); + + if (!def && (strcmp(pLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0)) + def = g_langMngr.GetDef("en", key, status); + + if (!def) + { + return _snprintf(output, maxlen, "ML_NOTFOUND: %s", key); + if (a) + AMXXLOG_Log("[AMXX] Language key \"%s\" not found, check \"%s\"", key, GetFileName(amx)); + } + } + return do_amx_format(amx, params, param, &def, output, maxlen, level + 1); + } + default: + { + return _snprintf(output, maxlen, "%s", fmtptr); + break; + } + } +} + +#define DUMP_CP_BUFFER(expr) \ + if (sofar > 0) { \ + if (sofar <= (int)maxlen) { \ + memcpy(output, save, sofar); \ + output += sofar; \ + maxlen -= sofar; \ + sofar = 0; \ + } else { \ + expr; \ + } \ + } + +size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level) +{ + int sofar = 0; + size_t written; + size_t orig_maxlen = maxlen; + const char *save = *lex; + register const char *lexptr = save; + + while (*lexptr) + { + switch (*lexptr) + { + case '%': + { + lexptr++; + if (*lexptr == '%' || *lexptr == '\0') + { + sofar+=2; + } else { + DUMP_CP_BUFFER(break); + written = do_amx_format_parameter(amx, params, &lexptr, param, output, maxlen, level + 1); + output += written; + maxlen -= written; + save = lexptr; + } + break; + } + case '^': + { + if (level) + { + DUMP_CP_BUFFER(break); + lexptr++; + switch (*lexptr) { - case 's': + case 'n': { - NEXT_PARAM(); - cell *tmpCell = get_amxaddr(amx, params[parm++]); - while (tmpPtr-tmpString(*tmpCell++); - *tmpPtr = 0; - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, tmpString); - ZEROTERM(outbuf); + *output++ = '\n'; break; } - case 'g': - case 'f': + case 't': { - NEXT_PARAM(); - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, *(REAL*)get_amxaddr(amx, params[parm++])); - break; - } - case 'i': - case 'd': - case 'c': - { - NEXT_PARAM(); - _snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, (int)*get_amxaddr(amx, params[parm++])); + *output++ = '\t'; break; } default: { - CHECK_OUTPTR(strlen(format) + 1); - strcpy(outptr, format); + *output++ = *lexptr; break; } } - - outptr += strlen(outptr); - } else { - CHECK_OUTPTR(1); - *outptr++ = '%'; + lexptr++; + maxlen--; + save = lexptr; + break; } } - } else { - CHECK_OUTPTR(1); - *outptr++ = static_cast(*src); + default: + { + lexptr++; + sofar++; + } } - ++src; } - - len = outptr - outbuf; - CHECK_OUTPTR(1); - *outptr++ = 0; - + DUMP_CP_BUFFER(;); + *output = '\0'; + + *lex = lexptr; + + return (orig_maxlen-maxlen); +} + +char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len) +{ + //do an initial run through all this + static char mystr[4096]; + static char outbuf[4096]; + const char *ptr = mystr; + + get_amxstring_r(amx, params[parm++], mystr, sizeof(mystr)-1); + + len = do_amx_format(amx, params, &parm, &ptr, outbuf, sizeof(outbuf)-1, 0); + return outbuf; } diff --git a/amxmodx/CLang.h b/amxmodx/CLang.h index 4289c742..3ee2864f 100755 --- a/amxmodx/CLang.h +++ b/amxmodx/CLang.h @@ -59,13 +59,12 @@ public: defentry() : definition(NULL) { }; + defentry(const defentry &src) + { + definition = src.definition; + } ~defentry() { - if (definition) - { - delete definition; - definition = NULL; - } } String *definition; }; @@ -184,6 +183,8 @@ public: // When a language id in a format string in FormatAmxString is LANG_PLAYER, the glob id decides which language to take. void SetDefLang(int id); + inline int GetDefLang() const { return m_CurGlobId; } + // Reset void Clear(); diff --git a/amxmodx/CString.h b/amxmodx/CString.h index 034e00e7..14b86509 100755 --- a/amxmodx/CString.h +++ b/amxmodx/CString.h @@ -107,8 +107,10 @@ public: { clear(); } else { - Grow(strlen(d) + 1, false); - strcpy(v, d); + size_t len = strlen(d); + Grow(len + 1, false); + memcpy(v, d, len); + v[len] = '\0'; } } diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index b9b7dcfb..5d25a813 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -257,6 +257,7 @@ char* format_amxstring(AMX *amx, cell *params, int parm, int& len); AMX* get_amxscript(int, void**, const char**); const char* get_amxscriptname(AMX* amx); char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len); +size_t get_amxstring_r(AMX *amx, cell amx_addr, char *destination, int maxlen); int amxstring_len(cell* cstr); int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug); diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index e73f9b39..93d1e917 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -249,10 +249,14 @@ int C_Spawn(edict_t *pent) // ###### Load lang char file[256]; g_langMngr.LoadCache(build_pathname_r(file, sizeof(file) - 1, "%s/dictionary.cache", get_localinfo("amxx_datadir", "addons/amxmodx/data"))); + DWORD stop,start=GetTickCount(); if (!g_langMngr.Load(build_pathname_r(file, sizeof(file) - 1, "%s/languages.dat", get_localinfo("amxmodx_datadir", "addons/amxmodx/data")))) { + LOG_MESSAGE(PLID, "Cache invalidated!"); g_langMngr.InvalidateCache(); } + stop=GetTickCount(); + LOG_MESSAGE(PLID, "CacheDB load time: %d milliseconds", stop-start); // ###### Initialize commands prefixes g_commands.registerPrefix("amx"); diff --git a/amxmodx/sh_tinyhash.h b/amxmodx/sh_tinyhash.h index 1960b5ca..89824785 100644 --- a/amxmodx/sh_tinyhash.h +++ b/amxmodx/sh_tinyhash.h @@ -13,7 +13,7 @@ #include "sh_list.h" -#define _T_INIT_HASH_SIZE 32 +#define _T_INIT_HASH_SIZE 128 //namespace SourceHook //{ @@ -43,7 +43,7 @@ K key; V val; }; - typedef List * NodePtr; + typedef List * NodePtr; public: THash() : m_Buckets(NULL), m_numBuckets(0), m_percentUsed(0.0f), m_NumItems(0) { @@ -93,15 +93,10 @@ private: void _Clear() { - List::iterator iter, begin, end; for (size_t i=0; ibegin(); - end = m_Buckets[i]->end(); - for (iter=begin; iter!=end; iter++) - delete (*iter); delete m_Buckets[i]; m_Buckets[i] = NULL; } @@ -118,25 +113,33 @@ THashNode *pNode = NULL; if (!m_Buckets[place]) { - m_Buckets[place] = new List; - pNode = new THashNode(key, V()); - m_Buckets[place]->push_back(pNode); + m_Buckets[place] = new List; + m_Buckets[place]->push_back(THashNode(key, V())); m_percentUsed += (1.0f / (float)m_numBuckets); m_NumItems++; + typename List::iterator iter; + iter = m_Buckets[place]->end(); + iter--; + pNode = &(*iter); } else { - typename List::iterator iter; - for (iter=m_Buckets[place]->begin(); iter!=m_Buckets[place]->end(); iter++) + typename List::iterator iter, end=m_Buckets[place]->end(); + for (iter=m_Buckets[place]->begin(); iter!=end; iter++) { - if (Compare((*iter)->key, key) == 0) - return (*iter); + if (Compare((*iter).key, key) == 0) + return &(*iter); } //node does not exist - pNode = new THashNode(key, V()); - m_Buckets[place]->push_back(pNode); + m_Buckets[place]->push_back(THashNode(key, V())); m_NumItems++; + iter = m_Buckets[place]->end(); + iter--; + pNode = &(*iter); } if (PercentUsed() > 0.75f) + { _Refactor(); + return _FindOrInsert(key); + } return pNode; } void _Refactor() @@ -151,7 +154,7 @@ } else { size_t oldSize = m_numBuckets; m_numBuckets *= 2; - typename List::iterator iter; + typename List::iterator iter, end; size_t place; THashNode *pHashNode; NodePtr *temp = new NodePtr[m_numBuckets]; @@ -164,18 +167,19 @@ if (m_Buckets[i]) { //go through the list of items - for (iter = m_Buckets[i]->begin(); iter != m_Buckets[i]->end(); iter++) + end = m_Buckets[i]->end(); + for (iter = m_Buckets[i]->begin(); iter!=end; iter++) { - pHashNode = (*iter); + pHashNode = &(*iter); //rehash it with the new bucket filter place = HashFunction(pHashNode->key) % m_numBuckets; //add it to the new hash table if (!temp[place]) { - temp[place] = new List; + temp[place] = new List; m_percentUsed += (1.0f / (float)m_numBuckets); } - temp[place]->push_back(pHashNode); + temp[place]->push_back((*iter)); } //delete that bucket! delete m_Buckets[i]; @@ -219,19 +223,19 @@ } const THashNode & operator * () const { - return *(*iter); + return (*iter); } THashNode & operator * () { - return *(*iter); + return (*iter); } const THashNode * operator ->() const { - return (*iter); + return &(*iter); } THashNode * operator ->() { - return (*iter); + return &(*iter); } bool operator ==(const iterator &where) const { @@ -308,7 +312,7 @@ } private: int curbucket; - typename List::iterator iter; + typename List::iterator iter; THash *hash; bool end; }; diff --git a/amxmodx/string.cpp b/amxmodx/string.cpp index 9d905b69..43b7c04b 100755 --- a/amxmodx/string.cpp +++ b/amxmodx/string.cpp @@ -87,6 +87,20 @@ int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max) return dest - start; } +size_t get_amxstring_r(AMX *amx, cell amx_addr, char *destination, int maxlen) +{ + register cell *source = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr)); + register char *dest = destination; + char *start = dest; + + while (*source && maxlen-- > 0) + *dest++=(char)(*source++); + if (dest) + *dest = '\0'; + + return --dest - start; +} + char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len) { static char buffor[4][3072];