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