diff --git a/core/ChatTriggers.cpp b/core/ChatTriggers.cpp
index 4e1ec018..d2491cc2 100644
--- a/core/ChatTriggers.cpp
+++ b/core/ChatTriggers.cpp
@@ -216,15 +216,14 @@ void ChatTriggers::OnSayCommand_Pre()
{
char buffer[128];
- /* :TODO: log an error? */
- if (g_Translator.CoreTransEx(g_pFloodPhrases,
- client,
- buffer,
+ if (!CoreTranslate(
+ buffer,
sizeof(buffer),
- "Flooding the server",
+ "%T",
+ 2,
NULL,
- NULL)
- != Trans_Okay)
+ "Flooding the server",
+ &client))
{
UTIL_Format(buffer, sizeof(buffer), "You are flooding the server!");
}
diff --git a/core/ConCmdManager.cpp b/core/ConCmdManager.cpp
index f177dd54..fac00fa2 100644
--- a/core/ConCmdManager.cpp
+++ b/core/ConCmdManager.cpp
@@ -494,8 +494,7 @@ bool ConCmdManager::CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmi
/* If we got here, the command failed... */
char buffer[128];
- if (g_Translator.CoreTrans(client, buffer, sizeof(buffer), "No Access", NULL, NULL)
- != Trans_Okay)
+ if (!CoreTranslate(buffer, sizeof(buffer), "%T", 1, NULL, "No Access", &client))
{
UTIL_Format(buffer, sizeof(buffer), "You do not have access to this command");
}
diff --git a/core/MenuManager.cpp b/core/MenuManager.cpp
index 34b83202..9121518d 100644
--- a/core/MenuManager.cpp
+++ b/core/MenuManager.cpp
@@ -41,6 +41,7 @@
#include "ShareSys.h"
#include "HandleSys.h"
#include "sourcemm_api.h"
+#include "Translator.h"
MenuManager g_Menus;
VoteMenuHandler s_VoteHandler;
@@ -585,14 +586,20 @@ skip_search:
{
if (exitBackButton)
{
- CorePlayerTranslate(client, text, sizeof(text), "Back", NULL);
+ if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Back", &client))
+ {
+ UTIL_Format(text, sizeof(text), "Back");
+ }
dr.style = ITEMDRAW_CONTROL;
position = panel->DrawItem(dr);
slots[position].type = ItemSel_ExitBack;
}
else
{
- CorePlayerTranslate(client, text, sizeof(text), "Previous", NULL);
+ if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Previous", &client))
+ {
+ UTIL_Format(text, sizeof(text), "Previous");
+ }
dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
position = panel->DrawItem(dr);
slots[position].type = ItemSel_Back;
@@ -610,7 +617,10 @@ skip_search:
/* NEXT */
if (displayNext || canDrawDisabled)
{
- CorePlayerTranslate(client, text, sizeof(text), "Next", NULL);
+ if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Next", &client))
+ {
+ UTIL_Format(text, sizeof(text), "Next");
+ }
dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
position = panel->DrawItem(dr);
slots[position].type = ItemSel_Next;
@@ -638,7 +648,10 @@ skip_search:
/* EXIT */
if (exitButton)
{
- CorePlayerTranslate(client, text, sizeof(text), "Exit", NULL);
+ if (!CoreTranslate(text, sizeof(text), "%T", 2, NULL, "Exit", &client))
+ {
+ UTIL_Format(text, sizeof(text), "Exit");
+ }
dr.style = ITEMDRAW_CONTROL;
position = panel->DrawItem(dr);
slots[position].type = ItemSel_Exit;
diff --git a/core/PhraseCollection.cpp b/core/PhraseCollection.cpp
new file mode 100644
index 00000000..0af77da8
--- /dev/null
+++ b/core/PhraseCollection.cpp
@@ -0,0 +1,131 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include "PhraseCollection.h"
+#include "Translator.h"
+#include "sm_stringutil.h"
+
+CPhraseCollection::CPhraseCollection()
+{
+}
+
+CPhraseCollection::~CPhraseCollection()
+{
+}
+
+void CPhraseCollection::Destroy()
+{
+ delete this;
+}
+
+IPhraseFile *CPhraseCollection::AddPhraseFile(const char *filename)
+{
+ size_t i;
+ unsigned int fid;
+ IPhraseFile *pFile;
+ char full_name[PLATFORM_MAX_PATH];
+
+ /* No compat shim here. The user should have read the doc. */
+ UTIL_Format(full_name, sizeof(full_name), "%s.txt", filename);
+
+ fid = g_Translator.FindOrAddPhraseFile(full_name);
+ pFile = g_Translator.GetFileByIndex(fid);
+
+ for (i = 0; i < m_Files.size(); i++)
+ {
+ if (m_Files[i] == pFile)
+ {
+ return pFile;
+ }
+ }
+
+ m_Files.push_back(pFile);
+
+ return pFile;
+}
+
+unsigned int CPhraseCollection::GetFileCount()
+{
+ return (unsigned int)m_Files.size();
+}
+
+IPhraseFile *CPhraseCollection::GetFile(unsigned int file)
+{
+ if (file >= m_Files.size())
+ {
+ return NULL;
+ }
+
+ return m_Files[file];
+}
+
+TransError CPhraseCollection::FindTranslation(const char *key, unsigned int langid, Translation *pTrans)
+{
+ size_t i;
+
+ for (i = 0; i < m_Files.size(); i++)
+ {
+ if (m_Files[i]->GetTranslation(key, langid, pTrans) == Trans_Okay)
+ {
+ return Trans_Okay;
+ }
+ }
+
+ return Trans_BadPhrase;
+}
+
+bool CPhraseCollection::FormatString(char *buffer,
+ size_t maxlength,
+ const char *format,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase)
+{
+ unsigned int arg;
+
+ arg = 0;
+ if (!gnprintf(buffer, maxlength, format, this, params, numparams, arg, pOutLength, pFailPhrase))
+ {
+ return false;
+ }
+
+ if (arg != numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+
+ return true;
+}
diff --git a/core/PhraseCollection.h b/core/PhraseCollection.h
new file mode 100644
index 00000000..a43cf602
--- /dev/null
+++ b/core/PhraseCollection.h
@@ -0,0 +1,65 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
+#define _INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
+
+#include
+#include
+#include
+
+using namespace SourceHook;
+using namespace SourceMod;
+
+class CPhraseCollection : public IPhraseCollection
+{
+public:
+ CPhraseCollection();
+ ~CPhraseCollection();
+public:
+ IPhraseFile *AddPhraseFile(const char *filename);
+ unsigned int GetFileCount();
+ IPhraseFile *GetFile(unsigned int file);
+ void Destroy();
+ TransError FindTranslation(const char *key, unsigned int langid, Translation *pTrans);
+ bool FormatString(
+ char *buffer,
+ size_t maxlength,
+ const char *format,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase);
+private:
+ CVector m_Files;
+};
+
+#endif //_INCLUDE_SOURCEMOD_PHRASECOLLECTION_H_
diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp
index 9e0ecc58..10e30b78 100644
--- a/core/PlayerManager.cpp
+++ b/core/PlayerManager.cpp
@@ -1203,7 +1203,7 @@ CPlayer::CPlayer()
m_bAdminCheckSignalled = false;
m_bIsInKickQueue = false;
m_LastPassword.clear();
- m_LangId = LANGUAGE_ENGLISH;
+ m_LangId = SOURCEMOD_LANGUAGE_ENGLISH;
}
void CPlayer::Initialize(const char *name, const char *ip, edict_t *pEntity)
diff --git a/core/Translator.cpp b/core/Translator.cpp
index 917cf372..f9f4b860 100644
--- a/core/Translator.cpp
+++ b/core/Translator.cpp
@@ -38,9 +38,11 @@
#include "sm_stringutil.h"
#include "sourcemod.h"
#include "PlayerManager.h"
+#include "PhraseCollection.h"
+#include "ShareSys.h"
Translator g_Translator;
-CPhraseFile *g_pCorePhrases = NULL;
+IPhraseCollection *g_pCorePhrases = NULL;
unsigned int g_pCorePhraseID = 0;
struct trans_t
@@ -663,7 +665,7 @@ const char *CPhraseFile::GetFilename()
** MAIN TRANSLATOR CODE **
**************************/
-Translator::Translator() : m_ServerLang(LANGUAGE_ENGLISH)
+Translator::Translator() : m_ServerLang(SOURCEMOD_LANGUAGE_ENGLISH)
{
m_pStringTab = new BaseStringTable(2048);
m_pLCodeLookup = sm_trie_create();
@@ -727,11 +729,15 @@ void Translator::OnSourceModAllInitialized()
{
AddLanguage("en", "English");
- unsigned int id;
+ g_pCorePhrases = CreatePhraseCollection();
+ g_pCorePhrases->AddPhraseFile("core.phrases");
- id = FindOrAddPhraseFile("core.phrases.txt");
- g_pCorePhraseID = id;
- g_pCorePhrases = GetFileByIndex(id);
+ g_ShareSys.AddInterface(NULL, this);
+}
+
+void Translator::OnSourceModShutdown()
+{
+ g_pCorePhrases->Destroy();
}
bool Translator::GetLanguageByCode(const char *code, unsigned int *index)
@@ -842,7 +848,7 @@ void Translator::RebuildLanguageDatabase(const char *lang_header_file)
g_Logger.LogError("Server language was set to bad language \"%s\" -- reverting to English", m_InitialLang);
strncopy(m_InitialLang, "en", sizeof(m_InitialLang));
- m_ServerLang = LANGUAGE_ENGLISH;
+ m_ServerLang = SOURCEMOD_LANGUAGE_ENGLISH;
}
m_ServerLang = reinterpret_cast(serverLang);
@@ -936,61 +942,6 @@ CPhraseFile *Translator::GetFileByIndex(unsigned int index)
return m_Files[index];
}
-size_t Translator::Translate(char *buffer, size_t maxlength, void **params, const Translation *pTrans)
-{
- void *new_params[MAX_TRANSLATE_PARAMS];
-
- /* Rewrite the parameter order */
- for (unsigned int i=0; ifmt_count; i++)
- {
- new_params[i] = params[pTrans->fmt_order[i]];
- }
-
- return gnprintf(buffer, maxlength, pTrans->szPhrase, new_params);
-}
-
-TransError Translator::CoreTransEx(CPhraseFile *pFile,
- int client,
- char *buffer,
- size_t maxlength,
- const char *phrase,
- void **params,
- size_t *outlen)
-{
- Translation trans;
- TransError err;
-
- /* Using server lang temporarily until client lang stuff is implemented */
- if ((err = pFile->GetTranslation(phrase, m_ServerLang, &trans)) != Trans_Okay)
- {
- return err;
- }
-
- size_t len = Translate(buffer, maxlength, params, &trans);
-
- if (outlen)
- {
- *outlen = len;
- }
-
- return Trans_Okay;
-}
-
-TransError Translator::CoreTrans(int client,
- char *buffer,
- size_t maxlength,
- const char *phrase,
- void **params,
- size_t *outlen)
-{
- if (!g_pCorePhrases)
- {
- return Trans_BadPhraseFile;
- }
-
- return CoreTransEx(g_pCorePhrases, client, buffer, maxlength, phrase, params, outlen);
-}
-
unsigned int Translator::GetServerLanguage()
{
return m_ServerLang;
@@ -1021,3 +972,105 @@ bool Translator::GetLanguageInfo(unsigned int number, const char **code, const c
return true;
}
+
+const char *Translator::GetInterfaceName()
+{
+ return SMINTERFACE_TRANSLATOR_NAME;
+}
+
+unsigned int Translator::GetInterfaceVersion()
+{
+ return SMINTERFACE_TRANSLATOR_VERSION;
+}
+
+IPhraseCollection *Translator::CreatePhraseCollection()
+{
+ return new CPhraseCollection();
+}
+
+int Translator::SetGlobalTarget(int index)
+{
+ return g_SourceMod.SetGlobalTarget(index);
+}
+
+int Translator::GetGlobalTarget() const
+{
+ return g_SourceMod.GetGlobalTarget();
+}
+
+bool CoreTranslate(char *buffer,
+ size_t maxlength,
+ const char *format,
+ unsigned int numparams,
+ size_t *pOutLength,
+ ...)
+{
+ va_list ap;
+ unsigned int i;
+ const char *fail_phrase;
+ void *params[MAX_TRANSLATE_PARAMS];
+
+ if (numparams > MAX_TRANSLATE_PARAMS)
+ {
+ assert(false);
+ return false;
+ }
+
+ va_start(ap, pOutLength);
+ for (i = 0; i < numparams; i++)
+ {
+ params[i] = va_arg(ap, void *);
+ }
+ va_end(ap);
+
+ if (!g_pCorePhrases->FormatString(buffer,
+ maxlength,
+ format,
+ params,
+ numparams,
+ pOutLength,
+ &fail_phrase))
+ {
+ if (fail_phrase != NULL)
+ {
+ g_Logger.LogError("[SM] Could not find core phrase: %s", fail_phrase);
+ }
+ else
+ {
+ g_Logger.LogError("[SM] Unknown fatal error while translating a core phrase.");
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool Translator::FormatString(char *buffer,
+ size_t maxlength,
+ const char *format,
+ IPhraseCollection *pPhrases,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase)
+{
+ unsigned int arg;
+
+ arg = 0;
+ if (!gnprintf(buffer, maxlength, format, pPhrases, params, numparams, arg, pOutLength, pFailPhrase))
+ {
+ return false;
+ }
+
+ if (arg != numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+
+ return true;
+}
diff --git a/core/Translator.h b/core/Translator.h
index c878e3e7..c003cf6a 100644
--- a/core/Translator.h
+++ b/core/Translator.h
@@ -38,6 +38,7 @@
#include "sm_globals.h"
#include "sm_memtable.h"
#include "ITextParsers.h"
+#include
#define MAX_TRANSLATE_PARAMS 32
#define CORELANG_ENGLISH 0
@@ -60,25 +61,9 @@ struct Language
int m_FullName;
};
-struct Translation
-{
- const char *szPhrase; /**< Translated phrase. */
- unsigned int fmt_count; /**< Number of format parameters. */
- int *fmt_order; /**< Format phrase order. */
-};
-
-#define LANGUAGE_ENGLISH 0
-
-enum TransError
-{
- Trans_Okay = 0,
- Trans_BadLanguage = 1,
- Trans_BadPhrase = 2,
- Trans_BadPhraseLanguage = 3,
- Trans_BadPhraseFile = 4,
-};
-
-class CPhraseFile : public ITextListener_SMC
+class CPhraseFile :
+ public ITextListener_SMC,
+ public IPhraseFile
{
public:
CPhraseFile(Translator *pTranslator, const char *file);
@@ -112,7 +97,8 @@ private:
class Translator :
public ITextListener_SMC,
- public SMGlobalClass
+ public SMGlobalClass,
+ public ITranslator
{
public:
Translator();
@@ -125,6 +111,7 @@ public: // SMGlobalClass
size_t maxlength);
void OnSourceModAllInitialized();
void OnSourceModLevelChange(const char *mapName);
+ void OnSourceModShutdown();
public: // ITextListener_SMC
void ReadSMC_ParseStart();
SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
@@ -138,23 +125,30 @@ public:
bool GetLanguageInfo(unsigned int number, const char **code, const char **name);
bool GetLanguageByCode(const char *code, unsigned int *index);
bool GetLanguageByName(const char *name, unsigned int *index);
- size_t Translate(char *buffer, size_t maxlength, void **params, const Translation *pTrans);
CPhraseFile *GetFileByIndex(unsigned int index);
- TransError CoreTransEx(CPhraseFile *pFile,
- int client,
- char *buffer,
- size_t maxlength,
- const char *phrase,
- void **params,
- size_t *outlen=NULL);
- TransError CoreTrans(int client,
- char *buffer,
- size_t maxlength,
- const char *phrase,
- void **params,
- size_t *outlen=NULL);
+public: //ITranslator
unsigned int GetServerLanguage();
unsigned int GetClientLanguage(int client);
+ const char *GetInterfaceName();
+ unsigned int GetInterfaceVersion();
+ IPhraseCollection *CreatePhraseCollection();
+ int SetGlobalTarget(int index);
+ int GetGlobalTarget() const;
+ size_t FormatString(
+ char *buffer,
+ size_t maxlength,
+ SourcePawn::IPluginContext *pContext,
+ const cell_t *params,
+ unsigned int param);
+ bool FormatString(
+ char *buffer,
+ size_t maxlength,
+ const char *format,
+ IPhraseCollection *pPhrases,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase);
private:
bool AddLanguage(const char *langcode, const char *description);
private:
@@ -168,7 +162,15 @@ private:
char m_InitialLang[3];
};
-extern CPhraseFile *g_pCorePhrases;
+/* Nice little wrapper to handle error logging and whatnot */
+bool CoreTranslate(char *buffer,
+ size_t maxlength,
+ const char *format,
+ unsigned int numparams,
+ size_t *pOutLength,
+ ...);
+
+extern IPhraseCollection *g_pCorePhrases;
extern unsigned int g_pCorePhraseID;
extern Translator g_Translator;
diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj
index a6921478..f61857de 100644
--- a/core/msvc8/sourcemod_mm.vcproj
+++ b/core/msvc8/sourcemod_mm.vcproj
@@ -901,6 +901,10 @@
RelativePath="..\MenuVoting.cpp"
>
+
+
@@ -1063,6 +1067,10 @@
RelativePath="..\MenuVoting.h"
>
+
+
@@ -1232,6 +1240,10 @@
RelativePath="..\..\public\ITimerSystem.h"
>
+
+
diff --git a/core/sm_stringutil.cpp b/core/sm_stringutil.cpp
index f12bba8b..020a87eb 100644
--- a/core/sm_stringutil.cpp
+++ b/core/sm_stringutil.cpp
@@ -50,43 +50,6 @@
return 0; \
}
-size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
-{
- Translation pTrans;
- TransError err;
-
- err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetClientLanguage(client), &pTrans);
- if (err != Trans_Okay)
- {
- err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
- if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
- {
- err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
- }
- }
-
- if (err != Trans_Okay)
- {
- return UTIL_Format(buffer, maxlength, "%s", phrase);
- }
-
- return g_Translator.Translate(buffer, maxlength, params, &pTrans);
-}
-
-inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
-{
- TransError err = Trans_BadLanguage;
- CPhraseFile *phrfl;
-
- for (size_t i=0; iGetLangFileByIndex(i));
- err = phrfl->GetTranslation(key, langid, pTrans);
- }
-
- return (err == Trans_Okay) ? true : false;
-}
-
inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
{
cell_t new_params[MAX_TRANSLATE_PARAMS];
@@ -110,11 +73,13 @@ size_t Translate(char *buffer,
*error = false;
Translation pTrans;
CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
- size_t langcount = pl->GetLangFileCount();
unsigned int max_params = 0;
+ IPhraseCollection *pPhrases;
+
+ pPhrases = pl->GetPhrases();
try_serverlang:
- if (target == LANG_SERVER)
+ if (target == SOURCEMOD_SERVER_LANGUAGE)
{
langid = g_Translator.GetServerLanguage();
}
@@ -128,16 +93,16 @@ try_serverlang:
goto error_out;
}
- if (!TryTranslation(pl, key, langid, langcount, &pTrans))
+ if (pPhrases->FindTranslation(key, langid, &pTrans) != Trans_Okay)
{
- if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
+ if (target != SOURCEMOD_SERVER_LANGUAGE && langid != g_Translator.GetServerLanguage())
{
- target = LANG_SERVER;
+ target = SOURCEMOD_SERVER_LANGUAGE;
goto try_serverlang;
}
- else if (langid != LANGUAGE_ENGLISH)
+ else if (langid != SOURCEMOD_LANGUAGE_ENGLISH)
{
- if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
+ if (!pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &pTrans))
{
pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
goto error_out;
@@ -575,11 +540,32 @@ void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags
*buf_p = buf;
}
-size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
+bool gnprintf(char *buffer,
+ size_t maxlen,
+ const char *format,
+ IPhraseCollection *pPhrases,
+ void **params,
+ unsigned int numparams,
+ unsigned int &curparam,
+ size_t *pOutLength,
+ const char **pFailPhrase)
{
if (!buffer || !maxlen)
{
- return 0;
+ if (pOutLength != NULL)
+ {
+ *pOutLength = 0;
+ }
+ return true;
+ }
+
+ if (numparams > MAX_TRANSLATE_PARAMS)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
}
int arg = 0;
@@ -668,7 +654,16 @@ reswitch:
{
goto done;
}
- char *c = (char *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ char *c = (char *)params[curparam];
+ curparam++;
*buf_p++ = *c;
llen--;
arg++;
@@ -676,7 +671,16 @@ reswitch:
}
case 'b':
{
- int *value = (int *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ int *value = (int *)params[curparam];
+ curparam++;
AddBinary(&buf_p, llen, *value, width, flags);
arg++;
break;
@@ -684,35 +688,224 @@ reswitch:
case 'd':
case 'i':
{
- int *value = (int *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ int *value = (int *)params[curparam];
+ curparam++;
AddInt(&buf_p, llen, *value, width, flags);
arg++;
break;
}
case 'u':
{
- unsigned int *value = (unsigned int *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ unsigned int *value = (unsigned int *)params[curparam];
+ curparam++;
AddUInt(&buf_p, llen, *value, width, flags);
arg++;
break;
}
case 'f':
{
- float *value = (float *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ float *value = (float *)params[curparam];
+ curparam++;
AddFloat(&buf_p, llen, *value, width, prec, flags);
arg++;
break;
}
case 's':
{
- const char *str = (const char *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ const char *str = (const char *)params[curparam];
+ curparam++;
AddString(&buf_p, llen, str, width, prec);
arg++;
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ int target;
+ const char *key;
+ size_t out_length;
+ Translation trans;
+ unsigned int lang_id;
+
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ key = (const char *)(params[curparam]);
+ curparam++;
+
+ if (ch == 'T')
+ {
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ target = *((int *)(params[curparam]));
+ curparam++;
+ }
+ else
+ {
+ target = g_Translator.GetGlobalTarget();
+ }
+
+try_again:
+ if (target == SOURCEMOD_SERVER_LANGUAGE)
+ {
+ lang_id = g_Translator.GetServerLanguage();
+ }
+ else if (target >= 1 && target <= g_Players.GetMaxClients())
+ {
+ lang_id = g_Translator.GetClientLanguage(target);
+ }
+ else
+ {
+ lang_id = g_Translator.GetServerLanguage();
+ }
+
+ if (pPhrases == NULL)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = key;
+ }
+ return false;
+ }
+
+ if (pPhrases->FindTranslation(key, lang_id, &trans) != Trans_Okay)
+ {
+ if (target != SOURCEMOD_SERVER_LANGUAGE && lang_id != g_Translator.GetServerLanguage())
+ {
+ target = SOURCEMOD_SERVER_LANGUAGE;
+ goto try_again;
+ }
+ else if (lang_id != SOURCEMOD_LANGUAGE_ENGLISH)
+ {
+ if (pPhrases->FindTranslation(key, SOURCEMOD_LANGUAGE_ENGLISH, &trans) != Trans_Okay)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = key;
+ }
+ return false;
+ }
+ }
+ else
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = key;
+ }
+ return false;
+ }
+ }
+
+ if (trans.fmt_count)
+ {
+ unsigned int i;
+ void *new_params[MAX_TRANSLATE_PARAMS];
+
+ if (curparam + trans.fmt_count > numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+
+ /* Copy the array and re-order the stack */
+ memcpy(new_params, params, sizeof(void *) * numparams);
+ for (i = 0; i < trans.fmt_count; i++)
+ {
+ new_params[i] = const_cast(params[curparam + trans.fmt_order[i]]);
+ }
+
+ if (!gnprintf(buf_p,
+ llen,
+ trans.szPhrase,
+ pPhrases,
+ new_params,
+ numparams,
+ curparam,
+ &out_length,
+ pFailPhrase))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!gnprintf(buf_p,
+ llen,
+ trans.szPhrase,
+ pPhrases,
+ params,
+ numparams,
+ curparam,
+ &out_length,
+ pFailPhrase))
+ {
+ return false;
+ }
+ }
+
+ buf_p += out_length;
+ llen -= out_length;
+
break;
}
case 'X':
{
- unsigned int *value = (unsigned int *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ unsigned int *value = (unsigned int *)params[curparam];
+ curparam++;
flags |= UPPERDIGITS;
AddHex(&buf_p, llen, *value, width, flags);
arg++;
@@ -720,7 +913,16 @@ reswitch:
}
case 'x':
{
- unsigned int *value = (unsigned int *)args[arg];
+ if (curparam >= numparams)
+ {
+ if (pFailPhrase != NULL)
+ {
+ *pFailPhrase = NULL;
+ }
+ return false;
+ }
+ unsigned int *value = (unsigned int *)params[curparam];
+ curparam++;
AddHex(&buf_p, llen, *value, width, flags);
arg++;
break;
@@ -761,7 +963,12 @@ reswitch:
done:
*buf_p = '\0';
- return (maxlen - llen - 1);
+ if (pOutLength != NULL)
+ {
+ *pOutLength = (maxlen - llen - 1);
+ }
+
+ return true;
}
size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
diff --git a/core/sm_stringutil.h b/core/sm_stringutil.h
index 12868046..8c3a7f24 100644
--- a/core/sm_stringutil.h
+++ b/core/sm_stringutil.h
@@ -33,23 +33,30 @@
#define _INCLUDE_SOURCEMOD_STRINGUTIL_H_
#include
-#include "sp_vm_api.h"
-#include "sp_typeutil.h"
+#include
+#include
+#include
using namespace SourcePawn;
-
-#define LANG_SERVER 0
+using namespace SourceMod;
#define IS_STR_FILLED(var) (var[0] != '\0')
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);
+bool gnprintf(char *buffer,
+ size_t maxlen,
+ const char *format,
+ IPhraseCollection *pPhrases,
+ void **params,
+ unsigned int numparams,
+ unsigned int &curparam,
+ size_t *pOutLength,
+ const char **pFailPhrase);
size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap);
char *sm_strdup(const char *str);
-size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params);
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace);
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen);
char *UTIL_TrimWhitespace(char *str, size_t &len);
diff --git a/core/smn_console.cpp b/core/smn_console.cpp
index 8f2a4eb7..c635a339 100644
--- a/core/smn_console.cpp
+++ b/core/smn_console.cpp
@@ -828,7 +828,7 @@ static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
@@ -849,7 +849,7 @@ static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
diff --git a/core/smn_core.cpp b/core/smn_core.cpp
index 93feb99d..78d18ebd 100644
--- a/core/smn_core.cpp
+++ b/core/smn_core.cpp
@@ -126,7 +126,7 @@ static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
{
char buffer[512];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
@@ -502,7 +502,7 @@ static cell_t LibraryExists(IPluginContext *pContext, const cell_t *params)
static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params)
{
char buffer[2048];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 3);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
@@ -532,7 +532,7 @@ static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
}
char buffer[2048];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
@@ -565,7 +565,7 @@ static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params)
}
char buffer[2048];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
diff --git a/core/smn_fakenatives.cpp b/core/smn_fakenatives.cpp
index 35697216..76f865f3 100644
--- a/core/smn_fakenatives.cpp
+++ b/core/smn_fakenatives.cpp
@@ -136,7 +136,7 @@ static cell_t ThrowNativeError(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Not called from inside a native function");
}
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[512];
diff --git a/core/smn_filesystem.cpp b/core/smn_filesystem.cpp
index 8c19e4af..9870d6a6 100644
--- a/core/smn_filesystem.cpp
+++ b/core/smn_filesystem.cpp
@@ -568,7 +568,7 @@ static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
@@ -594,7 +594,7 @@ static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
@@ -612,7 +612,7 @@ static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
@@ -667,7 +667,7 @@ static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
}
char buffer[2048];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
@@ -698,7 +698,7 @@ static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
}
char buffer[2048];
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
diff --git a/core/smn_lang.cpp b/core/smn_lang.cpp
index 56e6b6fa..dff43339 100644
--- a/core/smn_lang.cpp
+++ b/core/smn_lang.cpp
@@ -37,24 +37,25 @@
static cell_t sm_LoadTranslations(IPluginContext *pCtx, const cell_t *params)
{
- char *filename;
- unsigned int index;
+ char *filename, *ext;
+ char buffer[PLATFORM_MAX_PATH];
CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
pCtx->LocalToString(params[1], &filename);
+ UTIL_Format(buffer, sizeof(buffer), "%s", filename);
- /* Check if there is no extension */
- const char *ext = g_LibSys.GetFileExtension(filename);
- if (!ext || (strcmp(ext, "cfg") && strcmp(ext, "txt")))
+ /* Make sure there is no extension */
+ if ((ext = strstr(buffer, ".txt")) != NULL
+ || (ext = strstr(buffer, ".cfg")) != NULL)
{
- /* Append one */
- static char new_file[PLATFORM_MAX_PATH];
- UTIL_Format(new_file, sizeof(new_file), "%s.txt", filename);
- filename = new_file;
+ /* Simple heuristic -- just see if it's at the end and terminate if so */
+ if (ext - buffer == strlen(buffer) - 4)
+ {
+ *ext = '\0';
+ }
}
- index = g_Translator.FindOrAddPhraseFile(filename);
- pl->AddLangFile(index);
+ pl->GetPhrases()->AddPhraseFile(buffer);
return 1;
}
diff --git a/core/smn_menus.cpp b/core/smn_menus.cpp
index 72628c07..efaaaf63 100644
--- a/core/smn_menus.cpp
+++ b/core/smn_menus.cpp
@@ -838,7 +838,7 @@ static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
}
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
char buffer[1024];
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
diff --git a/core/smn_player.cpp b/core/smn_player.cpp
index 688f87ed..979e8a86 100644
--- a/core/smn_player.cpp
+++ b/core/smn_player.cpp
@@ -928,7 +928,7 @@ static cell_t _ShowActivity(IPluginContext *pContext,
}
else
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
@@ -1065,7 +1065,7 @@ static cell_t _ShowActivity2(IPluginContext *pContext,
}
else
{
- g_SourceMod.SetGlobalTarget(LANG_SERVER);
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, fmt_param);
if (pContext->GetContext()->n_err != SP_ERROR_NONE)
diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp
index 7b2dfcab..30b3bf6f 100644
--- a/core/systems/PluginSys.cpp
+++ b/core/systems/PluginSys.cpp
@@ -67,6 +67,7 @@ CPlugin::CPlugin(const char *file)
m_FakeNativesMissing = false;
m_LibraryMissing = false;
m_bGotAllLoaded = false;
+ m_pPhrases = g_Translator.CreatePhraseCollection();
}
CPlugin::~CPlugin()
@@ -111,6 +112,11 @@ CPlugin::~CPlugin()
delete m_configs[i];
}
m_configs.clear();
+ if (m_pPhrases != NULL)
+ {
+ m_pPhrases->Destroy();
+ m_pPhrases = NULL;
+ }
}
void CPlugin::InitIdentity()
@@ -128,7 +134,6 @@ unsigned int CPlugin::CalcMemUsage()
unsigned int base_size =
sizeof(CPlugin)
+ sizeof(IdentityToken_t)
- + (m_PhraseFiles.size() * sizeof(unsigned int))
+ (m_dependents.size() * sizeof(CPlugin *))
+ (m_dependsOn.size() * sizeof(CPlugin *))
+ (m_fakeNatives.size() * (sizeof(FakeNative *) + sizeof(FakeNative)))
@@ -710,19 +715,9 @@ void CPlugin::SetTimeStamp(time_t t)
m_LastAccess = t;
}
-void CPlugin::AddLangFile(unsigned int index)
+IPhraseCollection *CPlugin::GetPhrases()
{
- m_PhraseFiles.push_back(index);
-}
-
-size_t CPlugin::GetLangFileCount()
-{
- return m_PhraseFiles.size();
-}
-
-unsigned int CPlugin::GetLangFileByIndex(unsigned int index)
-{
- return m_PhraseFiles.at(index);
+ return m_pPhrases;
}
void CPlugin::DependencyDropped(CPlugin *pOwner)
@@ -1488,8 +1483,8 @@ bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxleng
OnLibraryAction((*s_iter).c_str(), true, false);
}
- /* Finally, add the core language file */
- pPlugin->AddLangFile(g_pCorePhraseID);
+ /* :TODO: optimize? does this even matter? */
+ pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
return true;
}
diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h
index d7dc8e65..a1aee68a 100644
--- a/core/systems/PluginSys.h
+++ b/core/systems/PluginSys.h
@@ -53,6 +53,7 @@
#else
#include "convar_sm.h"
#endif
+#include "ITranslator.h"
using namespace SourceHook;
@@ -244,19 +245,9 @@ public:
bool IsRunnable();
/**
- * Adds a language file index to the plugin's list.
+ * Get languages info.
*/
- void AddLangFile(unsigned int index);
-
- /**
- * Get language file count for this plugin.
- */
- size_t GetLangFileCount();
-
- /**
- * Get language file index based on the vector index.
- */
- unsigned int GetLangFileByIndex(unsigned int index);
+ IPhraseCollection *GetPhrases();
public:
/**
* Returns the modification time during last plugin load.
@@ -300,7 +291,7 @@ private:
IdentityToken_t *m_ident;
Handle_t m_handle;
bool m_WasRunning;
- CVector m_PhraseFiles;
+ IPhraseCollection *m_pPhrases;
List m_dependents;
List m_dependsOn;
List m_fakeNatives;
diff --git a/public/ISourceMod.h b/public/ISourceMod.h
index 3fb97263..80fa1d05 100644
--- a/public/ISourceMod.h
+++ b/public/ISourceMod.h
@@ -221,6 +221,7 @@ namespace SourceMod
* translations (that is, %t).
*
* @param index Client index.
+ * @deprecated Use ITranslator::GetGlobalTarget() instead.
* @return Old global client value.
*/
virtual unsigned int SetGlobalTarget(unsigned int index) =0;
@@ -229,6 +230,7 @@ namespace SourceMod
* @brief Returns the global client SourceMod is currently using
* for assisted translations (that is, %t).
*
+ * @deprecated Use ITranslator::GetGlobalTarget() instead.
* @return Global client value.
*/
virtual unsigned int GetGlobalTarget() const =0;
diff --git a/public/ITranslator.h b/public/ITranslator.h
new file mode 100644
index 00000000..cdd0e8f9
--- /dev/null
+++ b/public/ITranslator.h
@@ -0,0 +1,329 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#ifndef _INCLUDE_SOURCEMOD_TRANSLATOR_INTERFACE_H_
+#define _INCLUDE_SOURCEMOD_TRANSLATOR_INTERFACE_H_
+
+#include
+
+#define SMINTERFACE_TRANSLATOR_NAME "ITranslator"
+#define SMINTERFACE_TRANSLATOR_VERSION 1
+
+/**
+ * @file ITranslator.h
+ * @brief Defines interfaces related to translation files.
+ */
+
+namespace SourceMod
+{
+ /**
+ * @brief SourceMod hardcodes the English language (default) to ID 0.
+ * This cannot be changed and languages.cfg should never have it as anything
+ * other than the first index.
+ */
+ #define SOURCEMOD_LANGUAGE_ENGLISH 0
+
+ /**
+ * @brief For %T formats, specifies that the language should be that of the
+ * server and not a specific client.
+ */
+ #define SOURCEMOD_SERVER_LANGUAGE 0
+
+ /**
+ * @brief Translation error codes.
+ */
+ enum TransError
+ {
+ Trans_Okay = 0, /**< Translation succeeded. */
+ Trans_BadLanguage = 1, /**< Bad language ID. */
+ Trans_BadPhrase = 2, /**< Phrase not found. */
+ Trans_BadPhraseLanguage = 3, /**< Phrase not found in the given language. */
+ Trans_BadPhraseFile = 4, /**< Phrase file was unreadable. */
+ };
+
+ /**
+ * @brief Contains information about a translation phrase.
+ */
+ struct Translation
+ {
+ const char *szPhrase; /**< Translated phrase. */
+ unsigned int fmt_count; /**< Number of format parameters. */
+ int *fmt_order; /**< Array of size fmt_count where each
+ element is the numerical order of
+ parameter insertion, starting from
+ 0.
+ */
+ };
+
+ /**
+ * @brief Represents a phrase file from SourceMod's "translations" folder.
+ */
+ class IPhraseFile
+ {
+ public:
+ /**
+ * @brief Attempts to find a translation phrase in a phrase file.
+ *
+ * @param szPhrase String containing the phrase name.
+ * @param lang_id Language ID.
+ * @param pTrans Buffer to store translation info.
+ * @return Translation error code indicating success
+ * (pTrans is filled) or failure (pTrans
+ * contents is undefined).
+ */
+ virtual TransError GetTranslation(
+ const char *szPhrase,
+ unsigned int lang_id,
+ Translation *pTrans) =0;
+
+ /**
+ * @brief Returns the file name of this translation file.
+ *
+ * @return File name.
+ */
+ virtual const char *GetFilename() =0;
+ };
+
+ /**
+ * Represents a collection of phrase files.
+ */
+ class IPhraseCollection
+ {
+ public:
+ /**
+ * @brief Adds a phrase file to the collection, using a cached one
+ * if already found. The return value is provided for informational
+ * purposes and does not need to be saved. The life time of the
+ * return pointer is equal to the life time of the collection.
+ *
+ * This function will internally ignore dupliate additions but still
+ * return a valid pointer.
+ *
+ * @param filename File name, without the ".txt" extension, of
+ * the phrase file in the translations folder.
+ * @return An IPhraseFile pointer, even if the file does
+ * not exist.
+ */
+ virtual IPhraseFile *AddPhraseFile(const char *filename) =0;
+
+ /**
+ * @brief Returns the number of contained phrase files.
+ *
+ * @return Number of contained phrase files.
+ */
+ virtual unsigned int GetFileCount() =0;
+
+ /**
+ * @brief Returns the pointer to a contained phrase file.
+ *
+ * @param file File index, from 0 to GetFileCount()-1.
+ * @return IPhraseFile pointer, or NULL if out of
+ * range.
+ */
+ virtual IPhraseFile *GetFile(unsigned int file) =0;
+
+ /**
+ * @brief Destroys the phrase collection, freeing all internal
+ * resources and invalidating the object.
+ */
+ virtual void Destroy() =0;
+
+ /**
+ * @brief Attempts a translation across a given language. All
+ * contained files are searched for an appropriate match; the
+ * first valid match is returned.
+ *
+ * @param key String containing the phrase name.
+ * @param langid Language ID to translate to.
+ * @param pTrans Translation buffer.
+ * @return Translation error code; on success,
+ * pTrans is valid. On failure, the
+ * contents of pTrans is undefined.
+ */
+ virtual TransError FindTranslation(
+ const char *key,
+ unsigned int langid,
+ Translation *pTrans) =0;
+
+ /**
+ * @brief Formats a phrase given a parameter stack. The parameter
+ * stack size must exactly match the expected parameter count. If
+ * this count is too small or too large, the format fails.
+ *
+ * @param buffer Buffer to store formatted text.
+ * @param maxlength Maximum length of the buffer.
+ * @param format String containing format information.
+ * This is equivalent to SourceMod's Format()
+ * native, and sub-translations are acceptable.
+ * @param params An array of pointers to each parameter.
+ * Integer parameters must have a pointer to the integer.
+ * Float parameters must have a pointer to a float.
+ * String parameters must be a string pointer.
+ * Char parameters must be a pointer to a char.
+ * Translation parameters fill multiple indexes in the
+ * array. For %T translations, the expected stack is:
+ * [phrase string pointer] [int target id pointer] [...]
+ * Where [...] is the required parameters for the translation,
+ * in the order expected by the phrase, not the phrase's
+ * translation. For example, say the format is:
+ * "%d %T" and the phrase's format is {1:s,2:f}, then the
+ * parameter stack should be:
+ * int *, const char *, int *, const char *, float *
+ * The %t modifier is the same except the target id pointer
+ * would be removed:
+ * int *, const char *, const char *, float *
+ * @param numparams Number of parameters in the params array.
+ * @param pOutLength Optional pointer filled with output length on success.
+ * @param pFailPhrase Optional pointer; on failure, is filled with NULL if the
+ * failure was not due to a failed translation phrase.
+ * Otherwise, it is filled with the given phrase name pointer
+ * from the parameter stack. Undefined on success.
+ * @return True on success. False if the parameter stack was not
+ * exactly the right length, or if a translation phrase
+ * could not be found.
+ */
+ virtual bool FormatString(
+ char *buffer,
+ size_t maxlength,
+ const char *format,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase) =0;
+ };
+
+ /**
+ * @brief Provides functions for translation.
+ */
+ class ITranslator : public SMInterface
+ {
+ public:
+ virtual const char *GetInterfaceName() =0;
+ virtual unsigned int GetInterfaceVersion() =0;
+ public:
+ /**
+ * @brief Creates a new phrase collection object.
+ *
+ * @return A new phrase collection object, which must be
+ * destroyed via IPhraseCollection::Destroy() when
+ * no longer needed.
+ */
+ virtual IPhraseCollection *CreatePhraseCollection() =0;
+
+ /**
+ * @brief Returns the server language.
+ *
+ * @return Server language index.
+ */
+ virtual unsigned int GetServerLanguage() =0;
+
+ /**
+ * @brief Returns a client's language.
+ *
+ * @param client Client index.
+ * @return Client language index, or server's if client's is
+ * not known.
+ */
+ virtual unsigned int GetClientLanguage(int client) =0;
+
+ /**
+ * @brief Sets the global client SourceMod will use for assisted
+ * translations (that is, %t).
+ *
+ * @param index Client index (0 for server).
+ * @return Old global client value.
+ */
+ virtual int SetGlobalTarget(int index) =0;
+
+ /**
+ * @brief Returns the global client SourceMod is currently using
+ * for assisted translations (that is, %t).
+ *
+ * @return Global client index (0 for server).
+ */
+ virtual int GetGlobalTarget() const =0;
+
+ /**
+ * @brief Formats a phrase given a parameter stack. The parameter
+ * stack size must exactly match the expected parameter count. If
+ * this count is too small or too large, the format fails.
+ *
+ * Note: This is the same as IPhraseCollection::FormatString(), except
+ * that the IPhraseCollection parameter is explicit instead of implicit.
+ *
+ * @param buffer Buffer to store formatted text.
+ * @param maxlength Maximum length of the buffer.
+ * @param format String containing format information.
+ * This is equivalent to SourceMod's Format()
+ * native, and sub-translations are acceptable.
+ * @param pPhrases Optional phrase collection pointer to search for
+ * phrases.
+ * @param params An array of pointers to each parameter.
+ * Integer parameters must have a pointer to the integer.
+ * Float parameters must have a pointer to a float.
+ * String parameters must be a string pointer.
+ * Char parameters must be a pointer to a char.
+ * Translation parameters fill multiple indexes in the
+ * array. For %T translations, the expected stack is:
+ * [phrase string pointer] [int target id pointer] [...]
+ * Where [...] is the required parameters for the translation,
+ * in the order expected by the phrase, not the phrase's
+ * translation. For example, say the format is:
+ * "%d %T" and the phrase's format is {1:s,2:f}, then the
+ * parameter stack should be:
+ * int *, const char *, int *, const char *, float *
+ * The %t modifier is the same except the target id pointer
+ * would be removed:
+ * int *, const char *, const char *, float *
+ * @param numparams Number of parameters in the params array.
+ * @param pOutLength Optional pointer filled with output length on success.
+ * @param pFailPhrase Optional pointer; on failure, is filled with NULL if the
+ * failure was not due to a failed translation phrase.
+ * Otherwise, it is filled with the given phrase name pointer
+ * from the parameter stack. Undefined on success.
+ * @return True on success. False if the parameter stack was not
+ * exactly the right length, or if a translation phrase
+ * could not be found.
+ */
+ virtual bool FormatString(
+ char *buffer,
+ size_t maxlength,
+ const char *format,
+ IPhraseCollection *pPhrases,
+ void **params,
+ unsigned int numparams,
+ size_t *pOutLength,
+ const char **pFailPhrase) =0;
+ };
+}
+
+#endif //_INCLUDE_SOURCEMOD_TRANSLATOR_INTERFACE_H_
+
diff --git a/public/mms_sample_ext/sm_sdk_config.cpp b/public/mms_sample_ext/sm_sdk_config.cpp
index 6c5f377a..56107441 100644
--- a/public/mms_sample_ext/sm_sdk_config.cpp
+++ b/public/mms_sample_ext/sm_sdk_config.cpp
@@ -79,6 +79,9 @@ bool SM_AcquireInterfaces(char *error, size_t maxlength)
#if defined SMEXT_ENABLE_TEXTPARSERS
SM_FIND_IFACE_OR_FAIL(TEXTPARSERS, sm_text, error, maxlength);
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+ SM_FIND_IFACE_OR_FAIL(TRANSLATOR, sm_translator, error, maxlength);
+#endif
return true;
}
@@ -131,6 +134,9 @@ void SM_UnsetInterfaces()
#if defined SMEXT_ENABLE_TEXTPARSERS
sm_text = NULL;
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+ sm_translator = NULL;
+#endif
}
IExtension *myself = NULL;
@@ -179,4 +185,6 @@ SourceMod::IAdminSystem *sm_adminsys = NULL;
#if defined SMEXT_ENABLE_TEXTPARSERS
SourceMod::ITextParsers *sm_text = NULL;
#endif
-
+#if defined SMEXT_ENABLE_TRANSLATOR
+SourceMod::ITranslator *sm_translator = NULL;
+#endif
diff --git a/public/mms_sample_ext/sm_sdk_config.h b/public/mms_sample_ext/sm_sdk_config.h
index 8cd53d3e..65a3d1d9 100644
--- a/public/mms_sample_ext/sm_sdk_config.h
+++ b/public/mms_sample_ext/sm_sdk_config.h
@@ -70,6 +70,7 @@ void SM_UnsetInterfaces();
//#define SMEXT_ENABLE_PLUGINSYS
//#define SMEXT_ENABLE_ADMINSYS
//#define SMEXT_ENABLE_TEXTPARSERS
+//#define SMEXT_ENABLE_TRANSLATOR
/**
@@ -155,5 +156,10 @@ extern SourceMod::IAdminSystem *sm_adminsys;
extern SourceMod::ITextParsers *sm_text;
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+#include
+extern SourceMod::ITranslator *sm_translator;
+#endif
+
#endif //_INCLUDE_SOURCEMOD_CONFIG_H_
diff --git a/public/sample_ext/sdk/smsdk_config.h b/public/sample_ext/sdk/smsdk_config.h
index 9421f5aa..3d4f60cc 100644
--- a/public/sample_ext/sdk/smsdk_config.h
+++ b/public/sample_ext/sdk/smsdk_config.h
@@ -75,5 +75,6 @@
//#define SMEXT_ENABLE_ADMINSYS
//#define SMEXT_ENABLE_TEXTPARSERS
//#define SMEXT_ENABLE_USERMSGS
+//#define SMEXT_ENABLE_TRANSLATOR
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
diff --git a/public/sample_ext/sdk/smsdk_ext.cpp b/public/sample_ext/sdk/smsdk_ext.cpp
index 674e04e4..865f536e 100644
--- a/public/sample_ext/sdk/smsdk_ext.cpp
+++ b/public/sample_ext/sdk/smsdk_ext.cpp
@@ -94,6 +94,9 @@ ITextParsers *textparsers = NULL;
#if defined SMEXT_ENABLE_USERMSGS
IUserMessages *usermsgs = NULL;
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+ITranslator *translator = NULL;
+#endif
/** Exports the main interface */
PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI()
@@ -179,6 +182,9 @@ bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error,
#if defined SMEXT_ENABLE_USERMSGS
SM_GET_IFACE(USERMSGS, usermsgs);
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+ SM_GET_IFACE(TRANSLATOR, translator);
+#endif
if (SDK_OnLoad(error, maxlength, late))
{
diff --git a/public/sample_ext/sdk/smsdk_ext.h b/public/sample_ext/sdk/smsdk_ext.h
index 0ee0dfa2..115fc800 100644
--- a/public/sample_ext/sdk/smsdk_ext.h
+++ b/public/sample_ext/sdk/smsdk_ext.h
@@ -88,6 +88,9 @@
#if defined SMEXT_ENABLE_USERMSGS
#include
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+#include
+#endif
#if defined SMEXT_CONF_METAMOD
#include
@@ -283,6 +286,9 @@ extern IAdminSystem *adminsys;
#if defined SMEXT_ENABLE_USERMSGS
extern IUserMessages *usermsgs;
#endif
+#if defined SMEXT_ENABLE_TRANSLATOR
+extern ITranslator *translator;
+#endif
#if defined SMEXT_CONF_METAMOD
PLUGIN_GLOBALVARS();