Fix classique titles display
[ryzomcore.git] / ryzom / client / src / string_manager_client.cpp
blob74f650b1e9e1cff9126e83ca2bf03752405bf105
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 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/>.
22 #include "stdpch.h"
23 #include "string_manager_client.h"
24 #include "nel/misc/file.h"
25 #include "client_cfg.h"
26 #include "net_manager.h"
27 #include "connection.h"
28 #include "nel/misc/hierarchical_timer.h"
29 #include "nel/misc/algo.h"
30 #include "misc.h"
31 #include "entity_cl.h"
33 #ifdef DEBUG_NEW
34 #define new DEBUG_NEW
35 #endif
37 using namespace std;
38 using namespace NLMISC;
40 namespace STRING_MANAGER
43 // ***************************************************************************
44 map<string, CStringManagerClient::CItem> CStringManagerClient::_SpecItem_TempMap;
45 map<string, string> CStringManagerClient::_DynStrings;
46 vector<string> CStringManagerClient::_TitleWords;
47 bool CStringManagerClient::_SpecItem_MemoryCompressed = false;
48 char *CStringManagerClient::_SpecItem_Labels = NULL;
49 char *CStringManagerClient::_SpecItem_NameDesc = NULL;
50 vector<CStringManagerClient::CItemLight> CStringManagerClient::_SpecItems;
51 bool MustReleaseStaticArrays = true;
54 CStringManagerClient *CStringManagerClient::_Instance= NULL;
55 string CStringManagerClient::_WaitString("???");
58 CStringManagerClient::CStringManagerClient()
60 _CacheInited = false;
61 _CacheLoaded = false;
62 // insert the empty string.
63 _ReceivedStrings.insert(make_pair((uint)EmptyStringId, string()));
64 // reserve some place to avoid reallocation as possible
65 _CacheStringToSave.reserve(1024);
67 // destructor.
68 CStringManagerClient::~CStringManagerClient()
70 if (MustReleaseStaticArrays)
72 if (_SpecItem_MemoryCompressed)
74 delete [] _SpecItem_Labels;
75 _SpecItem_Labels = NULL;
76 delete [] _SpecItem_NameDesc;
77 _SpecItem_NameDesc = NULL;
78 _SpecItems.clear();
79 _SpecItem_MemoryCompressed = false;
85 CStringManagerClient *CStringManagerClient::instance()
87 if (_Instance == 0)
88 _Instance = new CStringManagerClient();
90 return _Instance;
93 void CStringManagerClient::release(bool mustReleaseStaticArrays)
95 if (_Instance != 0)
97 bool prev = MustReleaseStaticArrays;
98 MustReleaseStaticArrays = mustReleaseStaticArrays;
99 delete _Instance;
100 MustReleaseStaticArrays = prev;
101 _Instance =0;
106 void CStringManagerClient::initCache(const std::string &shardId, const std::string &languageCode)
108 H_AUTO( CStringManagerClient_initCache )
110 _ShardId = shardId;
111 _LanguageCode = languageCode;
113 // to be inited, shard id and language code must be filled
114 if (!_ShardId.empty() && !_LanguageCode.empty())
115 _CacheInited = true;
116 else
117 _CacheInited = false;
120 void CStringManagerClient::loadCache(uint32 timestamp)
122 H_AUTO( CStringManagerClient_loadCache )
124 if (_CacheInited)
128 const uint currentVersion = 1;
129 _CacheFilename = std::string("save/") + _ShardId.substr(0, _ShardId.find(":")) + ".string_cache";
131 nlinfo("SM : Try to open the string cache : %s", _CacheFilename.c_str());
133 if (CFile::fileExists(_CacheFilename) && CFile::getFileSize(_CacheFilename))
135 // there is a cache file, check date reset it if needed
137 NLMISC::CIFile file(_CacheFilename);
138 file.setVersionException(false, false);
139 file.serialVersion(currentVersion);
140 if (file.getPos() + sizeof(_Timestamp) > file.getFileSize())
141 _Timestamp = ~timestamp;
142 else
143 file.serial(_Timestamp);
146 if (_Timestamp != timestamp)
148 nlinfo("SM: Clearing string cache : outofdate");
149 // the cache is not sync, reset it
150 NLMISC::COFile file(_CacheFilename);
151 file.serialVersion(currentVersion);
152 file.serial(timestamp);
154 else
156 nlinfo("SM : string cache in sync. cool");
159 else
161 nlinfo("SM: Creating string cache");
162 // cache file don't exist, create it with the timestamp
163 NLMISC::COFile file(_CacheFilename);
164 file.serialVersion(currentVersion);
165 file.serial(timestamp);
168 // clear all current data.
169 _ReceivedStrings.clear();
170 _ReceivedDynStrings.clear();
171 // NB : we keep the waiting strings and dyn strings
173 // insert the empty string.
174 _ReceivedStrings.insert(make_pair((uint)EmptyStringId, string()));
176 // load the cache file
177 NLMISC::CIFile file(_CacheFilename);
178 int version = file.serialVersion(currentVersion);
179 file.serial(_Timestamp);
180 nlassert(_Timestamp == timestamp);
181 nlassert(version >= 1); // Initial version
183 while (!file.eof())
185 uint32 id;
186 string str;
188 file.serial(id);
189 file.serial(str);
191 //nldebug("SM : loading string [%6u] as [%s] in cache", id, str.toString().c_str());
193 _ReceivedStrings.insert(std::make_pair(id, str));
196 _CacheLoaded = true;
198 catch(const NLMISC::Exception &e)
200 nlinfo("SM : loadCache failed, exception : %s", e.what());
201 nlinfo("SM : cache deactivated");
202 // unactivated cache.
203 _CacheFilename.erase();
210 void CStringManagerClient::waitString(uint32 stringId, const IStringWaiterRemover *premover, string *result)
212 H_AUTO( CStringManagerClient_waitString )
214 nlassert(premover && result);
215 string value;
216 if (getString(stringId, value))
217 *result = value;
218 else
220 // wait for the string
221 TStringWaiter sw;
222 sw.Result = result;
223 sw.Remover = premover;
224 _StringsWaiters.insert(std::make_pair(stringId, sw));
228 void CStringManagerClient::waitString(uint32 stringId, IStringWaitCallback *pcallback)
230 H_AUTO( CStringManagerClient_waitString2 )
232 nlassert(pcallback != 0);
233 string value;
234 if (getString(stringId, value))
236 pcallback->onStringAvailable(stringId, value);
238 else
240 // wait for the string
241 _StringsCallbacks.insert(std::make_pair(stringId, pcallback));
246 void CStringManagerClient::waitDynString(uint32 stringId, const IStringWaiterRemover *premover, string *result)
248 H_AUTO( CStringManagerClient_waitDynString )
250 nlassert(premover && result);
251 string value;
252 if (getDynString(stringId, value))
253 *result = value;
254 else
256 // wait for the string
257 TStringWaiter sw;
258 sw.Result = result;
259 sw.Remover = premover;
260 _DynStringsWaiters.insert(std::make_pair(stringId, sw));
264 void CStringManagerClient::waitDynString(uint32 stringId, IStringWaitCallback *pcallback)
266 H_AUTO( CStringManagerClient_waitDynString2 )
268 nlassert(pcallback != 0);
269 string value;
270 if (getDynString(stringId, value))
272 pcallback->onDynStringAvailable(stringId, value);
274 else
276 _DynStringsCallbacks.insert(std::make_pair(stringId, pcallback));
280 void CStringManagerClient::removeStringWaiter(const IStringWaiterRemover *remover)
282 H_AUTO( CStringManagerClient_removeStringWaiter )
284 // search in waiting string
286 restartLoop1:
287 TStringWaitersContainer::iterator first(_StringsWaiters.begin()), last(_StringsWaiters.end());
288 for (; first != last; ++first)
290 if (first->second.Remover == remover)
292 _StringsWaiters.erase(first);
293 goto restartLoop1;
297 // search in waiting dyn string
299 restartLoop2:
300 TStringWaitersContainer::iterator first(_DynStringsWaiters.begin()), last(_DynStringsWaiters.end());
301 for (; first != last; ++first)
303 if (first->second.Remover == remover)
305 _DynStringsWaiters.erase(first);
306 goto restartLoop2;
312 void CStringManagerClient::removeStringWaiter(const IStringWaitCallback *callback)
314 // H_AUTO( CStringManagerClient_removeStringWaiter2 )
316 // search in waiting string
318 restartLoop3:
319 TStringCallbacksContainer::iterator first(_StringsCallbacks.begin()), last(_StringsCallbacks.end());
320 for (; first != last; ++first)
322 if (first->second == callback)
324 _StringsCallbacks.erase(first);
325 goto restartLoop3;
329 // search in waiting dyn string
331 restartLoop4:
332 TStringCallbacksContainer::iterator first(_DynStringsCallbacks.begin()), last(_DynStringsCallbacks.end());
333 for (; first != last; ++first)
335 if (first->second == callback)
337 _DynStringsCallbacks.erase(first);
338 goto restartLoop4;
346 bool CStringManagerClient::getString(uint32 stringId, string &result)
348 H_AUTO( CStringManagerClient_getString )
350 if (ClientCfg.Local)
352 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
353 if (it != _ReceivedStrings.end())
355 result = it->second;
357 else
359 result = "StringID = " + toString(stringId);
362 else
364 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
365 if (it == _ReceivedStrings.end())
367 CHashSet<uint>::iterator it(_WaitingStrings.find(stringId));
368 if (it == _WaitingStrings.end())
370 _WaitingStrings.insert(stringId);
371 // need to ask for this string.
372 NLMISC::CBitMemStream bms;
373 static const string msgType = "STRING_MANAGER:STRING_RQ";
374 if( GenericMsgHeaderMngr.pushNameToStream(msgType,bms) )
376 bms.serial( stringId );
377 NetMngr.push( bms );
378 //nldebug("<CStringManagerClient::getString> sending 'STRING_MANAGER:STRING_RQ' message to server");
380 else
382 nldebug("<CStringManagerClient::getString> unknown message name 'STRING_MANAGER:STRING_RQ'");
386 if (ClientCfg.DebugStringManager)
388 char tmp[1024];
389 sprintf(tmp, "<WAIT STR %u>", stringId);
390 result = tmp;
392 else
393 result.erase(); // = _WaitString;
394 return false;
397 if (ClientCfg.DebugStringManager)
399 char tmp[1024];
400 sprintf(tmp, "<STR %u>", stringId);
401 result = tmp + it->second;
403 else
405 result = it->second;
406 if (result.size() > 9 && result.substr(0, 9) == "<missing:")
408 map<string, string>::iterator itds = _DynStrings.find(result.substr(9, result.size()-10));
409 if (itds != _DynStrings.end())
410 result = itds->second;
415 return true;
418 void CStringManagerClient::receiveString(uint32 stringId, const string &str)
420 H_AUTO( CStringManagerClient_receiveString )
422 //nlinfo("String %u available : [%s]", stringId, str.toString().c_str());
424 CHashSet<uint>::iterator it(_WaitingStrings.find(stringId));
425 if (it != _WaitingStrings.end())
427 _WaitingStrings.erase(it);
429 bool updateCache = true;
430 if (_ReceivedStrings.find(stringId) != _ReceivedStrings.end())
432 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
433 nlwarning("Receiving stringID %u (%s), already in received string (%s), replacing with new value.",
434 stringId,
435 str.c_str(),
436 it->second.c_str());
438 if (it->second != str)
439 it->second = str;
440 else
441 updateCache = false;
443 else
445 _ReceivedStrings.insert(std::make_pair(stringId, str));
448 if (updateCache)
450 // update the string cache. DON'T SAVE now cause
451 if (_CacheInited && !_CacheFilename.empty())
453 CCacheString cs;
454 cs.StringId= stringId;
455 cs.String= str;
456 _CacheStringToSave.push_back(cs);
460 // update the waiting strings
462 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
463 _StringsWaiters.equal_range(stringId);
465 if (range.first != range.second)
467 for (; range.first != range.second; ++range.first)
469 TStringWaiter &sw = range.first->second;
470 *(sw.Result) = str;
472 _StringsWaiters.erase(stringId);
476 // callback the waiter
478 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
479 _StringsCallbacks.equal_range(stringId);
481 if (range.first != range.second)
483 for (; range.first != range.second; ++range.first)
485 range.first->second->onStringAvailable(stringId, str);
487 _StringsCallbacks.erase(stringId);
492 // try to complete any pending dyn string
494 TDynStringsContainer::iterator first, last;
495 restartLoop:
496 first = _WaitingDynStrings.begin();
497 last = _WaitingDynStrings.end();
498 for (; first != last; ++first)
500 string value;
501 uint number = first->first;
502 /// Warning: if getDynString() return true, 'first' is erased => don't use it after in this loop
503 if (getDynString(number, value))
505 //nlinfo("DynString %u available : [%s]", number, value.toString().c_str());
506 // this dyn string is now complete !
507 // update the waiting dyn strings
509 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
510 _DynStringsWaiters.equal_range(number);
512 if (range.first != range.second)
514 for (; range.first != range.second; ++range.first)
516 TStringWaiter &sw = range.first->second;
517 *(sw.Result) = str;
519 _DynStringsWaiters.erase(number);
522 // callback the waiting dyn strings
524 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
525 _DynStringsCallbacks.equal_range(number);
527 if (range.first != range.second)
529 for (; range.first != range.second; ++range.first)
531 range.first->second->onDynStringAvailable(number, value);
533 _DynStringsCallbacks.erase(number);
536 goto restartLoop;
542 void CStringManagerClient::flushStringCache()
544 if(!_CacheStringToSave.empty())
546 NLMISC::COFile file(_CacheFilename, true);
547 for(uint i=0;i<_CacheStringToSave.size();i++)
549 file.serial(_CacheStringToSave[i].StringId);
550 file.serial(_CacheStringToSave[i].String);
553 _CacheStringToSave.clear();
557 void CStringManagerClient::receiveDynString(NLMISC::CBitMemStream &bms)
559 H_AUTO( CStringManagerClient_receiveDynString )
561 TDynStringInfo dynInfo;
562 dynInfo.Status = TDynStringInfo::received;
563 // read the dynamic string Id
564 uint32 dynId;
565 bms.serial(dynId);
567 /// read the base string Id
568 bms.serial(dynInfo.StringId);
570 // try to build the string
571 dynInfo.Message = bms;
572 buildDynString(dynInfo);
574 if (dynInfo.Status == TDynStringInfo::complete)
576 if (!ClientCfg.Light)
578 //nlinfo("DynString %u available : [%s]", dynId, dynInfo.String.toString().c_str());
581 _ReceivedDynStrings.insert(std::make_pair(dynId, dynInfo));
582 // security, if dynstring Message received twice, it is possible that the dynstring is still in waiting list
583 _WaitingDynStrings.erase(dynId);
585 // update the waiting dyn strings
587 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
588 _DynStringsWaiters.equal_range(dynId);
590 if (range.first != range.second)
592 for (; range.first != range.second; ++range.first)
594 TStringWaiter &sw = range.first->second;
595 *(sw.Result) = dynInfo.String;
597 _DynStringsWaiters.erase(dynId);
600 // callback the waiting dyn strings
602 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
603 _DynStringsCallbacks.equal_range(dynId);
605 if (range.first != range.second)
607 for (; range.first != range.second; ++range.first)
609 range.first->second->onDynStringAvailable(dynId, dynInfo.String);
611 _DynStringsCallbacks.erase(dynId);
615 else
616 _WaitingDynStrings.insert(std::make_pair(dynId, dynInfo));
620 bool CStringManagerClient::buildDynString(TDynStringInfo &dynInfo)
622 H_AUTO( CStringManagerClient_buildDynString )
624 if (dynInfo.Status == TDynStringInfo::received)
626 if (!getString(dynInfo.StringId, dynInfo.String))
628 // can't continue now, need the base string.
629 return false;
631 // ok, we have the base string, we can serial the parameters
632 string::iterator first(dynInfo.String.begin()), last(dynInfo.String.end());
633 for (; first != last; ++first)
635 if (*first == '%')
637 first ++;
638 if (first != last && *first != '%')
640 // we have a replacement point.
641 TParamValue param;
642 param.ReplacementPoint = (first-1) - dynInfo.String.begin();
643 switch(*first)
645 case 's':
646 param.Type = string_id;
649 dynInfo.Message.serial(param.StringId);
651 catch(const Exception &)
653 param.StringId = EmptyStringId;
655 break;
656 case 'i':
657 param.Type = integer;
660 dynInfo.Message.serial(param.Integer);
662 catch(const Exception &)
664 param.Integer= 0;
666 break;
667 case 't':
668 param.Type = time;
671 dynInfo.Message.serial(param.Time);
673 catch(const Exception &)
675 param.Time= 0;
677 break;
678 case '$':
679 param.Type = money;
682 dynInfo.Message.serial(param.Money);
684 catch(const Exception &)
686 param.Money= 0;
688 break;
689 case 'm':
690 param.Type = dyn_string_id;
693 dynInfo.Message.serial(param.DynStringId);
695 catch(const Exception &)
697 param.DynStringId= EmptyDynStringId;
699 break;
700 default:
701 nlwarning("Error: unknown replacement tag %%%c", (char)*first);
702 return false;
705 dynInfo.Params.push_back(param);
709 dynInfo.Status = TDynStringInfo::serialized;
712 if (dynInfo.Status == TDynStringInfo::serialized)
714 // try to retreive all string parameter to build the string.
715 string temp;
716 temp.reserve(dynInfo.String.size() * 2);
717 string::iterator src(dynInfo.String.begin());
718 string::iterator move = src;
720 std::vector<TParamValue>::iterator first(dynInfo.Params.begin()), last(dynInfo.Params.end());
721 for (; first != last; ++first)
723 TParamValue &param = *first;
724 switch(param.Type)
726 case string_id:
728 string str;
729 if (!getString(param.StringId, str))
730 return false;
732 ucstring::size_type p1 = str.find('[');
733 if (p1 != ucstring::npos)
735 str = str.substr(0, p1)+STRING_MANAGER::CStringManagerClient::getLocalizedName(str.substr(p1));
738 // If the string is a player name, we may have to remove the shard name (if the string looks like a player name)
739 if(!str.empty() && !PlayerSelectedHomeShardNameWithParenthesis.empty())
741 // fast pre-test
742 if( str[str.size()-1]==')' )
744 // the player name must be at least bigger than the string with ()
745 if(str.size()>PlayerSelectedHomeShardNameWithParenthesis.size())
747 // If the shard name is the same as the player home shard name, remove it
748 uint len= (uint)PlayerSelectedHomeShardNameWithParenthesis.size();
749 uint start= (uint)str.size()-len;
750 if(ucstrnicmp(str, start, len, PlayerSelectedHomeShardNameWithParenthesis)==0) // TODO: NLMISC::compareCaseInsensitive
751 str.resize(start);
756 // If the string contains a title, then remove it
757 string::size_type pos = str.find('$');
758 if ( ! str.empty() && pos != string::npos)
760 str = CEntityCL::removeTitleFromName(str);
763 // if the string contains a special rename of creature, remove it
764 if (str.size() > 2 && str[0] == '<' && str[1] == '#')
766 str = toUpper(str[2])+str.substr(3);
769 // append this string
770 temp.append(move, src+param.ReplacementPoint);
771 temp += str;
772 move = dynInfo.String.begin()+param.ReplacementPoint+2;
775 break;
776 case integer:
778 char value[1024];
779 sprintf(value, "%d", param.Integer);
780 temp.append(move, src+param.ReplacementPoint);
781 temp += value;
782 move = dynInfo.String.begin()+param.ReplacementPoint+2;
784 break;
785 case time:
787 string value;
788 uint32 time = (uint32)param.Time;
789 if( time >= (10*60*60) )
791 uint32 nbHours = time / (10*60*60);
792 time -= nbHours * 10 * 60 * 60;
793 value = toString("%d ", nbHours) + CI18N::get("uiMissionTimerHour") + " ";
795 uint32 nbMinutes = time / (10*60);
796 time -= nbMinutes * 10 * 60;
797 value = value + toString("%d ", nbMinutes) + CI18N::get("uiMissionTimerMinute") + " ";
799 else if( time >= (10*60) )
801 uint32 nbMinutes = time / (10*60);
802 time -= nbMinutes * 10 * 60;
803 value = value + toString("%d ", nbMinutes) + CI18N::get("uiMissionTimerMinute") + " ";
805 uint32 nbSeconds = time / 10;
806 value = value + toString("%d", nbSeconds) + CI18N::get("uiMissionTimerSecond");
807 temp.append(move, src+param.ReplacementPoint);
808 temp+=value;
809 move = dynInfo.String.begin()+param.ReplacementPoint+2;
811 break;
812 case money:
813 ///\todo nicoB/Boris : this is a temp patch that display money as integers
815 char value[1024];
816 sprintf(value, "%u", (uint32)param.Money);
817 temp.append(move, src+param.ReplacementPoint);
818 temp += value;
819 move = dynInfo.String.begin()+param.ReplacementPoint+2;
821 // TODO
822 // temp.append(move, src+param.ReplacementPoint);
823 // move = dynInfo.String.begin()+param.ReplacementPoint+2;
824 break;
825 case dyn_string_id:
827 string dynStr;
828 if (!getDynString(param.DynStringId, dynStr))
829 return false;
830 temp.append(move, src+param.ReplacementPoint);
831 temp += dynStr;
832 move = dynInfo.String.begin()+param.ReplacementPoint+2;
834 break;
835 default:
836 nlwarning("Unknown parameter type.");
837 break;
840 // append the rest of the string
841 temp.append(move, dynInfo.String.end());
843 // apply any 'delete' character in the string and replace double '%'
845 ptrdiff_t i =0;
846 while (i < (ptrdiff_t)temp.size())
848 if (temp[i] == 8)
850 // remove the 'delete' char AND the next char
851 temp.erase(i, 2);
853 else if (temp[i] == '%' && i < temp.size()-1 && temp[i+1] == '%')
855 temp.erase(i, 1);
857 else
858 ++i;
862 dynInfo.Status = TDynStringInfo::complete;
863 dynInfo.Message.clear();
864 dynInfo.String = temp;
865 return true;
867 if (dynInfo.Status == TDynStringInfo::complete)
868 return true;
870 nlwarning("Inconsistent dyn string status : %u", dynInfo.Status);
871 return false;
875 bool CStringManagerClient::getDynString(uint32 dynStringId, std::string &result)
877 H_AUTO( CStringManagerClient_getDynString )
879 if (dynStringId == EmptyDynStringId)
880 return true;
881 if (ClientCfg.Local)
883 TDynStringsContainer::iterator it(_ReceivedDynStrings.find(dynStringId));
884 if (it != _ReceivedDynStrings.end())
886 result = it->second.String;
888 else
890 result = "DYNSTR = " + toString(dynStringId);
892 return true;
894 else
896 TDynStringsContainer::iterator it(_ReceivedDynStrings.find(dynStringId));
897 if (it != _ReceivedDynStrings.end())
899 // ok, we have the string with all the parts.
900 if (ClientCfg.DebugStringManager)
902 char tmp[1024];
903 sprintf(tmp, "<DYNSTR %u>", dynStringId);
904 result = tmp + it->second.String;
906 else
907 result = it->second.String;
909 // security/antiloop checking
910 it = _WaitingDynStrings.find(dynStringId);
911 if (it != _WaitingDynStrings.end())
913 nlwarning("CStringManager::getDynString : the string %u is received but still in _WaintingDynStrings !", dynStringId);
914 _WaitingDynStrings.erase(it);
917 return true;
919 else
921 // check to see if the string is available now.
922 it = _WaitingDynStrings.find(dynStringId);
923 if (it == _WaitingDynStrings.end())
925 if (ClientCfg.DebugStringManager)
927 nlwarning("DynStringID %u is unknown !", dynStringId);
928 char tmp[1024];
929 sprintf(tmp, "<UNKNOWN DYNSTR %u>", dynStringId);
930 result = tmp;
932 else
933 result.erase(); //_WaitString;
934 return false;
936 if (buildDynString(it->second))
938 if (ClientCfg.DebugStringManager)
940 char tmp[1024];
941 sprintf(tmp, "<DYNSTR %u>", dynStringId);
942 result = tmp + it->second.String;
944 else
945 result = it->second.String;
946 _ReceivedDynStrings.insert(std::make_pair(dynStringId, it->second));
947 _WaitingDynStrings.erase(it);
949 return true;
951 if (ClientCfg.DebugStringManager)
953 char tmp[1024];
954 sprintf(tmp, "<WAIT DYNSTR %u>", dynStringId);
955 result = tmp;
957 else
958 result.erase(); // = _WaitString;
959 return false;
964 // Tool fct to lookup a reference file
965 static string lookupReferenceFile(const string &fileName)
967 string referenceFile;
968 // special location for the "wk" language
969 if(ClientCfg.LanguageCode=="wk")
971 referenceFile = "./translation/translated/"+CFile::getFilename(fileName);
972 if (!CFile::fileExists(referenceFile))
974 nlwarning("Translation file: %s not found", referenceFile.c_str());
975 referenceFile.clear();
978 else
980 referenceFile = CPath::lookup(fileName, false);
983 return referenceFile;
986 void CLoadProxy::loadStringFile(const string &filename, ucstring &text) // TODO: UTF-8 (serial)
988 vector<TStringInfo> reference;
989 vector<TStringInfo> addition;
990 vector<TStringInfo> diff;
992 // get the correct path name of the ref file
993 string referenceFile= lookupReferenceFile(filename);
995 // load the reference file
996 if (!referenceFile.empty())
998 STRING_MANAGER::loadStringFile(referenceFile, reference, false);
1001 // try to find the working file
1002 string workingFile("./translation/work/"+CFile::getFilename(filename));
1003 if (CFile::fileExists(workingFile))
1005 STRING_MANAGER::loadStringFile(workingFile, addition, false);
1007 else
1009 // no addition file ? just copy the reference into addition
1010 addition = reference;
1013 TStringDiffContext context(addition, reference, diff);
1014 TStringDiff differ;
1015 differ.makeDiff(this, context);
1017 text.erase();
1018 text = prepareStringFile(context.Diff, true);
1021 void CLoadProxy::onEquivalent(uint addIndex, uint /* refIndex */, TStringDiffContext &context)
1023 context.Diff.push_back(context.Addition[addIndex]);
1025 void CLoadProxy::onAdd(uint addIndex, uint /* refIndex */, TStringDiffContext &context)
1027 context.Diff.push_back(context.Addition[addIndex]);
1028 //nldebug("Adding new string '%s' in CI18N", context.Addition[addIndex].Identifier.c_str());
1029 if (ClientCfg.DebugStringManager)
1030 context.Diff.back().Text = ucstring("<NEW>")+context.Diff.back().Text; // TODO: UTF-8 (serial)
1032 void CLoadProxy::onRemove(uint /* addIndex */, uint /* refIndex */, TStringDiffContext &/* context */)
1034 // nothing to do because we don't insert bad value
1036 void CLoadProxy::onChanged(uint addIndex, uint /* refIndex */, TStringDiffContext &context)
1038 // we use the addition value in this case
1039 context.Diff.push_back(context.Addition[addIndex]);
1040 //nldebug("Using changed string '%s' in CI18N", context.Addition[addIndex].Identifier.c_str());
1041 if (ClientCfg.DebugStringManager)
1042 context.Diff.back().Text = ucstring("<CHG>")+context.Diff.back().Text; // TODO: UTF-8 (serial)
1044 void CLoadProxy::onSwap(uint /* newIndex */, uint /* refIndex */, TStringDiffContext &/* context */)
1046 // don't swap.
1049 // ***************************************************************************
1051 The readed for skill_word_en.txt for example. Do all the job of Diffs with work and translated dirs.
1053 class CReadWorkSheetFile : public TWorkSheetDiff::IDiffCallback
1055 public:
1056 void readWorkSheetFile(const string &filename, ucstring &text) // TODO: UTF-8 (serial)
1058 TWorksheet addition;
1059 TWorksheet reference;
1060 TWorksheet diff;
1062 // get the correct path name of the ref file
1063 string referenceFile= lookupReferenceFile(filename);
1065 // load the reference file
1066 if (!referenceFile.empty())
1068 STRING_MANAGER::loadExcelSheet(referenceFile, reference);
1069 STRING_MANAGER::makeHashCode(reference, false);
1072 // try to find the working file
1073 string workingFile("./translation/work/"+CFile::getFilename(filename));
1074 if (CFile::fileExists(workingFile))
1076 STRING_MANAGER::loadExcelSheet(workingFile, addition);
1077 STRING_MANAGER::makeHashCode(addition, true);
1079 else
1081 text = prepareExcelSheet(reference);
1082 return;
1085 if (addition.size() == 0)
1086 return;
1088 // check column consistency.
1089 bool doDiff = true;
1090 uint i;
1091 if (reference.ColCount != addition.ColCount)
1093 nlwarning("Can't check for difference for file %s, column number is not the same!", filename.c_str());
1094 doDiff = false;
1096 if (doDiff)
1098 // check eachg column name
1099 for (i=0; i<addition.ColCount; ++i)
1101 if (addition.getData(0, i) != reference.getData(0, i))
1103 nlwarning("Can't check difference for file %s, collumn name differ !", filename.c_str());
1104 doDiff = false;
1105 break;
1108 if (doDiff)
1110 // create the column header.
1111 while (diff.ColCount < addition.ColCount)
1112 diff.insertColumn(0);
1113 diff.push_back(*addition.begin());
1114 TWordsDiffContext context(addition, reference, diff);
1115 TWorkSheetDiff differ;
1116 differ.makeDiff(this, context, true);
1120 if (!doDiff)
1122 diff = reference;
1125 text = prepareExcelSheet(diff);
1128 void onEquivalent(uint /* addIndex */, uint refIndex, TWordsDiffContext &context)
1130 context.Diff.push_back(context.Reference[refIndex]);
1132 void onAdd(uint addIndex, uint /* refIndex */, TWordsDiffContext &context)
1134 context.Diff.push_back(context.Addition[addIndex]);
1135 nlinfo("Using newly sheet row %s", context.Diff.getData(context.Diff.size()-1, 1).toString().c_str());
1137 void onRemove(uint /* addIndex */, uint /* refIndex */, TWordsDiffContext &/* context */)
1139 // nothing to do because we don't insert bad value
1141 void onChanged(uint addIndex, uint /* refIndex */, TWordsDiffContext &context)
1143 context.Diff.push_back(context.Addition[addIndex]);
1144 nlinfo("Using changed sheet row %s", context.Diff.getData(context.Diff.size()-1, 1).toString().c_str());
1146 void onSwap(uint /* newIndex */, uint /* refIndex */, TWordsDiffContext &/* context */)
1148 // don't swap.
1152 // ***************************************************************************
1153 const string StringClientPackedFileName= "./save/string_client.pack";
1154 // Must Increment this number if change are made to the code (else change not taken into account)
1155 const uint StringClientPackedVersion= 0;
1156 bool CStringManagerClient::checkWordFileDates(vector<CFileCheck> &fileChecks, const std::vector<string> &fileNames, const string &languageCode)
1158 fileChecks.resize(fileNames.size());
1160 // **** First get the date of all the .txt files.
1161 for(uint i=0;i<fileChecks.size();i++)
1163 // get the correct path name of the ref file
1164 string referenceFile= lookupReferenceFile(fileNames[i]);
1165 fileChecks[i].ReferenceDate= referenceFile.empty()?0:CFile::getFileModificationDate(referenceFile);
1167 // get then one of the working File (NB: 0 is a valid reponse for Final Client: no working file)
1168 string workingFile("./translation/work/"+CFile::getFilename(fileNames[i]));
1169 fileChecks[i].AdditionDate= CPath::exists(workingFile)?CFile::getFileModificationDate(workingFile):0;
1173 // **** Compare with the packed file
1174 CIFile packFile;
1175 if(packFile.open(StringClientPackedFileName))
1177 CPackHeader packHeader;
1178 packFile.serial(packHeader);
1179 // Compare!
1180 return fileChecks == packHeader.FileChecks &&
1181 languageCode == packHeader.LanguageCode &&
1182 StringClientPackedVersion == packHeader.PackedVersion;
1184 else
1185 // must rebuild!
1186 return false;
1190 // ***************************************************************************
1191 void CStringManagerClient::initI18NSpecialWords(const string &languageCode)
1193 ucstring womenNameColIdent = ucstring("women_name"); // TODO: UTF-8 (serial)
1194 ucstring descColIdent = ucstring("description"); // TODO: UTF-8 (serial)
1195 ucstring descColIdent2 = ucstring("description2"); // TODO: UTF-8 (serial)
1197 // List of words to append to the local CI18N system.
1198 static const char *specialWords[]=
1200 // The first is the name of the file. Second is the Key column identifier.
1201 // 3rd is the extenstion to add to the key for correct matchup (sheet cases)
1202 "skill", "skill ID", "",
1203 "faction", "faction", "",
1204 "place", "placeId", "",
1205 "item", "item ID", ".sitem",
1206 "creature", "creature ID", ".creature",
1207 "sbrick", "sbrick ID", ".sbrick",
1208 "sphrase", "sphrase ID", ".sphrase",
1209 "title", "title_id", "",
1210 "classificationtype", "classification_type", "",
1211 "outpost", "outpost ID", "",
1213 uint numSpecialWords= sizeof(specialWords) / (3*sizeof(specialWords[0]));
1215 // Build the file names
1216 vector<string> fileNames;
1217 fileNames.resize(numSpecialWords);
1218 for(uint i=0;i<numSpecialWords;i++)
1220 fileNames[i]= string(specialWords[i*3+0]) + "_words_" + languageCode + ".txt";
1223 // **** Check the file modification dates.
1224 vector<CFileCheck> fileChecks;
1225 bool mustRebuild= !checkWordFileDates(fileChecks, fileNames, languageCode);
1227 // **** rebuild or load?
1228 if(mustRebuild)
1230 for(uint i=0;i<numSpecialWords;i++)
1232 uint32 profile0= (uint32)ryzomGetLocalTime();
1234 ucstring ucs; // TODO: UTF-8 (serial)
1235 string fileName = fileNames[i];
1236 string keyExtenstion = specialWords[i*3+2];
1238 // read the ucstring and make diffs with data in ./translation/work. // TODO: UTF-8 (serial)
1239 CReadWorkSheetFile rwsf;
1240 rwsf.readWorkSheetFile(fileName, ucs);
1241 if(ucs.empty())
1242 continue;
1244 // transform the text into a WorkSheet.
1245 TWorksheet ws;
1246 STRING_MANAGER::readExcelSheet(ucs, ws);
1248 // Get the Key and Data ColIndex.
1249 uint nameColIndex = 0, keyColIndex = 0;
1250 if( !ws.findCol(ucstring("name"), nameColIndex) ) // TODO: UTF-8 (serial)
1251 continue;
1252 if( !ws.findCol(ucstring(specialWords[i*3+1]), keyColIndex) ) // TODO: UTF-8 (serial)
1253 continue;
1255 // Get the women name index if possible.
1256 uint womenNameColIndex = std::numeric_limits<uint>::max();
1257 if( !ws.findCol(womenNameColIdent, womenNameColIndex) )
1258 womenNameColIndex= std::numeric_limits<uint>::max();
1260 // Get the description index if possible.
1261 uint descColIndex = std::numeric_limits<uint>::max();
1262 if( !ws.findCol(descColIdent, descColIndex) )
1263 descColIndex= std::numeric_limits<uint>::max();
1264 uint descColIndex2 = std::numeric_limits<uint>::max();
1265 if( !ws.findCol(descColIdent2, descColIndex2) )
1266 descColIndex2= std::numeric_limits<uint>::max();
1268 // For all rows minus the first header one.
1269 for(uint j=1;j<ws.size();j++)
1271 // Get the key and name string.
1272 string key= ws.getData(j, keyColIndex).toUtf8(); // FIXME: const string & when UTF-8
1273 string name= ws.getData(j, nameColIndex).toUtf8(); // FIXME: const string & when UTF-8
1274 // Append to the I18N.
1275 // avoid case problems
1276 string keyStr = NLMISC::toLowerAscii(key);
1278 // append the special key extension.
1279 keyStr+= keyExtenstion;
1281 // insert in map.
1282 std::map<string, CItem>::iterator it;
1283 it= _SpecItem_TempMap.find( keyStr );
1284 if ( it!=_SpecItem_TempMap.end() )
1286 nlwarning("Error in initI18NSpecialWords(), %s already exist (not replaced)!", keyStr.c_str());
1288 else
1290 _SpecItem_TempMap[keyStr].Name= name;
1291 // replace all \n in the desc with true \n
1292 while(strFindReplace(_SpecItem_TempMap[keyStr].Name, "\\n", "\n"));
1294 // insert in map of Women Name if OK.
1295 if(womenNameColIndex!=std::numeric_limits<uint>::max())
1297 const ucstring &womenName= ws.getData(j, womenNameColIndex); // TODO: UTF-8 (serial)
1298 _SpecItem_TempMap[keyStr].WomenName= womenName.toUtf8();
1299 // replace all \n in the women name with true \n
1300 while(strFindReplace(_SpecItem_TempMap[keyStr].WomenName, "\\n", "\n"));
1303 // insert in map of Description if OK.
1304 if(descColIndex!=std::numeric_limits<uint>::max())
1306 const ucstring &desc= ws.getData(j, descColIndex); // TODO: UTF-8 (serial)
1307 _SpecItem_TempMap[keyStr].Desc= desc.toUtf8();
1308 // replace all \n in the desc with true \n
1309 while(strFindReplace(_SpecItem_TempMap[keyStr].Desc, "\\n", "\n"));
1312 // insert in map of Description2 if OK.
1313 if(descColIndex2!=std::numeric_limits<uint>::max())
1315 const ucstring &desc= ws.getData(j, descColIndex2); // TODO: UTF-8 (serial)
1316 _SpecItem_TempMap[keyStr].Desc2= desc.toUtf8();
1317 // replace all \n in the desc with true \n
1318 while(strFindReplace(_SpecItem_TempMap[keyStr].Desc2, "\\n", "\n"));
1323 nlinfo ("%d seconds for I18N words: Total: %s", (uint32)(ryzomGetLocalTime()-profile0+500)/1000, fileName.c_str());
1326 else
1328 CIFile packFile;
1329 nlverify(packFile.open(StringClientPackedFileName));
1331 // Serial the header
1332 CPackHeader packHeader;
1333 packFile.serial(packHeader);
1335 // Serial the map
1336 packFile.serialCont(_SpecItem_TempMap);
1339 // **** Save if rebuilt
1340 if(mustRebuild)
1342 COFile packFile;
1343 if(packFile.open(StringClientPackedFileName))
1345 // Write the header
1346 CPackHeader packHeader;
1347 packHeader.LanguageCode= languageCode;
1348 packHeader.FileChecks= fileChecks;
1349 packHeader.PackedVersion= StringClientPackedVersion;
1350 packFile.serial(packHeader);
1352 // Serial the map
1353 packFile.serialCont(_SpecItem_TempMap);
1358 // ***************************************************************************
1359 void CStringManagerClient::specialWordsMemoryCompress()
1361 // Convert map to the light structure
1363 // Count
1364 uint32 nNbEntries = 0, nLabelSize = 0, nNameDescSize = 0;
1365 map<string, CItem>::iterator it = _SpecItem_TempMap.begin();
1366 while (it != _SpecItem_TempMap.end())
1368 nNbEntries++;
1369 nLabelSize += (uint32)it->first.size() + 1;
1370 nNameDescSize += (uint32)it->second.Name.size() + 1;
1371 nNameDescSize += (uint32)it->second.WomenName.size() + 1;
1372 nNameDescSize += (uint32)it->second.Desc.size() + 1;
1373 nNameDescSize += (uint32)it->second.Desc2.size() + 1;
1374 it++;
1377 // Make big strings
1378 _SpecItems.resize(nNbEntries);
1379 _SpecItem_Labels = new char[nLabelSize];
1380 _SpecItem_NameDesc = new char[nNameDescSize];
1382 nNbEntries = 0;
1383 nLabelSize = 0;
1384 nNameDescSize = 0;
1385 it = _SpecItem_TempMap.begin();
1386 while (it != _SpecItem_TempMap.end())
1388 if (NLMISC::startsWith(it->first.c_str(), "bf"))
1390 uint nDbg = 0;
1391 nDbg++;
1394 _SpecItems[nNbEntries].Label = _SpecItem_Labels+nLabelSize;
1395 strcpy(_SpecItem_Labels+nLabelSize, it->first.c_str());
1396 nLabelSize += (uint32)it->first.size() + 1;
1398 _SpecItems[nNbEntries].Name = _SpecItem_NameDesc+nNameDescSize;
1399 strcpy(_SpecItem_NameDesc+nNameDescSize, it->second.Name.c_str());
1400 nNameDescSize += (uint32)it->second.Name.size() + 1;
1402 _SpecItems[nNbEntries].WomenName = _SpecItem_NameDesc+nNameDescSize;
1403 strcpy(_SpecItem_NameDesc+nNameDescSize, it->second.WomenName.c_str());
1404 nNameDescSize += (uint32)it->second.WomenName.size() + 1;
1406 _SpecItems[nNbEntries].Desc = _SpecItem_NameDesc+nNameDescSize;
1407 strcpy(_SpecItem_NameDesc+nNameDescSize, it->second.Desc.c_str());
1408 nNameDescSize += (uint32)it->second.Desc.size() + 1;
1410 _SpecItems[nNbEntries].Desc2 = _SpecItem_NameDesc+nNameDescSize;
1411 strcpy(_SpecItem_NameDesc+nNameDescSize, it->second.Desc2.c_str());
1412 nNameDescSize += (uint32)it->second.Desc2.size() + 1;
1414 nNbEntries++;
1415 it++;
1417 // No need to sort the vector because the map was already sorted in the good order
1418 contReset(_SpecItem_TempMap);
1419 _SpecItem_MemoryCompressed = true;
1422 // ***************************************************************************
1423 const char *CStringManagerClient::getSpecialWord(const string &label, bool women)
1425 if (label.empty())
1427 static string emptyString;
1428 return emptyString.c_str();
1431 if (label[0] == '#')
1433 return getLocalizedName(label.substr(1, label.size()-1));
1436 // avoid case problems
1437 static string lwrLabel;
1438 lwrLabel = toLowerAscii(label);
1440 if (_SpecItem_MemoryCompressed)
1442 CItemLight tmp;
1443 tmp.Label = (char*)lwrLabel.c_str();
1444 vector<CItemLight>::iterator it = lower_bound(_SpecItems.begin(), _SpecItems.end(), tmp, CItemLightComp());
1446 if (it != _SpecItems.end())
1448 if (strcmp(it->Label, lwrLabel.c_str()) == 0)
1450 if( UseFemaleTitles && women )
1452 if( !it->WomenName[0] )
1453 return it->WomenName;
1455 return it->Name;
1459 else
1461 map<string,CItem>::iterator it = _SpecItem_TempMap.find(lwrLabel);
1462 if (it != _SpecItem_TempMap.end())
1464 if( UseFemaleTitles && women )
1465 if (!it->second.WomenName.empty())
1466 return it->second.WomenName.c_str();
1467 return it->second.Name.c_str();
1471 static string badString;
1472 badString = "<NotExist:" + lwrLabel + ">";
1473 return badString.c_str();
1476 // ***************************************************************************
1477 const char *CStringManagerClient::getSpecialDesc(const string &label)
1479 static string emptyString;
1480 if (label.empty())
1481 return emptyString.c_str();
1483 // avoid case problems
1484 static string lwrLabel;
1485 lwrLabel = toLowerAscii(label);
1487 if (_SpecItem_MemoryCompressed)
1489 CItemLight tmp;
1490 tmp.Label = lwrLabel.c_str();
1491 vector<CItemLight>::iterator it = lower_bound(_SpecItems.begin(), _SpecItems.end(), tmp, CItemLightComp());
1493 if (it != _SpecItems.end())
1495 if (strcmp(it->Label, lwrLabel.c_str()) == 0)
1496 return it->Desc;
1499 else
1501 map<string,CItem>::iterator it = _SpecItem_TempMap.find(lwrLabel);
1502 if (it != _SpecItem_TempMap.end())
1503 return it->second.Desc.c_str();
1506 return emptyString.c_str();
1509 // ***************************************************************************
1510 const char *CStringManagerClient::getSpecialDesc2(const string &label)
1512 static string emptyString;
1513 if (label.empty())
1514 return emptyString.c_str();
1516 // avoid case problems
1517 static string lwrLabel;
1518 lwrLabel = toLowerAscii(label);
1520 if (_SpecItem_MemoryCompressed)
1522 CItemLight tmp;
1523 tmp.Label = lwrLabel.c_str();
1524 vector<CItemLight>::iterator it = lower_bound(_SpecItems.begin(), _SpecItems.end(), tmp, CItemLightComp());
1526 if (it != _SpecItems.end())
1528 if (strcmp(it->Label, lwrLabel.c_str()) == 0)
1529 return it->Desc2;
1532 else
1534 map<string,CItem>::iterator it = _SpecItem_TempMap.find(lwrLabel);
1535 if (it != _SpecItem_TempMap.end())
1537 return it->second.Desc2.c_str();
1541 return emptyString.c_str();
1544 // ***************************************************************************
1545 /*const ucchar *CStringManagerClient::getBrickLocalizedName(BRICK_FAMILIES::TBrickFamily e)
1547 return getSpecialWord(BRICK_FAMILIES::toString(e));
1551 // ***************************************************************************
1552 const char *CStringManagerClient::getPlaceLocalizedName(const string &placeNameID)
1554 return getSpecialWord(placeNameID);
1557 // ***************************************************************************
1558 const char *CStringManagerClient::getFactionLocalizedName(const string &factionNameID)
1560 return getSpecialWord(factionNameID);
1563 // ***************************************************************************
1564 const char *CStringManagerClient::getSkillLocalizedName(SKILLS::ESkills e)
1566 return getSpecialWord(SKILLS::toString(e));
1569 // ***************************************************************************
1570 const char *CStringManagerClient::getItemLocalizedName(CSheetId id)
1572 return getSpecialWord(id.toString());
1575 // ***************************************************************************
1576 const char *CStringManagerClient::getCreatureLocalizedName(NLMISC::CSheetId id)
1578 return getSpecialWord(id.toString());
1581 // ***************************************************************************
1582 const char *CStringManagerClient::getSBrickLocalizedName(NLMISC::CSheetId id)
1584 return getSpecialWord(id.toString());
1587 // ***************************************************************************
1588 const char *CStringManagerClient::getSPhraseLocalizedName(NLMISC::CSheetId id)
1590 return getSpecialWord(id.toString());
1593 // ***************************************************************************
1594 /*const char *CStringManagerClient::getBrickLocalizedDescription(BRICK_FAMILIES::TBrickFamily e)
1596 return getSpecialDesc(BRICK_FAMILIES::toString(e));
1599 // ***************************************************************************
1600 const char *CStringManagerClient::getSkillLocalizedDescription(SKILLS::ESkills e)
1602 return getSpecialDesc(SKILLS::toString(e));
1605 // ***************************************************************************
1606 const char *CStringManagerClient::getItemLocalizedDescription(CSheetId id)
1608 return getSpecialDesc(id.toString());
1611 // ***************************************************************************
1612 const char *CStringManagerClient::getSBrickLocalizedDescription(NLMISC::CSheetId id)
1614 return getSpecialDesc(id.toString());
1617 // ***************************************************************************
1618 const char *CStringManagerClient::getSBrickLocalizedCompositionDescription(NLMISC::CSheetId id)
1620 return getSpecialDesc2(id.toString());
1623 // ***************************************************************************
1624 const char *CStringManagerClient::getSPhraseLocalizedDescription(NLMISC::CSheetId id)
1626 return getSpecialDesc(id.toString());
1629 // ***************************************************************************
1630 const char *CStringManagerClient::getTitleLocalizedName(const string &titleId, bool women)
1632 vector<string> listInfos = getTitleInfos(titleId, women);
1634 if (!listInfos.empty())
1636 _TitleWords.push_back(listInfos[0]);
1637 return getLocalizedName(_TitleWords.back());
1640 return getLocalizedName(titleId);
1644 const char *CStringManagerClient::getLocalizedName(const string &uctext)
1646 string text = uctext;
1647 if (text[0] == '[')
1649 vector<string> textLocalizations;
1650 static string defaultText;
1651 splitString(text.substr(1), "[", textLocalizations);
1652 if (!textLocalizations.empty())
1654 for(uint i = 0; i<textLocalizations.size(); i++)
1656 if (textLocalizations[i].substr(0, 3) == CI18N::getCurrentLanguageCode()+"]")
1658 defaultText = textLocalizations[i].substr(3);
1659 return defaultText.c_str();
1661 else if (textLocalizations[i].substr(0, 3) == "wk]")
1663 defaultText = textLocalizations[i].substr(3);
1667 if (!defaultText.empty()) {
1668 return defaultText.c_str();
1671 return uctext.c_str();
1674 // ***************************************************************************
1675 vector<string> CStringManagerClient::getTitleInfos(const string &titleId, bool women)
1677 vector<string> listInfos;
1678 splitString(titleId, string("#"), listInfos);
1680 if (!listInfos.empty())
1682 if (titleId[0] != '#')
1684 // Check special case like SON_OF|jane|joe (with SON_OF = "Son of {1} and {2}")
1685 vector<string> titleReps;
1686 splitString(listInfos[0], string("|"), titleReps);
1687 if (titleReps.size() > 1)
1689 listInfos[0] = getSpecialWord(titleReps[0], women);
1690 for(uint i=1; i < titleReps.size(); ++i )
1692 while(strFindReplace(listInfos[0], toString("{%d}", i), titleReps[i]));
1695 else
1696 listInfos[0] = getSpecialWord(listInfos[0], women);
1700 return listInfos;
1703 // ***************************************************************************
1704 const char *CStringManagerClient::getClassificationTypeLocalizedName(EGSPD::CClassificationType::TClassificationType type)
1706 return getSpecialDesc(EGSPD::CClassificationType::toString(type));
1709 // ***************************************************************************
1710 const char *CStringManagerClient::getOutpostLocalizedName(NLMISC::CSheetId id)
1712 return getSpecialWord(id.toString());
1715 // ***************************************************************************
1716 const char *CStringManagerClient::getOutpostLocalizedDescription(NLMISC::CSheetId id)
1718 return getSpecialDesc(id.toString());
1721 // ***************************************************************************
1722 const char *CStringManagerClient::getOutpostBuildingLocalizedName(NLMISC::CSheetId id)
1724 return getSpecialWord(id.toString());
1727 // ***************************************************************************
1728 const char *CStringManagerClient::getOutpostBuildingLocalizedDescription(NLMISC::CSheetId id)
1730 return getSpecialDesc(id.toString());
1733 // ***************************************************************************
1734 const char *CStringManagerClient::getSquadLocalizedName(NLMISC::CSheetId id)
1736 return getSpecialWord(id.toString());
1739 // ***************************************************************************
1740 const char *CStringManagerClient::getSquadLocalizedDescription(NLMISC::CSheetId id)
1742 return getSpecialDesc(id.toString());
1745 // ***************************************************************************
1746 void CStringManagerClient::replaceDynString(const std::string &name, const std::string &text)
1748 _DynStrings[name] = text;
1752 // ***************************************************************************
1753 void CStringManagerClient::replaceSBrickName(NLMISC::CSheetId id, const std::string &name, const std::string &desc, const std::string &desc2)
1755 string label= id.toString();
1756 if (label.empty())
1758 return;
1761 // avoid case problems
1762 static string lwrLabel;
1763 lwrLabel = toLowerAscii(label);
1765 nlassert(!_SpecItem_MemoryCompressed); // Not allowed, strings are released!
1766 if (_SpecItem_MemoryCompressed)
1768 #if 0
1769 const char *strName = name.c_str();
1770 const char *strDesc = desc.c_str();
1771 const char *strDesc2 = desc2.c_str();
1772 CItemLight tmp;
1773 tmp.Label = lwrLabel.c_str();
1774 vector<CItemLight>::iterator it = lower_bound(_SpecItems.begin(), _SpecItems.end(), tmp, CItemLightComp());
1776 if (it != _SpecItems.end())
1778 if (strcmp(it->Label, lwrLabel.c_str()) == 0)
1780 it->Name = strName;
1781 it->Desc = strDesc;
1782 it->Desc2 = strDesc2;
1784 else
1786 it->Label = tmp.Label;
1787 it->Name = strName;
1788 it->Desc = strDesc;
1789 it->Desc2 = strDesc2;
1792 else
1794 tmp.Name = strName;
1795 tmp.Desc = strDesc;
1796 tmp.Desc2 = strDesc2;
1797 _SpecItems.push_back(tmp);
1799 #endif
1801 else
1803 map<string, CItem>::iterator it(_SpecItem_TempMap.find(lwrLabel));
1804 if (it != _SpecItem_TempMap.end())
1806 it->second.Name= name;
1807 it->second.Desc= desc;
1808 it->second.Desc2= desc2;
1810 else
1812 CItem newItem;
1813 newItem.Name = name;
1814 newItem.Desc = desc;
1815 newItem.Desc2 = desc2;
1816 _SpecItem_TempMap.insert(pair<string,CItem>(lwrLabel,newItem));
1822 } // namespace STRING_MANAGER