The Battle for Wesnoth  1.19.0-dev
wesnoth.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
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 #include "addon/manager.hpp"
17 #include "build_info.hpp"
18 #include "commandline_argv.hpp"
19 #include "commandline_options.hpp" // for commandline_options, etc
20 #include "config.hpp" // for config, config::error, etc
21 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL, etc
22 #include "filesystem.hpp" // for filesystem::file_exists, filesystem::io_exception, etc
23 #include "floating_label.hpp"
24 #include "font/error.hpp" // for error
25 #include "font/font_config.hpp" // for load_font_config, etc
26 #include "formula/formula.hpp" // for formula_error
27 #include "game_config.hpp" // for path, debug, debug_lua, etc
28 #include "game_config_manager.hpp" // for game_config_manager, etc
29 #include "game_end_exceptions.hpp"
30 #include "game_launcher.hpp" // for game_launcher, etc
31 #include "gettext.hpp"
32 #include "gui/core/event/handler.hpp" // for tmanager
35 #include "gui/dialogs/message.hpp" // for show_error_message
37 #include "gui/dialogs/title_screen.hpp" // for title_screen, etc
38 #include "gui/gui.hpp" // for init
39 #include "log.hpp" // for LOG_STREAM, general, logger, etc
43 #include "sdl/exception.hpp" // for exception
44 #include "serialization/binary_or_text.hpp" // for config_writer
45 #include "serialization/parser.hpp" // for read
46 #include "serialization/preprocessor.hpp" // for preproc_define, etc
47 #include "serialization/schema_validator.hpp" // for strict_validation_enabled and schema_validator
48 #include "sound.hpp" // for commit_music_changes, etc
49 #include "formula/string_utils.hpp" // VGETTEXT
50 #include <functional>
51 #include "game_version.hpp" // for version_info
52 #include "video.hpp" // for video::error and video::quit
53 #include "wesconfig.h" // for PACKAGE
54 #include "widgets/button.hpp" // for button
55 #include "wml_exception.hpp" // for wml_exception
56 
57 #ifdef _WIN32
58 #include "log_windows.hpp"
59 
60 #include <float.h>
61 #endif // _WIN32
62 
63 #ifndef _MSC_VER
64 #include <fenv.h>
65 #endif // _MSC_VER
66 
67 #include <SDL2/SDL.h> // for SDL_Init, SDL_INIT_TIMER
68 
69 #include <boost/program_options/errors.hpp> // for error
70 #include <boost/algorithm/string/predicate.hpp> // for checking cmdline options
71 #include <optional>
72 
73 #include <algorithm> // for transform
74 #include <cerrno> // for ENOMEM
75 #include <clocale> // for setlocale, LC_ALL, etc
76 #include <cstdio> // for remove, fprintf, stderr
77 #include <cstdlib> // for srand, exit
78 #include <ctime> // for time, ctime, std::time_t
79 #include <exception> // for exception
80 #include <vector>
81 #include <iostream>
82 
83 //#define NO_CATCH_AT_GAME_END
84 
85 #ifdef _WIN32
86 
87 #ifdef INADDR_ANY
88 #undef INADDR_ANY
89 #endif
90 
91 #ifdef INADDR_BROADCAST
92 #undef INADDR_BROADCAST
93 #endif
94 
95 #ifdef INADDR_NONE
96 #undef INADDR_NONE
97 #endif
98 
99 #include <windows.h>
100 
101 #endif // _WIN32
102 
103 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
104 #include "gui/widgets/debug.hpp"
105 #endif
106 
107 static lg::log_domain log_config("config");
108 #define LOG_CONFIG LOG_STREAM(info, log_config)
109 
110 #define LOG_GENERAL LOG_STREAM(info, lg::general())
111 
112 static lg::log_domain log_preprocessor("preprocessor");
113 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
114 
115 // this is needed to allow identical functionality with clean refactoring
116 // play_game only returns on an error, all returns within play_game can
117 // be replaced with this
118 static void safe_exit(int res)
119 {
120  LOG_GENERAL << "exiting with code " << res;
121  exit(res);
122 }
123 
124 static void handle_preprocess_command(const commandline_options& cmdline_opts)
125 {
126  preproc_map input_macros;
127 
128  if(cmdline_opts.preprocess_input_macros) {
129  std::string file = *cmdline_opts.preprocess_input_macros;
130  if(filesystem::file_exists(file) == false) {
131  PLAIN_LOG << "please specify an existing file. File " << file << " doesn't exist.";
132  return;
133  }
134 
135  PLAIN_LOG << SDL_GetTicks() << " Reading cached defines from: " << file;
136 
137  config cfg;
138 
139  try {
141  read(cfg, *stream);
142  } catch(const config::error& e) {
143  PLAIN_LOG << "Caught a config error while parsing file '" << file << "':\n" << e.message;
144  }
145 
146  int read = 0;
147 
148  // use static preproc_define::read_pair(config) to make a object
149  for(const config::any_child value : cfg.all_children_range()) {
150  const preproc_map::value_type def = preproc_define::read_pair(value.cfg);
151  input_macros[def.first] = def.second;
152  ++read;
153  }
154 
155  PLAIN_LOG << SDL_GetTicks() << " Read " << read << " defines.";
156  }
157 
158  const std::string resourceToProcess(*cmdline_opts.preprocess_path);
159  const std::string targetDir(*cmdline_opts.preprocess_target);
160 
161  uint32_t startTime = SDL_GetTicks();
162 
163  // If the users add the SKIP_CORE define we won't preprocess data/core
164  bool skipCore = false;
165  bool skipTerrainGFX = false;
166 
167  // The 'core_defines_map' is the one got from data/core macros
168  preproc_map defines_map(input_macros);
169 
170  if(cmdline_opts.preprocess_defines) {
171  // add the specified defines
172  for(const std::string& define : *cmdline_opts.preprocess_defines) {
173  if(define.empty()) {
174  PLAIN_LOG << "empty define supplied";
175  continue;
176  }
177 
178  LOG_PREPROC << "adding define: " << define;
179  defines_map.emplace(define, preproc_define(define));
180 
181  if(define == "SKIP_CORE") {
182  PLAIN_LOG << "'SKIP_CORE' defined.";
183  skipCore = true;
184  } else if(define == "NO_TERRAIN_GFX") {
185  PLAIN_LOG << "'NO_TERRAIN_GFX' defined.";
186  skipTerrainGFX = true;
187  }
188  }
189  }
190 
191  // add the WESNOTH_VERSION define
192  defines_map["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
193 
194  PLAIN_LOG << "added " << defines_map.size() << " defines.";
195 
196  // preprocess core macros first if we don't skip the core
197  if(skipCore == false) {
198  PLAIN_LOG << "preprocessing common macros from 'data/core' ...";
199 
200  // process each folder explicitly to gain speed
201  preprocess_resource(game_config::path + "/data/core/macros", &defines_map);
202 
203  if(skipTerrainGFX == false) {
204  preprocess_resource(game_config::path + "/data/core/terrain-graphics", &defines_map);
205  }
206 
207  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " 'data/core' defines.";
208  } else {
209  PLAIN_LOG << "skipped 'data/core'";
210  }
211 
212  // preprocess resource
213  PLAIN_LOG << "preprocessing specified resource: " << resourceToProcess << " ...";
214 
215  preprocess_resource(resourceToProcess, &defines_map, true, true, targetDir);
216  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " total defines.";
217 
218  if(cmdline_opts.preprocess_output_macros) {
219  std::string outputFileName = "_MACROS_.cfg";
220  if(!cmdline_opts.preprocess_output_macros->empty()) {
221  outputFileName = *cmdline_opts.preprocess_output_macros;
222  }
223 
224  std::string outputPath = targetDir + "/" + outputFileName;
225 
226  PLAIN_LOG << "writing '" << outputPath << "' with " << defines_map.size() << " defines.";
227 
229  if(!out->fail()) {
230  config_writer writer(*out, false);
231 
232  for(auto& define_pair : defines_map) {
233  define_pair.second.write(writer, define_pair.first);
234  }
235  } else {
236  PLAIN_LOG << "couldn't open the file.";
237  }
238  }
239 
240  PLAIN_LOG << "preprocessing finished. Took " << SDL_GetTicks() - startTime << " ticks.";
241 }
242 
243 static int handle_validate_command(const std::string& file, abstract_validator& validator, const std::vector<std::string>& defines) {
244  preproc_map defines_map;
245  // add the WESNOTH_VERSION define
246  defines_map["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
247  defines_map["SCHEMA_VALIDATION"] = preproc_define();
248  for(const std::string& define : defines) {
249  if(define.empty()) {
250  PLAIN_LOG << "empty define supplied";
251  continue;
252  }
253 
254  LOG_PREPROC << "adding define: " << define;
255  defines_map.emplace(define, preproc_define(define));
256  }
257  PLAIN_LOG << "Validating " << file << " against schema " << validator.name_;
259  filesystem::scoped_istream stream = preprocess_file(file, &defines_map);
260  config result;
261  read(result, *stream, &validator);
262  if(lg::broke_strict()) {
263  std::cout << "validation failed\n";
264  } else {
265  std::cout << "validation succeeded\n";
266  }
267  return lg::broke_strict();
268 }
269 
270 /** Process commandline-arguments */
271 static int process_command_args(const commandline_options& cmdline_opts)
272 {
273  // Options that don't change behavior based on any others should be checked alphabetically below.
274 
275  if(cmdline_opts.log) {
276  for(const auto& log_pair : *cmdline_opts.log) {
277  const std::string log_domain = log_pair.second;
278  const lg::severity severity = log_pair.first;
279  if(!lg::set_log_domain_severity(log_domain, severity)) {
280  PLAIN_LOG << "unknown log domain: " << log_domain;
281  return 2;
282  }
283  }
284  }
285 
286  if(cmdline_opts.usercache_dir) {
288  }
289 
290  if(cmdline_opts.usercache_path) {
291  std::cout << filesystem::get_cache_dir();
292  return 0;
293  }
294 
295  if(cmdline_opts.userconfig_dir) {
297  }
298 
299  if(cmdline_opts.userconfig_path) {
300  std::cout << filesystem::get_user_config_dir();
301  return 0;
302  }
303 
304  if(cmdline_opts.userdata_dir) {
306  }
307 
308  if(cmdline_opts.userdata_path) {
309  std::cout << filesystem::get_user_data_dir();
310  return 0;
311  }
312 
313  if(cmdline_opts.data_dir) {
314  const std::string datadir = *cmdline_opts.data_dir;
315  PLAIN_LOG << "Starting with directory: '" << datadir << "'";
316 #ifdef _WIN32
317  // use c_str to ensure that index 1 points to valid element since c_str() returns null-terminated string
318  if(datadir.c_str()[1] == ':') {
319 #else
320  if(datadir[0] == '/') {
321 #endif
322  game_config::path = datadir;
323  } else {
324  game_config::path = filesystem::get_cwd() + '/' + datadir;
325  }
326 
327  PLAIN_LOG << "Now have with directory: '" << game_config::path << "'";
329  if(!cmdline_opts.nobanner) {
330  PLAIN_LOG << "Overriding data directory with '" << game_config::path << "'";
331  }
332 
334  PLAIN_LOG << "Could not find directory '" << game_config::path << "'";
335  throw config::error("directory not found");
336  }
337 
338  // don't update font as we already updating it in game ctor
339  // font_manager_.update_font_path();
340  }
341 
342  if(cmdline_opts.data_path) {
343  std::cout << game_config::path;
344  return 0;
345  }
346 
347  if(cmdline_opts.debug_lua) {
348  game_config::debug_lua = true;
349  }
350 
351  if(cmdline_opts.allow_insecure) {
353  }
354 
355  if(cmdline_opts.strict_lua) {
357  }
358 
359  if(cmdline_opts.help) {
360  std::cout << cmdline_opts;
361  return 0;
362  }
363 
364  if(cmdline_opts.logdomains) {
365  std::cout << lg::list_log_domains(*cmdline_opts.logdomains);
366  return 0;
367  }
368 
369  if(cmdline_opts.log_precise_timestamps) {
371  }
372 
373  if(cmdline_opts.rng_seed) {
374  srand(*cmdline_opts.rng_seed);
375  }
376 
377  if(cmdline_opts.render_image) {
378  SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
379  }
380 
381  if(cmdline_opts.strict_validation) {
383  }
384 
385  if(cmdline_opts.version) {
386  std::cout << "Battle for Wesnoth" << " " << game_config::wesnoth_version.str() << "\n\n";
387  std::cout << "Library versions:\n" << game_config::library_versions_report() << '\n';
388  std::cout << "Optional features:\n" << game_config::optional_features_report();
389 
390  return 0;
391  }
392 
393  if(cmdline_opts.simple_version) {
394  std::cout << game_config::wesnoth_version.str() << "\n";
395 
396  return 0;
397  }
398 
399  if(cmdline_opts.report) {
400  std::cout << "\n========= BUILD INFORMATION =========\n\n" << game_config::full_build_report();
401  return 0;
402  }
403 
404  if(cmdline_opts.validate_schema) {
406  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
407  return handle_validate_command(*cmdline_opts.validate_schema, validator, {});
408  }
409 
410  if(cmdline_opts.do_diff) {
411  config left, right;
412  std::ifstream in_left(cmdline_opts.diff_left);
413  std::ifstream in_right(cmdline_opts.diff_right);
414  read(left, in_left);
415  read(right, in_right);
416  std::ostream* os = &std::cout;
417  if(cmdline_opts.output_file) {
418  os = new std::ofstream(*cmdline_opts.output_file);
419  }
421  out.write(right.get_diff(left));
422  if(os != &std::cout) delete os;
423  return 0;
424  }
425 
426  if(cmdline_opts.do_patch) {
427  config base, diff;
428  std::ifstream in_base(cmdline_opts.diff_left);
429  std::ifstream in_diff(cmdline_opts.diff_right);
430  read(base, in_base);
431  read(diff, in_diff);
432  base.apply_diff(diff);
433  std::ostream* os = &std::cout;
434  if(cmdline_opts.output_file) {
435  os = new std::ofstream(*cmdline_opts.output_file);
436  }
438  out.write(base);
439  if(os != &std::cout) delete os;
440  return 0;
441  }
442 
443  // Options changing their behavior dependent on some others should be checked below.
444 
445  if(cmdline_opts.preprocess) {
446  handle_preprocess_command(cmdline_opts);
447  return 0;
448  }
449 
450  if(cmdline_opts.validate_wml) {
451  std::string schema_path;
452  if(cmdline_opts.validate_with) {
453  schema_path = *cmdline_opts.validate_with;
454  if(!filesystem::file_exists(schema_path)) {
455  auto check = filesystem::get_wml_location(schema_path);
456  if(!filesystem::file_exists(check)) {
457  PLAIN_LOG << "Could not find schema file: " << schema_path;
458  } else {
459  schema_path = check;
460  }
461  } else {
462  schema_path = filesystem::normalize_path(schema_path);
463  }
464  } else {
465  schema_path = filesystem::get_wml_location("schema/game_config.cfg");
466  }
467  schema_validation::schema_validator validator(schema_path);
468  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
469  return handle_validate_command(*cmdline_opts.validate_wml, validator,
470  cmdline_opts.preprocess_defines.value_or<decltype(cmdline_opts.preprocess_defines)::value_type>({}));
471  }
472 
473  if(cmdline_opts.preprocess_defines || cmdline_opts.preprocess_input_macros || cmdline_opts.preprocess_path) {
474  // It would be good if this was supported for running tests too, possibly for other uses.
475  // For the moment show an error message instead of leaving the user wondering why it doesn't work.
476  PLAIN_LOG << "That --preprocess-* option is only supported when using --preprocess or --validate-wml.";
477  // Return an error status other than -1, because in our caller -1 means no error
478  return -2;
479  }
480 
481  // Not the most intuitive solution, but I wanted to leave current semantics for now
482  return -1;
483 }
484 
485 /**
486  * I would prefer to setup locale first so that early error
487  * messages can get localized, but we need the game_launcher
488  * initialized to have filesystem::get_intl_dir() to work. Note: setlocale()
489  * does not take GUI language setting into account.
490  */
491 static void init_locale()
492 {
493 #if defined _WIN32 || defined __APPLE__
494  setlocale(LC_ALL, "English");
495 #else
496  std::setlocale(LC_ALL, "C");
497 #endif
498 
499  const std::string& intl_dir = filesystem::get_intl_dir();
500 
501  translation::bind_textdomain(PACKAGE, intl_dir.c_str(), "UTF-8");
502  translation::bind_textdomain(PACKAGE "-lib", intl_dir.c_str(), "UTF-8");
504 }
505 
506 /**
507  * Print an alert and instructions to stderr about early initialization errors.
508  *
509  * This is provided as an aid for users dealing with potential data dir
510  * configuration issues. The first code to read core WML *has* the
511  * responsibility to call this function in the event of a problem, to inform
512  * the user of the most likely possible cause and suggest a course of action
513  * to solve the issue.
514  */
516 {
517  // NOTE: wrap output to 80 columns.
518  PLAIN_LOG << '\n'
519  << "An error at this point during initialization usually indicates that the data\n"
520  << "directory above was not correctly set or detected. Try passing the correct path\n"
521  << "in the command line with the --data-dir switch or as the only argument.";
522 }
523 
524 /**
525  * Handles the lua script command line arguments if present.
526  * This function will only run once.
527  */
529 {
530  static bool first_time = true;
531 
532  if(!first_time) {
533  return;
534  }
535 
536  first_time = false;
537 
538  if(!game->init_lua_script()) {
539  // PLAIN_LOG << "error when loading lua scripts at startup";
540  // PLAIN_LOG << "could not load lua script: " << *cmdline_opts.script_file;
541  }
542 }
543 
544 #ifdef _MSC_VER
545 static void check_fpu()
546 {
547  uint32_t f_control;
548 
549  if(_controlfp_s(&f_control, 0, 0) == 0) {
550  uint32_t unused;
551  uint32_t rounding_mode = f_control & _MCW_RC;
552 
553  if(rounding_mode != _RC_NEAR) {
554  PLAIN_LOG << "Floating point rounding mode is currently '"
555  << ((rounding_mode == _RC_CHOP)
556  ? "chop"
557  : (rounding_mode == _RC_UP)
558  ? "up"
559  : (rounding_mode == _RC_DOWN)
560  ? "down"
561  : (rounding_mode == _RC_NEAR) ? "near" : "unknown")
562  << "' setting to 'near'";
563 
564  if(_controlfp_s(&unused, _RC_NEAR, _MCW_RC)) {
565  PLAIN_LOG << "failed to set floating point rounding type to 'near'";
566  }
567  }
568 
569 #ifndef _M_AMD64
570  uint32_t precision_mode = f_control & _MCW_PC;
571  if(precision_mode != _PC_53) {
572  PLAIN_LOG << "Floating point precision mode is currently '"
573  << ((precision_mode == _PC_53)
574  ? "double"
575  : (precision_mode == _PC_24)
576  ? "single"
577  : (precision_mode == _PC_64) ? "double extended" : "unknown")
578  << "' setting to 'double'";
579 
580  if(_controlfp_s(&unused, _PC_53, _MCW_PC)) {
581  PLAIN_LOG << "failed to set floating point precision type to 'double'";
582  }
583  }
584 #endif
585 
586  } else {
587  PLAIN_LOG << "_controlfp_s failed.";
588  }
589 }
590 #else
591 static void check_fpu()
592 {
593  switch(fegetround()) {
594  case FE_TONEAREST:
595  break;
596  case FE_DOWNWARD:
597  STREAMING_LOG << "Floating point precision mode is currently 'downward'";
598  goto reset_fpu;
599  case FE_TOWARDZERO:
600  STREAMING_LOG << "Floating point precision mode is currently 'toward-zero'";
601  goto reset_fpu;
602  case FE_UPWARD:
603  STREAMING_LOG << "Floating point precision mode is currently 'upward'";
604  goto reset_fpu;
605  default:
606  STREAMING_LOG << "Floating point precision mode is currently 'unknown'";
607  goto reset_fpu;
608  reset_fpu:
609  STREAMING_LOG << " - setting to 'nearest'\n";
610  fesetround(FE_TONEAREST);
611  break;
612  }
613 }
614 #endif
615 
616 /**
617  * Setups the game environment and enters
618  * the titlescreen or game loops.
619  */
620 static int do_gameloop(const std::vector<std::string>& args)
621 {
622  srand(std::time(nullptr));
623 
624  commandline_options cmdline_opts = commandline_options(args);
625 
626  int finished = process_command_args(cmdline_opts);
627  if(finished != -1) {
628 #ifdef _WIN32
629  if(lg::using_own_console()) {
630  std::cerr << "Press enter to continue..." << std::endl;
631  std::cin.get();
632  }
633 #endif
634 
635  return finished;
636  }
637 
638  const auto game = std::make_unique<game_launcher>(cmdline_opts);
639  const int start_ticks = SDL_GetTicks();
640 
641  init_locale();
642 
643  bool res;
644 
645  // Do initialize fonts before reading the game config, to have game
646  // config error messages displayed. fonts will be re-initialized later
647  // when the language is read from the game config.
648  res = font::load_font_config();
649  if(res == false) {
650  PLAIN_LOG << "could not initialize fonts";
651  // The most common symptom of a bogus data dir path -- warn the user.
653  return 1;
654  }
655 
656  res = game->init_language();
657  if(res == false) {
658  PLAIN_LOG << "could not initialize the language";
659  return 1;
660  }
661 
662  res = game->init_video();
663  if(res == false) {
664  PLAIN_LOG << "could not initialize display";
665  return 1;
666  }
667 
668  check_fpu();
669  const cursor::manager cursor_manager;
671 
672 #if(defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
673  SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
674 #endif
675 
676  gui2::init();
677  const gui2::event::manager gui_event_manager;
678 
679  // if the log directory is not writable, then this is the error condition so show the error message.
680  // if the log directory is writable, then there's no issue.
681  // if the optional isn't set, then logging to file has been disabled, so there's no issue.
682  if(!lg::log_dir_writable().value_or(true)) {
683  utils::string_map symbols;
684  symbols["logdir"] = filesystem::get_logs_dir();
685  std::string msg = VGETTEXT("Unable to create log files in directory $logdir. This is often caused by incorrect folder permissions, anti-virus software restricting folder access, or using OneDrive to manage your My Documents folder.", symbols);
687  }
688 
689  game_config_manager config_manager(cmdline_opts);
690 
694  }
695 
696  gui2::dialogs::loading_screen::display([&res, &config_manager, &cmdline_opts]() {
699 
700  if(res == false) {
701  PLAIN_LOG << "could not initialize game config";
702  return;
703  }
704 
706 
707  res = font::load_font_config();
708  if(res == false) {
709  PLAIN_LOG << "could not re-initialize fonts for the current language";
710  return;
711  }
712 
713  if(!game_config::no_addons && !cmdline_opts.noaddons) {
715 
717  }
718  });
719 
720  if(res == false) {
721  return 1;
722  }
723 
724  LOG_CONFIG << "time elapsed: " << (SDL_GetTicks() - start_ticks) << " ms";
725 
726  plugins_manager plugins_man(new application_lua_kernel);
727 
728  const plugins_context::reg_vec callbacks {
729  {"play_multiplayer", std::bind(&game_launcher::play_multiplayer, game.get(), game_launcher::mp_mode::CONNECT)},
730  };
731 
732  const plugins_context::areg_vec accessors {
733  {"command_line", std::bind(&commandline_options::to_config, &cmdline_opts)},
734  };
735 
736  plugins_context plugins("titlescreen", callbacks, accessors);
737 
738  plugins.set_callback("exit", [](const config& cfg) { safe_exit(cfg["code"].to_int(0)); }, false);
739 
740  while(true) {
741  if(!game->has_load_data()) {
742  auto cfg = config_manager.game_config().optional_child("titlescreen_music");
743  if(cfg) {
744  for(const config& i : cfg->child_range("music")) {
746  }
747 
748  config title_music_config;
749  title_music_config["name"] = game_config::title_music;
750  title_music_config["append"] = true;
751  title_music_config["immediate"] = true;
752  sound::play_music_config(title_music_config);
753  } else {
756  }
757  }
758 
759  handle_lua_script_args(&*game, cmdline_opts);
760 
761  plugins.play_slice();
762  plugins.play_slice();
763 
764  if(!cmdline_opts.unit_test.empty()) {
765  return static_cast<int>(game->unit_test());
766  }
767 
768  if(game->play_test() == false) {
769  return 0;
770  }
771 
772  if(game->play_screenshot_mode() == false) {
773  return 0;
774  }
775 
776  if(game->play_render_image_mode() == false) {
777  return 0;
778  }
779 
780  // Start directly a campaign
781  if(game->goto_campaign() == false) {
782  if(game->jump_to_campaign_id().empty())
783  continue; // Go to main menu
784  else
785  return 1; // we got an error starting the campaign from command line
786  }
787 
788  // Start directly a multiplayer
789  // Eventually with a specified server
790  if(game->goto_multiplayer() == false) {
791  continue; // Go to main menu
792  }
793 
794  // Start directly a commandline multiplayer game
795  if(game->play_multiplayer_commandline() == false) {
796  return 0;
797  }
798 
799  if(game->goto_editor() == false) {
800  return 0;
801  }
802 
803  const font::floating_label_context label_manager;
804 
806 
807  // If loading a game, skip the titlescreen entirely
808  if(game->has_load_data() && game->load_game()) {
810  continue;
811  }
812 
813  int retval;
814  { // scope to not keep the title screen alive all game
816 
817  // Allows re-layout on resize
819  dlg.show();
820  }
821  retval = dlg.get_retval();
822  }
823 
824  switch(retval) {
826  LOG_GENERAL << "quitting game...";
827  return 0;
830  game->play_multiplayer(game_launcher::mp_mode::CONNECT);
831  break;
834  game->play_multiplayer(game_launcher::mp_mode::HOST);
835  break;
838  game->play_multiplayer(game_launcher::mp_mode::LOCAL);
839  break;
841  gui2::dialogs::loading_screen::display([&config_manager]() {
842  config_manager.reload_changed_game_config();
843  });
844  break;
846  game->start_editor();
847  break;
849  gui2::dialogs::end_credits::display();
850  break;
853  break;
855  break;
856  }
857  }
858 }
859 
860 /**
861  * Try to autodetect the location of the game data dir. Note that
862  * the root of the source tree currently doubles as the data dir.
863  */
864 static std::string autodetect_game_data_dir(std::string exe_dir)
865 {
866  std::string auto_dir;
867 
868  // scons leaves the resulting binaries at the root of the source
869  // tree by default.
870  if(filesystem::file_exists(exe_dir + "/data/_main.cfg")) {
871  auto_dir = std::move(exe_dir);
872  }
873  // cmake encourages creating a subdir at the root of the source
874  // tree for the build, and the resulting binaries are found in it.
875  else if(filesystem::file_exists(exe_dir + "/../data/_main.cfg")) {
876  auto_dir = filesystem::normalize_path(exe_dir + "/..");
877  }
878  // Allow using the current working directory as the game data dir
879  else if(filesystem::file_exists(filesystem::get_cwd() + "/data/_main.cfg")) {
880  auto_dir = filesystem::get_cwd();
881  }
882 #ifdef _WIN32
883  // In Windows builds made using Visual Studio and its CMake
884  // integration, the EXE is placed a few levels below the game data
885  // dir (e.g. .\out\build\x64-Debug).
886  else if(filesystem::file_exists(exe_dir + "/../../build") && filesystem::file_exists(exe_dir + "/../../../out")
887  && filesystem::file_exists(exe_dir + "/../../../data/_main.cfg")) {
888  auto_dir = filesystem::normalize_path(exe_dir + "/../../..");
889  }
890 #endif
891 
892  return auto_dir;
893 }
894 
895 #ifdef _WIN32
896 #define error_exit(res) \
897  do { \
898  if(lg::using_own_console()) { \
899  std::cerr << "Press enter to continue..." << std::endl; \
900  std::cin.get(); \
901  } \
902  return res; \
903  } while(false)
904 #else
905 #define error_exit(res) return res
906 #endif
907 
908 #ifdef __APPLE__
909 extern "C" int wesnoth_main(int argc, char** argv);
910 int wesnoth_main(int argc, char** argv)
911 #else
912 int main(int argc, char** argv)
913 #endif
914 {
915  auto args = read_argv(argc, argv);
916  assert(!args.empty());
917 
918 #ifdef _WIN32
919  _putenv("PANGOCAIRO_BACKEND=fontconfig");
920  _putenv("FONTCONFIG_PATH=fonts");
921 #endif
922 
923  // write_to_log_file means that writing to the log file will be done, if true.
924  // if false, output will be written to the terminal
925  // on windows, if wesnoth was not started from a console, then it will allocate one
926  bool write_to_log_file = !getenv("WESNOTH_NO_LOG_FILE");
927  [[maybe_unused]]
928  bool no_con = false;
929 
930  // --nobanner needs to be detected before the main command-line parsing happens
931  // --log-to needs to be detected so the logging output location is set before any actual logging happens
932  bool nobanner = false;
933  for(const auto& arg : args) {
934  if(arg == "--nobanner") {
935  nobanner = true;
936  break;
937  }
938  }
939 
940  // Some switches force a Windows console to be attached to the process even
941  // if Wesnoth is an IMAGE_SUBSYSTEM_WINDOWS_GUI executable because they
942  // turn it into a CLI application. Also, --no-log-to-file in particular attaches
943  // a console to a regular GUI game session.
944  //
945  // It's up to commandline_options later to handle these switches (except
946  // --no-log-to-file) later and emit any applicable console output, but right here
947  // we need a rudimentary check for the switches in question to set up the
948  // console before proceeding any further.
949  for(const auto& arg : args) {
950  // Switches that don't take arguments
951  static const std::set<std::string> terminal_switches = {
952  "--config-path", "--data-path", "-h", "--help", "--logdomains", "--nogui", "-R", "--report",
953  "--simple-version", "--userconfig-path", "--userdata-path", "-v", "--version"
954  };
955 
956  // Switches that take arguments, the switch may have the argument past
957  // the first = character, or in a subsequent argv entry which we don't
958  // care about -- we just want to see if the switch is there.
959  static const std::set<std::string> terminal_arg_switches = {
960  "-D", "--diff", "-p", "--preprocess", "-P", "--patch", "--render-image", "--screenshot",
961  "-u", "--unit", "-V", "--validate", "--validate-schema"
962  };
963 
964  auto switch_matches_arg = [&arg](const std::string& sw) {
965  const auto pos = arg.find('=');
966  return pos == std::string::npos ? arg == sw : arg.substr(0, pos) == sw;
967  };
968 
969  if(terminal_switches.find(arg) != terminal_switches.end() ||
970  std::find_if(terminal_arg_switches.begin(), terminal_arg_switches.end(), switch_matches_arg) != terminal_arg_switches.end()) {
971  write_to_log_file = false;
972  }
973 
974  if(arg == "--no-log-to-file") {
975  write_to_log_file = false;
976  } else if(arg == "--log-to-file") {
977  write_to_log_file = true;
978  }
979 
980  if(arg == "--wnoconsole") {
981  no_con = true;
982  }
983  }
984 
985  // setup logging to file
986  // else handle redirecting the output and potentially attaching a console on windows
987  if(write_to_log_file) {
989  } else {
990 #ifdef _WIN32
991  if(!no_con) {
993  }
994 #endif
995  }
996 
997  SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
998  // Is there a reason not to just use SDL_INIT_EVERYTHING?
999  if(SDL_Init(SDL_INIT_TIMER) < 0) {
1000  PLAIN_LOG << "Couldn't initialize SDL: " << SDL_GetError();
1001  return (1);
1002  }
1003  atexit(SDL_Quit);
1004 
1005  // Mac's touchpad generates touch events too.
1006  // Ignore them until Macs have a touchscreen: https://forums.libsdl.org/viewtopic.php?p=45758
1007 #if defined(__APPLE__) && !defined(__IPHONEOS__)
1008  SDL_EventState(SDL_FINGERMOTION, SDL_DISABLE);
1009  SDL_EventState(SDL_FINGERDOWN, SDL_DISABLE);
1010  SDL_EventState(SDL_FINGERUP, SDL_DISABLE);
1011 #endif
1012 
1013  // declare this here so that it will always be at the front of the event queue.
1014  events::event_context global_context;
1015 
1016  SDL_StartTextInput();
1017 
1018  try {
1019  if(!nobanner) {
1020  PLAIN_LOG << "Battle for Wesnoth v" << game_config::revision << " " << game_config::build_arch();
1021  const std::time_t t = std::time(nullptr);
1022  PLAIN_LOG << "Started on " << ctime(&t);
1023  }
1024 
1025  if(std::string exe_dir = filesystem::get_exe_dir(); !exe_dir.empty()) {
1026  if(std::string auto_dir = autodetect_game_data_dir(std::move(exe_dir)); !auto_dir.empty()) {
1027  if(!nobanner) {
1028  PLAIN_LOG << "Automatically found a possible data directory at: " << auto_dir;
1029  }
1030  game_config::path = std::move(auto_dir);
1031  } else if(game_config::path.empty()) {
1032  bool data_dir_specified = false;
1033  for(int i=0;i<argc;i++) {
1034  if(std::string(argv[i]) == "--data-dir" || boost::algorithm::starts_with(argv[i], "--data-dir=")) {
1035  data_dir_specified = true;
1036  break;
1037  }
1038  }
1039  if (!data_dir_specified) {
1040  PLAIN_LOG << "Cannot find a data directory. Specify one with --data-dir";
1041  return 1;
1042  }
1043  }
1044  }
1045 
1046  const int res = do_gameloop(args);
1047  safe_exit(res);
1048  } catch(const boost::program_options::error& e) {
1049  PLAIN_LOG << "Error in command line: " << e.what();
1050  error_exit(1);
1051  } catch(const video::error& e) {
1052  PLAIN_LOG << "Video system error: " << e.what();
1053  error_exit(1);
1054  } catch(const font::error& e) {
1055  PLAIN_LOG << "Could not initialize fonts.\n\n" << e.what() << "\n\nExiting.";
1056  error_exit(1);
1057  } catch(const config::error& e) {
1058  PLAIN_LOG << e.message;
1059  error_exit(1);
1060  } catch(const gui::button::error&) {
1061  PLAIN_LOG << "Could not create button: Image could not be found";
1062  error_exit(1);
1063  } catch(const video::quit&) {
1064  // just means the game should quit
1065  } catch(const return_to_play_side_exception&) {
1066  PLAIN_LOG << "caught return_to_play_side_exception, please report this bug (quitting)";
1067  } catch(const quit_game_exception&) {
1068  PLAIN_LOG << "caught quit_game_exception (quitting)";
1069  } catch(const wml_exception& e) {
1070  PLAIN_LOG << "WML exception:\nUser message: " << e.user_message << "\nDev message: " << e.dev_message;
1071  error_exit(1);
1072  } catch(const wfl::formula_error& e) {
1073  PLAIN_LOG << e.what() << "\n\nGame will be aborted.";
1074  error_exit(1);
1075  } catch(const sdl::exception& e) {
1076  PLAIN_LOG << e.what();
1077  error_exit(1);
1078  } catch(const game::error& e) {
1079  PLAIN_LOG << "Game error: " << e.what();
1080  error_exit(1);
1081  } catch(const std::bad_alloc&) {
1082  PLAIN_LOG << "Ran out of memory. Aborted.";
1083  error_exit(ENOMEM);
1084 #if !defined(NO_CATCH_AT_GAME_END)
1085  } catch(const std::exception& e) {
1086  // Try to catch unexpected exceptions.
1087  PLAIN_LOG << "Caught general '" << typeid(e).name() << "' exception:\n" << e.what();
1088  error_exit(1);
1089  } catch(const std::string& e) {
1090  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1091  error_exit(1);
1092  } catch(const char* e) {
1093  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1094  error_exit(1);
1095  } catch(...) {
1096  // Ensure that even when we terminate with `throw 42`, the exception
1097  // is caught and all destructors are actually called. (Apparently,
1098  // some compilers will simply terminate without calling destructors if
1099  // the exception isn't caught.)
1100  PLAIN_LOG << "Caught general exception " << utils::get_unknown_exception_type() << ". Terminating.";
1101  error_exit(1);
1102 #endif
1103  }
1104 
1105  return 0;
1106 } // end main
1107 
1108 /**
1109  * @page GUIToolkitWML GUIToolkitWML
1110  * @tableofcontents
1111  *
1112  * @section State State
1113  *
1114  * A state contains the info what to do in a state. At the moment this is rather focussed on the drawing part, might change later. Keys:
1115  * Key |Type |Default |Description
1116  * -----------------|------------------------------------|---------|-------------
1117  * draw | @ref guivartype_section "section" |mandatory|Section with drawing directions for a canvas.
1118  *
1119  * @section WindowDefinition Window Definition
1120  *
1121  * A window defines how a window looks in the game.
1122  * Key |Type |Default |Description
1123  * -----------------|------------------------------------|---------|-------------
1124  * id | @ref guivartype_string "string" |mandatory|Unique id for this window.
1125  * description | @ref guivartype_t_string "t_string"|mandatory|Unique translatable name for this window.
1126  * resolution | @ref guivartype_section "section" |mandatory|The definitions of the window in various resolutions.
1127  *
1128  * @section Cell Cell
1129  *
1130  * Every grid cell has some cell configuration values and one widget in the grid cell.
1131  * Here we describe the what is available more information about the usage can be found at @ref GUILayout.
1132  *
1133  * Key |Type |Default |Description
1134  * --------------------|----------------------------------------|---------|-------------
1135  * id | @ref guivartype_string "string" |"" |A grid is a widget and can have an id. This isn't used that often, but is allowed.
1136  * linked_group | @ref guivartype_string "string" |0 |.
1137  *
1138  * @section RowValues Row Values
1139  *
1140  * For every row the following variables are available:
1141  * Key |Type |Default |Description
1142  * --------------------|----------------------------------------|---------|-------------
1143  * grow_factor | @ref guivartype_unsigned "unsigned" |0 |The grow factor for a row.
1144  *
1145  * @section CellValues Cell Values
1146  *
1147  * For every column the following variables are available:
1148  * Key |Type |Default |Description
1149  * --------------------|----------------------------------------|---------|-------------
1150  * grow_factor | @ref guivartype_unsigned "unsigned" |0 |The grow factor for a column, this value is only read for the first row.
1151  * border_size | @ref guivartype_unsigned "unsigned" |0 |The border size for this grid cell.
1152  * border | @ref guivartype_border "border" |"" |Where to place the border in this grid cell.
1153  * vertical_alignment | @ref guivartype_v_align "v_align" |"" |The vertical alignment of the widget in the grid cell. (This value is ignored if vertical_grow is true.)
1154  * horizontal_alignment| @ref guivartype_h_align "h_align" |"" |The horizontal alignment of the widget in the grid cell.(This value is ignored if horizontal_grow is true.)
1155  * vertical_grow | @ref guivartype_bool "bool" |false |Does the widget grow in vertical direction when the grid cell grows in the vertical direction. This is used if the grid cell is wider as the best width for the widget.
1156  * horizontal_grow | @ref guivartype_bool "bool" |false |Does the widget grow in horizontal direction when the grid cell grows in the horizontal direction. This is used if the grid cell is higher as the best width for the widget.
1157  */
1158 
1159 /**
1160  * @page GUILayout GUILayout
1161  * @tableofcontents
1162  *
1163  * @section Abstract Abstract
1164  *
1165  * In the widget library the placement and sizes of elements is determined by
1166  * a grid. Therefore most widgets have no fixed size.
1167  *
1168  * @section Theory Theory
1169  *
1170  * We have two examples for the addon dialog, the first example the lower
1171  * buttons are in one grid, that means if the remove button gets wider
1172  * (due to translations) the connect button (4.1 - 2.2) will be aligned
1173  * to the left of the remove button. In the second example the connect
1174  * button will be partial underneath the remove button.
1175  *
1176  * A grid exists of x rows and y columns for all rows the number of columns
1177  * needs to be the same, there is no column (nor row) span. If spanning is
1178  * required place a nested grid to do so. In the examples every row has 1 column
1179  * but rows 3, 4 (and in the second 5) have a nested grid to add more elements
1180  * per row.
1181  *
1182  * In the grid every cell needs to have a widget, if no widget is wanted place
1183  * the special widget @a spacer. This is a non-visible item which normally
1184  * shouldn't have a size. It is possible to give a spacer a size as well but
1185  * that is discussed elsewhere.
1186  *
1187  * Every row and column has a @a grow_factor, since all columns in a grid are
1188  * aligned only the columns in the first row need to define their grow factor.
1189  * The grow factor is used to determine with the extra size available in a
1190  * dialog. The algorithm determines the extra size work like this:
1191  *
1192  * * determine the extra size
1193  * * determine the sum of the grow factors
1194  * * if this sum is 0 set the grow factor for every item to 1 and sum to sum of items.
1195  * * divide the extra size with the sum of grow factors
1196  * * for every item multiply the grow factor with the division value
1197  *
1198  * eg:
1199  * * extra size 100
1200  * * grow factors 1, 1, 2, 1
1201  * * sum 5
1202  * * division 100 / 5 = 20
1203  * * extra sizes 20, 20, 40, 20
1204  *
1205  * Since we force the factors to 1 if all zero it's not possible to have non
1206  * growing cells. This can be solved by adding an extra cell with a spacer and a
1207  * grow factor of 1. This is used for the buttons in the examples.
1208  *
1209  * Every cell has a @a border_size and @a border the @a border_size is the
1210  * number of pixels in the cell which aren't available for the widget. This is
1211  * used to make sure the items in different cells aren't put side to side. With
1212  * @a border it can be determined which sides get the border. So a border is
1213  * either 0 or @a border_size.
1214  *
1215  * If the widget doesn't grow when there's more space available the alignment
1216  * determines where in the cell the widget is placed.
1217  *
1218  * @subsection AbstractExample Abstract Example
1219  *
1220  * |---------------------------------------|
1221  * | 1.1 |
1222  * |---------------------------------------|
1223  * | 2.1 |
1224  * |---------------------------------------|
1225  * | |-----------------------------------| |
1226  * | | 3.1 - 1.1 | 3.1 - 1.2 | |
1227  * | |-----------------------------------| |
1228  * |---------------------------------------|
1229  * | |-----------------------------------| |
1230  * | | 4.1 - 1.1 | 4.1 - 1.2 | 4.1 - 1.3 | |
1231  * | |-----------------------------------| |
1232  * | | 4.1 - 2.1 | 4.1 - 2.2 | 4.1 - 2.3 | |
1233  * | |-----------------------------------| |
1234  * |---------------------------------------|
1235  *
1236  *
1237  * 1.1 label : title
1238  * 2.1 label : description
1239  * 3.1 - 1.1 label : server
1240  * 3.1 - 1.2 text box : server to connect to
1241  * 4.1 - 1.1 spacer
1242  * 4.1 - 1.2 spacer
1243  * 4.1 - 1.3 button : remove addon
1244  * 4.1 - 2.1 spacer
1245  * 4.1 - 2.2 button : connect
1246  * 4.1 - 2.3 button : cancel
1247  *
1248  *
1249  * |---------------------------------------|
1250  * | 1.1 |
1251  * |---------------------------------------|
1252  * | 2.1 |
1253  * |---------------------------------------|
1254  * | |-----------------------------------| |
1255  * | | 3.1 - 1.1 | 3.1 - 1.2 | |
1256  * | |-----------------------------------| |
1257  * |---------------------------------------|
1258  * | |-----------------------------------| |
1259  * | | 4.1 - 1.1 | 4.1 - 1.2 | |
1260  * | |-----------------------------------| |
1261  * |---------------------------------------|
1262  * | |-----------------------------------| |
1263  * | | 5.1 - 1.1 | 5.1 - 1.2 | 5.1 - 2.3 | |
1264  * | |-----------------------------------| |
1265  * |---------------------------------------|
1266  *
1267  *
1268  * 1.1 label : title
1269  * 2.1 label : description
1270  * 3.1 - 1.1 label : server
1271  * 3.1 - 1.2 text box : server to connect to
1272  * 4.1 - 1.1 spacer
1273  * 4.1 - 1.2 button : remove addon
1274  * 5.1 - 1.1 spacer
1275  * 5.1 - 1.2 button : connect
1276  * 5.1 - 1.3 button : cancel
1277  *
1278  * @subsection ConcreteExample Concrete Example
1279  *
1280  * This is the code needed to create the skeleton for the structure the extra
1281  * flags are omitted.
1282  *
1283  * [grid]
1284  * [row]
1285  * [column]
1286  * [label]
1287  * # 1.1
1288  * [/label]
1289  * [/column]
1290  * [/row]
1291  * [row]
1292  * [column]
1293  * [label]
1294  * # 2.1
1295  * [/label]
1296  * [/column]
1297  * [/row]
1298  * [row]
1299  * [column]
1300  * [grid]
1301  * [row]
1302  * [column]
1303  * [label]
1304  * # 3.1 - 1.1
1305  * [/label]
1306  * [/column]
1307  * [column]
1308  * [text_box]
1309  * # 3.1 - 1.2
1310  * [/text_box]
1311  * [/column]
1312  * [/row]
1313  * [/grid]
1314  * [/column]
1315  * [/row]
1316  * [row]
1317  * [column]
1318  * [grid]
1319  * [row]
1320  * [column]
1321  * [spacer]
1322  * # 4.1 - 1.1
1323  * [/spacer]
1324  * [/column]
1325  * [column]
1326  * [spacer]
1327  * # 4.1 - 1.2
1328  * [/spacer]
1329  * [/column]
1330  * [column]
1331  * [button]
1332  * # 4.1 - 1.3
1333  * [/button]
1334  * [/column]
1335  * [/row]
1336  * [row]
1337  * [column]
1338  * [spacer]
1339  * # 4.1 - 2.1
1340  * [/spacer]
1341  * [/column]
1342  * [column]
1343  * [button]
1344  * # 4.1 - 2.2
1345  * [/button]
1346  * [/column]
1347  * [column]
1348  * [button]
1349  * # 4.1 - 2.3
1350  * [/button]
1351  * [/column]
1352  * [/row]
1353  * [/grid]
1354  * [/column]
1355  * [/row]
1356  * [/grid]
1357  */
1358 
1359 /**
1360  * @defgroup GUIWidgetWML GUIWidgetWML
1361  * In various parts of the GUI there are several variables types in use. This section describes them.
1362  *
1363  * Below are the simple types which have one value or a short list of options:
1364  * Variable |description
1365  * ------------------------------------------------|-----------
1366  * @anchor guivartype_unsigned unsigned |Unsigned number (positive whole numbers and zero).
1367  * @anchor guivartype_f_unsigned f_unsigned |Unsigned number or formula returning an unsigned number.
1368  * @anchor guivartype_int int |Signed number (whole numbers).
1369  * @anchor guivartype_f_int f_int |Signed number or formula returning an signed number.
1370  * @anchor guivartype_bool bool |A boolean value accepts the normal values as the rest of the game.
1371  * @anchor guivartype_f_bool f_bool |Boolean value or a formula returning a boolean value.
1372  * @anchor guivartype_string string |A text.
1373  * @anchor guivartype_t_string t_string |A translatable string.
1374  * @anchor guivartype_f_tstring f_tstring |Formula returning a translatable string.
1375  * @anchor guivartype_function function |A string containing a set of function definition for the formula language.
1376  * @anchor guivartype_color color |A string which contains the color, this a group of 4 numbers between 0 and 255 separated by a comma. The numbers are red component, green component, blue component and alpha. A color of 0 is not available. An alpha of 255 is fully transparent. Omitted values are set to 0.
1377  * @anchor guivartype_font_style font_style |A string which contains the style of the font:<ul><li>normal</li><li>bold</li><li>italic</li><li>underlined</li></ul>Since SDL has problems combining these styles only one can be picked. Once SDL will allow multiple options, this type will be transformed to a comma separated list. If empty we default to the normal style. Since the render engine is replaced by Pango markup this field will change later on. Note widgets that allow marked up text can use markup to change the font style.
1378  * @anchor guivartype_v_align v_align |Vertical alignment; how an item is aligned vertically in the available space. Possible values:<ul><li>top</li><li>bottom</li><li>center</li></ul>When nothing is set or an another value as in the list the item is centered.
1379  * @anchor guivartype_h_align h_align |Horizontal alignment; how an item is aligned horizontal in the available space. Possible values:<ul><li>left</li><li>right</li><li>center</li></ul>
1380  * @anchor guivartype_f_h_align f_h_align |A horizontal alignment or a formula returning a horizontal alignment.
1381  * @anchor guivartype_border border |Comma separated list of borders to use. Possible values:<ul><li>left</li><li>right</li><li>top</li><li>bottom</li><li>all alias for "left, right, top, bottom"</li></ul>
1382  * @anchor guivartype_scrollbar_mode scrollbar_mode|How to show the scrollbar of a widget. Possible values:<ul><li>always - The scrollbar is always shown, regardless whether it's required or not.</li><li>never - The scrollbar is never shown, even not when needed. (Note when setting this mode dialogs might not properly fit anymore).</li><li>auto - Shows the scrollbar when needed. The widget will reserve space for the scrollbar, but only show when needed.</li><li>initial_auto - Like auto, but when the scrollbar is not needed the space is not reserved.</li></ul>Use auto when the list can be changed dynamically eg the game list in the lobby. For optimization you can also use auto when you really expect a scrollbar, but don't want it to be shown when not needed eg the language list will need a scrollbar on most screens.
1383  * @anchor guivartype_resize_mode resize_mode |Determines how an image is resized. Possible values:<ul><li>scale - The image is scaled smoothly.</li><li>scale_sharp - The image is scaled with sharp (nearest neighbour) interpolation. This is good for sprites.</li><li>stretch - The first row or column of pixels is copied over the entire image. (Can only be used to scale resize in one direction, else falls back to scale.)</li><li>tile - The image is placed several times until the entire surface is filled. The last images are truncated.</li><li>tile_center - like tile, except aligned so that one tile is always centered.</li><li>tile_highres - like tile, except rendered at full output resolution in high-dpi contexts. This is useful for texturing effects, but final tile size will be unpredictable.</li></ul>
1384  * @anchor guivartype_grow_direction grow_direction|The direction in which newly added items will grow a container. Possible values:<ul><li>horizontal</li><li>vertical</li></ul>
1385  *
1386  * For more complex parts, there are sections. Sections contain of several lines of WML and can have sub sections. For example a grid has sub sections which contain various widgets. Here's the list of sections:
1387  * Variable |description
1388  * ------------------------------------------------|-----------
1389  * @anchor guivartype_section section |A generic section. The documentation about the section should describe the section in further detail.
1390  * @anchor guivartype_grid grid |A grid contains several widgets.
1391  * @anchor guivartype_config config |.
1392  *
1393  * Every widget has some parts in common. First of all, every definition has the following fields:
1394  * Key |Type |Default |Description
1395  * -------------|------------------------------------|---------|-----------
1396  * id | @ref guivartype_string "string" |mandatory|Unique id for this gui (theme).
1397  * description | @ref guivartype_t_string "t_string"|mandatory|Unique translatable name for this gui.
1398  * resolution | @ref guivartype_section "section" |mandatory|The definitions of the widget in various resolutions.
1399  * Inside a grid (which is inside all container widgets) a widget is instantiated. With this instantiation some more variables of a widget can be tuned.
1400  */
1401 
1402 /**
1403  * @defgroup GUICanvasWML GUICanvasWML
1404  *
1405  * A canvas is a blank drawing area on which the user can draw several shapes.
1406  * The drawing is done by adding WML structures to the canvas.
1407  *
1408  * @section PreCommit Pre-commit
1409  *
1410  * This section contains the pre commit functions.
1411  * These functions will be executed before the drawn canvas is applied on top of the normal background.
1412  * There should only be one pre commit section and its order regarding the other shapes doesn't matter.
1413  * The function has effect on the entire canvas, it's not possible to affect only a small part of the canvas.
1414  *
1415  * @subsection Blur Blur
1416  *
1417  * Blurs the background before applying the canvas. This doesn't make sense if the widget isn't semi-transparent.
1418  *
1419  * Keys:
1420  * Key |Type |Default |Description
1421  * -------------|------------------------------------|---------|-----------
1422  * depth | @ref guivartype_unsigned "unsigned"|0 |The depth to blur.
1423  */
1424 
1425 /**
1426  * @defgroup GUIWindowDefinitionWML GUIWindowDefinitionWML
1427  *
1428  * The window definition define how the windows shown in the dialog look.
1429  */
int wesnoth_main(int argc, char **argv)
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
Definition: manager.cpp:388
double t
Definition: astarsearch.cpp:63
Used in parsing config file.
Definition: validator.hpp:38
const std::string name_
Definition: validator.hpp:101
std::optional< std::string > preprocess_input_macros
Non-empty if –preprocess-input-macros was given on the command line.
bool simple_version
True if –simple-version was given on the command line.
bool report
True if –report was given on the command line.
std::optional< std::string > validate_wml
Non-empty if –validate was given on the command line.
bool strict_lua
True if –strict-lua was given in the commandline.
std::optional< std::string > preprocess_path
Path to parse that was given to the –preprocess option.
std::optional< std::string > userdata_dir
Non-empty if –userdata-dir was given on the command line.
std::optional< std::string > render_image
Image path to render.
std::optional< std::string > preprocess_output_macros
Non-empty if –preprocess-output-macros was given on the command line.
std::optional< std::string > logdomains
Non-empty if –logdomains was given on the command line.
std::optional< unsigned int > rng_seed
RNG seed specified by –rng-seed option.
bool userconfig_path
True if –userconfig-path was given on the command line.
bool nobanner
True if –nobanner was given on the command line.
bool preprocess
True if –preprocess was given on the command line.
std::string diff_left
Files for diffing or patching.
bool data_path
True if –data-path was given on the command line.
bool version
True if –version was given on the command line.
bool allow_insecure
True if –allow-insecure was given in the commandline.
std::optional< std::string > output_file
Output filename for WML diff or preprocessing.
bool noaddons
True if –noaddons was given on the command line.
std::optional< std::string > preprocess_target
Target (output) path that was given to the –preprocess option.
bool debug_lua
True if –debug-lua was given in the commandline.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line.
bool userdata_path
True if –userdata-path was given on the command line.
bool log_precise_timestamps
True if –log-precise was given on the command line.
std::optional< std::string > data_dir
Non-empty if –data-dir was given on the command line.
std::optional< std::vector< std::string > > preprocess_defines
Defines that were given to the –preprocess option.
std::optional< std::vector< std::pair< lg::severity, std::string > > > log
Contains parsed arguments of –log-* (e.g.
std::optional< std::string > usercache_dir
Non-empty if –usercache-dir was given on the command line.
std::optional< std::string > userconfig_dir
Non-empty if –userconfig-dir was given on the command line.
bool strict_validation
True if –strict-validation was given on the command line.
bool help
True if –help was given on the command line.
std::optional< std::string > validate_with
Non-empty if –use-schema was given on the command line.
bool usercache_path
True if –usercache-path was given on the command line.
std::optional< std::string > validate_schema
Non-empty if –validate-schema was given on the command line.
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
child_itors child_range(config_key_type key)
Definition: config.cpp:273
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1029
config get_diff(const config &c) const
A function to get the differences between this object, and 'c', as another config object.
Definition: config.cpp:913
@ NO_FORCE_RELOAD
Don't reload if the previous defines equal the new defines.
bool init_game_config(FORCE_RELOAD_CONFIG force_reload)
const game_config_view & game_config() const
optional_const_config optional_child(config_key_type key) const
bool play_multiplayer(mp_mode mode)
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(std::function< void()> f)
@ ok_button
Shows an ok button.
Definition: message.hpp:73
bool show(const unsigned auto_close_time=0)
Shows the window.
int get_retval() const
Returns the cached window exit code.
This class implements the title screen.
void play_slice()
Definition: context.cpp:96
std::vector< Reg > reg_vec
Definition: context.hpp:40
std::vector< aReg > areg_vec
Definition: context.hpp:41
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:51
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
Realization of serialization/validator.hpp abstract validator.
std::string str() const
Serializes the version number into string form.
Type that can be thrown as an exception to quit to desktop.
Definition: video.hpp:317
std::vector< std::string > read_argv([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
Declarations for File-IO.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:968
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
#define STREAMING_LOG
Definition: log.hpp:296
#define PLAIN_LOG
Definition: log.hpp:295
@ WAIT
Definition: cursor.hpp:28
@ NORMAL
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
std::string get_cache_dir()
Definition: filesystem.cpp:880
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_config_dir()
Definition: filesystem.cpp:841
std::string get_user_data_dir()
Definition: filesystem.cpp:870
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:318
std::string get_exe_dir()
Definition: filesystem.cpp:990
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
void set_user_config_dir(const std::string &newconfigdir)
Definition: filesystem.cpp:814
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
void set_cache_dir(const std::string &newcachedir)
Definition: filesystem.cpp:827
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:50
std::string get_logs_dir()
Definition: filesystem.cpp:875
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:51
std::string get_intl_dir()
std::string get_cwd()
Definition: filesystem.cpp:962
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void set_user_data_dir(std::string newprefdir)
Definition: filesystem.cpp:663
bool load_font_config()
Definition: font_config.cpp:79
std::string path
Definition: filesystem.cpp:83
std::string full_build_report()
Produce a bug report-style info dump.
Definition: build_info.cpp:663
std::string library_versions_report()
Produce a plain-text report of library versions suitable for stdout/stderr.
Definition: build_info.cpp:653
const version_info wesnoth_version(VERSION)
bool allow_insecure
Definition: game_config.cpp:74
std::string title_music
std::string build_arch()
Obtain the processor architecture for this build.
Definition: build_info.cpp:315
std::string optional_features_report()
Produce a plain-text report of features suitable for stdout/stderr.
Definition: build_info.cpp:658
const std::string revision
void set_debug(bool new_debug)
Definition: game_config.cpp:93
bool check_migration
Definition: filesystem.cpp:91
void init()
Initializes the GUI subsystems.
Definition: gui.cpp:36
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:150
retval
Default window/dialog return values.
Definition: retval.hpp:30
bool using_own_console()
Returns true if a console was allocated by the Wesnoth process.
severity
Definition: log.hpp:82
std::string list_log_domains(const std::string &filter)
Definition: log.cpp:375
bool broke_strict()
Definition: log.cpp:395
void set_log_to_file()
Do the initial redirection to a log file if the logs directory is writable.
Definition: log.cpp:229
void do_console_redirect()
Allocates a console if needed and redirects output to CONOUT.
void precise_timestamps(bool pt)
Definition: log.cpp:300
std::optional< bool > log_dir_writable()
Returns the result set by check_log_dir_writable().
Definition: log.cpp:275
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:342
void set_strict_severity(severity severity)
Definition: log.cpp:385
void empty_playlist()
Definition: sound.cpp:610
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:711
void stop_music()
Definition: sound.cpp:555
void bind_textdomain(const char *domain, const char *directory, const char *)
Definition: gettext.cpp:479
void set_default_textdomain(const char *domain)
Definition: gettext.cpp:487
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::map< std::string, t_string > string_map
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void preprocess_resource(const std::string &res_name, preproc_map *defines_map, bool write_cfg, bool write_plain_cfg, const std::string &parent_directory)
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
std::map< std::string, struct preproc_define > preproc_map
One of the realizations of serialization/validator.hpp abstract validator.
Contains a basic exception class for SDL operations.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:627
structure which will hide all current floating labels, and cause floating labels instantiated after i...
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
static preproc_map::value_type read_pair(const config &)
An error specifically indicating video subsystem problems.
Definition: video.hpp:310
Helper class, don't construct this directly.
static map_location::DIRECTION sw
bool strict_validation_enabled
Definition: validator.cpp:21
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
#define PACKAGE
Definition: wesconfig.h:23
#define LOG_CONFIG
Definition: wesnoth.cpp:108
static lg::log_domain log_preprocessor("preprocessor")
static void safe_exit(int res)
Definition: wesnoth.cpp:118
static std::string autodetect_game_data_dir(std::string exe_dir)
Try to autodetect the location of the game data dir.
Definition: wesnoth.cpp:864
static int handle_validate_command(const std::string &file, abstract_validator &validator, const std::vector< std::string > &defines)
Definition: wesnoth.cpp:243
int main(int argc, char **argv)
Definition: wesnoth.cpp:912
static void handle_preprocess_command(const commandline_options &cmdline_opts)
Definition: wesnoth.cpp:124
static int do_gameloop(const std::vector< std::string > &args)
Setups the game environment and enters the titlescreen or game loops.
Definition: wesnoth.cpp:620
static void check_fpu()
Definition: wesnoth.cpp:591
#define LOG_PREPROC
Definition: wesnoth.cpp:113
static void init_locale()
I would prefer to setup locale first so that early error messages can get localized,...
Definition: wesnoth.cpp:491
static void handle_lua_script_args(game_launcher *game, commandline_options &)
Handles the lua script command line arguments if present.
Definition: wesnoth.cpp:528
static int process_command_args(const commandline_options &cmdline_opts)
Process commandline-arguments.
Definition: wesnoth.cpp:271
#define error_exit(res)
Definition: wesnoth.cpp:905
static void warn_early_init_failure()
Print an alert and instructions to stderr about early initialization errors.
Definition: wesnoth.cpp:515
#define LOG_GENERAL
Definition: wesnoth.cpp:110
static lg::log_domain log_config("config")
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define e