The Battle for Wesnoth  1.19.21+dev
build_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2025
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "build_info.hpp"
19 
20 #include "desktop/version.hpp"
21 #include "filesystem.hpp"
22 #include "formatter.hpp"
23 #include "gettext.hpp"
26 #include "game_version.hpp"
27 #include "sound.hpp"
28 #include "video.hpp"
29 #include "addon/manager.hpp"
30 #include "sdl/point.hpp"
31 
32 #include <algorithm>
33 #include <fstream>
34 #include <iomanip>
35 
36 #include "lua/wrapper_lua.h"
37 
38 #include <SDL2/SDL_image.h>
39 #include <SDL2/SDL_mixer.h>
40 
41 #include <boost/algorithm/string.hpp>
42 #include <boost/predef.h>
43 #include <boost/version.hpp>
44 
45 #if defined(__APPLE__)
46 #include <TargetConditionals.h>
47 #endif
48 
49 #ifndef __APPLE__
50 #include <openssl/crypto.h>
51 #include <openssl/opensslv.h>
52 #endif
53 
54 #if !(defined(__APPLE__) && TARGET_OS_IPHONE)
55 #include <curl/curl.h>
56 #endif
57 
58 #include <pango/pangocairo.h>
59 
60 #ifdef __APPLE__
61 // apple_notification.mm uses Foundation.h, which is an Objective-C header;
62 // but CoreFoundation.h is a C header which also defines these.
63 #include <CoreFoundation/CoreFoundation.h>
64 #endif
65 
66 namespace game_config
67 {
68 
69 namespace {
70 
71 struct version_table_manager
72 {
73  std::vector<std::string> compiled, linked, names;
74  std::vector<optional_feature> features;
75 
76  version_table_manager();
77 };
78 
79 const version_table_manager versions;
80 
81 std::string format_version(unsigned a, unsigned b, unsigned c)
82 {
83  return formatter() << a << '.' << b << '.' << c;
84 }
85 
86 std::string format_version(const SDL_version& v)
87 {
88  return formatter() << static_cast<unsigned>(v.major) << '.'
89  << static_cast<unsigned>(v.minor) << '.'
90  << static_cast<unsigned>(v.patch);
91 }
92 
93 #ifndef __APPLE__
94 
95 std::string format_openssl_patch_level(uint8_t p)
96 {
97  return p <= 26
98  ? std::string(1, 'a' + static_cast<char>(p) - 1)
99  : "patch" + std::to_string(p);
100 }
101 
102 std::string format_openssl_version(long v)
103 {
104  int major, minor, fix, patch, status;
105  std::ostringstream fmt;
106 
107  //
108  // The people who maintain OpenSSL are not from this world. I suppose it's
109  // only fair that I'm the one who gets to try to make sense of their version
110  // encoding scheme. -- shadowm
111  //
112 
113  if(v < 0x0930L) {
114  // Pre-0.9.3 seems simpler times overall.
115  minor = v & 0x0F00L >> 8;
116  fix = v & 0x00F0L >> 4;
117  patch = v & 0x000FL;
118 
119  fmt << "0." << minor << '.' << fix;
120  if(patch) {
121  fmt << format_openssl_patch_level(patch);
122  }
123  } else {
124  //
125  // Note that they either assume the major version will never be greater than
126  // 9, they plan to use hexadecimal digits for versions 10.x.x through
127  // 15.x.x, or they expect long to be always > 32-bits by then. Who the hell
128  // knows, really.
129  //
130  major = (v & 0xF0000000L) >> 28;
131  minor = (v & 0x0FF00000L) >> 20;
132  fix = (v & 0x000FF000L) >> 12;
133  patch = (v & 0x00000FF0L) >> 4;
134  status = (v & 0x0000000FL);
135 
136  if(v < 0x00905100L) {
137  //
138  // From wiki.openssl.org (also mentioned in opensslv.h, in the most oblique
139  // fashion possible):
140  //
141  // "Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation:
142  // MMNNFFRBB major minor fix final beta/patch"
143  //
144  // Both the wiki and opensslv.h fail to accurately list actual version
145  // numbers that ended up used in the wild -- e.g. 0.9.3a is supposedly
146  // 0x0090301f when it really was 0x00903101.
147  //
148  const uint8_t is_final = (v & 0xF00L) >> 8;
149  status = is_final ? 0xF : 0;
150  patch = v & 0xFFL;
151  } else if(v < 0x00906000L) {
152  //
153  // Quoth opensslv.h:
154  //
155  // "For continuity reasons (because 0.9.5 is already out, and is coded
156  // 0x00905100), between 0.9.5 and 0.9.6 the coding of the patch level
157  // part is slightly different, by setting the highest bit. This means
158  // that 0.9.5a looks like this: 0x0090581f. At 0.9.6, we can start
159  // with 0x0090600S..."
160  //
161  patch ^= 1 << 7;
162  }
163 
164  fmt << major << '.' << minor << '.' << fix;
165 
166  if(patch) {
167  fmt << format_openssl_patch_level(patch);
168  }
169 
170  if(status == 0x0) {
171  fmt << "-dev";
172  } else if(status < 0xF) {
173  fmt << "-beta" << status;
174  }
175  }
176 
177  return fmt.str();
178 }
179 
180 #endif
181 
182 version_table_manager::version_table_manager()
183  : compiled(LIB_COUNT, "")
184  , linked(LIB_COUNT, "")
185  , names(LIB_COUNT, "")
186  , features()
187 {
188  SDL_version sdl_version;
189 
190  //
191  // SDL
192  //
193 
194  SDL_VERSION(&sdl_version);
195  compiled[LIB_SDL] = format_version(sdl_version);
196 
197  SDL_GetVersion(&sdl_version);
198  linked[LIB_SDL] = format_version(sdl_version);
199 
200  names[LIB_SDL] = "SDL";
201 
202  //
203  // SDL_image
204  //
205 
206  SDL_IMAGE_VERSION(&sdl_version);
207  compiled[LIB_SDL_IMAGE] = format_version(sdl_version);
208 
209  const SDL_version* sdl_rt_version = IMG_Linked_Version();
210  if(sdl_rt_version) {
211  linked[LIB_SDL_IMAGE] = format_version(*sdl_rt_version);
212  }
213 
214  names[LIB_SDL_IMAGE] = "SDL_image";
215 
216  //
217  // SDL_mixer
218  //
219 
220  SDL_MIXER_VERSION(&sdl_version);
221  compiled[LIB_SDL_MIXER] = format_version(sdl_version);
222 
223  sdl_rt_version = Mix_Linked_Version();
224  if(sdl_rt_version) {
225  linked[LIB_SDL_MIXER] = format_version(*sdl_rt_version);
226  }
227 
228  names[LIB_SDL_MIXER] = "SDL_mixer";
229 
230  //
231  // Boost
232  //
233 
234  compiled[LIB_BOOST] = BOOST_LIB_VERSION;
235  std::replace(compiled[LIB_BOOST].begin(), compiled[LIB_BOOST].end(), '_', '.');
236  names[LIB_BOOST] = "Boost";
237 
238  //
239  // Lua
240  //
241 
242  compiled[LIB_LUA] = LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "." LUA_VERSION_RELEASE;
243  names[LIB_LUA] = "Lua";
244 
245  //
246  // OpenSSL/libcrypto
247  //
248 
249 #ifndef __APPLE__
250  compiled[LIB_CRYPTO] = format_openssl_version(OPENSSL_VERSION_NUMBER);
251  linked[LIB_CRYPTO] = format_openssl_version(SSLeay());
252  names[LIB_CRYPTO] = "OpenSSL/libcrypto";
253 #endif
254 
255  //
256  // libcurl
257  //
258 
259 #if defined(__APPLE__) && TARGET_OS_IPHONE
260  compiled[LIB_CURL] = "unavailable";
261  linked[LIB_CURL] = "unavailable";
262  names[LIB_CURL] = "libcurl";
263 #else
264  compiled[LIB_CURL] = format_version(
265  (LIBCURL_VERSION_NUM & 0xFF0000) >> 16,
266  (LIBCURL_VERSION_NUM & 0x00FF00) >> 8,
267  LIBCURL_VERSION_NUM & 0x0000FF);
268  curl_version_info_data *curl_ver = curl_version_info(CURLVERSION_NOW);
269  if(curl_ver && curl_ver->version) {
270  linked[LIB_CURL] = curl_ver->version;
271  }
272  // This is likely to upset somebody out there, but the cURL authors
273  // consistently call it 'libcurl' (all lowercase) in all documentation.
274  names[LIB_CURL] = "libcurl";
275 #endif
276 
277  //
278  // Cairo
279  //
280 
281  compiled[LIB_CAIRO] = CAIRO_VERSION_STRING;
282  linked[LIB_CAIRO] = cairo_version_string();
283  names[LIB_CAIRO] = "Cairo";
284 
285  //
286  // Pango
287  //
288 
289  compiled[LIB_PANGO] = PANGO_VERSION_STRING;
290  linked[LIB_PANGO] = pango_version_string();
291  names[LIB_PANGO] = "Pango";
292 
293  //
294  // Features table.
295  //
296 
297  features.emplace_back(N_("feature^Lua console completion"));
298 #ifdef HAVE_HISTORY
299  features.back().enabled = true;
300 #endif
301 
302 #ifdef _X11
303 
304  features.emplace_back(N_("feature^D-Bus notifications back end"));
305 #ifdef HAVE_LIBDBUS
306  features.back().enabled = true;
307 #endif
308 
309 #endif /* _X11 */
310 
311 #ifdef _WIN32
312  // Always compiled in.
313  features.emplace_back(N_("feature^Win32 notifications back end"));
314  features.back().enabled = true;
315 #endif
316 
317 #ifdef __APPLE__
318  // Always compiled in.
319  features.emplace_back(N_("feature^Cocoa notifications back end"));
320  features.back().enabled = true;
321 #endif /* __APPLE__ */
322 }
323 
324 const std::string empty_version = "";
325 
326 } // end anonymous namespace 1
327 
328 std::string build_arch()
329 {
330 #if BOOST_ARCH_X86_64
331  return "x86_64";
332 #elif BOOST_ARCH_X86_32
333  return "x86";
334 #elif BOOST_ARCH_ARM && (defined(__arm64) || defined(_M_ARM64))
335  return "arm64";
336 #elif BOOST_ARCH_ARM
337  return "arm";
338 #elif BOOST_ARCH_IA64
339  return "ia64";
340 #elif BOOST_ARCH_PPC
341  return "ppc";
342 #elif BOOST_ARCH_ALPHA
343  return "alpha";
344 #elif BOOST_ARCH_MIPS
345  return "mips";
346 #elif BOOST_ARCH_SPARC
347  return "sparc";
348 #else
349  #warning Unrecognized platform or Boost.Predef broken/unavailable
350  // Congratulations, you're running Wesnoth on an exotic platform -- either that or you live in
351  // the foretold future where x86 and ARM stopped being the dominant CPU architectures for the
352  // general-purpose consumer market. If you want to add label support for your platform, check
353  // out the Boost.Predef library's documentation and alter the code above accordingly.
354  //
355  // On the other hand, if you got here looking for Wesnoth's biggest secret let me just say
356  // right here and now that Irdya is round. There, I said the thing that nobody has dared say
357  // in mainline content before.
358  return _("cpu_architecture^<unknown>");
359 #endif
360 }
361 
362 std::vector<optional_feature> optional_features_table(bool localize)
363 {
364  std::vector<optional_feature> res = versions.features;
365 
366  for(std::size_t k = 0; k < res.size(); ++k) {
367  if(localize) {
368  res[k].name = _(res[k].name.c_str());
369  } else {
370  // Strip annotation carets ("blah blah^actual text here") from translatable
371  // strings.
372  const auto caret_pos = res[k].name.find('^');
373  if(caret_pos != std::string::npos) {
374  res[k].name.erase(0, caret_pos + 1);
375  }
376  }
377  }
378  return res;
379 }
380 
381 const std::string& library_build_version(LIBRARY_ID lib)
382 {
383  if(lib >= LIB_COUNT) {
384  return empty_version;
385  }
386 
387  return versions.compiled[lib];
388 }
389 
390 const std::string& library_runtime_version(LIBRARY_ID lib)
391 {
392  if(lib >= LIB_COUNT) {
393  return empty_version;
394  }
395 
396  return versions.linked[lib];
397 }
398 
399 const std::string& library_name(LIBRARY_ID lib)
400 {
401  if(lib >= LIB_COUNT) {
402  return empty_version;
403  }
404 
405  return versions.names[lib];
406 }
407 
408 std::string dist_channel_id()
409 {
410  std::string info;
411  std::ifstream infofile(game_config::path + "/data/dist");
412  if(infofile.is_open()) {
413  std::getline(infofile, info);
414  infofile.close();
415  boost::trim(info);
416  }
417 
418  if(info.empty()) {
419  return "Default";
420  }
421 
422  return info;
423 }
424 
425 namespace {
426 
427 /**
428  * Formats items into a tidy 2-column list with a fixed-length first column.
429  */
430 class list_formatter
431 {
432 public:
433  using list_entry = std::pair<std::string, std::string>;
434  using contents_list = std::vector<list_entry>;
435 
436  list_formatter(const std::string& heading, const contents_list& contents = {}, const std::string& empty_placeholder = "")
437  : heading_(heading)
438  , placeholder_(empty_placeholder)
439  , contents_(contents)
440  {
441  }
442 
443  void insert(const std::string& label, const std::string& value)
444  {
445  contents_.emplace_back(label, value);
446  }
447 
448  void set_placeholder(const std::string& placeholder)
449  {
450  placeholder_ = placeholder;
451  }
452 
453  void stream_put(std::ostream& os) const;
454 
455 private:
456  static const char heading_delimiter;
457  static const std::string label_delimiter;
458 
459  std::string heading_;
460  std::string placeholder_;
461 
462  contents_list contents_;
463 };
464 
465 const char list_formatter::heading_delimiter = '=';
466 const std::string list_formatter::label_delimiter = ": ";
467 
468 void list_formatter::stream_put(std::ostream& os) const
469 {
470  if(!heading_.empty()) {
471  os << heading_ << '\n' << std::string(utf8::size(heading_), heading_delimiter) << "\n\n";
472  }
473 
474  if(contents_.empty() && !placeholder_.empty()) {
475  os << placeholder_ << '\n';
476  } else if(!contents_.empty()) {
477  auto label_length_comparator = [](const list_entry& a, const list_entry& b)
478  {
479  return utf8::size(a.first) < utf8::size(b.first);
480  };
481 
482  const auto longest_entry_label = std::max_element(contents_.begin(), contents_.end(), label_length_comparator);
483  const std::size_t min_length = longest_entry_label != contents_.end()
484  ? utf8::size(label_delimiter) + utf8::size(longest_entry_label->first)
485  : 0;
486 
487  // Save stream attributes for resetting them later after completing the loop
488  const std::size_t prev_width = os.width();
489  const std::ostream::fmtflags prev_flags = os.flags();
490 
491  os << std::left;
492 
493  for(const auto& entry : contents_) {
494  os << std::setw(min_length) << entry.first + label_delimiter << entry.second << '\n';
495  }
496 
497  os.width(prev_width);
498  os.flags(prev_flags);
499  }
500 
501  os << '\n';
502 }
503 
504 std::ostream& operator<<(std::ostream& os, const list_formatter& fmt)
505 {
506  fmt.stream_put(os);
507  return os;
508 }
509 
510 list_formatter library_versions_report_internal(const std::string& heading = "")
511 {
512  list_formatter fmt{heading};
513 
514  for(unsigned n = 0; n < LIB_COUNT; ++n)
515  {
516  if(versions.names[n].empty()) {
517  continue;
518  }
519 
520  std::string text = versions.compiled[n];
521  if(!versions.linked[n].empty()) {
522  text += " (runtime " + versions.linked[n] + ")";
523  }
524 
525  fmt.insert(versions.names[n], text);
526  }
527 
528  return fmt;
529 }
530 
531 list_formatter optional_features_report_internal(const std::string& heading = "")
532 {
533  list_formatter fmt{heading};
534 
535  const std::vector<optional_feature>& features = optional_features_table(false);
536 
537  for(const auto& feature : features) {
538  fmt.insert(feature.name, feature.enabled ? "yes" : "no");
539  }
540 
541  return fmt;
542 }
543 
544 inline std::string geometry_to_string(point p)
545 {
546  return std::to_string(p.x) + 'x' + std::to_string(p.y);
547 }
548 
549 template<typename coordinateType>
550 inline std::string geometry_to_string(coordinateType horizontal, coordinateType vertical)
551 {
552  // Use a stream in order to control significant digits in non-integers
553  return formatter() << std::fixed << std::setprecision(2) << horizontal << 'x' << vertical;
554 }
555 
556 std::string format_sdl_driver_list(std::vector<std::string> drivers, const std::string& current_driver)
557 {
558  bool found_current_driver = false;
559 
560  for(auto& drvname : drivers) {
561  if(current_driver == drvname) {
562  found_current_driver = true;
563  drvname = "[" + current_driver + "]";
564  }
565  }
566 
567  if(drivers.empty() || !found_current_driver) {
568  // This shouldn't happen but SDL is weird at times so whatevs
569  drivers.emplace_back("[" + current_driver + "]");
570  }
571 
572  return utils::join(drivers, " ");
573 }
574 
575 list_formatter video_settings_report_internal(const std::string& heading = "")
576 {
577  list_formatter fmt{heading};
578 
579  std::string placeholder;
580 
581  if(video::headless()) {
582  placeholder = "Running in non-interactive mode.";
583  }
584 
585  if(!video::has_window()) {
586  placeholder = "Video not initialized yet.";
587  }
588 
589  if(!placeholder.empty()) {
590  fmt.set_placeholder(placeholder);
591  return fmt;
592  }
593 
594  const auto& current_driver = video::current_driver();
595  auto drivers = video::enumerate_drivers();
596 
597  const auto& dpi = video::get_dpi();
598  std::string dpi_report;
599 
600  dpi_report = dpi.first == 0.0f || dpi.second == 0.0f ?
601  "<unknown>" :
602  geometry_to_string(dpi.first, dpi.second);
603 
604  fmt.insert("SDL video drivers", format_sdl_driver_list(drivers, current_driver));
605  fmt.insert("Window size", geometry_to_string(video::current_resolution()));
606  fmt.insert("Game canvas size", geometry_to_string(video::game_canvas_size()));
607  fmt.insert("Final render target size", geometry_to_string(video::output_size()));
608  fmt.insert("Render refresh rate", std::to_string(video::current_refresh_rate()));
609  fmt.insert("Screen refresh rate", std::to_string(video::native_refresh_rate()));
610  fmt.insert("Screen dpi", dpi_report);
611 
612  const auto& renderer_report = video::renderer_report();
613 
614  for(const auto& info : renderer_report) {
615  fmt.insert(info.first, info.second);
616  }
617 
618  return fmt;
619 }
620 
621 list_formatter sound_settings_report_internal(const std::string& heading = "")
622 {
623  list_formatter fmt{heading};
624 
625  const auto& driver_status = sound::driver_status::query();
626 
627  if(!driver_status.initialized) {
628  fmt.set_placeholder("Audio not initialized.");
629  return fmt;
630  }
631 
632  const auto& current_driver = sound::current_driver();
633  auto drivers = sound::enumerate_drivers();
634 
635  static std::map<uint16_t, std::string> audio_format_names = {
636  // 8 bits
637  { AUDIO_U8, "unsigned 8 bit" },
638  { AUDIO_S8, "signed 8 bit" },
639  // 16 bits
640  { AUDIO_U16LSB, "unsigned 16 bit little-endian" },
641  { AUDIO_U16MSB, "unsigned 16 bit big-endian" },
642  { AUDIO_S16LSB, "signed 16 bit little-endian" },
643  { AUDIO_S16MSB, "signed 16 bit big-endian" },
644  // 32 bits
645  { AUDIO_S32LSB, "signed 32 bit little-endian" },
646  { AUDIO_S32MSB, "signed 32 bit big-endian" },
647  { AUDIO_F32LSB, "signed 32 bit floating point little-endian" },
648  { AUDIO_F32MSB, "signed 32 bit floating point big-endian" },
649  };
650 
651  auto fmt_names_it = audio_format_names.find(driver_status.format);
652  // If we don't recognize the format id just print the raw number
653  const std::string fmt_name = fmt_names_it != audio_format_names.end()
654  ? fmt_names_it->second
655  : formatter() << "0x" << std::setfill('0') << std::setw(2*sizeof(driver_status.format)) << std::hex << std::uppercase << driver_status.format;
656 
657  fmt.insert("SDL audio drivers", format_sdl_driver_list(drivers, current_driver));
658  fmt.insert("Number of channels", std::to_string(driver_status.channels));
659  fmt.insert("Output rate", std::to_string(driver_status.frequency) + " Hz");
660  fmt.insert("Sample format", fmt_name);
661  fmt.insert("Sample size", std::to_string(driver_status.chunk_size) + " bytes");
662 
663  return fmt;
664 }
665 
666 } // end anonymous namespace 2
667 
669 {
670  return formatter{} << library_versions_report_internal();
671 }
672 
674 {
675  return formatter{} << optional_features_report_internal();
676 }
677 
678 std::string full_build_report()
679 {
680  list_formatter::contents_list paths{
681  {"Data dir", game_config::path},
682  {"User data dir", filesystem::get_user_data_dir()},
683  {"Saves dir", filesystem::get_saves_dir()},
684  {"Add-ons dir", filesystem::get_addons_dir()},
685  {"Cache dir", filesystem::get_cache_dir()},
686  {"Logs dir", filesystem::get_logs_dir()},
687  };
688 
689  // Obfuscate usernames in paths
690  for(auto& entry : paths) {
691  entry.second = filesystem::sanitize_path(entry.second);
692  }
693 
694  list_formatter::contents_list addons;
695 
696  for(const auto& addon_info : installed_addons_and_versions()) {
697  addons.emplace_back(addon_info.first, addon_info.second);
698  }
699 
700  std::ostringstream o;
701 
702  o << "The Battle for Wesnoth version " << game_config::revision << " " << build_arch() << '\n'
703  << "Running on " << desktop::os_version() << '\n'
704  << "Distribution channel: " << dist_channel_id() << '\n'
705  << '\n'
706  << list_formatter{"Game paths", paths}
707  << library_versions_report_internal("Libraries")
708  << optional_features_report_internal("Features")
709  << video_settings_report_internal("Current video settings")
710  << sound_settings_report_internal("Current audio settings")
711  << list_formatter("Installed add-ons", addons, "No add-ons installed.");
712 
713  return o.str();
714 }
715 
716 } // end namespace game_config
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition: actions.cpp:1118
std::map< std::string, std::string > installed_addons_and_versions()
Retrieves the ids and versions of all installed add-ons.
Definition: manager.cpp:191
static const char heading_delimiter
Definition: build_info.cpp:456
std::vector< optional_feature > features
Definition: build_info.cpp:74
std::string heading_
Definition: build_info.cpp:459
std::vector< std::string > compiled
Definition: build_info.cpp:73
std::string placeholder_
Definition: build_info.cpp:460
contents_list contents_
Definition: build_info.cpp:462
std::vector< std::string > linked
Definition: build_info.cpp:73
std::vector< std::string > names
Definition: build_info.cpp:73
static const std::string label_delimiter
Definition: build_info.cpp:457
std::ostringstream wrapper.
Definition: formatter.hpp:40
Platform identification and version information functions.
Declarations for File-IO.
Interfaces for manipulating version numbers of engine, add-ons, etc.
#define N_(String)
Definition: gettext.hpp:105
static std::string _(const char *str)
Definition: gettext.hpp:97
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:201
T end(const std::pair< T, T > &p)
T begin(const std::pair< T, T > &p)
std::string os_version()
Returns a string with the running OS name and version information.
Definition: version.cpp:220
std::string get_cache_dir()
Definition: filesystem.cpp:885
std::string get_user_data_dir()
Definition: filesystem.cpp:875
std::string get_saves_dir()
std::string get_logs_dir()
Definition: filesystem.cpp:880
std::string get_addons_dir()
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user's name.
Game configuration data as global variables.
Definition: build_info.cpp:67
const std::string & library_name(LIBRARY_ID lib)
Retrieve the user-visible name for the given library.
Definition: build_info.cpp:399
std::string path
Definition: filesystem.cpp:106
std::string full_build_report()
Produce a bug report-style info dump.
Definition: build_info.cpp:678
std::string library_versions_report()
Produce a plain-text report of library versions suitable for stdout/stderr.
Definition: build_info.cpp:668
std::vector< optional_feature > optional_features_table(bool localize)
Retrieve the features table.
Definition: build_info.cpp:362
const std::string & library_runtime_version(LIBRARY_ID lib)
Retrieve the runtime version number of the given library.
Definition: build_info.cpp:390
const std::string & library_build_version(LIBRARY_ID lib)
Retrieve the build-time version number of the given library.
Definition: build_info.cpp:381
std::string build_arch()
Obtain the processor architecture for this build.
Definition: build_info.cpp:328
std::string optional_features_report()
Produce a plain-text report of features suitable for stdout/stderr.
Definition: build_info.cpp:673
const std::string revision
std::string dist_channel_id()
Return the distribution channel identifier, or "Default" if missing.
Definition: build_info.cpp:408
logger & info()
Definition: log.cpp:351
std::vector< std::string > enumerate_drivers()
Definition: sound.cpp:415
std::string current_driver()
Definition: sound.cpp:409
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
Definition: unicode.cpp:100
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:81
void trim(std::string_view &s)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
bool headless()
The game is running headless.
Definition: video.cpp:147
std::vector< std::pair< std::string, std::string > > renderer_report()
Provides diagnostic information about the current renderer for the build_info API.
Definition: video.cpp:900
point output_size()
Returns the size of the final render target.
Definition: video.cpp:427
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:449
bool has_window()
Whether the game has set up a window to render into.
Definition: video.cpp:422
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
Definition: video.cpp:703
std::pair< float, float > get_dpi()
Retrieves the current game screen DPI for the build_info API.
Definition: video.cpp:880
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:793
int native_refresh_rate()
The native refresh rate of display, not taking any user preferences into account.
Definition: video.cpp:508
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:513
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:697
Holds a 2D point.
Definition: point.hpp:25
static driver_status query()
Definition: sound.cpp:428
mock_char c
mock_party p
static map_location::direction n
#define b