diff --git a/core/CTranslator.cpp b/core/CTranslator.cpp new file mode 100644 index 00000000..723a984a --- /dev/null +++ b/core/CTranslator.cpp @@ -0,0 +1,748 @@ +#include +#include "CTranslator.h" +#include "CLogger.h" +#include "CTextParsers.h" +#include "LibrarySys.h" +#include "sm_stringutil.h" + +CTranslator g_Translator; + +struct trans_t +{ + int stridx; + int fmt_order; +}; + +struct phrase_t +{ + int fmt_list; + unsigned int fmt_count; + unsigned int fmt_bytes; + int trans_tbl; + unsigned int translations; +}; + +CPhraseFile::CPhraseFile(CTranslator *pTranslator, const char *file) +{ + m_pStringTab = pTranslator->GetStringTable(); + m_pMemory = m_pStringTab->GetMemTable(); + m_LangCount = pTranslator->GetLanguageCount(); + m_File.assign(file); + m_pTranslator = pTranslator; + m_pPhraseLookup = NULL; +} + +CPhraseFile::~CPhraseFile() +{ + if (m_pPhraseLookup) + { + sm_trie_destroy(m_pPhraseLookup); + } +} + +void CPhraseFile::ParseError(const char *message, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, message); + vsnprintf(buffer, sizeof(buffer), message, ap); + va_end(ap); + + m_ParseError.assign(buffer); +} + +void CPhraseFile::ParseWarning(const char *message, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, message); + vsnprintf(buffer, sizeof(buffer), message, ap); + va_end(ap); + + if (!m_FileLogged) + { + g_Logger.LogError("[SOURCEMOD] Warning(s) encountered in translation file \"%s\"", m_File.c_str()); + m_FileLogged; + } + + g_Logger.LogError("[SOURCEMOD] %s", message); +} + +void CPhraseFile::ReparseFile() +{ + if (m_pPhraseLookup) + { + sm_trie_destroy(m_pPhraseLookup); + } + m_pPhraseLookup = sm_trie_create(); + + m_LangCount = m_pTranslator->GetLanguageCount(); + + if (!m_LangCount) + { + return; + } + + SMCParseError err; + char path[PLATFORM_MAX_PATH+1]; + g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/translations/%s", g_SourceMod.GetSMBaseDir(), m_File.c_str()); + unsigned int line=0, col=0; + + if ((err=g_TextParser.ParseFile_SMC(path, this, &line, &col)) != SMCParse_Okay) + { + const char *msg = g_TextParser.GetSMCErrorString(err); + if (!msg) + { + msg = m_ParseError.c_str(); + } + + g_Logger.LogError("[SOURCEMOD] Fatal error encountered parsing translation file \"%s\"", m_File.c_str()); + g_Logger.LogError("[SOURCEMOD] Error (line %d, column %d): %s", line, col, msg); + } +} + +void CPhraseFile::ReadSMC_ParseStart() +{ + m_CurPhrase = -1; + m_ParseState = PPS_None; + m_CurLine = 0; + m_FileLogged = false; + m_LastPhraseString.clear(); +} + +SMCParseResult CPhraseFile::ReadSMC_RawLine(const char *line, unsigned int curline) +{ + m_CurLine = curline; + + return SMCParse_Continue; +} + +SMCParseResult CPhraseFile::ReadSMC_NewSection(const char *name, bool opt_quotes) +{ + bool recognized = false; + if (m_ParseState == PPS_None) + { + if (strcmp(name, "Phrases") == 0) + { + m_ParseState = PPS_Phrases; + recognized = true; + } + } else if (m_ParseState == PPS_Phrases) { + m_ParseState = PPS_InPhrase; + recognized = true; + + phrase_t *pPhrase; + m_CurPhrase = m_pMemory->CreateMem(sizeof(phrase_t), (void **)&pPhrase); + + /* Create the reverse lookup */ + if (!sm_trie_insert(m_pPhraseLookup, name, reinterpret_cast(m_CurPhrase))) + { + ParseWarning("Skipping duplicate phrase \"%s\" on line %d.", name, m_CurLine); + /* :IDEA: prevent memory waste by seeking backwards in memtable? */ + m_CurPhrase = -1; + } else { + /* Initialize new phrase */ + trans_t *pTrans; + + pPhrase->fmt_count = 0; + pPhrase->fmt_list = -1; + pPhrase->trans_tbl = m_pMemory->CreateMem(sizeof(trans_t) * m_LangCount, (void **)&pTrans); + pPhrase->translations = 0; + pPhrase->fmt_bytes = 0; + + for (unsigned int i=0; iGetAddress(m_CurPhrase); + + if (key[0] == '#' && strcmp(key, "#format") == 0) + { + if (pPhrase->fmt_list != -1) + { + ParseWarning("Ignoring duplicated #format property on line %d", m_CurLine); + return SMCParse_Continue; + } + + if (pPhrase->translations > 0) + { + ParseWarning("#format property should come before translations on line %d, ignoring", m_CurLine); + return SMCParse_Continue; + } + + /* Do an initial browsing for error checking and what not */ + enum ParseStates + { + Parse_None, + Parse_Index, + Parse_Format, + }; + + ParseStates state = Parse_None; + + const char *old_value = value; + while (*value != '\0') + { + if (state == Parse_None) + { + if (*value == '{') + { + pPhrase->fmt_count++; + state = Parse_Index; + } else if (*value == ',') { + /* Do nothing */ + } else { + unsigned int bytes = g_TextParser.GetUTF8CharBytes(value); + if (bytes != 1 || !isalpha(*value)) + { + ParseWarning("Invalid token '%c' in #format property on line %d.", *value, m_CurLine); + } + } + } else if (state == Parse_Index) { + if (*value == ':') + { + state = Parse_Format; + if (value - old_value >= 15) + { + ParseWarning("Too many digits in format index on line %d, phrase will be ignored.", m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + } else { + unsigned int bytes = g_TextParser.GetUTF8CharBytes(value); + if (bytes != 1 || !isdigit(*value)) + { + ParseWarning("Token '%c' in #format property on line %d is not a digit, phrase will be ignored.", + *value, + m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + } + } else if (state == Parse_Format) { + if (*value == '}') + { + state = Parse_None; + } + } + value++; + } + + if (state != Parse_None) + { + /* Moose clam cow. */ + ParseWarning("Unterminated format string on line %d, phrase will be ignored.", m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + value = old_value; + + /* Allocate the format table */ + char fmt_buf[16]; + int *fmt_list; + pPhrase->fmt_list = m_pMemory->CreateMem(sizeof(int) * pPhrase->fmt_count, (void **)&fmt_list); + + /* Initialize */ + for (size_t i=0; ifmt_count; i++) + { + fmt_list[i] = -1; + } + + /* Now we need to read again... this time into the format buffer */ + const char *in_ptr = value; + const char *idx_ptr = NULL; + char *out_ptr = NULL; + unsigned int cur_idx = 0; + state = Parse_None; + while (*in_ptr != '\0') + { + if (state == Parse_None) + { + if (*in_ptr == '{') + { + state = Parse_Index; + idx_ptr = NULL; + } + } else if (state == Parse_Index) { + if (*in_ptr == ':') + { + /* Check the number! */ + if (!idx_ptr) + { + ParseWarning("Format property contains unindexed format string on line %d, phrase will be ignored.", m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + long idx = strtol(idx_ptr, NULL, 10); + if (idx < 1 || idx > (long)pPhrase->fmt_count) + { + ParseWarning("Format property contains invalid index '%d' on line %d, phrase will be ignored.", idx, m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } else if (fmt_list[idx - 1] != -1) { + ParseWarning("Format property contains duplicated index '%d' on line %d, phrase will be ignored.", idx, m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + cur_idx = (unsigned int)idx; + state = Parse_Format; + out_ptr = NULL; + } else if (!idx_ptr) { + idx_ptr = in_ptr; + } + } else if (state == Parse_Format) { + if (*in_ptr == '}') + { + if (!out_ptr) + { + ParseWarning("Format property contains empty format string on line %d, phrase will be ignored.", m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + *out_ptr = '\0'; + state = Parse_None; + /* Now, add this to our table */ + fmt_list[cur_idx - 1] = m_pStringTab->AddString(fmt_buf); + pPhrase->fmt_bytes += strlen(fmt_buf); + } else { + if (!out_ptr) + { + out_ptr = fmt_buf; + *out_ptr++ = '%'; + } + /* Check length ... */ + if (out_ptr - fmt_buf >= sizeof(fmt_buf) - 1) + { + ParseWarning("Format property contains format string that exceeds maximum length on line %d, phrase will be ignored.", + m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + *out_ptr++ = *in_ptr; + } + } + in_ptr++; + } + + /* If we've gotten here, we only accepted unique indexes in a range. + * Therefore, the list has to be completely filled. Double check anyway. + */ + for (size_t i=0; ifmt_count; i++) + { + if (fmt_list[i] == -1) + { + ParseWarning("Format property contains no string for index %d on line %d, phrase will be ignored.", + i + 1, + m_CurLine); + m_CurPhrase = -1; + return SMCParse_Continue; + } + } + } else { + size_t len = strlen(key); + if (len != 2) + { + ParseWarning("Ignoring translation to invalid language \"%s\" on line %d.", key, m_CurLine); + return SMCParse_Continue; + } + + unsigned int lang; + if (!m_pTranslator->GetLanguageByCode(key, &lang)) + { + /* Ignore if we don't have a language. + * :IDEA: issue a one-time alert? + */ + return SMCParse_Continue; + } + + /* See how many bytes we need for this string, then allocate. + * NOTE: THIS SHOULD GUARANTEE THAT WE DO NOT NEED TO NEED TO SIZE CHECK + */ + len = strlen(value) + pPhrase->fmt_bytes + 1; + char *out_buf; + int out_idx; + + out_idx = m_pMemory->CreateMem(len, (void **)&out_buf); + + int *fmt_order; + int *fmt_list = (int *)m_pMemory->GetAddress(pPhrase->fmt_list); + trans_t *pTrans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl); + + pTrans = &pTrans[lang]; + pTrans->stridx = out_idx; + + bool params[MAX_TRANSLATE_PARAMS]; + + /* Build the format order list, if necessary */ + if (fmt_list) + { + pTrans->fmt_order = m_pMemory->CreateMem(pPhrase->fmt_count * sizeof(int), (void **)&fmt_order); + + for (unsigned int i=0; ifmt_count; i++) + { + fmt_order[i] = -1; + } + memset(¶ms[0], 0, sizeof(params)); + } + + const char *in_ptr = value; + char *out_ptr = out_buf; + unsigned int order_idx = 0; + while (*in_ptr != '\0') + { + if (*in_ptr == '\\') + { + switch (*(in_ptr + 1)) + { + case '\n': + *out_ptr++ = '\n'; + break; + case '\t': + *out_ptr++ = '\t'; + break; + case '\r': + *out_ptr++ = '\r'; + break; + case '{': + *out_ptr++ = '{'; + break; + default: + /* Copy both bytes since we don't know what's going on */ + *out_ptr++ = *in_ptr++; + *out_ptr++ = *in_ptr; + break; + } + /* Skip past the last byte read */ + in_ptr++; + } else if (*in_ptr == '{' && fmt_list != NULL) { + /* Search for parameters if this is a formatted string */ + const char *scrap_in_point = in_ptr; + const char *digit_start = ++in_ptr; + unsigned int bytes; + while (*in_ptr != '\0') + { + bytes = g_TextParser.GetUTF8CharBytes(in_ptr); + if (bytes != 1) + { + goto scrap_point; + } + if (*in_ptr == '}') + { + /* Did we get an index? */ + if (in_ptr == digit_start) + { + goto scrap_point; + } + /* Is it valid? */ + long idx = strtol(digit_start, NULL, 10); + if (idx < 1 || idx > (int)pPhrase->fmt_count) + { + goto scrap_point; + } + if (params[idx-1]) + { + goto scrap_point; + } + + /* We're safe to insert the string. First mark order. */ + fmt_order[order_idx++] = (int)idx - 1; + /* Now concatenate */ + out_ptr += sprintf(out_ptr, "%s", m_pStringTab->GetString(fmt_list[idx-1])); + /* Mark as used */ + params[idx-1] = true; + + goto cont_loop; + } + in_ptr++; + } +scrap_point: + /* Pretend none of this ever happened. Move along now! */ + in_ptr = scrap_in_point; + *out_ptr++ = *in_ptr; + } else { + *out_ptr++ = *in_ptr; + } +cont_loop: + in_ptr++; + } + *out_ptr = '\0'; + pPhrase->translations++; + } + + return SMCParse_Continue; +} + +SMCParseResult CPhraseFile::ReadSMC_LeavingSection() +{ + if (m_ParseState == PPS_InPhrase) + { + if (m_CurPhrase == -1 && m_LastPhraseString.size()) + { + sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str()); + } + m_CurPhrase = -1; + m_ParseState = PPS_Phrases; + m_LastPhraseString.assign(""); + } else if (m_ParseState == PPS_Phrases) { + m_ParseState = PPS_None; + } + + return SMCParse_Continue; +} + +void CPhraseFile::ReadSMC_ParseEnd(bool halted, bool failed) +{ + /* Check to see if we have any dangling phrases that weren't completed, and scrap them */ + if ((halted || failed) && m_LastPhraseString.size()) + { + sm_trie_delete(m_pPhraseLookup, m_LastPhraseString.c_str()); + } +} + +const char *CPhraseFile::GetTranslation(const Translation *pTrans, int **fmt_order, unsigned int *fmt_count) +{ + if (pTrans->lang_id >= m_LangCount) + { + return NULL; + } + + void *object; + if (!sm_trie_retrieve(m_pPhraseLookup, pTrans->szPhrase, &object)) + { + return NULL; + } + + phrase_t *pPhrase = (phrase_t *)m_pMemory->GetAddress(reinterpret_cast(object)); + trans_t *trans = (trans_t *)m_pMemory->GetAddress(pPhrase->trans_tbl); + + trans = &trans[pTrans->lang_id]; + + if (trans->stridx == -1) + { + return NULL; + } + + *fmt_order = (int *)m_pMemory->GetAddress(trans->fmt_order); + *fmt_count = pPhrase->fmt_count; + + return m_pStringTab->GetString(trans->stridx); +} + +const char *CPhraseFile::GetFilename() +{ + return m_File.c_str(); +} + +CTranslator::CTranslator() +{ + m_pStringTab = new BaseStringTable(2048); + m_pLCodeLookup = sm_trie_create(); +} + +CTranslator::~CTranslator() +{ + for (size_t i=0; i(_index); + } + + return true; +} + +unsigned int CTranslator::GetLanguageCount() +{ + return (unsigned int)m_Languages.size(); +} + +BaseStringTable *CTranslator::GetStringTable() +{ + return m_pStringTab; +} + +unsigned int CTranslator::FindOrAddPhraseFile(const char *phrase_file) +{ + for (size_t i=0; iGetFilename(), phrase_file) == 0) + { + return (unsigned int)i; + } + } + + CPhraseFile *pFile = new CPhraseFile(this, phrase_file); + unsigned int idx = (unsigned int)m_Files.size(); + + m_Files.push_back(pFile); + + pFile->ReparseFile(); + + return idx; +} + +void CTranslator::RebuildLanguageDatabase(const char *lang_header_file) +{ + /* Erase everything we have */ + sm_trie_destroy(m_pLCodeLookup); + m_pLCodeLookup = sm_trie_create(); + m_pStringTab->Reset(); + + for (size_t i=0; iReparseFile(); + } +} + +void CTranslator::ReadSMC_ParseStart() +{ + m_InLanguageSection = false; + m_CustomError.clear(); +} + +SMCParseResult CTranslator::ReadSMC_NewSection(const char *name, bool opt_quotes) +{ + if (!m_InLanguageSection) + { + if (strcmp(name, "Languages") == 0) + { + m_InLanguageSection = true; + } + } + + if (!m_InLanguageSection) + { + g_Logger.LogError("[SOURCEMOD] Warning: Unrecognized section \"%s\" in languages.cfg"); + } + + return SMCParse_Continue; +} + +SMCParseResult CTranslator::ReadSMC_LeavingSection() +{ + m_InLanguageSection = false; + + return SMCParse_Continue; +} + +SMCParseResult CTranslator::ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes) +{ + size_t len = strlen(key); + + if (len != 2) + { + g_Logger.LogError("[SOURCEMOD] Warning encountered parsing languages.cfg file."); + g_Logger.LogError("[SOURCEMOD] Invalid language code \"%s\" is being ignored."); + } + + Language *pLanguage = new Language; + unsigned int idx = m_Languages.size(); + + strcpy(pLanguage->m_code2, key); + pLanguage->m_FullName = m_pStringTab->AddString(value); + + sm_trie_insert(m_pLCodeLookup, key, reinterpret_cast(idx)); + + m_Languages.push_back(pLanguage); + + return SMCParse_Continue; +} + +size_t CTranslator::Translate(char *buffer, size_t maxlength, const Translation *pTrans) +{ + if (pTrans->file_id >= m_Files.size()) + { + return 0; + } + + CPhraseFile *pFile = m_Files[pTrans->file_id]; + unsigned int count; + int *order_table; + const char *str; + + if ((str=pFile->GetTranslation(pTrans, &order_table, &count)) == NULL) + { + return 0; + } + + void *params[MAX_TRANSLATE_PARAMS]; + + /* Rewrite the parameter order */ + for (unsigned int i=0; iparams[order_table[i]]; + } + + return gnprintf(buffer, maxlength, str, params); +} diff --git a/core/CTranslator.h b/core/CTranslator.h new file mode 100644 index 00000000..cb0da51f --- /dev/null +++ b/core/CTranslator.h @@ -0,0 +1,101 @@ +#ifndef _INCLUDE_SOURCEMOD_TRANSLATOR_H_ +#define _INCLUDE_SOURCEMOD_TRANSLATOR_H_ + +#include "sm_trie.h" +#include +#include +#include "sm_globals.h" +#include "sm_memtable.h" +#include "ITextParsers.h" + +#define MAX_TRANSLATE_PARAMS 32 + +/* :TODO: write a templatized version of tries? */ + +using namespace SourceHook; +class CTranslator; + +enum PhraseParseState +{ + PPS_None = 0, + PPS_Phrases, + PPS_InPhrase, +}; + +struct Language +{ + char m_code2[3]; + int m_FullName; +}; + +struct Translation +{ + const char *szPhrase; + unsigned int lang_id; + unsigned int file_id; + void **params; +}; + +class CPhraseFile : public ITextListener_SMC +{ +public: + CPhraseFile(CTranslator *pTranslator, const char *file); + ~CPhraseFile(); +public: + void ReparseFile(); + const char *GetFilename(); + const char *GetTranslation(const Translation *pTrans, int **fmt_order, unsigned int *fmt_count); +public: //ITextListener_SMC + void ReadSMC_ParseStart(); + void ReadSMC_ParseEnd(bool halted, bool failed); + SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); + SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes); + SMCParseResult ReadSMC_LeavingSection(); + SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline); +private: + void ParseError(const char *message, ...); + void ParseWarning(const char *message, ...); +private: + Trie *m_pPhraseLookup; + String m_File; + CTranslator *m_pTranslator; + PhraseParseState m_ParseState; + int m_CurPhrase; + BaseMemTable *m_pMemory; + BaseStringTable *m_pStringTab; + unsigned int m_LangCount; + String m_ParseError; + String m_LastPhraseString; + unsigned int m_CurLine; + bool m_FileLogged; +}; + +class CTranslator : public ITextListener_SMC +{ +public: + CTranslator(); + ~CTranslator(); +public: //ITextListener_SMC + void ReadSMC_ParseStart(); + SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes); + SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes); + SMCParseResult ReadSMC_LeavingSection(); +public: + void RebuildLanguageDatabase(const char *lang_header_file); + unsigned int FindOrAddPhraseFile(const char *phrase_file); + BaseStringTable *GetStringTable(); + unsigned int GetLanguageCount(); + bool GetLanguageByCode(const char *code, unsigned int *index); + size_t Translate(char *buffer, size_t maxlength, const Translation *pTrans); +private: + CVector m_Languages; + CVector m_Files; + BaseStringTable *m_pStringTab; + Trie *m_pLCodeLookup; + bool m_InLanguageSection; + String m_CustomError; +}; + +extern CTranslator g_Translator; + +#endif //_INCLUDE_SOURCEMOD_TRANSLATOR_H_ diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index db34d866..79ada0a9 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -1,7 +1,7 @@ + + @@ -265,6 +269,10 @@ RelativePath="..\CTextParsers.h" > + + diff --git a/core/sm_stringutil.cpp b/core/sm_stringutil.cpp index ff89a123..47d85685 100644 --- a/core/sm_stringutil.cpp +++ b/core/sm_stringutil.cpp @@ -3,18 +3,10 @@ #include #include "sm_stringutil.h" -#define ALT 0x00000001 /* alternate form */ -#define HEXPREFIX 0x00000002 /* add 0x or 0X prefix */ #define LADJUST 0x00000004 /* left adjustment */ -#define LONGDBL 0x00000008 /* long double */ -#define LONGINT 0x00000010 /* long integer */ -#define QUADINT 0x00000020 /* quad integer */ -#define SHORTINT 0x00000040 /* short integer */ #define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */ -#define FPT 0x00000100 /* floating point number */ #define to_digit(c) ((c) - '0') #define is_digit(c) ((unsigned)to_digit(c) <= 9) -#define to_char(n) ((n) + '0') //:TODO: fix this macro when we have a debugger @@ -25,6 +17,8 @@ } */ +//:TODO: review this code before we choose a license + void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec) { int size = 0; @@ -252,8 +246,180 @@ void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags) *buf_p = buf; } +size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args) +{ + if (!buffer || !maxlen) + { + return 0; + } + + int arg = 0; + char *buf_p; + char ch; + int flags; + int width; + int prec; + int n; + char sign; + const char *fmt; + size_t llen = maxlen - 1; + + buf_p = buffer; + fmt = format; + + while (true) + { + // run through the format string until we hit a '%' or '\0' + for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++) + { + *buf_p++ = ch; + llen--; + } + if ((ch == '\0') || (llen <= 0)) + { + goto done; + } + + // skip over the '%' + fmt++; + + // reset formatting state + flags = 0; + width = 0; + prec = -1; + sign = '\0'; + +rflag: + ch = *fmt++; +reswitch: + switch(ch) + { + case '-': + { + flags |= LADJUST; + goto rflag; + } + case '.': + { + n = 0; + while(is_digit((ch = *fmt++))) + { + n = 10 * n + (ch - '0'); + } + prec = (n < 0) ? -1 : n; + goto reswitch; + } + case '0': + { + flags |= ZEROPAD; + goto rflag; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + n = 0; + do + { + n = 10 * n + (ch - '0'); + ch = *fmt++; + } while(is_digit(ch)); + width = n; + goto reswitch; + } + case 'c': + { + if (!llen) + { + goto done; + } + char *c = (char *)args[arg]; + *buf_p++ = *c; + llen--; + arg++; + break; + } + case 'd': + case 'i': + { + int *value = (int *)args[arg]; + AddInt(&buf_p, llen, *value, width, flags); + arg++; + break; + } + case 'u': + { + unsigned int *value = (unsigned int *)args[arg]; + AddUInt(&buf_p, llen, *value, width, flags); + arg++; + break; + } + case 'f': + { + float *value = (float *)args[arg]; + AddFloat(&buf_p, llen, *value, width, prec); + arg++; + break; + } + case 's': + { + const char *str = (const char *)args[arg]; + AddString(&buf_p, llen, str, width, prec); + arg++; + break; + } + case '%': + { + if (!llen) + { + goto done; + } + *buf_p++ = ch; + llen--; + break; + } + case '\0': + { + if (!llen) + { + goto done; + } + *buf_p++ = '%'; + llen--; + goto done; + } + default: + { + if (!llen) + { + goto done; + } + *buf_p++ = ch; + llen--; + break; + } + } + } + +done: + *buf_p = '\0'; + + return (maxlen - llen - 1); +} + size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param) { + if (!buffer || !maxlen) + { + return 0; + } + int arg; //int args = params[0] / sizeof(cell); //:TODO: wrong, i think params[0] has now the param count not the byte count // either way this is only used when the above macro is fixed, until then not needed diff --git a/core/sm_stringutil.h b/core/sm_stringutil.h index 49facef6..8ef7336f 100644 --- a/core/sm_stringutil.h +++ b/core/sm_stringutil.h @@ -10,6 +10,7 @@ using namespace SourcePawn; size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param); const char *stristr(const char *str, const char *substr); unsigned int strncopy(char *dest, const char *src, size_t count); +size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args); size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...); #endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_ diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 150b68d2..52d1fee9 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -150,12 +150,6 @@ void SourceModBase::StartSourceMod(bool late) pBase->OnSourceModAllInitialized(); pBase = pBase->m_pGlobalClassNext; } - - /* If we're late, automatically load plugins now */ - if (late) - { - DoGlobalPluginLoads(); - } } bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)