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