Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / pipeline / project_config.cpp
blob7d34846f0baf4a9f834f870e043e4d79eb57b340
1 // NeL - MMORPG Framework <https://wiki.ryzom.dev/>
2 // Copyright (C) 2015 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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 // Author: Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
22 #include <nel/misc/types_nl.h>
23 #include "nel/pipeline/project_config.h"
25 #ifdef NL_OS_WINDOWS
26 # include <Windows.h>
27 #else
28 # include <stdlib.h>
29 #endif
31 #include <algorithm>
33 #include <nel/misc/debug.h>
34 #include <nel/misc/path.h>
35 #include <nel/misc/config_file.h>
37 using namespace std;
38 using namespace NLMISC;
40 namespace NLPIPELINE {
42 TPathString CProjectConfig::s_AssetConfigPath;
43 TPathString CProjectConfig::s_ProjectConfigPath;
44 std::vector<NLMISC::CConfigFile *> CProjectConfig::s_ConfigFiles;
45 std::vector<TPathString> CProjectConfig::s_ConfigPaths;
46 CProjectConfig CProjectConfig::s_Instance;
47 uint32 CProjectConfig::s_AssetConfigModification;
48 uint32 CProjectConfig::s_ProjectConfigModification;
49 CProjectConfig::Flags CProjectConfig::s_InitFlags = (CProjectConfig::Flags)0;
50 std::string CProjectConfig::s_ProjectName;
52 static std::set<TPathString> s_SearchPaths;
54 void CProjectConfig::cleanup()
56 for (std::vector<NLMISC::CConfigFile *>::iterator it(s_ConfigFiles.begin()), end(s_ConfigFiles.end()); it != end; ++it)
57 delete *it;
58 s_ConfigFiles.clear();
61 CProjectConfig::~CProjectConfig()
63 cleanup();
66 bool CProjectConfig::init(const std::string &asset, Flags flags, bool partial)
68 TPathString rootPath = NLMISC::CPath::standardizePath(asset, false);
69 TPathString configPath = rootPath + "/nel.cfg";
70 while (!CFile::fileExists(configPath))
72 std::string::size_type sep = CFile::getLastSeparator(rootPath);
73 if (sep == string::npos)
74 return false;
76 rootPath = rootPath.substr(0, sep);
77 if (rootPath.empty())
78 return false;
80 configPath = rootPath + "/nel.cfg";
83 rootPath += "/";
84 uint32 configFileModification = CFile::getFileModificationDate(configPath);
85 bool assetConfigSame = configPath == s_AssetConfigPath && s_AssetConfigModification == configFileModification && s_InitFlags == flags;
87 std::vector<TPathString> configRootPaths;
88 TPathString projectConfigPath;
89 uint32 projectConfigModification = 0;
90 std::string projectName;
91 if (partial)
93 if (assetConfigSame && s_ProjectConfigPath.empty())
94 return true; // Do not reload
96 else
98 if (assetConfigSame && !s_ProjectConfigPath.empty() && CFile::fileExists(s_ProjectConfigPath))
100 projectConfigModification = CFile::getFileModificationDate(s_ProjectConfigPath);
102 if (s_ProjectConfigModification == projectConfigModification)
103 return true; // Do not reload
106 // Search for project and load up all root paths
107 std::vector<std::string> files;
108 CPath::getPathContent(CPath::getApplicationDirectory("NeL", true) + "/projects", false, false, true, files);
109 for (std::vector<std::string>::iterator it(files.begin()), end(files.end()); it != end; ++it)
111 const std::string& file = *it;
112 if (file.length() >= 4 && (file.compare(file.length() - 4, 4, ".cfg") == 0))
114 CConfigFile project;
115 project.load(file);
116 CConfigFile::CVar &directories = project.getVar("Directories");
117 bool isProject = false;
118 for (uint i = 0; i < directories.size(); ++i)
120 if (rootPath == CPath::standardizePath(directories.asString(i), true))
122 isProject = true;
123 break;
126 if (isProject)
128 projectConfigModification = CFile::getFileModificationDate(file);
129 projectConfigPath = file;
131 for (uint i = 0; i < directories.size(); ++i)
133 std::string dir = CPath::standardizePath(directories.asString(i), true);
134 std::string cfgPath = dir + "nel.cfg";
135 if (CFile::fileExists(cfgPath))
136 configRootPaths.push_back(dir);
139 projectName = project.getVar("ProjectName").asString();
141 break;
147 if (projectConfigPath.empty())
149 projectName = "NeL Project";
150 configRootPaths.push_back(rootPath);
151 projectConfigModification = 0;
154 nldebug("Initializing project config '%s'", projectConfigPath.empty() ? configPath.c_str() : projectConfigPath.c_str());
155 release();
157 s_InitFlags = flags;
158 s_AssetConfigPath = configPath;
159 s_AssetConfigModification = configFileModification;
160 s_ProjectConfigPath = projectConfigPath;
161 s_ProjectConfigModification = projectConfigModification;
162 s_ProjectName = projectName;
163 s_ConfigPaths = configRootPaths;
165 std::map<std::string, CConfigFile *> configFiles;
166 for (std::vector<TPathString>::iterator it(configRootPaths.begin()), end(configRootPaths.end()); it != end; ++it)
168 const std::string &dir = *it;
169 const std::string &cfgPath = *it + "nel.cfg";
170 CConfigFile *cfgFile = new CConfigFile();
171 cfgFile->load(cfgPath);
172 std::string identifier = cfgFile->getVar("Identifier").asString();
173 if (configFiles.find(identifier) != configFiles.end()) // Identifier already exists
175 if (dir == rootPath)
177 // Replace config that was already added, asset root gets priority
178 std::vector<NLMISC::CConfigFile *>::iterator old = std::find(s_ConfigFiles.begin(), s_ConfigFiles.end(), configFiles[identifier]);
179 uint idx = old - s_ConfigFiles.begin();
180 s_ConfigFiles.erase(old);
181 s_ConfigPaths.erase(s_ConfigPaths.begin() + idx);
183 else
185 // Skip, first listed config gets priority
186 s_ConfigPaths.erase(s_ConfigPaths.begin() + s_ConfigFiles.size());
187 continue;
190 #ifdef NL_OS_WINDOWS
191 SetEnvironmentVariableA(identifier.c_str(), dir.c_str());
192 #else
193 setenv(identifier.c_str(), dir.c_str(), 1);
194 #endif
195 configFiles[identifier] = cfgFile;
196 s_ConfigFiles.push_back(cfgFile);
199 nlassert(s_ConfigFiles.size() == s_ConfigPaths.size());
201 if (flags & DatabaseTextureSearchPaths)
203 searchDirectories("DatabaseTextureSearchPaths");
206 return true;
209 void CProjectConfig::searchDirectories(const char *var)
211 for (uint i = 0; i < s_ConfigFiles.size(); ++i)
213 CConfigFile *cfg = s_ConfigFiles[i];
214 const TPathString &dir = s_ConfigPaths[i];
215 CConfigFile::CVar *paths = cfg->getVarPtr(var);
216 if (paths)
218 for (uint i = 0; i < paths->size(); i++)
220 TPathString path = paths->asString(i);
221 if (!CPath::isAbsolutePath(path)) path = dir + path;
222 path = CPath::standardizePath(path);
223 if (s_SearchPaths.find(path) == s_SearchPaths.end())
225 CPath::addSearchPath(path);
226 s_SearchPaths.insert(path);
233 void CProjectConfig::release()
235 s_SearchPaths.clear();
236 CPath::clearMap();
237 cleanup();
240 } /* namespace NLPIPELINE */
242 /* end of file */