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(const QString &identifierString)
31  : m_simulator(identifierToSimulator(identifierString))
32  {}
33 
35  {
36  const QString identifier = simulators.join(' ');
37  m_simulator = identifierToSimulator(identifier);
38  }
39 
40  CSimulatorInfo::CSimulatorInfo(Simulator simulator) : m_simulator(static_cast<int>(simulator)) {}
41 
42  CSimulatorInfo::CSimulatorInfo(bool fsx, bool fs9, bool xp, bool p3d, bool fg, bool msfs, bool msfs2024)
43  : m_simulator(boolToFlag(fsx, fs9, xp, p3d, fg, msfs, msfs2024))
44  {}
45 
46  CSimulatorInfo::CSimulatorInfo(int flagsAsInt) : m_simulator(flagsAsInt) {}
47 
48  bool CSimulatorInfo::isUnspecified() const { return m_simulator < 1; }
49 
50  bool CSimulatorInfo::isFSX() const { return getSimulator().testFlag(FSX); }
51 
52  bool CSimulatorInfo::isFS9() const { return getSimulator().testFlag(FS9); }
53 
54  bool CSimulatorInfo::isXPlane() const { return getSimulator().testFlag(XPLANE); }
55 
56  bool CSimulatorInfo::isP3D() const { return getSimulator().testFlag(P3D); }
57 
58  bool CSimulatorInfo::isFG() const { return getSimulator().testFlag(FG); }
59 
60  bool CSimulatorInfo::isMSFS() const { return getSimulator().testFlag(MSFS); }
61 
62  bool CSimulatorInfo::isMSFS2024() const { return getSimulator().testFlag(MSFS2024); }
63 
65  {
66  return isFSX() || isFS9() || isXPlane() || isP3D() || isFG() || isMSFS() || isMSFS2024();
67  }
68 
69  bool CSimulatorInfo::isSingleSimulator() const { return this->numberSimulators() == 1; }
70 
71  bool CSimulatorInfo::isNoSimulator() const { return m_simulator == 0; }
72 
73  bool CSimulatorInfo::isMultipleSimulators() const { return this->numberSimulators() > 1; }
74 
76  {
77  return isFSX() && isFS9() && isXPlane() && isP3D() && isFG() && isMSFS() && isMSFS2024();
78  }
79 
80  bool CSimulatorInfo::isMicrosoftSimulator() const { return isFSX() || isFS9() || isMSFS() || isMSFS2024(); }
81 
83 
84  bool CSimulatorInfo::isFsxP3DFamily() const { return isFSX() || isP3D() || isMSFS() || isMSFS2024(); }
85 
87  {
88  int c = isFS9() ? 1 : 0;
89  if (isFSX()) { c++; }
90  if (isXPlane()) { c++; }
91  if (isP3D()) { c++; }
92  if (isFG()) { c++; }
93  if (isMSFS()) { c++; }
94  if (isMSFS2024()) { c++; }
95  return c;
96  }
97 
98  bool CSimulatorInfo::matchesAll(const CSimulatorInfo &otherInfo) const
99  {
100  return (m_simulator & otherInfo.m_simulator) == otherInfo.m_simulator;
101  }
102 
103  bool CSimulatorInfo::matchesAny(const CSimulatorInfo &otherInfo) const
104  {
105  return (m_simulator & otherInfo.m_simulator) > 0;
106  }
107 
109  {
110  if (this->isNoSimulator()) { return true; }
111  return this->matchesAny(otherInfo);
112  }
113 
115  {
116  Q_UNUSED(index)
117  return Compare::compare(m_simulator, compareValue.m_simulator);
118  }
119 
121  {
122  Q_UNUSED(i18n)
123  const Simulator s = getSimulator();
124  const QString str = (s.testFlag(FSX) ? QStringLiteral("FSX ") : QString()) %
125  (s.testFlag(FS9) ? QStringLiteral("FS9 ") : QString()) %
126  (s.testFlag(P3D) ? QStringLiteral("P3D ") : QString()) %
127  (s.testFlag(XPLANE) ? QStringLiteral("XPlane ") : QString()) %
128  (s.testFlag(FG) ? QStringLiteral("FG ") : QString()) %
129  (s.testFlag(MSFS) ? QStringLiteral("MSFS ") : QString()) %
130  (s.testFlag(MSFS2024) ? QStringLiteral("MSFS2024 ") : QString());
131  return str.trimmed();
132  }
133 
134  CIcons::IconIndex CSimulatorInfo::toIcon() const { return CIcons::StandardIconEmpty; }
135 
137  {
138  // anything to add?
139  if (other.isUnspecified()) { return None; }
140  if (this->matchesAll(other)) { return None; }
141 
142  this->setSimulator(this->getSimulator() | other.getSimulator());
143  const CSimulatorInfo delta(this->getSimulator() & other.getSimulator());
144  return delta;
145  }
146 
148  {
150  if (m_simulator & FSX) { set.insert(CSimulatorInfo(FSX)); }
151  if (m_simulator & FS9) { set.insert(CSimulatorInfo(FS9)); }
152  if (m_simulator & P3D) { set.insert(CSimulatorInfo(P3D)); }
153  if (m_simulator & FG) { set.insert(CSimulatorInfo(FG)); }
154  if (m_simulator & XPLANE) { set.insert(CSimulatorInfo(XPLANE)); }
155  if (m_simulator & MSFS) { set.insert(CSimulatorInfo(MSFS)); }
156  if (m_simulator & MSFS2024) { set.insert(CSimulatorInfo(MSFS2024)); }
157  return set;
158  }
159 
161  {
162  m_simulator = (m_simulator ^ static_cast<int>(All)) & static_cast<int>(All);
163  }
164 
166  {
167  CStatusMessage m(this);
168  if (!this->isAnySimulator()) { return m.validationError(u"No simulator"); }
169  if (this->isMicrosoftOrPrepare3DSimulator() && this->isXPlane())
170  {
171  return m.validationError(u"Cannot combine XPlane and FS simulators");
172  }
173  if (this->isMicrosoftOrPrepare3DSimulator() && this->isFG())
174  {
175  return m.validationError(u"Cannot combine FG and FS simulators");
176  }
177  if (this->isXPlane() && this->isFG()) { return m.validationError(u"Cannot combine FG and XPlane simulators"); }
178  return m.info(u"Simulators OK for model");
179  }
180 
181  CSimulatorInfo::Simulator CSimulatorInfo::boolToFlag(bool fsx, bool fs9, bool xp, bool p3d, bool fg, bool msfs,
182  bool msfs2024)
183  {
184  Simulator s = fsx ? FSX : None;
185  if (fs9) { s |= FS9; }
186  if (xp) { s |= XPLANE; }
187  if (p3d) { s |= P3D; }
188  if (fg) { s |= FG; }
189  if (msfs) { s |= MSFS; }
190  if (msfs2024) { s |= MSFS2024; }
191  return s;
192  }
193 
194  CSimulatorInfo::Simulator CSimulatorInfo::identifierToSimulator(const QString &identifier)
195  {
196  const QString i(identifier.toLower().trimmed().remove(' ').remove('-'));
197  if (i.isEmpty()) { return None; }
198 
199  Simulator s = None;
200  if (i.contains("fsx") || i.contains("fs10")) { s |= FSX; }
201  if (i.contains("fs9") || i.contains("2004")) { s |= FS9; }
202  if (i.contains("plane") || i.contains("xp")) { s |= XPLANE; }
203  if (i.contains("gear") || stringCompare(QStringLiteral("fg"), identifier, Qt::CaseInsensitive)) { s |= FG; }
204  if (i.contains("3d") || i.contains("prepar") || i.contains("martin") || i.contains("lm") || i.contains("lock"))
205  {
206  s |= P3D;
207  }
208  if (i.contains("msfs2024"))
209  {
210  s |= MSFS2024;
211  return s;
212  }
213 
214  if (i.contains("msfs")) { s |= MSFS; }
215  return s;
216  }
217 
219  {
220  static const CSimulatorInfo s(All);
221  return s;
222  }
223 
225  {
226  static const QStringList sims = [] {
227  QStringList s;
228  for (const CSimulatorInfo &i : CSimulatorInfo::allSimulatorsSet()) { s.push_back(i.toQString(false)); }
230  return s;
231  }();
232  return sims;
233  }
234 
236  {
238  return all;
239  }
240 
242  {
243  static const CSimulatorInfo s(CSimulatorInfo::AllFsFamily);
244  return s;
245  }
246 
248  {
249  CSimulatorInfo sim;
250  bool fs9 = false;
251  bool fsx = false;
252  bool p3d = false;
253  bool fg = false;
254  bool msfs = false;
255  bool msfs2024 = false;
256 
257  if (CBuildConfig::isRunningOnWindowsNtPlatform())
258  {
259  fs9 = !CFsDirectories::fs9AircraftDir().isEmpty() && !CFsDirectories::fs9Dir().isEmpty();
260  fsx = !CFsDirectories::fsxSimObjectsDir().isEmpty() && !CFsDirectories::fsxDir().isEmpty();
261  p3d = !CFsDirectories::p3dDir().isEmpty() && !CFsDirectories::p3dSimObjectsDir().isEmpty();
262  msfs = !CFsDirectories::msfsDir().isEmpty() && !CFsDirectories::msfsPackagesDir().isEmpty();
263  msfs2024 = !CFsDirectories::msfs2024Dir().isEmpty() && !CFsDirectories::msfs2024PackagesDir().isEmpty();
264  }
265 
266  const bool xp = !CXPlaneUtil::xplaneRootDir().isEmpty();
267 
269  return sim;
270  }
271 
273  CSimulatorInfo guessDefaultSimulatorImpl()
274  {
275  static const CSimulatorInfo locallyInstalled(CSimulatorInfo::getLocallyInstalledSimulators());
276  if (CBuildConfig::isRunningOnLinuxPlatform() || CBuildConfig::isRunningOnMacOSPlatform())
277  {
278  return CSimulatorInfo::xplane();
279  }
280  if (locallyInstalled.isP3D()) { return CSimulatorInfo::p3d(); }
281  if (locallyInstalled.isFSX()) { return CSimulatorInfo::fsx(); }
282  if (locallyInstalled.isFS9()) { return CSimulatorInfo::fs9(); }
283 
284  // fallback
285  return CSimulatorInfo::p3d();
286  }
288 
290  {
291  static const CSimulatorInfo sim(guessDefaultSimulatorImpl());
292  return sim;
293  }
294 
296  {
297  const QJsonValue jfsx = json.value(prefix % u"simfsx");
298  const QJsonValue jfs9 = json.value(prefix % u"simfs9");
299  const QJsonValue jxp = json.value(prefix % u"simxplane");
300  const QJsonValue jp3d = json.value(prefix % u"simp3d");
301  const QJsonValue jfg = json.value(prefix % u"simfg");
302  const QJsonValue jmsfs = json.value(prefix % u"simmsfs");
303  const QJsonValue jmsfs2024 = json.value(prefix % u"simmsfs2024");
304 
305  // we handle bool JSON values and bool as string
306  const bool fsx = jfsx.isBool() ? jfsx.toBool() : CDatastoreUtility::dbBoolStringToBool(jfsx.toString());
307  const bool fs9 = jfs9.isBool() ? jfs9.toBool() : CDatastoreUtility::dbBoolStringToBool(jfs9.toString());
308  const bool xp = jxp.isBool() ? jxp.toBool() : CDatastoreUtility::dbBoolStringToBool(jxp.toString());
309  const bool p3d = jp3d.isBool() ? jp3d.toBool() : CDatastoreUtility::dbBoolStringToBool(jp3d.toString());
310  const bool fg = jfg.isBool() ? jfg.toBool() : CDatastoreUtility::dbBoolStringToBool(jfg.toString());
311  const bool msfs = jmsfs.isBool() ? jmsfs.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs.toString());
312  const bool msfs2024 =
313  jmsfs2024.isBool() ? jmsfs2024.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs2024.toString());
314 
315  const CSimulatorInfo simInfo(fsx, fs9, xp, p3d, fg, msfs, msfs2024);
316  return simInfo;
317  }
318 
320  {
322  for (int i = 0; i < CSimulatorInfo::NumberOfSimulators + 1; i++) { m_counts.push_back(0); }
323  }
324 
325  int CCountPerSimulator::getCount(const CSimulatorInfo &simulator) const
326  {
327  return m_counts[internalIndex(simulator)];
328  }
329 
331  {
332  return m_counts[CSimulatorInfo::NumberOfSimulators];
333  }
334 
336  {
337  return this->getCount(CSimulatorInfo::fsx()) + this->getCount(CSimulatorInfo::p3d()) +
340  }
341 
343  {
344  return this->getCount(CSimulatorInfo::fsx()) + this->getCount(CSimulatorInfo::p3d()) +
346  }
347 
348  int CCountPerSimulator::getMaximum() const { return *std::min_element(m_counts.begin(), m_counts.end()); }
349 
350  int CCountPerSimulator::getMinimum() const { return *std::max_element(m_counts.begin(), m_counts.end()); }
351 
353  {
354  int c = 0;
355  for (int i = 0; i < m_counts.size() - 1; i++)
356  {
357  if (m_counts[i] > 0) { c++; }
358  }
359  return c;
360  }
361 
363  {
365  for (int i = 0; i < m_counts.size(); i++) { counts.insert(m_counts[i], simulator(i)); }
366  return counts;
367  }
368 
370  {
371  return u"FSX: " % QString::number(m_counts[0]) % u" P3D: " % QString::number(m_counts[1]) % u" FS9: " %
372  QString::number(m_counts[2]) % u" XPlane: " % QString::number(m_counts[3]) % u" FG: " %
373  QString::number(m_counts[4]) % u" MSFS: " % QString::number(m_counts[5]) % u" MSFS2024: " %
374  QString::number(m_counts[6]);
375  }
376 
377  void CCountPerSimulator::setCount(int count, const CSimulatorInfo &simulator)
378  {
379  m_counts[internalIndex(simulator)] = count;
380  }
381 
383  {
384  if (simulator.isNoSimulator() || simulator.isUnspecified())
385  {
386  // unknown count
387  m_counts[6]++;
388  return;
389  }
390  if (simulator.isFSX()) { m_counts[0]++; }
391  if (simulator.isP3D()) { m_counts[1]++; }
392  if (simulator.isFS9()) { m_counts[2]++; }
393  if (simulator.isXPlane()) { m_counts[3]++; }
394  if (simulator.isFG()) { m_counts[4]++; }
395  if (simulator.isMSFS()) { m_counts[5]++; }
396  if (simulator.isMSFS2024()) { m_counts[6]++; }
397  }
398 
399  int CCountPerSimulator::internalIndex(const CSimulatorInfo &simulator)
400  {
401  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
402  switch (simulator.getSimulator())
403  {
404  case CSimulatorInfo::FSX: return 0;
405  case CSimulatorInfo::P3D: return 1;
406  case CSimulatorInfo::FS9: return 2;
407  case CSimulatorInfo::XPLANE: return 3;
408  case CSimulatorInfo::FG: return 4;
409  case CSimulatorInfo::MSFS: return 5;
410  case CSimulatorInfo::MSFS2024: return 6;
411  default: return CSimulatorInfo::NumberOfSimulators; // unknown
412  }
413  }
414 
415  CSimulatorInfo CCountPerSimulator::simulator(int internalIndex)
416  {
417  switch (internalIndex)
418  {
419  case 0: return { CSimulatorInfo::FSX };
420  case 1: return { CSimulatorInfo::P3D };
421  case 2: return { CSimulatorInfo::FS9 };
422  case 3: return { CSimulatorInfo::XPLANE };
423  case 4: return { CSimulatorInfo::FG };
424  case 5: return { CSimulatorInfo::MSFS };
425  case 6: return { CSimulatorInfo::MSFS2024 };
426  default: return { CSimulatorInfo::None };
427  }
428  }
429 } // 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.
CSimulatorInfo()=default
Default constructor.
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.
QJsonValue value(QLatin1StringView key) const const
bool isBool() const const
bool toBool(bool defaultValue) const const
QString toString() const const
QList< T >::iterator begin()
QList< T >::iterator end()
void push_back(QList< T >::parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
QMultiMap< Key, T >::iterator insert(QMultiMap< Key, T >::const_iterator pos, const Key &key, const T &value)
QSet< T >::iterator insert(QSet< T >::const_iterator it, const T &value)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString toLower() const const
QString trimmed() const const
QString join(QChar separator) const const
void sort(Qt::CaseSensitivity cs)
CaseInsensitive
#define SWIFT_DEFINE_VALUEOBJECT_MIXINS(Namespace, Class)
Explicit template definition of mixins for a CValueObject subclass.
Definition: valueobject.h:67