Public Functions
compiledFileNeedsUpdate(UTF8 * filename)
ConsoleDocClass(LangTable , "@brief Provides the code necessary <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> handle the low level management " "of the string tables <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">localization\n\n</a>" "One <a href="/coding/class/classlangtable/">LangTable</a> is created <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> each mod, as well as one <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the C++code. " "<a href="/coding/class/classlangtable/">LangTable</a> is responsible <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> obtaining the correct strings from each " "and relaying it <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the appropriate <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">controls.\n\n</a>" " @see Localization <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> full <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">description\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Localization\n</a>" )
DefineEngineFunction(CompileLanguage , void , (const char *inputFile, bool createMap) , (false) , "@brief Compiles <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> LSO language file." " <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> createIndex is true)
DefineEngineMethod(LangTable , addLanguage , S32 , (String filename, String languageName) , ("", "") , "(string filename, [string languageName])" "@brief Adds <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> language <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "@param filename Name and path <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file\n</a>" "@param languageName Optional name <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> assign <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">entry\n\n</a>" "@return True If <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> was successfully found and language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created\n</a>" )
DefineEngineMethod(LangTable , getCurrentLanguage , S32 , () , "()" "@brief Get the ID of the current language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "@return Numerical ID of the current language table" )
DefineEngineMethod(LangTable , getLangName , const char * , (S32 langId) , "(int language)" "@brief Return the readable name of the language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "@param language Numerical ID of the language table <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">access\n\n</a>" "@return <a href="/coding/class/classstring/">String</a> containing the name of the table, <a href="/coding/file/typesx86unix_8h/#typesx86unix_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a> <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> ID was invalid or name was never specified" )
DefineEngineMethod(LangTable , getNumLang , S32 , () , "()" "@brief Used <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> find out how many languages are in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "@return Size of the vector containing the languages, numerical" )
DefineEngineMethod(LangTable , getString , const char * , (U32 id) , "(string filename)" "@brief Grabs <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> string from the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "If an invalid is passed, the function will attempt <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> grab from the default <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" " @param filename Name of the language table <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">access\n\n</a>" " @return Text from the specified language table, \"\" <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> ID was invalid and default table is not set" )
DefineEngineMethod(LangTable , setCurrentLanguage , void , (S32 langId) , "(int language)" "@brief Sets the current language table <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> grabbing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">text\n\n</a>" "@param language ID of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n</a>" )
DefineEngineMethod(LangTable , setDefaultLanguage , void , (S32 langId) , "(int language)" "@brief Sets the default language <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n\n</a>" "@param language ID of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">table\n</a>" )
getCurrentModLangTable()
getCurrentModVarName(UTF8 * buffer, U32 bufsize)
getModLangTable(const UTF8 * mod)
IMPLEMENT_CONOBJECT(LangTable )
sanitiseVarName(const UTF8 * varName, UTF8 * buffer, U32 bufsize)
1
2//-----------------------------------------------------------------------------
3// Copyright (c) 2012 GarageGames, LLC
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22//-----------------------------------------------------------------------------
23
24#include "platform/platform.h"
25#include "core/stream/stream.h"
26#include "core/stream/fileStream.h"
27#include "console/console.h"
28#include "console/consoleInternal.h"
29#include "console/ast.h"
30#include "console/compiler.h"
31#include "core/util/safeDelete.h"
32#include "console/engineAPI.h"
33
34#include "i18n/lang.h"
35
36//-----------------------------------------------------------------------------
37// LangFile Class
38//-----------------------------------------------------------------------------
39
40LangFile::LangFile(const UTF8 *langName /* = NULL */)
41{
42 VECTOR_SET_ASSOCIATION( mStringTable );
43
44 if(langName)
45 {
46 dsize_t langNameLen = dStrlen(langName) + 1;
47 mLangName = new UTF8 [langNameLen];
48 dStrcpy(mLangName, langName, langNameLen);
49 }
50 else
51 mLangName = NULL;
52
53 mLangFile = NULL;
54}
55
56LangFile::~LangFile()
57{
58 // [tom, 3/1/2005] Note: If this is freed in FreeTable() then when the file
59 // is loaded, the language name will be blitzed.
60 // Programming after 36 hours without sleep != good.
61
62 SAFE_DELETE_ARRAY(mLangName);
63 SAFE_DELETE_ARRAY(mLangFile);
64 freeTable();
65}
66
67void LangFile::freeTable()
68{
69 U32 i;
70 for(i = 0;i < mStringTable.size();i++)
71 {
72 if(mStringTable[i])
73 delete [] mStringTable[i];
74 }
75 mStringTable.clear();
76}
77
78bool LangFile::load(const UTF8 *filename)
79{
80 FileStream * stream;
81 if((stream = FileStream::createAndOpen( filename, Torque::FS::File::Read )) == NULL)
82 return false;
83
84 bool ret = load(stream);
85 delete stream;
86 return ret;
87}
88
89bool LangFile::load(Stream *s)
90{
91 freeTable();
92
93 while (s->getStatus() == Stream::Ok)
94 {
95 char buf[2048];
96 s->readLongString(2048, buf);
97 if (s->getStatus() == Stream::Ok)
98 addString((const UTF8*)buf);
99 }
100 return true;
101}
102
103bool LangFile::save(const UTF8 *filename)
104{
105 FileStream *fs;
106
107 if(!isLoaded())
108 return false;
109
110 if((fs = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL)
111 return false;
112
113 bool ret = save(fs);
114 delete fs;
115
116 return ret;
117}
118
119bool LangFile::save(Stream *s)
120{
121 if (!isLoaded())
122 return false;
123
124 U32 i;
125 for (i = 0; i < mStringTable.size(); i++)
126 {
127 s->writeLongString(2048, (char*)mStringTable[i]); //irei1as_ lang
128 }
129 return true;
130}
131
132const UTF8 * LangFile::getString(U32 id)
133{
134 if(id == LANG_INVALID_ID || id >= mStringTable.size())
135 return NULL;
136 return mStringTable[id];
137}
138
139U32 LangFile::addString(const UTF8 *str)
140{
141 dsize_t newstrLen = dStrlen(str) + 1;
142 UTF8 *newstr = new UTF8 [newstrLen];
143 dStrcpy(newstr, str, newstrLen);
144 mStringTable.push_back(newstr);
145 return mStringTable.size() - 1;
146}
147
148void LangFile::setString(U32 id, const UTF8 *str)
149{
150 if(id >= mStringTable.size())
151 {
152 U32 oldsize = mStringTable.size();
153 mStringTable.setSize(id+1);
154 for( U32 i=oldsize; i<mStringTable.size(); ++i )
155 {
156 mStringTable[i] = NULL;
157 }
158 }
159
160 SAFE_DELETE_ARRAY(mStringTable[id]);
161
162 dsize_t newstrLen = dStrlen(str) + 1;
163 UTF8 *newstr = new UTF8 [newstrLen];
164 dStrcpy(newstr, str, newstrLen);
165 mStringTable[id] = newstr;
166}
167
168void LangFile::setLangName(const UTF8 *newName)
169{
170 if(mLangName)
171 delete [] mLangName;
172
173 dsize_t langNameLen = dStrlen(newName) + 1;
174 mLangName = new UTF8 [langNameLen];
175 dStrcpy(mLangName, newName, langNameLen);
176}
177
178void LangFile::setLangFile(const UTF8 *langFile)
179{
180 if(mLangFile)
181 delete [] mLangFile;
182
183 dsize_t langFileLen = dStrlen(langFile) + 1;
184 mLangFile = new UTF8 [langFileLen];
185 dStrcpy(mLangFile, langFile, langFileLen);
186}
187
188bool LangFile::activateLanguage()
189{
190 if(isLoaded())
191 return true;
192
193 if(mLangFile)
194 {
195 return load(mLangFile);
196 }
197 return false;
198}
199
200void LangFile::deactivateLanguage()
201{
202 if(mLangFile && isLoaded())
203 freeTable();
204}
205
206//-----------------------------------------------------------------------------
207// LangTable Class
208//-----------------------------------------------------------------------------
209
210IMPLEMENT_CONOBJECT(LangTable);
211
212ConsoleDocClass( LangTable,
213 "@brief Provides the code necessary to handle the low level management "
214 "of the string tables for localization\n\n"
215
216 "One LangTable is created for each mod, as well as one for the C++ code. "
217 "LangTable is responsible for obtaining the correct strings from each "
218 "and relaying it to the appropriate controls.\n\n"
219
220 "@see Localization for a full description\n\n"
221
222 "@ingroup Localization\n"
223);
224
225LangTable::LangTable() : mDefaultLang(-1), mCurrentLang(-1)
226{
227 VECTOR_SET_ASSOCIATION( mLangTable );
228}
229
230LangTable::~LangTable()
231{
232 S32 i;
233
234 for(i = 0;i < mLangTable.size();i++)
235 {
236 if(mLangTable[i])
237 delete mLangTable[i];
238 }
239 mLangTable.clear();
240}
241
242S32 LangTable::addLanguage(LangFile *lang, const UTF8 *name /* = NULL */)
243{
244 if(name)
245 lang->setLangName(name);
246
247 mLangTable.push_back(lang);
248
249 if(mDefaultLang == -1)
250 setDefaultLanguage(mLangTable.size() - 1);
251 if(mCurrentLang == -1)
252 setCurrentLanguage(mLangTable.size() - 1);
253
254 return mLangTable.size() - 1;
255}
256
257S32 LangTable::addLanguage(const UTF8 *filename, const UTF8 *name /* = NULL */)
258{
259 LangFile * lang = new LangFile(name);
260
261 if(Torque::FS::IsFile(filename))
262 {
263 lang->setLangFile(filename);
264
265 S32 ret = addLanguage(lang);
266 if(ret >= 0)
267 return ret;
268 }
269 delete lang;
270
271 return -1;
272}
273
274const UTF8 *LangTable::getString(const U32 id) const
275{
276 const UTF8 *s = NULL;
277
278 if(mCurrentLang >= 0)
279 s = mLangTable[mCurrentLang]->getString(id);
280 if(s == NULL && mDefaultLang >= 0 && mDefaultLang != mCurrentLang)
281 s = mLangTable[mDefaultLang]->getString(id);
282
283 return s;
284}
285
286const U32 LangTable::getStringLength(const U32 id) const
287{
288 const UTF8 *s = getString(id);
289 if(s)
290 return dStrlen(s);
291
292 return 0;
293}
294
295void LangTable::setDefaultLanguage(S32 langid)
296{
297 if(langid >= 0 && langid < mLangTable.size())
298 {
299 if(mLangTable[langid]->activateLanguage())
300 {
301 if(mDefaultLang >= 0)
302 mLangTable[mDefaultLang]->deactivateLanguage();
303
304 mDefaultLang = langid;
305 }
306 }
307}
308
309void LangTable::setCurrentLanguage(S32 langid)
310{
311 if(langid >= 0 && langid < mLangTable.size())
312 {
313 if(mLangTable[langid]->activateLanguage())
314 {
315 Con::printf("Language %s [%s] activated.", mLangTable[langid]->getLangName(), mLangTable[langid]->getLangFile());
316
317 if(mCurrentLang >= 0 && mCurrentLang != mDefaultLang)
318 {
319 mLangTable[mCurrentLang]->deactivateLanguage();
320 Con::printf("Language %s [%s] deactivated.", mLangTable[mCurrentLang]->getLangName(), mLangTable[mCurrentLang]->getLangFile());
321 }
322 mCurrentLang = langid;
323 }
324 }
325}
326
327//-----------------------------------------------------------------------------
328// LangTable Console Methods
329//-----------------------------------------------------------------------------
330
331
332
333DefineEngineMethod(LangTable, addLanguage, S32, (String filename, String languageName), ("", ""),
334 "(string filename, [string languageName])"
335 "@brief Adds a language to the table\n\n"
336 "@param filename Name and path to the language file\n"
337 "@param languageName Optional name to assign to the new language entry\n\n"
338 "@return True If file was successfully found and language created\n"
339 )
340{
341 UTF8 scriptFilenameBuffer[1024];
342
343 Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), filename);
344 return object->addLanguage(scriptFilenameBuffer, (const UTF8*)languageName);
345}
346
347DefineEngineMethod(LangTable, getString, const char *, (U32 id), ,
348 "(string filename)"
349 "@brief Grabs a string from the specified table\n\n"
350 "If an invalid is passed, the function will attempt to "
351 "to grab from the default table\n\n"
352 "@param filename Name of the language table to access\n\n"
353 "@return Text from the specified language table, \"\" if ID was invalid and default table is not set")
354{
355 const char * str = (const char*)object->getString(id);
356 if(str != NULL)
357 {
358 dsize_t retLen = dStrlen(str) + 1;
359 char * ret = Con::getReturnBuffer(retLen);
360 dStrcpy(ret, str, retLen);
361 return ret;
362 }
363
364 return "";
365}
366
367DefineEngineMethod(LangTable, setDefaultLanguage, void, (S32 langId), , "(int language)"
368 "@brief Sets the default language table\n\n"
369 "@param language ID of the table\n")
370{
371 object->setDefaultLanguage(langId);
372}
373
374DefineEngineMethod(LangTable, setCurrentLanguage, void, (S32 langId), ,
375 "(int language)"
376 "@brief Sets the current language table for grabbing text\n\n"
377 "@param language ID of the table\n")
378{
379 object->setCurrentLanguage(langId);
380}
381
382DefineEngineMethod(LangTable, getCurrentLanguage, S32, (), , "()"
383 "@brief Get the ID of the current language table\n\n"
384 "@return Numerical ID of the current language table")
385{
386 return object->getCurrentLanguage();
387}
388
389DefineEngineMethod(LangTable, getLangName, const char *, (S32 langId), , "(int language)"
390 "@brief Return the readable name of the language table\n\n"
391 "@param language Numerical ID of the language table to access\n\n"
392 "@return String containing the name of the table, NULL if ID was invalid or name was never specified")
393{
394 const char * str = (const char*)object->getLangName(langId);
395 if(str != NULL)
396 {
397 dsize_t retLen = dStrlen(str) + 1;
398 char * ret = Con::getReturnBuffer(retLen);
399 dStrcpy(ret, str, retLen);
400 return ret;
401 }
402
403 return "";
404}
405
406DefineEngineMethod(LangTable, getNumLang, S32, (), , "()"
407 "@brief Used to find out how many languages are in the table\n\n"
408 "@return Size of the vector containing the languages, numerical")
409{
410 return object->getNumLang();
411}
412
413//-----------------------------------------------------------------------------
414// Support Functions
415//-----------------------------------------------------------------------------
416
417UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize)
418{
419 if(! varName || bufsize < 10) // [tom, 3/3/2005] bufsize check gives room to be lazy below
420 {
421 *buffer = 0;
422 return NULL;
423 }
424
425 dStrcpy(buffer, (const UTF8*)"I18N::", bufsize);
426
427 UTF8 *dptr = buffer + 6;
428 const UTF8 *sptr = varName;
429 while(*sptr)
430 {
431 if(dIsalnum(*sptr))
432 *dptr++ = *sptr++;
433 else
434 {
435 if(*(dptr - 1) != '_')
436 *dptr++ = '_';
437 sptr++;
438 }
439
440 if((dptr - buffer) >= (bufsize - 1))
441 break;
442 }
443 *dptr = 0;
444
445 return buffer;
446}
447
448UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize)
449{
450 char varName[256];
451 StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName();
452
453 const UTF8 *slash = (const UTF8*)dStrchr(cbName, '/');
454 if (slash == NULL)
455 {
456 Con::errorf("Illegal CodeBlock path detected in sanitiseVarName() (no mod directory): %s", cbName);
457 return NULL;
458 }
459
460 dStrncpy(varName, cbName, slash - (const UTF8*)cbName);
461 varName[slash - (const UTF8*)cbName] = 0;
462
463 return sanitiseVarName((UTF8*)varName, buffer, bufsize);
464}
465
466const LangTable *getCurrentModLangTable()
467{
468 UTF8 saneVarName[256];
469
470 if(getCurrentModVarName(saneVarName, sizeof(saneVarName)))
471 {
472 const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
473 return lt;
474 }
475 return NULL;
476}
477
478const LangTable *getModLangTable(const UTF8 *mod)
479{
480 UTF8 saneVarName[256];
481
482 if(sanitiseVarName(mod, saneVarName, sizeof(saneVarName)))
483 {
484 const LangTable *lt = dynamic_cast<LangTable *>(Sim::findObject(Con::getIntVariable((const char*)saneVarName)));
485 return lt;
486 }
487 return NULL;
488}
489
490//lang_ localization
491bool compiledFileNeedsUpdate(UTF8* filename)
492{
493 Torque::Path filePath = Torque::Path(filename);
494 Torque::FS::FileNodeRef sourceFile = Torque::FS::GetFileNode(filePath);
495 Torque::Path compiledPath = Torque::Path(filePath);
496 compiledPath.setExtension("lso");
497 Torque::FS::FileNodeRef compiledFile = Torque::FS::GetFileNode(compiledPath);
498
499 Torque::Time sourceModifiedTime, compiledModifiedTime;
500
501 if (sourceFile != NULL)
502 sourceModifiedTime = sourceFile->getModifiedTime();
503
504 if (compiledFile != NULL)
505 compiledModifiedTime = compiledFile->getModifiedTime();
506
507 if (sourceModifiedTime > compiledModifiedTime)
508 return true;
509 return false;
510}
511
512DefineEngineFunction(CompileLanguage, void, (const char* inputFile, bool createMap), (false),
513 "@brief Compiles a LSO language file."
514 " if createIndex is true, will also create languageMap." TORQUE_SCRIPT_EXTENSION " with"
515 " the global variables for each string index."
516 " The input file must follow this example layout:"
517 " TXT_HELLO_WORLD = Hello world in english!")
518{
519 UTF8 scriptFilenameBuffer[1024];
520 Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), inputFile);
521
522 if (!Torque::FS::IsFile(scriptFilenameBuffer))
523 {
524 Con::errorf("CompileLanguage - file %s not found", scriptFilenameBuffer);
525 return;
526 }
527
528 FileObject file;
529 if (!file.readMemory(scriptFilenameBuffer))
530 {
531 Con::errorf("CompileLanguage - couldn't read file %s", scriptFilenameBuffer);
532 return;
533 }
534
535 if (compiledFileNeedsUpdate(scriptFilenameBuffer))
536 {
537 FileStream *mapStream = NULL;
538 if (createMap)
539 {
540 Torque::Path mapPath = scriptFilenameBuffer;
541 mapPath.setFileName("languageMap");
542 mapPath.setExtension(TORQUE_SCRIPT_EXTENSION);
543 if ((mapStream = FileStream::createAndOpen(mapPath, Torque::FS::File::Write)) == NULL)
544 Con::errorf("CompileLanguage - failed creating languageMap." TORQUE_SCRIPT_EXTENSION);
545 }
546
547 LangFile langFile;
548 const U8* inLine = NULL;
549 const char* separatorStr = " = ";
550 S32 stringId = 0;
551 while ((inLine = file.readLine())[0] != 0)
552 {
553 char* line;
554 chompUTF8BOM((const char *)inLine, &line);
555 char* div = dStrstr(line, separatorStr);
556 if (div == NULL)
557 {
558 Con::errorf("Separator %s not found in line: %s", separatorStr, line);
559 Con::errorf("Could not determine string name ID");
560 continue;
561 }
562 *div = 0;
563 char* text = div + dStrlen(separatorStr);
564
565 langFile.addString((const UTF8*)text);
566
567 if (mapStream)
568 {
569 String mapLine = String::ToString("$%s = %i;", line, stringId);
570 mapStream->writeLine((const U8*)mapLine.c_str());
571 String commentLine = String::ToString("// %s", text);
572 mapStream->writeLine((const U8*)commentLine.c_str());
573 }
574
575 stringId++;
576 }
577
578 Torque::Path lsoPath = scriptFilenameBuffer;
579 lsoPath.setExtension("lso");
580 langFile.save(lsoPath.getFullPath());
581
582 if (mapStream)
583 delete mapStream;
584 }
585}
586//end lang_ localization
587