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 // append this string
764 temp
.append(move
, src
+param
.ReplacementPoint
);
766 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
773 sprintf(value
, "%d", param
.Integer
);
774 temp
.append(move
, src
+param
.ReplacementPoint
);
776 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
782 uint32 time
= (uint32
)param
.Time
;
783 if( time
>= (10*60*60) )
785 uint32 nbHours
= time
/ (10*60*60);
786 time
-= nbHours
* 10 * 60 * 60;
787 value
= toString("%d ", nbHours
) + CI18N::get("uiMissionTimerHour") + " ";
789 uint32 nbMinutes
= time
/ (10*60);
790 time
-= nbMinutes
* 10 * 60;
791 value
= value
+ toString("%d ", nbMinutes
) + CI18N::get("uiMissionTimerMinute") + " ";
793 else if( time
>= (10*60) )
795 uint32 nbMinutes
= time
/ (10*60);
796 time
-= nbMinutes
* 10 * 60;
797 value
= value
+ toString("%d ", nbMinutes
) + CI18N::get("uiMissionTimerMinute") + " ";
799 uint32 nbSeconds
= time
/ 10;
800 value
= value
+ toString("%d", nbSeconds
) + CI18N::get("uiMissionTimerSecond");
801 temp
.append(move
, src
+param
.ReplacementPoint
);
803 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
807 ///\todo nicoB/Boris : this is a temp patch that display money as integers
810 sprintf(value
, "%u", (uint32
)param
.Money
);
811 temp
.append(move
, src
+param
.ReplacementPoint
);
813 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
816 // temp.append(move, src+param.ReplacementPoint);
817 // move = dynInfo.String.begin()+param.ReplacementPoint+2;
822 if (!getDynString(param
.DynStringId
, dynStr
))
824 temp
.append(move
, src
+param
.ReplacementPoint
);
826 move
= dynInfo
.String
.begin()+param
.ReplacementPoint
+2;
830 nlwarning("Unknown parameter type.");
834 // append the rest of the string
835 temp
.append(move
, dynInfo
.String
.end());
837 // apply any 'delete' character in the string and replace double '%'
840 while (i
< (ptrdiff_t)temp
.size())
844 // remove the 'delete' char AND the next char
847 else if (temp
[i
] == '%' && i
< temp
.size()-1 && temp
[i
+1] == '%')
856 dynInfo
.Status
= TDynStringInfo::complete
;
857 dynInfo
.Message
.clear();
858 dynInfo
.String
= temp
;
861 if (dynInfo
.Status
== TDynStringInfo::complete
)
864 nlwarning("Inconsistent dyn string status : %u", dynInfo
.Status
);
869 bool CStringManagerClient::getDynString(uint32 dynStringId
, std::string
&result
)
871 H_AUTO( CStringManagerClient_getDynString
)
873 if (dynStringId
== EmptyDynStringId
)
877 TDynStringsContainer::iterator
it(_ReceivedDynStrings
.find(dynStringId
));
878 if (it
!= _ReceivedDynStrings
.end())
880 result
= it
->second
.String
;
884 result
= "DYNSTR = " + toString(dynStringId
);
890 TDynStringsContainer::iterator
it(_ReceivedDynStrings
.find(dynStringId
));
891 if (it
!= _ReceivedDynStrings
.end())
893 // ok, we have the string with all the parts.
894 if (ClientCfg
.DebugStringManager
)
897 sprintf(tmp
, "<DYNSTR %u>", dynStringId
);
898 result
= tmp
+ it
->second
.String
;
901 result
= it
->second
.String
;
903 // security/antiloop checking
904 it
= _WaitingDynStrings
.find(dynStringId
);
905 if (it
!= _WaitingDynStrings
.end())
907 nlwarning("CStringManager::getDynString : the string %u is received but still in _WaintingDynStrings !", dynStringId
);
908 _WaitingDynStrings
.erase(it
);
915 // check to see if the string is available now.
916 it
= _WaitingDynStrings
.find(dynStringId
);
917 if (it
== _WaitingDynStrings
.end())
919 if (ClientCfg
.DebugStringManager
)
921 nlwarning("DynStringID %u is unknown !", dynStringId
);
923 sprintf(tmp
, "<UNKNOWN DYNSTR %u>", dynStringId
);
927 result
.erase(); //_WaitString;
930 if (buildDynString(it
->second
))
932 if (ClientCfg
.DebugStringManager
)
935 sprintf(tmp
, "<DYNSTR %u>", dynStringId
);
936 result
= tmp
+ it
->second
.String
;
939 result
= it
->second
.String
;
940 _ReceivedDynStrings
.insert(std::make_pair(dynStringId
, it
->second
));
941 _WaitingDynStrings
.erase(it
);
945 if (ClientCfg
.DebugStringManager
)
948 sprintf(tmp
, "<WAIT DYNSTR %u>", dynStringId
);
952 result
.erase(); // = _WaitString;
958 // Tool fct to lookup a reference file
959 static string
lookupReferenceFile(const string
&fileName
)
961 string referenceFile
;
962 // special location for the "wk" language
963 if(ClientCfg
.LanguageCode
=="wk")
965 referenceFile
= "./translation/translated/"+CFile::getFilename(fileName
);
966 if (!CFile::fileExists(referenceFile
))
968 nlwarning("Translation file: %s not found", referenceFile
.c_str());
969 referenceFile
.clear();
974 referenceFile
= CPath::lookup(fileName
, false);
977 return referenceFile
;
980 void CLoadProxy::loadStringFile(const string
&filename
, ucstring
&text
) // TODO: UTF-8 (serial)
982 vector
<TStringInfo
> reference
;
983 vector
<TStringInfo
> addition
;
984 vector
<TStringInfo
> diff
;
986 // get the correct path name of the ref file
987 string referenceFile
= lookupReferenceFile(filename
);
989 // load the reference file
990 if (!referenceFile
.empty())
992 STRING_MANAGER::loadStringFile(referenceFile
, reference
, false);
995 // try to find the working file
996 string
workingFile("./translation/work/"+CFile::getFilename(filename
));
997 if (CFile::fileExists(workingFile
))
999 STRING_MANAGER::loadStringFile(workingFile
, addition
, false);
1003 // no addition file ? just copy the reference into addition
1004 addition
= reference
;
1007 TStringDiffContext
context(addition
, reference
, diff
);
1009 differ
.makeDiff(this, context
);
1012 text
= prepareStringFile(context
.Diff
, true);
1015 void CLoadProxy::onEquivalent(uint addIndex
, uint
/* refIndex */, TStringDiffContext
&context
)
1017 context
.Diff
.push_back(context
.Addition
[addIndex
]);
1019 void CLoadProxy::onAdd(uint addIndex
, uint
/* refIndex */, TStringDiffContext
&context
)
1021 context
.Diff
.push_back(context
.Addition
[addIndex
]);
1022 //nldebug("Adding new string '%s' in CI18N", context.Addition[addIndex].Identifier.c_str());
1023 if (ClientCfg
.DebugStringManager
)
1024 context
.Diff
.back().Text
= ucstring("<NEW>")+context
.Diff
.back().Text
; // TODO: UTF-8 (serial)
1026 void CLoadProxy::onRemove(uint
/* addIndex */, uint
/* refIndex */, TStringDiffContext
&/* context */)
1028 // nothing to do because we don't insert bad value
1030 void CLoadProxy::onChanged(uint addIndex
, uint
/* refIndex */, TStringDiffContext
&context
)
1032 // we use the addition value in this case
1033 context
.Diff
.push_back(context
.Addition
[addIndex
]);
1034 //nldebug("Using changed string '%s' in CI18N", context.Addition[addIndex].Identifier.c_str());
1035 if (ClientCfg
.DebugStringManager
)
1036 context
.Diff
.back().Text
= ucstring("<CHG>")+context
.Diff
.back().Text
; // TODO: UTF-8 (serial)
1038 void CLoadProxy::onSwap(uint
/* newIndex */, uint
/* refIndex */, TStringDiffContext
&/* context */)
1043 // ***************************************************************************
1045 The readed for skill_word_en.txt for example. Do all the job of Diffs with work and translated dirs.
1047 class CReadWorkSheetFile
: public TWorkSheetDiff::IDiffCallback
1050 void readWorkSheetFile(const string
&filename
, ucstring
&text
) // TODO: UTF-8 (serial)
1052 TWorksheet addition
;
1053 TWorksheet reference
;
1056 // get the correct path name of the ref file
1057 string referenceFile
= lookupReferenceFile(filename
);
1059 // load the reference file
1060 if (!referenceFile
.empty())
1062 STRING_MANAGER::loadExcelSheet(referenceFile
, reference
);
1063 STRING_MANAGER::makeHashCode(reference
, false);
1066 // try to find the working file
1067 string
workingFile("./translation/work/"+CFile::getFilename(filename
));
1068 if (CFile::fileExists(workingFile
))
1070 STRING_MANAGER::loadExcelSheet(workingFile
, addition
);
1071 STRING_MANAGER::makeHashCode(addition
, true);
1075 text
= prepareExcelSheet(reference
);
1079 if (addition
.size() == 0)
1082 // check column consistency.
1085 if (reference
.ColCount
!= addition
.ColCount
)
1087 nlwarning("Can't check for difference for file %s, column number is not the same!", filename
.c_str());
1092 // check eachg column name
1093 for (i
=0; i
<addition
.ColCount
; ++i
)
1095 if (addition
.getData(0, i
) != reference
.getData(0, i
))
1097 nlwarning("Can't check difference for file %s, collumn name differ !", filename
.c_str());
1104 // create the column header.
1105 while (diff
.ColCount
< addition
.ColCount
)
1106 diff
.insertColumn(0);
1107 diff
.push_back(*addition
.begin());
1108 TWordsDiffContext
context(addition
, reference
, diff
);
1109 TWorkSheetDiff differ
;
1110 differ
.makeDiff(this, context
, true);
1119 text
= prepareExcelSheet(diff
);
1122 void onEquivalent(uint
/* addIndex */, uint refIndex
, TWordsDiffContext
&context
)
1124 context
.Diff
.push_back(context
.Reference
[refIndex
]);
1126 void onAdd(uint addIndex
, uint
/* refIndex */, TWordsDiffContext
&context
)
1128 context
.Diff
.push_back(context
.Addition
[addIndex
]);
1129 nlinfo("Using newly sheet row %s", context
.Diff
.getData(context
.Diff
.size()-1, 1).toString().c_str());
1131 void onRemove(uint
/* addIndex */, uint
/* refIndex */, TWordsDiffContext
&/* context */)
1133 // nothing to do because we don't insert bad value
1135 void onChanged(uint addIndex
, uint
/* refIndex */, TWordsDiffContext
&context
)
1137 context
.Diff
.push_back(context
.Addition
[addIndex
]);
1138 nlinfo("Using changed sheet row %s", context
.Diff
.getData(context
.Diff
.size()-1, 1).toString().c_str());
1140 void onSwap(uint
/* newIndex */, uint
/* refIndex */, TWordsDiffContext
&/* context */)
1146 // ***************************************************************************
1147 const string StringClientPackedFileName
= "./save/string_client.pack";
1148 // Must Increment this number if change are made to the code (else change not taken into account)
1149 const uint StringClientPackedVersion
= 0;
1150 bool CStringManagerClient::checkWordFileDates(vector
<CFileCheck
> &fileChecks
, const std::vector
<string
> &fileNames
, const string
&languageCode
)
1152 fileChecks
.resize(fileNames
.size());
1154 // **** First get the date of all the .txt files.
1155 for(uint i
=0;i
<fileChecks
.size();i
++)
1157 // get the correct path name of the ref file
1158 string referenceFile
= lookupReferenceFile(fileNames
[i
]);
1159 fileChecks
[i
].ReferenceDate
= referenceFile
.empty()?0:CFile::getFileModificationDate(referenceFile
);
1161 // get then one of the working File (NB: 0 is a valid reponse for Final Client: no working file)
1162 string
workingFile("./translation/work/"+CFile::getFilename(fileNames
[i
]));
1163 fileChecks
[i
].AdditionDate
= CPath::exists(workingFile
)?CFile::getFileModificationDate(workingFile
):0;
1167 // **** Compare with the packed file
1169 if(packFile
.open(StringClientPackedFileName
))
1171 CPackHeader packHeader
;
1172 packFile
.serial(packHeader
);
1174 return fileChecks
== packHeader
.FileChecks
&&
1175 languageCode
== packHeader
.LanguageCode
&&
1176 StringClientPackedVersion
== packHeader
.PackedVersion
;
1184 // ***************************************************************************
1185 void CStringManagerClient::initI18NSpecialWords(const string
&languageCode
)
1187 ucstring womenNameColIdent
= ucstring("women_name"); // TODO: UTF-8 (serial)
1188 ucstring descColIdent
= ucstring("description"); // TODO: UTF-8 (serial)
1189 ucstring descColIdent2
= ucstring("description2"); // TODO: UTF-8 (serial)
1191 // List of words to append to the local CI18N system.
1192 static const char *specialWords
[]=
1194 // The first is the name of the file. Second is the Key column identifier.
1195 // 3rd is the extenstion to add to the key for correct matchup (sheet cases)
1196 "skill", "skill ID", "",
1197 "faction", "faction", "",
1198 "place", "placeId", "",
1199 "item", "item ID", ".sitem",
1200 "creature", "creature ID", ".creature",
1201 "sbrick", "sbrick ID", ".sbrick",
1202 "sphrase", "sphrase ID", ".sphrase",
1203 "title", "title_id", "",
1204 "classificationtype", "classification_type", "",
1205 "outpost", "outpost ID", "",
1207 uint numSpecialWords
= sizeof(specialWords
) / (3*sizeof(specialWords
[0]));
1209 // Build the file names
1210 vector
<string
> fileNames
;
1211 fileNames
.resize(numSpecialWords
);
1212 for(uint i
=0;i
<numSpecialWords
;i
++)
1214 fileNames
[i
]= string(specialWords
[i
*3+0]) + "_words_" + languageCode
+ ".txt";
1217 // **** Check the file modification dates.
1218 vector
<CFileCheck
> fileChecks
;
1219 bool mustRebuild
= !checkWordFileDates(fileChecks
, fileNames
, languageCode
);
1221 // **** rebuild or load?
1224 for(uint i
=0;i
<numSpecialWords
;i
++)
1226 uint32 profile0
= (uint32
)ryzomGetLocalTime();
1228 ucstring ucs
; // TODO: UTF-8 (serial)
1229 string fileName
= fileNames
[i
];
1230 string keyExtenstion
= specialWords
[i
*3+2];
1232 // read the ucstring and make diffs with data in ./translation/work. // TODO: UTF-8 (serial)
1233 CReadWorkSheetFile rwsf
;
1234 rwsf
.readWorkSheetFile(fileName
, ucs
);
1238 // transform the text into a WorkSheet.
1240 STRING_MANAGER::readExcelSheet(ucs
, ws
);
1242 // Get the Key and Data ColIndex.
1243 uint nameColIndex
= 0, keyColIndex
= 0;
1244 if( !ws
.findCol(ucstring("name"), nameColIndex
) ) // TODO: UTF-8 (serial)
1246 if( !ws
.findCol(ucstring(specialWords
[i
*3+1]), keyColIndex
) ) // TODO: UTF-8 (serial)
1249 // Get the women name index if possible.
1250 uint womenNameColIndex
= std::numeric_limits
<uint
>::max();
1251 if( !ws
.findCol(womenNameColIdent
, womenNameColIndex
) )
1252 womenNameColIndex
= std::numeric_limits
<uint
>::max();
1254 // Get the description index if possible.
1255 uint descColIndex
= std::numeric_limits
<uint
>::max();
1256 if( !ws
.findCol(descColIdent
, descColIndex
) )
1257 descColIndex
= std::numeric_limits
<uint
>::max();
1258 uint descColIndex2
= std::numeric_limits
<uint
>::max();
1259 if( !ws
.findCol(descColIdent2
, descColIndex2
) )
1260 descColIndex2
= std::numeric_limits
<uint
>::max();
1262 // For all rows minus the first header one.
1263 for(uint j
=1;j
<ws
.size();j
++)
1265 // Get the key and name string.
1266 string key
= ws
.getData(j
, keyColIndex
).toUtf8(); // FIXME: const string & when UTF-8
1267 string name
= ws
.getData(j
, nameColIndex
).toUtf8(); // FIXME: const string & when UTF-8
1268 // Append to the I18N.
1269 // avoid case problems
1270 string keyStr
= NLMISC::toLowerAscii(key
);
1272 // append the special key extension.
1273 keyStr
+= keyExtenstion
;
1276 std::map
<string
, CItem
>::iterator it
;
1277 it
= _SpecItem_TempMap
.find( keyStr
);
1278 if ( it
!=_SpecItem_TempMap
.end() )
1280 nlwarning("Error in initI18NSpecialWords(), %s already exist (not replaced)!", keyStr
.c_str());
1284 _SpecItem_TempMap
[keyStr
].Name
= name
;
1285 // replace all \n in the desc with true \n
1286 while(strFindReplace(_SpecItem_TempMap
[keyStr
].Name
, "\\n", "\n"));
1288 // insert in map of Women Name if OK.
1289 if(womenNameColIndex
!=std::numeric_limits
<uint
>::max())
1291 const ucstring
&womenName
= ws
.getData(j
, womenNameColIndex
); // TODO: UTF-8 (serial)
1292 _SpecItem_TempMap
[keyStr
].WomenName
= womenName
.toUtf8();
1293 // replace all \n in the women name with true \n
1294 while(strFindReplace(_SpecItem_TempMap
[keyStr
].WomenName
, "\\n", "\n"));
1297 // insert in map of Description if OK.
1298 if(descColIndex
!=std::numeric_limits
<uint
>::max())
1300 const ucstring
&desc
= ws
.getData(j
, descColIndex
); // TODO: UTF-8 (serial)
1301 _SpecItem_TempMap
[keyStr
].Desc
= desc
.toUtf8();
1302 // replace all \n in the desc with true \n
1303 while(strFindReplace(_SpecItem_TempMap
[keyStr
].Desc
, "\\n", "\n"));
1306 // insert in map of Description2 if OK.
1307 if(descColIndex2
!=std::numeric_limits
<uint
>::max())
1309 const ucstring
&desc
= ws
.getData(j
, descColIndex2
); // TODO: UTF-8 (serial)
1310 _SpecItem_TempMap
[keyStr
].Desc2
= desc
.toUtf8();
1311 // replace all \n in the desc with true \n
1312 while(strFindReplace(_SpecItem_TempMap
[keyStr
].Desc2
, "\\n", "\n"));
1317 nlinfo ("%d seconds for I18N words: Total: %s", (uint32
)(ryzomGetLocalTime()-profile0
+500)/1000, fileName
.c_str());
1323 nlverify(packFile
.open(StringClientPackedFileName
));
1325 // Serial the header
1326 CPackHeader packHeader
;
1327 packFile
.serial(packHeader
);
1330 packFile
.serialCont(_SpecItem_TempMap
);
1333 // **** Save if rebuilt
1337 if(packFile
.open(StringClientPackedFileName
))
1340 CPackHeader packHeader
;
1341 packHeader
.LanguageCode
= languageCode
;
1342 packHeader
.FileChecks
= fileChecks
;
1343 packHeader
.PackedVersion
= StringClientPackedVersion
;
1344 packFile
.serial(packHeader
);
1347 packFile
.serialCont(_SpecItem_TempMap
);
1352 // ***************************************************************************
1353 void CStringManagerClient::specialWordsMemoryCompress()
1355 // Convert map to the light structure
1358 uint32 nNbEntries
= 0, nLabelSize
= 0, nNameDescSize
= 0;
1359 map
<string
, CItem
>::iterator it
= _SpecItem_TempMap
.begin();
1360 while (it
!= _SpecItem_TempMap
.end())
1363 nLabelSize
+= (uint32
)it
->first
.size() + 1;
1364 nNameDescSize
+= (uint32
)it
->second
.Name
.size() + 1;
1365 nNameDescSize
+= (uint32
)it
->second
.WomenName
.size() + 1;
1366 nNameDescSize
+= (uint32
)it
->second
.Desc
.size() + 1;
1367 nNameDescSize
+= (uint32
)it
->second
.Desc2
.size() + 1;
1372 _SpecItems
.resize(nNbEntries
);
1373 _SpecItem_Labels
= new char[nLabelSize
];
1374 _SpecItem_NameDesc
= new char[nNameDescSize
];
1379 it
= _SpecItem_TempMap
.begin();
1380 while (it
!= _SpecItem_TempMap
.end())
1382 if (NLMISC::startsWith(it
->first
.c_str(), "bf"))
1388 _SpecItems
[nNbEntries
].Label
= _SpecItem_Labels
+nLabelSize
;
1389 strcpy(_SpecItem_Labels
+nLabelSize
, it
->first
.c_str());
1390 nLabelSize
+= (uint32
)it
->first
.size() + 1;
1392 _SpecItems
[nNbEntries
].Name
= _SpecItem_NameDesc
+nNameDescSize
;
1393 strcpy(_SpecItem_NameDesc
+nNameDescSize
, it
->second
.Name
.c_str());
1394 nNameDescSize
+= (uint32
)it
->second
.Name
.size() + 1;
1396 _SpecItems
[nNbEntries
].WomenName
= _SpecItem_NameDesc
+nNameDescSize
;
1397 strcpy(_SpecItem_NameDesc
+nNameDescSize
, it
->second
.WomenName
.c_str());
1398 nNameDescSize
+= (uint32
)it
->second
.WomenName
.size() + 1;
1400 _SpecItems
[nNbEntries
].Desc
= _SpecItem_NameDesc
+nNameDescSize
;
1401 strcpy(_SpecItem_NameDesc
+nNameDescSize
, it
->second
.Desc
.c_str());
1402 nNameDescSize
+= (uint32
)it
->second
.Desc
.size() + 1;
1404 _SpecItems
[nNbEntries
].Desc2
= _SpecItem_NameDesc
+nNameDescSize
;
1405 strcpy(_SpecItem_NameDesc
+nNameDescSize
, it
->second
.Desc2
.c_str());
1406 nNameDescSize
+= (uint32
)it
->second
.Desc2
.size() + 1;
1411 // No need to sort the vector because the map was already sorted in the good order
1412 contReset(_SpecItem_TempMap
);
1413 _SpecItem_MemoryCompressed
= true;
1416 // ***************************************************************************
1417 const char *CStringManagerClient::getSpecialWord(const string
&label
, bool women
)
1421 static string emptyString
;
1422 return emptyString
.c_str();
1425 if (label
[0] == '#')
1427 return getLocalizedName(label
.substr(1, label
.size()-1));
1430 // avoid case problems
1431 static string lwrLabel
;
1432 lwrLabel
= toLowerAscii(label
);
1434 if (_SpecItem_MemoryCompressed
)
1437 tmp
.Label
= (char*)lwrLabel
.c_str();
1438 vector
<CItemLight
>::iterator it
= lower_bound(_SpecItems
.begin(), _SpecItems
.end(), tmp
, CItemLightComp());
1440 if (it
!= _SpecItems
.end())
1442 if (strcmp(it
->Label
, lwrLabel
.c_str()) == 0)
1444 if( UseFemaleTitles
&& women
)
1446 if( !it
->WomenName
[0] )
1447 return it
->WomenName
;
1455 map
<string
,CItem
>::iterator it
= _SpecItem_TempMap
.find(lwrLabel
);
1456 if (it
!= _SpecItem_TempMap
.end())
1458 if( UseFemaleTitles
&& women
)
1459 if (!it
->second
.WomenName
.empty())
1460 return it
->second
.WomenName
.c_str();
1461 return it
->second
.Name
.c_str();
1465 static string badString
;
1466 badString
= "<NotExist:" + lwrLabel
+ ">";
1467 return badString
.c_str();
1470 // ***************************************************************************
1471 const char *CStringManagerClient::getSpecialDesc(const string
&label
)
1473 static string emptyString
;
1475 return emptyString
.c_str();
1477 // avoid case problems
1478 static string lwrLabel
;
1479 lwrLabel
= toLowerAscii(label
);
1481 if (_SpecItem_MemoryCompressed
)
1484 tmp
.Label
= lwrLabel
.c_str();
1485 vector
<CItemLight
>::iterator it
= lower_bound(_SpecItems
.begin(), _SpecItems
.end(), tmp
, CItemLightComp());
1487 if (it
!= _SpecItems
.end())
1489 if (strcmp(it
->Label
, lwrLabel
.c_str()) == 0)
1495 map
<string
,CItem
>::iterator it
= _SpecItem_TempMap
.find(lwrLabel
);
1496 if (it
!= _SpecItem_TempMap
.end())
1497 return it
->second
.Desc
.c_str();
1500 return emptyString
.c_str();
1503 // ***************************************************************************
1504 const char *CStringManagerClient::getSpecialDesc2(const string
&label
)
1506 static string emptyString
;
1508 return emptyString
.c_str();
1510 // avoid case problems
1511 static string lwrLabel
;
1512 lwrLabel
= toLowerAscii(label
);
1514 if (_SpecItem_MemoryCompressed
)
1517 tmp
.Label
= lwrLabel
.c_str();
1518 vector
<CItemLight
>::iterator it
= lower_bound(_SpecItems
.begin(), _SpecItems
.end(), tmp
, CItemLightComp());
1520 if (it
!= _SpecItems
.end())
1522 if (strcmp(it
->Label
, lwrLabel
.c_str()) == 0)
1528 map
<string
,CItem
>::iterator it
= _SpecItem_TempMap
.find(lwrLabel
);
1529 if (it
!= _SpecItem_TempMap
.end())
1531 return it
->second
.Desc2
.c_str();
1535 return emptyString
.c_str();
1538 // ***************************************************************************
1539 /*const ucchar *CStringManagerClient::getBrickLocalizedName(BRICK_FAMILIES::TBrickFamily e)
1541 return getSpecialWord(BRICK_FAMILIES::toString(e));
1545 // ***************************************************************************
1546 const char *CStringManagerClient::getPlaceLocalizedName(const string
&placeNameID
)
1548 return getSpecialWord(placeNameID
);
1551 // ***************************************************************************
1552 const char *CStringManagerClient::getFactionLocalizedName(const string
&factionNameID
)
1554 return getSpecialWord(factionNameID
);
1557 // ***************************************************************************
1558 const char *CStringManagerClient::getSkillLocalizedName(SKILLS::ESkills e
)
1560 return getSpecialWord(SKILLS::toString(e
));
1563 // ***************************************************************************
1564 const char *CStringManagerClient::getItemLocalizedName(CSheetId id
)
1566 return getSpecialWord(id
.toString());
1569 // ***************************************************************************
1570 const char *CStringManagerClient::getCreatureLocalizedName(NLMISC::CSheetId id
)
1572 return getSpecialWord(id
.toString());
1575 // ***************************************************************************
1576 const char *CStringManagerClient::getSBrickLocalizedName(NLMISC::CSheetId id
)
1578 return getSpecialWord(id
.toString());
1581 // ***************************************************************************
1582 const char *CStringManagerClient::getSPhraseLocalizedName(NLMISC::CSheetId id
)
1584 return getSpecialWord(id
.toString());
1587 // ***************************************************************************
1588 /*const char *CStringManagerClient::getBrickLocalizedDescription(BRICK_FAMILIES::TBrickFamily e)
1590 return getSpecialDesc(BRICK_FAMILIES::toString(e));
1593 // ***************************************************************************
1594 const char *CStringManagerClient::getSkillLocalizedDescription(SKILLS::ESkills e
)
1596 return getSpecialDesc(SKILLS::toString(e
));
1599 // ***************************************************************************
1600 const char *CStringManagerClient::getItemLocalizedDescription(CSheetId id
)
1602 return getSpecialDesc(id
.toString());
1605 // ***************************************************************************
1606 const char *CStringManagerClient::getSBrickLocalizedDescription(NLMISC::CSheetId id
)
1608 return getSpecialDesc(id
.toString());
1611 // ***************************************************************************
1612 const char *CStringManagerClient::getSBrickLocalizedCompositionDescription(NLMISC::CSheetId id
)
1614 return getSpecialDesc2(id
.toString());
1617 // ***************************************************************************
1618 const char *CStringManagerClient::getSPhraseLocalizedDescription(NLMISC::CSheetId id
)
1620 return getSpecialDesc(id
.toString());
1623 // ***************************************************************************
1624 const char *CStringManagerClient::getTitleLocalizedName(const string
&titleId
, bool women
)
1626 vector
<string
> listInfos
= getTitleInfos(titleId
, women
);
1628 if (!listInfos
.empty())
1630 _TitleWords
.push_back(listInfos
[0]);
1631 return getLocalizedName(_TitleWords
.back());
1634 return getLocalizedName(titleId
);
1638 const char *CStringManagerClient::getLocalizedName(const string
&uctext
)
1640 string text
= uctext
;
1643 vector
<string
> textLocalizations
;
1644 static string defaultText
;
1645 splitString(text
.substr(1), "[", textLocalizations
);
1646 if (!textLocalizations
.empty())
1648 for(uint i
= 0; i
<textLocalizations
.size(); i
++)
1650 if (textLocalizations
[i
].substr(0, 3) == CI18N::getCurrentLanguageCode()+"]")
1652 defaultText
= textLocalizations
[i
].substr(3);
1653 return defaultText
.c_str();
1655 else if (textLocalizations
[i
].substr(0, 3) == "wk]")
1657 defaultText
= textLocalizations
[i
].substr(3);
1661 if (!defaultText
.empty()) {
1662 return defaultText
.c_str();
1665 return uctext
.c_str();
1668 // ***************************************************************************
1669 vector
<string
> CStringManagerClient::getTitleInfos(const string
&titleId
, bool women
)
1671 vector
<string
> listInfos
;
1672 splitString(titleId
, string("#"), listInfos
);
1674 if (!listInfos
.empty())
1676 if (titleId
[0] != '#')
1678 listInfos
[0] = getSpecialWord(listInfos
[0], women
);
1685 // ***************************************************************************
1686 const char *CStringManagerClient::getClassificationTypeLocalizedName(EGSPD::CClassificationType::TClassificationType type
)
1688 return getSpecialDesc(EGSPD::CClassificationType::toString(type
));
1691 // ***************************************************************************
1692 const char *CStringManagerClient::getOutpostLocalizedName(NLMISC::CSheetId id
)
1694 return getSpecialWord(id
.toString());
1697 // ***************************************************************************
1698 const char *CStringManagerClient::getOutpostLocalizedDescription(NLMISC::CSheetId id
)
1700 return getSpecialDesc(id
.toString());
1703 // ***************************************************************************
1704 const char *CStringManagerClient::getOutpostBuildingLocalizedName(NLMISC::CSheetId id
)
1706 return getSpecialWord(id
.toString());
1709 // ***************************************************************************
1710 const char *CStringManagerClient::getOutpostBuildingLocalizedDescription(NLMISC::CSheetId id
)
1712 return getSpecialDesc(id
.toString());
1715 // ***************************************************************************
1716 const char *CStringManagerClient::getSquadLocalizedName(NLMISC::CSheetId id
)
1718 return getSpecialWord(id
.toString());
1721 // ***************************************************************************
1722 const char *CStringManagerClient::getSquadLocalizedDescription(NLMISC::CSheetId id
)
1724 return getSpecialDesc(id
.toString());
1727 // ***************************************************************************
1728 void CStringManagerClient::replaceDynString(const std::string
&name
, const std::string
&text
)
1730 _DynStrings
[name
] = text
;
1734 // ***************************************************************************
1735 void CStringManagerClient::replaceSBrickName(NLMISC::CSheetId id
, const std::string
&name
, const std::string
&desc
, const std::string
&desc2
)
1737 string label
= id
.toString();
1743 // avoid case problems
1744 static string lwrLabel
;
1745 lwrLabel
= toLowerAscii(label
);
1747 nlassert(!_SpecItem_MemoryCompressed
); // Not allowed, strings are released!
1748 if (_SpecItem_MemoryCompressed
)
1751 const char *strName
= name
.c_str();
1752 const char *strDesc
= desc
.c_str();
1753 const char *strDesc2
= desc2
.c_str();
1755 tmp
.Label
= lwrLabel
.c_str();
1756 vector
<CItemLight
>::iterator it
= lower_bound(_SpecItems
.begin(), _SpecItems
.end(), tmp
, CItemLightComp());
1758 if (it
!= _SpecItems
.end())
1760 if (strcmp(it
->Label
, lwrLabel
.c_str()) == 0)
1764 it
->Desc2
= strDesc2
;
1768 it
->Label
= tmp
.Label
;
1771 it
->Desc2
= strDesc2
;
1778 tmp
.Desc2
= strDesc2
;
1779 _SpecItems
.push_back(tmp
);
1785 map
<string
, CItem
>::iterator
it(_SpecItem_TempMap
.find(lwrLabel
));
1786 if (it
!= _SpecItem_TempMap
.end())
1788 it
->second
.Name
= name
;
1789 it
->second
.Desc
= desc
;
1790 it
->second
.Desc2
= desc2
;
1795 newItem
.Name
= name
;
1796 newItem
.Desc
= desc
;
1797 newItem
.Desc2
= desc2
;
1798 _SpecItem_TempMap
.insert(pair
<string
,CItem
>(lwrLabel
,newItem
));
1804 } // namespace STRING_MANAGER