1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 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/>.
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"
31 #include "entity_cl.h"
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()
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);
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
;
79 _SpecItem_MemoryCompressed
= false;
85 CStringManagerClient
*CStringManagerClient::instance()
88 _Instance
= new CStringManagerClient();
93 void CStringManagerClient::release(bool mustReleaseStaticArrays
)
97 bool prev
= MustReleaseStaticArrays
;
98 MustReleaseStaticArrays
= mustReleaseStaticArrays
;
100 MustReleaseStaticArrays
= prev
;
106 void CStringManagerClient::initCache(const std::string
&shardId
, const std::string
&languageCode
)
108 H_AUTO( CStringManagerClient_initCache
)
111 _LanguageCode
= languageCode
;
113 // to be inited, shard id and language code must be filled
114 if (!_ShardId
.empty() && !_LanguageCode
.empty())
117 _CacheInited
= false;
120 void CStringManagerClient::loadCache(uint32 timestamp
)
122 H_AUTO( CStringManagerClient_loadCache
)
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
;
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
);
156 nlinfo("SM : string cache in sync. cool");
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
191 //nldebug("SM : loading string [%6u] as [%s] in cache", id, str.toString().c_str());
193 _ReceivedStrings
.insert(std::make_pair(id
, str
));
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
);
216 if (getString(stringId
, value
))
220 // wait for the string
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);
234 if (getString(stringId
, value
))
236 pcallback
->onStringAvailable(stringId
, value
);
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
);
252 if (getDynString(stringId
, value
))
256 // wait for the string
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);
270 if (getDynString(stringId
, value
))
272 pcallback
->onDynStringAvailable(stringId
, value
);
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
287 TStringWaitersContainer::iterator
first(_StringsWaiters
.begin()), last(_StringsWaiters
.end());
288 for (; first
!= last
; ++first
)
290 if (first
->second
.Remover
== remover
)
292 _StringsWaiters
.erase(first
);
297 // search in waiting dyn string
300 TStringWaitersContainer::iterator
first(_DynStringsWaiters
.begin()), last(_DynStringsWaiters
.end());
301 for (; first
!= last
; ++first
)
303 if (first
->second
.Remover
== remover
)
305 _DynStringsWaiters
.erase(first
);
312 void CStringManagerClient::removeStringWaiter(const IStringWaitCallback
*callback
)
314 // H_AUTO( CStringManagerClient_removeStringWaiter2 )
316 // search in waiting string
319 TStringCallbacksContainer::iterator
first(_StringsCallbacks
.begin()), last(_StringsCallbacks
.end());
320 for (; first
!= last
; ++first
)
322 if (first
->second
== callback
)
324 _StringsCallbacks
.erase(first
);
329 // search in waiting dyn string
332 TStringCallbacksContainer::iterator
first(_DynStringsCallbacks
.begin()), last(_DynStringsCallbacks
.end());
333 for (; first
!= last
; ++first
)
335 if (first
->second
== callback
)
337 _DynStringsCallbacks
.erase(first
);
346 bool CStringManagerClient::getString(uint32 stringId
, string
&result
)
348 H_AUTO( CStringManagerClient_getString
)
352 TStringsContainer::iterator
it(_ReceivedStrings
.find(stringId
));
353 if (it
!= _ReceivedStrings
.end())
359 result
= "StringID = " + toString(stringId
);
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
);
378 //nldebug("<CStringManagerClient::getString> sending 'STRING_MANAGER:STRING_RQ' message to server");
382 nldebug("<CStringManagerClient::getString> unknown message name 'STRING_MANAGER:STRING_RQ'");
386 if (ClientCfg
.DebugStringManager
)
389 sprintf(tmp
, "<WAIT STR %u>", stringId
);
393 result
.erase(); // = _WaitString;
397 if (ClientCfg
.DebugStringManager
)
400 sprintf(tmp
, "<STR %u>", stringId
);
401 result
= tmp
+ 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
;
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.",
438 if (it
->second
!= str
)
445 _ReceivedStrings
.insert(std::make_pair(stringId
, str
));
450 // update the string cache. DON'T SAVE now cause
451 if (_CacheInited
&& !_CacheFilename
.empty())
454 cs
.StringId
= stringId
;
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
;
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
;
496 first
= _WaitingDynStrings
.begin();
497 last
= _WaitingDynStrings
.end();
498 for (; first
!= last
; ++first
)
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
;
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
);
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
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
);
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.
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
)
638 if (first
!= last
&& *first
!= '%')
640 // we have a replacement point.
642 param
.ReplacementPoint
= (first
-1) - dynInfo
.String
.begin();
646 param
.Type
= string_id
;
649 dynInfo
.Message
.serial(param
.StringId
);
651 catch(const Exception
&)
653 param
.StringId
= EmptyStringId
;
657 param
.Type
= integer
;
660 dynInfo
.Message
.serial(param
.Integer
);
662 catch(const Exception
&)
671 dynInfo
.Message
.serial(param
.Time
);
673 catch(const Exception
&)
682 dynInfo
.Message
.serial(param
.Money
);
684 catch(const Exception
&)
690 param
.Type
= dyn_string_id
;
693 dynInfo
.Message
.serial(param
.DynStringId
);
695 catch(const Exception
&)
697 param
.DynStringId
= EmptyDynStringId
;
701 nlwarning("Error: unknown replacement tag %%%c", (char)*first
);
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.
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
¶m
= *first
;
729 if (!getString(param
.StringId
, str
))
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())
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
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
);
772 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
779 sprintf(value
, "%d", param
.Integer
);
780 temp
.append(move
, src
+param
.ReplacementPoint
);
782 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
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
);
809 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
813 ///\todo nicoB/Boris : this is a temp patch that display money as integers
816 sprintf(value
, "%u", (uint32
)param
.Money
);
817 temp
.append(move
, src
+param
.ReplacementPoint
);
819 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
822 // temp.append(move, src+param.ReplacementPoint);
823 // move = dynInfo.String.begin()+param.ReplacementPoint+2;
828 if (!getDynString(param
.DynStringId
, dynStr
))
830 temp
.append(move
, src
+param
.ReplacementPoint
);
832 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
836 nlwarning("Unknown parameter type.");
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 '%'
846 while (i
< (ptrdiff_t)temp
.size())
850 // remove the 'delete' char AND the next char
853 else if (temp
[i
] == '%' && i
< temp
.size()-1 && temp
[i
+1] == '%')
862 dynInfo
.Status
= TDynStringInfo::complete
;
863 dynInfo
.Message
.clear();
864 dynInfo
.String
= temp
;
867 if (dynInfo
.Status
== TDynStringInfo::complete
)
870 nlwarning("Inconsistent dyn string status : %u", dynInfo
.Status
);
875 bool CStringManagerClient::getDynString(uint32 dynStringId
, std::string
&result
)
877 H_AUTO( CStringManagerClient_getDynString
)
879 if (dynStringId
== EmptyDynStringId
)
883 TDynStringsContainer::iterator
it(_ReceivedDynStrings
.find(dynStringId
));
884 if (it
!= _ReceivedDynStrings
.end())
886 result
= it
->second
.String
;
890 result
= "DYNSTR = " + toString(dynStringId
);
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
)
903 sprintf(tmp
, "<DYNSTR %u>", dynStringId
);
904 result
= tmp
+ it
->second
.String
;
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
);
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
);
929 sprintf(tmp
, "<UNKNOWN DYNSTR %u>", dynStringId
);
933 result
.erase(); //_WaitString;
936 if (buildDynString(it
->second
))
938 if (ClientCfg
.DebugStringManager
)
941 sprintf(tmp
, "<DYNSTR %u>", dynStringId
);
942 result
= tmp
+ it
->second
.String
;
945 result
= it
->second
.String
;
946 _ReceivedDynStrings
.insert(std::make_pair(dynStringId
, it
->second
));
947 _WaitingDynStrings
.erase(it
);
951 if (ClientCfg
.DebugStringManager
)
954 sprintf(tmp
, "<WAIT DYNSTR %u>", dynStringId
);
958 result
.erase(); // = _WaitString;
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();
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);
1009 // no addition file ? just copy the reference into addition
1010 addition
= reference
;
1013 TStringDiffContext
context(addition
, reference
, diff
);
1015 differ
.makeDiff(this, context
);
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 */)
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
1056 void readWorkSheetFile(const string
&filename
, ucstring
&text
) // TODO: UTF-8 (serial)
1058 TWorksheet addition
;
1059 TWorksheet reference
;
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);
1081 text
= prepareExcelSheet(reference
);
1085 if (addition
.size() == 0)
1088 // check column consistency.
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());
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());
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);
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 */)
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
1175 if(packFile
.open(StringClientPackedFileName
))
1177 CPackHeader packHeader
;
1178 packFile
.serial(packHeader
);
1180 return fileChecks
== packHeader
.FileChecks
&&
1181 languageCode
== packHeader
.LanguageCode
&&
1182 StringClientPackedVersion
== packHeader
.PackedVersion
;
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?
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
);
1244 // transform the text into a WorkSheet.
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)
1252 if( !ws
.findCol(ucstring(specialWords
[i
*3+1]), keyColIndex
) ) // TODO: UTF-8 (serial)
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
;
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());
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());
1329 nlverify(packFile
.open(StringClientPackedFileName
));
1331 // Serial the header
1332 CPackHeader packHeader
;
1333 packFile
.serial(packHeader
);
1336 packFile
.serialCont(_SpecItem_TempMap
);
1339 // **** Save if rebuilt
1343 if(packFile
.open(StringClientPackedFileName
))
1346 CPackHeader packHeader
;
1347 packHeader
.LanguageCode
= languageCode
;
1348 packHeader
.FileChecks
= fileChecks
;
1349 packHeader
.PackedVersion
= StringClientPackedVersion
;
1350 packFile
.serial(packHeader
);
1353 packFile
.serialCont(_SpecItem_TempMap
);
1358 // ***************************************************************************
1359 void CStringManagerClient::specialWordsMemoryCompress()
1361 // Convert map to the light structure
1364 uint32 nNbEntries
= 0, nLabelSize
= 0, nNameDescSize
= 0;
1365 map
<string
, CItem
>::iterator it
= _SpecItem_TempMap
.begin();
1366 while (it
!= _SpecItem_TempMap
.end())
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;
1378 _SpecItems
.resize(nNbEntries
);
1379 _SpecItem_Labels
= new char[nLabelSize
];
1380 _SpecItem_NameDesc
= new char[nNameDescSize
];
1385 it
= _SpecItem_TempMap
.begin();
1386 while (it
!= _SpecItem_TempMap
.end())
1388 if (NLMISC::startsWith(it
->first
.c_str(), "bf"))
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;
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
)
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
)
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
;
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
;
1481 return emptyString
.c_str();
1483 // avoid case problems
1484 static string lwrLabel
;
1485 lwrLabel
= toLowerAscii(label
);
1487 if (_SpecItem_MemoryCompressed
)
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)
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
;
1514 return emptyString
.c_str();
1516 // avoid case problems
1517 static string lwrLabel
;
1518 lwrLabel
= toLowerAscii(label
);
1520 if (_SpecItem_MemoryCompressed
)
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)
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
;
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
]));
1696 listInfos
[0] = getSpecialWord(listInfos
[0], women
);
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();
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
)
1769 const char *strName
= name
.c_str();
1770 const char *strDesc
= desc
.c_str();
1771 const char *strDesc2
= desc2
.c_str();
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)
1782 it
->Desc2
= strDesc2
;
1786 it
->Label
= tmp
.Label
;
1789 it
->Desc2
= strDesc2
;
1796 tmp
.Desc2
= strDesc2
;
1797 _SpecItems
.push_back(tmp
);
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
;
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