Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / ligo / ligo_config.cpp
blobd898e44edd10ad186340929a90b8ab79bdc5556a
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdligo.h"
18 #include "nel/ligo/ligo_config.h"
20 #include "nel/ligo/primitive.h"
21 #include "nel/misc/config_file.h"
22 #include "nel/misc/i_xml.h"
23 #include "nel/misc/path.h"
24 #include "nel/misc/file.h"
26 #include <cstdlib>
28 using namespace std;
29 using namespace NLMISC;
31 namespace NLLIGO
34 // ***************************************************************************
35 CLigoConfig::CLigoConfig()
36 : _DynamicAliasBitCount(32)
40 // ***************************************************************************
42 bool CLigoConfig::readConfigFile (const std::string &fileName, bool parsePrimitiveComboContent)
44 // The CF
45 CConfigFile cf;
47 // Load and parse the file
48 cf.load (fileName);
50 // Read the parameters
51 CConfigFile::CVar &cell_size = cf.getVar ("cell_size");
52 CellSize = cell_size.asFloat ();
53 CConfigFile::CVar &snap = cf.getVar ("snap");
54 Snap = snap.asFloat ();
55 CConfigFile::CVar &snapShot = cf.getVar ("zone_snapeshot_res");
56 ZoneSnapShotRes = (uint)snapShot.asInt ();
57 CConfigFile::CVar &primitiveClassFilename = cf.getVar ("primitive_class_filename");
58 PrimitiveClassFilename= primitiveClassFilename.asString ();
60 // Clear the previous classes
61 _Contexts.clear();
62 _Contexts.push_back ("default");
63 _PrimitiveClasses.clear();
64 _PrimitiveConfigurations.clear();
66 // Read the primitive class name
67 if (!PrimitiveClassFilename.empty())
69 return readPrimitiveClass (PrimitiveClassFilename.c_str(), parsePrimitiveComboContent);
71 return true;
74 // ***************************************************************************
76 bool CLigoConfig::readPrimitiveClass (const std::string &_fileName, bool parsePrimitiveComboContent)
78 // File exist ?
79 string filename = _fileName;
80 filename = CPath::lookup (_fileName, false, false, false);
81 if (filename.empty())
82 filename = _fileName;
84 // The context strings
85 set<string> contextStrings;
87 // Read the document
88 CIFile file;
89 if (file.open (filename))
91 try
93 // XML stream
94 CIXml xml;
95 xml.init (file);
97 // Get the root node
98 xmlNodePtr root = xml.getRootNode ();
99 nlassert (root);
101 // Check the header
102 if (strcmp ((const char*)root->name, "NEL_LIGO_PRIMITIVE_CLASS") == 0)
104 // ALIAS_DYNAMIC_BITS
105 xmlNodePtr aliasBits = CIXml::getFirstChildNode (root, "ALIAS_DYNAMIC_BITS");
106 if (aliasBits)
108 string bits;
109 if (getPropertyString (bits, filename.c_str(), aliasBits, "BIT_COUNT"))
111 uint32 uBits;
112 NLMISC::fromString(bits, uBits);
113 _DynamicAliasBitCount = std::min((uint32)32, uBits);
115 else
116 return false;
119 else
121 // by default, set the size of dynamic alias to 32 bits
122 _DynamicAliasBitCount = 32;
124 // ALIAS_STATIC_FILE_ID
125 xmlNodePtr indexFileNameNode = CIXml::getFirstChildNode (root, "ALIAS_STATIC_FILE_ID");
126 if (indexFileNameNode)
128 string indexFileName;
129 if (getPropertyString (indexFileName, filename.c_str(), indexFileNameNode, "FILE_NAME"))
131 if (CPath::lookup(indexFileName, false, false, true).empty())
133 // try to append the class file path
134 indexFileName = CFile::getPath(_fileName)+indexFileName;
136 // load the configuration file
137 reloadIndexFile(indexFileName);
139 else
140 nlwarning("Can't find XML element <FILE_NAME>, no file index available for alias" );
142 else
144 // by default, set the size of dynamic alias to 32 bits
145 _DynamicAliasBitCount = 32;
149 // Get the first primitive description
150 xmlNodePtr primitive = CIXml::getFirstChildNode (root, "PRIMITIVE");
151 if (primitive)
155 // Get the primitive name
156 std::string name;
157 if (getPropertyString (name, filename.c_str(), primitive, "CLASS_NAME"))
159 // Add the primitive
160 pair<std::map<std::string, CPrimitiveClass>::iterator, bool> insertResult =
161 _PrimitiveClasses.insert (std::map<std::string, CPrimitiveClass>::value_type (name, CPrimitiveClass ()));
162 if (insertResult.second)
164 if (!insertResult.first->second.read (primitive, filename.c_str(), name.c_str (), contextStrings, _ContextFilesLookup, *this, parsePrimitiveComboContent))
165 return false;
167 else
169 syntaxError (filename.c_str(), root, "Class (%s) already defined", name.c_str ());
172 else
173 return false;
175 primitive = CIXml::getNextChildNode (primitive, "PRIMITIVE");
177 while (primitive);
180 // Add the context strings
182 set<string>::iterator ite = contextStrings.begin ();
183 while (ite != contextStrings.end ())
185 if (*ite != "default")
186 _Contexts.push_back (*ite);
187 ite++;
191 // Get the first primitive configuration
192 _PrimitiveConfigurations.reserve (_PrimitiveConfigurations.size()+CIXml::countChildren (root, "CONFIGURATION"));
193 xmlNodePtr configuration = CIXml::getFirstChildNode (root, "CONFIGURATION");
194 if (configuration)
198 // Get the configuration name
199 std::string name;
200 if (getPropertyString (name, filename.c_str(), configuration, "NAME"))
202 // Add the configuration
203 _PrimitiveConfigurations.resize (_PrimitiveConfigurations.size()+1);
204 if (!_PrimitiveConfigurations.back().read (configuration, filename.c_str(), name.c_str (), *this))
205 return false;
207 else
208 return false;
210 configuration = CIXml::getNextChildNode (configuration, "CONFIGURATION");
212 while (configuration);
215 // Ok
216 return true;
218 else
220 syntaxError (filename.c_str(), root, "Wrong root node, should be NEL_LIGO_PRIMITIVE_CLASS");
223 catch (const Exception &e)
225 errorMessage ("File read error (%s):%s", filename.c_str(), e.what ());
228 else
230 errorMessage ("Can't open the file %s for reading.", filename.c_str());
232 return false;
235 // ***************************************************************************
237 bool CLigoConfig::reloadIndexFile(const std::string &indexFileName)
239 if (_IndexFileName.empty() && indexFileName.empty())
241 nlwarning("CLigoConfig::reloadIndexFile: no file name specified and index file not previously loaded, can't load anything");
242 return false;
245 if (!_IndexFileName.empty() && !indexFileName.empty() && _IndexFileName != indexFileName)
247 nlwarning("CLigoConfig::reloadIndexFile: index file already loaded as '%s', can't load another file '%s'!",
248 _IndexFileName.c_str(),
249 indexFileName.c_str());
250 return false;
253 if (_IndexFileName.empty())
254 _IndexFileName = indexFileName;
256 // load the configuration file
257 CConfigFile cf;
258 string pathName = CPath::lookup(_IndexFileName, false);
260 if (pathName.empty())
262 nlwarning("Can't find index file '%s' in search path, no file index available for alias", indexFileName.c_str());
263 return false;
265 cf.load(pathName);
267 // get the variable
268 CConfigFile::CVar *files = cf.getVarPtr("Files");
269 if (files != NULL)
271 for (uint i=0; i<files->size()/2; ++i)
273 string fileName;
274 uint32 index;
276 fileName = files->asString(i*2);
277 index = files->asInt(i*2+1);
279 if (isFileStaticAliasMapped(fileName))
281 // check that the mapping as not changed
282 if (getFileStaticAliasMapping(fileName) != index)
284 nlwarning("CLigoConfig::reloadIndexFile: the mapping for the file '%s' as changed from %u to %u in the config file, the change is ignored",
285 fileName.c_str(),
286 index,
287 getFileStaticAliasMapping(fileName));
290 else
292 registerFileToStaticAliasTranslation(fileName, index);
297 return true;
300 // ***************************************************************************
302 NLMISC::CRGBA CLigoConfig::getPrimitiveColor (const NLLIGO::IPrimitive &primitive)
304 // Get the class
305 string className;
306 if (primitive.getPropertyByName ("class", className))
308 // Get the class
309 std::map<std::string, CPrimitiveClass>::iterator ite = _PrimitiveClasses.find (className);
310 if (ite != _PrimitiveClasses.end ())
312 return ite->second.Color;
315 return DEFAULT_PRIMITIVE_COLOR;
318 // ***************************************************************************
320 bool CLigoConfig::isPrimitiveLinked (const NLLIGO::IPrimitive &primitive)
322 // Get the class
323 string className;
324 if (primitive.getPropertyByName ("class", className))
326 // Get the class
327 std::map<std::string, CPrimitiveClass>::iterator ite = _PrimitiveClasses.find (className);
328 if (ite != _PrimitiveClasses.end ())
330 return ite->second.LinkBrothers;
333 return false;
336 // ***************************************************************************
338 const NLLIGO::IPrimitive *CLigoConfig::getLinkedPrimitive (const NLLIGO::IPrimitive &primitive) const
340 // Get the parent
341 const IPrimitive *parent = primitive.getParent ();
342 if (parent)
344 uint childId;
345 if (parent->getChildId (childId, &primitive))
347 // Test the next primitive
349 // Get the primitive class
350 string className;
351 if (primitive.getPropertyByName ("class", className))
353 // Get the class
354 std::map<std::string, CPrimitiveClass>::const_iterator ite = _PrimitiveClasses.find (className);
355 if (ite != _PrimitiveClasses.end ())
357 if (ite->second.LinkBrothers)
359 // Add the next child
360 const IPrimitive *brother;
361 if (parent->getChild (brother, childId+1))
362 return brother;
368 return NULL;
371 // ***************************************************************************
373 const NLLIGO::IPrimitive *CLigoConfig::getPreviousLinkedPrimitive (const NLLIGO::IPrimitive &primitive) const
375 // Get the parent
376 const IPrimitive *parent = primitive.getParent ();
377 if (parent)
379 uint childId;
380 if (parent->getChildId (childId, &primitive))
382 // Test the previous primitive
383 if (childId > 0)
385 const IPrimitive *brother;
386 if (parent->getChild (brother, childId-1) && brother)
388 // Get the primitive class
389 string className;
390 if (brother->getPropertyByName ("class", className))
392 // Get the class
393 std::map<std::string, CPrimitiveClass>::const_iterator ite = _PrimitiveClasses.find (className);
394 if (ite != _PrimitiveClasses.end ())
396 if (ite->second.LinkBrothers)
398 // Return the previous child
399 return brother;
407 return NULL;
410 // ***************************************************************************
412 bool CLigoConfig::isPrimitiveDeletable (const NLLIGO::IPrimitive &primitive)
414 // If it is a static child, it can't be deleted.
415 if (isStaticChild (primitive))
416 return false;
418 // Get the class
419 string className;
420 if (primitive.getPropertyByName ("class", className))
422 // Get the class
423 std::map<std::string, CPrimitiveClass>::iterator ite = _PrimitiveClasses.find (className);
424 if (ite != _PrimitiveClasses.end ())
426 return ite->second.Deletable;
429 return false;
432 // ***************************************************************************
434 bool CLigoConfig::canBeChild (const NLLIGO::IPrimitive &child, const NLLIGO::IPrimitive &parent)
436 // Get the child class
437 string childClassName;
438 if (child.getPropertyByName ("class", childClassName))
440 // Get the parent class
441 const CPrimitiveClass *parentClass = getPrimitiveClass (parent);
442 if (parentClass)
444 // Search for the child class
445 uint i;
446 for (i=0; i<parentClass->DynamicChildren.size (); i++)
448 // The same ?
449 if (parentClass->DynamicChildren[i].ClassName == childClassName)
450 break;
453 if (i<parentClass->DynamicChildren.size ())
454 return true;
456 for (i=0; i<parentClass->GeneratedChildren.size (); i++)
458 // The same ?
459 if (parentClass->GeneratedChildren[i].ClassName == childClassName)
460 break;
463 return (i<parentClass->GeneratedChildren.size ());
465 else
466 return true;
468 else
470 // Only if it is a root node or parent class doesn't exist
471 string parentClassName;
472 return ( (parent.getParent () == NULL) || (!parent.getPropertyByName ("class", parentClassName) ) );
476 // ***************************************************************************
478 bool CLigoConfig::canBeRoot (const NLLIGO::IPrimitive &child)
480 // Get the child class
481 string childClassName;
482 if (child.getPropertyByName ("class", childClassName))
484 // Get the parent class
485 const CPrimitiveClass *parentClass = getPrimitiveClass ("root");
486 if (parentClass)
488 // Search for the child class
489 uint i;
490 for (i=0; i<parentClass->DynamicChildren.size (); i++)
492 // The same ?
493 if (parentClass->DynamicChildren[i].ClassName == childClassName)
494 break;
497 return (i<parentClass->DynamicChildren.size ());
499 else
500 return true;
502 else
504 // Root class doesn't exist
505 return ( !getPrimitiveClass ("root") );
509 // ***************************************************************************
511 bool CLigoConfig::getPropertyString(std::string &result, const std::string &filename, xmlNodePtr xmlNode, const std::string &propName)
513 // Call the CIXml version
514 if (!CIXml::getPropertyString (result, xmlNode, propName))
516 // Output a formated error
517 syntaxError (filename, xmlNode, "Missing XML node property (%s)", propName.c_str());
518 return false;
520 return true;
523 // ***************************************************************************
525 void CLigoConfig::syntaxError (const std::string &filename, xmlNodePtr xmlNode, const char *format, ...)
527 va_list args;
528 va_start( args, format );
529 char buffer[1024];
530 vsnprintf( buffer, 1024, format, args );
531 va_end( args );
533 errorMessage ("(%s), node (%s), line (%d) :\n%s", filename.c_str(), xmlNode->name, (sint)xmlNode->line, buffer);
536 // ***************************************************************************
538 void CLigoConfig::errorMessage (const char *format, ... )
540 // Make a buffer string
541 va_list args;
542 va_start( args, format );
543 char buffer[1024];
544 vsnprintf( buffer, 1024, format, args );
545 va_end( args );
547 nlwarning (buffer);
550 // ***************************************************************************
552 const std::vector<std::string> &CLigoConfig::getContextString () const
554 return _Contexts;
557 // ***************************************************************************
559 const CPrimitiveClass *CLigoConfig::getPrimitiveClass (const IPrimitive &primitive) const
561 const CPrimitiveClass *primClass = NULL;
563 // Get property class
564 string className;
565 if (primitive.getPropertyByName ("class", className))
567 std::map<std::string, CPrimitiveClass>::const_iterator ite = _PrimitiveClasses.find (className);
568 if (ite != _PrimitiveClasses.end ())
570 primClass = &(ite->second);
574 // Not found ?
575 if (!primClass)
577 // Root ?
578 if (!primitive.getParent ())
580 std::map<std::string, CPrimitiveClass>::const_iterator ite = _PrimitiveClasses.find ("root");
581 if (ite != _PrimitiveClasses.end ())
583 primClass = &(ite->second);
587 return primClass;
590 // ***************************************************************************
592 const CPrimitiveClass *CLigoConfig::getPrimitiveClass(const std::string &className) const
594 std::map<std::string, CPrimitiveClass>::const_iterator ite = _PrimitiveClasses.find (className);
595 if (ite != _PrimitiveClasses.end ())
597 return &(ite->second);
599 return NULL;
602 // ***************************************************************************
604 void CLigoConfig::resetPrimitiveConfiguration ()
606 _PrimitiveConfigurations.clear ();
609 // ***************************************************************************
611 bool CLigoConfig::isStaticChild (const NLLIGO::IPrimitive &primitive)
613 // Has a parent ?
614 const IPrimitive *parent = primitive.getParent ();
615 if (parent)
617 // Get the classes
618 const CPrimitiveClass *parentClass = getPrimitiveClass (*parent);
619 string className;
620 string name;
621 if (parentClass && primitive.getPropertyByName ("class", className) && primitive.getPropertyByName ("name", name))
623 // Does it belong to the static children ?
624 uint i;
625 for (i=0; i<parentClass->StaticChildren.size(); i++)
627 if (parentClass->StaticChildren[i].Name == name &&
628 parentClass->StaticChildren[i].ClassName == className)
630 // Found
631 return true;
636 return false;
639 // ***************************************************************************
641 /// Get the dynamic bit size for alias
642 uint32 CLigoConfig::getDynamicAliasSize() const
644 return _DynamicAliasBitCount;
647 // ***************************************************************************
648 /// Get the dynamic bit mask for alias
649 uint32 CLigoConfig::getDynamicAliasMask() const
651 // this 'strange' test because VC fail to generate a correct shift if
652 // _DynamicAliasBitCount is 32 bits.
653 // The generated code lead to no shift at all and return 0 instead of 0xffffffff
654 if (_DynamicAliasBitCount >= 32)
655 return 0xffffffff;
656 else
657 return (1U<<_DynamicAliasBitCount)-1;
660 // ***************************************************************************
661 /// Get the static bit size for alias
662 uint32 CLigoConfig::getStaticAliasSize() const
664 return 32-_DynamicAliasBitCount;
667 // ***************************************************************************
668 /// Get the static bit mask for alias
669 uint32 CLigoConfig::getStaticAliasMask() const
671 // the opposite of the dynamic mask
672 return ~getDynamicAliasMask();
675 // ***************************************************************************
676 /// Build an alias given a static and dynamic part
677 uint32 CLigoConfig::buildAlias(uint32 staticPart, uint32 dynamicPart, bool warnIfOverload) const
679 if (warnIfOverload)
681 if (staticPart != (staticPart & (getStaticAliasMask()>>getDynamicAliasSize())))
683 nlwarning("CLigoConfig::buildAlias: staticPart 0x%x is outside the mask 0x%x",
684 staticPart,
685 getStaticAliasMask()>>getDynamicAliasSize());
687 if (dynamicPart != (dynamicPart & getDynamicAliasMask()))
689 nlwarning("CLigoConfig::buildAlias: dynamicPart 0x%x is outside the mask 0x%x",
690 dynamicPart,
691 getDynamicAliasMask());
695 return dynamicPart | (staticPart << _DynamicAliasBitCount);
698 // ***************************************************************************
700 void CLigoConfig::registerFileToStaticAliasTranslation(const std::string &fileName, uint32 staticPart)
702 // check the existing mapping
703 std::map<std::string, uint32>::iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end());
704 for (; first != last; ++first)
706 if (first->second == staticPart)
708 nlassertex(false, ("While registering static alias %u to file '%s', the alias is already assigned to file '%s'",
709 staticPart,
710 fileName.c_str(),
711 first->first.c_str()));
714 if ((staticPart<<getDynamicAliasSize()) != ((staticPart<<getDynamicAliasSize()) & getStaticAliasMask()))
716 nlwarning("CLigoConfig::registerStaticAliasTranslation: staticPart 0x%x(%u) is outside the mask 0x%x, the staticPart will be clipped to 0x%x(%u)",
717 staticPart,
718 staticPart,
719 getStaticAliasMask(),
720 staticPart & getStaticAliasMask(),
721 staticPart & getStaticAliasMask());
723 staticPart = (staticPart & getStaticAliasMask());
727 _StaticAliasFileMapping[fileName] = staticPart;
730 const std::string &CLigoConfig::getFileNameForStaticAlias(uint32 staticAlias) const
732 std::map<std::string, uint32>::const_iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end());
734 for (; first != last; ++first)
736 if (first->second == staticAlias)
737 return first->first;
739 static string emptyString;
741 return emptyString;
745 // ***************************************************************************
747 uint32 CLigoConfig::getFileStaticAliasMapping(const std::string &fileName) const
749 std::map<std::string, uint32>::const_iterator it(_StaticAliasFileMapping.find(fileName));
751 if (it != _StaticAliasFileMapping.end())
753 return it->second;
755 else
756 // no mapping defined.
757 return 0;
760 // ***************************************************************************
762 bool CLigoConfig::isFileStaticAliasMapped(const std::string &fileName) const
764 std::map<std::string, uint32>::const_iterator it(_StaticAliasFileMapping.find(fileName));
766 if (it != _StaticAliasFileMapping.end())
768 return true;
770 else
771 // no mapping defined.
772 return false;
776 std::string CLigoConfig::aliasToString(uint32 fullAlias)
778 uint32 staticPart;
779 uint32 dynPart;
781 staticPart = (fullAlias & getStaticAliasMask())>>getDynamicAliasSize();
782 dynPart = fullAlias & getDynamicAliasMask();
784 return toString("(A:%u:%u)", staticPart, dynPart);
788 uint32 CLigoConfig::aliasFromString(const std::string &fullAlias)
790 uint32 staticPart;
791 uint32 dynPart;
792 sscanf(fullAlias.c_str(), "(A:%u:%u)", &staticPart, &dynPart);
794 return ((staticPart<<getDynamicAliasSize()) & getStaticAliasMask()) | (dynPart & getDynamicAliasMask());
799 void CLigoConfig::updateDynamicAliasBitCount(uint32 newDynamicAliasBitCount)
801 sint32 diff = _DynamicAliasBitCount - newDynamicAliasBitCount;
803 if (diff <= 0)
805 nlwarning("New bit count must be less than previous");
806 nlassert(0);
810 std::map<std::string, uint32>::iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end());
811 for ( ; first != last; ++first)
813 first->second = first->second << diff;
815 _DynamicAliasBitCount = newDynamicAliasBitCount;