7 #include <QDomDocument>
8 #include <QDomNodeList>
13 #include <QStandardPaths>
14 #include <QStringBuilder>
15 #include <QStringList>
16 #include <QTextStream>
26 using namespace swift::config;
28 namespace swift::misc::simulation::fscommon
34 static const QStringList cats({ CLogCategories::validation(), CLogCategories::driver() });
38 QString fsxDirFromRegistryImpl()
42 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft Games\\Flight Simulator\\10.0"),
43 QStringLiteral(
"AppPath") },
45 "HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft Games\\Flight Simulator - Steam Edition\\10.0"),
46 QStringLiteral(
"AppPath") },
47 { QStringLiteral(
"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Microsoft Games\\Flight Simulator\\10.0"),
48 QStringLiteral(
"SetupPath") },
50 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft Games\\Flight Simulator\\10.0"),
51 QStringLiteral(
"SetupPath") }
54 for (
const auto ®istryPair : fsxRegistryPathPairs)
57 fsxPath = fsxRegistry.value(registryPair.second).toString().
trimmed();
59 if (fsxPath.
isEmpty()) {
continue; }
60 fsxPath = CFileUtils::normalizeFilePathToQtStandard(fsxPath);
63 const QDir dir(fsxPath);
64 if (dir.exists()) {
break; }
67 return CFileUtils::normalizeFilePathToQtStandard(fsxPath);
70 const QString &CFsDirectories::fsxDirFromRegistry()
72 static const QString fsxPath(fsxDirFromRegistryImpl());
78 const QString dir(CFsDirectories::fsxDirFromRegistry());
79 if (!dir.isEmpty()) {
return dir; }
81 {
"C:/Program Files (x86)/Microsoft Games/Microsoft Flight Simulator X",
"C:/FSX" });
85 someDefaultDirs.
push_back(
"P:/FSX (MSI)");
87 return CFileUtils::findFirstExisting(someDefaultDirs);
92 static const QString dir(fsxDirImpl());
100 for (
const QString &path : locations)
102 const QString msfsPackage = CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(path,
"Packages"),
103 "Microsoft.FlightSimulator_8wekyb3d8bbwe");
104 const QDir d(msfsPackage);
105 if (!d.exists()) {
continue; }
113 path.
replace(
"Local",
"Roaming");
114 const QString msfsPackage = CFileUtils::appendFilePaths(path,
"Microsoft Flight Simulator");
115 const QString fileName = CFileUtils::appendFilePaths(msfsPackage,
"UserCfg.opt");
117 if (!fi.exists()) {
continue; }
125 static const QString dir(msfsDirImpl());
133 QString msfsDirectory(CFsDirectories::msfsDir());
138 userCfg = CFileUtils::appendFilePaths(msfsDirectory,
"UserCfg.opt");
143 CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(msfsDirectory,
"LocalCache"),
"UserCfg.opt");
146 QFile file(CFileUtils::normalizeFilePathToQtStandard(userCfg));
153 if (line.
contains(
"InstalledPackagesPath"))
158 if (split.size() != 3) {
return {}; }
160 const QDir dir(packagePath);
161 if (dir.exists()) {
return packagePath; }
167 const QString &CFsDirectories::msfsPackagesDir()
169 static const QString dir(msfsPackagesDirImpl());
173 static QString msfs2024DirImpl()
176 for (
const QString &path : locations)
178 const QString msfs2024Package = CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(path,
"Packages"),
179 "Microsoft.Limitless_8wekyb3d8bbwe");
180 const QDir d(msfs2024Package);
181 if (!d.exists()) {
continue; }
182 return msfs2024Package;
189 path.
replace(
"Local",
"Roaming");
190 const QString msfsPackage = CFileUtils::appendFilePaths(path,
"Microsoft Flight Simulator 2024");
191 const QString fileName = CFileUtils::appendFilePaths(msfsPackage,
"UserCfg.opt");
193 if (!fi.exists()) {
continue; }
201 static const QString dir(msfs2024DirImpl());
205 QString msfs2024PackagesDirImpl()
209 QString msfs2024Directory(CFsDirectories::msfs2024Dir());
213 userCfg = CFileUtils::appendFilePaths(msfs2024Directory,
"UserCfg.opt");
217 userCfg = CFileUtils::appendFilePaths(CFileUtils::appendFilePaths(msfs2024Directory,
"LocalCache"),
228 if (line.
contains(
"InstalledPackagesPath"))
233 if (split.size() != 3) {
return {}; }
235 const QDir dir(packagePath);
236 if (dir.exists()) {
return packagePath; }
242 const QString &CFsDirectories::msfs2024PackagesDir()
244 static const QString dir(msfs2024PackagesDirImpl());
248 QString fsxSimObjectsDirFromRegistryImpl()
250 const QString fsxPath = CFileUtils::normalizeFilePathToQtStandard(CFsDirectories::fsxDirFromRegistry());
251 if (fsxPath.
isEmpty()) {
return {}; }
252 return CFsDirectories::fsxSimObjectsDirFromSimDir(fsxPath);
255 const QString &CFsDirectories::fsxSimObjectsDirFromRegistry()
257 static const QString fsxPath(fsxSimObjectsDirFromRegistryImpl());
263 QString dir(CFsDirectories::fsxDir());
264 if (dir.isEmpty()) {
return {}; }
265 return CFsDirectories::fsxSimObjectsDirFromSimDir(dir);
268 QString msfsSimObjectsDirImpl()
270 QString dir(CFsDirectories::msfsDir());
271 if (dir.isEmpty()) {
return {}; }
272 return CFileUtils::normalizeFilePathToQtStandard(msfsPackagesDirImpl());
275 QString msfs2024SimObjectsDirImpl()
277 QString dir(CFsDirectories::msfs2024Dir());
278 if (dir.isEmpty()) {
return {}; }
279 return CFileUtils::normalizeFilePathToQtStandard(msfs2024PackagesDirImpl());
282 const QString &CFsDirectories::fsxSimObjectsDir()
284 static const QString dir(fsxSimObjectsDirImpl());
288 const QString &CFsDirectories::msfsSimObjectsDir()
290 static const QString dir(msfsSimObjectsDirImpl());
294 const QString &CFsDirectories::msfs2024SimObjectsDir()
296 static const QString dir(msfs2024SimObjectsDirImpl());
301 if (simDir.
isEmpty()) {
return {}; }
302 return CFileUtils::appendFilePaths(CFileUtils::normalizeFilePathToQtStandard(simDir),
"SimObjects");
305 const QStringList &CFsDirectories::fsxSimObjectsExcludeDirectoryPatterns()
307 static const QStringList exclude {
"SimObjects/Animals",
"SimObjects/Misc",
"SimObjects/GroundVehicles",
308 "SimObjects/Boats" };
312 const QStringList &CFsDirectories::msfs20SimObjectsExcludeDirectoryPatterns()
315 "OneStore/asobo-discovery",
316 "OneStore/asobo-flight",
317 "OneStore/asobo-landingchallenge",
318 "OneStore/asobo-mission",
319 "OneStore/asobo-tutorials",
320 "OneStore/asobo-vcockpits",
321 "OneStore/asobo-simobjects",
322 "OneStore/asobo-services",
323 "OneStore/asobo-vcockpits",
326 "OneStore/asobo-vfx",
329 "OneStore/microsoft-airport",
330 "OneStore/microsoft-bushtrip",
331 "OneStore/microsoft-discovery",
341 const QStringList &CFsDirectories::msfs2024SimObjectsExcludeDirectoryPatterns()
343 static const QStringList exclude {
"PassiveAircraft",
"STUB",
"ZZZZ" };
347 QString p3dDirFromRegistryImpl()
352 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v6"), QStringLiteral(
"AppPath") },
353 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v5"), QStringLiteral(
"AppPath") },
354 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v4"), QStringLiteral(
"AppPath") },
355 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v3"), QStringLiteral(
"AppPath") },
356 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v2"), QStringLiteral(
"AppPath") },
357 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\LockheedMartin\\Prepar3d"), QStringLiteral(
"AppPath") }
359 for (
const auto ®istryPair : p3dRegistryPathPairs)
362 p3dPath = p3dRegistry.value(registryPair.second).toString().
trimmed();
364 if (p3dPath.
isEmpty()) {
continue; }
365 p3dPath = CFileUtils::normalizeFilePathToQtStandard(p3dPath);
368 const QDir dir(p3dPath);
369 if (dir.exists()) {
break; }
375 const QString &CFsDirectories::p3dDirFromRegistry()
377 static const QString p3dPath = CFileUtils::normalizeFilePathToQtStandard(p3dDirFromRegistryImpl());
383 QString dir(CFsDirectories::p3dDirFromRegistry());
384 if (!dir.isEmpty()) {
return dir; }
385 const QStringList someDefaultDirs({
"C:/Program Files (x86)/Lockheed Martin/Prepar3D v4",
386 "C:/Program Files (x86)/Lockheed Martin/Prepar3D v3",
387 "C:/Program Files (x86)/Lockheed Martin/Prepar3D v2",
388 "C:/Program Files (x86)/Lockheed Martin/Prepar3D" });
389 return CFileUtils::findFirstExisting(someDefaultDirs);
394 static const QString dir(p3dDirImpl());
398 QString p3dSimObjectsDirFromRegistryImpl()
400 const QString p3dPath = CFsDirectories::p3dDirFromRegistry();
401 if (p3dPath.
isEmpty()) {
return {}; }
402 return CFsDirectories::fsxSimObjectsDirFromSimDir(p3dPath);
405 const QString &CFsDirectories::p3dSimObjectsDirFromRegistry()
407 static const QString p3dPath(p3dSimObjectsDirFromRegistryImpl());
413 QString dir(CFsDirectories::p3dDir());
414 if (dir.isEmpty()) {
return {}; }
415 return CFsDirectories::p3dSimObjectsDirFromSimDir(dir);
418 const QString &CFsDirectories::p3dSimObjectsDir()
420 static const QString dir(p3dSimObjectsDirImpl());
424 QStringList CFsDirectories::fsxSimObjectsDirPlusAddOnXmlSimObjectsPaths(
const QString &simObjectsDir)
427 QStringList allPaths = CFsDirectories::allFsxSimObjectPaths().values();
428 const QString sod = CFileUtils::normalizeFilePathToQtStandard(
429 simObjectsDir.
isEmpty() ? CFsDirectories::fsxSimObjectsDir() : simObjectsDir);
444 Q_UNUSED(simObjectsDir);
445 static const QStringList Path { CFsDirectories::msfsSimObjectsDir() };
451 Q_UNUSED(simObjectsDir);
452 static const QStringList Path { CFsDirectories::msfs2024SimObjectsDir() };
455 QStringList CFsDirectories::p3dSimObjectsDirPlusAddOnXmlSimObjectsPaths(
const QString &simObjectsDir,
459 QStringList allPaths = CFsDirectories::allP3dAddOnXmlSimObjectPaths(versionHint).values();
460 const QString sod = CFileUtils::normalizeFilePathToQtStandard(
461 simObjectsDir.
isEmpty() ? CFsDirectories::p3dSimObjectsDir() : simObjectsDir);
476 if (candidate.
isEmpty()) {
return "v5"; }
488 if (simDir.
isEmpty()) {
return {}; }
489 return CFileUtils::normalizeFilePathToQtStandard(CFileUtils::appendFilePaths(simDir,
"SimObjects"));
492 const QStringList &CFsDirectories::p3dSimObjectsExcludeDirectoryPatterns()
496 "SimObjects/Animals",
498 "SimObjects/GroundVehicles",
502 "SimObjects/Avatars",
503 "SimObjects/Countermeasures",
504 "SimObjects/Submersible",
505 "SimObjects/Weapons",
510 QString fs9DirFromRegistryImpl()
515 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\DirectPlay\\Applications\\Microsoft Flight Simulator 2004"),
516 QStringLiteral(
"AppPath") },
518 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\DirectPlay\\Applications\\Microsoft Flight "
520 QStringLiteral(
"AppPath") }
523 for (
const auto ®istryPair : fs9RegistryPathPairs)
526 fs9Path = fs9Registry.value(registryPair.second).toString().
trimmed();
528 if (fs9Path.
isEmpty()) {
continue; }
529 fs9Path = CFileUtils::normalizeFilePathToQtStandard(fs9Path);
532 const QDir dir(fs9Path);
533 if (dir.exists()) {
break; }
539 const QString &CFsDirectories::fs9DirFromRegistry()
541 static const QString fs9Path(fs9DirFromRegistryImpl());
547 QString dir(CFsDirectories::fs9DirFromRegistry());
548 if (!dir.isEmpty()) {
return dir; }
549 const QStringList someDefaultDirs({
"C:/Flight Simulator 9",
"C:/FS9" });
550 return CFileUtils::findFirstExisting(someDefaultDirs);
555 static const QString v(fs9DirImpl());
559 QString fs9AircraftDirFromRegistryImpl()
561 QString fs9Path = CFsDirectories::fs9DirFromRegistry();
562 if (fs9Path.
isEmpty()) {
return {}; }
563 return CFsDirectories::fs9AircraftDirFromSimDir(fs9Path);
566 const QString &CFsDirectories::fs9AircraftDirFromRegistry()
568 static const QString dir(fs9AircraftDirFromRegistryImpl());
574 const QString dir(CFsDirectories::fs9Dir());
575 if (dir.isEmpty()) {
return {}; }
576 return CFsDirectories::fs9AircraftDirFromSimDir(dir);
579 const QString &CFsDirectories::fs9AircraftDir()
581 static const QString dir(fs9AircraftDirImpl());
587 if (simDir.
isEmpty()) {
return {}; }
588 return CFileUtils::appendFilePaths(simDir,
"Aircraft");
591 const QStringList &CFsDirectories::fs9AircraftObjectsExcludeDirectoryPatterns()
599 static const QString cfgFile(
"add-ons.cfg");
600 return CFsDirectories::findP3dConfigFiles(cfgFile, versionHint);
605 static const QString cfgFile(
"simobjects.cfg");
606 return CFsDirectories::findP3dConfigFiles(cfgFile, versionHint);
614 for (
const QString &path : locations)
616 const QString pathUp = CFileUtils::appendFilePaths(CFileUtils::pathUp(path),
"Lockheed Martin");
617 const QDir d(pathUp);
618 if (!d.exists()) {
continue; }
619 CLogMessage(
static_cast<CFsDirectories *
>(
nullptr)).
info(u
"P3D config dir: '%1'") << d.absolutePath();
624 for (
const QString &entry : entries)
629 const QString f = CFileUtils::appendFilePaths(d.absolutePath(), entry, configFile);
634 CLogMessage(
static_cast<CFsDirectories *
>(
nullptr)).
info(u
"P3D config file: '%1'") << f;
645 if (configFiles.
isEmpty()) {
return {}; }
647 for (
const QString &configFile : configFiles)
650 const QString fileContent = CFileUtils::readFileToString(configFile);
651 if (fileContent.
isEmpty()) {
continue; }
653 static const QString p(
"Path=");
657 if (i < 0 || line.
endsWith(
'=')) {
continue; }
661 CFileUtils::appendFilePathsAndFixUnc(pathPrefix, path.
toString())));
670 if (addOnPaths.
isEmpty()) {
return {}; }
672 for (
const QString &addOnPath : addOnPaths)
674 const QString filename = CFileUtils::appendFilePaths(addOnPath,
"add-on.xml");
676 QFile file(filename);
683 for (
int i = 0; i < components.
size(); i++)
690 const QString pathValue = CFileUtils::normalizeFilePathToQtStandard(path.
text());
693 if (!correctPath) {
continue; }
698 CFileUtils::appendFilePaths(addOnPath, pathValue);
703 if (!checked ||
QDir(fp).exists())
705 simObjectPaths.
insert(CFileUtils::normalizeFilePathToQtStandard(fp));
712 return simObjectPaths;
718 const QStringList addOnConfigFiles = CFsDirectories::findP3dAddOnConfigFiles(versionHint).values();
721 const QStringList addOnPaths = CFsDirectories::allConfigFilesPathValues(addOnConfigFiles,
true, {}).values();
724 const QSet<QString> all = CFsDirectories::allP3dAddOnXmlSimObjectPaths(addOnPaths,
true);
730 return CFsDirectories::fsxSimObjectsPaths(CFsDirectories::findFsxConfigFiles(),
true);
737 for (
const QString &path : locations)
739 const QString file = CFileUtils::appendFilePaths(CFileUtils::pathUp(path),
"Microsoft/FSX/fsx.cfg");
755 for (
const QString &fsxFile : fsxFiles) { paths.
unite(CFsDirectories::fsxSimObjectsPaths(fsxFile, checked)); }
762 for (
const QString &msfsFile : msfsFiles)
764 paths.
unite(CFsDirectories::msfsSimObjectsPaths(msfsFile, checked));
771 const QString fileContent = CFileUtils::readFileToString(fsxFile);
772 if (fileContent.
isEmpty()) {
return {}; }
774 static const QString p(
"SimObjectPaths.");
783 if (i1 < 0) {
continue; }
785 if (i2 < 0 || i1 >= i2 || line.
endsWith(
'=')) {
continue; }
801 soPath = CFileUtils::appendFilePaths(relPath, soPath);
804 const QDir dir(soPath);
805 if (checked && !dir.
exists())
810 .
info(u
"FSX SimObjects path skipped, not existing: '%1' in '%2'")
817 if (!CDirectoryUtils::containsFileInDir(afp, airFileFilter(),
true))
821 .
info(u
"FSX SimObjects path: Skipping '%1' from '%2', no '%3' file")
822 << afp << fsxFile << airFileFilter();
837 const QString fileContent = CFileUtils::readFileToString(msfsFile);
838 if (fileContent.
isEmpty()) {
return {}; }
840 static const QString p(
"SimObjectPaths.");
849 if (i1 < 0) {
continue; }
851 if (i2 < 0 || i1 >= i2 || line.
endsWith(
'=')) {
continue; }
865 if (!
QStringView(soPath).left(3).contains(
':')) { soPath = CFileUtils::appendFilePaths(relPath, soPath); }
867 const QDir dir(soPath);
868 if (checked && !dir.
exists())
872 .
info(u
"FSX SimObjects path skipped, not existing: '%1' in '%2'")
879 if (!CDirectoryUtils::containsFileInDir(afp, airFileFilter(),
true))
883 .
info(u
"FSX SimObjects path: Skipping '%1' from '%2', no '%3' file")
884 << afp << msfsFile << airFileFilter();
897 const QString &CFsDirectories::airFileFilter()
899 static const QString a(
"*.air");
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Class for emitting a log message.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
SWIFT_MISC_EXPORT QList< QStringView > splitLinesRefs(const QString &s)
Split a string into multiple lines. Blank lines are skipped.
SWIFT_MISC_EXPORT bool caseInsensitiveStringCompare(const QString &c1, const QString &c2)
Case insensitive string compare.
SWIFT_MISC_EXPORT bool containsAny(const QString &testString, const QStringList &any, Qt::CaseSensitivity cs)
Contains any string of the list?
QString absolutePath() const const
bool exists() const const
QString fromNativeSeparators(const QString &pathName)
QDomNodeList elementsByTagName(const QString &tagname) const const
QDomDocument::ParseResult setContent(QAnyStringView text, QDomDocument::ParseOptions options)
QString text() const const
QDomElement firstChildElement(const QString &tagName, const QString &namespaceURI) const const
QDomNode item(int index) const const
virtual QString fileName() const const override
bool open(FILE *fh, QIODeviceBase::OpenMode mode, QFileDevice::FileHandleFlags handleFlags)
QString absoluteFilePath() const const
QString absolutePath() const const
bool exists(const QString &path)
bool isEmpty() const const
void push_back(QList< T >::parameter_type value)
void push_front(QList< T >::parameter_type value)
qsizetype removeAll(const AT &t)
QSet< T >::iterator insert(QSet< T >::const_iterator it, const T &value)
QSet< T > & unite(QSet< T > &&other)
QStringList standardLocations(QStandardPaths::StandardLocation type)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString left(qsizetype n) &&
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString trimmed() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
qsizetype removeDuplicates()
void sort(Qt::CaseSensitivity cs)
QString toString() const const
std::vector< std::string > split(const std::string &str, size_t maxSplitCount=0, const std::string &delimiter=" ")
Split string by delimiter and maxSplitCount times.