Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / ryzom / tools / translation_tools / extract_bot_names.cpp
blob15d8933c7e03d7c2e5c59a7675e1e5e52871a107
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2011 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "nel/misc/types_nl.h"
21 #include "nel/misc/config_file.h"
22 #include "nel/misc/sheet_id.h"
23 #include "nel/misc/path.h"
24 #include "nel/misc/diff_tool.h"
25 #include "nel/georges/u_form.h"
26 #include "nel/georges/u_form_elm.h"
27 #include "nel/georges/load_form.h"
28 #include "nel/ligo/ligo_config.h"
29 #include "nel/ligo/primitive.h"
30 #include "nel/ligo/primitive_utils.h"
32 using namespace std;
33 using namespace NLMISC;
34 using namespace NLLIGO;
35 using namespace STRING_MANAGER;
37 vector<string> Filters;
39 static CLigoConfig LigoConfig;
40 static bool RemoveOlds = false;
42 struct TCreatureInfo
44 CSheetId SheetId;
45 bool ForceSheetName;
46 bool DisplayName;
49 void readGeorges (const NLMISC::CSmartPtr<NLGEORGES::UForm> &form, const NLMISC::CSheetId &sheetId)
51 const NLGEORGES::UFormElm &item=form->getRootNode();
53 SheetId=sheetId;
54 item.getValueByName(ForceSheetName, "3d data.ForceDisplayCreatureName");
55 item.getValueByName(DisplayName, "3d data.DisplayName");
58 void serial(NLMISC::IStream &f)
60 f.serial(SheetId);
61 f.serial(ForceSheetName);
62 f.serial(DisplayName);
66 static uint getVersion ()
68 return 1;
71 void removed()
77 std::map<CSheetId, TCreatureInfo> Creatures;
79 TCreatureInfo *getCreature(const std::string &sheetName)
81 CSheetId id(sheetName+".creature");
83 if (Creatures.find(id) != Creatures.end())
84 return &(Creatures.find(id)->second);
85 else
86 return NULL;
89 string cleanupName(const std::string &name)
91 string ret;
93 for (uint i=0; i<name.size(); ++i)
95 if (name[i] != ' ')
96 ret += name[i];
97 else
98 ret += '_';
101 return ret;
104 ucstring cleanupUcName(const ucstring &name)
106 ucstring ret;
108 for (uint i=0; i<name.size(); ++i)
110 if (name[i] != ' ')
111 ret += name[i];
112 else
113 ret += '_';
116 return ret;
121 Removes first and last '$'
123 ucstring makeGroupName(const ucstring & translationName)
125 ucstring ret = translationName;
126 if (ret.size() >= 2)
128 if ( *ret.begin() == ucchar('$'))
130 ret=ret.substr(1);
132 if ( *ret.rbegin() == ucchar('$'))
134 ret = ret.substr(0, ret.size()-1);
137 ret = cleanupUcName(ret);
138 return ret;
141 struct TEntryInfo
143 string SheetName;
146 set<string> GenericNames;
147 map<string, TEntryInfo> SimpleNames;
148 set<string> Functions;
151 string removeAndStoreFunction(const std::string &fullName)
153 string::size_type pos = fullName.find("$");
154 if (pos == string::npos)
155 return fullName;
156 else
158 // extract and store the function name
159 string ret;
161 ret = fullName.substr(0, pos);
162 string::size_type pos2 = fullName.find("$", pos+1);
164 string fct = fullName.substr(pos+1, pos2-(pos+1));
166 ret += fullName.substr(pos2+1);
168 if (Functions.find(fct) == Functions.end())
170 nldebug("Adding function '%s'", fct.c_str());
171 Functions.insert(fct);
174 return ret;
179 void addGenericName(const std::string &name, const std::string &sheetName)
181 TCreatureInfo *c = getCreature(sheetName);
182 if (!c || c->ForceSheetName || !c->DisplayName)
183 return;
185 if (SimpleNames.find(name) != SimpleNames.end())
187 nldebug("Name '%s' is now a generic name", name.c_str());
188 GenericNames.insert(name);
189 SimpleNames.erase(name);
192 else if (GenericNames.find(name) == GenericNames.end())
194 nldebug("Adding generic name '%s'", name.c_str());
195 GenericNames.insert(name);
199 void addSimpleName(const std::string &name, const std::string &sheetName)
201 TCreatureInfo *c = getCreature(sheetName);
202 if (!c || c->ForceSheetName || !c->DisplayName)
203 return;
205 if (SimpleNames.find(name) != SimpleNames.end())
207 addGenericName(name, sheetName);
209 else if (GenericNames.find(name) != GenericNames.end())
211 return;
213 else
215 nldebug("Adding simple name '%s'", name.c_str());
217 TEntryInfo ei;
218 ei.SheetName = sheetName;
220 SimpleNames.insert(make_pair(name, ei));
224 int extractBotNames(int argc, char *argv[])
226 //-------------------------------------------------------------------
227 // read the parameters
228 for (int i=2; i<argc; ++i)
230 string s = argv[i];
231 if (s == "-r")
233 // active remove mode
234 RemoveOlds = true;
236 else
238 nlwarning("Unknow option '%s'", argv[i]);
239 return -1;
243 //-------------------------------------------------------------------
244 // read the configuration file
245 CConfigFile cf;
247 cf.load("bin/translation_tools.cfg");
249 //-------------------------------------------------------------------
250 // read the vars
251 CConfigFile::CVar &paths = cf.getVar("Paths");
252 CConfigFile::CVar &filtersVar = cf.getVar("Filters");
253 CConfigFile::CVar &ligoClassFile= cf.getVar("LigoClassFile");
254 CConfigFile::CVar &georgesPaths= cf.getVar("GeorgesPaths");
255 CConfigFile::CVar &pathNoRecurse= cf.getVar("PathsNoRecurse");
256 CConfigFile::CVar &workBotNamesFile= cf.getVar("WorkBotNamesFile");
257 CConfigFile::CVar &transBotNamesFile= cf.getVar("TransBotNamesFile");
258 CConfigFile::CVar &workTitleFile= cf.getVar("WorkTitleFile");
260 for (uint i=0; i<paths.size(); ++i)
262 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(paths.asString(i)), true, false);
264 for (uint i=0; i<pathNoRecurse.size(); ++i)
266 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(pathNoRecurse.asString(i)), false, false);
269 for (uint i=0; i<filtersVar.size(); ++i)
271 Filters.push_back(filtersVar.asString(i));
275 //-------------------------------------------------------------------
276 // init the sheets
277 CSheetId::init(false);
278 const string PACKED_SHEETS_NAME = "bin/translation_tools_creature.packed_sheets";
279 loadForm("creature", PACKED_SHEETS_NAME, Creatures, false, false);
281 if (Creatures.empty())
283 for (uint i=0;i<georgesPaths.size();++i)
284 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(georgesPaths.asString(i)), true, false);
286 loadForm("creature", PACKED_SHEETS_NAME, Creatures, true);
290 //-------------------------------------------------------------------
291 // init ligo config
292 string ligoPath = CPath::lookup(ligoClassFile.asString(), true, true);
293 LigoConfig.readPrimitiveClass(ligoPath.c_str(), false);
294 NLLIGO::Register();
296 CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig;
298 //-------------------------------------------------------------------
299 // ok, ready for the real work,
300 // first, read the primitives files and parse the primitives
301 vector<string> files;
302 CPath::getFileList("primitive", files);
304 for (uint i=0; i<files.size(); ++i)
306 string pathName = files[i];
307 pathName = CPath::lookup(pathName);
309 // check filters
310 uint j=0;
311 for (j=0; j<Filters.size(); ++j)
313 if (pathName.find(Filters[j]) != string::npos)
314 break;
316 if (j != Filters.size())
317 // skip this file
318 continue;
320 nlinfo("Loading file '%s'...", CFile::getFilename(pathName).c_str());
322 CPrimitives primDoc;
323 CPrimitiveContext::instance().CurrentPrimitive = &primDoc;
324 loadXmlPrimitiveFile(primDoc, pathName, LigoConfig);
326 // now parse the file
328 // look for group template
330 TPrimitiveClassPredicate pred("group_template_npc");
331 TPrimitiveSet result;
333 CPrimitiveSet<TPrimitiveClassPredicate> ps;
334 ps.buildSet(primDoc.RootNode, pred, result);
336 for (uint i=0; i<result.size(); ++i)
338 string name;
339 string countStr;
340 string sheetStr;
341 result[i]->getPropertyByName("name", name);
342 result[i]->getPropertyByName("count", countStr);
343 result[i]->getPropertyByName("bot_sheet_look", sheetStr);
345 uint32 count;
346 NLMISC::fromString(countStr, count);
348 if (count != 0)
350 if (sheetStr.empty())
352 nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str());
354 else
356 addGenericName(removeAndStoreFunction(name), sheetStr);
361 // look for bot template
363 TPrimitiveClassPredicate pred("bot_template_npc");
364 TPrimitiveSet result;
366 CPrimitiveSet<TPrimitiveClassPredicate> ps;
367 ps.buildSet(primDoc.RootNode, pred, result);
369 for (uint i=0; i<result.size(); ++i)
371 string name;
372 string sheetStr;
373 result[i]->getPropertyByName("name", name);
374 result[i]->getPropertyByName("sheet_look", sheetStr);
376 if (sheetStr.empty())
378 // take the sheet in the parent
379 result[i]->getParent()->getPropertyByName("bot_sheet_look", sheetStr);
382 if (sheetStr.empty())
384 nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str());
386 else
388 addGenericName(removeAndStoreFunction(name), sheetStr);
392 // look for npc_group
394 TPrimitiveClassPredicate pred("npc_group");
395 TPrimitiveSet result;
397 CPrimitiveSet<TPrimitiveClassPredicate> ps;
398 ps.buildSet(primDoc.RootNode, pred, result);
400 for (uint i=0; i<result.size(); ++i)
402 string name;
403 string countStr;
404 string sheetStr;
405 result[i]->getPropertyByName("name", name);
406 result[i]->getPropertyByName("count", countStr);
407 result[i]->getPropertyByName("bot_sheet_client", sheetStr);
409 uint32 count;
410 NLMISC::fromString(countStr, count);
412 if (count > 0 && sheetStr.empty())
414 nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str());
416 else
418 if (count == 1)
420 addSimpleName(removeAndStoreFunction(name), sheetStr);
422 else if (count > 1)
424 addGenericName(removeAndStoreFunction(name), sheetStr);
429 // look for bot
431 TPrimitiveClassPredicate pred("npc_bot");
432 TPrimitiveSet result;
434 CPrimitiveSet<TPrimitiveClassPredicate> ps;
435 ps.buildSet(primDoc.RootNode, pred, result);
437 for (uint i=0; i<result.size(); ++i)
439 string name;
440 string sheetStr;
441 result[i]->getPropertyByName("name", name);
442 result[i]->getPropertyByName("sheet_client", sheetStr);
444 if (sheetStr.empty())
446 // take the sheet in the parent
447 result[i]->getParent()->getPropertyByName("bot_sheet_client", sheetStr);
450 if (sheetStr.empty())
452 nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str());
454 else
456 TEntryInfo ei;
457 addSimpleName(removeAndStoreFunction(name), sheetStr);
463 //-------------------------------------------------------------------
464 // step 2 : load the reference file
466 nlinfo("Looking for missing translation:");
468 TWorksheet botNames;
469 loadExcelSheet(workBotNamesFile.asString(), botNames, true);
470 TWorksheet transBotNames;
471 loadExcelSheet(transBotNamesFile.asString(), transBotNames, true);
473 TWorksheet fcts;
474 loadExcelSheet(workTitleFile.asString(), fcts, true);
477 // add missing element
479 uint nbAddSimpleName = 0;
480 uint nbAddFunction = 0;
481 uint nbAddGenericName = 0;
483 uint botIdCol;
484 nlverify(botNames.findId(botIdCol));
485 uint transIdCol;
486 nlverify(transBotNames.findId(transIdCol));
487 uint fctsIdCol;
488 nlverify(fcts.findId(fctsIdCol));
490 // special treatment to add the sheet_name col
492 uint sheetCol;
493 if (!botNames.findCol(ucstring("sheet_name"), sheetCol))
495 botNames.insertColumn(botNames.ColCount);
496 botNames.setData(0, botNames.ColCount-1, ucstring("sheet_name"));
499 if (!transBotNames.findCol(ucstring("sheet_name"), sheetCol))
501 transBotNames.insertColumn(transBotNames.ColCount);
502 transBotNames.setData(0, transBotNames.ColCount-1, ucstring("sheet_name"));
505 // 1 - simple names
507 nlinfo(" Simple names...");
510 map<string, TEntryInfo>::iterator first(SimpleNames.begin()), last(SimpleNames.end());
511 for (; first != last; ++first)
513 uint rowIdx = 0;
514 if (!botNames.findRow(botIdCol, first->first, rowIdx))
516 // we need to add the entry
517 rowIdx = botNames.size();
518 botNames.resize(botNames.size()+1);
520 botNames.setData(rowIdx, ucstring("bot name"), first->first);
521 botNames.setData(rowIdx, ucstring("translated name"), first->first);
522 botNames.setData(rowIdx, ucstring("sheet_name"), first->second.SheetName);
524 nbAddSimpleName++;
526 else
528 // set/update the sheet name info
529 // try to restore the existing translation
530 uint transRowIdx = 0;
531 if (transBotNames.findRow(transIdCol, first->first, transRowIdx))
533 ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name"));
534 ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name"));
535 ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name"));
536 ucstring ucWkHash;
537 uint64 hash = CI18N::makeHash(wkBotName + wkTranslationName +wkSheetName);
538 CI18N::hashToUCString(hash, ucWkHash);
539 ucstring trUcHash = transBotNames[transRowIdx][0];
540 bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos;
541 bool hashIsValide = std::equal(ucWkHash.begin(), ucWkHash.end(), trUcHash.begin()+1);
542 // Hash is equal get the translation
543 if (hashIsValide && !isWkTranslationNameAGroupName)
545 wkTranslationName = transBotNames.getData(transRowIdx, ucstring("translated name"));
546 wkSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name"));
547 botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName);
548 botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName);
549 hash = CI18N::makeHash(wkBotName + wkTranslationName + wkSheetName);
550 // update the hash code
551 CI18N::hashToUCString(hash, transBotNames[transRowIdx][0]);
553 // bots_name.txt has been manually changed. We trust what the Level Designer has done. We don't destroy is work.
554 // or it is a simple
555 else
557 //use the "translated name" of the manually changed work/bot_name.txt
558 botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName);
559 botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName);
566 // 2 - generic names
569 nlinfo(" Generic names...");
571 set<string>::iterator first(GenericNames.begin()), last(GenericNames.end());
572 for (; first != last; ++first)
574 string gnName = "gn_" + cleanupName(*first);
576 ucstring fctsTitleId;
577 ucstring fctsName;
578 // add or modify the bot names
579 uint rowIdx;
580 if (!botNames.findRow(botIdCol, *first, rowIdx))
582 // we need to add the entry
583 rowIdx = botNames.size();
584 botNames.resize(botNames.size()+1);
586 botNames.setData(rowIdx, ucstring("bot name"), *first);
587 botNames.setData(rowIdx, ucstring("translated name"), ucstring("$") + gnName + "$");
588 botNames.setData(rowIdx, ucstring("sheet_name"), ucstring());
589 fctsTitleId = gnName;
590 fctsName = *first;
592 nbAddSimpleName++;
594 else
596 // look in the translated table to remember the translated name to write it in the string file
597 ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name"));
598 ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name"));
599 ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name"));
602 nlinfo("Bot name:%s\n",wkBotName.toString().c_str());
603 bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos;
605 if ( isWkTranslationNameAGroupName ) //work name looks like "$gn_***$: do not modify
608 //Do not change work/bot_name.txt
609 // update work/world_title.txt
611 ucstring transName;
612 fctsTitleId = makeGroupName(wkTranslationName);
613 uint transRowIdx;
614 if (transBotNames.findRow(transIdCol, *first, transRowIdx))
616 transName = transBotNames.getData(transRowIdx, ucstring("translated name"));
618 if (transName.find(ucstring("$")) != ucstring::npos)
620 transName = fctsTitleId;
623 else
625 transName = fctsTitleId;
627 //Do not touch anything
628 botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName);
629 botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName);
630 // fctsTitleId = makeGroupName(wkTranslationName);
631 fctsName = transName;
634 else // WkTranslationName != "$gn*$"
636 uint transRowIdx;
637 ucstring transName;
638 ucstring wkSheetName;
639 // Get the translation as a simple name.
640 if (transBotNames.findRow(transIdCol, *first, transRowIdx))
643 transName = transBotNames.getData(transRowIdx, ucstring("translated name"));
644 ucstring trSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name"));
646 //tr."translation name" is
647 if (transName.find(ucstring("$")) != ucstring::npos)
649 //get Translation, update hash
650 botNames[rowIdx][1] = transName;
651 botNames[rowIdx][2] = trSheetName;
652 fctsTitleId = makeGroupName(transName);
653 fctsName = makeGroupName(transName);
654 ucstring trNewUcHash;
655 uint64 hash = CI18N::makeHash(wkBotName + transName +trSheetName);
656 CI18N::hashToUCString(hash, trNewUcHash);
657 transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash;
659 else //botNames."translated name" != $gn_$ && tansName."translated name" != $gn_$
662 // get the translation back
663 //update work/bot_name.txt
664 wkTranslationName = ucstring("$")+gnName+"$";
665 botNames[rowIdx][0] = wkBotName;
666 botNames[rowIdx][1] = wkTranslationName;
667 botNames[rowIdx][2] = wkSheetName;
669 //update translated/bot_name.txt
671 fctsName = transName; //transName
672 fctsTitleId = gnName;
673 ucstring trNewUcHash;
674 uint64 hash = CI18N::makeHash(botNames[rowIdx][0] + botNames[rowIdx][1] +botNames[rowIdx][2]);
675 CI18N::hashToUCString(hash, trNewUcHash);
676 transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash;
680 else //There is no translation yet
682 fctsName = wkTranslationName;
683 wkTranslationName = ucstring("$")+gnName+"$";
684 botNames[rowIdx][0] = wkBotName;
685 botNames[rowIdx][1] = wkTranslationName;
686 botNames[rowIdx][2] = wkSheetName;
687 fctsTitleId = gnName;
696 // look for a corresponding entry
697 uint gnNameRow;
700 if (!fcts.findRow(fctsIdCol, fctsTitleId, gnNameRow))
703 // not found, add it
704 gnNameRow = fcts.size();
705 fcts.resize(fcts.size()+1);
706 fcts.setData(gnNameRow, ucstring("title_id"), fctsTitleId);
707 fcts.setData(gnNameRow, ucstring("name"), fctsName);
708 nbAddGenericName++;
711 else //Update
719 // 3 - functions
721 nlinfo(" Functions...");
723 set<string>::iterator first(Functions.begin()), last(Functions.end());
724 for (; first != last; ++first)
726 string fctName = *first;
727 // look for a corresponding entry
728 uint functionRow;
729 if (!fcts.findRow(fctsIdCol, fctName, functionRow))
731 // not found, add it
732 functionRow = fcts.size();
733 fcts.resize(fcts.size()+1);
735 fcts.setData(functionRow, ucstring("title_id"), fctName);
736 fcts.setData(functionRow, ucstring("name"), *first);
738 nbAddFunction++;
743 // display summary
744 nlinfo("Adding %u new simple name", nbAddSimpleName);
745 nlinfo("Adding %u new generic name", nbAddGenericName);
746 nlinfo("Adding %u new function name", nbAddFunction);
748 // saving the modified files
750 ucstring s = prepareExcelSheet(botNames);
751 CI18N::writeTextFile(workBotNamesFile.asString(), s);
752 s = prepareExcelSheet(transBotNames);
753 CI18N::writeTextFile(transBotNamesFile.asString(), s);
754 s = prepareExcelSheet(fcts);
755 CI18N::writeTextFile(workTitleFile.asString(), s);
757 return 0;