The Battle for Wesnoth  1.19.8+dev
wesnoth.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "addon/manager.hpp"
17 #include "build_info.hpp"
18 #include "commandline_argv.hpp"
19 #include "commandline_options.hpp" // for commandline_options, etc
20 #include "config.hpp" // for config, config::error, etc
21 #include "cursor.hpp" // for set, CURSOR_TYPE::NORMAL, etc
22 #include "filesystem.hpp" // for filesystem::file_exists, filesystem::io_exception, etc
23 #include "floating_label.hpp"
24 #include "font/error.hpp" // for error
25 #include "font/font_config.hpp" // for load_font_config, etc
26 #include "formula/formula.hpp" // for formula_error
27 #include "game_config.hpp" // for path, debug, debug_lua, etc
28 #include "game_config_manager.hpp" // for game_config_manager, etc
29 #include "game_end_exceptions.hpp"
30 #include "game_launcher.hpp" // for game_launcher, etc
31 #include "gettext.hpp"
32 #include "gui/core/event/handler.hpp" // for tmanager
34 #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 "log.hpp" // for LOG_STREAM, general, logger, etc
43 #include "sdl/exception.hpp" // for exception
44 #include "serialization/binary_or_text.hpp" // for config_writer
45 #include "serialization/parser.hpp" // for read
46 #include "serialization/preprocessor.hpp" // for preproc_define, etc
47 #include "serialization/schema_validator.hpp" // for strict_validation_enabled and schema_validator
48 #include "sound.hpp" // for commit_music_changes, etc
49 #include "utils/optimer.hpp"
50 #include "formula/string_utils.hpp" // VGETTEXT
51 #include <functional>
52 #include "game_version.hpp" // for version_info
53 #include "video.hpp" // for video::error and video::quit
54 #include "wesconfig.h" // for PACKAGE
55 #include "widgets/button.hpp" // for button
56 #include "wml_exception.hpp" // for wml_exception
57 
59 #ifdef _WIN32
60 #include "log_windows.hpp"
61 
62 #include <float.h>
63 #endif // _WIN32
64 
65 #ifndef _MSC_VER
66 #include <fenv.h>
67 #endif // _MSC_VER
68 
69 #include <SDL2/SDL.h> // for SDL_Init, SDL_INIT_TIMER
70 
71 #include <boost/program_options/errors.hpp> // for error
72 #include <boost/algorithm/string/predicate.hpp> // for checking cmdline options
73 #include "utils/optional_fwd.hpp"
74 
75 #include <cerrno> // for ENOMEM
76 #include <clocale> // for setlocale, LC_ALL, etc
77 #include <cstdio> // for remove, fprintf, stderr
78 #include <cstdlib> // for srand, exit
79 #include <ctime> // for time, ctime, std::time_t
80 #include <exception> // for exception
81 #include <vector>
82 #include <iostream>
83 
84 //#define NO_CATCH_AT_GAME_END
85 
86 #ifdef _WIN32
87 
88 #ifdef INADDR_ANY
89 #undef INADDR_ANY
90 #endif
91 
92 #ifdef INADDR_BROADCAST
93 #undef INADDR_BROADCAST
94 #endif
95 
96 #ifdef INADDR_NONE
97 #undef INADDR_NONE
98 #endif
99 
100 #include <windows.h>
101 
102 #endif // _WIN32
103 
104 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
105 #include "gui/widgets/debug.hpp"
106 #endif
107 
108 // this file acts as a launcher for various gui2 dialogs
109 // so this reduces some boilerplate.
110 using namespace gui2::dialogs;
111 
112 static lg::log_domain log_config("config");
113 #define LOG_CONFIG LOG_STREAM(info, log_config)
114 
115 #define LOG_GENERAL LOG_STREAM(info, lg::general())
116 
117 static lg::log_domain log_preprocessor("preprocessor");
118 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
119 
120 // this is needed to allow identical functionality with clean refactoring
121 // play_game only returns on an error, all returns within play_game can
122 // be replaced with this
123 static void safe_exit(int res)
124 {
125  LOG_GENERAL << "exiting with code " << res;
126  exit(res);
127 }
128 
129 static void handle_preprocess_string(const commandline_options& cmdline_opts)
130 {
131  preproc_map defines_map;
132 
133  if(cmdline_opts.preprocess_input_macros) {
134  std::string file = *cmdline_opts.preprocess_input_macros;
135  if(!filesystem::file_exists(file)) {
136  PLAIN_LOG << "please specify an existing file. File " << file << " doesn't exist.";
137  return;
138  }
139 
140  PLAIN_LOG << "Reading cached defines from: " << file;
141 
142  config cfg;
143 
144  try {
146  read(cfg, *stream);
147  } catch(const config::error& e) {
148  PLAIN_LOG << "Caught a config error while parsing file '" << file << "':\n" << e.message;
149  }
150 
151  int read = 0;
152 
153  // use static preproc_define::read_pair(config) to make a object
154  for(const auto [_, cfg] : cfg.all_children_view()) {
155  const preproc_map::value_type def = preproc_define::read_pair(cfg);
156  defines_map[def.first] = def.second;
157  ++read;
158  }
159 
160  PLAIN_LOG << "Read " << read << " defines.";
161  }
162 
163  if(cmdline_opts.preprocess_defines) {
164  // add the specified defines
165  for(const std::string& define : *cmdline_opts.preprocess_defines) {
166  if(define.empty()) {
167  PLAIN_LOG << "empty define supplied";
168  continue;
169  }
170 
171  LOG_PREPROC << "adding define: " << define;
172  defines_map.emplace(define, preproc_define(define));
173  }
174  }
175 
176  // add the WESNOTH_VERSION define
177  defines_map["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
178 
179  // preprocess resource
180  PLAIN_LOG << "preprocessing specified string: " << *cmdline_opts.preprocess_source_string;
181  const utils::ms_optimer timer(
182  [](const auto& timer) { PLAIN_LOG << "preprocessing finished. Took " << timer << " ticks."; });
183  std::cout << preprocess_string(*cmdline_opts.preprocess_source_string, &defines_map) << std::endl;
184  PLAIN_LOG << "added " << defines_map.size() << " defines.";
185 }
186 
187 static void handle_preprocess_command(const commandline_options& cmdline_opts)
188 {
189  preproc_map input_macros;
190 
191  if(cmdline_opts.preprocess_input_macros) {
192  std::string file = *cmdline_opts.preprocess_input_macros;
193  if(filesystem::file_exists(file) == false) {
194  PLAIN_LOG << "please specify an existing file. File " << file << " doesn't exist.";
195  return;
196  }
197 
198  PLAIN_LOG << "Reading cached defines from: " << file;
199 
200  config cfg;
201 
202  try {
204  read(cfg, *stream);
205  } catch(const config::error& e) {
206  PLAIN_LOG << "Caught a config error while parsing file '" << file << "':\n" << e.message;
207  }
208 
209  int read = 0;
210 
211  // use static preproc_define::read_pair(config) to make a object
212  for(const auto [_, cfg] : cfg.all_children_view()) {
213  const preproc_map::value_type def = preproc_define::read_pair(cfg);
214  input_macros[def.first] = def.second;
215  ++read;
216  }
217 
218  PLAIN_LOG << "Read " << read << " defines.";
219  }
220 
221  const std::string resourceToProcess(*cmdline_opts.preprocess_path);
222  const std::string targetDir(*cmdline_opts.preprocess_target);
223 
224  const utils::ms_optimer timer(
225  [](const auto& timer) { PLAIN_LOG << "preprocessing finished. Took " << timer << " ticks."; });
226 
227  // If the users add the SKIP_CORE define we won't preprocess data/core
228  bool skipCore = false;
229  bool skipTerrainGFX = false;
230 
231  // The 'core_defines_map' is the one got from data/core macros
232  preproc_map defines_map(input_macros);
233 
234  if(cmdline_opts.preprocess_defines) {
235  // add the specified defines
236  for(const std::string& define : *cmdline_opts.preprocess_defines) {
237  if(define.empty()) {
238  PLAIN_LOG << "empty define supplied";
239  continue;
240  }
241 
242  LOG_PREPROC << "adding define: " << define;
243  defines_map.emplace(define, preproc_define(define));
244 
245  if(define == "SKIP_CORE") {
246  PLAIN_LOG << "'SKIP_CORE' defined.";
247  skipCore = true;
248  } else if(define == "NO_TERRAIN_GFX") {
249  PLAIN_LOG << "'NO_TERRAIN_GFX' defined.";
250  skipTerrainGFX = true;
251  }
252  }
253  }
254 
255  // add the WESNOTH_VERSION define
256  defines_map["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
257 
258  PLAIN_LOG << "added " << defines_map.size() << " defines.";
259 
260  // preprocess core macros first if we don't skip the core
261  if(skipCore == false) {
262  PLAIN_LOG << "preprocessing common macros from 'data/core' ...";
263 
264  // process each folder explicitly to gain speed
265  preprocess_resource(game_config::path + "/data/core/macros", &defines_map);
266 
267  if(skipTerrainGFX == false) {
268  preprocess_resource(game_config::path + "/data/core/terrain-graphics", &defines_map);
269  }
270 
271  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " 'data/core' defines.";
272  } else {
273  PLAIN_LOG << "skipped 'data/core'";
274  }
275 
276  // preprocess resource
277  PLAIN_LOG << "preprocessing specified resource: " << resourceToProcess << " ...";
278 
279  preprocess_resource(resourceToProcess, &defines_map, true, true, targetDir);
280  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " total defines.";
281 
282  if(cmdline_opts.preprocess_output_macros) {
283  std::string outputFileName = "_MACROS_.cfg";
284  if(!cmdline_opts.preprocess_output_macros->empty()) {
285  outputFileName = *cmdline_opts.preprocess_output_macros;
286  }
287 
288  std::string outputPath = targetDir + "/" + outputFileName;
289 
290  PLAIN_LOG << "writing '" << outputPath << "' with " << defines_map.size() << " defines.";
291 
293  if(!out->fail()) {
294  config_writer writer(*out, false);
295 
296  for(auto& define_pair : defines_map) {
297  define_pair.second.write(writer, define_pair.first);
298  }
299  } else {
300  PLAIN_LOG << "couldn't open the file.";
301  }
302  }
303 }
304 
305 static int handle_validate_command(const std::string& file, abstract_validator& validator, const std::vector<std::string>& defines) {
306  preproc_map defines_map;
307  // add the WESNOTH_VERSION define
308  defines_map["WESNOTH_VERSION"] = preproc_define(game_config::wesnoth_version.str());
309  defines_map["SCHEMA_VALIDATION"] = preproc_define();
310  for(const std::string& define : defines) {
311  if(define.empty()) {
312  PLAIN_LOG << "empty define supplied";
313  continue;
314  }
315 
316  LOG_PREPROC << "adding define: " << define;
317  defines_map.emplace(define, preproc_define(define));
318  }
319  PLAIN_LOG << "Validating " << file << " against schema " << validator.name_;
321  filesystem::scoped_istream stream = preprocess_file(file, &defines_map);
322  config result;
323  read(result, *stream, &validator);
324  if(lg::broke_strict()) {
325  std::cout << "validation failed\n";
326  } else {
327  std::cout << "validation succeeded\n";
328  }
329  return lg::broke_strict();
330 }
331 
332 /** Process commandline-arguments */
333 static int process_command_args(commandline_options& cmdline_opts)
334 {
335  // Options that don't change behavior based on any others should be checked alphabetically below.
336 
337  if(cmdline_opts.no_log_sanitize) {
338  lg::set_log_sanitize(false);
339  }
340 
341  if(cmdline_opts.usercache_dir) {
343  }
344 
345  if(cmdline_opts.userdata_dir) {
347  }
348 
349  // earliest possible point to ensure the userdata directory is known
351  filesystem::set_user_data_dir(std::string());
352  }
353 
354  // userdata is initialized, so initialize logging to file if enabled
355  // If true, output will be redirected to file, else output be written to console.
356  // On Windows, if Wesnoth was not started from a console, one will be allocated.
357  if(cmdline_opts.log_to_file
358  || (!cmdline_opts.no_log_to_file
359  && !getenv("WESNOTH_NO_LOG_FILE")
360  // command line options that imply not redirecting output to a log file
361  && !cmdline_opts.data_path
362  && !cmdline_opts.userdata_path
363  && !cmdline_opts.usercache_path
364  && !cmdline_opts.version
365  && !cmdline_opts.simple_version
366  && !cmdline_opts.logdomains
367  && !cmdline_opts.help
368  && !cmdline_opts.report
369  && !cmdline_opts.do_diff
370  && !cmdline_opts.do_patch
371  && !cmdline_opts.preprocess
372  && !cmdline_opts.render_image
373  && !cmdline_opts.screenshot
374  && !cmdline_opts.nogui
375  && !cmdline_opts.headless_unit_test
376  && !cmdline_opts.validate_schema
377  && !cmdline_opts.validate_wml
378  )
379  )
380  {
382  }
383 #ifdef _WIN32
384  // This forces a Windows console to be attached to the process even
385  // if Wesnoth is an IMAGE_SUBSYSTEM_WINDOWS_GUI executable because it
386  // turns Wesnoth into a CLI application. (unless --wnoconsole is given)
387  else if(!cmdline_opts.no_console) {
389  }
390 #endif
391 
392  if(cmdline_opts.log) {
393  for(const auto& log_pair : *cmdline_opts.log) {
394  const std::string log_domain = log_pair.second;
395  const lg::severity severity = log_pair.first;
396  if(!lg::set_log_domain_severity(log_domain, severity)) {
397  PLAIN_LOG << "unknown log domain: " << log_domain;
398  return 2;
399  }
400  }
401  }
402 
403  if(!cmdline_opts.nobanner) {
404  PLAIN_LOG << "Battle for Wesnoth v" << game_config::revision << " " << game_config::build_arch();
405  const std::time_t t = std::time(nullptr);
406  PLAIN_LOG << "Started on " << ctime(&t);
407  }
408 
409  if(cmdline_opts.usercache_path) {
410  std::cout << filesystem::get_cache_dir();
411  return 0;
412  }
413 
414  if(cmdline_opts.userdata_path) {
415  std::cout << filesystem::get_user_data_dir();
416  return 0;
417  }
418 
419  if(cmdline_opts.data_dir) {
420  game_config::path = filesystem::normalize_path(*cmdline_opts.data_dir, true, true);
421  if(!cmdline_opts.nobanner) {
422  PLAIN_LOG << "Overriding data directory with '" << game_config::path << "'";
423  }
424  } else {
425  // if a pre-defined path does not exist this will empty it
427  if(game_config::path.empty()) {
428  if(std::string exe_dir = filesystem::get_exe_dir(); !exe_dir.empty()) {
429  if(std::string auto_dir = filesystem::autodetect_game_data_dir(std::move(exe_dir)); !auto_dir.empty()) {
430  if(!cmdline_opts.nobanner) {
431  PLAIN_LOG << "Automatically found a possible data directory at: " << auto_dir;
432  }
433  game_config::path = filesystem::normalize_path(auto_dir, true, true);
434  }
435  } else {
436  PLAIN_LOG << "Cannot find game data directory. Specify one with --data-dir";
437  return 1;
438  }
439  }
440  }
441 
443  PLAIN_LOG << "Could not find game data directory '" << game_config::path << "'";
444  return 1;
445  }
446 
447  if(cmdline_opts.data_path) {
448  std::cout << game_config::path;
449  return 0;
450  }
451 
452  if(cmdline_opts.debug_lua) {
453  game_config::debug_lua = true;
454  }
455 
456  if(cmdline_opts.allow_insecure) {
458  }
459 
460  if(cmdline_opts.addon_server_info) {
462  }
463 
464  if(cmdline_opts.strict_lua) {
466  }
467 
468  if(cmdline_opts.help) {
469  std::cout << cmdline_opts;
470  return 0;
471  }
472 
473  if(cmdline_opts.logdomains) {
474  std::cout << lg::list_log_domains(*cmdline_opts.logdomains);
475  return 0;
476  }
477 
478  if(cmdline_opts.log_precise_timestamps) {
480  }
481 
482  if(cmdline_opts.rng_seed) {
483  srand(*cmdline_opts.rng_seed);
484  }
485 
486  if(cmdline_opts.render_image) {
487  SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
488  }
489 
490  if(cmdline_opts.strict_validation) {
492  }
493 
494  if(cmdline_opts.version) {
495  std::cout << "Battle for Wesnoth" << " " << game_config::wesnoth_version.str() << "\n\n";
496  std::cout << "Library versions:\n" << game_config::library_versions_report() << '\n';
497  std::cout << "Optional features:\n" << game_config::optional_features_report();
498 
499  return 0;
500  }
501 
502  if(cmdline_opts.simple_version) {
503  std::cout << game_config::wesnoth_version.str() << "\n";
504 
505  return 0;
506  }
507 
508  if(cmdline_opts.report) {
509  std::cout << "\n========= BUILD INFORMATION =========\n\n" << game_config::full_build_report();
510  return 0;
511  }
512 
513  if(cmdline_opts.validate_schema) {
515  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
516  return handle_validate_command(*cmdline_opts.validate_schema, validator, {});
517  }
518 
519  if(cmdline_opts.do_diff) {
520  config left, right;
521  std::ifstream in_left(cmdline_opts.diff_left);
522  std::ifstream in_right(cmdline_opts.diff_right);
523  read(left, in_left);
524  read(right, in_right);
525  std::ostream* os = &std::cout;
526  if(cmdline_opts.output_file) {
527  os = new std::ofstream(*cmdline_opts.output_file);
528  }
530  out.write(right.get_diff(left));
531  if(os != &std::cout) delete os;
532  return 0;
533  }
534 
535  if(cmdline_opts.do_patch) {
536  config base, diff;
537  std::ifstream in_base(cmdline_opts.diff_left);
538  std::ifstream in_diff(cmdline_opts.diff_right);
539  read(base, in_base);
540  read(diff, in_diff);
541  base.apply_diff(diff);
542  std::ostream* os = &std::cout;
543  if(cmdline_opts.output_file) {
544  os = new std::ofstream(*cmdline_opts.output_file);
545  }
547  out.write(base);
548  if(os != &std::cout) delete os;
549  return 0;
550  }
551 
552  if(cmdline_opts.generate_spritesheet) {
553  PLAIN_LOG << "sheet path " << *cmdline_opts.generate_spritesheet;
555  return 0;
556  }
557 
558  // Options changing their behavior dependent on some others should be checked below.
559 
560  if(cmdline_opts.preprocess) {
561  handle_preprocess_command(cmdline_opts);
562  return 0;
563  }
564 
565  if(cmdline_opts.preprocess_source_string.has_value()) {
566  handle_preprocess_string(cmdline_opts);
567  return 0;
568  }
569 
570  if(cmdline_opts.validate_wml) {
571  std::string schema_path;
572  if(cmdline_opts.validate_with) {
573  schema_path = *cmdline_opts.validate_with;
574  if(!filesystem::file_exists(schema_path)) {
575  if(auto check = filesystem::get_wml_location(schema_path)) {
576  schema_path = check.value();
577  } else {
578  PLAIN_LOG << "Could not find schema file: " << schema_path;
579  }
580  } else {
581  schema_path = filesystem::normalize_path(schema_path);
582  }
583  } else {
584  schema_path = filesystem::get_wml_location("schema/game_config.cfg").value();
585  }
587  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
588  return handle_validate_command(*cmdline_opts.validate_wml, validator,
589  cmdline_opts.preprocess_defines.value_or<decltype(cmdline_opts.preprocess_defines)::value_type>({}));
590  }
591 
592  if(cmdline_opts.preprocess_defines || cmdline_opts.preprocess_input_macros || cmdline_opts.preprocess_path) {
593  // It would be good if this was supported for running tests too, possibly for other uses.
594  // For the moment show an error message instead of leaving the user wondering why it doesn't work.
595  PLAIN_LOG << "That --preprocess-* option is only supported when using --preprocess or --validate.";
596  return 2;
597  }
598 
599  // Not the most intuitive solution, but I wanted to leave current semantics for now
600  return -1;
601 }
602 
603 /**
604  * I would prefer to setup locale first so that early error
605  * messages can get localized, but we need the game_launcher
606  * initialized to have filesystem::get_intl_dir() to work. Note: setlocale()
607  * does not take GUI language setting into account.
608  */
609 static void init_locale()
610 {
611 #if defined _WIN32 || defined __APPLE__
612  setlocale(LC_ALL, "English");
613 #else
614  std::setlocale(LC_ALL, "C");
615 #endif
616 
617  const std::string& intl_dir = filesystem::get_intl_dir();
618 
619  translation::bind_textdomain(PACKAGE, intl_dir.c_str(), "UTF-8");
620  translation::bind_textdomain(PACKAGE "-lib", intl_dir.c_str(), "UTF-8");
622 }
623 
624 /**
625  * Print an alert and instructions to stderr about early initialization errors.
626  *
627  * This is provided as an aid for users dealing with potential data dir
628  * configuration issues. The first code to read core WML *has* the
629  * responsibility to call this function in the event of a problem, to inform
630  * the user of the most likely possible cause and suggest a course of action
631  * to solve the issue.
632  */
634 {
635  // NOTE: wrap output to 80 columns.
636  PLAIN_LOG << '\n'
637  << "An error at this point during initialization usually indicates that the data\n"
638  << "directory above was not correctly set or detected. Try passing the correct path\n"
639  << "in the command line with the --data-dir switch or as the only argument.";
640 }
641 
642 /**
643  * Handles the lua script command line arguments if present.
644  * This function will only run once.
645  */
647 {
648  static bool first_time = true;
649 
650  if(!first_time) {
651  return;
652  }
653 
654  first_time = false;
655 
656  if(!game->init_lua_script()) {
657  // PLAIN_LOG << "error when loading lua scripts at startup";
658  // PLAIN_LOG << "could not load lua script: " << *cmdline_opts.script_file;
659  }
660 }
661 
662 #ifdef _MSC_VER
663 static void check_fpu()
664 {
665  uint32_t f_control;
666 
667  if(_controlfp_s(&f_control, 0, 0) == 0) {
668  uint32_t unused;
669  uint32_t rounding_mode = f_control & _MCW_RC;
670 
671  if(rounding_mode != _RC_NEAR) {
672  PLAIN_LOG << "Floating point rounding mode is currently '"
673  << ((rounding_mode == _RC_CHOP)
674  ? "chop"
675  : (rounding_mode == _RC_UP)
676  ? "up"
677  : (rounding_mode == _RC_DOWN)
678  ? "down"
679  : (rounding_mode == _RC_NEAR) ? "near" : "unknown")
680  << "' setting to 'near'";
681 
682  if(_controlfp_s(&unused, _RC_NEAR, _MCW_RC)) {
683  PLAIN_LOG << "failed to set floating point rounding type to 'near'";
684  }
685  }
686 
687 #ifndef _M_AMD64
688  uint32_t precision_mode = f_control & _MCW_PC;
689  if(precision_mode != _PC_53) {
690  PLAIN_LOG << "Floating point precision mode is currently '"
691  << ((precision_mode == _PC_53)
692  ? "double"
693  : (precision_mode == _PC_24)
694  ? "single"
695  : (precision_mode == _PC_64) ? "double extended" : "unknown")
696  << "' setting to 'double'";
697 
698  if(_controlfp_s(&unused, _PC_53, _MCW_PC)) {
699  PLAIN_LOG << "failed to set floating point precision type to 'double'";
700  }
701  }
702 #endif
703 
704  } else {
705  PLAIN_LOG << "_controlfp_s failed.";
706  }
707 }
708 #else
709 static void check_fpu()
710 {
711  switch(fegetround()) {
712  case FE_TONEAREST:
713  break;
714  case FE_DOWNWARD:
715  STREAMING_LOG << "Floating point precision mode is currently 'downward'";
716  goto reset_fpu;
717  case FE_TOWARDZERO:
718  STREAMING_LOG << "Floating point precision mode is currently 'toward-zero'";
719  goto reset_fpu;
720  case FE_UPWARD:
721  STREAMING_LOG << "Floating point precision mode is currently 'upward'";
722  goto reset_fpu;
723  default:
724  STREAMING_LOG << "Floating point precision mode is currently 'unknown'";
725  goto reset_fpu;
726  reset_fpu:
727  STREAMING_LOG << " - setting to 'nearest'\n";
728  fesetround(FE_TONEAREST);
729  break;
730  }
731 }
732 #endif
733 
734 /**
735  * Setups the game environment and enters
736  * the titlescreen or game loops.
737  */
738 static int do_gameloop(commandline_options& cmdline_opts)
739 {
740  srand(std::time(nullptr));
741 
742  const auto game = std::make_unique<game_launcher>(cmdline_opts);
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  PLAIN_LOG << "could not initialize fonts";
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  PLAIN_LOG << "could not initialize the language";
762  return 1;
763  }
764 
765  res = game->init_video();
766  if(res == false) {
767  PLAIN_LOG << "could not initialize display";
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  gui2::switch_theme(prefs::get().gui2_theme());
781  const gui2::event::manager gui_event_manager;
782 
783  // if the log directory is not writable, then this is the error condition so show the error message.
784  // if the log directory is writable, then there's no issue.
785  // if the optional isn't set, then logging to file has been disabled, so there's no issue.
786  if(!lg::log_dir_writable().value_or(true)) {
787  utils::string_map symbols;
788  symbols["logdir"] = filesystem::get_logs_dir();
789  std::string msg = VGETTEXT("Unable to create log files in directory $logdir. This is often caused by incorrect folder permissions, anti-virus software restricting folder access, or using OneDrive to manage your My Documents folder.", symbols);
790  gui2::show_message(_("Logging Failure"), msg, message::ok_button);
791  }
792 
793  game_config_manager config_manager(cmdline_opts);
794 
798  }
799 
800  loading_screen::display([&res, &config_manager, &cmdline_opts]() {
803 
804  if(res == false) {
805  PLAIN_LOG << "could not initialize game config";
806  return;
807  }
808 
810 
811  res = font::load_font_config();
812  if(res == false) {
813  PLAIN_LOG << "could not re-initialize fonts for the current language";
814  return;
815  }
816 
817  if(!game_config::no_addons && !cmdline_opts.noaddons) {
819 
821  }
822  });
823 
824  if(res == false) {
825  return 1;
826  }
827 
828  plugins_manager plugins_man(new application_lua_kernel);
829 
830  const plugins_context::reg_vec callbacks {
831  {"play_multiplayer", std::bind(&game_launcher::play_multiplayer, game.get(), game_launcher::mp_mode::CONNECT)},
832  {"play_local", std::bind(&game_launcher::play_multiplayer, game.get(), game_launcher::mp_mode::LOCAL)},
833  {"play_campaign", std::bind(&game_launcher::play_campaign, game.get())},
834  };
835 
836  const plugins_context::areg_vec accessors {
837  {"command_line", std::bind(&commandline_options::to_config, &cmdline_opts)},
838  };
839 
840  plugins_context plugins("titlescreen", callbacks, accessors);
841 
842  plugins.set_callback("exit", [](const config& cfg) { safe_exit(cfg["code"].to_int(0)); }, false);
843 
844  while(true) {
845  if(!game->has_load_data()) {
846  auto cfg = config_manager.game_config().optional_child("titlescreen_music");
847  if(cfg) {
848  for(const config& i : cfg->child_range("music")) {
850  }
851 
852  config title_music_config;
853  title_music_config["name"] = game_config::title_music;
854  title_music_config["append"] = true;
855  title_music_config["immediate"] = true;
856  sound::play_music_config(title_music_config);
857  } else {
860  }
861  }
862 
863  handle_lua_script_args(&*game, cmdline_opts);
864 
865  plugins.play_slice();
866  plugins.play_slice();
867 
868  if(!cmdline_opts.unit_test.empty()) {
869  return static_cast<int>(game->unit_test());
870  }
871 
872  if(game->play_test() == false) {
873  return 0;
874  }
875 
876  if(game->play_screenshot_mode() == false) {
877  return 0;
878  }
879 
880  if(game->play_render_image_mode() == false) {
881  return 0;
882  }
883 
884  // Start directly a campaign
885  if(game->goto_campaign() == false) {
886  if(game->jump_to_campaign_id().empty())
887  continue; // Go to main menu
888  else
889  return 1; // we got an error starting the campaign from command line
890  }
891 
892  // Start directly a multiplayer
893  // Eventually with a specified server
894  if(game->goto_multiplayer() == false) {
895  continue; // Go to main menu
896  }
897 
898  // Start directly a commandline multiplayer game
899  if(game->play_multiplayer_commandline() == false) {
900  return 0;
901  }
902 
903  if(game->goto_editor() == false) {
904  return 0;
905  }
906 
907  const font::floating_label_context label_manager;
908 
910 
911  // If loading a game, skip the titlescreen entirely
912  if(game->has_load_data() && game->load_game()) {
914  continue;
915  }
916 
917  int retval;
918  { // scope to not keep the title screen alive all game
919  title_screen dlg(*game);
920 
921  // Allows re-layout on resize.
922  // Since RELOAD_UI is not checked here, it causes
923  // the dialog to be closed and reshown with changes.
925  dlg.show();
926  }
927  retval = dlg.get_retval();
928  }
929 
930  switch(retval) {
932  LOG_GENERAL << "quitting game...";
933  return 0;
935  game->play_multiplayer(game_launcher::mp_mode::CONNECT);
936  break;
938  game->play_multiplayer(game_launcher::mp_mode::HOST);
939  break;
941  game->play_multiplayer(game_launcher::mp_mode::LOCAL);
942  break;
944  loading_screen::display([&config_manager]() {
945  config_manager.reload_changed_game_config();
946  gui2::init();
947  gui2::switch_theme(prefs::get().gui2_theme());
948  });
949  break;
951  game->start_editor();
952  break;
955  break;
957  break;
959  gui2::switch_theme(prefs::get().gui2_theme());
960  break;
961  }
962  }
963 }
964 
965 #ifdef _WIN32
966 #define error_exit(res) \
967  do { \
968  if(lg::using_own_console()) { \
969  std::cerr << "Press enter to continue..." << std::endl; \
970  std::cin.get(); \
971  } \
972  return res; \
973  } while(false)
974 #else
975 #define error_exit(res) return res
976 #endif
977 
978 #ifdef __APPLE__
979 extern "C" int wesnoth_main(int argc, char** argv);
980 int wesnoth_main(int argc, char** argv)
981 #else
982 int main(int argc, char** argv)
983 #endif
984 {
985  auto args = read_argv(argc, argv);
986  assert(!args.empty());
987 
988 #ifdef _WIN32
989  _putenv("PANGOCAIRO_BACKEND=fontconfig");
990  _putenv("FONTCONFIG_PATH=fonts");
991 #endif
992 #ifdef __APPLE__
993  // Using setenv with overwrite disabled so we can override this in the
994  // original process environment for research/testing purposes.
995  setenv("PANGOCAIRO_BACKEND", "fontconfig", 0);
996 #endif
997 
998  try {
999  commandline_options cmdline_opts = commandline_options(args);
1000  int finished = process_command_args(cmdline_opts);
1001 
1002  if(finished != -1) {
1003 #ifdef _WIN32
1004  if(lg::using_own_console()) {
1005  std::cerr << "Press enter to continue..." << std::endl;
1006  std::cin.get();
1007  }
1008 #endif
1009  safe_exit(finished);
1010  }
1011 
1012  SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
1013  // Is there a reason not to just use SDL_INIT_EVERYTHING?
1014  if(SDL_Init(SDL_INIT_TIMER) < 0) {
1015  PLAIN_LOG << "Couldn't initialize SDL: " << SDL_GetError();
1016  return (1);
1017  }
1018  atexit(SDL_Quit);
1019 
1020  // Mac's touchpad generates touch events too.
1021  // Ignore them until Macs have a touchscreen: https://forums.libsdl.org/viewtopic.php?p=45758
1022 #if defined(__APPLE__) && !defined(__IPHONEOS__)
1023  SDL_EventState(SDL_FINGERMOTION, SDL_DISABLE);
1024  SDL_EventState(SDL_FINGERDOWN, SDL_DISABLE);
1025  SDL_EventState(SDL_FINGERUP, SDL_DISABLE);
1026 #endif
1027 
1028  // declare this here so that it will always be at the front of the event queue.
1029  events::event_context global_context;
1030 
1031  SDL_StartTextInput();
1032 
1033  const int res = do_gameloop(cmdline_opts);
1034  safe_exit(res);
1035  } catch(const boost::program_options::error& e) {
1036  // logging hasn't been initialized by this point
1037  std::cerr << "Error in command line: " << e.what() << std::endl;
1038  std::string error = "Error parsing command line arguments: ";
1039  error += e.what();
1040  SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", error.c_str(), nullptr);
1041  error_exit(2);
1042  } catch(const video::error& e) {
1043  PLAIN_LOG << "Video system error: " << e.what();
1044  error_exit(1);
1045  } catch(const font::error& e) {
1046  PLAIN_LOG << "Could not initialize fonts.\n\n" << e.what() << "\n\nExiting.";
1047  error_exit(1);
1048  } catch(const config::error& e) {
1049  PLAIN_LOG << e.message;
1050  error_exit(1);
1051  } catch(const video::quit&) {
1052  // just means the game should quit
1053  } catch(const return_to_play_side_exception&) {
1054  PLAIN_LOG << "caught return_to_play_side_exception, please report this bug (quitting)";
1055  } catch(const quit_game_exception&) {
1056  PLAIN_LOG << "caught quit_game_exception (quitting)";
1057  } catch(const wml_exception& e) {
1058  PLAIN_LOG << "WML exception:\nUser message: " << e.user_message << "\nDev message: " << e.dev_message;
1059  error_exit(1);
1060  } catch(const wfl::formula_error& e) {
1061  PLAIN_LOG << e.what() << "\n\nGame will be aborted.";
1062  error_exit(1);
1063  } catch(const sdl::exception& e) {
1064  PLAIN_LOG << e.what();
1065  error_exit(1);
1066  } catch(const game::error& e) {
1067  PLAIN_LOG << "Game error: " << e.what();
1068  error_exit(1);
1069  } catch(const std::bad_alloc&) {
1070  PLAIN_LOG << "Ran out of memory. Aborted.";
1071  error_exit(ENOMEM);
1072 #if !defined(NO_CATCH_AT_GAME_END)
1073  } catch(const std::exception& e) {
1074  // Try to catch unexpected exceptions.
1075  PLAIN_LOG << "Caught general '" << typeid(e).name() << "' exception:\n" << e.what();
1076  error_exit(1);
1077  } catch(const std::string& e) {
1078  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1079  error_exit(1);
1080  } catch(const char* e) {
1081  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1082  error_exit(1);
1083  } catch(...) {
1084  // Ensure that even when we terminate with `throw 42`, the exception
1085  // is caught and all destructors are actually called. (Apparently,
1086  // some compilers will simply terminate without calling destructors if
1087  // the exception isn't caught.)
1088  PLAIN_LOG << "Caught general exception " << utils::get_unknown_exception_type() << ". Terminating.";
1089  error_exit(1);
1090 #endif
1091  }
1092 
1093  return 0;
1094 } // end main
int wesnoth_main(int argc, char **argv)
void refresh_addon_version_info_cache()
Refreshes the per-session cache of add-on's version information structs.
Definition: manager.cpp:387
double t
Definition: astarsearch.cpp:63
Used in parsing config file.
Definition: validator.hpp:38
bool nogui
True if –nogui was given on the command line.
utils::optional< std::string > validate_wml
Non-empty if –validate was given on the command line.
bool simple_version
True if –simple-version was given on the command line.
bool report
True if –report was given on the command line.
bool headless_unit_test
True if –unit is used and –showgui is not present.
bool no_log_sanitize
True if –no-log-sanitize was given on the command line.
bool strict_lua
True if –strict-lua was given in the commandline.
utils::optional< std::vector< std::string > > preprocess_defines
Defines that were given to the –preprocess option.
utils::optional< std::string > usercache_dir
Non-empty if –usercache-dir was given on the command line.
utils::optional< std::string > validate_schema
Non-empty if –validate-schema was given on the command line.
utils::optional< std::string > userdata_dir
Non-empty if –userdata-dir was given on the command line.
bool nobanner
True if –nobanner was given on the command line.
utils::optional< std::vector< std::pair< lg::severity, std::string > > > log
Contains parsed arguments of –log-* (e.g.
utils::optional< std::string > generate_spritesheet
Path of which to generate a spritesheet.
utils::optional< std::string > render_image
Image path to render.
utils::optional< std::string > logdomains
Non-empty if –logdomains was given on the command line.
utils::optional< std::string > preprocess_input_macros
Non-empty if –preprocess-input-macros was given on the command line.
bool preprocess
True if –preprocess was given on the command line.
utils::optional< unsigned int > rng_seed
RNG seed specified by –rng-seed option.
std::string diff_left
Files for diffing or patching.
bool data_path
True if –data-path was given on the command line.
bool version
True if –version was given on the command line.
bool allow_insecure
True if –allow-insecure was given in the commandline.
utils::optional< std::string > validate_with
Non-empty if –use-schema was given on the command line.
bool noaddons
True if –noaddons was given on the command line.
utils::optional< std::string > output_file
Output filename for WML diff or preprocessing.
utils::optional< std::string > data_dir
Non-empty if –data-dir was given on the command line.
utils::optional< std::string > preprocess_target
Target (output) path that was given to the –preprocess option.
bool screenshot
True if –screenshot was given on the command line.
bool log_to_file
True if –log-to-file was given on the command line.
bool debug_lua
True if –debug-lua was given in the commandline.
std::vector< std::string > unit_test
Non-empty if –unit was given on the command line.
bool userdata_path
True if –userdata-path was given on the command line.
bool log_precise_timestamps
True if –log-precise was given on the command line.
utils::optional< std::string > preprocess_source_string
String to preprocess.
bool no_log_to_file
True if –no-log-to-file was given on the command line.
utils::optional< std::string > preprocess_output_macros
Non-empty if –preprocess-output-macros was given on the command line.
bool strict_validation
True if –strict-validation was given on the command line.
utils::optional< std::string > preprocess_path
Path to parse that was given to the –preprocess option.
bool help
True if –help was given on the command line.
bool usercache_path
True if –usercache-path was given on the command line.
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:796
child_itors child_range(config_key_type key)
Definition: config.cpp:272
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1026
config get_diff(const config &c) const
A function to get the differences between this object, and 'c', as another config object.
Definition: config.cpp:910
@ NO_FORCE_RELOAD
Don't reload if the previous defines equal the new defines.
bool init_game_config(FORCE_RELOAD_CONFIG force_reload)
const game_config_view & game_config() const
optional_const_config optional_child(config_key_type key) const
bool play_multiplayer(mp_mode mode)
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void display(const std::function< void()> &f)
@ ok_button
Shows an ok button.
Definition: message.hpp:73
bool show(const unsigned auto_close_time=0)
Shows the window.
This class implements the title screen.
int get_retval()
Definition: window.hpp:402
void play_slice()
Definition: context.cpp:103
std::vector< Reg > reg_vec
Definition: context.hpp:40
std::vector< aReg > areg_vec
Definition: context.hpp:41
void set_callback(const std::string &name, callback_function)
Definition: context.cpp:53
static prefs & get()
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
Realization of serialization/validator.hpp abstract validator.
std::string str() const
Serializes the version number into string form.
Type that can be thrown as an exception to quit to desktop.
Definition: video.hpp:323
std::vector< std::string > read_argv([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
Definitions for the interface to Wesnoth Markup Language (WML).
Declarations for File-IO.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1029
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
#define STREAMING_LOG
Definition: log.hpp:298
#define PLAIN_LOG
Definition: log.hpp:297
@ WAIT
Definition: cursor.hpp:28
@ NORMAL
Definition: cursor.hpp:28
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
std::string get_cache_dir()
Definition: filesystem.cpp:865
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_data_dir()
Definition: filesystem.cpp:855
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:327
std::string get_exe_dir()
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir)
Returns a translated path to the actual file or directory, if it exists.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::string autodetect_game_data_dir(std::string exe_dir)
Try to autodetect the location of the game data dir.
void set_cache_dir(const std::string &newcachedir)
Definition: filesystem.cpp:818
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:53
std::string get_logs_dir()
Definition: filesystem.cpp:860
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:54
bool is_userdata_initialized()
Definition: filesystem.cpp:610
std::string get_intl_dir()
std::string normalize_path(const std::string &fpath, bool normalize_separators, bool resolve_dot_entries)
Returns the absolute path of a file.
void set_user_data_dir(std::string newprefdir)
Definition: filesystem.cpp:722
bool load_font_config()
Definition: font_config.cpp:58
std::string path
Definition: filesystem.cpp:92
bool addon_server_info
Definition: game_config.cpp:79
std::string full_build_report()
Produce a bug report-style info dump.
Definition: build_info.cpp:666
std::string library_versions_report()
Produce a plain-text report of library versions suitable for stdout/stderr.
Definition: build_info.cpp:656
const version_info wesnoth_version(VERSION)
bool allow_insecure
Definition: game_config.cpp:78
std::string title_music
std::string build_arch()
Obtain the processor architecture for this build.
Definition: build_info.cpp:316
std::string optional_features_report()
Produce a plain-text report of features suitable for stdout/stderr.
Definition: build_info.cpp:661
const std::string revision
bool check_migration
Definition: filesystem.cpp:100
void init()
Initializes the GUI subsystems.
Definition: gui.cpp:34
void switch_theme(const std::string &current_theme)
Set and activate the given gui2 theme.
Definition: gui.cpp:135
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
retval
Default window/dialog return values.
Definition: retval.hpp:30
void build_spritesheet_from(const std::string &entry_point)
bool using_own_console()
Returns true if a console was allocated by the Wesnoth process.
severity
Definition: log.hpp:83
std::string list_log_domains(const std::string &filter)
Definition: log.cpp:380
bool broke_strict()
Definition: log.cpp:400
void set_log_to_file()
Do the initial redirection to a log file if the logs directory is writable.
Definition: log.cpp:234
void set_log_sanitize(bool sanitize)
toggle log sanitization
Definition: log.cpp:404
utils::optional< bool > log_dir_writable()
Returns the result set by check_log_dir_writable().
Definition: log.cpp:280
void do_console_redirect()
Allocates a console if needed and redirects output to CONOUT.
void precise_timestamps(bool pt)
Definition: log.cpp:305
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:347
void set_strict_severity(severity severity)
Definition: log.cpp:390
void empty_playlist()
Definition: sound.cpp:613
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:716
void stop_music()
Definition: sound.cpp:558
void bind_textdomain(const char *domain, const char *directory, const char *)
Definition: gettext.cpp:479
void set_default_textdomain(const char *domain)
Definition: gettext.cpp:487
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::map< std::string, t_string > string_map
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::string preprocess_string(const std::string &contents, preproc_map *defines, const std::string &textdomain)
Function to use the WML preprocessor on a string.
void preprocess_resource(const std::string &res_name, preproc_map *defines_map, bool write_cfg, bool write_plain_cfg, const std::string &parent_directory)
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map *defines)
Function to use the WML preprocessor on a file.
std::map< std::string, struct preproc_define > preproc_map
One of the realizations of serialization/validator.hpp abstract validator.
Contains a basic exception class for SDL operations.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:629
structure which will hide all current floating labels, and cause floating labels instantiated after i...
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:29
static preproc_map::value_type read_pair(const config &)
Reports time elapsed at the end of an object scope.
Definition: optimer.hpp:37
An error specifically indicating video subsystem problems.
Definition: video.hpp:316
Helper class, don't construct this directly.
bool strict_validation_enabled
Definition: validator.cpp:21
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
#define PACKAGE
Definition: wesconfig.h:23
static lg::log_domain log_preprocessor("preprocessor")
static void handle_preprocess_string(const commandline_options &cmdline_opts)
Definition: wesnoth.cpp:129
static void safe_exit(int res)
Definition: wesnoth.cpp:123
static int do_gameloop(commandline_options &cmdline_opts)
Setups the game environment and enters the titlescreen or game loops.
Definition: wesnoth.cpp:738
static int handle_validate_command(const std::string &file, abstract_validator &validator, const std::vector< std::string > &defines)
Definition: wesnoth.cpp:305
int main(int argc, char **argv)
Definition: wesnoth.cpp:982
static int process_command_args(commandline_options &cmdline_opts)
Process commandline-arguments.
Definition: wesnoth.cpp:333
static void handle_preprocess_command(const commandline_options &cmdline_opts)
Definition: wesnoth.cpp:187
static void check_fpu()
Definition: wesnoth.cpp:709
#define LOG_PREPROC
Definition: wesnoth.cpp:118
static void init_locale()
I would prefer to setup locale first so that early error messages can get localized,...
Definition: wesnoth.cpp:609
static void handle_lua_script_args(game_launcher *game, commandline_options &)
Handles the lua script command line arguments if present.
Definition: wesnoth.cpp:646
#define error_exit(res)
Definition: wesnoth.cpp:975
static void warn_early_init_failure()
Print an alert and instructions to stderr about early initialization errors.
Definition: wesnoth.cpp:633
#define LOG_GENERAL
Definition: wesnoth.cpp:115
static lg::log_domain log_config("config")
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define e