23 #include "datapaths.h" 25 #include "application.h" 26 #include "doomseekerfilepaths.h" 27 #include "fileutils.h" 29 #include "plugins/engineplugin.h" 30 #include "strings.hpp" 35 #include <QCoreApplication> 36 #include <QDesktopServices> 38 #include <QProcessEnvironment> 41 #include <QStandardPaths> 44 #if !defined(INSTALL_PREFIX) || !defined(INSTALL_LIBDIR) 45 #error Build system should provide definition for INSTALL_PREFIX and INSTALL_LIBDIR 53 extern Q_CORE_EXPORT
int qt_ntfs_permission_lookup;
56 int qt_ntfs_permission_lookup;
59 static QList<DataPaths::DirErrno> uniqueErrnosByDir(
const QList<DataPaths::DirErrno> &errnos)
61 QSet<QString> uniqueDirs;
62 QList<DataPaths::DirErrno> uniqueErrnos;
65 if (!uniqueDirs.contains(dirErrno.directory.path()))
67 uniqueDirs.insert(dirErrno.directory.path());
68 uniqueErrnos << dirErrno;
74 static QStringList uniquePaths(
const QStringList &paths)
76 QList<QFileInfo> uniqueMarkers;
78 for (
const QString &path : paths)
80 if (!uniqueMarkers.contains(path))
82 uniqueMarkers << path;
92 static const QString PLUGINS_DIR_NAME;
98 QString workingDirectory;
100 bool bIsPortableModeOn;
107 static const QString LEGACY_APPDATA_DIR_NAME = ".doomseeker";
108 static const QString DEMOS_DIR_NAME = "demos";
110 const QString
DataPaths::CHATLOGS_DIR_NAME = "chatlogs";
112 const QString
DataPaths::TRANSLATIONS_DIR_NAME = "translations";
113 const QString
DataPaths::UPDATE_PACKAGES_DIR_NAME = "updates";
114 const QString
DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX = "doomseeker-update-pkg-";
118 d->bIsPortableModeOn = bPortableModeOn;
126 d->cacheDirectory.setPath(systemAppDataDirectory(
".cache"));
127 d->configDirectory.setPath(systemAppDataDirectory(LEGACY_APPDATA_DIR_NAME));
128 d->dataDirectory.setPath(systemAppDataDirectory(
".static"));
132 d->cacheDirectory.setPath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
133 #if QT_VERSION >= 0x050500 135 d->configDirectory.setPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
139 QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation),
142 d->dataDirectory.setPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
145 gLog << QString(
"Cache directory: %1").arg(d->cacheDirectory.absolutePath());
146 gLog << QString(
"Config directory: %1").arg(d->configDirectory.absolutePath());
147 gLog << QString(
"Data directory: %1").arg(d->dataDirectory.absolutePath());
150 DataPaths::~DataPaths()
156 return d->cacheDirectory.absolutePath();
161 QStringList failedList;
165 failedList.append(dataDirectory);
172 QList<DirErrno> failedDirs;
177 if (cacheDirError.isError())
178 failedDirs << cacheDirError;
184 if (!d->configDirectory.exists(DoomseekerFilePaths::INI_FILENAME))
187 if (configDirError.isError())
188 failedDirs << configDirError;
190 #if !defined(Q_OS_DARWIN) 191 else if (appDataDir.exists(
".doomseeker"))
194 const QDir oldConfigDir(appDataDir.absolutePath() + QDir::separator() +
".doomseeker");
195 gLog << QString(
"Migrating configuration data from '%1'\n\tto '%2'.")
196 .arg(oldConfigDir.absolutePath())
197 .arg(d->configDirectory.absolutePath());
199 for (QFileInfo fileinfo : oldConfigDir.entryInfoList(QStringList(
"*.ini"), QDir::Files))
201 QFile(fileinfo.absoluteFilePath()).copy(d->configDirectory.absoluteFilePath(fileinfo.fileName()));
210 if (!d->dataDirectory.exists(DEMOS_DIR_NAME))
213 const QString legacyPrefDirectory =
"Library/Preferences/Doomseeker";
215 const QString legacyPrefDirectory =
".doomseeker";
218 if (dataDirError.isError())
219 failedDirs << dataDirError;
220 else if (appDataDir.exists(legacyPrefDirectory))
223 const QDir oldConfigDir(appDataDir.absolutePath() + QDir::separator() + legacyPrefDirectory);
224 gLog << QString(
"Migrating user data from '%1'\n\tto '%2'.")
225 .arg(oldConfigDir.absolutePath())
226 .arg(d->dataDirectory.absolutePath());
228 for (QFileInfo fileinfo : oldConfigDir.entryInfoList(QDir::Dirs))
230 const QString origPath = fileinfo.absoluteFilePath();
231 QFile file(origPath);
232 if (file.rename(d->dataDirectory.absoluteFilePath(fileinfo.fileName())))
236 #if !defined(Q_OS_WIN32) 245 if (demosDirError.isError())
246 failedDirs << demosDirError;
248 return uniqueErrnosByDir(failedDirs);
253 return staticDefaultInstance;
259 QStringList filePaths;
266 if (progBinDirName !=
"bin" && progBinDirName !=
"MacOS")
272 QString DataPaths::demosDirectoryPath()
const 274 return d->dataDirectory.absoluteFilePath(DEMOS_DIR_NAME);
280 if (!isPortableModeOn())
282 rootPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
290 QString DataPaths::env(
const QString &key)
292 return QProcessEnvironment::systemEnvironment().value(key);
295 void DataPaths::initDefault(
bool bPortableModeOn)
297 assert(staticDefaultInstance ==
nullptr &&
"DataPaths can have only one default.");
298 if (staticDefaultInstance ==
nullptr)
299 staticDefaultInstance =
new DataPaths(bPortableModeOn);
302 bool DataPaths::isPortableModeOn()
const 304 return d->bIsPortableModeOn;
330 #if !defined(Q_OS_DARWIN) && !defined(Q_OS_WIN32) 334 const QString installDir = INSTALL_PREFIX
"/" INSTALL_LIBDIR
"/doomseeker/";
336 paths = QStringList(installDir);
338 paths.append(installDir);
341 paths = uniquePaths(paths);
348 QString envVarName =
"";
353 envVarName =
"ProgramFiles(x86)";
357 envVarName =
"ProgramW6432";
361 envVarName =
"ProgramFiles";
368 QString path = env(envVarName);
369 if (path.isEmpty() && machineType != Preferred)
382 Q_UNUSED(machineType);
389 return d->configDirectory.absolutePath();
400 paths.append(QDir::currentPath());
401 paths.append(QCoreApplication::applicationDirPath());
403 paths.append(INSTALL_PREFIX
"/share/doomseeker");
405 paths = uniquePaths(paths);
406 QString subdirFiltered = subdir.trimmed();
407 if (!subdirFiltered.isEmpty())
409 for (
int i = 0; i < paths.size(); ++i)
417 Strings::triml(append,
"/\\");
419 if (isPortableModeOn())
421 QString path = d->workingDirectory +
"/" + append;
422 return QDir(path).absolutePath();
431 QString envVar = env(
"APPDATA");
439 dir = QDir::homePath();
444 Strings::trimr(dir,
"/\\");
446 dir += QDir::separator() + append;
448 return QDir(dir).absolutePath();
457 if (!rootDir.mkpath(dirToCreate))
459 int errnoval = errno;
461 return DirErrno(fullDirPath, errnoval, strerror(errnoval));
469 if (!pathToBottomMostExisting.isEmpty())
471 QFileInfo parentDir(pathToBottomMostExisting);
472 if (parentDir.exists() && !parentDir.isDir())
474 return DirErrno(fullDirPath, DirErrno::CUSTOM_ERROR,
475 QObject::tr(
"parent node is not a directory: %1")
476 .arg(parentDir.filePath()));
480 ++qt_ntfs_permission_lookup;
481 bool permissions = parentDir.isReadable()
482 && parentDir.isWritable()
483 && parentDir.isExecutable();
484 --qt_ntfs_permission_lookup;
488 return DirErrno(fullDirPath, DirErrno::CUSTOM_ERROR,
489 QObject::tr(
"lack of necessary permissions to the parent directory: %1")
490 .arg(parentDir.filePath()));
494 return DirErrno(fullDirPath, DirErrno::CUSTOM_ERROR, QObject::tr(
"cannot create directory"));
507 QFileInfo fileInfo(path);
509 bool bCondition1 = !path.isEmpty();
510 bool bCondition2 = fileInfo.exists();
511 bool bCondition3 = fileInfo.isDir();
513 return bCondition1 && bCondition2 && bCondition3;
518 return d->workingDirectory;
static QString combinePaths(QString pathFront, QString pathEnd)
QStringList defaultWadPaths() const
Returns a list of the default file paths for WAD files.
QString pluginDocumentsLocationPath(const EnginePlugin &plugin) const
Place where EnginePlugin can store its "My Documents" content.
QList< DirErrno > createDirectories()
Creates necessary directories for application run.
void setWorkingDirectory(const QString &workingDirectory)
Changes the location returned by workingDirectory.
static bool validateDir(const QString &path)
QString programsDataDirectoryPath() const
Path to directory where this concrete application should store its data.
QString cacheLocationPath() const
Path to the cache directory with Doomseeker's own subpath suffix.
Represents directories used by Doomseeker to store data.
static QString cdUpUntilExists(QString path)
Moves upwards the path until it finds the path that exists.
QStringList pluginSearchLocationPaths() const
Ordered locations were plugin libraries could be loaded from.
static const QString NAME
Program name - doomseeker.
Struct which contains the relevant QDir, the errno reported, and the QString generated by the errno...
static QString programFilesDirectory(MachineType machineType)
QString localDataLocationPath(const QString &subpath=QString()) const
Path to the directory where large data should be saved.
const QString & workingDirectory() const
Program working directory.
QString documentsLocationPath(const QString &subpath=QString()) const
Path to the "My Documents" directory with Doomseeker's own subpath suffix.
static QStringList combineManyPaths(const QStringList &fronts, const QString &pathEnd)
Combines path suffix with all fronts, returns new list.
QString nameCanonical() const
Either specified explicitly by plugin or derived from the actual plugin name.
QStringList canWrite() const
Checks if all directories can be written to.
QString systemAppDataDirectory(QString append=QString()) const
Gets path to the root directory for data storage.
bool validateAppDataDirectory()
Checks if the root directory for Doomseeker data storage is accessible.
QString pluginLocalDataLocationPath(const EnginePlugin &plugin) const
Place where EnginePlugin can store its local files.
DirErrno tryCreateDirectory(const QDir &rootDir, const QString &dirToCreate) const
If directory already exists true is returned.
static QStringList staticDataSearchDirs(const QString &subdir=QString())
Paths to directories where program should search for its static data.
static DataPaths * defaultInstance()
Retrieves default instance that is used throughout the program.