// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // 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 <http://www.gnu.org/licenses/>. #include "nel/misc/types_nl.h" #include "nel/misc/config_file.h" #include "nel/misc/path.h" #include "nel/ligo/primitive.h" #include "nel/ligo/ligo_config.h" #include "nel/ligo/primitive_utils.h" #include "nel/misc/algo.h" using namespace std; using namespace NLMISC; using namespace NLLIGO; CLigoConfig LigoConfig; vector<string> Filters; // always true predicate struct TAllPrimitivePredicate : public std::unary_function<IPrimitive*, bool> { bool operator () (IPrimitive *prim) { return true; } }; bool isFiltered(const std::string &path) { for (uint i=0; i<Filters.size(); ++i) { if (path.find(Filters[i]) != string::npos) return true; } return false; } void syncFile(const string &srcPath, const string &dstPath) { if (isFiltered(srcPath)) return; nlinfo("Synchronizing file '%s'", CFile::getFilename(srcPath).c_str()); bool modified = false; CPrimitives srcDoc; CPrimitives dstDoc; // load the src primitive CPrimitiveContext::instance().CurrentPrimitive = &srcDoc; loadXmlPrimitiveFile(srcDoc, srcPath, LigoConfig); // load the dst primitive CPrimitiveContext::instance().CurrentPrimitive = &dstDoc; loadXmlPrimitiveFile(dstDoc, dstPath, LigoConfig); CPrimitiveContext::instance().CurrentPrimitive = NULL; // retrieve ALL the alias node in the src doc TPrimitiveClassPredicate pred("alias"); TPrimitiveSet primAlias; CPrimitiveSet<TPrimitiveClassPredicate> setBuilder; setBuilder.buildSet(srcDoc.RootNode, pred, primAlias); // look at each matched node for (uint i=0; i<primAlias.size(); ++i) { CPrimAlias *srcPa = dynamic_cast<CPrimAlias*>(primAlias[i]); if (srcPa == NULL) continue; // build a ascii path to the prim string primPath = buildPrimPath(srcPa); // look in the dst doc for a node with this path TPrimitiveSet dstAlias; selectPrimByPath(dstDoc.RootNode, primPath, dstAlias); if (dstAlias.size() == 1) { // yeah ! we found a match CPrimAlias *pa = dynamic_cast<CPrimAlias*>(dstAlias.front()); if (pa != NULL && pa->getAlias() != srcPa->getAlias()) { // nldebug("%s : forcing alias %u", primPath.c_str(), srcPa->getAlias()); dstDoc.forceAlias(pa, srcPa->getAlias()); modified = true; } } else if (dstAlias.size() == 0) { // try to go up one level and add a prim alias node while (!primPath.empty() && primPath[primPath.size()-1] != '.') primPath.resize(primPath.size()-1); if (primPath.empty()) continue; // remove the '.' primPath.resize(primPath.size()-1); if (primPath.empty()) continue; // ok, retry the primitive select now selectPrimByPath(dstDoc.RootNode, primPath, dstAlias); if (dstAlias.size() == 1) { IPrimitive *parent = dstAlias.front(); CPrimAlias *pa = static_cast<CPrimAlias*> (CClassRegistry::create ("CPrimAlias")); CPropertyString *pname = new CPropertyString ("alias"); pa->addPropertyByName("name", pname); CPropertyString *pclass = new CPropertyString ("alias"); pa->addPropertyByName("class", pclass); // nldebug("%s : creating new CPrimAlias for %u", primPath.c_str(), srcPa->getAlias()); // insert the prim alias at first pos CPrimitiveContext::instance().CurrentPrimitive = &dstDoc; parent->insertChild(pa, 0); dstDoc.forceAlias(pa, srcPa->getAlias()); modified = true; } else { // multiple node match ! // try to affect the alias if src an dst have the same number of match TPrimitiveSet srcAlias; selectPrimByPath(srcDoc.RootNode, primPath, srcAlias); if (dstAlias.size() == srcAlias.size()) { // ok, we can match for (uint i=0; i<srcAlias.size(); ++i) { IPrimitive *parent = dstAlias[i]; TPrimitiveClassPredicate pred("alias"); CPrimAlias *srcPa = static_cast<CPrimAlias*>(getPrimitiveChild(srcAlias[i], pred)); CPrimAlias *pa = static_cast<CPrimAlias*> (CClassRegistry::create ("CPrimAlias")); CPropertyString *pname = new CPropertyString ("alias"); pa->addPropertyByName("name", pname); CPropertyString *pclass = new CPropertyString ("alias"); pa->addPropertyByName("class", pclass); // nldebug("%s : creating new CPrimAlias for %u", primPath.c_str(), srcPa->getAlias()); // insert the prim alias at first pos CPrimitiveContext::instance().CurrentPrimitive = &dstDoc; parent->insertChild(pa, 0); dstDoc.forceAlias(pa, srcPa->getAlias()); modified = true; } } else if (!dstAlias.empty()) { nlwarning("In '%s', the path '%s' match to a different multiple node set, no alias restored !", CFile::getFilename(srcPath).c_str(), primPath.c_str()); } } } else { // multiple dst node mapping // try to affect the alias if src an dst have the same number of match TPrimitiveSet srcAlias; selectPrimByPath(srcDoc.RootNode, primPath, srcAlias); if (dstAlias.size() == srcAlias.size()) { // ok, we can match for (uint i=0; i<srcAlias.size(); ++i) { CPrimAlias *pa = dynamic_cast<CPrimAlias*>(dstAlias[i]); CPrimAlias *srcPa = dynamic_cast<CPrimAlias*>(srcAlias[i]); if (pa != NULL && pa->getAlias() != srcPa->getAlias()) { // nldebug("%s : forcing alias %u", primPath.c_str(), srcPa->getAlias()); dstDoc.forceAlias(pa, srcPa->getAlias()); modified = true; } } } else if (!dstAlias.empty()) { nlwarning("In '%s', the path '%s' match to a different multiple node set, no alias restored !", CFile::getFilename(srcPath).c_str(), primPath.c_str()); } } } // second loop : for each dst node, look if it need a missing CPrimAlias node and generated it if needed { TAllPrimitivePredicate pred; CPrimitiveEnumerator<TAllPrimitivePredicate> pe(dstDoc.RootNode, pred); IPrimitive *prim; while ((prim = pe.getNextMatch()) != NULL) { const CPrimitiveClass *pc = LigoConfig.getPrimitiveClass(*prim); if (pc) { // check all the static childrens for (uint i=0; i<pc->StaticChildren.size(); ++i) { const CPrimitiveClass::CChild &cc = pc->StaticChildren[i]; if (cc.ClassName == "alias" && cc.Name == "alias") { bool found = false; // ok, this node need an alias, check if it is present for (uint i=0; i<prim->getNumChildren(); ++i) { IPrimitive *child; if (prim->getChild(child, i)) { string className; if (child->getPropertyByName("class", className) && className == "alias") { found = true; break; } } } if (!found) { // ok, we need to add the alias class CPrimAlias *pa = static_cast<CPrimAlias*> (CClassRegistry::create ("CPrimAlias")); CPropertyString *pname = new CPropertyString ("alias"); pa->addPropertyByName("name", pname); CPropertyString *pclass = new CPropertyString ("alias"); pa->addPropertyByName("class", pclass); // nldebug("%s : creating new CPrimAlias for %u", primPath.c_str(), srcPa->getAlias()); // insert the prim alias at first pos CPrimitiveContext::instance().CurrentPrimitive = &dstDoc; prim->insertChild(pa, 0); modified = true; } } } } } } if (modified) { saveXmlPrimitiveFile(dstDoc, dstPath); } } struct TSetFileName { void operator () (std::string &str) { str = CFile::getFilename(str); } }; struct TSetLastFolder { void operator () (std::string &str) { vector<string> folders; str = CPath::standardizePath(str, false); #ifdef NL_OS_WINDOWS explode(str, std::string("/"), folders, true); #else // NL_OS_WINDOW NLMISC::splitString(str, "/", folders); #endif // NL_OS_WINDOWS if (!folders.empty()) str = folders.back(); else str = ""; } }; void syncFolder(const string &srcPath, const string &dstPath) { string tmp(srcPath); TSetLastFolder slf; slf.operator()(tmp); nlinfo("Synchronizing folder '%s'", tmp.c_str()); // check all the file in the current folder vector<string> srcFiles; CPath::getPathContent(srcPath, false, false, true, srcFiles); for_each(srcFiles.begin(), srcFiles.end(), TSetFileName()); sort(srcFiles.begin(), srcFiles.end()); vector<string> dstFiles; CPath::getPathContent(dstPath, false, false, true, dstFiles); for_each(dstFiles.begin(), dstFiles.end(), TSetFileName()); sort(dstFiles.begin(), dstFiles.end()); uint si=0; uint di=0; for (; si < srcFiles.size() && di < dstFiles.size(); ++si, ++di) { if (srcFiles[si] < dstFiles[di]) --di; else if (srcFiles[si] > dstFiles[di]) --si; else if (srcFiles[si].find(".primitive") != string::npos) { // two file identical, synch the alias inside syncFile(srcPath+"/"+srcFiles[si], dstPath+"/"+dstFiles[di]); } } // check all the folders in the current folder vector<string> srcFolders; CPath::getPathContent(srcPath, false, true, false, srcFolders); for_each(srcFolders.begin(), srcFolders.end(), TSetLastFolder()); sort(srcFolders.begin(), srcFolders.end()); vector<string> dstFolders; CPath::getPathContent(dstPath, false, true, false, dstFolders); for_each(dstFolders.begin(), dstFolders.end(), TSetLastFolder()); sort(dstFolders.begin(), dstFolders.end()); si=0; di=0; for (; si < srcFolders.size() && di < dstFolders.size(); ++si, ++di) { if (srcFolders[si] < dstFolders[di]) --di; else if (srcFolders[si] > dstFolders[di]) --si; else { // two folder identical, synch the files inside syncFolder(srcPath+"/"+srcFolders[si], dstPath+"/"+dstFolders[di]); } } } int main() { new NLMISC::CApplicationContext; CConfigFile cf; // register ligo primitives Register(); cf.load("alias_synchronizer.cfg"); CConfigFile::CVar &paths = cf.getVar("Paths"); CConfigFile::CVar &srcPath = cf.getVar("SrcPath"); CConfigFile::CVar &dstPath = cf.getVar("DstPath"); CConfigFile::CVar &filters = cf.getVar("Filters"); // store the filters for (uint i=0; i<filters.size(); ++i) { Filters.push_back(filters.asString(i)); } // add the search paths for (uint i=0; i<paths.size(); ++i) { CPath::addSearchPath(NLMISC::expandEnvironmentVariables(paths.asString(i)), true, false); } // init ligo LigoConfig.readPrimitiveClass("world_editor_classes.xml", false); CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; nlinfo("Synchronizing folder '%s' to '%s'", srcPath.asString().c_str(), dstPath.asString().c_str()); // do a recursive scan of the src and dst folder syncFolder(srcPath.asString(), dstPath.asString()); return 0; }