void DataDirLocater::AddPortableDir() { const std::string dd_curWorkDir = GetBinaryLocation(); // This is useful in case of multiple engine/unitsync versions installed // together in a sub-dir of the data-dir // The data-dir structure then might look similar to this: // maps/ // games/ // engines/engine-0.83.0.0.exe // engines/engine-0.83.1.0.exe // unitsyncs/unitsync-0.83.0.0.exe // unitsyncs/unitsync-0.83.1.0.exe const std::string curWorkDirParent = FileSystem::GetParent(dd_curWorkDir); if ((curWorkDirParent != "") && LooksLikeMultiVersionDataDir(curWorkDirParent)) { AddDirs(curWorkDirParent); // "../" } AddDirs(dd_curWorkDir); }
void DataDirLocater::AddCwdOrParentDir(const std::string& curWorkDir, bool forceAdd) { // This is useful in case of multiple engine/unitsync versions installed // together in a sub-dir of the data-dir // The data-dir structure then might look similar to this: // maps/ // games/ // engines/engine-0.83.0.0.exe // engines/engine-0.83.1.0.exe // unitsyncs/unitsync-0.83.0.0.exe // unitsyncs/unitsync-0.83.1.0.exe const std::string curWorkDirParent = FileSystem::GetParent(curWorkDir); // we can not add both ./ and ../ as data-dir if ((curWorkDirParent != "") && LooksLikeMultiVersionDataDir(curWorkDirParent)) { AddDirs(curWorkDirParent); // "../" } else if (IsPortableMode() || forceAdd) { // always using this would be unclean, because spring and unitsync // would end up with different sets of data-dirs AddDirs(curWorkDir); // "./" } }
void DataDirLocater::LocateDataDirs() { // Prepare the data-dirs defined in different places // environment variable std::string dd_env = ""; { char* env = getenv("SPRING_DATADIR"); if (env && *env) { dd_env = SubstEnvVars(env); } } #if defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetModulePath(); #else // defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetProcessExecutablePath(); #endif // defined(UNITSYNC) // This is useful in case of multiple engine/unitsync versions installed // together in a sub-dir of the data-dir // The data-dir structure then might look similar to this: // maps/ // games/ // engines/engine-0.83.0.0.exe // engines/engine-0.83.1.0.exe // unitsyncs/unitsync-0.83.0.0.exe // unitsyncs/unitsync-0.83.1.0.exe const std::string dd_curWorkDirParent = FileSystemHandler::GetParent(dd_curWorkDir); #if defined(WIN32) // fetch my documents path TCHAR pathMyDocs[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathMyDocs); // fetch app-data path TCHAR pathAppData[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, pathAppData); std::string dd_myDocs = pathMyDocs; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring dd_myDocs += "\\Spring"; std::string dd_myDocsMyGames = pathMyDocs; // My Documents\My Games seems to be the MS standard even if no official guidelines exist // most if not all new Games For Windows(TM) games use this dir dd_myDocsMyGames += "\\My Games\\Spring"; std::string dd_appData = pathAppData; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring dd_appData += "\\Spring"; #elif defined(MACOSX_BUNDLE) const std::string dd_curWorkDirData = dd_curWorkDir + "/" + SubstEnvVars(DATADIR); const std::string dd_curWorkDirLib = dd_curWorkDir + "/" + SubstEnvVars(LIBDIR); #else // *nix (-OSX) // settings in /etc std::string dd_etc = ""; { FILE* fileH = ::fopen("/etc/spring/datadir", "r"); if (fileH) { const char whiteSpaces[3] = {'\t', ' ', '\0'}; char lineBuf[1024]; while (fgets(lineBuf, sizeof(lineBuf), fileH)) { char* newLineCharPos = strchr(lineBuf, '\n'); if (newLineCharPos) { // remove the new line char *newLineCharPos = '\0'; } // ignore lines consisting of only whitespaces if ((strlen(lineBuf) > 0) && strspn(lineBuf, whiteSpaces) != strlen(lineBuf)) { // append, separated by sPD (depending on OS): ';' or ':' dd_etc = dd_etc + (dd_etc.empty() ? "" : sPD) + SubstEnvVars(lineBuf); } } fclose(fileH); } } #endif // defined(WIN32), defined(MACOSX_BUNDLE), else // Construct the list of datadirs from various sources. datadirs.clear(); // The first dir added will be the writeable data dir. // same on all platforms AddDirs(dd_env); // ENV{SPRING_DATADIR} // user defined in spring config handler // (Linux: ~/.springrc, Windows: .\springsettings.cfg) AddDirs(SubstEnvVars(configHandler->GetString("SpringData", ""))); #ifdef WIN32 // All MS Windows variants if ((dd_curWorkDirParent != "") && LooksLikeMultiVersionDataDir(dd_curWorkDirParent)) { AddDirs(dd_curWorkDirParent); // "../" } else if (IsPortableMode()) { // we can not add both ./ and ../ as data-dir AddDirs(dd_curWorkDir); // "./" } AddDirs(dd_myDocsMyGames); // "C:/.../My Documents/My Games/Spring/" AddDirs(dd_myDocs); // "C:/.../My Documents/Spring/" AddDirs(dd_appData); // "C:/.../All Users/Applications/Spring/" #elif defined(MACOSX_BUNDLE) // Mac OS X // Maps and mods are supposed to be located in spring's executable location on Mac, but unitsync // cannot find them since it does not know spring binary path. I have no idea but to force users // to locate lobby executables in the same as spring's dir and add its location to search dirs. #ifdef UNITSYNC AddDirs(dd_curWorkDir); // "./" #endif // libs and data are supposed to be located in subdirectories of spring executable, so they // sould be added instead of SPRING_DATADIR definition. AddDirs(dd_curWorkDirData); // "./data/" AddDirs(dd_curWorkDirLib); // "./lib/" #else // Linux, FreeBSD, Solaris, Apple non-bundle if ((dd_curWorkDirParent != "") && LooksLikeMultiVersionDataDir(dd_curWorkDirParent)) { AddDirs(dd_curWorkDirParent); // "../" } else if (IsPortableMode()) { // we can not add both ./ and ../ as data-dir // always using this would be unclean, because spring and unitsync // would end up with different sets of data-dirs AddDirs(dd_curWorkDir); // "./" } AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_etc); // from /etc/spring/datadir #endif #ifdef SPRING_DATADIR AddDirs(SubstEnvVars(SPRING_DATADIR)); // from -DSPRING_DATADIR #endif // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // bail out const std::string errstr = "Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" #ifdef WIN32 "- a SpringData=C:/path/to/data declaration in spring's config file ./springsettings.cfg\n" "- by giving you write access to the installation directory"; #else "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"; #endif throw content_error(errstr); } // for now, chdir to the data directory as a safety measure: // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory FileSystemHandler::GetInstance().Chdir(GetWriteDir()->path.c_str()); // Initialize the log. Only after this moment log will be written to file. logOutput.Initialize(); // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. // Update: now it actually may start before, log has preInitLog. for (std::vector<DataDir>::const_iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->writable) { logOutput.Print("Using read-write data directory: %s", d->path.c_str()); // tag the cache dir const std::string cacheDir = d->path + "cache"; if (filesystem.CreateDirectory(cacheDir)) { CacheDir::SetCacheDir(cacheDir, true); } } else { logOutput.Print("Using read-only data directory: %s", d->path.c_str()); } } }