1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2011 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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"
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;
49 void readGeorges (const NLMISC::CSmartPtr
<NLGEORGES::UForm
> &form
, const NLMISC::CSheetId
&sheetId
)
51 const NLGEORGES::UFormElm
&item
=form
->getRootNode();
54 item
.getValueByName(ForceSheetName
, "3d data.ForceDisplayCreatureName");
55 item
.getValueByName(DisplayName
, "3d data.DisplayName");
58 void serial(NLMISC::IStream
&f
)
61 f
.serial(ForceSheetName
);
62 f
.serial(DisplayName
);
66 static uint
getVersion ()
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
);
89 string
cleanupName(const std::string
&name
)
93 for (uint i
=0; i
<name
.size(); ++i
)
104 ucstring
cleanupUcName(const ucstring
&name
)
108 for (uint i
=0; i
<name
.size(); ++i
)
121 Removes first and last '$'
123 ucstring
makeGroupName(const ucstring
& translationName
)
125 ucstring ret
= translationName
;
128 if ( *ret
.begin() == ucchar('$'))
132 if ( *ret
.rbegin() == ucchar('$'))
134 ret
= ret
.substr(0, ret
.size()-1);
137 ret
= cleanupUcName(ret
);
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
)
158 // extract and store the function name
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
);
179 void addGenericName(const std::string
&name
, const std::string
&sheetName
)
181 TCreatureInfo
*c
= getCreature(sheetName
);
182 if (!c
|| c
->ForceSheetName
|| !c
->DisplayName
)
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
)
205 if (SimpleNames
.find(name
) != SimpleNames
.end())
207 addGenericName(name
, sheetName
);
209 else if (GenericNames
.find(name
) != GenericNames
.end())
215 nldebug("Adding simple name '%s'", name
.c_str());
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
)
233 // active remove mode
238 nlwarning("Unknow option '%s'", argv
[i
]);
243 //-------------------------------------------------------------------
244 // read the configuration file
247 cf
.load("bin/translation_tools.cfg");
249 //-------------------------------------------------------------------
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 //-------------------------------------------------------------------
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 //-------------------------------------------------------------------
292 string ligoPath
= CPath::lookup(ligoClassFile
.asString(), true, true);
293 LigoConfig
.readPrimitiveClass(ligoPath
.c_str(), false);
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
);
311 for (j
=0; j
<Filters
.size(); ++j
)
313 if (pathName
.find(Filters
[j
]) != string::npos
)
316 if (j
!= Filters
.size())
320 nlinfo("Loading file '%s'...", CFile::getFilename(pathName
).c_str());
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
)
341 result
[i
]->getPropertyByName("name", name
);
342 result
[i
]->getPropertyByName("count", countStr
);
343 result
[i
]->getPropertyByName("bot_sheet_look", sheetStr
);
346 NLMISC::fromString(countStr
, count
);
350 if (sheetStr
.empty())
352 nlwarning("In '%s', empty sheet !", buildPrimPath(result
[i
]).c_str());
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
)
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());
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
)
405 result
[i
]->getPropertyByName("name", name
);
406 result
[i
]->getPropertyByName("count", countStr
);
407 result
[i
]->getPropertyByName("bot_sheet_client", sheetStr
);
410 NLMISC::fromString(countStr
, count
);
412 if (count
> 0 && sheetStr
.empty())
414 nlwarning("In '%s', empty sheet !", buildPrimPath(result
[i
]).c_str());
420 addSimpleName(removeAndStoreFunction(name
), sheetStr
);
424 addGenericName(removeAndStoreFunction(name
), sheetStr
);
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
)
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());
457 addSimpleName(removeAndStoreFunction(name
), sheetStr
);
463 //-------------------------------------------------------------------
464 // step 2 : load the reference file
466 nlinfo("Looking for missing translation:");
469 loadExcelSheet(workBotNamesFile
.asString(), botNames
, true);
470 TWorksheet transBotNames
;
471 loadExcelSheet(transBotNamesFile
.asString(), transBotNames
, true);
474 loadExcelSheet(workTitleFile
.asString(), fcts
, true);
477 // add missing element
479 uint nbAddSimpleName
= 0;
480 uint nbAddFunction
= 0;
481 uint nbAddGenericName
= 0;
484 nlverify(botNames
.findId(botIdCol
));
486 nlverify(transBotNames
.findId(transIdCol
));
488 nlverify(fcts
.findId(fctsIdCol
));
490 // special treatment to add the sheet_name col
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"));
507 nlinfo(" Simple names...");
510 map
<string
, TEntryInfo
>::iterator
first(SimpleNames
.begin()), last(SimpleNames
.end());
511 for (; first
!= last
; ++first
)
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
);
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"));
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.
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
);
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
;
578 // add or modify the bot names
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
;
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
612 fctsTitleId
= makeGroupName(wkTranslationName
);
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
;
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*$"
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
700 if (!fcts
.findRow(fctsIdCol
, fctsTitleId
, gnNameRow
))
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
);
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
729 if (!fcts
.findRow(fctsIdCol
, fctName
, functionRow
))
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
);
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
);