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