lang.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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(lang != NULL)
  208. {
  209. if(Torque::FS::IsFile(filename))
  210. {
  211. lang->setLangFile(filename);
  212. S32 ret = addLanguage(lang);
  213. if(ret >= 0)
  214. return ret;
  215. }
  216. delete lang;
  217. }
  218. return -1;
  219. }
  220. const UTF8 *LangTable::getString(const U32 id) const
  221. {
  222. const UTF8 *s = NULL;
  223. if(mCurrentLang >= 0)
  224. s = mLangTable[mCurrentLang]->getString(id);
  225. if(s == NULL && mDefaultLang >= 0 && mDefaultLang != mCurrentLang)
  226. s = mLangTable[mDefaultLang]->getString(id);
  227. return s;
  228. }
  229. const U32 LangTable::getStringLength(const U32 id) const
  230. {
  231. const UTF8 *s = getString(id);
  232. if(s)
  233. return dStrlen(s);
  234. return 0;
  235. }
  236. void LangTable::setDefaultLanguage(S32 langid)
  237. {
  238. if(langid >= 0 && langid < mLangTable.size())
  239. {
  240. if(mLangTable[langid]->activateLanguage())
  241. {
  242. if(mDefaultLang >= 0)
  243. mLangTable[mDefaultLang]->deactivateLanguage();
  244. mDefaultLang = langid;
  245. }
  246. }
  247. }
  248. void LangTable::setCurrentLanguage(S32 langid)
  249. {
  250. if(langid >= 0 && langid < mLangTable.size())
  251. {
  252. if(mLangTable[langid]->activateLanguage())
  253. {
  254. Con::printf("Language %s [%s] activated.", mLangTable[langid]->getLangName(), mLangTable[langid]->getLangFile());
  255. if(mCurrentLang >= 0 && mCurrentLang != mDefaultLang)
  256. {
  257. mLangTable[mCurrentLang]->deactivateLanguage();
  258. Con::printf("Language %s [%s] deactivated.", mLangTable[mCurrentLang]->getLangName(), mLangTable[mCurrentLang]->getLangFile());
  259. }
  260. mCurrentLang = langid;
  261. }
  262. }
  263. }
  264. //-----------------------------------------------------------------------------
  265. // LangTable Console Methods
  266. //-----------------------------------------------------------------------------
  267. ConsoleMethod(LangTable, addLanguage, S32, 3, 4,
  268. "(string filename, [string languageName])"
  269. "@brief Adds a language to the table\n\n"
  270. "@param filename Name and path to the language file\n"
  271. "@param languageName Optional name to assign to the new language entry\n\n"
  272. "@return True If file was successfully found and language created\n"
  273. )
  274. {
  275. UTF8 scriptFilenameBuffer[1024];
  276. Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), argv[2]);
  277. return object->addLanguage(scriptFilenameBuffer, argc == 4 ? (const UTF8*)argv[3] : NULL);
  278. }
  279. ConsoleMethod(LangTable, getString, const char *, 3, 3,
  280. "(string filename)"
  281. "@brief Grabs a string from the specified table\n\n"
  282. "If an invalid is passed, the function will attempt to "
  283. "to grab from the default table\n\n"
  284. "@param filename Name of the language table to access\n\n"
  285. "@return Text from the specified language table, \"\" if ID was invalid and default table is not set")
  286. {
  287. const char * str = (const char*)object->getString(dAtoi(argv[2]));
  288. if(str != NULL)
  289. {
  290. char * ret = Con::getReturnBuffer(dStrlen(str) + 1);
  291. dStrcpy(ret, str);
  292. return ret;
  293. }
  294. return "";
  295. }
  296. ConsoleMethod(LangTable, setDefaultLanguage, void, 3, 3, "(int language)"
  297. "@brief Sets the default language table\n\n"
  298. "@param language ID of the table\n")
  299. {
  300. object->setDefaultLanguage(dAtoi(argv[2]));
  301. }
  302. ConsoleMethod(LangTable, setCurrentLanguage, void, 3, 3,
  303. "(int language)"
  304. "@brief Sets the current language table for grabbing text\n\n"
  305. "@param language ID of the table\n")
  306. {
  307. object->setCurrentLanguage(dAtoi(argv[2]));
  308. }
  309. ConsoleMethod(LangTable, getCurrentLanguage, S32, 2, 2, "()"
  310. "@brief Get the ID of the current language table\n\n"
  311. "@return Numerical ID of the current language table")
  312. {
  313. return object->getCurrentLanguage();
  314. }
  315. ConsoleMethod(LangTable, getLangName, const char *, 3, 3, "(int language)"
  316. "@brief Return the readable name of the language table\n\n"
  317. "@param language Numerical ID of the language table to access\n\n"
  318. "@return String containing the name of the table, NULL if ID was invalid or name was never specified")
  319. {
  320. const char * str = (const char*)object->getLangName(dAtoi(argv[2]));
  321. if(str != NULL)
  322. {
  323. char * ret = Con::getReturnBuffer(dStrlen(str) + 1);
  324. dStrcpy(ret, str);
  325. return ret;
  326. }
  327. return "";
  328. }
  329. ConsoleMethod(LangTable, getNumLang, S32, 2, 2, "()"
  330. "@brief Used to find out how many languages are in the table\n\n"
  331. "@return Size of the vector containing the languages, numerical")
  332. {
  333. return object->getNumLang();
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Support Functions
  337. //-----------------------------------------------------------------------------
  338. UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize)
  339. {
  340. if(! varName || bufsize < 10) // [tom, 3/3/2005] bufsize check gives room to be lazy below
  341. {
  342. *buffer = 0;
  343. return NULL;
  344. }
  345. dStrcpy(buffer, (const UTF8*)"I18N::");
  346. UTF8 *dptr = buffer + 6;
  347. const UTF8 *sptr = varName;
  348. while(*sptr)
  349. {
  350. if(dIsalnum(*sptr))
  351. *dptr++ = *sptr++;
  352. else
  353. {
  354. if(*(dptr - 1) != '_')
  355. *dptr++ = '_';
  356. sptr++;
  357. }
  358. if((dptr - buffer) >= (bufsize - 1))
  359. break;
  360. }
  361. *dptr = 0;
  362. return buffer;
  363. }
  364. UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize)
  365. {
  366. char varName[256];
  367. StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName();
  368. const UTF8 *slash = (const UTF8*)dStrchr(cbName, '/');
  369. if (slash == NULL)
  370. {
  371. Con::errorf("Illegal CodeBlock path detected in sanitiseVarName() (no mod directory): %s", cbName);
  372. return NULL;
  373. }
  374. dStrncpy(varName, cbName, slash - (const UTF8*)cbName);
  375. varName[slash - (const UTF8*)cbName] = 0;
  376. return sanitiseVarName((UTF8*)varName, buffer, bufsize);
  377. }
  378. const LangTable *getCurrentModLangTable()
  379. {
  380. UTF8 saneVarName[256];
  381. if(getCurrentModVarName(saneVarName, sizeof(saneVarName)))
  382. {
  383. const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
  384. return lt;
  385. }
  386. return NULL;
  387. }
  388. const LangTable *getModLangTable(const UTF8 *mod)
  389. {
  390. UTF8 saneVarName[256];
  391. if(sanitiseVarName(mod, saneVarName, sizeof(saneVarName)))
  392. {
  393. const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
  394. return lt;
  395. }
  396. return NULL;
  397. }