Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / string_manager_client.cpp
blobbf26471b906db932987e3f6cc3a8ff41dfe2499e
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "string_manager_client.h"
24 #include "nel/misc/file.h"
25 #include "client_cfg.h"
26 #include "net_manager.h"
27 #include "connection.h"
28 #include "nel/misc/hierarchical_timer.h"
29 #include "nel/misc/algo.h"
30 #include "misc.h"
31 #include "entity_cl.h"
33 #ifdef DEBUG_NEW
34 #define new DEBUG_NEW
35 #endif
37 using namespace std;
38 using namespace NLMISC;
40 namespace STRING_MANAGER
43 // ***************************************************************************
44 map<string, CStringManagerClient::CItem> CStringManagerClient::_SpecItem_TempMap;
45 map<string, string> CStringManagerClient::_DynStrings;
46 vector<string> CStringManagerClient::_TitleWords;
47 bool CStringManagerClient::_SpecItem_MemoryCompressed = false;
48 char *CStringManagerClient::_SpecItem_Labels = NULL;
49 char *CStringManagerClient::_SpecItem_NameDesc = NULL;
50 vector<CStringManagerClient::CItemLight> CStringManagerClient::_SpecItems;
51 bool MustReleaseStaticArrays = true;
54 CStringManagerClient *CStringManagerClient::_Instance= NULL;
55 string CStringManagerClient::_WaitString("???");
58 CStringManagerClient::CStringManagerClient()
60 _CacheInited = false;
61 _CacheLoaded = false;
62 // insert the empty string.
63 _ReceivedStrings.insert(make_pair((uint)EmptyStringId, string()));
64 // reserve some place to avoid reallocation as possible
65 _CacheStringToSave.reserve(1024);
67 // destructor.
68 CStringManagerClient::~CStringManagerClient()
70 if (MustReleaseStaticArrays)
72 if (_SpecItem_MemoryCompressed)
74 delete [] _SpecItem_Labels;
75 _SpecItem_Labels = NULL;
76 delete [] _SpecItem_NameDesc;
77 _SpecItem_NameDesc = NULL;
78 _SpecItems.clear();
79 _SpecItem_MemoryCompressed = false;
85 CStringManagerClient *CStringManagerClient::instance()
87 if (_Instance == 0)
88 _Instance = new CStringManagerClient();
90 return _Instance;
93 void CStringManagerClient::release(bool mustReleaseStaticArrays)
95 if (_Instance != 0)
97 bool prev = MustReleaseStaticArrays;
98 MustReleaseStaticArrays = mustReleaseStaticArrays;
99 delete _Instance;
100 MustReleaseStaticArrays = prev;
101 _Instance =0;
106 void CStringManagerClient::initCache(const std::string &shardId, const std::string &languageCode)
108 H_AUTO( CStringManagerClient_initCache )
110 _ShardId = shardId;
111 _LanguageCode = languageCode;
113 // to be inited, shard id and language code must be filled
114 if (!_ShardId.empty() && !_LanguageCode.empty())
115 _CacheInited = true;
116 else
117 _CacheInited = false;
120 void CStringManagerClient::loadCache(uint32 timestamp)
122 H_AUTO( CStringManagerClient_loadCache )
124 if (_CacheInited)
128 const uint currentVersion = 1;
129 _CacheFilename = std::string("save/") + _ShardId.substr(0, _ShardId.find(":")) + ".string_cache";
131 nlinfo("SM : Try to open the string cache : %s", _CacheFilename.c_str());
133 if (CFile::fileExists(_CacheFilename) && CFile::getFileSize(_CacheFilename))
135 // there is a cache file, check date reset it if needed
137 NLMISC::CIFile file(_CacheFilename);
138 file.setVersionException(false, false);
139 file.serialVersion(currentVersion);
140 if (file.getPos() + sizeof(_Timestamp) > file.getFileSize())
141 _Timestamp = ~timestamp;
142 else
143 file.serial(_Timestamp);
146 if (_Timestamp != timestamp)
148 nlinfo("SM: Clearing string cache : outofdate");
149 // the cache is not sync, reset it
150 NLMISC::COFile file(_CacheFilename);
151 file.serialVersion(currentVersion);
152 file.serial(timestamp);
154 else
156 nlinfo("SM : string cache in sync. cool");
159 else
161 nlinfo("SM: Creating string cache");
162 // cache file don't exist, create it with the timestamp
163 NLMISC::COFile file(_CacheFilename);
164 file.serialVersion(currentVersion);
165 file.serial(timestamp);
168 // clear all current data.
169 _ReceivedStrings.clear();
170 _ReceivedDynStrings.clear();
171 // NB : we keep the waiting strings and dyn strings
173 // insert the empty string.
174 _ReceivedStrings.insert(make_pair((uint)EmptyStringId, string()));
176 // load the cache file
177 NLMISC::CIFile file(_CacheFilename);
178 int version = file.serialVersion(currentVersion);
179 file.serial(_Timestamp);
180 nlassert(_Timestamp == timestamp);
181 nlassert(version >= 1); // Initial version
183 while (!file.eof())
185 uint32 id;
186 string str;
188 file.serial(id);
189 file.serial(str);
191 //nldebug("SM : loading string [%6u] as [%s] in cache", id, str.toString().c_str());
193 _ReceivedStrings.insert(std::make_pair(id, str));
196 _CacheLoaded = true;
198 catch(const NLMISC::Exception &e)
200 nlinfo("SM : loadCache failed, exception : %s", e.what());
201 nlinfo("SM : cache deactivated");
202 // unactivated cache.
203 _CacheFilename.erase();
210 void CStringManagerClient::waitString(uint32 stringId, const IStringWaiterRemover *premover, string *result)
212 H_AUTO( CStringManagerClient_waitString )
214 nlassert(premover && result);
215 string value;
216 if (getString(stringId, value))
217 *result = value;
218 else
220 // wait for the string
221 TStringWaiter sw;
222 sw.Result = result;
223 sw.Remover = premover;
224 _StringsWaiters.insert(std::make_pair(stringId, sw));
228 void CStringManagerClient::waitString(uint32 stringId, IStringWaitCallback *pcallback)
230 H_AUTO( CStringManagerClient_waitString2 )
232 nlassert(pcallback != 0);
233 string value;
234 if (getString(stringId, value))
236 pcallback->onStringAvailable(stringId, value);
238 else
240 // wait for the string
241 _StringsCallbacks.insert(std::make_pair(stringId, pcallback));
246 void CStringManagerClient::waitDynString(uint32 stringId, const IStringWaiterRemover *premover, string *result)
248 H_AUTO( CStringManagerClient_waitDynString )
250 nlassert(premover && result);
251 string value;
252 if (getDynString(stringId, value))
253 *result = value;
254 else
256 // wait for the string
257 TStringWaiter sw;
258 sw.Result = result;
259 sw.Remover = premover;
260 _DynStringsWaiters.insert(std::make_pair(stringId, sw));
264 void CStringManagerClient::waitDynString(uint32 stringId, IStringWaitCallback *pcallback)
266 H_AUTO( CStringManagerClient_waitDynString2 )
268 nlassert(pcallback != 0);
269 string value;
270 if (getDynString(stringId, value))
272 pcallback->onDynStringAvailable(stringId, value);
274 else
276 _DynStringsCallbacks.insert(std::make_pair(stringId, pcallback));
280 void CStringManagerClient::removeStringWaiter(const IStringWaiterRemover *remover)
282 H_AUTO( CStringManagerClient_removeStringWaiter )
284 // search in waiting string
286 restartLoop1:
287 TStringWaitersContainer::iterator first(_StringsWaiters.begin()), last(_StringsWaiters.end());
288 for (; first != last; ++first)
290 if (first->second.Remover == remover)
292 _StringsWaiters.erase(first);
293 goto restartLoop1;
297 // search in waiting dyn string
299 restartLoop2:
300 TStringWaitersContainer::iterator first(_DynStringsWaiters.begin()), last(_DynStringsWaiters.end());
301 for (; first != last; ++first)
303 if (first->second.Remover == remover)
305 _DynStringsWaiters.erase(first);
306 goto restartLoop2;
312 void CStringManagerClient::removeStringWaiter(const IStringWaitCallback *callback)
314 // H_AUTO( CStringManagerClient_removeStringWaiter2 )
316 // search in waiting string
318 restartLoop3:
319 TStringCallbacksContainer::iterator first(_StringsCallbacks.begin()), last(_StringsCallbacks.end());
320 for (; first != last; ++first)
322 if (first->second == callback)
324 _StringsCallbacks.erase(first);
325 goto restartLoop3;
329 // search in waiting dyn string
331 restartLoop4:
332 TStringCallbacksContainer::iterator first(_DynStringsCallbacks.begin()), last(_DynStringsCallbacks.end());
333 for (; first != last; ++first)
335 if (first->second == callback)
337 _DynStringsCallbacks.erase(first);
338 goto restartLoop4;
346 bool CStringManagerClient::getString(uint32 stringId, string &result)
348 H_AUTO( CStringManagerClient_getString )
350 if (ClientCfg.Local)
352 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
353 if (it != _ReceivedStrings.end())
355 result = it->second;
357 else
359 result = "StringID = " + toString(stringId);
362 else
364 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
365 if (it == _ReceivedStrings.end())
367 CHashSet<uint>::iterator it(_WaitingStrings.find(stringId));
368 if (it == _WaitingStrings.end())
370 _WaitingStrings.insert(stringId);
371 // need to ask for this string.
372 NLMISC::CBitMemStream bms;
373 static const string msgType = "STRING_MANAGER:STRING_RQ";
374 if( GenericMsgHeaderMngr.pushNameToStream(msgType,bms) )
376 bms.serial( stringId );
377 NetMngr.push( bms );
378 //nldebug("<CStringManagerClient::getString> sending 'STRING_MANAGER:STRING_RQ' message to server");
380 else
382 nldebug("<CStringManagerClient::getString> unknown message name 'STRING_MANAGER:STRING_RQ'");
386 if (ClientCfg.DebugStringManager)
388 char tmp[1024];
389 sprintf(tmp, "<WAIT STR %u>", stringId);
390 result = tmp;
392 else
393 result.erase(); // = _WaitString;
394 return false;
397 if (ClientCfg.DebugStringManager)
399 char tmp[1024];
400 sprintf(tmp, "<STR %u>", stringId);
401 result = tmp + it->second;
403 else
405 result = it->second;
406 if (result.size() > 9 && result.substr(0, 9) == "<missing:")
408 map<string, string>::iterator itds = _DynStrings.find(result.substr(9, result.size()-10));
409 if (itds != _DynStrings.end())
410 result = itds->second;
415 return true;
418 void CStringManagerClient::receiveString(uint32 stringId, const string &str)
420 H_AUTO( CStringManagerClient_receiveString )
422 //nlinfo("String %u available : [%s]", stringId, str.toString().c_str());
424 CHashSet<uint>::iterator it(_WaitingStrings.find(stringId));
425 if (it != _WaitingStrings.end())
427 _WaitingStrings.erase(it);
429 bool updateCache = true;
430 if (_ReceivedStrings.find(stringId) != _ReceivedStrings.end())
432 TStringsContainer::iterator it(_ReceivedStrings.find(stringId));
433 nlwarning("Receiving stringID %u (%s), already in received string (%s), replacing with new value.",
434 stringId,
435 str.c_str(),
436 it->second.c_str());
438 if (it->second != str)
439 it->second = str;
440 else
441 updateCache = false;
443 else
445 _ReceivedStrings.insert(std::make_pair(stringId, str));
448 if (updateCache)
450 // update the string cache. DON'T SAVE now cause
451 if (_CacheInited && !_CacheFilename.empty())
453 CCacheString cs;
454 cs.StringId= stringId;
455 cs.String= str;
456 _CacheStringToSave.push_back(cs);
460 // update the waiting strings
462 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
463 _StringsWaiters.equal_range(stringId);
465 if (range.first != range.second)
467 for (; range.first != range.second; ++range.first)
469 TStringWaiter &sw = range.first->second;
470 *(sw.Result) = str;
472 _StringsWaiters.erase(stringId);
476 // callback the waiter
478 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
479 _StringsCallbacks.equal_range(stringId);
481 if (range.first != range.second)
483 for (; range.first != range.second; ++range.first)
485 range.first->second->onStringAvailable(stringId, str);
487 _StringsCallbacks.erase(stringId);
492 // try to complete any pending dyn string
494 TDynStringsContainer::iterator first, last;
495 restartLoop:
496 first = _WaitingDynStrings.begin();
497 last = _WaitingDynStrings.end();
498 for (; first != last; ++first)
500 string value;
501 uint number = first->first;
502 /// Warning: if getDynString() return true, 'first' is erased => don't use it after in this loop
503 if (getDynString(number, value))
505 //nlinfo("DynString %u available : [%s]", number, value.toString().c_str());
506 // this dyn string is now complete !
507 // update the waiting dyn strings
509 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
510 _DynStringsWaiters.equal_range(number);
512 if (range.first != range.second)
514 for (; range.first != range.second; ++range.first)
516 TStringWaiter &sw = range.first->second;
517 *(sw.Result) = str;
519 _DynStringsWaiters.erase(number);
522 // callback the waiting dyn strings
524 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
525 _DynStringsCallbacks.equal_range(number);
527 if (range.first != range.second)
529 for (; range.first != range.second; ++range.first)
531 range.first->second->onDynStringAvailable(number, value);
533 _DynStringsCallbacks.erase(number);
536 goto restartLoop;
542 void CStringManagerClient::flushStringCache()
544 if(!_CacheStringToSave.empty())
546 NLMISC::COFile file(_CacheFilename, true);
547 for(uint i=0;i<_CacheStringToSave.size();i++)
549 file.serial(_CacheStringToSave[i].StringId);
550 file.serial(_CacheStringToSave[i].String);
553 _CacheStringToSave.clear();
557 void CStringManagerClient::receiveDynString(NLMISC::CBitMemStream &bms)
559 H_AUTO( CStringManagerClient_receiveDynString )
561 TDynStringInfo dynInfo;
562 dynInfo.Status = TDynStringInfo::received;
563 // read the dynamic string Id
564 uint32 dynId;
565 bms.serial(dynId);
567 /// read the base string Id
568 bms.serial(dynInfo.StringId);
570 // try to build the string
571 dynInfo.Message = bms;
572 buildDynString(dynInfo);
574 if (dynInfo.Status == TDynStringInfo::complete)
576 if (!ClientCfg.Light)
578 //nlinfo("DynString %u available : [%s]", dynId, dynInfo.String.toString().c_str());
581 _ReceivedDynStrings.insert(std::make_pair(dynId, dynInfo));
582 // security, if dynstring Message received twice, it is possible that the dynstring is still in waiting list
583 _WaitingDynStrings.erase(dynId);
585 // update the waiting dyn strings
587 std::pair<TStringWaitersContainer::iterator, TStringWaitersContainer::iterator> range =
588 _DynStringsWaiters.equal_range(dynId);
590 if (range.first != range.second)
592 for (; range.first != range.second; ++range.first)
594 TStringWaiter &sw = range.first->second;
595 *(sw.Result) = dynInfo.String;
597 _DynStringsWaiters.erase(dynId);
600 // callback the waiting dyn strings
602 std::pair<TStringCallbacksContainer::iterator, TStringCallbacksContainer::iterator> range =
603 _DynStringsCallbacks.equal_range(dynId);
605 if (range.first != range.second)
607 for (; range.first != range.second; ++range.first)
609 range.first->second->onDynStringAvailable(dynId, dynInfo.String);
611 _DynStringsCallbacks.erase(dynId);
615 else
616 _WaitingDynStrings.insert(std::make_pair(dynId, dynInfo));
620 bool CStringManagerClient::buildDynString(TDynStringInfo &dynInfo)
622 H_AUTO( CStringManagerClient_buildDynString )
624 if (dynInfo.Status == TDynStringInfo::received)
626 if (!getString(dynInfo.StringId, dynInfo.String))
628 // can't continue now, need the base string.
629 return false;
631 // ok, we have the base string, we can serial the parameters
632 string::iterator first(dynInfo.String.begin()), last(dynInfo.String.end());
633 for (; first != last; ++first)
635 if (*first == '%')
637 first ++;
638 if (first != last && *first != '%')
640 // we have a replacement point.
641 TParamValue param;
642 param.ReplacementPoint = (first-1) - dynInfo.String.begin();
643 switch(*first)
645 case 's':
646 param.Type = string_id;
649 dynInfo.Message.serial(param.StringId);
651 catch(const Exception &)
653 param.StringId = EmptyStringId;
655 break;
656 case 'i':
657 param.Type = integer;
660 dynInfo.Message.serial(param.Integer);
662 catch(const Exception &)
664 param.Integer= 0;
666 break;
667 case 't':
668 param.Type = time;
671 dynInfo.Message.serial(param.Time);
673 catch(const Exception &)
675 param.Time= 0;
677 break;
678 case '$':
679 param.Type = money;
682 dynInfo.Message.serial(param.Money);
684 catch(const Exception &)
686 param.Money= 0;
688 break;
689 case 'm':
690 param.Type = dyn_string_id;
693 dynInfo.Message.serial(param.DynStringId);
695 catch(const Exception &)
697 param.DynStringId= EmptyDynStringId;
699 break;
700 default:
701 nlwarning("Error: unknown replacement tag %%%c", (char)*first);
702 return false;
705 dynInfo.Params.push_back(param);
709 dynInfo.Status = TDynStringInfo::serialized;
712 if (dynInfo.Status == TDynStringInfo::serialized)
714 // try to retreive all string parameter to build the string.
715 string temp;
716 temp.reserve(dynInfo.String.size() * 2);
717 string::iterator src(dynInfo.String.begin());
718 string::iterator move = src;
720 std::vector<TParamValue>::iterator first(dynInfo.Params.begin()), last(dynInfo.Params.end());
721 for (; first != last; ++first)
723 TParamValue &param = *first;
724 switch(param.Type)
726 case string_id:
728 string str;
729 if (!getString(param.StringId, str))
730 return false;
732 ucstring::size_type p1 = str.find('[');
733 if (p1 != ucstring::npos)
735 str = str.substr(0, p1)+STRING_MANAGER::CStringManagerClient::getLocalizedName(str.substr(p1));
738 // If the string is a player name, we may have to remove the shard name (if the string looks like a player name)
739 if(!str.empty() && !PlayerSelectedHomeShardNameWithParenthesis.empty())
741 // fast pre-test
742 if( str[str.size()-1]==')' )
744 // the player name must be at least bigger than the string with ()
745 if(str.size()>PlayerSelectedHomeShardNameWithParenthesis.size())
747 // If the shard name is the same as the player home shard name, remove it
748 uint len= (uint)PlayerSelectedHomeShardNameWithParenthesis.size();
749 uint start= (uint)str.size()-len;
750 if(ucstrnicmp(str, start, len, PlayerSelectedHomeShardNameWithParenthesis)==0) // TODO: NLMISC::compareCaseInsensitive
751 str.resize(start);
756 // If the string contains a title, then remove it
757 string::size_type pos = str.find('$');
758 if ( ! str.empty() && pos != string::npos)
760 str = CEntityCL::removeTitleFromName(str);
763 // append this string
764 temp.append(move, src+param.ReplacementPoint);
765 temp += str;
766 move = dynInfo.String.begin()+param.ReplacementPoint+2;
769 break;
770 case integer:
772 char value[1024];
773 sprintf(value, "%d", param.Integer);
774 temp.append(move, src+param.ReplacementPoint);
775 temp += value;
776 move = dynInfo.String.begin()+param.ReplacementPoint+2;
778 break;
779 case time:
781 string value;
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);
802 temp+=value;
803 move = dynInfo.String.begin()+param.ReplacementPoint+2;
805 break;
806 case money:
807 ///\todo nicoB/Boris : this is a temp patch that display money as integers
809 char value[1024];
810 sprintf(value, "%u", (uint32)param.Money);
811 temp.append(move, src+param.ReplacementPoint);
812 temp += value;
813 move = dynInfo.String.begin()+param.ReplacementPoint+2;
815 // TODO
816 // temp.append(move, src+param.ReplacementPoint);
817 // move = dynInfo.String.begin()+param.ReplacementPoint+2;
818 break;
819 case dyn_string_id:
821 string dynStr;
822 if (!getDynString(param.DynStringId, dynStr))
823 return false;
824 temp.append(move, src+param.ReplacementPoint);
825 temp += dynStr;
826 move = dynInfo.String.begin()+param.ReplacementPoint+2;
828 break;
829 default:
830 nlwarning("Unknown parameter type.");
831 break;
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 '%'
839 ptrdiff_t i =0;
840 while (i < (ptrdiff_t)temp.size())
842 if (temp[i] == 8)
844 // remove the 'delete' char AND the next char
845 temp.erase(i, 2);
847 else if (temp[i] == '%' && i < temp.size()-1 && temp[i+1] == '%')
849 temp.erase(i, 1);
851 else
852 ++i;
856 dynInfo.Status = TDynStringInfo::complete;
857 dynInfo.Message.clear();
858 dynInfo.String = temp;
859 return true;
861 if (dynInfo.Status == TDynStringInfo::complete)
862 return true;
864 nlwarning("Inconsistent dyn string status : %u", dynInfo.Status);
865 return false;
869 bool CStringManagerClient::getDynString(uint32 dynStringId, std::string &result)
871 H_AUTO( CStringManagerClient_getDynString )
873 if (dynStringId == EmptyDynStringId)
874 return true;
875 if (ClientCfg.Local)
877 TDynStringsContainer::iterator it(_ReceivedDynStrings.find(dynStringId));
878 if (it != _ReceivedDynStrings.end())
880 result = it->second.String;
882 else
884 result = "DYNSTR = " + toString(dynStringId);
886 return true;
888 else
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)
896 char tmp[1024];
897 sprintf(tmp, "<DYNSTR %u>", dynStringId);
898 result = tmp + it->second.String;
900 else
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);
911 return true;
913 else
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);
922 char tmp[1024];
923 sprintf(tmp, "<UNKNOWN DYNSTR %u>", dynStringId);
924 result = tmp;
926 else
927 result.erase(); //_WaitString;
928 return false;
930 if (buildDynString(it->second))
932 if (ClientCfg.DebugStringManager)
934 char tmp[1024];
935 sprintf(tmp, "<DYNSTR %u>", dynStringId);
936 result = tmp + it->second.String;
938 else
939 result = it->second.String;
940 _ReceivedDynStrings.insert(std::make_pair(dynStringId, it->second));
941 _WaitingDynStrings.erase(it);
943 return true;
945 if (ClientCfg.DebugStringManager)
947 char tmp[1024];
948 sprintf(tmp, "<WAIT DYNSTR %u>", dynStringId);
949 result = tmp;
951 else
952 result.erase(); // = _WaitString;
953 return false;
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();
972 else
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);
1001 else
1003 // no addition file ? just copy the reference into addition
1004 addition = reference;
1007 TStringDiffContext context(addition, reference, diff);
1008 TStringDiff differ;
1009 differ.makeDiff(this, context);
1011 text.erase();
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 */)
1040 // don't swap.
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
1049 public:
1050 void readWorkSheetFile(const string &filename, ucstring &text) // TODO: UTF-8 (serial)
1052 TWorksheet addition;
1053 TWorksheet reference;
1054 TWorksheet diff;
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);
1073 else
1075 text = prepareExcelSheet(reference);
1076 return;
1079 if (addition.size() == 0)
1080 return;
1082 // check column consistency.
1083 bool doDiff = true;
1084 uint i;
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());
1088 doDiff = false;
1090 if (doDiff)
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());
1098 doDiff = false;
1099 break;
1102 if (doDiff)
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);
1114 if (!doDiff)
1116 diff = reference;
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 */)
1142 // don't swap.
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
1168 CIFile packFile;
1169 if(packFile.open(StringClientPackedFileName))
1171 CPackHeader packHeader;
1172 packFile.serial(packHeader);
1173 // Compare!
1174 return fileChecks == packHeader.FileChecks &&
1175 languageCode == packHeader.LanguageCode &&
1176 StringClientPackedVersion == packHeader.PackedVersion;
1178 else
1179 // must rebuild!
1180 return false;
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?
1222 if(mustRebuild)
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);
1235 if(ucs.empty())
1236 continue;
1238 // transform the text into a WorkSheet.
1239 TWorksheet ws;
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)
1245 continue;
1246 if( !ws.findCol(ucstring(specialWords[i*3+1]), keyColIndex) ) // TODO: UTF-8 (serial)
1247 continue;
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;
1275 // insert in map.
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());
1282 else
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());
1320 else
1322 CIFile packFile;
1323 nlverify(packFile.open(StringClientPackedFileName));
1325 // Serial the header
1326 CPackHeader packHeader;
1327 packFile.serial(packHeader);
1329 // Serial the map
1330 packFile.serialCont(_SpecItem_TempMap);
1333 // **** Save if rebuilt
1334 if(mustRebuild)
1336 COFile packFile;
1337 if(packFile.open(StringClientPackedFileName))
1339 // Write the header
1340 CPackHeader packHeader;
1341 packHeader.LanguageCode= languageCode;
1342 packHeader.FileChecks= fileChecks;
1343 packHeader.PackedVersion= StringClientPackedVersion;
1344 packFile.serial(packHeader);
1346 // Serial the map
1347 packFile.serialCont(_SpecItem_TempMap);
1352 // ***************************************************************************
1353 void CStringManagerClient::specialWordsMemoryCompress()
1355 // Convert map to the light structure
1357 // Count
1358 uint32 nNbEntries = 0, nLabelSize = 0, nNameDescSize = 0;
1359 map<string, CItem>::iterator it = _SpecItem_TempMap.begin();
1360 while (it != _SpecItem_TempMap.end())
1362 nNbEntries++;
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;
1368 it++;
1371 // Make big strings
1372 _SpecItems.resize(nNbEntries);
1373 _SpecItem_Labels = new char[nLabelSize];
1374 _SpecItem_NameDesc = new char[nNameDescSize];
1376 nNbEntries = 0;
1377 nLabelSize = 0;
1378 nNameDescSize = 0;
1379 it = _SpecItem_TempMap.begin();
1380 while (it != _SpecItem_TempMap.end())
1382 if (NLMISC::startsWith(it->first.c_str(), "bf"))
1384 uint nDbg = 0;
1385 nDbg++;
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;
1408 nNbEntries++;
1409 it++;
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)
1419 if (label.empty())
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)
1436 CItemLight tmp;
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;
1449 return it->Name;
1453 else
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;
1474 if (label.empty())
1475 return emptyString.c_str();
1477 // avoid case problems
1478 static string lwrLabel;
1479 lwrLabel = toLowerAscii(label);
1481 if (_SpecItem_MemoryCompressed)
1483 CItemLight tmp;
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)
1490 return it->Desc;
1493 else
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;
1507 if (label.empty())
1508 return emptyString.c_str();
1510 // avoid case problems
1511 static string lwrLabel;
1512 lwrLabel = toLowerAscii(label);
1514 if (_SpecItem_MemoryCompressed)
1516 CItemLight tmp;
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)
1523 return it->Desc2;
1526 else
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;
1641 if (text[0] == '[')
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);
1682 return listInfos;
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();
1738 if (label.empty())
1740 return;
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)
1750 #if 0
1751 const char *strName = name.c_str();
1752 const char *strDesc = desc.c_str();
1753 const char *strDesc2 = desc2.c_str();
1754 CItemLight tmp;
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)
1762 it->Name = strName;
1763 it->Desc = strDesc;
1764 it->Desc2 = strDesc2;
1766 else
1768 it->Label = tmp.Label;
1769 it->Name = strName;
1770 it->Desc = strDesc;
1771 it->Desc2 = strDesc2;
1774 else
1776 tmp.Name = strName;
1777 tmp.Desc = strDesc;
1778 tmp.Desc2 = strDesc2;
1779 _SpecItems.push_back(tmp);
1781 #endif
1783 else
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;
1792 else
1794 CItem newItem;
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