1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-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/>.
20 /* This class is case unsensitive. It means that you can call build() and
21 * buildIdVector() with string with anycase, it'll work.
26 #include "nel/misc/file.h"
27 #include "nel/misc/path.h"
29 #include "nel/misc/sheet_id.h"
30 #include "nel/misc/common.h"
31 #include "nel/misc/hierarchical_timer.h"
42 CSheetId::CChar
CSheetId::_AllStrings
;
43 CStaticMap
<uint32
, CSheetId::CChar
> CSheetId::_SheetIdToName
;
44 CStaticMap
<CSheetId::CChar
, uint32
, CSheetId::CCharComp
> CSheetId::_SheetNameToId
;
45 //map<uint32,std::string> CSheetId::_SheetIdToName;
46 //map<std::string,uint32> CSheetId::_SheetNameToId;
47 vector
<std::string
> CSheetId::_FileExtensions
;
48 bool CSheetId::_Initialised
= false;
49 bool CSheetId::_RemoveUnknownSheet
= true;
50 bool CSheetId::_DontHaveSheetKnowledge
= false;
51 std::map
<std::string
, uint32
> CSheetId::_DevTypeNameToId
;
52 std::vector
<std::vector
<std::string
>> CSheetId::_DevSheetIdToName
;
53 std::map
<std::string
, uint32
> CSheetId::_DevSheetNameToId
;
55 const CSheetId
CSheetId::Unknown((uint32
)0);
57 void CSheetId::cbFileChange(const std::string
&filename
)
59 nlinfo("SHEETID: %s changed, reload it", filename
.c_str());
64 //-----------------------------------------------
67 //-----------------------------------------------
68 CSheetId::CSheetId(uint32 sheetRef
)
72 #ifdef NL_DEBUG_SHEET_ID
73 // Yoyo: don't access the static map, because of order of static ctor call.
74 // For now, all static CSheetId are 0 (eg: CSheetId::Unknown)
77 TSheetIdToNameMap::iterator
it(_SheetIdToName
.find(sheetRef
));
78 if (it
!= _SheetIdToName
.end())
80 _DebugSheetName
= it
->second
.Ptr
;
83 _DebugSheetName
= NULL
;
87 _DebugSheetName
= NULL
;
92 //-----------------------------------------------
95 //-----------------------------------------------
96 CSheetId::CSheetId(int sheetRef
)
98 _Id
.Id
= (uint32
)sheetRef
;
100 #ifdef NL_DEBUG_SHEET_ID
101 // Yoyo: don't access the static map, because of order of static ctor call.
102 // For now, all static CSheetId are 0 (eg: CSheetId::Unknown)
105 TSheetIdToNameMap::iterator
it(_SheetIdToName
.find(sheetRef
));
106 if (it
!= _SheetIdToName
.end())
108 _DebugSheetName
= it
->second
.Ptr
;
111 _DebugSheetName
= NULL
;
115 _DebugSheetName
= NULL
;
120 //-----------------------------------------------
123 //-----------------------------------------------
124 CSheetId::CSheetId(const string
&sheetName
)
126 if (!buildSheetId(sheetName
.c_str(), sheetName
.size()))
128 if (sheetName
.empty())
129 nlwarning("SHEETID: Try to create an CSheetId with empty name. TODO: check why.");
131 nlwarning("SHEETID: The sheet '%s' is not in sheet_id.bin, setting it to Unknown", sheetName
.c_str());
133 //NLMISC::getCallStack(stack);
134 //std::vector<std::string> contexts;
135 //NLMISC::explode(stack, string("\n"), contexts);
136 //nldebug("Dumping callstack :");
137 //for (uint i=0; i<contexts.size(); ++i)
138 // nldebug(" %3u : %s", i, contexts[i].c_str());
142 // nldebug("LIST_SHEET_ID: %s (%s)", toString().c_str(), sheetName.c_str());
146 //-----------------------------------------------
149 //-----------------------------------------------
150 CSheetId::CSheetId(const char *sheetName
)
152 if (!buildSheetId(sheetName
, strlen(sheetName
)))
155 nlwarning("SHEETID: Try to create an CSheetId with empty name. TODO: check why.");
157 nlwarning("SHEETID: The sheet '%s' is not in sheet_id.bin, setting it to Unknown", sheetName
);
161 // nldebug("LIST_SHEET_ID: %s (%s)", toString().c_str(), sheetName.c_str());
165 CSheetId::CSheetId(const std::string
&sheetName
, const std::string
&defaultType
)
167 // Don't use this function without defaultType, use the one above.
168 nlassert(defaultType
.size() != 0);
170 if (sheetName
.rfind('.') == std::string::npos
)
172 std::string withType
= sheetName
+ "." + defaultType
;
173 *this = CSheetId(withType
);
174 // nldebug("SHEETID: Constructing CSheetId from name '%s' without explicit type, defaulting as '%s' to '%s'", sheetName.c_str(), defaultType.c_str(), withType.c_str());
178 *this = CSheetId(sheetName
);
182 static std::string s_Dot
= ".";
184 CSheetId::CSheetId(const std::string
&sheetName
, const char *defaultType
)
186 // Don't use this function without defaultType, use the one above.
187 nlassert(defaultType
[0]);
189 if (sheetName
.rfind('.') == std::string::npos
)
191 std::string withType
= sheetName
+ s_Dot
+ defaultType
;
192 *this = CSheetId(withType
);
193 // nldebug("SHEETID: Constructing CSheetId from name '%s' without explicit type, defaulting as '%s' to '%s'", sheetName.c_str(), defaultType.c_str(), withType.c_str());
197 *this = CSheetId(sheetName
);
201 CSheetId::CSheetId(const char *sheetName
, const char *defaultType
)
203 // Don't use this function without defaultType, use the one above.
204 nlassert(defaultType
[0]);
206 if (!strchr(sheetName
, '.'))
208 std::string withType
= sheetName
+ s_Dot
+ defaultType
;
209 *this = CSheetId(withType
);
210 // nldebug("SHEETID: Constructing CSheetId from name '%s' without explicit type, defaulting as '%s' to '%s'", sheetName.c_str(), defaultType.c_str(), withType.c_str());
214 *this = CSheetId(sheetName
);
218 //-----------------------------------------------
221 //-----------------------------------------------
222 bool CSheetId::buildSheetId(const char *sheetName
, size_t sheetNameLen
)
224 nlassert(_Initialised
);
226 // When no sheet_id.bin is loaded, use dynamically assigned IDs.
227 if (_DontHaveSheetKnowledge
)
229 std::string sheetNameLc
= toLowerAscii(sheetName
);
230 std::map
<std::string
, uint32
>::iterator it
= _DevSheetNameToId
.find(sheetNameLc
);
231 if (it
== _DevSheetNameToId
.end())
233 // Create a new dynamic sheet ID.
234 // nldebug("SHEETID: Creating a dynamic sheet id for '%s'", sheetName.c_str());
235 std::string sheetType
= CFile::getExtension(sheetNameLc
);
236 std::string sheetName
= CFile::getFilenameWithoutExtension(sheetNameLc
);
237 std::map
<std::string
, uint32
>::iterator tit
= _DevTypeNameToId
.find(sheetType
);
239 if (tit
== _DevTypeNameToId
.end())
241 _FileExtensions
.push_back(sheetType
);
242 _DevSheetIdToName
.push_back(std::vector
<std::string
>());
243 typeId
= (uint32
)_FileExtensions
.size() - 1;
244 _DevTypeNameToId
[sheetType
] = typeId
;
245 std::string unknownNewType
= std::string("unknown." + sheetType
);
246 _DevSheetIdToName
[typeId
].push_back(unknownNewType
);
247 _Id
.IdInfos
.Type
= typeId
;
248 _Id
.IdInfos
.Id
= _DevSheetIdToName
[typeId
].size() - 1;
249 _DevSheetNameToId
[unknownNewType
] = _Id
.Id
;
250 if (sheetName
== "unknown")
251 return true; // Return with the unknown sheet id of this type
255 typeId
= tit
->second
;
256 _Id
.IdInfos
.Type
= typeId
;
258 // Add a new sheet name to the type
259 _DevSheetIdToName
[typeId
].push_back(sheetNameLc
);
260 _Id
.IdInfos
.Id
= _DevSheetIdToName
[typeId
].size() - 1;
261 // nldebug("SHEETID: Type %i, id %i, sheetid %i", _Id.IdInfos.Type, _Id.IdInfos.Id, _Id.Id);
262 _DevSheetNameToId
[sheetNameLc
] = _Id
.Id
;
269 // try looking up the sheet name in _SheetNameToId
270 TSheetNameToIdMap::const_iterator itId
;
273 #pragma warning(push)
274 #pragma warning(disable : 6255)
275 c
.Ptr
= (char *)alloca(sheetNameLen
+ 1);
278 c
.Ptr
= new char[sheetName
.size() + 1];
280 strcpy(c
.Ptr
, sheetName
);
283 itId
= _SheetNameToId
.find(c
);
285 delete[] c
.Ptr
; // Don't delete alloca
287 if (itId
!= _SheetNameToId
.end())
289 _Id
.Id
= itId
->second
;
290 #ifdef NL_DEBUG_SHEET_ID
292 _DebugSheetName
= itId
->first
.Ptr
;
297 // we failed to find the sheet name in the sheetname map so see if the string is numeric
298 if (sheetName
[0] == '#' && sheetName
[1])
301 if (NLMISC::fromString((const char *)(sheetName
+ 1), numericId
))
311 void CSheetId::loadSheetId()
313 H_AUTO(CSheetIdInit
);
314 //nldebug("Loading sheet_id.bin");
316 // Open the sheet id to sheet file name association
318 std::string path
= CPath::lookup("sheet_id.bin", false, false);
319 if (!path
.empty() && file
.open(path
))
322 _FileExtensions
.clear();
323 _SheetIdToName
.clear();
324 _SheetNameToId
.clear();
326 // reserve space for the vector of file extensions
327 _FileExtensions
.resize(1 << (NL_SHEET_ID_TYPE_BITS
));
329 // Get the map from the file
330 map
<uint32
, string
> tempMap
;
332 file
.serialCont(tempMap
);
335 if (_RemoveUnknownSheet
)
337 uint32 removednbfiles
= 0;
338 uint32 nbfiles
= (uint32
)tempMap
.size();
340 // now we remove all files that not available
341 map
<uint32
, string
>::iterator itStr2
;
342 for (itStr2
= tempMap
.begin(); itStr2
!= tempMap
.end();)
344 if (CPath::exists((*itStr2
).second
))
350 map
<uint32
, string
>::iterator olditStr
= itStr2
;
351 //nldebug ("Removing file '%s' from CSheetId because the file not exists", (*olditStr).second.c_str ());
353 tempMap
.erase(olditStr
);
357 nlinfo("SHEETID: Removed %d files on %d from CSheetId because these files don't exist", removednbfiles
, nbfiles
);
360 // Convert the map to one big string and 1 static map (id to name)
362 // Get the number and size of all strings
363 vector
<CChar
> tempVec
; // Used to initialise the first map
366 map
<uint32
, string
>::const_iterator it
= tempMap
.begin();
367 while (it
!= tempMap
.end())
369 nSize
+= (uint32
)it
->second
.size() + 1;
374 // Make the big string (composed of all strings) and a vector referencing each string
376 _AllStrings
.Ptr
= new char[nSize
];
377 it
= tempMap
.begin();
380 while (it
!= tempMap
.end())
382 tempVec
[nNb
].Ptr
= _AllStrings
.Ptr
+ nSize
;
383 strcpy(_AllStrings
.Ptr
+ nSize
, it
->second
.c_str());
384 toLowerAscii(_AllStrings
.Ptr
+ nSize
);
385 nSize
+= (uint32
)it
->second
.size() + 1;
390 // Finally build the static map (id to name)
391 _SheetIdToName
.reserve(tempVec
.size());
392 it
= tempMap
.begin();
394 while (it
!= tempMap
.end())
396 _SheetIdToName
.add(pair
<uint32
, CChar
>(it
->first
, CChar(tempVec
[nNb
])));
402 // The vector of all small string is not needed anymore we have all the info in
403 // the static map and with the pointer AllStrings referencing the beginning.
406 // Build the invert map (Name to Id) & file extension vector
408 uint32 nSize
= (uint32
)_SheetIdToName
.size();
409 _SheetNameToId
.reserve(nSize
);
410 TSheetIdToNameMap::iterator itStr
;
411 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
413 // add entry to the inverse map
414 _SheetNameToId
.add(make_pair((*itStr
).second
, (*itStr
).first
));
416 // work out the type value for this entry in the map
418 sheetId
.Id
= (*itStr
).first
;
419 uint32 type
= sheetId
.IdInfos
.Type
;
421 // check whether we need to add an entry to the file extensions vector
422 if (_FileExtensions
[type
].empty())
424 // find the file extension part of the given file name
425 _FileExtensions
[type
] = toLowerAscii(CFile::getExtension((*itStr
).second
.Ptr
));
429 _SheetNameToId
.endAdd();
434 nlerror("<CSheetId::init> Can't open the file sheet_id.bin");
436 nldebug("Finished loading sheet_id.bin: %u entries read", _SheetIdToName
.size());
439 //-----------------------------------------------
442 //-----------------------------------------------
443 void CSheetId::init(bool removeUnknownSheet
)
445 // allow multiple calls to init in case libraries depending on sheetid call this init from their own
448 if (_DontHaveSheetKnowledge
)
449 nlinfo("SHEETID: CSheetId is already initialized without sheet_id.bin");
453 // CFile::addFileChangeCallback ("sheet_id.bin", cbFileChange);
455 _RemoveUnknownSheet
= removeUnknownSheet
;
462 void CSheetId::initWithoutSheet()
466 nlassert(_DontHaveSheetKnowledge
);
471 _DontHaveSheetKnowledge
= true;
473 // Initialize id 0,0 as unknown.unknown
474 CSheetId unknownUnknown
= CSheetId("unknown.unknown");
475 nlassert(unknownUnknown
== CSheetId::Unknown
);
478 //-----------------------------------------------
481 //-----------------------------------------------
482 void CSheetId::uninit()
484 delete[] _AllStrings
.Ptr
;
485 _FileExtensions
.clear();
486 _DevTypeNameToId
.clear();
487 _DevSheetIdToName
.clear();
488 _DevSheetNameToId
.clear();
491 //-----------------------------------------------
494 //-----------------------------------------------
495 CSheetId
&CSheetId::operator=(const CSheetId
&sheetId
)
500 if (this == &sheetId
)
505 _Id
.Id
= sheetId
.asInt();
507 #ifdef NL_DEBUG_SHEET_ID
508 _DebugSheetName
= sheetId
._DebugSheetName
;
515 //-----------------------------------------------
518 //-----------------------------------------------
519 CSheetId
&CSheetId::operator=(const string
&sheetName
)
522 if (!buildSheetId(sheetName
.c_str(), sheetName
.size()))
525 // nldebug("LIST_SHEET_ID: %s (%s)", toString().c_str(), sheetName.c_str());
531 //-----------------------------------------------
534 //-----------------------------------------------
535 CSheetId
&CSheetId::operator=(const char *sheetName
)
538 if (!buildSheetId(sheetName
, strlen(sheetName
)))
541 // nldebug("LIST_SHEET_ID: %s (%s)", toString().c_str(), sheetName);
547 //-----------------------------------------------
550 //-----------------------------------------------
551 CSheetId
&CSheetId::operator=(uint32 sheetRef
)
562 //-----------------------------------------------
565 //-----------------------------------------------
566 CSheetId
&CSheetId::operator=(int sheetRef
)
571 _Id
.Id
= (uint32
)sheetRef
;
577 //-----------------------------------------------
580 //-----------------------------------------------
581 bool CSheetId::operator<(const CSheetId
&sheetRef
) const
586 if (_Id
.Id
< sheetRef
.asInt())
595 //-----------------------------------------------
598 //-----------------------------------------------
599 string
CSheetId::toString(bool ifNotFoundUseNumericId
) const
604 if (_DontHaveSheetKnowledge
)
606 if (_Id
.IdInfos
.Type
< _DevSheetIdToName
.size()
607 && _Id
.IdInfos
.Id
< _DevSheetIdToName
[_Id
.IdInfos
.Type
].size())
609 return _DevSheetIdToName
[_Id
.IdInfos
.Type
][_Id
.IdInfos
.Id
];
613 if (ifNotFoundUseNumericId
)
615 return NLMISC::toString("#%u", _Id
.Id
);
619 return NLMISC::toString("<Sheet %u not found in loaded sheets>", _Id
.Id
);
624 TSheetIdToNameMap::const_iterator itStr
= _SheetIdToName
.find(_Id
.Id
);
625 if (itStr
!= _SheetIdToName
.end())
627 return string((*itStr
).second
.Ptr
);
631 // This nlwarning is commented out because the loggers are mutexed, therefore
632 // you couldn't use toString() within a nlwarning().
633 //nlwarning("<CSheetId::toString> The sheet %08x is not in sheet_id.bin",_Id.Id);
634 if (ifNotFoundUseNumericId
)
636 return NLMISC::toString("#%u", _Id
.Id
);
640 return NLMISC::toString("<Sheet %u not found in sheet_id.bin>", _Id
.Id
);
646 void CSheetId::serial(NLMISC::IStream
&f
)
648 nlassert(!_DontHaveSheetKnowledge
);
652 #ifdef NL_DEBUG_SHEET_ID
653 TSheetIdToNameMap::iterator
it(_SheetIdToName
.find(_Id
.Id
));
654 if (it
!= _SheetIdToName
.end())
655 _DebugSheetName
= it
->second
.Ptr
;
657 _DebugSheetName
= NULL
;
661 void CSheetId::serialString(NLMISC::IStream
&f
, const std::string
&defaultType
)
663 nlassert(_Initialised
);
667 std::string sheetName
;
669 *this = CSheetId(sheetName
, defaultType
);
673 // if this assert fails, you may be using an outdated id bin
674 nlassert(*this != CSheetId::Unknown
);
675 std::string sheetName
= toString();
680 //-----------------------------------------------
683 //-----------------------------------------------
684 void CSheetId::display()
689 TSheetIdToNameMap::const_iterator itStr
;
690 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
692 //nlinfo("%d %s",(*itStr).first,(*itStr).second.c_str());
693 nlinfo("SHEETID: (%08x %d) %s", (*itStr
).first
, (*itStr
).first
, (*itStr
).second
.Ptr
);
698 //-----------------------------------------------
701 //-----------------------------------------------
702 void CSheetId::display(uint32 type
)
707 TSheetIdToNameMap::const_iterator itStr
;
708 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
710 // work out the type value for this entry in the map
712 sheetId
.Id
= (*itStr
).first
;
714 // decide whether or not to display the entry
715 if (type
== sheetId
.IdInfos
.Type
)
717 //nlinfo("%d %s",(*itStr).first,(*itStr).second.c_str());
718 nlinfo("SHEETID: (%08x %d) %s", (*itStr
).first
, (*itStr
).first
, (*itStr
).second
.Ptr
);
724 //-----------------------------------------------
727 //-----------------------------------------------
728 void CSheetId::buildIdVector(std::vector
<CSheetId
> &result
)
733 TSheetIdToNameMap::const_iterator itStr
;
734 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
736 result
.push_back((CSheetId
)(*itStr
).first
);
739 } // buildIdVector //
741 //-----------------------------------------------
744 //-----------------------------------------------
745 void CSheetId::buildIdVector(std::vector
<CSheetId
> &result
, uint32 type
)
749 nlassert(type
< (1 << (NL_SHEET_ID_TYPE_BITS
)));
751 TSheetIdToNameMap::const_iterator itStr
;
752 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
754 // work out the type value for this entry in the map
756 sheetId
.Id
= (*itStr
).first
;
758 // decide whether or not to use the entry
759 if (type
== sheetId
.IdInfos
.Type
)
761 result
.push_back((CSheetId
)sheetId
.Id
);
765 } // buildIdVector //
767 //-----------------------------------------------
770 //-----------------------------------------------
771 void CSheetId::buildIdVector(std::vector
<CSheetId
> &result
, std::vector
<std::string
> &resultFilenames
, uint32 type
)
775 nlassert(type
< (1 << (NL_SHEET_ID_TYPE_BITS
)));
777 TSheetIdToNameMap::const_iterator itStr
;
778 for (itStr
= _SheetIdToName
.begin(); itStr
!= _SheetIdToName
.end(); ++itStr
)
780 // work out the type value for this entry in the map
782 sheetId
.Id
= (*itStr
).first
;
784 // decide whether or not to use the entry
785 if (type
== sheetId
.IdInfos
.Type
)
787 result
.push_back((CSheetId
)sheetId
.Id
);
788 resultFilenames
.push_back((*itStr
).second
.Ptr
);
792 } // buildIdVector //
794 //-----------------------------------------------
797 //-----------------------------------------------
798 void CSheetId::buildIdVector(std::vector
<CSheetId
> &result
, const std::string
&fileExtension
)
800 uint32 type
= typeFromFileExtension(fileExtension
);
801 if (type
!= std::numeric_limits
<uint32
>::max())
802 buildIdVector(result
, type
);
804 } // buildIdVector //
806 //-----------------------------------------------
809 //-----------------------------------------------
810 void CSheetId::buildIdVector(std::vector
<CSheetId
> &result
, std::vector
<std::string
> &resultFilenames
, const std::string
&fileExtension
)
812 uint32 type
= typeFromFileExtension(fileExtension
);
813 if (type
!= std::numeric_limits
<uint32
>::max())
814 buildIdVector(result
, resultFilenames
, type
);
816 } // buildIdVector //
818 //-----------------------------------------------
819 // typeFromFileExtension
821 //-----------------------------------------------
822 uint32
CSheetId::typeFromFileExtension(const std::string
&fileExtension
)
828 for (i
= 0; i
< _FileExtensions
.size(); i
++)
829 if (toLowerAscii(fileExtension
) == _FileExtensions
[i
])
832 return std::numeric_limits
<uint32
>::max();
833 } // typeFromFileExtension //
835 //-----------------------------------------------
836 // fileExtensionFromType
838 //-----------------------------------------------
839 const std::string
&CSheetId::fileExtensionFromType(uint32 type
)
843 nlassert(type
< (1 << (NL_SHEET_ID_TYPE_BITS
)));
845 return _FileExtensions
[type
];
847 } // fileExtensionFromType //
849 //-----------------------------------------------
852 //-----------------------------------------------
853 void CSheetId::buildSheetId(uint32 shortId
, uint32 type
)
855 nlassert(shortId
< (1 << NL_SHEET_ID_ID_BITS
));
856 nlassert(type
< (1 << (NL_SHEET_ID_TYPE_BITS
)));
858 _Id
.IdInfos
.Id
= shortId
;
859 _Id
.IdInfos
.Type
= type
;
861 #ifdef NL_DEBUG_SHEET_ID
862 TSheetIdToNameMap::iterator
it(_SheetIdToName
.find(_Id
.Id
));
863 if (it
!= _SheetIdToName
.end())
865 _DebugSheetName
= it
->second
.Ptr
;
868 _DebugSheetName
= NULL
;