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 xp, bool p3d, bool fg, bool msfs, bool msfs2024)
43  : m_simulator(boolToFlag(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::isXPlane() const { return getSimulator().testFlag(XPLANE); }
51 
52  bool CSimulatorInfo::isP3D() const { return getSimulator().testFlag(P3D); }
53 
54  bool CSimulatorInfo::isFG() const { return getSimulator().testFlag(FG); }
55 
56  bool CSimulatorInfo::isMSFS() const { return getSimulator().testFlag(MSFS); }
57 
58  bool CSimulatorInfo::isMSFS2024() const { return getSimulator().testFlag(MSFS2024); }
59 
60  bool CSimulatorInfo::isAnySimulator() const { return isXPlane() || isP3D() || isFG() || isMSFS() || isMSFS2024(); }
61 
62  bool CSimulatorInfo::isSingleSimulator() const { return this->numberSimulators() == 1; }
63 
64  bool CSimulatorInfo::isNoSimulator() const { return m_simulator == 0; }
65 
66  bool CSimulatorInfo::isMultipleSimulators() const { return this->numberSimulators() > 1; }
67 
68  bool CSimulatorInfo::isAllSimulators() const { return isXPlane() && isP3D() && isFG() && isMSFS() && isMSFS2024(); }
69 
70  bool CSimulatorInfo::isMicrosoftSimulator() const { return isMSFS() || isMSFS2024(); }
71 
73 
74  bool CSimulatorInfo::isFsxP3DFamily() const { return isP3D() || isMSFS() || isMSFS2024(); }
75 
77  {
78  int c = isXPlane() ? 1 : 0;
79  if (isP3D()) { c++; }
80  if (isFG()) { c++; }
81  if (isMSFS()) { c++; }
82  if (isMSFS2024()) { c++; }
83  return c;
84  }
85 
86  bool CSimulatorInfo::matchesAll(const CSimulatorInfo &otherInfo) const
87  {
88  return (m_simulator & otherInfo.m_simulator) == otherInfo.m_simulator;
89  }
90 
91  bool CSimulatorInfo::matchesAny(const CSimulatorInfo &otherInfo) const
92  {
93  return (m_simulator & otherInfo.m_simulator) > 0;
94  }
95 
96  bool CSimulatorInfo::matchesAnyOrNone(const CSimulatorInfo &otherInfo) const
97  {
98  if (this->isNoSimulator()) { return true; }
99  return this->matchesAny(otherInfo);
100  }
101 
103  {
104  Q_UNUSED(index)
105  return Compare::compare(m_simulator, compareValue.m_simulator);
106  }
107 
109  {
110  Q_UNUSED(i18n)
111  const Simulator s = getSimulator();
112  const QString str = (s.testFlag(P3D) ? QStringLiteral("P3D ") : QString()) %
113  (s.testFlag(XPLANE) ? QStringLiteral("XPlane ") : QString()) %
114  (s.testFlag(FG) ? QStringLiteral("FG ") : QString()) %
115  (s.testFlag(MSFS) ? QStringLiteral("MSFS ") : QString()) %
116  (s.testFlag(MSFS2024) ? QStringLiteral("MSFS2024 ") : QString());
117  return str.trimmed();
118  }
119 
120  CIcons::IconIndex CSimulatorInfo::toIcon() const { return CIcons::StandardIconEmpty; }
121 
123  {
124  // anything to add?
125  if (other.isUnspecified()) { return None; }
126  if (this->matchesAll(other)) { return None; }
127 
128  this->setSimulator(this->getSimulator() | other.getSimulator());
129  const CSimulatorInfo delta(this->getSimulator() & other.getSimulator());
130  return delta;
131  }
132 
134  {
136  if (m_simulator & P3D) { set.insert(CSimulatorInfo(P3D)); }
137  if (m_simulator & FG) { set.insert(CSimulatorInfo(FG)); }
138  if (m_simulator & XPLANE) { set.insert(CSimulatorInfo(XPLANE)); }
139  if (m_simulator & MSFS) { set.insert(CSimulatorInfo(MSFS)); }
140  if (m_simulator & MSFS2024) { set.insert(CSimulatorInfo(MSFS2024)); }
141  return set;
142  }
143 
145  {
146  m_simulator = (m_simulator ^ static_cast<int>(All)) & static_cast<int>(All);
147  }
148 
150  {
151  CStatusMessage m(this);
152  if (!this->isAnySimulator()) { return m.validationError(u"No simulator"); }
153  if (this->isMicrosoftOrPrepare3DSimulator() && this->isXPlane())
154  {
155  return m.validationError(u"Cannot combine XPlane and FS simulators");
156  }
157  if (this->isMicrosoftOrPrepare3DSimulator() && this->isFG())
158  {
159  return m.validationError(u"Cannot combine FG and FS simulators");
160  }
161  if (this->isXPlane() && this->isFG()) { return m.validationError(u"Cannot combine FG and XPlane simulators"); }
162  return m.info(u"Simulators OK for model");
163  }
164 
165  CSimulatorInfo::Simulator CSimulatorInfo::boolToFlag(bool xp, bool p3d, bool fg, bool msfs, bool msfs2024)
166  {
167  Simulator s = xp ? XPLANE : None;
168  if (p3d) { s |= P3D; }
169  if (fg) { s |= FG; }
170  if (msfs) { s |= MSFS; }
171  if (msfs2024) { s |= MSFS2024; }
172  return s;
173  }
174 
175  CSimulatorInfo::Simulator CSimulatorInfo::identifierToSimulator(const QString &identifier)
176  {
177  const QString i(identifier.toLower().trimmed().remove(' ').remove('-'));
178  if (i.isEmpty()) { return None; }
179 
180  Simulator s = None;
181  if (i.contains("plane") || i.contains("xp")) { s |= XPLANE; }
182  if (i.contains("gear") || stringCompare(QStringLiteral("fg"), identifier, Qt::CaseInsensitive)) { s |= FG; }
183  if (i.contains("3d") || i.contains("prepar") || i.contains("martin") || i.contains("lm") || i.contains("lock"))
184  {
185  s |= P3D;
186  }
187  if (i.contains("msfs2024"))
188  {
189  s |= MSFS2024;
190  return s;
191  }
192 
193  if (i.contains("msfs")) { s |= MSFS; }
194  return s;
195  }
196 
198  {
199  static const CSimulatorInfo s(All);
200  return s;
201  }
202 
204  {
205  static const QStringList sims = [] {
206  QStringList s;
207  for (const CSimulatorInfo &i : CSimulatorInfo::allSimulatorsSet()) { s.push_back(i.toQString(false)); }
209  return s;
210  }();
211  return sims;
212  }
213 
215  {
217  return all;
218  }
219 
221  {
222  static const CSimulatorInfo s(CSimulatorInfo::AllFsFamily);
223  return s;
224  }
225 
227  {
228  CSimulatorInfo sim;
229  bool p3d = false;
230  bool fg = false;
231  bool msfs = false;
232  bool msfs2024 = false;
233 
234  if (CBuildConfig::isRunningOnWindowsNtPlatform())
235  {
236  p3d = !CFsDirectories::p3dDir().isEmpty() && !CFsDirectories::p3dSimObjectsDir().isEmpty();
237  msfs = !CFsDirectories::msfsDir().isEmpty() && !CFsDirectories::msfsPackagesDir().isEmpty();
238  msfs2024 = !CFsDirectories::msfs2024Dir().isEmpty() && !CFsDirectories::msfs2024PackagesDir().isEmpty();
239  }
240 
241  const bool xp = !CXPlaneUtil::xplaneRootDir().isEmpty();
242 
244  return sim;
245  }
246 
248  CSimulatorInfo guessDefaultSimulatorImpl()
249  {
250  static const CSimulatorInfo locallyInstalled(CSimulatorInfo::getLocallyInstalledSimulators());
251  if (CBuildConfig::isRunningOnLinuxPlatform() || CBuildConfig::isRunningOnMacOSPlatform())
252  {
253  return CSimulatorInfo::xplane();
254  }
255  if (locallyInstalled.isP3D()) { return CSimulatorInfo::p3d(); }
256 
257  // fallback
258  return CSimulatorInfo::p3d();
259  }
261 
263  {
264  static const CSimulatorInfo sim(guessDefaultSimulatorImpl());
265  return sim;
266  }
267 
269  {
270  const QJsonValue jxp = json.value(prefix % u"simxplane");
271  const QJsonValue jp3d = json.value(prefix % u"simp3d");
272  const QJsonValue jfg = json.value(prefix % u"simfg");
273  const QJsonValue jmsfs = json.value(prefix % u"simmsfs");
274  const QJsonValue jmsfs2024 = json.value(prefix % u"simmsfs2024");
275 
276  // we handle bool JSON values and bool as string
277  const bool xp = jxp.isBool() ? jxp.toBool() : CDatastoreUtility::dbBoolStringToBool(jxp.toString());
278  const bool p3d = jp3d.isBool() ? jp3d.toBool() : CDatastoreUtility::dbBoolStringToBool(jp3d.toString());
279  const bool fg = jfg.isBool() ? jfg.toBool() : CDatastoreUtility::dbBoolStringToBool(jfg.toString());
280  const bool msfs = jmsfs.isBool() ? jmsfs.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs.toString());
281  const bool msfs2024 =
282  jmsfs2024.isBool() ? jmsfs2024.toBool() : CDatastoreUtility::dbBoolStringToBool(jmsfs2024.toString());
283 
284  const CSimulatorInfo simInfo(xp, p3d, fg, msfs, msfs2024);
285  return simInfo;
286  }
287 
289  {
291  for (int i = 0; i < CSimulatorInfo::NumberOfSimulators + 1; i++) { m_counts.push_back(0); }
292  }
293 
294  int CCountPerSimulator::getCount(const CSimulatorInfo &simulator) const
295  {
296  return m_counts[internalIndex(simulator)];
297  }
298 
300  {
301  return m_counts[CSimulatorInfo::NumberOfSimulators];
302  }
303 
305  {
306  return this->getCount(CSimulatorInfo::p3d()) + this->getCount(CSimulatorInfo::msfs()) +
308  }
309 
311  {
312  return this->getCount(CSimulatorInfo::p3d()) + this->getCount(CSimulatorInfo::msfs()) +
314  }
315 
316  int CCountPerSimulator::getMaximum() const { return *std::min_element(m_counts.begin(), m_counts.end()); }
317 
318  int CCountPerSimulator::getMinimum() const { return *std::max_element(m_counts.begin(), m_counts.end()); }
319 
321  {
322  int c = 0;
323  for (int i = 0; i < m_counts.size() - 1; i++)
324  {
325  if (m_counts[i] > 0) { c++; }
326  }
327  return c;
328  }
329 
331  {
333  for (int i = 0; i < m_counts.size(); i++) { counts.insert(m_counts[i], simulator(i)); }
334  return counts;
335  }
336 
338  {
339  return u" P3D: " % QString::number(m_counts[1]) % u" XPlane: " % QString::number(m_counts[3]) % u" FG: " %
340  QString::number(m_counts[4]) % u" MSFS: " % QString::number(m_counts[5]) % u" MSFS2024: " %
341  QString::number(m_counts[6]);
342  }
343 
344  void CCountPerSimulator::setCount(int count, const CSimulatorInfo &simulator)
345  {
346  m_counts[internalIndex(simulator)] = count;
347  }
348 
350  {
351  if (simulator.isNoSimulator() || simulator.isUnspecified())
352  {
353  // unknown count
354  m_counts[6]++;
355  return;
356  }
357  if (simulator.isP3D()) { m_counts[1]++; }
358  if (simulator.isXPlane()) { m_counts[3]++; }
359  if (simulator.isFG()) { m_counts[4]++; }
360  if (simulator.isMSFS()) { m_counts[5]++; }
361  if (simulator.isMSFS2024()) { m_counts[6]++; }
362  }
363 
364  int CCountPerSimulator::internalIndex(const CSimulatorInfo &simulator)
365  {
366  Q_ASSERT_X(simulator.isSingleSimulator(), Q_FUNC_INFO, "Need single simulator");
367  switch (simulator.getSimulator())
368  {
369  // 0 => FSX
370  case CSimulatorInfo::P3D: return 1;
371  // 2 => FS9
372  case CSimulatorInfo::XPLANE: return 3;
373  case CSimulatorInfo::FG: return 4;
374  case CSimulatorInfo::MSFS: return 5;
375  case CSimulatorInfo::MSFS2024: return 6;
376  default: return CSimulatorInfo::NumberOfSimulators; // unknown
377  }
378  }
379 
380  CSimulatorInfo CCountPerSimulator::simulator(int internalIndex)
381  {
382  switch (internalIndex)
383  {
384  case 1: return { CSimulatorInfo::P3D };
385  case 3: return { CSimulatorInfo::XPLANE };
386  case 4: return { CSimulatorInfo::FG };
387  case 5: return { CSimulatorInfo::MSFS };
388  case 6: return { CSimulatorInfo::MSFS2024 };
389  default: return { CSimulatorInfo::None };
390  }
391  }
392 } // 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 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 Simulator boolToFlag(bool xp, bool isP3D, bool fg, bool msfs, bool msfs2024)
Bool flags to enum.
static CSimulatorInfo fromDatabaseJson(const QJsonObject &json, const QString &prefix)
From database JSON.
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:60
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)
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