The Battle for Wesnoth  1.19.15+dev
wesnoth.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
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 "events.hpp"
23 #include "filesystem.hpp" // for filesystem::file_exists, filesystem::io_exception, etc
24 #include "floating_label.hpp"
25 #include "font/error.hpp" // for error
26 #include "font/font_config.hpp" // for load_font_config, etc
27 #include "formula/formula.hpp" // for formula_error
28 #include "game_config.hpp" // for path, debug, debug_lua, etc
29 #include "game_config_manager.hpp" // for game_config_manager, etc
30 #include "game_end_exceptions.hpp"
31 #include "game_launcher.hpp" // for game_launcher, etc
32 #include "gettext.hpp"
33 #include "gui/core/event/handler.hpp" // for tmanager
35 #include "gui/dialogs/message.hpp" // for show_error_message
37 #include "gui/dialogs/title_screen.hpp" // for title_screen, etc
38 #include "gui/gui.hpp" // for init
39 #include "log.hpp" // for LOG_STREAM, general, logger, etc
44 #include "sdl/exception.hpp" // for exception
45 #include "serialization/binary_or_text.hpp" // for config_writer
46 #include "serialization/chrono.hpp"
47 #include "serialization/parser.hpp" // for read
48 #include "serialization/preprocessor.hpp" // for preproc_define, etc
49 #include "serialization/schema_validator.hpp" // for strict_validation_enabled and schema_validator
50 #include "sound.hpp" // for commit_music_changes, etc
51 #include "utils/optimer.hpp"
52 #include "formula/string_utils.hpp" // VGETTEXT
53 #include <functional>
54 #include "game_version.hpp" // for version_info
55 #include "video.hpp" // for video::error and video::quit
56 #include "wesconfig.h" // for PACKAGE
57 #include "wml_exception.hpp" // for wml_exception
58 
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/program_options/errors.hpp> // for error
73 #include <boost/algorithm/string/predicate.hpp> // for checking cmdline options
74 #include "utils/optional_fwd.hpp"
75 
76 #include <cerrno> // for ENOMEM
77 #include <clocale> // for setlocale, LC_ALL, etc
78 #include <cstdio> // for remove, fprintf, stderr
79 #include <cstdlib> // for srand, exit
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 __ANDROID__
105 #define main SDL_main
106 #endif
107 
108 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
109 #include "gui/widgets/debug.hpp"
110 #endif
111 
112 // this file acts as a launcher for various gui2 dialogs
113 // so this reduces some boilerplate.
114 using namespace gui2::dialogs;
115 
116 static lg::log_domain log_config("config");
117 #define LOG_CONFIG LOG_STREAM(info, log_config)
118 
119 #define LOG_GENERAL LOG_STREAM(info, lg::general())
120 
121 static lg::log_domain log_preprocessor("preprocessor");
122 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
123 
124 // this is needed to allow identical functionality with clean refactoring
125 // play_game only returns on an error, all returns within play_game can
126 // be replaced with this
127 static void safe_exit(int res)
128 {
129  LOG_GENERAL << "exiting with code " << res;
130  exit(res);
131 }
132 
133 static void handle_preprocess_string(const commandline_options& cmdline_opts)
134 {
135  preproc_map defines_map;
136 
137  if(cmdline_opts.preprocess_input_macros) {
138  std::string file = *cmdline_opts.preprocess_input_macros;
139  if(!filesystem::file_exists(file)) {
140  PLAIN_LOG << "please specify an existing file. File " << file << " doesn't exist.";
141  return;
142  }
143 
144  PLAIN_LOG << "Reading cached defines from: " << file;
145 
146  config cfg;
147 
148  try {
150  } catch(const config::error& e) {
151  PLAIN_LOG << "Caught a config error while parsing file '" << file << "':\n" << e.message;
152  }
153 
154  int read = 0;
155 
156  for(const auto [_, cfg] : cfg.all_children_view()) {
157  preproc_define::insert(defines_map, cfg);
158  ++read;
159  }
160 
161  PLAIN_LOG << "Read " << read << " defines.";
162  }
163 
164  if(cmdline_opts.preprocess_defines) {
165  // add the specified defines
166  for(const std::string& define : *cmdline_opts.preprocess_defines) {
167  if(define.empty()) {
168  PLAIN_LOG << "empty define supplied";
169  continue;
170  }
171 
172  LOG_PREPROC << "adding define: " << define;
173  defines_map.try_emplace(define, define);
174  }
175  }
176 
177  // add the WESNOTH_VERSION define
178  defines_map.try_emplace("WESNOTH_VERSION", game_config::wesnoth_version.str());
179 
180  // preprocess resource
181  PLAIN_LOG << "added " << defines_map.size() << " defines.";
182  PLAIN_LOG << "preprocessing specified string: " << *cmdline_opts.preprocess_source_string;
183 
184  const utils::ms_optimer timer(
185  [](const auto& timer) { PLAIN_LOG << "preprocessing finished. Took " << timer << " ticks."; });
186 
187  const auto output_stream = preprocess_string(*cmdline_opts.preprocess_source_string, "wesnoth", defines_map);
188  std::cout << output_stream.get() << std::endl;
189 }
190 
191 static void handle_preprocess_command(const commandline_options& cmdline_opts)
192 {
193  preproc_map input_macros;
194 
195  if(cmdline_opts.preprocess_input_macros) {
196  std::string file = *cmdline_opts.preprocess_input_macros;
197  if(filesystem::file_exists(file) == false) {
198  PLAIN_LOG << "please specify an existing file. File " << file << " doesn't exist.";
199  return;
200  }
201 
202  PLAIN_LOG << "Reading cached defines from: " << file;
203 
204  config cfg;
205 
206  try {
208  } catch(const config::error& e) {
209  PLAIN_LOG << "Caught a config error while parsing file '" << file << "':\n" << e.message;
210  }
211 
212  int read = 0;
213 
214  for(const auto [_, cfg] : cfg.all_children_view()) {
215  preproc_define::insert(input_macros, cfg);
216  ++read;
217  }
218 
219  PLAIN_LOG << "Read " << read << " defines.";
220  }
221 
222  const std::string resourceToProcess(*cmdline_opts.preprocess_path);
223  const std::string targetDir(*cmdline_opts.preprocess_target);
224 
225  const utils::ms_optimer timer(
226  [](const auto& timer) { PLAIN_LOG << "preprocessing finished. Took " << timer << " ticks."; });
227 
228  // If the users add the SKIP_CORE define we won't preprocess data/core
229  bool skipCore = false;
230  bool skipTerrainGFX = false;
231 
232  // The 'core_defines_map' is the one got from data/core macros
233  preproc_map defines_map(input_macros);
234 
235  if(cmdline_opts.preprocess_defines) {
236  // add the specified defines
237  for(const std::string& define : *cmdline_opts.preprocess_defines) {
238  if(define.empty()) {
239  PLAIN_LOG << "empty define supplied";
240  continue;
241  }
242 
243  LOG_PREPROC << "adding define: " << define;
244  defines_map.try_emplace(define, define);
245 
246  if(define == "SKIP_CORE") {
247  PLAIN_LOG << "'SKIP_CORE' defined.";
248  skipCore = true;
249  } else if(define == "NO_TERRAIN_GFX") {
250  PLAIN_LOG << "'NO_TERRAIN_GFX' defined.";
251  skipTerrainGFX = true;
252  }
253  }
254  }
255 
256  // add the WESNOTH_VERSION define
257  defines_map.try_emplace("WESNOTH_VERSION", game_config::wesnoth_version.str());
258 
259  PLAIN_LOG << "added " << defines_map.size() << " defines.";
260 
261  // preprocess core macros first if we don't skip the core
262  if(skipCore == false) {
263  PLAIN_LOG << "preprocessing common macros from 'data/core' ...";
264 
265  // process each folder explicitly to gain speed
266  preprocess_resource(game_config::path + "/data/core/macros", &defines_map);
267 
268  if(skipTerrainGFX == false) {
269  preprocess_resource(game_config::path + "/data/core/terrain-graphics", &defines_map);
270  }
271 
272  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " 'data/core' defines.";
273  } else {
274  PLAIN_LOG << "skipped 'data/core'";
275  }
276 
277  // preprocess resource
278  PLAIN_LOG << "preprocessing specified resource: " << resourceToProcess << " ...";
279 
280  preprocess_resource(resourceToProcess, &defines_map, true, true, targetDir);
281  PLAIN_LOG << "acquired " << (defines_map.size() - input_macros.size()) << " total defines.";
282 
283  if(cmdline_opts.preprocess_output_macros) {
284  std::string outputFileName = "_MACROS_.cfg";
285  if(!cmdline_opts.preprocess_output_macros->empty()) {
286  outputFileName = *cmdline_opts.preprocess_output_macros;
287  }
288 
289  std::string outputPath = targetDir + "/" + outputFileName;
290 
291  PLAIN_LOG << "writing '" << outputPath << "' with " << defines_map.size() << " defines.";
292 
294  if(!out->fail()) {
295  config_writer writer(*out, false);
296 
297  for(auto& define_pair : defines_map) {
298  define_pair.second.write(writer, define_pair.first);
299  }
300  } else {
301  PLAIN_LOG << "couldn't open the file.";
302  }
303  }
304 }
305 
306 static int handle_validate_command(const std::string& file, abstract_validator& validator, const std::vector<std::string>& defines)
307 {
308  preproc_map defines_map;
309  defines_map.try_emplace("WESNOTH_VERSION", game_config::wesnoth_version.str());
310  defines_map.try_emplace("SCHEMA_VALIDATION");
311 
312  for(const std::string& define : defines) {
313  if(define.empty()) {
314  PLAIN_LOG << "empty define supplied";
315  continue;
316  }
317 
318  LOG_PREPROC << "adding define: " << define;
319  defines_map.try_emplace(define, define);
320  }
321 
322  PLAIN_LOG << "Validating " << file << " against schema " << validator.name_;
324  io::read(*preprocess_file(file, defines_map), &validator);
325  if(lg::broke_strict()) {
326  std::cout << "validation failed\n";
327  } else {
328  std::cout << "validation succeeded\n";
329  }
330  return lg::broke_strict();
331 }
332 
333 /** Process commandline-arguments */
334 static int process_command_args(commandline_options& cmdline_opts)
335 {
336  // Options that output info unaffected by other options and return.
337  if(cmdline_opts.help) {
338  std::cout << cmdline_opts;
339  return 0;
340  }
341 
342  if(cmdline_opts.logdomains) {
343  std::cout << lg::list_log_domains(*cmdline_opts.logdomains);
344  return 0;
345  }
346 
347  if(cmdline_opts.version) {
348  std::cout << "Battle for Wesnoth" << " " << game_config::wesnoth_version.str() << "\n\n";
349  std::cout << "Library versions:\n" << game_config::library_versions_report() << '\n';
350  std::cout << "Optional features:\n" << game_config::optional_features_report();
351  return 0;
352  }
353 
354  if(cmdline_opts.simple_version) {
355  std::cout << game_config::wesnoth_version.str() << "\n";
356  return 0;
357  }
358 
359  // Options that don't change behavior based on any others should be checked alphabetically below.
360 
361  if(cmdline_opts.no_log_sanitize) {
362  lg::set_log_sanitize(false);
363  }
364 
365  if(cmdline_opts.usercache_dir) {
367  }
368 
369  if(cmdline_opts.userdata_dir) {
371  }
372 
373  // earliest possible point to ensure the userdata directory is known
375  filesystem::set_user_data_dir(std::string());
376  }
377 
378  // userdata is initialized, so initialize logging to file if enabled
379  // If true, output will be redirected to file, else output be written to console.
380  // On Windows, if Wesnoth was not started from a console, one will be allocated.
381  if(cmdline_opts.log_to_file
382  || (!cmdline_opts.no_log_to_file
383  && !getenv("WESNOTH_NO_LOG_FILE")
384  // command line options that imply not redirecting output to a log file
385  && !cmdline_opts.data_path
386  && !cmdline_opts.userdata_path
387  && !cmdline_opts.usercache_path
388  && !cmdline_opts.report
389  && !cmdline_opts.do_diff
390  && !cmdline_opts.do_patch
391  && !cmdline_opts.preprocess
392  && !cmdline_opts.render_image
393  && !cmdline_opts.screenshot
394  && !cmdline_opts.nogui
395  && !cmdline_opts.headless_unit_test
396  && !cmdline_opts.validate_schema
397  && !cmdline_opts.validate_wml
398  )
399  )
400  {
402  }
403 #ifdef _WIN32
404  // This forces a Windows console to be attached to the process even
405  // if Wesnoth is an IMAGE_SUBSYSTEM_WINDOWS_GUI executable because it
406  // turns Wesnoth into a CLI application. (unless --wnoconsole is given)
407  else if(!cmdline_opts.no_console) {
409  }
410 #endif
411 
412  if(cmdline_opts.log) {
413  for(const auto& log_pair : *cmdline_opts.log) {
414  const std::string log_domain = log_pair.second;
415  const lg::severity severity = log_pair.first;
416  if(!lg::set_log_domain_severity(log_domain, severity)) {
417  PLAIN_LOG << "unknown log domain: " << log_domain;
418  return 2;
419  }
420  }
421  }
422 
423  if(!cmdline_opts.nobanner) {
424  const auto now = std::chrono::system_clock::now();
425  PLAIN_LOG << "Battle for Wesnoth v" << game_config::revision << " " << game_config::build_arch();
426  PLAIN_LOG << "Started on " << chrono::format_local_timestamp(now, "%a %b %d %T %Y") << '\n';
427  }
428 
429  if(cmdline_opts.usercache_path) {
430  std::cout << filesystem::get_cache_dir();
431  return 0;
432  }
433 
434  if(cmdline_opts.userdata_path) {
435  std::cout << filesystem::get_user_data_dir();
436  return 0;
437  }
438 
439  if(cmdline_opts.data_dir) {
440  game_config::path = filesystem::normalize_path(*cmdline_opts.data_dir, true, true);
441  if(!cmdline_opts.nobanner) {
442  PLAIN_LOG << "Overriding data directory with '" << game_config::path << "'";
443  }
444  } else {
445 #if defined(__ANDROID__) && !defined(WESNOTH_PATH)
446  if(const char* ext_path = SDL_AndroidGetExternalStoragePath()) {
447  game_config::path = ext_path + std::string("/gamedata");
448  PLAIN_LOG << "Determined game data directory: " << game_config::path;
449  } else {
450  PLAIN_LOG << "Cannot find game data directory, specify one with --data-dir. SDL_AndroidGetExternalStoragePath() failed: " << SDL_GetError();
451  }
452 #endif
453  // if a pre-defined path does not exist this will empty it
455  if(game_config::path.empty()) {
456  if(std::string exe_dir = filesystem::get_exe_dir(); !exe_dir.empty()) {
457  if(std::string auto_dir = filesystem::autodetect_game_data_dir(std::move(exe_dir)); !auto_dir.empty()) {
458  if(!cmdline_opts.nobanner) {
459  PLAIN_LOG << "Automatically found a possible data directory at: " << auto_dir;
460  }
461  game_config::path = filesystem::normalize_path(auto_dir, true, true);
462  }
463  } else {
464  PLAIN_LOG << "Cannot find game data directory. Specify one with --data-dir";
465  return 1;
466  }
467  }
468  }
469 
471  PLAIN_LOG << "Could not find game data directory '" << game_config::path << "'";
472  return 1;
473  }
474 
475  if(cmdline_opts.data_path) {
476  std::cout << game_config::path;
477  return 0;
478  }
479 
480  if(cmdline_opts.debug_lua) {
481  game_config::debug_lua = true;
482  }
483 
484  if(cmdline_opts.allow_insecure) {
486  }
487 
488  if(cmdline_opts.addon_server_info) {
490  }
491 
492  if(cmdline_opts.strict_lua) {
494  }
495 
496  if(cmdline_opts.log_precise_timestamps) {
498  }
499 
500  if(cmdline_opts.rng_seed) {
501  srand(*cmdline_opts.rng_seed);
502  }
503 
504  if(cmdline_opts.render_image) {
505  SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
506  }
507 
508  if(cmdline_opts.strict_validation) {
510  }
511 
512  if(cmdline_opts.report) {
513  std::cout << "\n========= BUILD INFORMATION =========\n\n" << game_config::full_build_report();
514  return 0;
515  }
516 
517  if(cmdline_opts.validate_schema) {
519  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
520  return handle_validate_command(*cmdline_opts.validate_schema, validator, {});
521  }
522 
523  if(cmdline_opts.do_diff) {
524  std::ifstream in_left(cmdline_opts.diff_left);
525  std::ifstream in_right(cmdline_opts.diff_right);
526  config left = io::read(in_left);
527  config right = io::read(in_right);
528  std::ostream* os = &std::cout;
529  if(cmdline_opts.output_file) {
530  os = new std::ofstream(*cmdline_opts.output_file);
531  }
533  out.write(right.get_diff(left));
534  if(os != &std::cout) delete os;
535  return 0;
536  }
537 
538  if(cmdline_opts.do_patch) {
539  std::ifstream in_base(cmdline_opts.diff_left);
540  std::ifstream in_diff(cmdline_opts.diff_right);
541  config base = io::read(in_base);
542  config diff = io::read(in_diff);
543  base.apply_diff(diff);
544  std::ostream* os = &std::cout;
545  if(cmdline_opts.output_file) {
546  os = new std::ofstream(*cmdline_opts.output_file);
547  }
549  out.write(base);
550  if(os != &std::cout) delete os;
551  return 0;
552  }
553 
554  if(cmdline_opts.generate_spritesheet) {
555  PLAIN_LOG << "sheet path " << *cmdline_opts.generate_spritesheet;
557  return 0;
558  }
559 
560  // Options changing their behavior dependent on some others should be checked below.
561 
562  if(cmdline_opts.preprocess) {
563  handle_preprocess_command(cmdline_opts);
564  return 0;
565  }
566 
567  if(cmdline_opts.preprocess_source_string.has_value()) {
568  handle_preprocess_string(cmdline_opts);
569  return 0;
570  }
571 
572  if(cmdline_opts.validate_wml) {
573  std::string schema_path;
574  if(cmdline_opts.validate_with) {
575  schema_path = *cmdline_opts.validate_with;
576  if(!filesystem::file_exists(schema_path)) {
577  if(auto check = filesystem::get_wml_location(schema_path)) {
578  schema_path = check.value();
579  } else {
580  PLAIN_LOG << "Could not find schema file: " << schema_path;
581  }
582  } else {
583  schema_path = filesystem::normalize_path(schema_path);
584  }
585  } else {
586  schema_path = filesystem::get_wml_location("schema/game_config.cfg").value();
587  }
589  validator.set_create_exceptions(false); // Don't crash if there's an error, just go ahead anyway
590  return handle_validate_command(*cmdline_opts.validate_wml, validator,
591  cmdline_opts.preprocess_defines.value_or<decltype(cmdline_opts.preprocess_defines)::value_type>({}));
592  }
593 
594  if(cmdline_opts.preprocess_defines || cmdline_opts.preprocess_input_macros || cmdline_opts.preprocess_path) {
595  // It would be good if this was supported for running tests too, possibly for other uses.
596  // For the moment show an error message instead of leaving the user wondering why it doesn't work.
597  PLAIN_LOG << "That --preprocess-* option is only supported when using --preprocess or --validate.";
598  return 2;
599  }
600 
601  // Not the most intuitive solution, but I wanted to leave current semantics for now
602  return -1;
603 }
604 
605 /**
606  * I would prefer to setup locale first so that early error
607  * messages can get localized, but we need the game_launcher
608  * initialized to have filesystem::get_intl_dir() to work. Note: setlocale()
609  * does not take GUI language setting into account.
610  */
611 static void init_locale()
612 {
613 #if defined _WIN32 || defined __APPLE__
614  setlocale(LC_ALL, "English");
615 #else
616  std::setlocale(LC_ALL, "C");
617 #endif
618 
619  const std::string& intl_dir = filesystem::get_intl_dir();
620 
621  translation::bind_textdomain(PACKAGE, intl_dir.c_str(), "UTF-8");
622  translation::bind_textdomain(PACKAGE "-lib", intl_dir.c_str(), "UTF-8");
624 }
625 
626 /**
627  * Print an alert and instructions to stderr about early initialization errors.
628  *
629  * This is provided as an aid for users dealing with potential data dir
630  * configuration issues. The first code to read core WML *has* the
631  * responsibility to call this function in the event of a problem, to inform
632  * the user of the most likely possible cause and suggest a course of action
633  * to solve the issue.
634  */
636 {
637  // NOTE: wrap output to 80 columns.
638  PLAIN_LOG << '\n'
639  << "An error at this point during initialization usually indicates that the data\n"
640  << "directory above was not correctly set or detected. Try passing the correct path\n"
641  << "in the command line with the --data-dir switch or as the only argument.";
642 }
643 
644 /**
645  * Handles the lua script command line arguments if present.
646  * This function will only run once.
647  */
649 {
650  static bool first_time = true;
651 
652  if(!first_time) {
653  return;
654  }
655 
656  first_time = false;
657 
658  if(!game->init_lua_script()) {
659  // PLAIN_LOG << "error when loading lua scripts at startup";
660  // PLAIN_LOG << "could not load lua script: " << *cmdline_opts.script_file;
661  }
662 }
663 
664 #ifdef _MSC_VER
665 static void check_fpu()
666 {
667  uint32_t f_control;
668 
669  if(_controlfp_s(&f_control, 0, 0) == 0) {
670  uint32_t unused;
671  uint32_t rounding_mode = f_control & _MCW_RC;
672 
673  if(rounding_mode != _RC_NEAR) {
674  PLAIN_LOG << "Floating point rounding mode is currently '"
675  << ((rounding_mode == _RC_CHOP)
676  ? "chop"
677  : (rounding_mode == _RC_UP)
678  ? "up"
679  : (rounding_mode == _RC_DOWN)
680  ? "down"
681  : (rounding_mode == _RC_NEAR) ? "near" : "unknown")
682  << "' setting to 'near'";
683 
684  if(_controlfp_s(&unused, _RC_NEAR, _MCW_RC)) {
685  PLAIN_LOG << "failed to set floating point rounding type to 'near'";
686  }
687  }
688 
689 #ifndef _M_AMD64
690  uint32_t precision_mode = f_control & _MCW_PC;
691  if(precision_mode != _PC_53) {
692  PLAIN_LOG << "Floating point precision mode is currently '"
693  << ((precision_mode == _PC_53)
694  ? "double"
695  : (precision_mode == _PC_24)
696  ? "single"
697  : (precision_mode == _PC_64) ? "double extended" : "unknown")
698  << "' setting to 'double'";
699 
700  if(_controlfp_s(&unused, _PC_53, _MCW_PC)) {
701  PLAIN_LOG << "failed to set floating point precision type to 'double'";
702  }
703  }
704 #endif
705 
706  } else {
707  PLAIN_LOG << "_controlfp_s failed.";
708  }
709 }
710 #else
711 static void check_fpu()
712 {
713  switch(fegetround()) {
714  case FE_TONEAREST:
715  break;
716  case FE_DOWNWARD:
717  STREAMING_LOG << "Floating point precision mode is currently 'downward'";
718  goto reset_fpu;
719  case FE_TOWARDZERO:
720  STREAMING_LOG << "Floating point precision mode is currently 'toward-zero'";
721  goto reset_fpu;
722  case FE_UPWARD:
723  STREAMING_LOG << "Floating point precision mode is currently 'upward'";
724  goto reset_fpu;
725  default:
726  STREAMING_LOG << "Floating point precision mode is currently 'unknown'";
727  goto reset_fpu;
728  reset_fpu:
729  STREAMING_LOG << " - setting to 'nearest'\n";
730  fesetround(FE_TONEAREST);
731  break;
732  }
733 }
734 #endif
735 
736 /** Gets the appropriate action code for the current iteration of the game loop. */
738 {
739  // If loading a game, skip the titlescreen entirely
740  if(game.has_load_data()) {
742  }
743 
744  title_screen dlg{game};
745 
746  // Allows re-layout on resize.
747  // Since RELOAD_UI is not checked here, it causes
748  // the dialog to be closed and reshown with changes.
749  while(dlg.get_retval() == title_screen::REDRAW_BACKGROUND) {
750  dlg.show();
751  }
752 
753  return dlg.get_retval();
754 }
755 
756 /**
757  * Setups the game environment and enters
758  * the titlescreen or game loops.
759  */
760 static int do_gameloop(commandline_options& cmdline_opts)
761 {
762  srand(std::time(nullptr));
763 
764  const auto game = std::make_unique<game_launcher>(cmdline_opts);
765 
766  init_locale();
767 
768  bool res;
769 
770  // Do initialize fonts before reading the game config, to have game
771  // config error messages displayed. fonts will be re-initialized later
772  // when the language is read from the game config.
773  res = font::load_font_config();
774  if(res == false) {
775  PLAIN_LOG << "could not initialize fonts";
776  // The most common symptom of a bogus data dir path -- warn the user.
778  return 1;
779  }
780 
781  res = game->init_language();
782  if(res == false) {
783  PLAIN_LOG << "could not initialize the language";
784  return 1;
785  }
786 
787  res = game->init_video();
788  if(res == false) {
789  PLAIN_LOG << "could not initialize display";
790  return 1;
791  }
792 
793  check_fpu();
794  const cursor::manager cursor_manager;
796 
797 #if(defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
798  SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
799 #endif
800 
801  gui2::init();
802  gui2::switch_theme(prefs::get().gui2_theme());
803  const gui2::event::manager gui_event_manager;
804 
805  // if the log directory is not writable, then this is the error condition so show the error message.
806  // if the log directory is writable, then there's no issue.
807  // if the optional isn't set, then logging to file has been disabled, so there's no issue.
808  if(!lg::log_dir_writable().value_or(true)) {
809  utils::string_map symbols;
810  symbols["logdir"] = filesystem::get_logs_dir();
811  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);
812  gui2::show_message(_("Logging Failure"), msg, message::ok_button);
813  }
814 
815  game_config_manager config_manager(cmdline_opts);
816 
820  }
821 
822  loading_screen::display([&res, &config_manager, &cmdline_opts]() {
825 
826  if(res == false) {
827  PLAIN_LOG << "could not initialize game config";
828  return;
829  }
830 
832 
833  res = font::load_font_config();
834  if(res == false) {
835  PLAIN_LOG << "could not re-initialize fonts for the current language";
836  return;
837  }
838 
839  if(!game_config::no_addons && !cmdline_opts.noaddons) {
841 
843  }
844  });
845 
846  if(res == false) {
847  return 1;
848  }
849 
850  plugins_manager plugins_man(new application_lua_kernel);
851 
852  const plugins_context::reg_vec callbacks {
853  {"play_multiplayer", std::bind(&game_launcher::play_multiplayer, game.get(), game_launcher::mp_mode::CONNECT)},
854  {"play_local", std::bind(&game_launcher::play_multiplayer, game.get(), game_launcher::mp_mode::LOCAL)},
855  {"play_campaign", std::bind(&game_launcher::play_campaign, game.get())},
856  };
857 
858  const plugins_context::areg_vec accessors {
859  {"command_line", std::bind(&commandline_options::to_config, &cmdline_opts)},
860  };
861 
862  plugins_context plugins("titlescreen", callbacks, accessors);
863 
864  plugins.set_callback("exit", [](const config& cfg) { safe_exit(cfg["code"].to_int(0)); }, false);
865 
866  while(true) {
867  if(!game->has_load_data()) {
868  auto cfg = config_manager.game_config().optional_child("titlescreen_music");
869  if(cfg) {
870  for(const config& i : cfg->child_range("music")) {
872  }
873 
874  config title_music_config;
875  title_music_config["name"] = game_config::title_music;
876  title_music_config["append"] = true;
877  title_music_config["immediate"] = true;
878  sound::play_music_config(title_music_config);
879  } else {
882  }
883  }
884 
885  handle_lua_script_args(&*game, cmdline_opts);
886 
887  plugins.play_slice();
888  plugins.play_slice();
889 
890  if(!cmdline_opts.unit_test.empty()) {
891  return static_cast<int>(game->unit_test());
892  }
893 
894  if(game->play_test() == false) {
895  return 0;
896  }
897 
898  if(game->play_screenshot_mode() == false) {
899  return 0;
900  }
901 
902  if(game->play_render_image_mode() == false) {
903  return 0;
904  }
905 
906  // Start directly a campaign
907  if(game->goto_campaign() == false) {
908  if(game->jump_to_campaign_id().empty())
909  continue; // Go to main menu
910  else
911  return 1; // we got an error starting the campaign from command line
912  }
913 
914  // Start directly a multiplayer
915  // Eventually with a specified server
916  if(game->goto_multiplayer() == false) {
917  continue; // Go to main menu
918  }
919 
920  // Start directly a commandline multiplayer game
921  if(game->play_multiplayer_commandline() == false) {
922  return 0;
923  }
924 
925  if(game->goto_editor() == false) {
926  return 0;
927  }
928 
929  const font::floating_label_context label_manager;
930 
932 
933  switch(get_gameloop_action(*game)) {
935  LOG_GENERAL << "quitting game...";
936  return 0;
938  game->play_multiplayer(game_launcher::mp_mode::CONNECT);
939  break;
941  game->play_multiplayer(game_launcher::mp_mode::HOST);
942  break;
944  game->play_multiplayer(game_launcher::mp_mode::LOCAL);
945  break;
947  loading_screen::display([&config_manager]() {
948  config_manager.reload_changed_game_config();
949  gui2::init();
950  gui2::switch_theme(prefs::get().gui2_theme());
951  });
952  break;
954  game->start_editor();
955  break;
957  if(!game->load_prepared_game()) {
958  break;
959  }
960  [[fallthrough]];
963  break;
965  break;
967  gui2::switch_theme(prefs::get().gui2_theme());
968  break;
969  }
970  }
971 }
972 
973 #ifdef _WIN32
974 #define error_exit(res) \
975  do { \
976  if(lg::using_own_console()) { \
977  std::cerr << "Press enter to continue..." << std::endl; \
978  std::cin.get(); \
979  } \
980  return res; \
981  } while(false)
982 #else
983 #define error_exit(res) return res
984 #endif
985 
986 #ifdef __APPLE__
987 extern "C" int wesnoth_main(int argc, char** argv);
988 int wesnoth_main(int argc, char** argv)
989 #else
990 int main(int argc, char** argv)
991 #endif
992 {
994  auto args = read_argv(argc, argv);
995  assert(!args.empty());
996 
997 #ifdef _WIN32
998  _putenv("PANGOCAIRO_BACKEND=fontconfig");
999  _putenv("FONTCONFIG_PATH=fonts");
1000 #endif
1001 #ifdef __APPLE__
1002  // Using setenv with overwrite disabled so we can override this in the
1003  // original process environment for research/testing purposes.
1004  setenv("PANGOCAIRO_BACKEND", "fontconfig", 0);
1005 #endif
1006 #ifdef __ANDROID__
1007  setenv("PANGOCAIRO_BACKEND", "fontconfig", 0);
1008  setenv("SDL_HINT_AUDIODRIVER", "android", 0);
1009  setenv("SDL_HINT_ANDROID_TRAP_BACK_BUTTON", "1", 0);
1010 #endif
1011 
1012  try {
1013  commandline_options cmdline_opts = commandline_options(args);
1014  int finished = process_command_args(cmdline_opts);
1015 
1016  if(finished != -1) {
1017 #ifdef _WIN32
1018  if(lg::using_own_console()) {
1019  std::cerr << "Press enter to continue..." << std::endl;
1020  std::cin.get();
1021  }
1022 #endif
1023  safe_exit(finished);
1024  }
1025 
1026  SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
1027  // Is there a reason not to just use SDL_INIT_EVERYTHING?
1028  if(SDL_Init(SDL_INIT_TIMER) < 0) {
1029  PLAIN_LOG << "Couldn't initialize SDL: " << SDL_GetError();
1030  return (1);
1031  }
1032  atexit(SDL_Quit);
1033 
1034  // Mac's touchpad generates touch events too.
1035  // Ignore them until Macs have a touchscreen: https://forums.libsdl.org/viewtopic.php?p=45758
1036 #if defined(__APPLE__) && !defined(__IPHONEOS__)
1037  SDL_EventState(SDL_FINGERMOTION, SDL_DISABLE);
1038  SDL_EventState(SDL_FINGERDOWN, SDL_DISABLE);
1039  SDL_EventState(SDL_FINGERUP, SDL_DISABLE);
1040 #endif
1041 
1042  // declare this here so that it will always be at the front of the event queue.
1043  events::event_context global_context;
1044 
1045 #ifndef __ANDROID__
1046  SDL_StartTextInput();
1047 #endif
1048  const int res = do_gameloop(cmdline_opts);
1049  safe_exit(res);
1050  } catch(const boost::program_options::error& e) {
1051  // logging hasn't been initialized by this point
1052  std::cerr << "Error in command line: " << e.what() << std::endl;
1053  std::string error = "Error parsing command line arguments: ";
1054  error += e.what();
1055  SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", error.c_str(), nullptr);
1056  error_exit(2);
1057  } catch(const video::error& e) {
1058  PLAIN_LOG << "Video system error: " << e.what();
1059  error_exit(1);
1060  } catch(const font::error& e) {
1061  PLAIN_LOG << "Could not initialize fonts.\n\n" << e.what() << "\n\nExiting.";
1062  error_exit(1);
1063  } catch(const config::error& e) {
1064  PLAIN_LOG << e.message;
1065  error_exit(1);
1066  } catch(const video::quit&) {
1067  // just means the game should quit
1068  } catch(const return_to_play_side_exception&) {
1069  PLAIN_LOG << "caught return_to_play_side_exception, please report this bug (quitting)";
1070  } catch(const quit_game_exception&) {
1071  PLAIN_LOG << "caught quit_game_exception (quitting)";
1072  } catch(const wml_exception& e) {
1073  PLAIN_LOG << "WML exception:\nUser message: " << e.user_message << "\nDev message: " << e.dev_message;
1074  error_exit(1);
1075  } catch(const wfl::formula_error& e) {
1076  PLAIN_LOG << e.what() << "\n\nGame will be aborted.";
1077  error_exit(1);
1078  } catch(const sdl::exception& e) {
1079  PLAIN_LOG << e.what();
1080  error_exit(1);
1081  } catch(const game::error& e) {
1082  PLAIN_LOG << "Game error: " << e.what();
1083  error_exit(1);
1084  } catch(const std::bad_alloc&) {
1085  PLAIN_LOG << "Ran out of memory. Aborted.";
1086  error_exit(ENOMEM);
1087 #if !defined(NO_CATCH_AT_GAME_END)
1088  } catch(const std::exception& e) {
1089  // Try to catch unexpected exceptions.
1090  PLAIN_LOG << "Caught general '" << typeid(e).name() << "' exception:\n" << e.what();
1091  error_exit(1);
1092  } catch(const std::string& e) {
1093  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1094  error_exit(1);
1095  } catch(const char* e) {
1096  PLAIN_LOG << "Caught a string thrown as an exception:\n" << e;
1097  error_exit(1);
1098  } catch(...) {
1099  // Ensure that even when we terminate with `throw 42`, the exception
1100  // is caught and all destructors are actually called. (Apparently,
1101  // some compilers will simply terminate without calling destructors if
1102  // the exception isn't caught.)
1103  PLAIN_LOG << "Caught general exception " << utils::get_unknown_exception_type() << ". Terminating.";
1104  error_exit(1);
1105 #endif
1106  }
1107 
1108  return 0;
1109 } // 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:381
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:268
void apply_diff(const config &diff, bool track=false)
A function to apply a diff config onto this config object.
Definition: config.cpp:1016
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:900
@ 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
This class implements the title screen.
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:328
std::vector< std::string > read_argv([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Declarations for File-IO.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1032
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:97
Standard logging facilities (interface).
#define STREAMING_LOG
Definition: log.hpp:297
#define PLAIN_LOG
Definition: log.hpp:296
auto format_local_timestamp(const std::chrono::system_clock::time_point &time, std::string_view format="%F %T")
Definition: chrono.hpp:62
@ 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:178
void set_main_thread()
Definition: events.cpp:468
std::string get_cache_dir()
Definition: filesystem.cpp:882
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
std::string get_user_data_dir()
Definition: filesystem.cpp:872
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:341
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:835
std::string get_logs_dir()
Definition: filesystem.cpp:877
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:54
bool is_userdata_initialized()
Definition: filesystem.cpp:624
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:737
bool load_font_config()
Definition: font_config.cpp:78
std::string path
Definition: filesystem.cpp:106
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:114
void init()
Initializes the GUI subsystems.
Definition: gui.cpp:112
void switch_theme(const std::string &theme_id)
Set and activate the given gui2 theme.
Definition: gui.cpp:153
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
void build_spritesheet_from(const std::string &entry_point)
config read(std::istream &in, abstract_validator *validator)
Definition: parser.cpp:600
bool using_own_console()
Returns true if a console was allocated by the Wesnoth process.
severity
Definition: log.hpp:82
std::string list_log_domains(const std::string &filter)
Definition: log.cpp:410
bool broke_strict()
Definition: log.cpp:430
void set_log_to_file()
Do the initial redirection to a log file if the logs directory is writable.
Definition: log.cpp:266
void set_log_sanitize(bool sanitize)
toggle log sanitization
Definition: log.cpp:434
utils::optional< bool > log_dir_writable()
Returns the result set by check_log_dir_writable().
Definition: log.cpp:312
void do_console_redirect()
Allocates a console if needed and redirects output to CONOUT.
void precise_timestamps(bool pt)
Definition: log.cpp:337
bool set_log_domain_severity(const std::string &name, severity severity)
Definition: log.cpp:379
void set_strict_severity(severity severity)
Definition: log.cpp:420
void empty_playlist()
Definition: sound.cpp:611
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:689
void stop_music()
Definition: sound.cpp:554
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
filesystem::scoped_istream preprocess_string(const std::string &contents, const std::string &textdomain)
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.
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 void insert(preproc_map &, 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:321
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:133
static void safe_exit(int res)
Definition: wesnoth.cpp:127
static int get_gameloop_action(game_launcher &game)
Gets the appropriate action code for the current iteration of the game loop.
Definition: wesnoth.cpp:737
static int do_gameloop(commandline_options &cmdline_opts)
Setups the game environment and enters the titlescreen or game loops.
Definition: wesnoth.cpp:760
static int handle_validate_command(const std::string &file, abstract_validator &validator, const std::vector< std::string > &defines)
Definition: wesnoth.cpp:306
int main(int argc, char **argv)
Definition: wesnoth.cpp:990
static int process_command_args(commandline_options &cmdline_opts)
Process commandline-arguments.
Definition: wesnoth.cpp:334
static void handle_preprocess_command(const commandline_options &cmdline_opts)
Definition: wesnoth.cpp:191
static void check_fpu()
Definition: wesnoth.cpp:711
#define LOG_PREPROC
Definition: wesnoth.cpp:122
static void init_locale()
I would prefer to setup locale first so that early error messages can get localized,...
Definition: wesnoth.cpp:611
static void handle_lua_script_args(game_launcher *game, commandline_options &)
Handles the lua script command line arguments if present.
Definition: wesnoth.cpp:648
#define error_exit(res)
Definition: wesnoth.cpp:983
static void warn_early_init_failure()
Print an alert and instructions to stderr about early initialization errors.
Definition: wesnoth.cpp:635
#define LOG_GENERAL
Definition: wesnoth.cpp:119
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