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  // future versions
185  case SimType::XPLANE12:
186  case SimType::P3Dv5: return "0";
187  }
188 
189  Q_UNREACHABLE();
190  return "0";
191  }
192 
193  template <>
194  SimType fromQString(const QString &str)
195  {
196  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
197  if (str.isEmpty()) return SimType::Unknown;
198 
199  // valid types
200  if (str == "0")
201  return SimType::Unknown;
202  else if (str == "1")
203  return SimType::MSFS95;
204  else if (str == "2")
205  return SimType::MSFS98;
206  else if (str == "3")
207  return SimType::MSCFS;
208  else if (str == "4")
209  return SimType::MSFS2000;
210  else if (str == "5")
211  return SimType::MSCFS2;
212  else if (str == "6")
213  return SimType::MSFS2002;
214  else if (str == "7")
215  return SimType::MSCFS3;
216  else if (str == "8")
217  return SimType::MSFS2004;
218  else if (str == "9")
219  return SimType::MSFSX;
220  else if (str == "10")
221  return SimType::MSFS;
222  else if (str == "11")
223  return SimType::MSFS2024;
224  else if (str == "12")
225  return SimType::XPLANE8;
226  else if (str == "13")
227  return SimType::XPLANE9;
228  else if (str == "14")
229  return SimType::XPLANE10;
230  else if (str == "16")
231  return SimType::XPLANE11;
232  else if (str == "25")
233  return SimType::FlightGear;
234  // Still ambigious which P3D version. No standard defined yet.
235  else if (str == "30")
236  return SimType::P3Dv4;
237 
238  // we should NOT get here
239  if (!Private::s_invalidSimType.contains(str))
240  {
241  Private::s_invalidSimType.push_back(str);
242  const QString msg = QStringLiteral("FSD unknown SimType '%1'").arg(str);
243  Private::logUnknownType(msg);
244  }
245  return SimType::Unknown;
246  }
247 
248  template <>
249  QString toQString(const CFacilityType &value)
250  {
251  switch (value.getFacilityType())
252  {
253  case CFacilityType::OBS: return "0";
254  case CFacilityType::FSS: return "1";
255  case CFacilityType::DEL: return "2";
256  case CFacilityType::GND: return "3";
257  case CFacilityType::TWR: return "4";
258  case CFacilityType::APP: return "5";
259  case CFacilityType::CTR: return "6";
260  case CFacilityType::Unknown: return {};
261  }
262 
263  Q_UNREACHABLE();
264  return {};
265  }
266 
267  template <>
268  CFacilityType fromQString(const QString &str)
269  {
270  // empty string intentionally ignored, also for UNIT test and 3rd parth networks
271  if (str.isEmpty()) return CFacilityType::Unknown;
272 
273  if (str == "0") return CFacilityType::OBS;
274  if (str == "1")
275  return CFacilityType::FSS;
276  else if (str == "2")
277  return CFacilityType::DEL;
278  else if (str == "3")
279  return CFacilityType::GND;
280  else if (str == "4")
281  return CFacilityType::TWR;
282  else if (str == "5")
283  return CFacilityType::APP;
284  else if (str == "6")
285  return CFacilityType::CTR;
286 
287  // we should NOT get here
288  if (!Private::s_invalidFacilityType.contains(str))
289  {
290  Private::s_invalidFacilityType.push_back(str);
291  const QString msg = QStringLiteral("FSD unknown CFacilityType '%1'");
292  Private::logUnknownType(msg);
293  }
294 
295  return CFacilityType::Unknown;
296  }
297 
298  template <>
299  QString toQString(const ClientQueryType &value)
300  {
301  switch (value)
302  {
303  case ClientQueryType::IsValidATC: return "ATC";
304  case ClientQueryType::Capabilities: return "CAPS";
305  case ClientQueryType::Com1Freq: return "C?";
306  case ClientQueryType::RealName: return "RN";
307  case ClientQueryType::Server: return "SV";
308  case ClientQueryType::ATIS: return "ATIS";
309  case ClientQueryType::PublicIP: return "IP";
310  case ClientQueryType::INF: return "INF";
311  case ClientQueryType::FP: return "FP";
312  case ClientQueryType::AircraftConfig: return "ACC";
313  case ClientQueryType::EuroscopeSimData: return "SIMDATA";
314  case ClientQueryType::Unknown: return "Unknown query type";
315  }
316 
317  Q_UNREACHABLE();
318  return "Unknown query type";
319  }
320 
321  template <>
322  ClientQueryType fromQString(const QString &str)
323  {
324  // empty string intentionally ignored, also for UNIT test, 3rd party networks
325  if (str.isEmpty()) return ClientQueryType::Unknown;
326 
327  // valid queries
328  // second part of a $CQ:, e.g. $CQ:DI, $CQ:ATC
329  if (str == "ATC") return ClientQueryType::IsValidATC;
330  if (str == "CAPS") return ClientQueryType::Capabilities;
331  if (str == "C?") return ClientQueryType::Com1Freq;
332  if (str == "RN") return ClientQueryType::RealName;
333  if (str == "SV") return ClientQueryType::Server;
334  if (str == "ATIS") return ClientQueryType::ATIS;
335  if (str == "IP") return ClientQueryType::PublicIP;
336  if (str == "INF") return ClientQueryType::INF;
337  if (str == "FP") return ClientQueryType::FP;
338  if (str == "ACC") return ClientQueryType::AircraftConfig;
339 
340  // intentionally ignored (ATC ONLY)
341  // discussion: https://discordapp.com/channels/539048679160676382/539925070550794240/669186795790925848
342  if (str == "BC") return ClientQueryType::Unknown;
343  if (str == "BY") return ClientQueryType::Unknown; // CCP_Break
344  if (str == "DI") return ClientQueryType::Unknown; // CCP_ASRC_DI
345  if (str == "DP") return ClientQueryType::Unknown; // CCP_PushToDepartureList
346  if (str == "DR") return ClientQueryType::Unknown; // CCP_DropTrack
347  if (str == "FA") return ClientQueryType::Unknown;
348  if (str == "HC") return ClientQueryType::Unknown; // CCP_HandoffCancelled
349  if (str == "HI") return ClientQueryType::Unknown; // CCP_NoBreak
350  if (str == "HT") return ClientQueryType::Unknown; // CCP_HandoffTo
351  if (str == "ID") return ClientQueryType::Unknown; // CCP_ASRC_ID
352  if (str == "IH") return ClientQueryType::Unknown; // CCP_IHave
353  if (str == "IT") return ClientQueryType::Unknown; // CCP_StartTrack
354  if (str == "PT") return ClientQueryType::Unknown; // CCP_Pointout
355  if (str == "SC") return ClientQueryType::Unknown;
356  if (str == "ST") return ClientQueryType::Unknown; // CCP_PushStrip
357  if (str == "TA") return ClientQueryType::Unknown;
358  if (str == "VT") return ClientQueryType::Unknown;
359  if (str == "VER") return ClientQueryType::Unknown; // CCP_Version
360  if (str == "WH") return ClientQueryType::Unknown; // CCP_WhoHas
361  // -- help
362  if (str == "HLP") return ClientQueryType::Unknown; // CCP_HelpOn
363  if (str == "NOHLP") return ClientQueryType::Unknown; // CCP_HelpOff
364 
365  // Query types from customized FSD servers
366  if (str == "NEWATIS") return ClientQueryType::Unknown;
367  if (str == "NEWINFO") return ClientQueryType::Unknown; // probably causing the Linux crash
368 
369  // we should NOT get here
370  if (!Private::s_invalidQueryType.contains(str))
371  {
372  Private::s_invalidQueryType.push_back(str);
373  const QString msg = QStringLiteral("FSD unknown ClientQueryType '%1'").arg(str);
374  Private::logUnknownType(msg);
375  }
376 
377  return ClientQueryType::Unknown;
378  }
379 
380  template <>
381  QString toQString(const FlightType &value)
382  {
383  switch (value)
384  {
385  case FlightType::IFR: return "I";
386  case FlightType::VFR: return "V";
387  case FlightType::SVFR: return "S";
388  case FlightType::DVFR: return "D";
389  }
390 
391  Q_UNREACHABLE();
392  return {};
393  }
394 
395  template <>
396  FlightType fromQString(const QString &str)
397  {
398  if (str == QLatin1String("I"))
399  return FlightType::IFR;
400  else if (str == QLatin1String("V"))
401  return FlightType::VFR;
402  else if (str == QLatin1String("S"))
403  return FlightType::SVFR;
404  else if (str == QLatin1String("D"))
405  return FlightType::DVFR;
406 
407  return FlightType::IFR;
408  }
409 
410  template <>
411  QString toQString(const CTransponder::TransponderMode &value)
412  {
413  switch (value)
414  {
415  case CTransponder::StateStandby: return QStringLiteral("S");
416  case CTransponder::ModeMil1:
417  case CTransponder::ModeMil2:
418  case CTransponder::ModeMil3:
419  case CTransponder::ModeMil4:
420  case CTransponder::ModeMil5:
421  case CTransponder::ModeA:
422  case CTransponder::ModeC:
423  case CTransponder::ModeS: return QStringLiteral("N");
424  case CTransponder::StateIdent: return QStringLiteral("Y");
425  }
426 
427  Q_UNREACHABLE();
428  return QStringLiteral("S");
429  }
430 
431  template <>
432  CTransponder::TransponderMode fromQString(const QString &str)
433  {
434  if (str == "S")
435  return CTransponder::StateStandby;
436  else if (str == "N")
437  return CTransponder::ModeC;
438  else if (str == "Y")
439  return CTransponder::StateIdent;
440 
441  return CTransponder::StateStandby;
442  }
443 
444  template <>
445  QString toQString(const Capabilities &value)
446  {
447  switch (value)
448  {
449  case Capabilities::None: return {};
450  case Capabilities::AtcInfo: return "ATCINFO";
451  case Capabilities::SecondaryPos: return "SECPOS";
452  case Capabilities::AircraftInfo: return "MODELDESC";
453  case Capabilities::OngoingCoord: return "ONGOINGCOORD";
454  case Capabilities::InterminPos: return "INTERIMPOS";
455  case Capabilities::VisPos: return "VISUPDATE";
456  case Capabilities::FastPos: return "FASTPOS";
457  case Capabilities::Stealth: return "STEALTH";
458  case Capabilities::AircraftConfig: return "ACCONFIG";
459  case Capabilities::IcaoEquipment: return "ICAOEQ";
460  }
461 
462  return {};
463  }
464 
465  template <>
466  Capabilities fromQString(const QString &str)
467  {
468  if (str == "ATCINFO")
469  return Capabilities::AtcInfo;
470  else if (str == "SECPOS")
471  return Capabilities::SecondaryPos;
472  else if (str == "MODELDESC")
473  return Capabilities::AircraftInfo;
474  else if (str == "ONGOINGCOORD")
475  return Capabilities::OngoingCoord;
476  else if (str == "INTERIMPOS")
477  return Capabilities::InterminPos;
478  else if (str == "FASTPOS")
479  return Capabilities::FastPos;
480  else if (str == "VISUPDATE")
481  return Capabilities::VisPos;
482  else if (str == "STEALTH")
483  return Capabilities::Stealth;
484  else if (str == "ACCONFIG")
485  return Capabilities::AircraftConfig;
486  else if (str == "ICAOEQ")
487  return Capabilities::IcaoEquipment;
488 
489  return Capabilities::None;
490  }
491 
492  template <>
493  AtisLineType fromQString(const QString &str)
494  {
495  if (str == "V")
496  return AtisLineType::VoiceRoom;
497  else if (str == "Z")
498  return AtisLineType::ZuluLogoff;
499  else if (str == "T")
500  return AtisLineType::TextMessage;
501  else if (str == "E")
502  return AtisLineType::LineCount;
503 
504  return AtisLineType::Unknown;
505  }
506 
508 
509 } // 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