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