lang.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "core/stream/stream.h"
  24. #include "core/stream/fileStream.h"
  25. #include "console/console.h"
  26. #include "console/consoleInternal.h"
  27. #include "console/ast.h"
  28. #include "console/compiler.h"
  29. #include "core/util/safeDelete.h"
  30. #include "console/engineAPI.h"
  31. #include "i18n/lang.h"
  32. //-----------------------------------------------------------------------------
  33. // LangFile Class
  34. //-----------------------------------------------------------------------------
  35. LangFile::LangFile(const UTF8 *langName /* = NULL */)
  36. {
  37. VECTOR_SET_ASSOCIATION( mStringTable );
  38. if(langName)
  39. {
  40. mLangName = new UTF8 [dStrlen(langName) + 1];
  41. dStrcpy(mLangName, langName);
  42. }
  43. else
  44. mLangName = NULL;
  45. mLangFile = NULL;
  46. }
  47. LangFile::~LangFile()
  48. {
  49. // [tom, 3/1/2005] Note: If this is freed in FreeTable() then when the file
  50. // is loaded, the language name will be blitzed.
  51. // Programming after 36 hours without sleep != good.
  52. SAFE_DELETE_ARRAY(mLangName);
  53. SAFE_DELETE_ARRAY(mLangFile);
  54. freeTable();
  55. }
  56. void LangFile::freeTable()
  57. {
  58. U32 i;
  59. for(i = 0;i < mStringTable.size();i++)
  60. {
  61. if(mStringTable[i])
  62. delete [] mStringTable[i];
  63. }
  64. mStringTable.clear();
  65. }
  66. bool LangFile::load(const UTF8 *filename)
  67. {
  68. FileStream * stream;
  69. if((stream = FileStream::createAndOpen( filename, Torque::FS::File::Read )) == NULL)
  70. return false;
  71. bool ret = load(stream);
  72. delete stream;
  73. return ret;
  74. }
  75. bool LangFile::load(Stream *s)
  76. {
  77. freeTable();
  78. while(s->getStatus() != Stream::EOS)
  79. {
  80. char buf[256];
  81. s->readString(buf);
  82. addString((const UTF8*)buf);
  83. }
  84. return true;
  85. }
  86. bool LangFile::save(const UTF8 *filename)
  87. {
  88. FileStream *fs;
  89. if(!isLoaded())
  90. return false;
  91. if((fs = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL)
  92. return false;
  93. bool ret = save(fs);
  94. delete fs;
  95. return ret;
  96. }
  97. bool LangFile::save(Stream *s)
  98. {
  99. if(!isLoaded())
  100. return false;
  101. U32 i;
  102. for(i = 0;i < mStringTable.size();i++)
  103. {
  104. s->writeString((char*)mStringTable[i]);
  105. }
  106. return true;
  107. }
  108. const UTF8 * LangFile::getString(U32 id)
  109. {
  110. if(id == LANG_INVALID_ID || id >= mStringTable.size())
  111. return NULL;
  112. return mStringTable[id];
  113. }
  114. U32 LangFile::addString(const UTF8 *str)
  115. {
  116. UTF8 *newstr = new UTF8 [dStrlen(str) + 1];
  117. dStrcpy(newstr, str);
  118. mStringTable.push_back(newstr);
  119. return mStringTable.size() - 1;
  120. }
  121. void LangFile::setString(U32 id, const UTF8 *str)
  122. {
  123. if(id >= mStringTable.size())
  124. {
  125. U32 oldsize = mStringTable.size();
  126. mStringTable.setSize(id+1);
  127. for( U32 i=oldsize; i<mStringTable.size(); ++i )
  128. {
  129. mStringTable[i] = NULL;
  130. }
  131. }
  132. SAFE_DELETE_ARRAY(mStringTable[id]);
  133. UTF8 *newstr = new UTF8 [dStrlen(str) + 1];
  134. dStrcpy(newstr, str);
  135. mStringTable[id] = newstr;
  136. }
  137. void LangFile::setLangName(const UTF8 *newName)
  138. {
  139. if(mLangName)
  140. delete [] mLangName;
  141. mLangName = new UTF8 [dStrlen(newName) + 1];
  142. dStrcpy(mLangName, newName);
  143. }
  144. void LangFile::setLangFile(const UTF8 *langFile)
  145. {
  146. if(mLangFile)
  147. delete [] mLangFile;
  148. mLangFile = new UTF8 [dStrlen(langFile) + 1];
  149. dStrcpy(mLangFile, langFile);
  150. }
  151. bool LangFile::activateLanguage()
  152. {
  153. if(isLoaded())
  154. return true;
  155. if(mLangFile)
  156. {
  157. return load(mLangFile);
  158. }
  159. return false;
  160. }
  161. void LangFile::deactivateLanguage()
  162. {
  163. if(mLangFile && isLoaded())
  164. freeTable();
  165. }
  166. //-----------------------------------------------------------------------------
  167. // LangTable Class
  168. //-----------------------------------------------------------------------------
  169. IMPLEMENT_CONOBJECT(LangTable);
  170. ConsoleDocClass( LangTable,
  171. "@brief Provides the code necessary to handle the low level management "
  172. "of the string tables for localization\n\n"
  173. "One LangTable is created for each mod, as well as one for the C++ code. "
  174. "LangTable is responsible for obtaining the correct strings from each "
  175. "and relaying it to the appropriate controls.\n\n"
  176. "@see Localization for a full description\n\n"
  177. "@ingroup Localization\n"
  178. );
  179. LangTable::LangTable() : mDefaultLang(-1), mCurrentLang(-1)
  180. {
  181. VECTOR_SET_ASSOCIATION( mLangTable );
  182. }
  183. LangTable::~LangTable()
  184. {
  185. S32 i;
  186. for(i = 0;i < mLangTable.size();i++)
  187. {
  188. if(mLangTable[i])
  189. delete mLangTable[i];
  190. }
  191. mLangTable.clear();
  192. }
  193. S32 LangTable::addLanguage(LangFile *lang, const UTF8 *name /* = NULL */)
  194. {
  195. if(name)
  196. lang->setLangName(name);
  197. mLangTable.push_back(lang);
  198. if(mDefaultLang == -1)
  199. setDefaultLanguage(mLangTable.size() - 1);
  200. if(mCurrentLang == -1)
  201. setCurrentLanguage(mLangTable.size() - 1);
  202. return mLangTable.size() - 1;
  203. }
  204. S32 LangTable::addLanguage(const UTF8 *filename, const UTF8 *name /* = NULL */)
  205. {
  206. LangFile * lang = new LangFile(name);
  207. if(Torque::FS::IsFile(filename))
  208. {
  209. lang->setLangFile(filename);
  210. S32 ret = addLanguage(lang);
  211. if(ret >= 0)
  212. return ret;
  213. }
  214. delete lang;
  215. return -1;
  216. }
  217. const UTF8 *LangTable::getString(const U32 id) const
  218. {
  219. const UTF8 *s = NULL;
  220. if(mCurrentLang >= 0)
  221. s = mLangTable[mCurrentLang]->getString(id);
  222. if(s == NULL && mDefaultLang >= 0 && mDefaultLang != mCurrentLang)
  223. s = mLangTable[mDefaultLang]->getString(id);
  224. return s;
  225. }
  226. const U32 LangTable::getStringLength(const U32 id) const
  227. {
  228. const UTF8 *s = getString(id);
  229. if(s)
  230. return dStrlen(s);
  231. return 0;
  232. }
  233. void LangTable::setDefaultLanguage(S32 langid)
  234. {
  235. if(langid >= 0 && langid < mLangTable.size())
  236. {
  237. if(mLangTable[langid]->activateLanguage())
  238. {
  239. if(mDefaultLang >= 0)
  240. mLangTable[mDefaultLang]->deactivateLanguage();
  241. mDefaultLang = langid;
  242. }
  243. }
  244. }
  245. void LangTable::setCurrentLanguage(S32 langid)
  246. {
  247. if(langid >= 0 && langid < mLangTable.size())
  248. {
  249. if(mLangTable[langid]->activateLanguage())
  250. {
  251. Con::printf("Language %s [%s] activated.", mLangTable[langid]->getLangName(), mLangTable[langid]->getLangFile());
  252. if(mCurrentLang >= 0 && mCurrentLang != mDefaultLang)
  253. {
  254. mLangTable[mCurrentLang]->deactivateLanguage();
  255. Con::printf("Language %s [%s] deactivated.", mLangTable[mCurrentLang]->getLangName(), mLangTable[mCurrentLang]->getLangFile());
  256. }
  257. mCurrentLang = langid;
  258. }
  259. }
  260. }
  261. //-----------------------------------------------------------------------------
  262. // LangTable Console Methods
  263. //-----------------------------------------------------------------------------
  264. DefineConsoleMethod(LangTable, addLanguage, S32, (String filename, String languageName), ("", ""),
  265. "(string filename, [string languageName])"
  266. "@brief Adds a language to the table\n\n"
  267. "@param filename Name and path to the language file\n"
  268. "@param languageName Optional name to assign to the new language entry\n\n"
  269. "@return True If file was successfully found and language created\n"
  270. )
  271. {
  272. UTF8 scriptFilenameBuffer[1024];
  273. Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), filename);
  274. return object->addLanguage(scriptFilenameBuffer, (const UTF8*)languageName);
  275. }
  276. DefineConsoleMethod(LangTable, getString, const char *, (U32 id), ,
  277. "(string filename)"
  278. "@brief Grabs a string from the specified table\n\n"
  279. "If an invalid is passed, the function will attempt to "
  280. "to grab from the default table\n\n"
  281. "@param filename Name of the language table to access\n\n"
  282. "@return Text from the specified language table, \"\" if ID was invalid and default table is not set")
  283. {
  284. const char * str = (const char*)object->getString(id);
  285. if(str != NULL)
  286. {
  287. char * ret = Con::getReturnBuffer(dStrlen(str) + 1);
  288. dStrcpy(ret, str);
  289. return ret;
  290. }
  291. return "";
  292. }
  293. DefineConsoleMethod(LangTable, setDefaultLanguage, void, (S32 langId), , "(int language)"
  294. "@brief Sets the default language table\n\n"
  295. "@param language ID of the table\n")
  296. {
  297. object->setDefaultLanguage(langId);
  298. }
  299. DefineConsoleMethod(LangTable, setCurrentLanguage, void, (S32 langId), ,
  300. "(int language)"
  301. "@brief Sets the current language table for grabbing text\n\n"
  302. "@param language ID of the table\n")
  303. {
  304. object->setCurrentLanguage(langId);
  305. }
  306. DefineConsoleMethod(LangTable, getCurrentLanguage, S32, (), , "()"
  307. "@brief Get the ID of the current language table\n\n"
  308. "@return Numerical ID of the current language table")
  309. {
  310. return object->getCurrentLanguage();
  311. }
  312. DefineConsoleMethod(LangTable, getLangName, const char *, (S32 langId), , "(int language)"
  313. "@brief Return the readable name of the language table\n\n"
  314. "@param language Numerical ID of the language table to access\n\n"
  315. "@return String containing the name of the table, NULL if ID was invalid or name was never specified")
  316. {
  317. const char * str = (const char*)object->getLangName(langId);
  318. if(str != NULL)
  319. {
  320. char * ret = Con::getReturnBuffer(dStrlen(str) + 1);
  321. dStrcpy(ret, str);
  322. return ret;
  323. }
  324. return "";
  325. }
  326. DefineConsoleMethod(LangTable, getNumLang, S32, (), , "()"
  327. "@brief Used to find out how many languages are in the table\n\n"
  328. "@return Size of the vector containing the languages, numerical")
  329. {
  330. return object->getNumLang();
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Support Functions
  334. //-----------------------------------------------------------------------------
  335. UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize)
  336. {
  337. if(! varName || bufsize < 10) // [tom, 3/3/2005] bufsize check gives room to be lazy below
  338. {
  339. *buffer = 0;
  340. return NULL;
  341. }
  342. dStrcpy(buffer, (const UTF8*)"I18N::");
  343. UTF8 *dptr = buffer + 6;
  344. const UTF8 *sptr = varName;
  345. while(*sptr)
  346. {
  347. if(dIsalnum(*sptr))
  348. *dptr++ = *sptr++;
  349. else
  350. {
  351. if(*(dptr - 1) != '_')
  352. *dptr++ = '_';
  353. sptr++;
  354. }
  355. if((dptr - buffer) >= (bufsize - 1))
  356. break;
  357. }
  358. *dptr = 0;
  359. return buffer;
  360. }
  361. UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize)
  362. {
  363. char varName[256];
  364. StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName();
  365. const UTF8 *slash = (const UTF8*)dStrchr(cbName, '/');
  366. if (slash == NULL)
  367. {
  368. Con::errorf("Illegal CodeBlock path detected in sanitiseVarName() (no mod directory): %s", cbName);
  369. return NULL;
  370. }
  371. dStrncpy(varName, cbName, slash - (const UTF8*)cbName);
  372. varName[slash - (const UTF8*)cbName] = 0;
  373. return sanitiseVarName((UTF8*)varName, buffer, bufsize);
  374. }
  375. const LangTable *getCurrentModLangTable()
  376. {
  377. UTF8 saneVarName[256];
  378. if(getCurrentModVarName(saneVarName, sizeof(saneVarName)))
  379. {
  380. const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
  381. return lt;
  382. }
  383. return NULL;
  384. }
  385. const LangTable *getModLangTable(const UTF8 *mod)
  386. {
  387. UTF8 saneVarName[256];
  388. if(sanitiseVarName(mod, saneVarName, sizeof(saneVarName)))
  389. {
  390. const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
  391. return lt;
  392. }
  393. return NULL;
  394. }