swift
xplaneutil.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <qsystemdetection.h>
7 
8 #include <QDir>
9 #include <QFile>
10 #include <QFileInfo>
11 #include <QIODevice>
12 #include <QTextStream>
13 
14 #include "misc/directoryutils.h"
15 #include "misc/fileutils.h"
16 #include "misc/swiftdirectories.h"
17 
18 #if defined(Q_OS_WIN)
19 # include <ShlObj.h>
20 #endif
21 
22 using namespace swift::misc;
23 
24 namespace swift::misc::simulation::xplane
25 {
26  const QStringList &CXPlaneUtil::getLogCategories()
27  {
28  static const QStringList cats { CLogCategories::matching() };
29  return cats;
30  }
31 
32  // Returns the last path from filePath, which does exist on the file system
33  QString getLastExistingPathFromFile(const QString &filePath)
34  {
35  const QFileInfo fileInfo(filePath);
36  if (!fileInfo.exists()) { return {}; }
37  QFile file(fileInfo.absoluteFilePath());
38  if (!file.open(QIODevice::ReadOnly)) { return {}; }
39 
40  QString lastLine;
41  QTextStream ts(&file);
42  while (!ts.atEnd())
43  {
44  QString pathToCheck = ts.readLine();
45  if (QFileInfo::exists(pathToCheck)) { lastLine = pathToCheck; }
46  }
47  return lastLine;
48  }
49 
50 #if defined(Q_OS_WIN)
51  QString getWindowsLocalAppDataPath()
52  {
53  QString result;
54  TCHAR szLocalAppDataPath[MAX_PATH];
55  if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, szLocalAppDataPath)))
56  {
57  result = QString::fromWCharArray(szLocalAppDataPath);
58  }
59  return result;
60  }
61 #endif
62 
64  {
65  static const QString xplaneInstallFile("x-plane_install.txt");
66  const QString xplaneInstallFilePath = xplaneDir(xplaneInstallFile);
67  return getLastExistingPathFromFile(xplaneInstallFilePath);
68  }
69 
71  {
72  static const QString xplaneInstallFile("x-plane_install_10.txt");
73  const QString xplaneInstallFilePath = xplaneDir(xplaneInstallFile);
74  return getLastExistingPathFromFile(xplaneInstallFilePath);
75  }
76 
78  {
79  static const QString xplaneInstallFile("x-plane_install_11.txt");
80  const QString xplaneInstallFilePath = xplaneDir(xplaneInstallFile);
81  return getLastExistingPathFromFile(xplaneInstallFilePath);
82  }
83 
84  QString CXPlaneUtil::xplaneDir(const QString &xplaneInstallFile)
85  {
88 #if defined(Q_OS_WIN)
89  return CFileUtils::appendFilePathsAndFixUnc(getWindowsLocalAppDataPath(), xplaneInstallFile);
90 #elif defined(Q_OS_LINUX)
91  static const QString xp(".x-plane");
92  return CFileUtils::appendFilePaths(QDir::homePath(), xp, xplaneInstallFile);
93 #elif defined(Q_OS_OSX)
94  static const QString lib("Library/Preferences");
95  return CFileUtils::appendFilePaths(QDir::homePath(), lib, xplaneInstallFile);
96 #endif
97  }
98 
99  const QString &CXPlaneUtil::xplaneRootDir()
100  {
101  static const QString dir = [] {
102  if (!xplane11Dir().isEmpty()) { return xplane11Dir(); }
103  else if (!xplane10Dir().isEmpty()) { return xplane10Dir(); }
104  else if (!xplane9Dir().isEmpty()) { return xplane9Dir(); }
105  else { return QString(); }
106  }();
107  return dir;
108  }
109 
111  {
112  static const bool exists = QDir(xplaneRootDir()).exists();
113  return exists;
114  }
115 
117  {
118  static const QString pd(
120  return pd;
121  }
122 
123  QString CXPlaneUtil::pluginDirFromRootDir(const QString &rootDir)
124  {
126  }
127 
128  QStringList CXPlaneUtil::modelDirectoriesFromSimDir(const QString &simulatorDir)
129  {
130  if (simulatorDir.isEmpty()) { return QStringList(); }
131  return QStringList({ simulatorDir });
132  }
133 
135  {
136  static const QString p("Resources/plugins");
137  return p;
138  }
139 
141  {
142  static const QString p("xswiftbus");
143  return p;
144  }
145 
147  {
148  static const bool exists = QDir(xplanePluginDir()).exists();
149  return exists;
150  }
151 
152  QStringList CXPlaneUtil::pluginSubdirectories(const QString &pluginDir)
153  {
154  const QString dirName = pluginDir.isEmpty() ? xplaneRootDir() : pluginDir;
155  if (!CDirectoryUtils::isDirExisting(dirName)) { return QStringList(); }
156  const QDir dir(dirName);
157  return dir.entryList(QDir::Dirs, QDir::Name | QDir::IgnoreCase);
158  }
159 
161  {
162  static const QStringList dirs = xplaneRootDir().isEmpty() ? QStringList() : QStringList({ xplaneRootDir() });
163  return dirs;
164  }
165 
167  {
168  static const QStringList empty;
169  return empty;
170  }
171 
172  QString CXPlaneUtil::xswiftbusPluginDir(const QString &xplaneRootDir)
173  {
174  const QString rootDir = xplaneRootDir.isEmpty() ? CXPlaneUtil::xplaneRootDir() : xplaneRootDir;
175  if (!rootDir.isEmpty())
176  {
177  const QString xswiftbusDir = CFileUtils::appendFilePathsAndFixUnc(
179  if (CDirectoryUtils::isDirExisting(xswiftbusDir)) { return xswiftbusDir; }
180  }
181  return {};
182  }
183 
184  QString CXPlaneUtil::xswiftbusLegacyDir(const QString &xplaneRootDir)
185  {
186  static const QString legacyPath("/Resources/plugins/xswiftbus/LegacyData");
187  // Return the first non empty path, we can find.
188 
189  const QString rootDir = xplaneRootDir.isEmpty() ? CXPlaneUtil::xplaneRootDir() : xplaneRootDir;
190  if (!rootDir.isEmpty())
191  {
192  const QString xswiftbusLegacy = CFileUtils::appendFilePathsAndFixUnc(xplaneRootDir, legacyPath);
193  if (CDirectoryUtils::isDirExisting(xswiftbusLegacy)) { return xswiftbusLegacy; }
194  }
195  return {};
196  }
197 
198  bool CXPlaneUtil::hasXSwiftBusBuildAndPluginDir(const QString &xplaneRootDir)
199  {
200  if (CSwiftDirectories::getXSwiftBusBuildDirectory().isEmpty()) { return false; }
201  const QString xswiftBusPluginDir = CXPlaneUtil::xswiftbusPluginDir(xplaneRootDir);
202  return (!xswiftBusPluginDir.isEmpty());
203  }
204 
205  QStringList CXPlaneUtil::findConflictingPlugins(const QString &pluginDir)
206  {
207  const QStringList files = findAllXplFiles(pluginDir);
208  QStringList conflicts;
209  for (const QString &file : files)
210  {
211  if (file.contains("swift", Qt::CaseInsensitive)) { continue; }
212  if (file.contains("ivap", Qt::CaseInsensitive))
213  {
214  conflicts.push_back(file);
215  continue;
216  }
217  if (file.contains("XSquawkBox", Qt::CaseInsensitive))
218  {
219  conflicts.push_back(file);
220  continue;
221  }
222  }
223  return conflicts;
224  }
225 
226  QStringList CXPlaneUtil::findAllXplFiles(const QString &pluginDir)
227  {
228  const QString dirName = CFileUtils::fixWindowsUncPath(pluginDir.isEmpty() ? xplaneRootDir() : pluginDir);
229  const QDir directory(dirName);
230  if (!directory.exists()) { return QStringList(); }
231 
232  // this finds the current levels XPLs
233  QStringList files = directory.entryList(xplFileFilter(), QDir::Files, QDir::Name | QDir::IgnoreCase);
234  const QStringList dirs = directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase);
235  for (const QString &dir : dirs)
236  {
237  const QString subDir = CFileUtils::appendFilePaths(dirName, dir);
238  const QStringList subDirFiles = CXPlaneUtil::findAllXplFiles(subDir);
239  if (subDirFiles.isEmpty()) { continue; }
240  for (const QString &file : subDirFiles) { files.push_back(CFileUtils::appendFilePaths(dir, file)); }
241  }
242  return files;
243  }
244 
245  bool CXPlaneUtil::hasNewerXSwiftBusBuild(const QString &xplaneRootDir)
246  {
247  if (CSwiftDirectories::getXSwiftBusBuildDirectory().isEmpty()) { return false; }
248  const QString xswiftBusPluginDir = CXPlaneUtil::xswiftbusPluginDir(xplaneRootDir);
249  if (xswiftBusPluginDir.isEmpty()) { return false; }
250 
251  const QFileInfo fiLatestBuild =
253  if (!fiLatestBuild.lastModified().isValid()) { return false; }
254 
255  const QFileInfo fiLatestDeployed = CFileUtils::findLastModified(xswiftBusPluginDir, true, xplFileFilter());
256  if (!fiLatestDeployed.lastModified().isValid()) { return true; } // not yet existing
257 
258  // newer?
259  return fiLatestBuild.lastModified() > fiLatestDeployed.lastModified();
260  }
261 
262  int CXPlaneUtil::copyXSwiftBusBuildFiles(const QString &xplaneRootDir)
263  {
264  if (CSwiftDirectories::getXSwiftBusBuildDirectory().isEmpty()) { return -1; }
265  const QString xswiftBusPluginDir = CXPlaneUtil::xswiftbusPluginDir(xplaneRootDir);
266  if (xswiftBusPluginDir.isEmpty()) { return -1; }
267 
269  xswiftBusPluginDir, true);
270  }
271 
272  const QStringList &CXPlaneUtil::xplFileFilter()
273  {
274  static const QStringList filter({ "*.xpl" });
275  return filter;
276  }
277 
278  CStatusMessageList CXPlaneUtil::validateModelDirectories(const QString &simDir, const QStringList &modelDirectories)
279  {
280  if (simDir.isEmpty())
281  {
282  return CStatusMessage(static_cast<CXPlaneUtil *>(nullptr), CStatusMessage::SeverityWarning,
283  u"no simulator directory", true);
284  }
285 
286  CStatusMessageList msgs;
287  if (modelDirectories.isEmpty()) { return msgs; }
288  const QDir sd(simDir);
289  const bool simDirExists = sd.exists();
290  for (const QString &modelDir : modelDirectories)
291  {
292  const bool exists = simDirExists ?
294  CDirectoryUtils::isSameOrSubDirectoryOfStringBased(modelDir, sd.absolutePath());
295  const CStatusMessage m =
296  exists ?
297  CStatusMessage(static_cast<CXPlaneUtil *>(nullptr)).info(u"Model directory '%1' inside '%2'")
298  << modelDir << sd.absolutePath() :
299  CStatusMessage(static_cast<CXPlaneUtil *>(nullptr)).error(u"Model directory '%1' NOT inside '%2'")
300  << modelDir << sd.absolutePath();
301  msgs.push_back(m);
302  }
303  msgs.addValidationCategory();
304  return msgs;
305  }
306 } // namespace swift::misc::simulation::xplane
static bool isSameOrSubDirectoryOf(const QString &testDir, const QString &parentDir)
Is "testDir" a subdirectory (possibly nested) of "parentDir" or the same directory.
static bool isSameOrSubDirectoryOfStringBased(const QString &testDir, const QString &parentDir)
Is "testDir" a subdirectory (possibly nested) of "parentDir" or the same directory.
static int copyDirectoryRecursively(const QString &fromDir, const QString &toDir, bool replaceOnConflict)
Copy directory recursively.
static bool isDirExisting(const QString &path)
Directory existing? Also checking UNC paths upfront.
static QFileInfo findLastModified(const QDir &dir, bool recursive, const QStringList &nameFilters={}, const QStringList &excludeDirectories={})
Returns path to the last modifed file in dir, optionally matching a wildcard, or empty string.
Definition: fileutils.cpp:370
static QString appendFilePathsAndFixUnc(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:108
static QString appendFilePaths(const QString &path1, const QString &path2)
Append file paths.
Definition: fileutils.cpp:95
static QString fixWindowsUncPath(const QString &filePath)
UNC file paths on Qt start with "/", but UNC file paths only work when they start with "//".
Definition: fileutils.cpp:444
static const QString & matching()
Matching.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
Streamable status message, e.g.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
void addValidationCategory()
Validation category.
static const QString & getXSwiftBusBuildDirectory()
The build directory.
static bool hasXSwiftBusBuildAndPluginDir(const QString &xplaneRootDir)
Both directories, plugin and.
Definition: xplaneutil.cpp:198
static CStatusMessageList validateModelDirectories(const QString &simDir, const QStringList &modelDirectories)
Validate the model directories.
Definition: xplaneutil.cpp:278
static const QStringList & xplaneModelExcludeDirectoryPatterns()
Exclude directories for models.
Definition: xplaneutil.cpp:166
static bool isXplaneRootDirExisting()
Is the xplaneRootDir existing?
Definition: xplaneutil.cpp:110
static QStringList modelDirectoriesFromSimDir(const QString &simulatorDir)
Model directories from simultaor directory.
Definition: xplaneutil.cpp:128
static const QStringList & getLogCategories()
Log categories.
Definition: xplaneutil.cpp:26
static QStringList findAllXplFiles(const QString &pluginDir={})
Definition: xplaneutil.cpp:226
static QStringList findConflictingPlugins(const QString &pluginDir={})
Finds conflicting plugins.
Definition: xplaneutil.cpp:205
static const QString & xplaneRootDir()
XPlane root directory In case more then one XPlane version is found, the path to the highest version ...
Definition: xplaneutil.cpp:99
static QString xswiftbusPluginDir(const QString &xplaneRootDir=CXPlaneUtil::xplaneRootDir())
xswiftbus plugin directory
Definition: xplaneutil.cpp:172
static bool isXplanePluginDirDirExisting()
Is the xplanePluginDir existing?
Definition: xplaneutil.cpp:146
static const QString & xswiftbusPathName()
xswiftbus path name
Definition: xplaneutil.cpp:140
static const QStringList & xplaneModelDirectories()
Directories with models.
Definition: xplaneutil.cpp:160
static bool hasNewerXSwiftBusBuild(const QString &xplaneRootDir=CXPlaneUtil::xplaneRootDir())
Newer xswiftbus build.
Definition: xplaneutil.cpp:245
static QString xplane11Dir()
XPlane 11 directory.
Definition: xplaneutil.cpp:77
static QString xswiftbusLegacyDir(const QString &xplaneRootDir=CXPlaneUtil::xplaneRootDir())
xswiftbus legacy directory
Definition: xplaneutil.cpp:184
static QString xplane10Dir()
XPlane 10 directory.
Definition: xplaneutil.cpp:70
static QString xplane9Dir()
XPlane 9 directory.
Definition: xplaneutil.cpp:63
static int copyXSwiftBusBuildFiles(const QString &xplaneRootDir=CXPlaneUtil::xplaneRootDir())
Copy a xswiftbus build.
Definition: xplaneutil.cpp:262
static const QString & xplanePluginPathName()
XPlane relative plugin path.
Definition: xplaneutil.cpp:134
static QString pluginDirFromRootDir(const QString &rootDir=CXPlaneUtil::xplaneRootDir())
Plugin directory from given simulator directory.
Definition: xplaneutil.cpp:123
static const QString & xplanePluginDir()
XPlane plugin directory In case more then one XPlane version is found, the path to the highest versio...
Definition: xplaneutil.cpp:116
static QStringList pluginSubdirectories(const QString &pluginDir={})
All sub directories of the plugin directory.
Definition: xplaneutil.cpp:152
static const QStringList & xplFileFilter()
Filter filter for xpl files.
Definition: xplaneutil.cpp:272
Free functions in swift::misc.