swift
qtfreeutils.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2018 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #ifndef SWIFT_MISC_SIMULATION_XPLANE_XPLANQTFREEUTILS_H
7 #define SWIFT_MISC_SIMULATION_XPLANE_XPLANQTFREEUTILS_H
8 
9 #include <algorithm>
10 #include <cctype>
11 #include <cmath>
12 #include <fstream>
13 #include <iterator>
14 #include <string>
15 #include <vector>
16 
17 // Strict header only X-Plane model parser utils shared between Misc and xswiftbus.
18 // Header only is necessary to no require xswiftbus to link against Misc.
19 
20 namespace swift::misc::simulation::xplane::qtfreeutils
21 {
23  inline std::string getFileName(const std::string &filePath)
24  {
25  const std::string seperator = "/\\";
26  const std::size_t sepPos = filePath.find_last_of(seperator);
27  if (sepPos != std::string::npos) { return filePath.substr(sepPos + 1, filePath.size() - 1); }
28  else { return filePath; }
29  }
30 
32  inline std::string getDirName(const std::string &filePath)
33  {
34  const std::string seperator = "/\\";
35  const std::size_t sepPos = filePath.find_last_of(seperator);
36  if (sepPos != std::string::npos)
37  {
38  std::string dirPath = filePath.substr(0, sepPos);
39  return getFileName(dirPath);
40  }
41  else { return {}; }
42  }
43 
45  inline std::string getBaseName(const std::string &filePath)
46  {
47  const std::string seperator = ".";
48  const std::string fileName = getFileName(filePath);
49  std::size_t sepPos = fileName.find(seperator);
50  if (sepPos != std::string::npos) { return fileName.substr(0, sepPos); }
51  else { return fileName; }
52  }
53 
55  inline std::vector<std::string> split(const std::string &str, size_t maxSplitCount = 0,
56  const std::string &delimiter = " ")
57  {
58  std::string s(str);
59  size_t pos = 0;
60  std::vector<std::string> tokens;
61  while ((pos = s.find(delimiter)) != std::string::npos)
62  {
63  tokens.push_back(s.substr(0, pos));
64  s.erase(0, pos + delimiter.length());
65  if (maxSplitCount > 0 && tokens.size() == maxSplitCount) { break; }
66  }
67  tokens.push_back(s);
68  return tokens;
69  }
70 
72  inline double normalizeValue(const double value, const double start, const double end)
73  {
74  // https://stackoverflow.com/questions/1628386/normalise-orientation-between-0-and-360
75  if (value >= start && value <= end) { return value; }
76  const double width = end - start;
77  const double offsetValue = value - start; // value relative to 0
78  return (offsetValue - (floor(offsetValue / width) * width)) + start;
79  }
80 
83  {
84  std::string aircraftIcaoCode;
85  std::string modelDescription;
86  std::string modelName;
87  std::string author;
88  std::string modelString;
89  };
90 
92  inline std::string stringForFlyableModel(const AcfProperties &acfProperties, const std::string &acfFile)
93  {
94  if (!acfProperties.author.empty())
95  {
96  if (!acfProperties.modelName.empty())
97  {
98  if (acfProperties.modelName.find(acfProperties.author) != std::string::npos)
99  {
100  return acfProperties.modelName;
101  }
102  else { return acfProperties.author + ' ' + acfProperties.modelName; }
103  }
104  else if (!acfProperties.aircraftIcaoCode.empty())
105  {
106  return acfProperties.author + ' ' + acfProperties.aircraftIcaoCode;
107  }
108  }
109  return getDirName(acfFile) + ' ' + getBaseName(acfFile);
110  }
111 
113  inline std::string toLower(std::string s)
114  {
115  std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
116  return s;
117  }
118 
120  inline bool stringCompareCaseInsensitive(const std::string &str1, const std::string &str2)
121  {
122  if (str1.size() != str2.size()) { return false; }
123  return std::equal(str1.begin(), str1.end(), str2.begin(), [](const char &c1, const char &c2) {
124  return (c1 == c2 || std::toupper(c1) == std::toupper(c2));
125  });
126  }
127 
129  inline const std::string &boolToYesNo(bool t)
130  {
131  static const std::string y("yes");
132  static const std::string n("no");
133  return t ? y : n;
134  }
135 
137  inline const std::string &boolTotrueFalse(bool t)
138  {
139  static const std::string tr("true");
140  static const std::string fa("false");
141  return t ? tr : fa;
142  }
143 
145  inline bool isFuzzyEqual(double v1, double v2)
146  {
147  // we allow some epsilon here
148  // static const double Epsilon = 5 * std::numeric_limits<double>::min();
149  static const double Epsilon = 1E-08;
150  return (fabs(v1 - v2) < Epsilon);
151  }
152 
154  inline std::string simplifyWhitespace(const std::string &s)
155  {
156  std::string result;
157  for (char c : s)
158  {
159  if (std::isspace(c))
160  {
161  if (!result.empty() && result.back() != ' ') { result.push_back(' '); }
162  }
163  else { result.push_back(c); }
164  }
165  while (!result.empty() && result.back() == ' ') { result.pop_back(); }
166  return result;
167  }
168 
170  inline AcfProperties extractAcfProperties(const std::string &filePath)
171  {
172  std::ifstream fs(filePath, std::ios::in);
173  if (!fs.is_open()) { return {}; }
174 
175  std::string i;
176  std::string version;
177  std::string acf;
178  std::getline(fs, i);
179  i = toLower(i);
180  std::getline(fs, version);
181  version = toLower(version);
182  std::getline(fs, acf);
183  acf = toLower(acf);
184 
185  AcfProperties acfProperties;
186 
187  if (i == "i" && version.find("version") != std::string::npos && acf == "acf")
188  {
189  std::string line;
190  while (std::getline(fs, line))
191  {
192  auto tokens = split(line, 2);
193  if (tokens.size() < 3 || tokens.at(0) != "P") { continue; }
194 
195  if (tokens.at(1) == "acf/_ICAO") { acfProperties.aircraftIcaoCode = tokens.at(2); }
196  else if (tokens.at(1) == "acf/_descrip") { acfProperties.modelDescription = "[ACF] " + tokens.at(2); }
197  else if (tokens.at(1) == "acf/_name") { acfProperties.modelName = tokens.at(2); }
198  else if (tokens.at(1) == "acf/_studio") { acfProperties.author = tokens.at(2); }
199  else if (tokens.at(1) == "acf/_author")
200  {
201  if (!acfProperties.author.empty()) { continue; }
202  std::string author = tokens.at(2);
203  size_t pos =
204  author.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_");
205  author = author.substr(0, pos);
206  if (author.empty()) { continue; }
207  acfProperties.author = author;
208  }
209  }
210  }
211 
212  fs.close();
213  acfProperties.modelString = simplifyWhitespace(stringForFlyableModel(acfProperties, filePath));
214  return acfProperties;
215  }
216 
218  template <typename I>
220  {
223  using value_type = typename std::iterator_traits<I>::value_type;
224  using difference_type = typename std::iterator_traits<I>::difference_type;
225  using reference = typename std::iterator_traits<I>::reference;
226  using pointer = typename std::iterator_traits<I>::pointer;
227  using iterator_category = std::forward_iterator_tag;
229 
231  Utf8Iterator() = default;
232 
235 
238  friend bool operator==(Utf8Iterator a, Utf8Iterator b) { return a.base == b.base; }
239  friend bool operator!=(Utf8Iterator a, Utf8Iterator b) { return a.base != b.base; }
240  friend bool operator==(Utf8Iterator a, I b) { return a.base == b; }
241  friend bool operator!=(Utf8Iterator a, I b) { return a.base != b; }
242  friend bool operator==(I a, Utf8Iterator b) { return a == b.base; }
243  friend bool operator!=(I a, Utf8Iterator b) { return a != b.base; }
245 
247  reference operator*() const { return *base; }
248 
250  pointer operator->() const { return base.operator->(); }
251 
254  {
255  constexpr auto isContinuation = [](auto c) {
256  return (c & static_cast<value_type>(0b11000000)) == static_cast<value_type>(0b10000000);
257  };
258  do {
259  ++base;
260  }
261  while (base != end && isContinuation(*base));
262  return *this;
263  }
264 
267  {
268  auto copy = *this;
269  ++*this;
270  return copy;
271  }
272 
273  I base;
274  I end;
275  };
276 } // namespace swift::misc::simulation::xplane::qtfreeutils
277 
278 #endif // SWIFT_MISC_SIMULATION_XPLANE_XPLANQTFREEUTILS_H
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
SWIFT_MISC_EXPORT const QString & boolToYesNo(bool v)
Bool to yes/no.
bool stringCompareCaseInsensitive(const std::string &str1, const std::string &str2)
Compare case insensitive.
Definition: qtfreeutils.h:120
std::string stringForFlyableModel(const AcfProperties &acfProperties, const std::string &acfFile)
Get the model string for a flyable aircraft.
Definition: qtfreeutils.h:92
bool isFuzzyEqual(double v1, double v2)
Qt free version of fuzzy compare.
Definition: qtfreeutils.h:145
std::string getBaseName(const std::string &filePath)
Get the base name of the file.
Definition: qtfreeutils.h:45
AcfProperties extractAcfProperties(const std::string &filePath)
Extract ACF properties from an aircraft file.
Definition: qtfreeutils.h:170
const std::string & boolTotrueFalse(bool t)
True/false from bool.
Definition: qtfreeutils.h:137
std::string simplifyWhitespace(const std::string &s)
Trim whitespace from the beginning and end, and replace sequences of whitespace with single space cha...
Definition: qtfreeutils.h:154
std::string getFileName(const std::string &filePath)
Get filename (including all extensions) from a filePath.
Definition: qtfreeutils.h:23
std::string toLower(std::string s)
String to lower case.
Definition: qtfreeutils.h:113
double normalizeValue(const double value, const double start, const double end)
Normalize value to range start -> end (like for +-180degrees)
Definition: qtfreeutils.h:72
std::string getDirName(const std::string &filePath)
Get the name of the parent directory.
Definition: qtfreeutils.h:32
std::vector< std::string > split(const std::string &str, size_t maxSplitCount=0, const std::string &delimiter=" ")
Split string by delimiter and maxSplitCount times.
Definition: qtfreeutils.h:55
Encoding-aware iterator adaptor for std::u8string.
Definition: qtfreeutils.h:220
typename std::iterator_traits< I >::pointer pointer
STL compatibility.
Definition: qtfreeutils.h:226
typename std::iterator_traits< I >::difference_type difference_type
STL compatibility.
Definition: qtfreeutils.h:224
friend bool operator==(Utf8Iterator a, I b)
Equality.
Definition: qtfreeutils.h:240
friend bool operator==(Utf8Iterator a, Utf8Iterator b)
Equality.
Definition: qtfreeutils.h:238
friend bool operator!=(Utf8Iterator a, I b)
Equality.
Definition: qtfreeutils.h:241
friend bool operator!=(I a, Utf8Iterator b)
Equality.
Definition: qtfreeutils.h:243
friend bool operator!=(Utf8Iterator a, Utf8Iterator b)
Equality.
Definition: qtfreeutils.h:239
std::forward_iterator_tag iterator_category
STL compatibility.
Definition: qtfreeutils.h:227
typename std::iterator_traits< I >::reference reference
STL compatibility.
Definition: qtfreeutils.h:225
reference operator*() const
Dereference (not encoding-aware)
Definition: qtfreeutils.h:247
pointer operator->() const
Pointer indirection (not encoding-aware)
Definition: qtfreeutils.h:250
typename std::iterator_traits< I >::value_type value_type
STL compatibility.
Definition: qtfreeutils.h:223
friend bool operator==(I a, Utf8Iterator b)
Equality.
Definition: qtfreeutils.h:242