//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "platform/platform.h" #include "core/stream.h" #include "core/fileStream.h" #include "core/resManager.h" #include "console/console.h" #include "console/consoleInternal.h" #include "console/ast.h" #include "console/compiler.h" #include "util/safeDelete.h" #include "i18n/lang.h" ////////////////////////////////////////////////////////////////////////// // LangFile Class ////////////////////////////////////////////////////////////////////////// LangFile::LangFile(const UTF8 *langName /* = NULL */) { if(langName) { mLangName = new UTF8 [dStrlen(langName) + 1]; dStrcpy(mLangName, langName); } else mLangName = NULL; mLangFile = NULL; } LangFile::~LangFile() { // [tom, 3/1/2005] Note: If this is freed in FreeTable() then when the file // is loaded, the language name will be blitzed. // Programming after 36 hours without sleep != good. SAFE_DELETE(mLangName); SAFE_DELETE(mLangFile); freeTable(); } void LangFile::freeTable() { U32 i; for(i = 0;i < mStringTable.size();i++) { if(mStringTable[i]) delete [] mStringTable[i]; } mStringTable.clear(); } bool LangFile::load(const UTF8 *filename) { Stream *pS; bool bRet = false; if(pS = ResourceManager->openStream((const char*)filename)) { bRet = load(pS); ResourceManager->closeStream(pS); } return bRet; } bool LangFile::load(Stream *s) { freeTable(); while(s->getStatus() != Stream::EOS) { char buf[256]; s->readString(buf); addString((const UTF8*)buf); } return true; } bool LangFile::save(const UTF8 *filename) { FileStream fs; bool bRet = false; if(!isLoaded()) return false; if(ResourceManager->openFileForWrite(fs, (const char*)filename)) { bRet = save(&fs); fs.close(); } return bRet; } bool LangFile::save(Stream *s) { if(!isLoaded()) return false; U32 i; for(i = 0;i < mStringTable.size();i++) { s->writeString((char*)mStringTable[i]); } return true; } const UTF8 * LangFile::getString(U32 id) { if(id == LANG_INVALID_ID || id >= mStringTable.size()) return NULL; return mStringTable[id]; } U32 LangFile::addString(const UTF8 *str) { UTF8 *newstr = new UTF8 [dStrlen(str) + 1]; dStrcpy(newstr, str); mStringTable.push_back(newstr); return mStringTable.size() - 1; } void LangFile::setString(U32 id, const UTF8 *str) { if(id >= mStringTable.size()) mStringTable.setSize(id+1); UTF8 *newstr = new UTF8 [dStrlen(str) + 1]; dStrcpy(newstr, str); mStringTable[id] = newstr; } void LangFile::setLangName(const UTF8 *newName) { if(mLangName) delete [] mLangName; mLangName = new UTF8 [dStrlen(newName) + 1]; dStrcpy(mLangName, newName); } void LangFile::setLangFile(const UTF8 *langFile) { if(mLangFile) delete [] mLangFile; mLangFile = new UTF8 [dStrlen(langFile) + 1]; dStrcpy(mLangFile, langFile); } bool LangFile::activateLanguage() { if(isLoaded()) return true; if(mLangFile) { return load(mLangFile); } return false; } void LangFile::deactivateLanguage() { if(mLangFile && isLoaded()) freeTable(); } ////////////////////////////////////////////////////////////////////////// // LangTable Class ////////////////////////////////////////////////////////////////////////// IMPLEMENT_CONOBJECT(LangTable); LangTable::LangTable() : mDefaultLang(-1), mCurrentLang(-1) { } LangTable::~LangTable() { S32 i; for(i = 0;i < mLangTable.size();i++) { if(mLangTable[i]) delete mLangTable[i]; } mLangTable.clear(); } S32 LangTable::addLanguage(LangFile *lang, const UTF8 *name /* = NULL */) { if(name) lang->setLangName(name); mLangTable.push_back(lang); if(mDefaultLang == -1) setDefaultLanguage(mLangTable.size() - 1); if(mCurrentLang == -1) setCurrentLanguage(mLangTable.size() - 1); return mLangTable.size() - 1; } S32 LangTable::addLanguage(const UTF8 *filename, const UTF8 *name /* = NULL */) { LangFile *pLang; S32 ret = -1; if(pLang = new LangFile(name)) { if(bool(ResourceManager->find((const char*)filename))) { pLang->setLangFile(filename); if((ret = addLanguage(pLang)) >= 0) return ret; } delete pLang; } return -1; } const UTF8 *LangTable::getString(const U32 id) const { const UTF8 *s = NULL; if(mCurrentLang >= 0) s = mLangTable[mCurrentLang]->getString(id); if(s == NULL && mDefaultLang >= 0 && mDefaultLang != mCurrentLang) s = mLangTable[mDefaultLang]->getString(id); return s; } const U32 LangTable::getStringLength(const U32 id) const { const UTF8 *s = getString(id); if(s) return dStrlen(s); return 0; } void LangTable::setDefaultLanguage(S32 langid) { if(langid >= 0 && langid < mLangTable.size()) { if(mLangTable[langid]->activateLanguage()) { if(mDefaultLang >= 0) mLangTable[mDefaultLang]->deactivateLanguage(); mDefaultLang = langid; } } } void LangTable::setCurrentLanguage(S32 langid) { if(langid >= 0 && langid < mLangTable.size()) { if(mLangTable[langid]->activateLanguage()) { Con::printf("Language %s [%s] activated.", mLangTable[langid]->getLangName(), mLangTable[langid]->getLangFile()); if(mCurrentLang >= 0 && mCurrentLang != mDefaultLang) { mLangTable[mCurrentLang]->deactivateLanguage(); Con::printf("Language %s [%s] deactivated.", mLangTable[mCurrentLang]->getLangName(), mLangTable[mCurrentLang]->getLangFile()); } mCurrentLang = langid; } } } ////////////////////////////////////////////////////////////////////////// // LangTable Console Methods ////////////////////////////////////////////////////////////////////////// ConsoleMethod(LangTable, addLanguage, S32, 3, 4, "(string filename, [string languageName])") { UTF8 scriptFilenameBuffer[1024]; Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), argv[2]); return object->addLanguage(scriptFilenameBuffer, argc == 4 ? (const UTF8*)argv[3] : NULL); } ConsoleMethod(LangTable, getString, const char *, 3, 3, "(string filename)") { char *ret; const char *str; if(str = (const char*)object->getString(dAtoi(argv[2]))) { ret = Con::getReturnBuffer(dStrlen(str) + 1); dStrcpy(ret, str); return ret; } return ""; } ConsoleMethod(LangTable, setDefaultLanguage, void, 3, 3, "(int language)") { object->setDefaultLanguage(dAtoi(argv[2])); } ConsoleMethod(LangTable, setCurrentLanguage, void, 3, 3, "(int language)") { object->setCurrentLanguage(dAtoi(argv[2])); } ConsoleMethod(LangTable, getCurrentLanguage, S32, 2, 2, "()") { return object->getCurrentLanguage(); } ConsoleMethod(LangTable, getLangName, const char *, 3, 3, "(int language)") { char *ret; const char *str; if(str = (const char*)object->getLangName(dAtoi(argv[2]))) { ret = Con::getReturnBuffer(dStrlen(str) + 1); dStrcpy(ret, str); return ret; } return ""; } ConsoleMethod(LangTable, getNumLang, S32, 2, 2, "()") { return object->getNumLang(); } ////////////////////////////////////////////////////////////////////////// // Support Functions ////////////////////////////////////////////////////////////////////////// UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize) { if(! varName || bufsize < 10) // [tom, 3/3/2005] bufsize check gives room to be lazy below { *buffer = 0; return NULL; } dStrcpy(buffer, (const UTF8*)"I18N::"); UTF8 *dptr = buffer + 6; const UTF8 *sptr = varName; while(*sptr) { if(dIsalnum(*sptr)) *dptr++ = *sptr++; else { if(*(dptr - 1) != '_') *dptr++ = '_'; sptr++; } if((dptr - buffer) >= (bufsize - 1)) break; } *dptr = 0; return buffer; } UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize) { char varName[256]; StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName(); const UTF8 *slash = (const UTF8*)dStrchr(cbName, '/'); if (slash == NULL) { Con::errorf("Illegal CodeBlock path detected in sanitiseVarName() (no mod directory): %s", cbName); return NULL; } dStrncpy(varName, cbName, slash - (const UTF8*)cbName); varName[slash - (const UTF8*)cbName] = 0; return sanitiseVarName((UTF8*)varName, buffer, bufsize); } const LangTable *getCurrentModLangTable() { UTF8 saneVarName[256]; if(getCurrentModVarName(saneVarName, sizeof(saneVarName))) { const LangTable *lt = dynamic_cast(Sim::findObject(Con::getIntVariable((const char*)saneVarName))); return lt; } return NULL; } const LangTable *getModLangTable(const UTF8 *mod) { UTF8 saneVarName[256]; if(sanitiseVarName(mod, saneVarName, sizeof(saneVarName))) { const LangTable *lt = dynamic_cast(Sim::findObject(Con::getIntVariable((const char*)saneVarName))); return lt; } return NULL; }