swift
simulatorinfo.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <algorithm>
7 
8 #include <QJsonValue>
9 #include <QStringBuilder>
10 #include <QtGlobal>
11 
12 #include "config/buildconfig.h"
13 #include "misc/comparefunctions.h"
15 #include "misc/iconlist.h"
18 #include "misc/stringutils.h"
19 
20 using namespace swift::config;
21 using namespace swift::misc;
22 using namespace swift::misc::db;
23 using namespace swift::misc::simulation::fscommon;
24 using namespace swift::misc::simulation::xplane;
25 
26 SWIFT_DEFINE_VALUEOBJECT_MIXINS(swift::misc::simulation, CSimulatorInfo)
27 
28 namespace swift::misc::simulation
29 {
30  CSimulatorInfo::CSimulatorInfo() {}
31 
32  CSimulatorInfo::CSimulatorInfo(const QString &identifierString)
33  : m_simulator(identifierToSimulator(identifierString))
34  {}
35 
36  CSimulatorInfo::CSimulatorInfo(const QStringList &simulators)
37  {
38  const QString identifier = simulators.join(' ');
39  m_simulator = identifierToSimulator(identifier);
40  }
41 
42  CSimulatorInfo::CSimulatorInfo(Simulator simulator) : m_simulator(static_cast<int>(simulator)) {}
43 
44  CSimulatorInfo::CSimulatorInfo(bool fsx, bool fs9, bool xp, bool p3d, bool fg, bool msfs, bool msfs2024)
45  : m_simulator(boolToFlag(fsx, fs9, xp, p3d, fg, msfs, msfs2024))
46  {}
47 
48  CSimulatorInfo::CSimulatorInfo(int flagsAsInt) : m_simulator(flagsAsInt) {}
49 
50  bool CSimulatorInfo::isUnspecified() const { return m_simulator < 1; }
51 
52  bool CSimulatorInfo::isFSX() const { return getSimulator().testFlag(FSX); }
53 
54  bool CSimulatorInfo::isFS9() const { return getSimulator().testFlag(FS9); }
55 
56  bool CSimulatorInfo::isXPlane() const { return getSimulator().testFlag(XPLANE); }
57 
58  bool CSimulatorInfo::isP3D() const { return getSimulator().testFlag(P3D); }
59 
60  bool CSimulatorInfo::isFG() const { return getSimulator().testFlag(FG); }
61 
62  bool CSimulatorInfo::isMSFS() const { return getSimulator().testFlag(MSFS); }
63 
64  bool CSimulatorInfo::isMSFS2024() const { return getSimulator().testFlag(MSFS2024); }
65 
67  {
68  return isFSX() || isFS9() || isXPlane() || isP3D() || isFG() || isMSFS() || isMSFS2024();
69  }
70 
71  bool CSimulatorInfo::isSingleSimulator() const { return this->numberSimulators() == 1; }
72 
73  bool CSimulatorInfo::isNoSimulator() const { return m_simulator == 0; }
74 
75  bool CSimulatorInfo::isMultipleSimulators() const { return this->numberSimulators() > 1; }
76 
78  {
79  return isFSX() && isFS9() && isXPlane() && isP3D() && isFG() && isMSFS() && isMSFS2024();
80  }
81 
82  bool CSimulatorInfo::isMicrosoftSimulator() const { return isFSX() || isFS9() || isMSFS() || isMSFS2024(); }
83 
85 
86  bool CSimulatorInfo::isFsxP3DFamily() const { return isFSX() || isP3D() || isMSFS() || isMSFS2024(); }
87 
89  {
90  int c = isFS9() ? 1 : 0;
91  if (isFSX()) { c++; }
92  if (isXPlane()) { c++; }
93  if (isP3D()) { c++; }
94  if (isFG()) { c++; }
95  if (isMSFS()) { c++; }
96  if (isMSFS2024()) { c++; }
97  return c;
98  }
99 
100  bool CSimulatorInfo::matchesAll(const CSimulatorInfo &otherInfo) const
101  {
102  return (m_simulator & otherInfo.m_simulator) == otherInfo.m_simulator;
103  }
104 
105  bool CSimulatorInfo::matchesAny(const CSimulatorInfo &otherInfo) const
106  {
107  return (m_simulator & otherInfo.m_simulator) > 0;
108  }
109 
111  {
112  if (this->isNoSimulator()) { return true; }
113  return this->matchesAny(otherInfo);
114  }
115 
117  {
118  Q_UNUSED(index)
119  return Compare::compare(m_simulator, compareValue.m_simulator);
120  }
121 
122  QString CSimulatorInfo::convertToQString(bool i18n) const
123  {
124  Q_UNUSED(i18n)
125  const Simulator s = getSimulator();
126  const QString str = (s.testFlag(FSX) ? QStringLiteral("FSX ") : QString()) %
127  (s.testFlag(FS9) ? QStringLiteral("FS9 ") : QString()) %
128  (s.testFlag(P3D) ? QStringLiteral("P3D ") : QString()) %
129  (s.testFlag(XPLANE) ? QStringLiteral("XPlane ") : QString()) %
130  (s.testFlag(FG) ? QStringLiteral("FG ") : QString()) %
131  (s.testFlag(MSFS) ? QStringLiteral("MSFS ") : QString()) %
132  (s.testFlag(MSFS2024) ? QStringLiteral("MSFS2024 ") : QString());
133  return str.trimmed();
134  }
135 
136  CIcons::IconIndex CSimulatorInfo::toIcon() const { return CIcons::StandardIconEmpty; }
137 
139  {
140  // anything to add?
141  if (other.isUnspecified()) { return None; }
142  if (this->matchesAll(other)) { return None; }
143 
144  this->setSimulator(this->getSimulator() | other.getSimulator());
145  const CSimulatorInfo delta(this->getSimulator() & other.getSimulator());
146  return delta;
147  }
148 
149  QSet<CSimulatorInfo> CSimulatorInfo::asSingleSimulatorSet() const
150  {
151  QSet<CSimulatorInfo> set;
152  if (m_simulator & FSX) { set.insert(CSimulatorInfo(FSX)); }
153  if (m_simulator & FS9) { set.insert(CSimulatorInfo(FS9)); }
154  if (m_simulator & P3D) { set.insert(CSimulatorInfo(P3D)); }
155  if (m_simulator & FG) { set.insert(CSimulatorInfo(FG)); }
156  if (m_simulator & XPLANE) { set.insert(CSimulatorInfo(XPLANE)); }
157  if (m_simulator & MSFS) { set.insert(CSimulatorInfo(MSFS)); }
158  if (m_simulator & MSFS2024) { set.insert(CSimulatorInfo(MSFS2024)); }
159  return set;
160  }
161 
163  {
164  m_simulator = (m_simulator ^ static_cast<int>(All)) & static_cast<int>(All);
165  }
166 
168  {
169  CStatusMessage m(this);
170  if (!this->isAnySimulator()) { return m.validationError(u"No simulator"); }
171  if (this->isMicrosoftOrPrepare3DSimulator() && this->isXPlane())
172  {
173  return m.validationError(u"Cannot combine XPlane and FS simulators");
174  }
175  if (this->isMicrosoftOrPrepare3DSimulator() && this->isFG())
176  {
177  return m.validationError(u"Cannot combine FG and FS simulators");
178  }
179  if (this->isXPlane() && this->isFG()) { return m.validationError(u"Cannot combine FG and XPlane simulators"); }
180  return m.info(u"Simulators OK for model");
181  }
182 
183  CSimulatorInfo::Simulator CSimulatorInfo::boolToFlag(bool fsx, bool fs9, bool xp, bool p3d, bool fg, bool msfs,
184  bool msfs2024)
185  {
186  Simulator s = fsx ? FSX : None;
187  if (fs9) { s |= FS9; }
188  if (xp) { s |= XPLANE; }
189  if (p3d) { s |= P3D; }
190  if (fg) { s |= FG; }
191  if (msfs) { s |= MSFS; }
192  if (msfs2024) { s |= MSFS2024; }
193  return s;
194  }
195 
196  CSimulatorInfo::Simulator CSimulatorInfo::identifierToSimulator(const QString &identifier)
197  {
198  const QString i(identifier.toLower().trimmed().remove(' ').remove('-'));
199  if (i.isEmpty()) { return None; }
200 
201  Simulator s = None;
202  if (i.contains("fsx") || i.contains("fs10")) { s |= FSX; }
203  if (i.contains("fs9") || i.contains("2004")) { s |= FS9; }
204  if (i.contains("plane") || i.contains("xp")) { s |= XPLANE; }
205  if (i.contains("gear") || stringCompare(QStringLiteral("fg"), identifier, Qt::CaseInsensitive)) { s |= FG; }
206  if (i.contains("3d") || i.contains("prepar") || i.contains("martin") || i.contains("lm") || i.contains("lock"))
207  {
208  s |= P3D;
209  }
210  if (i.contains("msfs2024"))
211  {
212  s |= MSFS2024;
213  return s;
214  }
215 
216  if (i.contains("msfs")) { s |= MSFS; }
217  return s;
218  }
219 
221  {
222  static const CSimulatorInfo s(All);
223  return s;
224  }
225 
227  {
228  static const QStringList sims = [] {
229  QStringList s;
230  for (const CSimulatorInfo &i : CSimulatorInfo::allSimulatorsSet()) { s.push_back(i.toQString(false)); }
231  s.sort(Qt::CaseInsensitive);
232  return s;
233  }();
234  return sims;
235  }
236 
237  const QSet<CSimulatorInfo> &CSimulatorInfo::allSimulatorsSet()
238  {
239  static const QSet<CSimulatorInfo> all(allSimulators().asSingleSimulatorSet());
240  return all;
241  }
242 
244  {
245  static const CSimulatorInfo s(CSimulatorInfo::AllFsFamily);
246  return s;
247  }
248 
250  {
251  CSimulatorInfo sim;
252  bool fs9 = false;
253  bool fsx = false;
254  bool p3d = false;
255  bool fg = false;
256  bool msfs = false;
257  bool msfs2024 = false;
258 
259  if (CBuildConfig::isRunningOnWindowsNtPlatform())
260  {
261  fs9 = !CFsDirectories::fs9AircraftDir().isEmpty() && !CFsDirectories::fs9Dir().isEmpty();
262  fsx = !CFsDirectories::fsxSimObjectsDir().isEmpty() && !CFsDirectories::fsxDir().isEmpty();
263  p3d = !CFsDirectories::p3dDir().isEmpty() && !CFsDirectories::p3dSimObjectsDir().isEmpty();
264  msfs = !CFsDirectories::msfsDir().isEmpty() && !CFsDirectories::msfsPackagesDir().isEmpty();
265  msfs2024 = !CFsDirectories::msfs2024Dir().isEmpty() && !CFsDirectories::msfs2024PackagesDir().isEmpty();
266  }
267 
268  const bool xp = !CXPlaneUtil::xplaneRootDir().isEmpty();
269 
271  return sim;
272  }
273 
275  CSimulatorInfo guessDefaultSimulatorImpl()
276  {
277  static const CSimulatorInfo locallyInstalled(CSimulatorInfo::getLocallyInstalledSimulators());
278  if (CBuildConfig::isRunningOnLinuxPlatform() || CBuildConfig::isRunningOnMacOSPlatform())
279  {
280  return CSimulatorInfo::xplane();
281  }
282  if (locallyInstalled.isP3D()) { return CSimulatorInfo::p3d(); }
283  if (locallyInstalled.isFSX()) { return CSimulatorInfo::fsx(); }
284  if (locallyInstalled.isFS9()) { return CSimulatorInfo::fs9(); }
285 
286  // fallback
287  return CSimulatorInfo::p3d();
288  }
290 
292  {
293  static const CSimulatorInfo sim(guessDefaultSimulatorImpl());
294  return sim;
295  }
296 
297  CSimulatorInfo CSimulatorInfo::fromDatabaseJson(const QJsonObject &json, const QString &prefix)
298  {
299  const QJsonValue jfsx = json.value(prefix % u"simfsx");
300  const QJsonValue jfs9 = json.value(prefix % u"simfs9");
301  const QJsonValue jxp = json.value(prefix % u"simxplane");
302  const QJsonValue jp3d = json.value(prefix % u"simp3d");
303  const QJsonValue jfg = json.value(prefix % u"simfg");
304  const QJsonValue jmsfs = json.value(prefix % u"simmsfs");
305  const QJsonValue jmsfs2024 = json.value(prefix % u"simmsfs2024");
306 
307  // we handle bool JSON values and bool as string
308  const bool fsx = jfsx.isBool() ? jfsx.toBool() : CDatastoreUtility::dbBoolStringToBool(jfsx.toString());
309  const bool fs9 = jfs9.isBool() ? jfs9.toBool() : CDatastoreUtility::dbBoolStringToBool(jfs9.toString());
310  const bool xp = jxp.isBool() ? jxp.toBool() : CDatastoreUtility::dbBoolStringToBool(jxp.toString());
311  const bool p3d = jp3d.isBool() ? jp3d.toBool() : CDatastoreUtility::dbBoolStringToBool(jp3d.toString());
312  const bool fg = jfg.isBool() ? jfg.toBool() : CDatastoreUtility::dbBoolStringToBool(jfg.toString());
313  const bool msfs = jmsfs.isBool() ? jmsfs.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs.toString());
314  const bool msfs2024 =
315  jmsfs2024.isBool() ? jmsfs2024.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs2024.toString());
316 
317  const CSimulatorInfo simInfo(fsx, fs9, xp, p3d, fg, msfs, msfs2024);
318  return simInfo;
319  }
320 
322  {
323  m_counts.reserve(CSimulatorInfo::NumberOfSimulators + 1);
324  for (int i = 0; i < CSimulatorInfo::NumberOfSimulators + 1; i++) { m_counts.push_back(0); }
325  }
326 
327  int CCountPerSimulator::getCount(const CSimulatorInfo &simulator) const
328  {
329  return m_counts[internalIndex(simulator)];
330  }
331 
333  {
334  return m_counts[CSimulatorInfo::NumberOfSimulators];
335  }
336 
338  {
339  return this->getCount(CSimulatorInfo::fsx()) + this->getCount(CSimulatorInfo::p3d()) +
342  }
343 
345  {
346  return this->getCount(CSimulatorInfo::fsx()) + this->getCount(CSimulatorInfo::p3d()) +
348  }
349 
350  int CCountPerSimulator::getMaximum() const { return *std::min_element(m_counts.begin(), m_counts.end()); }
351 
352  int CCountPerSimulator::getMinimum() const { return *std::max_element(m_counts.begin(), m_counts.end()); }
353 
355  {
356  int c = 0;
357  for (int i = 0; i < m_counts.size() - 1; i++)
358  {
359  if (m_counts[i] > 0) { c++; }
360  }
361  return c;
362  }
363 
364  QMultiMap<int, CSimulatorInfo> CCountPerSimulator::countPerSimulator() const
365  {
366  QMultiMap<int, CSimulatorInfo> counts;
367  for (int i = 0; i < m_counts.size(); i++) { counts.insert(m_counts[i], simulator(i)); }
368  return counts;
369  }
370 
372  {
373  return u"FSX: " % QString::number(m_counts[0]) % u" P3D: " % QString::number(m_counts[1]) % u" FS9: " %
374  QString::number(m_counts[2]) % u" XPlane: " % QString::number(m_counts[3]) % u" FG: " %
375  QString::number(m_counts[4]) % u" MSFS: " % QString::number(m_counts[5]) % u" MSFS2024: " %
376  QString::number(m_counts[6]);
377  }
378 
379  void CCountPerSimulator::setCount(int count, const CSimulatorInfo &simulator)
380  {
381  m_counts[internalIndex(simulator)] = count;
382  }
383 
385  {
386  if (simulator.isNoSimulator() || simulator.isUnspecified())
387  {
388  // unknown count
389  m_counts[6]++;
390  return;
391  }
392  if (simulator.isFSX()) { m_counts[0]++; }
393  if (simulator.isP3D()) { m_counts[1]++; }
394  if (simulator.isFS9()) { m_counts[2]++; }
395  if (simulator.isXPlane()) { m_counts[3]++; }
396  if (simulator.isFG()) { m_counts[4]++; }
397  if (simulator.isMSFS()) { m_counts[5]++; }
398  if (simulator.isMSFS2024()) { m_counts[6]++; }
399  }
400 
401  int CCountPerSimulator::internalIndex(const CSimulatorInfo &simulator)
402  {
403  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
404  switch (simulator.getSimulator())
405  {
406  case CSimulatorInfo::FSX: return 0;
407  case CSimulatorInfo::P3D: return 1;
408  case CSimulatorInfo::FS9: return 2;
409  case CSimulatorInfo::XPLANE: return 3;
410  case CSimulatorInfo::FG: return 4;
411  case CSimulatorInfo::MSFS: return 5;
412  case CSimulatorInfo::MSFS2024: return 6;
413  default: return CSimulatorInfo::NumberOfSimulators; // unknown
414  }
415  }
416 
417  CSimulatorInfo CCountPerSimulator::simulator(int internalIndex)
418  {
419  switch (internalIndex)
420  {
421  case 0: return CSimulatorInfo(CSimulatorInfo::FSX);
422  case 1: return CSimulatorInfo(CSimulatorInfo::P3D);
423  case 2: return CSimulatorInfo(CSimulatorInfo::FS9);
424  case 3: return CSimulatorInfo(CSimulatorInfo::XPLANE);
425  case 4: return CSimulatorInfo(CSimulatorInfo::FG);
426  case 5: return CSimulatorInfo(CSimulatorInfo::MSFS);
427  case 6: return CSimulatorInfo(CSimulatorInfo::MSFS2024);
428  default: return CSimulatorInfo(CSimulatorInfo::None);
429  }
430  }
431 } // namespace swift::misc::simulation
IconIndex
Index for each icon, allows to send them via DBus, efficiently store them, etc.
Definition: icons.h:32
Derived & validationError(const char16_t(&format)[N])
Set the severity to error, providing a format string, and adding the validation category.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Non-owning reference to a CPropertyIndex with a subset of its features.
Streamable status message, e.g.
void setCount(int count, const CSimulatorInfo &simulator)
Set count.
int getCountForFsxFamilySimulators() const
P3D, MSFS or FSX.
void increaseSimulatorCounts(const CSimulatorInfo &simulator)
Increase all simulators given here.
int simulatorsRepresented() const
Number of simulators with count > 0.
int getCount(const CSimulatorInfo &simulator) const
Object count for given simulator.
int getCountForUnknownSimulators() const
Unknown count.
QMultiMap< int, CSimulatorInfo > countPerSimulator() const
Sorted (ascending) per simulator.
int getCountForFsFamilySimulators() const
P3D, FSX, MSFS or FS9.
Simple hardcoded info about the corresponding simulator.
Definition: simulatorinfo.h:41
static const QSet< CSimulatorInfo > & allSimulatorsSet()
All simulators as set.
bool isAllSimulators() const
Is all simulators?
CSimulatorInfo add(const CSimulatorInfo &other)
Add simulator.
QSet< CSimulatorInfo > asSingleSimulatorSet() const
As a set of single simulator info objects.
bool matchesAll(const CSimulatorInfo &otherInfo) const
Matches all simulators.
int comparePropertyByIndex(CPropertyIndexRef index, const CSimulatorInfo &compareValue) const
Cast as QString.
static const CSimulatorInfo & allSimulators()
All simulators.
static Simulator boolToFlag(bool isFSX, bool isFS9, bool xp, bool isP3D, bool fg, bool msfs, bool msfs2024)
Bool flags to enum.
static CSimulatorInfo getLocallyInstalledSimulators()
Locally installed simulators.
bool isSingleSimulator() const
Single simulator selected.
bool isNoSimulator() const
No simulator?
bool isMultipleSimulators() const
Represents > 1 simulator.
bool matchesAny(const CSimulatorInfo &otherInfo) const
Matches any simulator.
bool isUnspecified() const
Unspecified simulator.
static const CSimulatorInfo & guessDefaultSimulator()
Guess a default simulator based on installation.
void invertSimulators()
All simulators selected become unselected and vice versa.
Simulator getSimulator() const
Simulator.
static const CSimulatorInfo & msfs()
Const simulator info objects.
bool isMicrosoftSimulator() const
Microsoft Simulator?
bool isFsxP3DFamily() const
FSX family, i.e. FSX or P3D?
static const CSimulatorInfo & fs9()
Const simulator info objects.
static CSimulatorInfo fromDatabaseJson(const QJsonObject &json, const QString &prefix)
From database JSON.
static const CSimulatorInfo & fsx()
Const simulator info objects.
CIcons::IconIndex toIcon() const
As icon, not implemented by all classes.
static const CSimulatorInfo & p3d()
Const simulator info objects.
static const CSimulatorInfo & xplane()
Const simulator info objects.
static Simulator identifierToSimulator(const QString &identifier)
Identifer, as provided by plugin.
static const CSimulatorInfo & msfs2024()
Const simulator info objects.
void setSimulator(Simulator s)
Simulator.
bool isAnySimulator() const
Any simulator?
QString convertToQString(bool i18n=false) const
Cast as QString.
int numberSimulators() const
Number simulators selected.
static const QStringList & allSimulatorStrings()
All simulator strings.
static const CSimulatorInfo & allFsFamilySimulators()
All simulators of the FS family (P3D FSX, FS9)
CStatusMessage validateSimulatorsForModel() const
Validate simulators for an aircraft model.
static constexpr int NumberOfSimulators
Number of known individual simulators.
Definition: simulatorinfo.h:61
static const CSimulatorInfo & fg()
Const simulator info objects.
bool isMicrosoftOrPrepare3DSimulator() const
Microsoft Simulator or P3D?
bool matchesAnyOrNone(const CSimulatorInfo &otherInfo) const
Matches any simulator or None (NULL)
Utility classes for FSX, OS and driver independent code.
Free functions in swift::misc.
SWIFT_MISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs)
String compare.
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
Definition: valueobject.h:67