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