// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdligo.h" #include "nel/ligo/ligo_config.h" #include "nel/ligo/primitive.h" #include "nel/misc/config_file.h" #include "nel/misc/i_xml.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include using namespace std; using namespace NLMISC; namespace NLLIGO { // *************************************************************************** CLigoConfig::CLigoConfig() : _DynamicAliasBitCount(32) { } // *************************************************************************** bool CLigoConfig::readConfigFile (const char *fileName, bool parsePrimitiveComboContent) { // The CF CConfigFile cf; // Load and parse the file cf.load (fileName); // Read the parameters CConfigFile::CVar &cell_size = cf.getVar ("cell_size"); CellSize = cell_size.asFloat (); CConfigFile::CVar &snap = cf.getVar ("snap"); Snap = snap.asFloat (); CConfigFile::CVar &snapShot = cf.getVar ("zone_snapeshot_res"); ZoneSnapShotRes = (uint)snapShot.asInt (); CConfigFile::CVar &primitiveClassFilename = cf.getVar ("primitive_class_filename"); PrimitiveClassFilename= primitiveClassFilename.asString (); // Clear the previous classes _Contexts.clear(); _Contexts.push_back ("default"); _PrimitiveClasses.clear(); _PrimitiveConfigurations.clear(); // Read the primitive class name if (!PrimitiveClassFilename.empty()) { return readPrimitiveClass (PrimitiveClassFilename.c_str(), parsePrimitiveComboContent); } return true; } // *************************************************************************** bool CLigoConfig::readPrimitiveClass (const char *_fileName, bool parsePrimitiveComboContent) { // File exist ? string filename = _fileName; filename = CPath::lookup (_fileName, false, false, false); if (filename.empty()) filename = _fileName; // The context strings set contextStrings; // Read the document CIFile file; if (file.open (filename)) { try { // XML stream CIXml xml; xml.init (file); // Get the root node xmlNodePtr root = xml.getRootNode (); nlassert (root); // Check the header if (strcmp ((const char*)root->name, "NEL_LIGO_PRIMITIVE_CLASS") == 0) { // ALIAS_DYNAMIC_BITS xmlNodePtr aliasBits = CIXml::getFirstChildNode (root, "ALIAS_DYNAMIC_BITS"); if (aliasBits) { string bits; if (getPropertyString (bits, filename.c_str(), aliasBits, "BIT_COUNT")) { uint32 uBits; NLMISC::fromString(bits, uBits); _DynamicAliasBitCount = std::min((uint32)32, uBits); } else return false; } else { // by default, set the size of dynamic alias to 32 bits _DynamicAliasBitCount = 32; } // ALIAS_STATIC_FILE_ID xmlNodePtr indexFileNameNode = CIXml::getFirstChildNode (root, "ALIAS_STATIC_FILE_ID"); if (indexFileNameNode) { string indexFileName; if (getPropertyString (indexFileName, filename.c_str(), indexFileNameNode, "FILE_NAME")) { if (CPath::lookup(indexFileName, false, false, true).empty()) { // try to append the class file path indexFileName = CFile::getPath(_fileName)+indexFileName; } // load the configuration file reloadIndexFile(indexFileName); } else nlwarning("Can't find XML element , no file index available for alias" ); } else { // by default, set the size of dynamic alias to 32 bits _DynamicAliasBitCount = 32; } // Get the first primitive description xmlNodePtr primitive = CIXml::getFirstChildNode (root, "PRIMITIVE"); if (primitive) { do { // Get the primitive name std::string name; if (getPropertyString (name, filename.c_str(), primitive, "CLASS_NAME")) { // Add the primitive pair::iterator, bool> insertResult = _PrimitiveClasses.insert (std::map::value_type (name, CPrimitiveClass ())); if (insertResult.second) { if (!insertResult.first->second.read (primitive, filename.c_str(), name.c_str (), contextStrings, _ContextFilesLookup, *this, parsePrimitiveComboContent)) return false; } else { syntaxError (filename.c_str(), root, "Class (%s) already defined", name.c_str ()); } } else return false; primitive = CIXml::getNextChildNode (primitive, "PRIMITIVE"); } while (primitive); } // Add the context strings { set::iterator ite = contextStrings.begin (); while (ite != contextStrings.end ()) { if (*ite != "default") _Contexts.push_back (*ite); ite++; } } // Get the first primitive configuration _PrimitiveConfigurations.reserve (_PrimitiveConfigurations.size()+CIXml::countChildren (root, "CONFIGURATION")); xmlNodePtr configuration = CIXml::getFirstChildNode (root, "CONFIGURATION"); if (configuration) { do { // Get the configuration name std::string name; if (getPropertyString (name, filename.c_str(), configuration, "NAME")) { // Add the configuration _PrimitiveConfigurations.resize (_PrimitiveConfigurations.size()+1); if (!_PrimitiveConfigurations.back().read (configuration, filename.c_str(), name.c_str (), *this)) return false; } else return false; configuration = CIXml::getNextChildNode (configuration, "CONFIGURATION"); } while (configuration); } // Ok return true; } else { syntaxError (filename.c_str(), root, "Wrong root node, should be NEL_LIGO_PRIMITIVE_CLASS"); } } catch (const Exception &e) { errorMessage ("File read error (%s):%s", filename.c_str(), e.what ()); } } else { errorMessage ("Can't open the file %s for reading.", filename.c_str()); } return false; } // *************************************************************************** bool CLigoConfig::reloadIndexFile(const std::string &indexFileName) { if (_IndexFileName.empty() && indexFileName.empty()) { nlwarning("CLigoConfig::reloadIndexFile: no file name specified and index file not previously loaded, can't load anything"); return false; } if (!_IndexFileName.empty() && !indexFileName.empty() && _IndexFileName != indexFileName) { nlwarning("CLigoConfig::reloadIndexFile: index file already loaded as '%s', can't load another file '%s'!", _IndexFileName.c_str(), indexFileName.c_str()); return false; } if (_IndexFileName.empty()) _IndexFileName = indexFileName; // load the configuration file CConfigFile cf; string pathName = CPath::lookup(_IndexFileName, false); if (pathName.empty()) { nlwarning("Can't find index file '%s' in search path, no file index available for alias", indexFileName.c_str()); return false; } cf.load(pathName); // get the variable CConfigFile::CVar *files = cf.getVarPtr("Files"); if (files != NULL) { for (uint i=0; isize()/2; ++i) { string fileName; uint32 index; fileName = files->asString(i*2); index = files->asInt(i*2+1); if (isFileStaticAliasMapped(fileName)) { // check that the mapping as not changed if (getFileStaticAliasMapping(fileName) != index) { nlwarning("CLigoConfig::reloadIndexFile: the mapping for the file '%s' as changed from %u to %u in the config file, the change is ignored", fileName.c_str(), index, getFileStaticAliasMapping(fileName)); } } else { registerFileToStaticAliasTranslation(fileName, index); } } } return true; } // *************************************************************************** NLMISC::CRGBA CLigoConfig::getPrimitiveColor (const NLLIGO::IPrimitive &primitive) { // Get the class string className; if (primitive.getPropertyByName ("class", className)) { // Get the class std::map::iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { return ite->second.Color; } } return DEFAULT_PRIMITIVE_COLOR; } // *************************************************************************** bool CLigoConfig::isPrimitiveLinked (const NLLIGO::IPrimitive &primitive) { // Get the class string className; if (primitive.getPropertyByName ("class", className)) { // Get the class std::map::iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { return ite->second.LinkBrothers; } } return false; } // *************************************************************************** const NLLIGO::IPrimitive *CLigoConfig::getLinkedPrimitive (const NLLIGO::IPrimitive &primitive) const { // Get the parent const IPrimitive *parent = primitive.getParent (); if (parent) { uint childId; if (parent->getChildId (childId, &primitive)) { // Test the next primitive // Get the primitive class string className; if (primitive.getPropertyByName ("class", className)) { // Get the class std::map::const_iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { if (ite->second.LinkBrothers) { // Add the next child const IPrimitive *brother; if (parent->getChild (brother, childId+1)) return brother; } } } } } return NULL; } // *************************************************************************** const NLLIGO::IPrimitive *CLigoConfig::getPreviousLinkedPrimitive (const NLLIGO::IPrimitive &primitive) const { // Get the parent const IPrimitive *parent = primitive.getParent (); if (parent) { uint childId; if (parent->getChildId (childId, &primitive)) { // Test the previous primitive if (childId > 0) { const IPrimitive *brother; if (parent->getChild (brother, childId-1) && brother) { // Get the primitive class string className; if (brother->getPropertyByName ("class", className)) { // Get the class std::map::const_iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { if (ite->second.LinkBrothers) { // Return the previous child return brother; } } } } } } } return NULL; } // *************************************************************************** bool CLigoConfig::isPrimitiveDeletable (const NLLIGO::IPrimitive &primitive) { // If it is a static child, it can't be deleted. if (isStaticChild (primitive)) return false; // Get the class string className; if (primitive.getPropertyByName ("class", className)) { // Get the class std::map::iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { return ite->second.Deletable; } } return false; } // *************************************************************************** bool CLigoConfig::canBeChild (const NLLIGO::IPrimitive &child, const NLLIGO::IPrimitive &parent) { // Get the child class string childClassName; if (child.getPropertyByName ("class", childClassName)) { // Get the parent class const CPrimitiveClass *parentClass = getPrimitiveClass (parent); if (parentClass) { // Search for the child class uint i; for (i=0; iDynamicChildren.size (); i++) { // The same ? if (parentClass->DynamicChildren[i].ClassName == childClassName) break; } if (iDynamicChildren.size ()) return true; for (i=0; iGeneratedChildren.size (); i++) { // The same ? if (parentClass->GeneratedChildren[i].ClassName == childClassName) break; } return (iGeneratedChildren.size ()); } else return true; } else { // Only if it is a root node or parent class doesn't exist string parentClassName; return ( (parent.getParent () == NULL) || (!parent.getPropertyByName ("class", parentClassName) ) ); } } // *************************************************************************** bool CLigoConfig::canBeRoot (const NLLIGO::IPrimitive &child) { // Get the child class string childClassName; if (child.getPropertyByName ("class", childClassName)) { // Get the parent class const CPrimitiveClass *parentClass = getPrimitiveClass ("root"); if (parentClass) { // Search for the child class uint i; for (i=0; iDynamicChildren.size (); i++) { // The same ? if (parentClass->DynamicChildren[i].ClassName == childClassName) break; } return (iDynamicChildren.size ()); } else return true; } else { // Root class doesn't exist return ( !getPrimitiveClass ("root") ); } } // *************************************************************************** bool CLigoConfig::getPropertyString (std::string &result, const char *filename, xmlNodePtr xmlNode, const char *propName) { // Call the CIXml version if (!CIXml::getPropertyString (result, xmlNode, propName)) { // Output a formated error syntaxError (filename, xmlNode, "Missing XML node property (%s)", propName); return false; } return true; } // *************************************************************************** void CLigoConfig::syntaxError (const char *filename, xmlNodePtr xmlNode, const char *format, ...) { va_list args; va_start( args, format ); char buffer[1024]; vsnprintf( buffer, 1024, format, args ); va_end( args ); errorMessage ("(%s), node (%s), line (%p) :\n%s", filename, xmlNode->name, xmlNode->content, buffer); } // *************************************************************************** void CLigoConfig::errorMessage (const char *format, ... ) { // Make a buffer string va_list args; va_start( args, format ); char buffer[1024]; vsnprintf( buffer, 1024, format, args ); va_end( args ); nlwarning (buffer); } // *************************************************************************** const std::vector &CLigoConfig::getContextString () const { return _Contexts; } // *************************************************************************** const CPrimitiveClass *CLigoConfig::getPrimitiveClass (const IPrimitive &primitive) const { const CPrimitiveClass *primClass = NULL; // Get property class string className; if (primitive.getPropertyByName ("class", className)) { std::map::const_iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { primClass = &(ite->second); } } // Not found ? if (!primClass) { // Root ? if (!primitive.getParent ()) { std::map::const_iterator ite = _PrimitiveClasses.find ("root"); if (ite != _PrimitiveClasses.end ()) { primClass = &(ite->second); } } } return primClass; } // *************************************************************************** const CPrimitiveClass *CLigoConfig::getPrimitiveClass (const char *className) const { std::map::const_iterator ite = _PrimitiveClasses.find (className); if (ite != _PrimitiveClasses.end ()) { return &(ite->second); } return NULL; } // *************************************************************************** void CLigoConfig::resetPrimitiveConfiguration () { _PrimitiveConfigurations.clear (); } // *************************************************************************** bool CLigoConfig::isStaticChild (const NLLIGO::IPrimitive &primitive) { // Has a parent ? const IPrimitive *parent = primitive.getParent (); if (parent) { // Get the classes const CPrimitiveClass *parentClass = getPrimitiveClass (*parent); string className; string name; if (parentClass && primitive.getPropertyByName ("class", className) && primitive.getPropertyByName ("name", name)) { // Does it belong to the static children ? uint i; for (i=0; iStaticChildren.size(); i++) { if (parentClass->StaticChildren[i].Name == name && parentClass->StaticChildren[i].ClassName == className) { // Found return true; } } } } return false; } // *************************************************************************** /// Get the dynamic bit size for alias uint32 CLigoConfig::getDynamicAliasSize() const { return _DynamicAliasBitCount; } // *************************************************************************** /// Get the dynamic bit mask for alias uint32 CLigoConfig::getDynamicAliasMask() const { // this 'strange' test because VC fail to generate a correct shift if // _DynamicAliasBitCount is 32 bits. // The generated code lead to no shift at all and return 0 instead of 0xffffffff if (_DynamicAliasBitCount >= 32) return 0xffffffff; else return (1U<<_DynamicAliasBitCount)-1; } // *************************************************************************** /// Get the static bit size for alias uint32 CLigoConfig::getStaticAliasSize() const { return 32-_DynamicAliasBitCount; } // *************************************************************************** /// Get the static bit mask for alias uint32 CLigoConfig::getStaticAliasMask() const { // the opposite of the dynamic mask return ~getDynamicAliasMask(); } // *************************************************************************** /// Build an alias given a static and dynamic part uint32 CLigoConfig::buildAlias(uint32 staticPart, uint32 dynamicPart, bool warnIfOverload) const { if (warnIfOverload) { if (staticPart != (staticPart & (getStaticAliasMask()>>getDynamicAliasSize()))) { nlwarning("CLigoConfig::buildAlias: staticPart 0x%x is outside the mask 0x%x", staticPart, getStaticAliasMask()>>getDynamicAliasSize()); } if (dynamicPart != (dynamicPart & getDynamicAliasMask())) { nlwarning("CLigoConfig::buildAlias: dynamicPart 0x%x is outside the mask 0x%x", dynamicPart, getDynamicAliasMask()); } } return dynamicPart | (staticPart << _DynamicAliasBitCount); } // *************************************************************************** void CLigoConfig::registerFileToStaticAliasTranslation(const std::string &fileName, uint32 staticPart) { // check the existing mapping std::map::iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end()); for (; first != last; ++first) { if (first->second == staticPart) { nlassertex(false, ("While registering static alias %u to file '%s', the alias is already assigned to file '%s'", staticPart, fileName.c_str(), first->first.c_str())); } } if ((staticPart<::const_iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end()); for (; first != last; ++first) { if (first->second == staticAlias) return first->first; } static string emptyString; return emptyString; } // *************************************************************************** uint32 CLigoConfig::getFileStaticAliasMapping(const std::string &fileName) const { std::map::const_iterator it(_StaticAliasFileMapping.find(fileName)); if (it != _StaticAliasFileMapping.end()) { return it->second; } else // no mapping defined. return 0; } // *************************************************************************** bool CLigoConfig::isFileStaticAliasMapped(const std::string &fileName) const { std::map::const_iterator it(_StaticAliasFileMapping.find(fileName)); if (it != _StaticAliasFileMapping.end()) { return true; } else // no mapping defined. return false; } std::string CLigoConfig::aliasToString(uint32 fullAlias) { uint32 staticPart; uint32 dynPart; staticPart = (fullAlias & getStaticAliasMask())>>getDynamicAliasSize(); dynPart = fullAlias & getDynamicAliasMask(); return toString("(A:%u:%u)", staticPart, dynPart); } uint32 CLigoConfig::aliasFromString(std::string fullAlias) { uint32 staticPart; uint32 dynPart; sscanf(fullAlias.c_str(), "(A:%u:%u)", &staticPart, &dynPart); return ((staticPart<::iterator first(_StaticAliasFileMapping.begin()), last(_StaticAliasFileMapping.end()); for ( ; first != last; ++first) { first->second = first->second << diff; } _DynamicAliasBitCount = newDynamicAliasBitCount; } }