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",
339 const QStringList &CFsDirectories::msfs2024SimObjectsExcludeDirectoryPatterns()
349 QString p3dDirFromRegistryImpl()
354 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v6"), QStringLiteral(
"AppPath") },
355 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v5"), QStringLiteral(
"AppPath") },
356 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v4"), QStringLiteral(
"AppPath") },
357 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v3"), QStringLiteral(
"AppPath") },
358 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\Lockheed Martin\\Prepar3d v2"), QStringLiteral(
"AppPath") },
359 { QStringLiteral(
"HKEY_CURRENT_USER\\Software\\LockheedMartin\\Prepar3d"), QStringLiteral(
"AppPath") }
361 for (
const auto ®istryPair : p3dRegistryPathPairs)
364 p3dPath = p3dRegistry.value(registryPair.second).toString().
trimmed();
366 if (p3dPath.
isEmpty()) {
continue; }
367 p3dPath = CFileUtils::normalizeFilePathToQtStandard(p3dPath);
370 const QDir dir(p3dPath);
371 if (dir.exists()) {
break; }
377 const QString &CFsDirectories::p3dDirFromRegistry()
379 static const QString p3dPath = CFileUtils::normalizeFilePathToQtStandard(p3dDirFromRegistryImpl());
385 QString dir(CFsDirectories::p3dDirFromRegistry());
386 if (!dir.isEmpty()) {
return dir; }
387 const QStringList someDefaultDirs({
"C:/Program Files (x86)/Lockheed Martin/Prepar3D v4",
388 "C:/Program Files (x86)/Lockheed Martin/Prepar3D v3",
389 "C:/Program Files (x86)/Lockheed Martin/Prepar3D v2",
390 "C:/Program Files (x86)/Lockheed Martin/Prepar3D" });
391 return CFileUtils::findFirstExisting(someDefaultDirs);
396 static const QString dir(p3dDirImpl());
400 QString p3dSimObjectsDirFromRegistryImpl()
402 const QString p3dPath = CFsDirectories::p3dDirFromRegistry();
403 if (p3dPath.
isEmpty()) {
return {}; }
404 return CFsDirectories::fsxSimObjectsDirFromSimDir(p3dPath);
407 const QString &CFsDirectories::p3dSimObjectsDirFromRegistry()
409 static const QString p3dPath(p3dSimObjectsDirFromRegistryImpl());
415 QString dir(CFsDirectories::p3dDir());
416 if (dir.isEmpty()) {
return {}; }
417 return CFsDirectories::p3dSimObjectsDirFromSimDir(dir);
420 const QString &CFsDirectories::p3dSimObjectsDir()
422 static const QString dir(p3dSimObjectsDirImpl());
426 QStringList CFsDirectories::fsxSimObjectsDirPlusAddOnXmlSimObjectsPaths(
const QString &simObjectsDir)
429 QStringList allPaths = CFsDirectories::allFsxSimObjectPaths().values();
430 const QString sod = CFileUtils::normalizeFilePathToQtStandard(
431 simObjectsDir.
isEmpty() ? CFsDirectories::fsxSimObjectsDir() : simObjectsDir);
446 Q_UNUSED(simObjectsDir);
447 static const QStringList Path { CFsDirectories::msfsSimObjectsDir() };
453 Q_UNUSED(simObjectsDir);
454 static const QStringList Path { CFsDirectories::msfs2024SimObjectsDir() };
457 QStringList CFsDirectories::p3dSimObjectsDirPlusAddOnXmlSimObjectsPaths(
const QString &simObjectsDir,
461 QStringList allPaths = CFsDirectories::allP3dAddOnXmlSimObjectPaths(versionHint).values();
462 const QString sod = CFileUtils::normalizeFilePathToQtStandard(
463 simObjectsDir.
isEmpty() ? CFsDirectories::p3dSimObjectsDir() : simObjectsDir);
478 if (candidate.
isEmpty()) {
return "v5"; }
490 if (simDir.
isEmpty()) {
return {}; }
491 return CFileUtils::normalizeFilePathToQtStandard(CFileUtils::appendFilePaths(simDir,
"SimObjects"));
494 const QStringList &CFsDirectories::p3dSimObjectsExcludeDirectoryPatterns()
498 "SimObjects/Animals",
500 "SimObjects/GroundVehicles",
504 "SimObjects/Avatars",
505 "SimObjects/Countermeasures",
506 "SimObjects/Submersible",
507 "SimObjects/Weapons",
512 QString fs9DirFromRegistryImpl()
517 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\DirectPlay\\Applications\\Microsoft Flight Simulator 2004"),
518 QStringLiteral(
"AppPath") },
520 "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\DirectPlay\\Applications\\Microsoft Flight "
522 QStringLiteral(
"AppPath") }
525 for (
const auto ®istryPair : fs9RegistryPathPairs)
528 fs9Path = fs9Registry.value(registryPair.second).toString().
trimmed();
530 if (fs9Path.
isEmpty()) {
continue; }
531 fs9Path = CFileUtils::normalizeFilePathToQtStandard(fs9Path);
534 const QDir dir(fs9Path);
535 if (dir.exists()) {
break; }
541 const QString &CFsDirectories::fs9DirFromRegistry()
543 static const QString fs9Path(fs9DirFromRegistryImpl());
549 QString dir(CFsDirectories::fs9DirFromRegistry());
550 if (!dir.isEmpty()) {
return dir; }
551 const QStringList someDefaultDirs({
"C:/Flight Simulator 9",
"C:/FS9" });
552 return CFileUtils::findFirstExisting(someDefaultDirs);
557 static const QString v(fs9DirImpl());
561 QString fs9AircraftDirFromRegistryImpl()
563 QString fs9Path = CFsDirectories::fs9DirFromRegistry();
564 if (fs9Path.
isEmpty()) {
return {}; }
565 return CFsDirectories::fs9AircraftDirFromSimDir(fs9Path);
568 const QString &CFsDirectories::fs9AircraftDirFromRegistry()
570 static const QString dir(fs9AircraftDirFromRegistryImpl());
576 const QString dir(CFsDirectories::fs9Dir());
577 if (dir.isEmpty()) {
return {}; }
578 return CFsDirectories::fs9AircraftDirFromSimDir(dir);
581 const QString &CFsDirectories::fs9AircraftDir()
583 static const QString dir(fs9AircraftDirImpl());
589 if (simDir.
isEmpty()) {
return {}; }
590 return CFileUtils::appendFilePaths(simDir,
"Aircraft");
593 const QStringList &CFsDirectories::fs9AircraftObjectsExcludeDirectoryPatterns()
601 static const QString cfgFile(
"add-ons.cfg");
602 return CFsDirectories::findP3dConfigFiles(cfgFile, versionHint);
607 static const QString cfgFile(
"simobjects.cfg");
608 return CFsDirectories::findP3dConfigFiles(cfgFile, versionHint);
616 for (
const QString &path : locations)
618 const QString pathUp = CFileUtils::appendFilePaths(CFileUtils::pathUp(path),
"Lockheed Martin");
619 const QDir d(pathUp);
620 if (!d.exists()) {
continue; }
621 CLogMessage(
static_cast<CFsDirectories *
>(
nullptr)).
info(u
"P3D config dir: '%1'") << d.absolutePath();
626 for (
const QString &entry : entries)
631 const QString f = CFileUtils::appendFilePaths(d.absolutePath(), entry, configFile);
636 CLogMessage(
static_cast<CFsDirectories *
>(
nullptr)).
info(u
"P3D config file: '%1'") << f;
647 if (configFiles.
isEmpty()) {
return {}; }
649 for (
const QString &configFile : configFiles)
652 const QString fileContent = CFileUtils::readFileToString(configFile);
653 if (fileContent.
isEmpty()) {
continue; }
655 static const QString p(
"Path=");
659 if (i < 0 || line.
endsWith(
'=')) {
continue; }
663 CFileUtils::appendFilePathsAndFixUnc(pathPrefix, path.
toString())));
672 if (addOnPaths.
isEmpty()) {
return {}; }
674 for (
const QString &addOnPath : addOnPaths)
676 const QString filename = CFileUtils::appendFilePaths(addOnPath,
"add-on.xml");
678 QFile file(filename);
685 for (
int i = 0; i < components.
size(); i++)
692 const QString pathValue = CFileUtils::normalizeFilePathToQtStandard(path.
text());
695 if (!correctPath) {
continue; }
700 CFileUtils::appendFilePaths(addOnPath, pathValue);
705 if (!checked ||
QDir(fp).exists())
707 simObjectPaths.
insert(CFileUtils::normalizeFilePathToQtStandard(fp));
714 return simObjectPaths;
720 const QStringList addOnConfigFiles = CFsDirectories::findP3dAddOnConfigFiles(versionHint).values();
723 const QStringList addOnPaths = CFsDirectories::allConfigFilesPathValues(addOnConfigFiles,
true, {}).values();
726 const QSet<QString> all = CFsDirectories::allP3dAddOnXmlSimObjectPaths(addOnPaths,
true);
732 return CFsDirectories::fsxSimObjectsPaths(CFsDirectories::findFsxConfigFiles(),
true);
739 for (
const QString &path : locations)
741 const QString file = CFileUtils::appendFilePaths(CFileUtils::pathUp(path),
"Microsoft/FSX/fsx.cfg");
757 for (
const QString &fsxFile : fsxFiles) { paths.
unite(CFsDirectories::fsxSimObjectsPaths(fsxFile, checked)); }
764 for (
const QString &msfsFile : msfsFiles)
766 paths.
unite(CFsDirectories::msfsSimObjectsPaths(msfsFile, checked));
773 const QString fileContent = CFileUtils::readFileToString(fsxFile);
774 if (fileContent.
isEmpty()) {
return {}; }
776 static const QString p(
"SimObjectPaths.");
785 if (i1 < 0) {
continue; }
787 if (i2 < 0 || i1 >= i2 || line.
endsWith(
'=')) {
continue; }
803 soPath = CFileUtils::appendFilePaths(relPath, soPath);
806 const QDir dir(soPath);
807 if (checked && !dir.
exists())
812 .
info(u
"FSX SimObjects path skipped, not existing: '%1' in '%2'")
819 if (!CDirectoryUtils::containsFileInDir(afp, airFileFilter(),
true))
823 .
info(u
"FSX SimObjects path: Skipping '%1' from '%2', no '%3' file")
824 << afp << fsxFile << airFileFilter();
839 const QString fileContent = CFileUtils::readFileToString(msfsFile);
840 if (fileContent.
isEmpty()) {
return {}; }
842 static const QString p(
"SimObjectPaths.");
851 if (i1 < 0) {
continue; }
853 if (i2 < 0 || i1 >= i2 || line.
endsWith(
'=')) {
continue; }
867 if (!
QStringView(soPath).left(3).contains(
':')) { soPath = CFileUtils::appendFilePaths(relPath, soPath); }
869 const QDir dir(soPath);
870 if (checked && !dir.
exists())
874 .
info(u
"FSX SimObjects path skipped, not existing: '%1' in '%2'")
881 if (!CDirectoryUtils::containsFileInDir(afp, airFileFilter(),
true))
885 .
info(u
"FSX SimObjects path: Skipping '%1' from '%2', no '%3' file")
886 << afp << msfsFile << airFileFilter();
899 const QString &CFsDirectories::airFileFilter()
901 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.