swift
serializer.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2019 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "core/fsd/serializer.h"
5 
6 #include "config/buildconfig.h"
7 #include "misc/logcategories.h"
8 #include "misc/logmessage.h"
9 #include "misc/verify.h"
10 
11 using namespace swift::config;
12 using namespace swift::misc;
13 using namespace swift::misc::aviation;
14 using namespace swift::misc::network;
15 
16 namespace swift::core::fsd
17 {
19 
20  namespace Private
21  {
22  // log each issue only once
23  static QStringList s_invalidAtcRatings;
24  static QStringList s_invalidPilotRatings;
25  static QStringList s_invalidSimType;
26  static QStringList s_invalidFacilityType;
27  static QStringList s_invalidQueryType;
28 
30  void logUnknownType(const QString &message)
31  {
33  {
34  // developers should record these types and EXPLICITLY exclude them
35  const QByteArray msg = message.toLatin1();
36  SWIFT_VERIFY_X(false, Q_FUNC_INFO, msg);
37  }
38 
39  CLogMessage(CLogCategories::fsd()).info(u"%1. Please report this to the DEVELOPERS!") << message;
40  }
41  } // namespace Private
42 
43  template <>
44  QString toQString(const AtcRating &value)
45  {
46  switch (value)
47  {
48  case AtcRating::Unknown: return "0";
49  case AtcRating::Observer: return "1";
50  case AtcRating::Student: return "2";
51  case AtcRating::Student2: return "3";
52  case AtcRating::Student3: return "4";
53  case AtcRating::Controller1: return "5";
54  case AtcRating::Controller2: return "6";
55  case AtcRating::Controller3: return "7";
56  case AtcRating::Instructor1: return "8";
57  case AtcRating::Instructor2: return "9";
58  case AtcRating::Instructor3: return "10";
59  case AtcRating::Supervisor: return "11";
60  case AtcRating::Administrator: return "12";
61  }
62 
63  Q_UNREACHABLE();
64  return "0";
65  }
66 
67  template <>
68  AtcRating fromQString(const QString &str)
69  {
70  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
71  if (str.isEmpty()) return AtcRating::Unknown;
72 
73  // valid types
74  if (str == "1")
75  return AtcRating::Observer;
76  else if (str == "2")
77  return AtcRating::Student;
78  else if (str == "3")
79  return AtcRating::Student2;
80  else if (str == "4")
81  return AtcRating::Student3;
82  else if (str == "5")
83  return AtcRating::Controller1;
84  else if (str == "6")
85  return AtcRating::Controller2;
86  else if (str == "7")
87  return AtcRating::Controller3;
88  else if (str == "8")
89  return AtcRating::Instructor1;
90  else if (str == "9")
91  return AtcRating::Instructor2;
92  else if (str == "10")
93  return AtcRating::Instructor3;
94  else if (str == "11")
95  return AtcRating::Supervisor;
96  else if (str == "12")
97  return AtcRating::Administrator;
98 
99  // we should NOT get here
100  if (!Private::s_invalidAtcRatings.contains(str))
101  {
102  Private::s_invalidAtcRatings.push_back(str);
103  const QString msg = QStringLiteral("FSD unknown ATC rating '%1'").arg(str);
104  Private::logUnknownType(msg);
105  }
106  return AtcRating::Unknown;
107  }
108 
109  template <>
110  QString toQString(const PilotRating &value)
111  {
112  switch (value)
113  {
114  case PilotRating::Unknown: return "0";
115  case PilotRating::Student: return "1";
116  case PilotRating::VFR: return "2";
117  case PilotRating::IFR: return "3";
118  case PilotRating::Instructor: return "4";
119  case PilotRating::Supervisor: return "5";
120  }
121 
122  Q_UNREACHABLE();
123  return "0";
124  }
125 
126  template <>
127  PilotRating fromQString(const QString &str)
128  {
129  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
130  if (str.isEmpty()) return PilotRating::Unknown;
131 
132  // valid types
133  if (str == "0")
134  return PilotRating::Unknown;
135  else if (str == "1")
136  return PilotRating::Student;
137  else if (str == "2")
138  return PilotRating::VFR;
139  else if (str == "3")
140  return PilotRating::IFR;
141  else if (str == "4")
142  return PilotRating::Instructor;
143  else if (str == "5")
144  return PilotRating::Supervisor;
145 
146  // we should NOT get here
147  if (!Private::s_invalidPilotRatings.contains(str))
148  {
149  Private::s_invalidPilotRatings.push_back(str);
150  const QString msg = QStringLiteral("FSD Unknown Pilot rating '%1'").arg(str);
151  Private::logUnknownType(msg);
152  }
153  return PilotRating::Unknown;
154  }
155 
156  template <>
157  QString toQString(const SimType &value)
158  {
159  switch (value)
160  {
161  case SimType::Unknown: return "0";
162  case SimType::MSFS95: return "1";
163  case SimType::MSFS98: return "2";
164  case SimType::MSCFS: return "3";
165  case SimType::MSFS2000: return "4";
166  case SimType::MSCFS2: return "5";
167  case SimType::MSFS2002: return "6";
168  case SimType::MSCFS3: return "7";
169  case SimType::MSFS2004: return "8";
170  case SimType::MSFSX: return "9";
171  case SimType::MSFS: return "10";
172  case SimType::MSFS2024: return "11";
173  case SimType::XPLANE8: return "12";
174  case SimType::XPLANE9: return "13";
175  case SimType::XPLANE10: return "14";
176  case SimType::XPLANE11: return "16";
177  case SimType::FlightGear: return "25";
178  case SimType::P3Dv1: return "30";
179  case SimType::P3Dv2: return "30";
180  case SimType::P3Dv3: return "30";
181  case SimType::P3Dv4: return "30";
182 
183  // future versions
184  case SimType::XPLANE12:
185  case SimType::P3Dv5: return "0";
186  }
187 
188  Q_UNREACHABLE();
189  return "0";
190  }
191 
192  template <>
193  SimType fromQString(const QString &str)
194  {
195  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
196  if (str.isEmpty()) return SimType::Unknown;
197 
198  // valid types
199  if (str == "0")
200  return SimType::Unknown;
201  else if (str == "1")
202  return SimType::MSFS95;
203  else if (str == "2")
204  return SimType::MSFS98;
205  else if (str == "3")
206  return SimType::MSCFS;
207  else if (str == "4")
208  return SimType::MSFS2000;
209  else if (str == "5")
210  return SimType::MSCFS2;
211  else if (str == "6")
212  return SimType::MSFS2002;
213  else if (str == "7")
214  return SimType::MSCFS3;
215  else if (str == "8")
216  return SimType::MSFS2004;
217  else if (str == "9")
218  return SimType::MSFSX;
219  else if (str == "10")
220  return SimType::MSFS;
221  else if (str == "11")
222  return SimType::MSFS2024;
223  else if (str == "12")
224  return SimType::XPLANE8;
225  else if (str == "13")
226  return SimType::XPLANE9;
227  else if (str == "14")
228  return SimType::XPLANE10;
229  else if (str == "16")
230  return SimType::XPLANE11;
231  else if (str == "25")
232  return SimType::FlightGear;
233  // Still ambigious which P3D version. No standard defined yet.
234  else if (str == "30")
235  return SimType::P3Dv4;
236 
237  // we should NOT get here
238  if (!Private::s_invalidSimType.contains(str))
239  {
240  Private::s_invalidSimType.push_back(str);
241  const QString msg = QStringLiteral("FSD unknown SimType '%1'").arg(str);
242  Private::logUnknownType(msg);
243  }
244  return SimType::Unknown;
245  }
246 
247  template <>
248  QString toQString(const CFacilityType &value)
249  {
250  switch (value.getFacilityType())
251  {
252  case CFacilityType::OBS: return "0";
253  case CFacilityType::FSS: return "1";
254  case CFacilityType::DEL: return "2";
255  case CFacilityType::GND: return "3";
256  case CFacilityType::TWR: return "4";
257  case CFacilityType::APP: return "5";
258  case CFacilityType::CTR: return "6";
259  case CFacilityType::Unknown: return {};
260  }
261 
262  Q_UNREACHABLE();
263  return {};
264  }
265 
266  template <>
267  CFacilityType fromQString(const QString &str)
268  {
269  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
270  if (str.isEmpty()) return CFacilityType::Unknown;
271 
272  if (str == "0") return CFacilityType::OBS;
273  if (str == "1")
274  return CFacilityType::FSS;
275  else if (str == "2")
276  return CFacilityType::DEL;
277  else if (str == "3")
278  return CFacilityType::GND;
279  else if (str == "4")
280  return CFacilityType::TWR;
281  else if (str == "5")
282  return CFacilityType::APP;
283  else if (str == "6")
284  return CFacilityType::CTR;
285 
286  // we should NOT get here
287  if (!Private::s_invalidFacilityType.contains(str))
288  {
289  Private::s_invalidFacilityType.push_back(str);
290  const QString msg = QStringLiteral("FSD unknown CFacilityType '%1'");
291  Private::logUnknownType(msg);
292  }
293 
294  return CFacilityType::Unknown;
295  }
296 
297  template <>
298  QString toQString(const ClientQueryType &value)
299  {
300  switch (value)
301  {
302  case ClientQueryType::IsValidATC: return "ATC";
303  case ClientQueryType::Capabilities: return "CAPS";
304  case ClientQueryType::Com1Freq: return "C?";
305  case ClientQueryType::RealName: return "RN";
306  case ClientQueryType::Server: return "SV";
307  case ClientQueryType::ATIS: return "ATIS";
308  case ClientQueryType::PublicIP: return "IP";
309  case ClientQueryType::INF: return "INF";
310  case ClientQueryType::FP: return "FP";
311  case ClientQueryType::AircraftConfig: return "ACC";
312  case ClientQueryType::EuroscopeSimData: return "SIMDATA";
313  case ClientQueryType::Unknown: return "Unknown query type";
314  }
315 
316  Q_UNREACHABLE();
317  return "Unknown query type";
318  }
319 
320  template <>
321  ClientQueryType fromQString(const QString &str)
322  {
323  // empty string intentionally ignored, also for UNIT test, 3rd party networks
324  if (str.isEmpty()) return ClientQueryType::Unknown;
325 
326  // valid queries
327  // second part of a $CQ:, e.g. $CQ:DI, $CQ:ATC
328  if (str == "ATC") return ClientQueryType::IsValidATC;
329  if (str == "CAPS") return ClientQueryType::Capabilities;
330  if (str == "C?") return ClientQueryType::Com1Freq;
331  if (str == "RN") return ClientQueryType::RealName;
332  if (str == "SV") return ClientQueryType::Server;
333  if (str == "ATIS") return ClientQueryType::ATIS;
334  if (str == "IP") return ClientQueryType::PublicIP;
335  if (str == "INF") return ClientQueryType::INF;
336  if (str == "FP") return ClientQueryType::FP;
337  if (str == "ACC") return ClientQueryType::AircraftConfig;
338 
339  // intentionally ignored (ATC ONLY)
340  // discussion: https://discordapp.com/channels/539048679160676382/539925070550794240/669186795790925848
341  if (str == "BC") return ClientQueryType::Unknown;
342  if (str == "BY") return ClientQueryType::Unknown; // CCP_Break
343  if (str == "DI") return ClientQueryType::Unknown; // CCP_ASRC_DI
344  if (str == "DP") return ClientQueryType::Unknown; // CCP_PushToDepartureList
345  if (str == "DR") return ClientQueryType::Unknown; // CCP_DropTrack
346  if (str == "FA") return ClientQueryType::Unknown;
347  if (str == "HC") return ClientQueryType::Unknown; // CCP_HandoffCancelled
348  if (str == "HI") return ClientQueryType::Unknown; // CCP_NoBreak
349  if (str == "HT") return ClientQueryType::Unknown; // CCP_HandoffTo
350  if (str == "ID") return ClientQueryType::Unknown; // CCP_ASRC_ID
351  if (str == "IH") return ClientQueryType::Unknown; // CCP_IHave
352  if (str == "IT") return ClientQueryType::Unknown; // CCP_StartTrack
353  if (str == "PT") return ClientQueryType::Unknown; // CCP_Pointout
354  if (str == "SC") return ClientQueryType::Unknown;
355  if (str == "ST") return ClientQueryType::Unknown; // CCP_PushStrip
356  if (str == "TA") return ClientQueryType::Unknown;
357  if (str == "VT") return ClientQueryType::Unknown;
358  if (str == "VER") return ClientQueryType::Unknown; // CCP_Version
359  if (str == "WH") return ClientQueryType::Unknown; // CCP_WhoHas
360  // -- help
361  if (str == "HLP") return ClientQueryType::Unknown; // CCP_HelpOn
362  if (str == "NOHLP") return ClientQueryType::Unknown; // CCP_HelpOff
363 
364  // Query types from customized FSD servers
365  if (str == "NEWATIS") return ClientQueryType::Unknown;
366  if (str == "NEWINFO") return ClientQueryType::Unknown; // probably causing the Linux crash
367 
368  // we should NOT get here
369  if (!Private::s_invalidQueryType.contains(str))
370  {
371  Private::s_invalidQueryType.push_back(str);
372  const QString msg = QStringLiteral("FSD unknown ClientQueryType '%1'").arg(str);
373  Private::logUnknownType(msg);
374  }
375 
376  return ClientQueryType::Unknown;
377  }
378 
379  template <>
380  QString toQString(const FlightType &value)
381  {
382  switch (value)
383  {
384  case FlightType::IFR: return "I";
385  case FlightType::VFR: return "V";
386  case FlightType::SVFR: return "S";
387  case FlightType::DVFR: return "D";
388  }
389 
390  Q_UNREACHABLE();
391  return {};
392  }
393 
394  template <>
395  FlightType fromQString(const QString &str)
396  {
397  if (str == QLatin1String("I"))
398  return FlightType::IFR;
399  else if (str == QLatin1String("V"))
400  return FlightType::VFR;
401  else if (str == QLatin1String("S"))
402  return FlightType::SVFR;
403  else if (str == QLatin1String("D"))
404  return FlightType::DVFR;
405 
406  return FlightType::IFR;
407  }
408 
409  template <>
410  QString toQString(const CTransponder::TransponderMode &value)
411  {
412  switch (value)
413  {
414  case CTransponder::StateStandby: return QStringLiteral("S");
415  case CTransponder::ModeMil1:
416  case CTransponder::ModeMil2:
417  case CTransponder::ModeMil3:
418  case CTransponder::ModeMil4:
419  case CTransponder::ModeMil5:
420  case CTransponder::ModeA:
421  case CTransponder::ModeC:
422  case CTransponder::ModeS: return QStringLiteral("N");
423  case CTransponder::StateIdent: return QStringLiteral("Y");
424  }
425 
426  Q_UNREACHABLE();
427  return QStringLiteral("S");
428  }
429 
430  template <>
431  CTransponder::TransponderMode fromQString(const QString &str)
432  {
433  if (str == "S")
434  return CTransponder::StateStandby;
435  else if (str == "N")
436  return CTransponder::ModeC;
437  else if (str == "Y")
438  return CTransponder::StateIdent;
439 
440  return CTransponder::StateStandby;
441  }
442 
443  template <>
444  QString toQString(const Capabilities &value)
445  {
446  switch (value)
447  {
448  case Capabilities::None: return {};
449  case Capabilities::AtcInfo: return "ATCINFO";
450  case Capabilities::SecondaryPos: return "SECPOS";
451  case Capabilities::AircraftInfo: return "MODELDESC";
452  case Capabilities::OngoingCoord: return "ONGOINGCOORD";
453  case Capabilities::InterminPos: return "INTERIMPOS";
454  case Capabilities::VisPos: return "VISUPDATE";
455  case Capabilities::FastPos: return "FASTPOS";
456  case Capabilities::Stealth: return "STEALTH";
457  case Capabilities::AircraftConfig: return "ACCONFIG";
458  case Capabilities::IcaoEquipment: return "ICAOEQ";
459  }
460 
461  return {};
462  }
463 
464  template <>
465  Capabilities fromQString(const QString &str)
466  {
467  if (str == "ATCINFO")
468  return Capabilities::AtcInfo;
469  else if (str == "SECPOS")
470  return Capabilities::SecondaryPos;
471  else if (str == "MODELDESC")
472  return Capabilities::AircraftInfo;
473  else if (str == "ONGOINGCOORD")
474  return Capabilities::OngoingCoord;
475  else if (str == "INTERIMPOS")
476  return Capabilities::InterminPos;
477  else if (str == "FASTPOS")
478  return Capabilities::FastPos;
479  else if (str == "VISUPDATE")
480  return Capabilities::VisPos;
481  else if (str == "STEALTH")
482  return Capabilities::Stealth;
483  else if (str == "ACCONFIG")
484  return Capabilities::AircraftConfig;
485  else if (str == "ICAOEQ")
486  return Capabilities::IcaoEquipment;
487 
488  return Capabilities::None;
489  }
490 
491  template <>
492  AtisLineType fromQString(const QString &str)
493  {
494  if (str == "V")
495  return AtisLineType::VoiceRoom;
496  else if (str == "Z")
497  return AtisLineType::ZuluLogoff;
498  else if (str == "T")
499  return AtisLineType::TextMessage;
500  else if (str == "E")
501  return AtisLineType::LineCount;
502 
503  return AtisLineType::Unknown;
504  }
505 
507 
508 } // namespace swift::core::fsd
static bool isLocalDeveloperDebugBuild()
Local build for developers.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Value object encapsulating the ATC facility type, e.g. TWR, DEP, APP.
Definition: facilitytype.h:18
FacilityType getFacilityType() const
Get type.
Definition: facilitytype.h:40
FlightType
Flight types.
Definition: enums.h:90
AtisLineType
ATIS line type.
Definition: enums.h:196
SimType
Flight simulator type.
Definition: enums.h:44
AtcRating
ATC ratings.
Definition: enums.h:15
PilotRating
Pilot ratings.
Definition: enums.h:33
ClientQueryType
Client query types.
Definition: enums.h:72
Capabilities
Client capability flags *‍/.
Definition: enums.h:130
Free functions in swift::misc.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26