The Battle for Wesnoth  1.19.0-dev
commandline_options.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024
3  by Lukasz Dobrogowski <lukasz.dobrogowski@gmail.com>
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 "commandline_options.hpp"
17 
18 #include "config.hpp"
19 #include "formatter.hpp"
20 #include "lexical_cast.hpp"
21 #include "serialization/string_utils.hpp" // for split
22 
23 #include <boost/any.hpp> // for any
24 #include <boost/program_options/cmdline.hpp>
25 #include <boost/program_options/errors.hpp> // for validation_error, etc
26 #include <boost/program_options/parsers.hpp>
27 #include <boost/program_options/positional_options.hpp>
28 #include <boost/program_options/value_semantic.hpp> // for value, etc
29 #include <boost/program_options/variables_map.hpp> // for variables_map, etc
30 
31 #include <array>
32 
33 namespace po = boost::program_options;
34 
35 class two_strings : public std::pair<std::string,std::string> {};
36 
37 static void validate(boost::any& v, const std::vector<std::string>& values,
38  two_strings*, int)
39 {
40  two_strings ret_val;
41  if(values.size() != 2) {
42  throw po::validation_error(po::validation_error::invalid_option_value);
43  }
44  ret_val.first = values[0];
45  ret_val.second = values[1];
46  v = ret_val;
47 }
48 
50  : error(formatter() << "Invalid resolution \"" << resolution
51  << "\" (WIDTHxHEIGHT expected)")
52 {
53 }
54 
56  const std::string& expected_format)
57  : error(formatter() << "Invalid value set \"" << str
58  << "\" (" << expected_format << " expected)")
59 {
60 }
61 
62 
63 #define IMPLY_TERMINAL " Implies --no-log-to-file"
64 
65 
66 commandline_options::commandline_options(const std::vector<std::string>& args)
67  : bunzip2()
68  , bzip2()
69  , campaign()
70  , campaign_difficulty()
71  , campaign_scenario()
72  , campaign_skip_story(false)
73  , clock(false)
74  , core_id()
75  , data_path(false)
76  , data_dir()
77  , debug(false)
78  , debug_lua(false)
79  , strict_lua(false)
80  , allow_insecure(false)
81 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
82  , debug_dot_domain()
83  , debug_dot_level()
84 #endif
85  , editor()
86  , fps(false)
87  , fullscreen(false)
88  , gunzip()
89  , gzip()
90  , help()
91  , language()
92  , log()
93  , load()
94  , logdomains()
95  , log_precise_timestamps(false)
96  , multiplayer(false)
97  , multiplayer_ai_config()
98  , multiplayer_algorithm()
99  , multiplayer_controller()
100  , multiplayer_era()
101  , multiplayer_exit_at_end()
102  , multiplayer_ignore_map_settings()
103  , multiplayer_label()
104  , multiplayer_parm()
105  , multiplayer_repeat()
106  , multiplayer_scenario()
107  , multiplayer_side()
108  , multiplayer_turns()
109  , max_fps()
110  , noaddons(false)
111  , nocache(false)
112  , nodelay(false)
113  , nogui(false)
114  , nobanner(false)
115  , nomusic(false)
116  , nosound(false)
117  , new_widgets(false)
118  , preprocess(false)
119  , preprocess_defines()
120  , preprocess_input_macros()
121  , preprocess_output_macros()
122  , preprocess_path()
123  , preprocess_target()
124  , resolution()
125  , rng_seed()
126  , server()
127  , username()
128  , password()
129  , render_image()
130  , render_image_dst()
131  , screenshot(false)
132  , screenshot_map_file()
133  , screenshot_output_file()
134  , script_file()
135  , plugin_file()
136  , script_unsafe_mode(false)
137  , strict_validation(false)
138  , test()
139  , unit_test()
140  , headless_unit_test(false)
141  , noreplaycheck(false)
142  , mptest(false)
143  , usercache_path(false)
144  , usercache_dir()
145  , userconfig_path(false)
146  , userconfig_dir()
147  , userdata_path(false)
148  , userdata_dir()
149  , validcache(false)
150  , validate_core(false)
151  , validate_addon()
152  , validate_schema()
153  , validate_wml()
154  , validate_with()
155  , do_diff()
156  , do_patch()
157  , diff_left()
158  , diff_right()
159  , version(false)
160  , simple_version(false)
161  , report(false)
162  , windowed(false)
163  , with_replay(false)
164  , translation_percent()
165  , args_(args.begin() + 1, args.end())
166  , args0_(*args.begin())
167  , all_()
168  , visible_()
169  , hidden_()
170 {
171  // When adding items don't forget to update doc/man/wesnoth.6
172  // Options are sorted alphabetically by --long-option.
173  po::options_description general_opts("General options");
174  general_opts.add_options()
175  ("all-translations", "Show all translations, even incomplete ones.")
176  ("bunzip2", po::value<std::string>(), "decompresses a file (<arg>.bz2) in bzip2 format and stores it without the .bz2 suffix. <arg>.bz2 will be removed." IMPLY_TERMINAL)
177  ("bzip2", po::value<std::string>(), "compresses a file (<arg>) in bzip2 format, stores it as <arg>.bz2 and removes <arg>." IMPLY_TERMINAL)
178  ("clock", "Adds the option to show a clock for testing the drawing timer.")
179  ("config-dir", po::value<std::string>(), "sets the path of the userdata directory to $HOME/<arg> or My Documents\\My Games\\<arg> for Windows. You can specify also an absolute path outside the $HOME or My Documents\\My Games directory. DEPRECATED: use userdata-dir instead.")
180  ("config-path", "prints the path of the userdata directory and exits. DEPRECATED: use userdata-path instead." IMPLY_TERMINAL)
181  ("core", po::value<std::string>(), "overrides the loaded core with the one whose id is specified.")
182  ("data-dir", po::value<std::string>(), "overrides the data directory with the one specified.")
183  ("data-path", "prints the path of the data directory and exits." IMPLY_TERMINAL)
184  ("debug,d", "enables additional command mode options in-game.")
185  ("debug-lua", "enables some Lua debugging mechanisms")
186  ("strict-lua", "disallow deprecated Lua API calls")
187  ("allow-insecure", "Allows sending a plaintext password over an unencrypted connection. Should only ever be used for local testing.")
188 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
189  ("debug-dot-level", po::value<std::string>(), "sets the level of the debug dot files. <arg> should be a comma separated list of levels. These files are used for debugging the widgets especially the for the layout engine. When enabled the engine will produce dot files which can be converted to images with the dot tool. Available levels: size (generate the size info of the widget), state (generate the state info of the widget).")
190  ("debug-dot-domain", po::value<std::string>(), "sets the domain of the debug dot files. <arg> should be a comma separated list of domains. See --debug-dot-level for more info. Available domains: show (generate the data when the dialog is about to be shown), layout (generate the data during the layout phase - might result in multiple files). The data can also be generated when the F12 is pressed in a dialog.")
191 #endif
192  ("editor,e", po::value<std::string>()->implicit_value(std::string()), "starts the in-game map editor directly. If file <arg> is specified, equivalent to -e --load <arg>.")
193  ("gunzip", po::value<std::string>(), "decompresses a file (<arg>.gz) in gzip format and stores it without the .gz suffix. <arg>.gz will be removed." IMPLY_TERMINAL)
194  ("gzip", po::value<std::string>(), "compresses a file (<arg>) in gzip format, stores it as <arg>.gz and removes <arg>." IMPLY_TERMINAL)
195  ("help,h", "prints this message and exits." IMPLY_TERMINAL)
196  ("language,L", po::value<std::string>(), "uses language <arg> (symbol) this session. Example: --language ang_GB@latin")
197  ("load,l", po::value<std::string>(), "loads the save <arg> from the standard save game directory. When launching the map editor via -e, the map <arg> is loaded, relative to the current directory. If it is a directory, the editor will start with a load map dialog opened there.")
198  ("noaddons", "disables the loading of all add-ons.")
199  ("nocache", "disables caching of game data.")
200  ("nodelay", "runs the game without any delays.")
201  ("nomusic", "runs the game without music.")
202  ("nosound", "runs the game without sounds and music.")
203  ("password", po::value<std::string>(), "uses <password> when connecting to a server, ignoring other preferences.")
204  ("plugin", po::value<std::string>(), "(experimental) load a script which defines a wesnoth plugin. similar to --script below, but Lua file should return a function which will be run as a coroutine and periodically woken up with updates.")
205  ("render-image", po::value<two_strings>()->multitoken(), "takes two arguments: <image> <output>. Like screenshot, but instead of a map, takes a valid Wesnoth 'image path string' with image path functions, and writes it to a .png file." IMPLY_TERMINAL)
206  ("report,R", "initializes game directories, prints build information suitable for use in bug reports, and exits." IMPLY_TERMINAL)
207  ("rng-seed", po::value<unsigned int>(), "seeds the random number generator with number <arg>. Example: --rng-seed 0")
208  ("screenshot", po::value<two_strings>()->multitoken(), "takes two arguments: <map> <output>. Saves a screenshot of <map> to <output> without initializing a screen. Editor must be compiled in for this to work." IMPLY_TERMINAL)
209  ("script", po::value<std::string>(), "(experimental) file containing a Lua script to control the client")
210  ("server,s", po::value<std::string>()->implicit_value(std::string()), "connects to the host <arg> if specified or to the first host in your preferences.")
211  ("strict-validation", "makes validation errors fatal")
212  ("translations-over", po::value<unsigned int>(), "Specify the standard for determining whether a translation is complete.")
213  ("unsafe-scripts", "makes the \'package\' package available to Lua scripts, so that they can load arbitrary packages. Do not do this with untrusted scripts! This action gives ua the same permissions as the Wesnoth executable.")
214  ("usercache-dir", po::value<std::string>(), "sets the path of the cache directory to $HOME/<arg> or My Documents\\My Games\\<arg> for Windows. You can specify also an absolute path outside the $HOME or My Documents\\My Games directory. Defaults to $HOME/.cache/wesnoth on X11 and to the userdata-dir on other systems.")
215  ("usercache-path", "prints the path of the cache directory and exits.")
216  ("userconfig-dir", po::value<std::string>(), "sets the path of the user config directory to $HOME/<arg> or My Documents\\My Games\\<arg> for Windows. You can specify also an absolute path outside the $HOME or My Documents\\My Games directory. Defaults to $HOME/.config/wesnoth on X11 and to the userdata-dir on other systems.")
217  ("userconfig-path", "prints the path of the user config directory and exits.")
218  ("userdata-dir", po::value<std::string>(), "sets the path of the userdata directory to $HOME/<arg> or My Documents\\My Games\\<arg> for Windows. You can specify also an absolute path outside the $HOME or My Documents\\My Games directory.")
219  ("userdata-path", "prints the path of the userdata directory and exits." IMPLY_TERMINAL)
220  ("username", po::value<std::string>(), "uses <username> when connecting to a server, ignoring other preferences.")
221  ("validcache", "assumes that the cache is valid. (dangerous)")
222  ("version,v", "prints the game's version number and exits." IMPLY_TERMINAL)
223  ("simple-version", "prints the game's version number and nothing else." IMPLY_TERMINAL)
224  ("with-replay", "replays the file loaded with the --load option.")
225  ;
226 
227  po::options_description campaign_opts("Campaign options");
228  campaign_opts.add_options()
229  ("campaign,c", po::value<std::string>()->implicit_value(std::string()), "goes directly to the campaign with id <arg>. A selection menu will appear if no id was specified.")
230  ("campaign-difficulty", po::value<int>(), "The difficulty of the specified campaign (1 to max). If none specified, the campaign difficulty selection widget will appear.")
231  ("campaign-scenario", po::value<std::string>(),"The id of the scenario from the specified campaign. The default is the first scenario.")
232  ("campaign-skip-story", "Skip [story] tags of the specified campaign.")
233  ;
234 
235  po::options_description display_opts("Display options");
236  display_opts.add_options()
237  ("fps", "displays the number of frames per second the game is currently running at, in a corner of the screen. Min/avg/max don't take the FPS limiter into account, act does.")
238  ("fullscreen,f", "runs the game in full screen mode.")
239  ("max-fps", po::value<int>(), "the maximum fps the game tries to run at. Values should be between 1 and 1000, the default is the display's refresh rate.")
240  ("new-widgets", "there is a new WIP widget toolkit this switch enables the new toolkit (VERY EXPERIMENTAL don't file bug reports since most are known). Parts of the library are deemed stable and will work without this switch.")
241  ("resolution,r", po::value<std::string>(), "sets the screen resolution. <arg> should have format XxY. Example: --resolution 800x600")
242  ("windowed,w", "runs the game in windowed mode.")
243  ;
244 
245  po::options_description logging_opts("Logging options");
246  logging_opts.add_options()
247  ("logdomains", po::value<std::string>()->implicit_value(std::string()), "lists defined log domains (only the ones containing <arg> filter if such is provided) and exits." IMPLY_TERMINAL)
248  ("log-error", po::value<std::vector<std::string>>()->composing(), "sets the severity level of the specified log domain(s) to 'error'. <arg> should be given as a comma-separated list of domains, wildcards are allowed. Example: --log-error=network,gui/*,engine/enemies")
249  ("log-warning", po::value<std::vector<std::string>>()->composing(), "sets the severity level of the specified log domain(s) to 'warning'. Similar to --log-error.")
250  ("log-info", po::value<std::vector<std::string>>()->composing(), "sets the severity level of the specified log domain(s) to 'info'. Similar to --log-error.")
251  ("log-debug", po::value<std::vector<std::string>>()->composing(), "sets the severity level of the specified log domain(s) to 'debug'. Similar to --log-error.")
252  ("log-none", po::value<std::vector<std::string>>()->composing(), "sets the severity level of the specified log domain(s) to 'none'. Similar to --log-error.")
253  ("log-precise", "shows the timestamps in log output with more precision.")
254  ("no-log-to-file", "log output is written only to standard error rather than to a file. The environment variable WESNOTH_NO_LOG_FILE can also be set as an alternative.")
255  ("log-to-file", "log output is written to a file. Cancels the effect of --no-log-to-file whether implicit or explicit.")
256  ("wnoconsole", "For Windows, when used with --no-log-to-file, results in output being written to cerr/cout instead of CONOUT. Otherwise, does nothing.")
257  ;
258 
259  po::options_description multiplayer_opts("Multiplayer options");
260  multiplayer_opts.add_options()
261  ("multiplayer,m", "Starts a multiplayer game. There are additional options that can be used as explained below:")
262  ("ai-config", po::value<std::vector<std::string>>()->composing(), "selects a configuration file to load for this side. <arg> should have format side:value")
263  ("algorithm", po::value<std::vector<std::string>>()->composing(), "selects a non-standard algorithm to be used by the AI controller for this side. <arg> should have format side:value")
264  ("controller", po::value<std::vector<std::string>>()->composing(), "selects the controller for this side. <arg> should have format side:value")
265  ("era", po::value<std::string>(), "selects the era to be played in by its id.")
266  ("exit-at-end", "exit Wesnoth at the end of the scenario.")
267  ("ignore-map-settings", "do not use map settings.")
268  ("label", po::value<std::string>(), "sets the label for AIs.") // TODO: is the description precise? this option was undocumented before.
269  ("multiplayer-repeat", po::value<unsigned int>(), "repeats a multiplayer game after it is finished <arg> times.")
270  ("nogui", "runs the game without the GUI." IMPLY_TERMINAL)
271  ("parm", po::value<std::vector<std::string>>()->composing(), "sets additional parameters for this side. <arg> should have format side:name:value.")
272  ("scenario", po::value<std::string>(), "selects a multiplayer scenario. The default scenario is \"multiplayer_The_Freelands\".")
273  ("side", po::value<std::vector<std::string>>()->composing(), "selects a faction of the current era for this side by id. <arg> should have format side:value.")
274  ("turns", po::value<std::string>(), "sets the number of turns. By default no turn limit is set.")
275  ;
276 
277  po::options_description testing_opts("Testing options");
278  testing_opts.add_options()
279  ("test,t", po::value<std::string>()->implicit_value(std::string()), "runs the game in a small test scenario. If specified, scenario <arg> will be used instead.")
280  ("unit,u", po::value<std::vector<std::string>>(), "runs a unit test scenario. The GUI is not shown and the exit code of the program reflects the victory / defeat conditions of the scenario.\n\t0 - PASS\n\t1 - FAIL\n\t3 - FAIL (INVALID REPLAY)\n\t4 - FAIL (ERRORED REPLAY)\n\t5 - FAIL (BROKE STRICT)\n\t6 - FAIL (WML EXCEPTION)\n\tMultiple tests can be run by giving this option multiple times, in this case the test run will stop immediately after any test which doesn't PASS and the return code will be the status of the test that caused the stop." IMPLY_TERMINAL)
281  ("showgui", "don't run headlessly (for debugging a failing test)")
282  ("log-strict", po::value<std::string>(), "sets the strict level of the logger. any messages sent to log domains of this level or more severe will cause the unit test to fail regardless of the victory result.")
283  ("nobanner", "suppress startup banner.")
284  ("noreplaycheck", "don't try to validate replay of unit test.")
285  ("mp-test", "load the test mp scenarios.")
286  ;
287 
288  po::options_description parsing_opts("WML parsing options");
289  parsing_opts.add_options()
290  ("use-schema,S", po::value<std::string>(), "specify a schema to validate WML against (defaults to the core schema).")
291  ("validate,V", po::value<std::string>(), "validate a specified WML file against a schema." IMPLY_TERMINAL)
292  ("validate-addon", po::value<std::string>()->value_name("addon_id"), "validate the specified addon's WML against the schema. Requires the user to play the campaign (in the GUI) to trigger the validation.")
293  ("validate-core", "validate the core WML against the schema.")
294  ("validate-schema", po::value<std::string>(), "validate a specified WML schema." IMPLY_TERMINAL)
295  ("diff,D", po::value<two_strings>()->multitoken(), "diff two preprocessed WML documents." IMPLY_TERMINAL)
296  ("output,o", po::value<std::string>(), "output to specified file")
297  ("patch,P", po::value<two_strings>()->multitoken(), "apply a patch to a preprocessed WML document." IMPLY_TERMINAL)
298  ("preprocess,p", po::value<two_strings>()->multitoken(), "requires two arguments: <file/folder> <target directory>. Preprocesses a specified file/folder. The preprocessed file(s) will be written in the specified target directory: a plain cfg file and a processed cfg file." IMPLY_TERMINAL)
299  ("preprocess-defines", po::value<std::string>(), "comma separated list of defines to be used by '--preprocess' command. If 'SKIP_CORE' is in the define list the data/core won't be preprocessed. Example: --preprocess-defines=FOO,BAR")
300  ("preprocess-input-macros", po::value<std::string>(), "used only by the '--preprocess' command. Specifies source file <arg> that contains [preproc_define]s to be included before preprocessing.")
301  ("preprocess-output-macros", po::value<std::string>()->implicit_value(std::string()), "used only by the '--preprocess' command. Will output all preprocessed macros in the target file <arg>. If the file is not specified the output will be file '_MACROS_.cfg' in the target directory of preprocess's command.")
302  ;
303 
304  //hidden_.add_options()
305  // ("example-hidden-option", "")
306  // ;
307  visible_.add(general_opts).add(campaign_opts).add(display_opts).add(logging_opts).add(multiplayer_opts).add(testing_opts).add(parsing_opts);
308 
309  all_.add(visible_).add(hidden_);
310 
311  po::positional_options_description positional;
312  positional.add("data-dir",1);
313 
314  po::variables_map vm;
315  const int parsing_style = po::command_line_style::default_style ^ po::command_line_style::allow_guessing;
316 
317  const auto parsed_options = po::command_line_parser(args_)
318  .options(all_)
319  .positional(positional)
320  .style(parsing_style)
321  .run();
322 
323  po::store(parsed_options, vm);
324 
325  if(vm.count("ai-config"))
326  multiplayer_ai_config = parse_to_uint_string_tuples_(vm["ai-config"].as<std::vector<std::string>>());
327  if(vm.count("algorithm"))
328  multiplayer_algorithm = parse_to_uint_string_tuples_(vm["algorithm"].as<std::vector<std::string>>());
329  if(vm.count("bunzip2"))
330  bunzip2 = vm["bunzip2"].as<std::string>();
331  if(vm.count("bzip2"))
332  bzip2 = vm["bzip2"].as<std::string>();
333  if(vm.count("campaign"))
334  campaign = vm["campaign"].as<std::string>();
335  if(vm.count("campaign-difficulty"))
336  campaign_difficulty = vm["campaign-difficulty"].as<int>();
337  if(vm.count("campaign-scenario"))
338  campaign_scenario = vm["campaign-scenario"].as<std::string>();
339  if(vm.count("campaign-skip-story"))
340  campaign_skip_story = true;
341  if(vm.count("clock"))
342  clock = true;
343  if(vm.count("core"))
344  core_id = vm["core"].as<std::string>();
345  if(vm.count("config-dir"))
346  userdata_dir = vm["config-dir"].as<std::string>(); //TODO: complain and remove
347  if(vm.count("config-path"))
348  userdata_path = true; //TODO: complain and remove
349  if(vm.count("controller"))
350  multiplayer_controller = parse_to_uint_string_tuples_(vm["controller"].as<std::vector<std::string>>());
351  if(vm.count("data-dir"))
352  data_dir = vm["data-dir"].as<std::string>();
353  if(vm.count("data-path"))
354  data_path = true;
355  if(vm.count("debug"))
356  debug = true;
357  if(vm.count("debug-lua"))
358  debug_lua = true;
359  if(vm.count("strict-lua"))
360  strict_lua = true;
361  if(vm.count("allow-insecure"))
362  allow_insecure = true;
363 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
364  if(vm.count("debug-dot-domain")) {
365  debug_dot_domain = vm["debug-dot-domain"].as<std::string>();
366  }
367  if(vm.count("debug-dot-level")) {
368  debug_dot_level = vm["debug-dot-level"].as<std::string>();
369  }
370 #endif
371  if(vm.count("editor"))
372  editor = vm["editor"].as<std::string>();
373  if(vm.count("era"))
374  multiplayer_era = vm["era"].as<std::string>();
375  if(vm.count("exit-at-end"))
377  if(vm.count("fps"))
378  fps = true;
379  if(vm.count("fullscreen"))
380  fullscreen = true;
381  if(vm.count("gunzip"))
382  gunzip = vm["gunzip"].as<std::string>();
383  if(vm.count("gzip"))
384  gzip = vm["gzip"].as<std::string>();
385  if(vm.count("help"))
386  help = true;
387  if(vm.count("ignore-map-settings"))
389  if(vm.count("label"))
390  multiplayer_label = vm["label"].as<std::string>();
391  if(vm.count("language"))
392  language = vm["language"].as<std::string>();
393  if(vm.count("load"))
394  load = vm["load"].as<std::string>();
395  if(vm.count("logdomains"))
396  logdomains = vm["logdomains"].as<std::string>();
397  if(vm.count("log-precise"))
398  log_precise_timestamps = true;
399  if(vm.count("log-strict"))
400  parse_log_strictness(vm["log-strict"].as<std::string>());
401  if(vm.count("max-fps"))
402  max_fps = vm["max-fps"].as<int>();
403  if(vm.count("mp-test"))
404  mptest = true;
405  if(vm.count("multiplayer"))
406  multiplayer = true;
407  if(vm.count("multiplayer-repeat"))
408  multiplayer_repeat = vm["multiplayer-repeat"].as<unsigned int>();
409  if(vm.count("new-widgets"))
410  new_widgets = true;
411  if(vm.count("noaddons"))
412  noaddons = true;
413  if(vm.count("nocache"))
414  nocache = true;
415  if(vm.count("nodelay"))
416  nodelay = true;
417  if(vm.count("nomusic"))
418  nomusic = true;
419  if(vm.count("noreplaycheck"))
420  noreplaycheck = true;
421  if(vm.count("nosound"))
422  nosound = true;
423  if(vm.count("nogui"))
424  nogui = true;
425  if(vm.count("nobanner"))
426  nobanner = true;
427  if(vm.count("parm"))
428  multiplayer_parm = parse_to_uint_string_string_tuples_(vm["parm"].as<std::vector<std::string>>());
429  if(vm.count("preprocess"))
430  {
431  preprocess = true;
432  preprocess_path = vm["preprocess"].as<two_strings>().first;
433  preprocess_target = vm["preprocess"].as<two_strings>().second;
434  }
435  if(vm.count("diff"))
436  {
437  do_diff = true;
438  diff_left = vm["diff"].as<two_strings>().first;
439  diff_right = vm["diff"].as<two_strings>().second;
440  }
441  if(vm.count("patch"))
442  {
443  do_patch = true;
444  diff_left = vm["patch"].as<two_strings>().first;
445  diff_right = vm["patch"].as<two_strings>().second;
446  }
447  if(vm.count("output"))
448  {
449  output_file = vm["output"].as<std::string>();
450  }
451  if(vm.count("preprocess-defines"))
452  preprocess_defines = utils::split(vm["preprocess-defines"].as<std::string>(), ',');
453  if(vm.count("preprocess-input-macros"))
454  preprocess_input_macros = vm["preprocess-input-macros"].as<std::string>();
455  if(vm.count("preprocess-output-macros"))
456  preprocess_output_macros = vm["preprocess-output-macros"].as<std::string>();
457  if(vm.count("resolution"))
458  parse_resolution_(vm["resolution"].as<std::string>());
459  if(vm.count("rng-seed"))
460  rng_seed = vm["rng-seed"].as<unsigned int>();
461  if(vm.count("scenario"))
462  multiplayer_scenario = vm["scenario"].as<std::string>();
463  if(vm.count("render-image"))
464  {
465  render_image = vm["render-image"].as<two_strings>().first;
466  render_image_dst = vm["render-image"].as<two_strings>().second;
467  }
468  if(vm.count("screenshot"))
469  {
470  screenshot = true;
471  screenshot_map_file = vm["screenshot"].as<two_strings>().first;
472  screenshot_output_file = vm["screenshot"].as<two_strings>().second;
473  }
474  if(vm.count("script"))
475  script_file = vm["script"].as<std::string>();
476  if(vm.count("unsafe-scripts"))
477  script_unsafe_mode = true;
478  if(vm.count("plugin"))
479  plugin_file = vm["plugin"].as<std::string>();
480  if(vm.count("server"))
481  server = vm["server"].as<std::string>();
482  if(vm.count("username"))
483  username = vm["username"].as<std::string>();
484  if(vm.count("password"))
485  password = vm["password"].as<std::string>();
486  if(vm.count("report"))
487  report = true;
488  if(vm.count("side"))
489  multiplayer_side = parse_to_uint_string_tuples_(vm["side"].as<std::vector<std::string>>());
490  if(vm.count("test"))
491  test = vm["test"].as<std::string>();
492  if(vm.count("unit"))
493  {
494  unit_test = vm["unit"].as<std::vector<std::string>>();
495  headless_unit_test = true;
496  }
497  if(vm.count("showgui"))
498  headless_unit_test = false;
499  if(vm.count("noreplaycheck"))
500  noreplaycheck = true;
501  if(vm.count("turns"))
502  multiplayer_turns = vm["turns"].as<std::string>();
503  if(vm.count("strict-validation"))
504  strict_validation = true;
505  if(vm.count("usercache-dir"))
506  usercache_dir = vm["usercache-dir"].as<std::string>();
507  if(vm.count("usercache-path"))
508  usercache_path = true;
509  if(vm.count("userconfig-dir"))
510  userconfig_dir = vm["userconfig-dir"].as<std::string>();
511  if(vm.count("userconfig-path"))
512  userconfig_path = true;
513  if(vm.count("userdata-dir"))
514  userdata_dir = vm["userdata-dir"].as<std::string>();
515  if(vm.count("userdata-path"))
516  userdata_path = true;
517  if(vm.count("validcache"))
518  validcache = true;
519  // If you add a new validate-* option, remember the any_validation_option() function
520  if(vm.count("validate"))
521  validate_wml = vm["validate"].as<std::string>();
522  if(vm.count("validate-core"))
523  validate_core = true;
524  if(vm.count("validate-addon"))
525  validate_addon = vm["validate-addon"].as<std::string>();
526  if(vm.count("validate-schema"))
527  validate_schema = vm["validate-schema"].as<std::string>();
528  // If you add a new validate-* option, remember the any_validation_option() function
529  if(vm.count("use-schema"))
530  validate_with = vm["use-schema"].as<std::string>();
531  if(vm.count("version"))
532  version = true;
533  if(vm.count("simple-version"))
534  {
535  simple_version = true;
536  nobanner = true;
537  }
538  if(vm.count("windowed"))
539  windowed = true;
540  if(vm.count("with-replay"))
541  with_replay = true;
542  if(vm.count("all-translations"))
544  else if(vm.count("translations-over"))
545  translation_percent = std::clamp<unsigned int>(vm["translations-over"].as<unsigned int>(), 0, 100);
546 
547  // Parse log domain severity following the command line order.
548  for (const auto& option : parsed_options.options) {
549  if (!option.value.empty()) {
550  if (option.string_key == "log-error") {
551  parse_log_domains_(option.value.front(),lg::err().get_severity());
552  } else if (option.string_key == "log-warning") {
553  parse_log_domains_(option.value.front(),lg::warn().get_severity());
554  } else if (option.string_key == "log-info") {
555  parse_log_domains_(option.value.front(),lg::info().get_severity());
556  } else if (option.string_key == "log-debug") {
557  parse_log_domains_(option.value.front(),lg::debug().get_severity());
558  } else if (option.string_key == "log-none") {
559  parse_log_domains_(option.value.front(),lg::severity::LG_NONE);
560  }
561  }
562  }
563 }
564 
565 void commandline_options::parse_log_domains_(const std::string &domains_string, const lg::severity severity)
566 {
567  if(std::vector<std::string> domains = utils::split(domains_string, ','); !domains.empty()) {
568  if(!log) {
569  log.emplace();
570  }
571  for(auto&& domain : domains) {
572  log->emplace_back(severity, std::move(domain));
573  }
574  }
575 }
576 
578  static const std::array<const lg::logger*, 4> loggers {{&lg::err(), &lg::warn(), &lg::info(), &lg::debug()}};
579  for(const lg::logger * l : loggers ) {
580  if(severity == l->get_name()) {
582  return;
583  }
584  }
585  PLAIN_LOG << "Unrecognized argument to --log-strict : " << severity << " . \nDisabling strict mode logging.";
587 }
588 
589 void commandline_options::parse_resolution_ (const std::string& resolution_string)
590 {
591  const std::vector<std::string> tokens = utils::split(resolution_string, 'x');
592  if(tokens.size() != 2) {
593  throw bad_commandline_resolution(resolution_string);
594  }
595 
596  int xres, yres;
597 
598  try {
599  xres = std::stoi(tokens[0]);
600  yres = std::stoi(tokens[1]);
601  } catch(const std::invalid_argument &) {
602  throw bad_commandline_resolution(resolution_string);
603  }
604 
605  resolution = std::pair(xres, yres);
606 }
607 
608 std::vector<std::pair<unsigned int,std::string>> commandline_options::parse_to_uint_string_tuples_(const std::vector<std::string> &strings, char separator)
609 {
610  std::vector<std::pair<unsigned int,std::string>> vec;
611 
612  using namespace std::literals;
613  const std::string expected_format
614  = "UINT"s + separator + "STRING";
615 
616  for(const std::string &s : strings) {
617  std::vector<std::string> tokens = utils::split(s, separator);
618  if(tokens.size() != 2) {
619  throw bad_commandline_tuple(s, expected_format);
620  }
621 
622  unsigned int temp;
623  try {
624  temp = lexical_cast<unsigned int>(tokens[0]);
625  } catch (const bad_lexical_cast &) {
626  throw bad_commandline_tuple(s, expected_format);
627  }
628 
629  vec.emplace_back(temp, std::move(tokens[1]));
630  }
631  return vec;
632 }
633 
634 std::vector<std::tuple<unsigned int,std::string,std::string>> commandline_options::parse_to_uint_string_string_tuples_(const std::vector<std::string> &strings, char separator)
635 {
636  std::vector<std::tuple<unsigned int,std::string,std::string>> vec;
637 
638  using namespace std::literals;
639  const std::string expected_format
640  = "UINT"s + separator + "STRING" + separator + "STRING";
641 
642  for(const std::string &s : strings) {
643  const std::vector<std::string> tokens = utils::split(s, separator);
644  if(tokens.size() != 3) {
645  throw bad_commandline_tuple(s, expected_format);
646  }
647 
648  unsigned int temp;
649  try {
650  temp = lexical_cast<unsigned int>(tokens[0]);
651  } catch (const bad_lexical_cast &) {
652  throw bad_commandline_tuple(s, expected_format);
653  }
654 
655  vec.emplace_back(temp, tokens[1], tokens[2]);
656  }
657  return vec;
658 }
659 
660 std::ostream& operator<<(std::ostream &os, const commandline_options& cmdline_opts)
661 {
662  os << "Usage: " << cmdline_opts.args0_ << " [<options>] [<data-directory>]\n";
663  os << cmdline_opts.visible_;
664  return os;
665 }
666 
668  config ret;
669  if(server) {
670  ret["server"] = *server;
671  }
672  if(username) {
673  ret["username"] = *username;
674  }
675  if(password) {
676  ret["password"] = *password;
677  }
678  return ret;
679 }
680 
682 {
684 }
#define debug(x)
bad_commandline_resolution(const std::string &resolution)
bad_commandline_tuple(const std::string &str, const std::string &expected_format)
std::optional< std::string > preprocess_input_macros
Non-empty if –preprocess-input-macros was given on the command line.
std::optional< std::string > render_image_dst
Output file to put rendered image path in.
std::optional< std::string > validate_addon
Non-empty if –validate-addon was given on the command line.
bool nogui
True if –nogui was given on the command line.
std::optional< std::string > server
Non-empty if –server was given on the command line.
std::optional< std::string > plugin_file
File to load a lua plugin (similar to a script) from.
bool simple_version
True if –simple-version was given on the command line.
bool report
True if –report was given on the command line.
boost::program_options::options_description hidden_
bool headless_unit_test
True if –unit is used and –showgui is not present.
std::optional< std::string > validate_wml
Non-empty if –validate was given on the command line.
std::optional< unsigned int > multiplayer_repeat
Repeats specified by –multiplayer-repeat option.
std::optional< std::string > multiplayer_turns
Non-empty if –turns was given on the command line.
bool windowed
True if –windowed was given on the command line.
bool strict_lua
True if –strict-lua was given in the commandline.
std::optional< std::string > preprocess_path
Path to parse that was given to the –preprocess option.
std::optional< std::string > bzip2
Non-empty if –bzip2 was given on the command line.
std::vector< std::pair< unsigned int, std::string > > parse_to_uint_string_tuples_(const std::vector< std::string > &strings, char separator=':')
A helper function splitting vector of strings of format unsigned int:string to vector of tuples (unsi...
std::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_controller
Non-empty if –controller was given on the command line.
bool noreplaycheck
True if –noreplaycheck was given on the command line.
void parse_log_domains_(const std::string &domains_string, const lg::severity severity)
std::optional< std::string > multiplayer_label
Non-empty if –label was given on the command line.
bool multiplayer_exit_at_end
True if –exit-at-and was given on the command line.
std::optional< std::string > userdata_dir
Non-empty if –userdata-dir was given on the command line.
std::optional< std::string > render_image
Image path to render.
std::optional< std::string > preprocess_output_macros
Non-empty if –preprocess-output-macros was given on the command line.
std::optional< std::string > logdomains
Non-empty if –logdomains was given on the command line.
std::optional< int > campaign_difficulty
Non-empty if –campaign-difficulty was given on the command line.
std::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_algorithm
Non-empty if –algorithm was given on the command line.
std::optional< unsigned int > translation_percent
Non-empty if –all-translations or –translations-over is given on the command line.
std::optional< std::string > multiplayer_scenario
Non-empty if –scenario was given on the command line.
std::optional< std::string > multiplayer_era
Non-empty if –era was given on the command line.
std::optional< std::string > campaign_scenario
Non-empty if –campaign-scenario was given on the command line.
std::optional< std::string > password
Non-empty if –password was given on the command line.
bool debug
True if –debug was given on the command line.
bool mptest
True if –mp-test was given on the command line.
void parse_log_strictness(const std::string &severity)
std::optional< unsigned int > rng_seed
RNG seed specified by –rng-seed option.
bool nosound
True if –nosound was given on the command line.
bool multiplayer
True if –multiplayer was given on the command line.
bool userconfig_path
True if –userconfig-path was given on the command line.
bool script_unsafe_mode
Whether to load the "package" package for the scripting environment.
std::optional< std::vector< std::tuple< unsigned int, std::string, std::string > > > multiplayer_parm
Non-empty if –parm was given on the command line.
std::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_side
Non-empty if –side was given on the command line.
std::vector< std::string > args_
bool nobanner
True if –nobanner was given on the command line.
std::optional< std::vector< std::pair< unsigned int, std::string > > > multiplayer_ai_config
Non-empty if –ai-config was given on the command line.
bool validcache
True if –validcache was given on the command line.
std::vector< std::tuple< unsigned int, std::string, std::string > > parse_to_uint_string_string_tuples_(const std::vector< std::string > &strings, char separator=':')
A helper function splitting vector of strings of format unsigned int:string:string to vector of tuple...
bool nomusic
True if –nomusic was given on the command line.
bool clock
True if –clock was given on the command line.
bool nodelay
True if –nodelay was given on the command line.
boost::program_options::options_description visible_
std::optional< std::string > username
Non-empty if –username was given on the command line.
std::optional< std::string > gunzip
Non-empty if –gunzip was given on the command line.
bool new_widgets
True if –new-widgets was given on the command line.
bool preprocess
True if –preprocess was given on the command line.
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.
std::optional< std::string > load
Non-empty if –load was given on the command line.
bool allow_insecure
True if –allow-insecure was given in the commandline.
void parse_resolution_(const std::string &resolution_string)
std::optional< std::string > campaign
Non-empty if –campaign was given on the command line.
std::optional< std::string > output_file
Output filename for WML diff or preprocessing.
bool noaddons
True if –noaddons was given on the command line.
bool fps
True if –fps was given on the command line.
std::optional< std::string > preprocess_target
Target (output) path that was given to the –preprocess option.
std::optional< std::string > script_file
File to load lua script from.
commandline_options(const std::vector< std::string > &args)
bool campaign_skip_story
True if –skip-story was given on the command line.
bool with_replay
True if –with-replay was given on the command line.
std::optional< std::string > core_id
Non-empty if –core was given on the command line.
bool screenshot
True if –screenshot 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.
std::optional< std::string > data_dir
Non-empty if –data-dir was given on the command line.
bool multiplayer_ignore_map_settings
True if –ignore-map-settings was given at the command line.
std::optional< std::vector< std::string > > preprocess_defines
Defines that were given to the –preprocess option.
std::optional< std::string > bunzip2
Non-empty if –bunzip2 was given on the command line.
std::optional< std::vector< std::pair< lg::severity, std::string > > > log
Contains parsed arguments of –log-* (e.g.
std::optional< std::string > usercache_dir
Non-empty if –usercache-dir was given on the command line.
bool fullscreen
True if –fullscreen was given on the command line.
friend std::ostream & operator<<(std::ostream &os, const commandline_options &cmdline_opts)
To be used for printing help to the commandline.
std::optional< std::string > test
Non-empty if –test was given on the command line.
bool any_validation_option() const
True if the –validate or any of the –validate-* options are given.
std::optional< std::string > userconfig_dir
Non-empty if –userconfig-dir was given on the command line.
std::optional< std::string > language
Non-empty if –language was given on the command line.
bool strict_validation
True if –strict-validation was given on the command line.
bool validate_core
True if –validate-core was given on the command line.
std::optional< int > max_fps
Max FPS specified by –max-fps option.
std::optional< std::pair< int, int > > resolution
Pair of AxB values specified after –resolution.
std::optional< std::string > validate_with
Non-empty if –use-schema was given on the command line.
bool usercache_path
True if –usercache-path was given on the command line.
bool nocache
True if –nocache was given on the command line.
boost::program_options::options_description all_
std::optional< std::string > screenshot_map_file
Map file to make a screenshot of.
std::optional< std::string > screenshot_output_file
Output file to put screenshot in.
std::optional< std::string > gzip
Non-empty if –gzip was given on the command line.
std::optional< std::string > validate_schema
Non-empty if –validate-schema was given on the command line.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
std::ostringstream wrapper.
Definition: formatter.hpp:40
severity get_severity() const
Definition: log.hpp:216
#define IMPLY_TERMINAL
static void validate(boost::any &v, const std::vector< std::string > &values, two_strings *, int)
New lexcical_cast header.
#define PLAIN_LOG
Definition: log.hpp:295
Manage the empty-palette in the editor.
Definition: action.cpp:31
bool allow_insecure
Definition: game_config.cpp:74
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:36
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:23
Definition: help.cpp:53
logger & err()
Definition: log.cpp:302
severity
Definition: log.hpp:82
logger & debug()
Definition: log.cpp:320
logger & warn()
Definition: log.cpp:308
static domain_map * domains
Definition: log.cpp:297
logger & info()
Definition: log.cpp:314
void set_strict_severity(severity severity)
Definition: log.cpp:385
std::string password(const std::string &server, const std::string &login)
point resolution()
Definition: general.cpp:392
bool fullscreen()
Definition: general.cpp:434
std::string core_id()
Definition: general.cpp:332
std::string language()
Definition: general.cpp:535
std::vector< std::string > split(const config_attribute_value &val)
Thrown when a lexical_cast fails.
static map_location::DIRECTION s