// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "nel/misc/types_nl.h" #include "nel/misc/config_file.h" #include "nel/misc/sheet_id.h" #include "nel/misc/path.h" #include "nel/misc/diff_tool.h" #include "nel/georges/u_form.h" #include "nel/georges/u_form_elm.h" #include "nel/georges/load_form.h" #include "nel/ligo/ligo_config.h" #include "nel/ligo/primitive.h" #include "nel/ligo/primitive_utils.h" using namespace std; using namespace NLMISC; using namespace NLLIGO; using namespace STRING_MANAGER; vector<string> Filters; static CLigoConfig LigoConfig; static bool RemoveOlds = false; struct TCreatureInfo { CSheetId SheetId; bool ForceSheetName; bool DisplayName; void readGeorges (const NLMISC::CSmartPtr<NLGEORGES::UForm> &form, const NLMISC::CSheetId &sheetId) { const NLGEORGES::UFormElm &item=form->getRootNode(); SheetId=sheetId; item.getValueByName(ForceSheetName, "3d data.ForceDisplayCreatureName"); item.getValueByName(DisplayName, "3d data.DisplayName"); } void serial(NLMISC::IStream &f) { f.serial(SheetId); f.serial(ForceSheetName); f.serial(DisplayName); } static uint getVersion () { return 1; } void removed() { } }; std::map<CSheetId, TCreatureInfo> Creatures; TCreatureInfo *getCreature(const std::string &sheetName) { CSheetId id(sheetName+".creature"); if (Creatures.find(id) != Creatures.end()) return &(Creatures.find(id)->second); else return NULL; } string cleanupName(const std::string &name) { string ret; for (uint i=0; i<name.size(); ++i) { if (name[i] != ' ') ret += name[i]; else ret += '_'; } return ret; } ucstring cleanupUcName(const ucstring &name) { ucstring ret; for (uint i=0; i<name.size(); ++i) { if (name[i] != ' ') ret += name[i]; else ret += '_'; } return ret; } /* Removes first and last '$' */ ucstring makeGroupName(const ucstring & translationName) { ucstring ret = translationName; if (ret.size() >= 2) { if ( *ret.begin() == ucchar('$')) { ret=ret.substr(1); } if ( *ret.rbegin() == ucchar('$')) { ret = ret.substr(0, ret.size()-1); } } ret = cleanupUcName(ret); return ret; } struct TEntryInfo { string SheetName; }; set<string> GenericNames; map<string, TEntryInfo> SimpleNames; set<string> Functions; string removeAndStoreFunction(const std::string &fullName) { string::size_type pos = fullName.find("$"); if (pos == string::npos) return fullName; else { // extract and store the function name string ret; ret = fullName.substr(0, pos); string::size_type pos2 = fullName.find("$", pos+1); string fct = fullName.substr(pos+1, pos2-(pos+1)); ret += fullName.substr(pos2+1); if (Functions.find(fct) == Functions.end()) { nldebug("Adding function '%s'", fct.c_str()); Functions.insert(fct); } return ret; } } void addGenericName(const std::string &name, const std::string &sheetName) { TCreatureInfo *c = getCreature(sheetName); if (!c || c->ForceSheetName || !c->DisplayName) return; if (SimpleNames.find(name) != SimpleNames.end()) { nldebug("Name '%s' is now a generic name", name.c_str()); GenericNames.insert(name); SimpleNames.erase(name); } else if (GenericNames.find(name) == GenericNames.end()) { nldebug("Adding generic name '%s'", name.c_str()); GenericNames.insert(name); } } void addSimpleName(const std::string &name, const std::string &sheetName) { TCreatureInfo *c = getCreature(sheetName); if (!c || c->ForceSheetName || !c->DisplayName) return; if (SimpleNames.find(name) != SimpleNames.end()) { addGenericName(name, sheetName); } else if (GenericNames.find(name) != GenericNames.end()) { return; } else { nldebug("Adding simple name '%s'", name.c_str()); TEntryInfo ei; ei.SheetName = sheetName; SimpleNames.insert(make_pair(name, ei)); } } int extractBotNames(int argc, char *argv[]) { //------------------------------------------------------------------- // read the parameters for (int i=2; i<argc; ++i) { string s = argv[i]; if (s == "-r") { // active remove mode RemoveOlds = true; } else { nlwarning("Unknow option '%s'", argv[i]); return -1; } } //------------------------------------------------------------------- // read the configuration file CConfigFile cf; cf.load("bin/translation_tools.cfg"); //------------------------------------------------------------------- // read the vars CConfigFile::CVar &paths = cf.getVar("Paths"); CConfigFile::CVar &filtersVar = cf.getVar("Filters"); CConfigFile::CVar &ligoClassFile= cf.getVar("LigoClassFile"); CConfigFile::CVar &georgesPaths= cf.getVar("GeorgesPaths"); CConfigFile::CVar &pathNoRecurse= cf.getVar("PathsNoRecurse"); CConfigFile::CVar &workBotNamesFile= cf.getVar("WorkBotNamesFile"); CConfigFile::CVar &transBotNamesFile= cf.getVar("TransBotNamesFile"); CConfigFile::CVar &workTitleFile= cf.getVar("WorkTitleFile"); for (uint i=0; i<paths.size(); ++i) { CPath::addSearchPath(paths.asString(i), true, false); } for (uint i=0; i<pathNoRecurse.size(); ++i) { CPath::addSearchPath(pathNoRecurse.asString(i), false, false); } for (uint i=0; i<filtersVar.size(); ++i) { Filters.push_back(filtersVar.asString(i)); } //------------------------------------------------------------------- // init the sheets CSheetId::init(false); const string PACKED_SHEETS_NAME = "bin/translation_tools_creature.packed_sheets"; loadForm("creature", PACKED_SHEETS_NAME, Creatures, false, false); if (Creatures.empty()) { for (uint i=0;i<georgesPaths.size();++i) CPath::addSearchPath(georgesPaths.asString(i).c_str(), true, false); loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); } //------------------------------------------------------------------- // init ligo config string ligoPath = CPath::lookup(ligoClassFile.asString(), true, true); LigoConfig.readPrimitiveClass(ligoPath.c_str(), false); NLLIGO::Register(); CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; //------------------------------------------------------------------- // ok, ready for the real work, // first, read the primitives files and parse the primitives vector<string> files; CPath::getFileList("primitive", files); for (uint i=0; i<files.size(); ++i) { string pathName = files[i]; pathName = CPath::lookup(pathName); // check filters uint j=0; for (j=0; j<Filters.size(); ++j) { if (pathName.find(Filters[j]) != string::npos) break; } if (j != Filters.size()) // skip this file continue; nlinfo("Loading file '%s'...", CFile::getFilename(pathName).c_str()); CPrimitives primDoc; CPrimitiveContext::instance().CurrentPrimitive = &primDoc; loadXmlPrimitiveFile(primDoc, pathName, LigoConfig); // now parse the file // look for group template { TPrimitiveClassPredicate pred("group_template_npc"); TPrimitiveSet result; CPrimitiveSet<TPrimitiveClassPredicate> ps; ps.buildSet(primDoc.RootNode, pred, result); for (uint i=0; i<result.size(); ++i) { string name; string countStr; string sheetStr; result[i]->getPropertyByName("name", name); result[i]->getPropertyByName("count", countStr); result[i]->getPropertyByName("bot_sheet_look", sheetStr); uint32 count; NLMISC::fromString(countStr, count); if (count != 0) { if (sheetStr.empty()) { nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); } else { addGenericName(removeAndStoreFunction(name), sheetStr); } } } } // look for bot template { TPrimitiveClassPredicate pred("bot_template_npc"); TPrimitiveSet result; CPrimitiveSet<TPrimitiveClassPredicate> ps; ps.buildSet(primDoc.RootNode, pred, result); for (uint i=0; i<result.size(); ++i) { string name; string sheetStr; result[i]->getPropertyByName("name", name); result[i]->getPropertyByName("sheet_look", sheetStr); if (sheetStr.empty()) { // take the sheet in the parent result[i]->getParent()->getPropertyByName("bot_sheet_look", sheetStr); } if (sheetStr.empty()) { nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); } else { addGenericName(removeAndStoreFunction(name), sheetStr); } } } // look for npc_group { TPrimitiveClassPredicate pred("npc_group"); TPrimitiveSet result; CPrimitiveSet<TPrimitiveClassPredicate> ps; ps.buildSet(primDoc.RootNode, pred, result); for (uint i=0; i<result.size(); ++i) { string name; string countStr; string sheetStr; result[i]->getPropertyByName("name", name); result[i]->getPropertyByName("count", countStr); result[i]->getPropertyByName("bot_sheet_client", sheetStr); uint32 count; NLMISC::fromString(countStr, count); if (count > 0 && sheetStr.empty()) { nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); } else { if (count == 1) { addSimpleName(removeAndStoreFunction(name), sheetStr); } else if (count > 1) { addGenericName(removeAndStoreFunction(name), sheetStr); } } } } // look for bot { TPrimitiveClassPredicate pred("npc_bot"); TPrimitiveSet result; CPrimitiveSet<TPrimitiveClassPredicate> ps; ps.buildSet(primDoc.RootNode, pred, result); for (uint i=0; i<result.size(); ++i) { string name; string sheetStr; result[i]->getPropertyByName("name", name); result[i]->getPropertyByName("sheet_client", sheetStr); if (sheetStr.empty()) { // take the sheet in the parent result[i]->getParent()->getPropertyByName("bot_sheet_client", sheetStr); } if (sheetStr.empty()) { nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); } else { TEntryInfo ei; addSimpleName(removeAndStoreFunction(name), sheetStr); } } } } //------------------------------------------------------------------- // step 2 : load the reference file nlinfo("Looking for missing translation:"); TWorksheet botNames; loadExcelSheet(workBotNamesFile.asString(), botNames, true); TWorksheet transBotNames; loadExcelSheet(transBotNamesFile.asString(), transBotNames, true); TWorksheet fcts; loadExcelSheet(workTitleFile.asString(), fcts, true); // add missing element uint nbAddSimpleName = 0; uint nbAddFunction = 0; uint nbAddGenericName = 0; uint botIdCol; nlverify(botNames.findId(botIdCol)); uint transIdCol; nlverify(transBotNames.findId(transIdCol)); uint fctsIdCol; nlverify(fcts.findId(fctsIdCol)); // special treatment to add the sheet_name col { uint sheetCol; if (!botNames.findCol(ucstring("sheet_name"), sheetCol)) { botNames.insertColumn(botNames.ColCount); botNames.setData(0, botNames.ColCount-1, ucstring("sheet_name")); } if (!transBotNames.findCol(ucstring("sheet_name"), sheetCol)) { transBotNames.insertColumn(transBotNames.ColCount); transBotNames.setData(0, transBotNames.ColCount-1, ucstring("sheet_name")); } } // 1 - simple names { nlinfo(" Simple names..."); map<string, TEntryInfo>::iterator first(SimpleNames.begin()), last(SimpleNames.end()); for (; first != last; ++first) { uint rowIdx = 0; if (!botNames.findRow(botIdCol, first->first, rowIdx)) { // we need to add the entry rowIdx = botNames.size(); botNames.resize(botNames.size()+1); botNames.setData(rowIdx, ucstring("bot name"), first->first); botNames.setData(rowIdx, ucstring("translated name"), first->first); botNames.setData(rowIdx, ucstring("sheet_name"), first->second.SheetName); nbAddSimpleName++; } else { // set/update the sheet name info // try to restore the existing translation uint transRowIdx = 0; if (transBotNames.findRow(transIdCol, first->first, transRowIdx)) { ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); ucstring ucWkHash; uint64 hash = CI18N::makeHash(wkBotName + wkTranslationName +wkSheetName); CI18N::hashToUCString(hash, ucWkHash); ucstring trUcHash = transBotNames[transRowIdx][0]; bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; bool hashIsValide = std::equal(ucWkHash.begin(), ucWkHash.end(), trUcHash.begin()+1); // Hash is equal get the translation if (hashIsValide && !isWkTranslationNameAGroupName) { wkTranslationName = transBotNames.getData(transRowIdx, ucstring("translated name")); wkSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); hash = CI18N::makeHash(wkBotName + wkTranslationName + wkSheetName); // update the hash code CI18N::hashToUCString(hash, transBotNames[transRowIdx][0]); } // bots_name.txt has been manually changed. We trust what the Level Designer has done. We don't destroy is work. // or it is a simple else { //use the "translated name" of the manually changed work/bot_name.txt botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); } } } } } // 2 - generic names { nlinfo(" Generic names..."); set<string>::iterator first(GenericNames.begin()), last(GenericNames.end()); for (; first != last; ++first) { string gnName = "gn_" + cleanupName(*first); ucstring fctsTitleId; ucstring fctsName; // add or modify the bot names uint rowIdx; if (!botNames.findRow(botIdCol, *first, rowIdx)) { // we need to add the entry rowIdx = botNames.size(); botNames.resize(botNames.size()+1); botNames.setData(rowIdx, ucstring("bot name"), *first); botNames.setData(rowIdx, ucstring("translated name"), ucstring("$") + gnName + "$"); botNames.setData(rowIdx, ucstring("sheet_name"), ucstring()); fctsTitleId = gnName; fctsName = *first; nbAddSimpleName++; } else { // look in the translated table to remember the translated name to write it in the string file ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); nlinfo("Bot name:%s\n",wkBotName.toString().c_str()); bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; if ( isWkTranslationNameAGroupName ) //work name looks like "$gn_***$: do not modify { //Do not change work/bot_name.txt // update work/world_title.txt ucstring transName; fctsTitleId = makeGroupName(wkTranslationName); uint transRowIdx; if (transBotNames.findRow(transIdCol, *first, transRowIdx)) { transName = transBotNames.getData(transRowIdx, ucstring("translated name")); if (transName.find(ucstring("$")) != ucstring::npos) { transName = fctsTitleId; } } else { transName = fctsTitleId; } //Do not touch anything botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); // fctsTitleId = makeGroupName(wkTranslationName); fctsName = transName; } else // WkTranslationName != "$gn*$" { uint transRowIdx; ucstring transName; ucstring wkSheetName; // Get the translation as a simple name. if (transBotNames.findRow(transIdCol, *first, transRowIdx)) { transName = transBotNames.getData(transRowIdx, ucstring("translated name")); ucstring trSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); //tr."translation name" is if (transName.find(ucstring("$")) != ucstring::npos) { //get Translation, update hash botNames[rowIdx][1] = transName; botNames[rowIdx][2] = trSheetName; fctsTitleId = makeGroupName(transName); fctsName = makeGroupName(transName); ucstring trNewUcHash; uint64 hash = CI18N::makeHash(wkBotName + transName +trSheetName); CI18N::hashToUCString(hash, trNewUcHash); transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; } else //botNames."translated name" != $gn_$ && tansName."translated name" != $gn_$ { // get the translation back //update work/bot_name.txt wkTranslationName = ucstring("$")+gnName+"$"; botNames[rowIdx][0] = wkBotName; botNames[rowIdx][1] = wkTranslationName; botNames[rowIdx][2] = wkSheetName; //update translated/bot_name.txt fctsName = transName; //transName fctsTitleId = gnName; ucstring trNewUcHash; uint64 hash = CI18N::makeHash(botNames[rowIdx][0] + botNames[rowIdx][1] +botNames[rowIdx][2]); CI18N::hashToUCString(hash, trNewUcHash); transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; } } else //There is no translation yet { fctsName = wkTranslationName; wkTranslationName = ucstring("$")+gnName+"$"; botNames[rowIdx][0] = wkBotName; botNames[rowIdx][1] = wkTranslationName; botNames[rowIdx][2] = wkSheetName; fctsTitleId = gnName; } } } // look for a corresponding entry uint gnNameRow; if (!fcts.findRow(fctsIdCol, fctsTitleId, gnNameRow)) { // not found, add it gnNameRow = fcts.size(); fcts.resize(fcts.size()+1); fcts.setData(gnNameRow, ucstring("title_id"), fctsTitleId); fcts.setData(gnNameRow, ucstring("name"), fctsName); nbAddGenericName++; } else //Update { } } } // 3 - functions { nlinfo(" Functions..."); set<string>::iterator first(Functions.begin()), last(Functions.end()); for (; first != last; ++first) { string fctName = *first; // look for a corresponding entry uint functionRow; if (!fcts.findRow(fctsIdCol, fctName, functionRow)) { // not found, add it functionRow = fcts.size(); fcts.resize(fcts.size()+1); fcts.setData(functionRow, ucstring("title_id"), fctName); fcts.setData(functionRow, ucstring("name"), *first); nbAddFunction++; } } } // display summary nlinfo("Adding %u new simple name", nbAddSimpleName); nlinfo("Adding %u new generic name", nbAddGenericName); nlinfo("Adding %u new function name", nbAddFunction); // saving the modified files ucstring s = prepareExcelSheet(botNames); CI18N::writeTextFile(workBotNamesFile.asString(), s, false); s = prepareExcelSheet(transBotNames); CI18N::writeTextFile(transBotNamesFile.asString(), s, false); s = prepareExcelSheet(fcts); CI18N::writeTextFile(workTitleFile.asString(), s, false); return 0; }