1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
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"
29 using namespace NLMISC
;
34 // ***************************************************************************
35 CLigoConfig::CLigoConfig()
36 : _DynamicAliasBitCount(32)
40 // ***************************************************************************
42 bool CLigoConfig::readConfigFile (const std::string
&fileName
, bool parsePrimitiveComboContent
)
47 // Load and parse the file
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
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
);
74 // ***************************************************************************
76 bool CLigoConfig::readPrimitiveClass (const std::string
&_fileName
, bool parsePrimitiveComboContent
)
79 string filename
= _fileName
;
80 filename
= CPath::lookup (_fileName
, false, false, false);
84 // The context strings
85 set
<string
> contextStrings
;
89 if (file
.open (filename
))
98 xmlNodePtr root
= xml
.getRootNode ();
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");
109 if (getPropertyString (bits
, filename
.c_str(), aliasBits
, "BIT_COUNT"))
112 NLMISC::fromString(bits
, uBits
);
113 _DynamicAliasBitCount
= std::min((uint32
)32, uBits
);
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
);
140 nlwarning("Can't find XML element <FILE_NAME>, no file index available for alias" );
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");
155 // Get the primitive name
157 if (getPropertyString (name
, filename
.c_str(), primitive
, "CLASS_NAME"))
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
))
169 syntaxError (filename
.c_str(), root
, "Class (%s) already defined", name
.c_str ());
175 primitive
= CIXml::getNextChildNode (primitive
, "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
);
191 // Get the first primitive configuration
192 _PrimitiveConfigurations
.reserve (_PrimitiveConfigurations
.size()+CIXml::countChildren (root
, "CONFIGURATION"));
193 xmlNodePtr configuration
= CIXml::getFirstChildNode (root
, "CONFIGURATION");
198 // Get the configuration 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))
210 configuration
= CIXml::getNextChildNode (configuration
, "CONFIGURATION");
212 while (configuration
);
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 ());
230 errorMessage ("Can't open the file %s for reading.", filename
.c_str());
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");
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());
253 if (_IndexFileName
.empty())
254 _IndexFileName
= indexFileName
;
256 // load the configuration file
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());
268 CConfigFile::CVar
*files
= cf
.getVarPtr("Files");
271 for (uint i
=0; i
<files
->size()/2; ++i
)
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",
287 getFileStaticAliasMapping(fileName
));
292 registerFileToStaticAliasTranslation(fileName
, index
);
300 // ***************************************************************************
302 NLMISC::CRGBA
CLigoConfig::getPrimitiveColor (const NLLIGO::IPrimitive
&primitive
)
306 if (primitive
.getPropertyByName ("class", className
))
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
)
324 if (primitive
.getPropertyByName ("class", className
))
327 std::map
<std::string
, CPrimitiveClass
>::iterator ite
= _PrimitiveClasses
.find (className
);
328 if (ite
!= _PrimitiveClasses
.end ())
330 return ite
->second
.LinkBrothers
;
336 // ***************************************************************************
338 const NLLIGO::IPrimitive
*CLigoConfig::getLinkedPrimitive (const NLLIGO::IPrimitive
&primitive
) const
341 const IPrimitive
*parent
= primitive
.getParent ();
345 if (parent
->getChildId (childId
, &primitive
))
347 // Test the next primitive
349 // Get the primitive class
351 if (primitive
.getPropertyByName ("class", className
))
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))
371 // ***************************************************************************
373 const NLLIGO::IPrimitive
*CLigoConfig::getPreviousLinkedPrimitive (const NLLIGO::IPrimitive
&primitive
) const
376 const IPrimitive
*parent
= primitive
.getParent ();
380 if (parent
->getChildId (childId
, &primitive
))
382 // Test the previous primitive
385 const IPrimitive
*brother
;
386 if (parent
->getChild (brother
, childId
-1) && brother
)
388 // Get the primitive class
390 if (brother
->getPropertyByName ("class", className
))
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
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
))
420 if (primitive
.getPropertyByName ("class", className
))
423 std::map
<std::string
, CPrimitiveClass
>::iterator ite
= _PrimitiveClasses
.find (className
);
424 if (ite
!= _PrimitiveClasses
.end ())
426 return ite
->second
.Deletable
;
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
);
444 // Search for the child class
446 for (i
=0; i
<parentClass
->DynamicChildren
.size (); i
++)
449 if (parentClass
->DynamicChildren
[i
].ClassName
== childClassName
)
453 if (i
<parentClass
->DynamicChildren
.size ())
456 for (i
=0; i
<parentClass
->GeneratedChildren
.size (); i
++)
459 if (parentClass
->GeneratedChildren
[i
].ClassName
== childClassName
)
463 return (i
<parentClass
->GeneratedChildren
.size ());
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");
488 // Search for the child class
490 for (i
=0; i
<parentClass
->DynamicChildren
.size (); i
++)
493 if (parentClass
->DynamicChildren
[i
].ClassName
== childClassName
)
497 return (i
<parentClass
->DynamicChildren
.size ());
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());
523 // ***************************************************************************
525 void CLigoConfig::syntaxError (const std::string
&filename
, xmlNodePtr xmlNode
, const char *format
, ...)
528 va_start( args
, format
);
530 vsnprintf( buffer
, 1024, format
, 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
542 va_start( args
, format
);
544 vsnprintf( buffer
, 1024, format
, args
);
550 // ***************************************************************************
552 const std::vector
<std::string
> &CLigoConfig::getContextString () const
557 // ***************************************************************************
559 const CPrimitiveClass
*CLigoConfig::getPrimitiveClass (const IPrimitive
&primitive
) const
561 const CPrimitiveClass
*primClass
= NULL
;
563 // Get property class
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
);
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
);
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
);
602 // ***************************************************************************
604 void CLigoConfig::resetPrimitiveConfiguration ()
606 _PrimitiveConfigurations
.clear ();
609 // ***************************************************************************
611 bool CLigoConfig::isStaticChild (const NLLIGO::IPrimitive
&primitive
)
614 const IPrimitive
*parent
= primitive
.getParent ();
618 const CPrimitiveClass
*parentClass
= getPrimitiveClass (*parent
);
621 if (parentClass
&& primitive
.getPropertyByName ("class", className
) && primitive
.getPropertyByName ("name", name
))
623 // Does it belong to the static children ?
625 for (i
=0; i
<parentClass
->StaticChildren
.size(); i
++)
627 if (parentClass
->StaticChildren
[i
].Name
== name
&&
628 parentClass
->StaticChildren
[i
].ClassName
== className
)
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)
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
681 if (staticPart
!= (staticPart
& (getStaticAliasMask()>>getDynamicAliasSize())))
683 nlwarning("CLigoConfig::buildAlias: staticPart 0x%x is outside the mask 0x%x",
685 getStaticAliasMask()>>getDynamicAliasSize());
687 if (dynamicPart
!= (dynamicPart
& getDynamicAliasMask()))
689 nlwarning("CLigoConfig::buildAlias: dynamicPart 0x%x is outside the mask 0x%x",
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'",
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)",
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
)
739 static string 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())
756 // no mapping defined.
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())
771 // no mapping defined.
776 std::string
CLigoConfig::aliasToString(uint32 fullAlias
)
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
)
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
;
805 nlwarning("New bit count must be less than previous");
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
;