1 /*
2    Copyright (C) 2011 - 2018 by Lukasz Dobrogowski <lukasz.dobrogowski@gmail.com>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any lfooater version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 #include "commandline_options.hpp"
16 
17 #include "config.hpp"
18 #include "formatter.hpp"
19 #include "lexical_cast.hpp"
20 #include "log.hpp"                      // for logger, set_strict_severity, etc
21 #include "serialization/string_utils.hpp"  // for split
22 #include "utils/general.hpp" // for clamp
23 
24 #include <boost/any.hpp>                // for any
25 #include <boost/program_options/cmdline.hpp>
26 #include <boost/program_options/errors.hpp>  // for validation_error, etc
27 #include <boost/program_options/parsers.hpp>
28 #include <boost/program_options/positional_options.hpp>
29 #include <boost/program_options/value_semantic.hpp>  // for value, etc
30 #include <boost/program_options/variables_map.hpp>  // for variables_map, etc
31 #include <iostream>                     // for operator<<, basic_ostream, etc
32 
33 namespace po = boost::program_options;
34 
35 class two_strings : public std::pair<std::string,std::string> {};
36 
validate(boost::any & v,const std::vector<std::string> & values,two_strings *,int)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.at(0);
45 	ret_val.second = values.at(1);
46 	v = ret_val;
47 }
48 
bad_commandline_resolution(const std::string & resolution)49 bad_commandline_resolution::bad_commandline_resolution(const std::string& resolution)
50 	: error(formatter() << "Invalid resolution \"" << resolution
51 						 << "\" (WIDTHxHEIGHT expected)")
52 {
53 }
54 
bad_commandline_tuple(const std::string & str,const std::string & expected_format)55 bad_commandline_tuple::bad_commandline_tuple(const std::string& str,
56 											 const std::string& expected_format)
57 	: error(formatter() << "Invalid value set \"" << str
58 						 << "\" (" << expected_format << " expected)")
59 {
60 }
61 
commandline_options(const std::vector<std::string> & args)62 commandline_options::commandline_options (const std::vector<std::string>& args) :
63 	bunzip2(),
64 	bzip2(),
65 	campaign(),
66 	campaign_difficulty(),
67 	campaign_scenario(),
68 	campaign_skip_story(false),
69 	clock(false),
70 	data_path(false),
71 	data_dir(),
72 	debug(false),
73 	debug_lua(false),
74 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
75 	debug_dot_domain(),
76 	debug_dot_level(),
77 #endif
78 	editor(),
79 	fps(false),
80 	fullscreen(false),
81 	gunzip(),
82 	gzip(),
83 	help(),
84 	language(),
85 	log(),
86 	load(),
87 	logdomains(),
88 	log_precise_timestamps(false),
89 	multiplayer(false),
90 	multiplayer_ai_config(),
91 	multiplayer_algorithm(),
92 	multiplayer_controller(),
93 	multiplayer_era(),
94 	multiplayer_exit_at_end(),
95 	multiplayer_ignore_map_settings(),
96 	multiplayer_label(),
97 	multiplayer_parm(),
98 	multiplayer_repeat(),
99 	multiplayer_scenario(),
100 	multiplayer_side(),
101 	multiplayer_turns(),
102 	max_fps(),
103 	noaddons(false),
104 	nocache(false),
105 	nodelay(false),
106 	nogui(false),
107 	nomusic(false),
108 	nosound(false),
109 	new_widgets(false),
110 	preprocess(false),
111 	preprocess_defines(),
112 	preprocess_input_macros(),
113 	preprocess_output_macros(),
114 	preprocess_path(),
115 	preprocess_target(),
116 	resolution(),
117 	rng_seed(),
118 	server(),
119 	username(),
120 	password(),
121 	screenshot(false),
122 	screenshot_map_file(),
123 	screenshot_output_file(),
124 	script_unsafe_mode(false),
125 	strict_validation(false),
126 	test(),
127 	unit_test(),
128 	headless_unit_test(false),
129 	noreplaycheck(false),
130 	mptest(false),
131 	userconfig_path(false),
132 	userconfig_dir(),
133 	userdata_path(false),
134 	userdata_dir(),
135 	validcache(false),
136 	version(false),
137 	report(false),
138 	windowed(false),
139 	with_replay(false),
140 	args_(args.begin() + 1 , args.end()),
141 	args0_(*args.begin()),
142 	all_(),
143 	visible_(),
144 	hidden_()
145 {
146 	// When adding items don't forget to update doc/man/wesnoth.6
147 	// Options are sorted alphabetically by --long-option.
148 	po::options_description general_opts("General options");
149 	general_opts.add_options()
150 		("all-translations", "Show all translations, even incomplete ones.")
151 		("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.")
152 		("bzip2", po::value<std::string>(), "compresses a file (<arg>) in bzip2 format, stores it as <arg>.bz2 and removes <arg>.")
153 		("clock", "Adds the option to show a clock for testing the drawing timer.")
154 		("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.")
155 		("config-path", "prints the path of the userdata directory and exits. DEPRECATED: use userdata-path instead.")
156 		("core", po::value<std::string>(), "overrides the loaded core with the one whose id is specified.")
157 		("data-dir", po::value<std::string>(), "overrides the data directory with the one specified.")
158 		("data-path", "prints the path of the data directory and exits.")
159 		("debug,d", "enables additional command mode options in-game.")
160 		("debug-lua", "enables some Lua debugging mechanisms")
161 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
162 		("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).")
163 		("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.")
164 #endif
165 		("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>.")
166 		("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.")
167 		("gzip", po::value<std::string>(), "compresses a file (<arg>) in gzip format, stores it as <arg>.gz and removes <arg>.")
168 		("help,h", "prints this message and exits.")
169 		("language,L", po::value<std::string>(), "uses language <arg> (symbol) this session. Example: --language ang_GB@latin")
170 		("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.")
171 		("noaddons", "disables the loading of all add-ons.")
172 		("nocache", "disables caching of game data.")
173 		("nodelay", "runs the game without any delays.")
174 		("nomusic", "runs the game without music.")
175 		("nosound", "runs the game without sounds and music.")
176 		("password", po::value<std::string>(), "uses <password> when connecting to a server, ignoring other preferences.")
177 		("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.")
178 		("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."
179 #ifdef _WIN32
180 		 " Implies --wconsole."
181 #endif // _WIN32
182 		 )
183 		("report,R", "initializes game directories, prints build information suitable for use in bug reports, and exits."
184 #ifdef _WIN32
185 		 " Implies --wconsole."
186 #endif // _WIN32
187 		 )
188 		("rng-seed", po::value<unsigned int>(), "seeds the random number generator with number <arg>. Example: --rng-seed 0")
189 		("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."
190 #ifdef _WIN32
191 		 " Implies --wconsole."
192 #endif // _WIN32
193 		 )
194 		("script", po::value<std::string>(), "(experimental) file containing a Lua script to control the client")
195 		("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.")
196 		("strict-validation", "makes validation errors fatal")
197 		("translations-over", po::value<unsigned int>(), "Specify the standard for determining whether a translation is complete.")
198 		("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.")
199 		("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.")
200 		("userconfig-path", "prints the path of the user config directory and exits.")
201 		("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.")
202 		("userdata-path", "prints the path of the userdata directory and exits.")
203 		("username", po::value<std::string>(), "uses <username> when connecting to a server, ignoring other preferences.")
204 		("validcache", "assumes that the cache is valid. (dangerous)")
205 		("version,v", "prints the game's version number and exits.")
206 		("with-replay", "replays the file loaded with the --load option.")
207 #ifdef _WIN32
208 		("wconsole", "attaches a console window on startup (Windows only). Implied by any option that prints something and exits.")
209 #endif // _WIN32
210 		;
211 
212 	po::options_description campaign_opts("Campaign options");
213 	campaign_opts.add_options()
214 		("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.")
215 		("campaign-difficulty", po::value<int>(), "The difficulty of the specified campaign (1 to max). If none specified, the campaign difficulty selection widget will appear.")
216 		("campaign-scenario", po::value<std::string>(),"The id of the scenario from the specified campaign. The default is the first scenario.")
217 		("campaign-skip-story", "Skip [story] tags of the specified campaign.")
218 		;
219 
220 	po::options_description display_opts("Display options");
221 	display_opts.add_options()
222 		("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.")
223 		("fullscreen,f", "runs the game in full screen mode.")
224 		("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.")
225 		("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.")
226 		("resolution,r", po::value<std::string>(), "sets the screen resolution. <arg> should have format XxY. Example: --resolution 800x600")
227 		("windowed,w", "runs the game in windowed mode.")
228 		;
229 
230 	po::options_description logging_opts("Logging options");
231 	logging_opts.add_options()
232 		("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.")
233 		("log-error", po::value<std::string>(), "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")
234 		("log-warning", po::value<std::string>(), "sets the severity level of the specified log domain(s) to 'warning'. Similar to --log-error.")
235 		("log-info", po::value<std::string>(), "sets the severity level of the specified log domain(s) to 'info'. Similar to --log-error.")
236 		("log-debug", po::value<std::string>(), "sets the severity level of the specified log domain(s) to 'debug'. Similar to --log-error.")
237 		("log-none", po::value<std::string>(), "sets the severity level of the specified log domain(s) to 'none'. Similar to --log-error.")
238 		("log-precise", "shows the timestamps in log output with more precision.")
239 		;
240 
241 	po::options_description multiplayer_opts("Multiplayer options");
242 	multiplayer_opts.add_options()
243 		("multiplayer,m", "Starts a multiplayer game. There are additional options that can be used as explained below:")
244 		("ai-config", po::value<std::vector<std::string>>()->composing(), "selects a configuration file to load for this side. <arg> should have format side:value")
245 		("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")
246 		("controller", po::value<std::vector<std::string>>()->composing(), "selects the controller for this side. <arg> should have format side:value")
247 		("era", po::value<std::string>(), "selects the era to be played in by its id.")
248 		("exit-at-end", "exit Wesnoth at the end of the scenario.")
249 		("ignore-map-settings", "do not use map settings.")
250 		("label", po::value<std::string>(), "sets the label for AIs.") //TODO is the description precise? this option was undocumented before.
251 		("multiplayer-repeat",  po::value<unsigned int>(), "repeats a multiplayer game after it is finished <arg> times.")
252 		("nogui", "runs the game without the GUI.")
253 		("parm", po::value<std::vector<std::string>>()->composing(), "sets additional parameters for this side. <arg> should have format side:name:value.")
254 		("scenario", po::value<std::string>(), "selects a multiplayer scenario. The default scenario is \"multiplayer_The_Freelands\".")
255 		("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.")
256                 ("turns", po::value<std::string>(), "sets the number of turns. By default no turn limit is set.")
257 		;
258 
259 	po::options_description testing_opts("Testing options");
260 	testing_opts.add_options()
261 		("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.")
262 		("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.")
263 		("showgui", "don't run headlessly (for debugging a failing test)")
264 		("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.")
265 		("noreplaycheck", "don't try to validate replay of unit test.")
266 		("mp-test", "load the test mp scenarios.")
267 		;
268 
269 	po::options_description preprocessor_opts("Preprocessor mode options");
270 	preprocessor_opts.add_options()
271 		("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.")
272 		("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")
273 		("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.")
274 		("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.")
275 		;
276 
277 	//hidden_.add_options()
278 	//	("example-hidden-option", "")
279 	//	;
280 	visible_.add(general_opts).add(campaign_opts).add(display_opts).add(logging_opts).add(multiplayer_opts).add(testing_opts).add(preprocessor_opts);
281 
282 	all_.add(visible_).add(hidden_);
283 
284 	po::positional_options_description positional;
285 	positional.add("data-dir",1);
286 
287 	po::variables_map vm;
288 	const int parsing_style = po::command_line_style::default_style ^ po::command_line_style::allow_guessing;
289 	po::store(po::command_line_parser(args_).options(all_).positional(positional).style(parsing_style).run(),vm);
290 
291 	if (vm.count("ai-config"))
292 		multiplayer_ai_config = parse_to_uint_string_tuples_(vm["ai-config"].as<std::vector<std::string>>());
293 	if (vm.count("algorithm"))
294 		multiplayer_algorithm = parse_to_uint_string_tuples_(vm["algorithm"].as<std::vector<std::string>>());
295 	if (vm.count("bunzip2"))
296 		bunzip2 = vm["bunzip2"].as<std::string>();
297 	if (vm.count("bzip2"))
298 		bzip2 = vm["bzip2"].as<std::string>();
299 	if (vm.count("campaign"))
300 		campaign = vm["campaign"].as<std::string>();
301 	if (vm.count("campaign-difficulty"))
302 		campaign_difficulty = vm["campaign-difficulty"].as<int>();
303 	if (vm.count("campaign-scenario"))
304 		campaign_scenario = vm["campaign-scenario"].as<std::string>();
305 	if (vm.count("campaign-skip-story"))
306 		campaign_skip_story = true;
307 	if (vm.count("clock"))
308 		clock = true;
309 	if (vm.count("core"))
310 		core_id = vm["core"].as<std::string>();
311 	if (vm.count("config-dir"))
312 		userdata_dir = vm["config-dir"].as<std::string>(); //TODO: complain and remove
313 	if (vm.count("config-path"))
314 		userdata_path = true; //TODO: complain and remove
315 	if (vm.count("controller"))
316 		multiplayer_controller = parse_to_uint_string_tuples_(vm["controller"].as<std::vector<std::string>>());
317 	if (vm.count("data-dir"))
318 		data_dir = vm["data-dir"].as<std::string>();
319 	if (vm.count("data-path"))
320 		data_path = true;
321 	if (vm.count("debug"))
322 		debug = true;
323 	if (vm.count("debug-lua"))
324 		debug_lua = true;
325 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
326 	if (vm.count("debug-dot-domain")) {
327 		debug_dot_domain = vm["debug-dot-domain"].as<std::string>();
328 	}
329 	if (vm.count("debug-dot-level")) {
330 		debug_dot_level = vm["debug-dot-level"].as<std::string>();
331 	}
332 #endif
333 	if (vm.count("editor"))
334 		editor = vm["editor"].as<std::string>();
335 	if (vm.count("era"))
336 		multiplayer_era = vm["era"].as<std::string>();
337 	if (vm.count("exit-at-end"))
338 		multiplayer_exit_at_end = true;
339 	if (vm.count("fps"))
340 		fps = true;
341 	if (vm.count("fullscreen"))
342 		fullscreen = true;
343 	if (vm.count("gunzip"))
344 		gunzip = vm["gunzip"].as<std::string>();
345 	if (vm.count("gzip"))
346 		gzip = vm["gzip"].as<std::string>();
347 	if (vm.count("help"))
348 		help = true;
349 	if (vm.count("ignore-map-settings"))
350 		multiplayer_ignore_map_settings = true;
351 	if (vm.count("label"))
352 		multiplayer_label = vm["label"].as<std::string>();
353 	if (vm.count("language"))
354 		language = vm["language"].as<std::string>();
355 	if (vm.count("load"))
356 		load = vm["load"].as<std::string>();
357 	if (vm.count("log-error"))
358 		 parse_log_domains_(vm["log-error"].as<std::string>(),lg::err().get_severity());
359 	if (vm.count("log-warning"))
360 		 parse_log_domains_(vm["log-warning"].as<std::string>(),lg::warn().get_severity());
361 	if (vm.count("log-info"))
362 		 parse_log_domains_(vm["log-info"].as<std::string>(),lg::info().get_severity());
363 	if (vm.count("log-debug"))
364 		 parse_log_domains_(vm["log-debug"].as<std::string>(),lg::debug().get_severity());
365 	if (vm.count("log-none"))
366 		 parse_log_domains_(vm["log-none"].as<std::string>(),-1);
367 	if (vm.count("logdomains"))
368 		logdomains = vm["logdomains"].as<std::string>();
369 	if (vm.count("log-precise"))
370 		log_precise_timestamps = true;
371 	if (vm.count("log-strict"))
372 		parse_log_strictness(vm["log-strict"].as<std::string>());
373 	if (vm.count("max-fps"))
374 		max_fps = vm["max-fps"].as<int>();
375 	if (vm.count("mp-test"))
376 		mptest = true;
377 	if (vm.count("multiplayer"))
378 		multiplayer = true;
379 	if (vm.count("multiplayer-repeat"))
380 		multiplayer_repeat = vm["multiplayer-repeat"].as<unsigned int>();
381 	if (vm.count("new-widgets"))
382 		new_widgets = true;
383 	if (vm.count("noaddons"))
384 		noaddons = true;
385 	if (vm.count("nocache"))
386 		nocache = true;
387 	if (vm.count("nodelay"))
388 		nodelay = true;
389 	if (vm.count("nomusic"))
390 		nomusic = true;
391 	if (vm.count("noreplaycheck"))
392 		noreplaycheck = true;
393 	if (vm.count("nosound"))
394 		nosound = true;
395 	if (vm.count("nogui"))
396 		nogui = true;
397 	if (vm.count("parm"))
398 		multiplayer_parm = parse_to_uint_string_string_tuples_(vm["parm"].as<std::vector<std::string>>());
399 	if (vm.count("preprocess"))
400 	{
401 		preprocess = true;
402 		preprocess_path = vm["preprocess"].as<two_strings>().first;
403 		preprocess_target = vm["preprocess"].as<two_strings>().second;
404 	}
405 	if (vm.count("preprocess-defines"))
406 		preprocess_defines = utils::split(vm["preprocess-defines"].as<std::string>(), ',');
407 	if (vm.count("preprocess-input-macros"))
408 		preprocess_input_macros = vm["preprocess-input-macros"].as<std::string>();
409 	if (vm.count("preprocess-output-macros"))
410 		preprocess_output_macros = vm["preprocess-output-macros"].as<std::string>();
411 	if (vm.count("resolution"))
412 		parse_resolution_(vm["resolution"].as<std::string>());
413 	if (vm.count("rng-seed"))
414 		rng_seed = vm["rng-seed"].as<unsigned int>();
415 	if (vm.count("scenario"))
416 		multiplayer_scenario = vm["scenario"].as<std::string>();
417 	if (vm.count("render-image"))
418 	{
419 		render_image = vm["render-image"].as<two_strings>().first;
420 		render_image_dst = vm["render-image"].as<two_strings>().second;
421 	}
422 	if (vm.count("screenshot"))
423 	{
424 		screenshot = true;
425 		screenshot_map_file = vm["screenshot"].as<two_strings>().first;
426 		screenshot_output_file = vm["screenshot"].as<two_strings>().second;
427 	}
428 	if (vm.count("script"))
429 		script_file = vm["script"].as<std::string>();
430 	if (vm.count("unsafe-scripts"))
431 		script_unsafe_mode = true;
432 	if (vm.count("plugin"))
433 		plugin_file = vm["plugin"].as<std::string>();
434 	if (vm.count("server"))
435 		server = vm["server"].as<std::string>();
436 	if (vm.count("username"))
437 		username = vm["username"].as<std::string>();
438 	if (vm.count("password"))
439 		password = vm["password"].as<std::string>();
440 	if (vm.count("report"))
441 		report = true;
442 	if (vm.count("side"))
443 		multiplayer_side = parse_to_uint_string_tuples_(vm["side"].as<std::vector<std::string>>());
444 	if (vm.count("test"))
445 		test = vm["test"].as<std::string>();
446 	if (vm.count("unit"))
447 	{
448 		unit_test = vm["unit"].as<std::vector<std::string>>();
449 		headless_unit_test = true;
450 	}
451 	if (vm.count("showgui"))
452 		headless_unit_test = false;
453 	if (vm.count("noreplaycheck"))
454 		noreplaycheck = true;
455 	if (vm.count("turns"))
456 		multiplayer_turns = vm["turns"].as<std::string>();
457 	if (vm.count("strict-validation"))
458 		strict_validation = true;
459 	if (vm.count("userconfig-dir"))
460 		userconfig_dir = vm["userconfig-dir"].as<std::string>();
461 	if (vm.count("userconfig-path"))
462 		userconfig_path = true;
463 	if (vm.count("userdata-dir"))
464 		userdata_dir = vm["userdata-dir"].as<std::string>();
465 	if (vm.count("userdata-path"))
466 		userdata_path = true;
467 	if (vm.count("validcache"))
468 		validcache = true;
469 	if (vm.count("version"))
470 		version = true;
471 	if (vm.count("windowed"))
472 		windowed = true;
473 	if (vm.count("with-replay"))
474 		with_replay = true;
475 	if(vm.count("all-translations"))
476 		translation_percent = 0;
477 	else if(vm.count("translations-over"))
478 		translation_percent = utils::clamp<unsigned int>(vm["translations-over"].as<unsigned int>(), 0, 100);
479 }
480 
parse_log_domains_(const std::string & domains_string,const int severity)481 void commandline_options::parse_log_domains_(const std::string &domains_string, const int severity)
482 {
483 	const std::vector<std::string> domains = utils::split(domains_string, ',');
484 	for (const std::string& domain : domains)
485 	{
486 		if (!log)
487 			log = std::vector<std::pair<int, std::string>>();
488 		log->emplace_back(severity, domain);
489 	}
490 }
491 
parse_log_strictness(const std::string & severity)492 void commandline_options::parse_log_strictness (const std::string & severity ) {
493 	static lg::logger const *loggers[] { &lg::err(), &lg::warn(), &lg::info(), &lg::debug() };
494 	for (const lg::logger * l : loggers ) {
495 		if (severity == l->get_name()) {
496 			lg::set_strict_severity(*l);
497 			return ;
498 		}
499 	}
500 	std::cerr << "Unrecognized argument to --log-strict : " << severity << " . \nDisabling strict mode logging." << std::endl;
501 	lg::set_strict_severity(-1);
502 }
503 
parse_resolution_(const std::string & resolution_string)504 void commandline_options::parse_resolution_ ( const std::string& resolution_string )
505 {
506 	const std::vector<std::string> tokens = utils::split(resolution_string, 'x');
507 	if (tokens.size() != 2) {
508 		throw bad_commandline_resolution(resolution_string);
509 	}
510 
511 	int xres, yres;
512 
513 	try {
514 		xres = std::stoi(tokens[0]);
515 		yres = std::stoi(tokens[1]);
516 	} catch(const std::invalid_argument &) {
517 		throw bad_commandline_resolution(resolution_string);
518 	}
519 
520 	resolution = std::make_pair(xres, yres);
521 }
522 
parse_to_uint_string_tuples_(const std::vector<std::string> & strings,char separator)523 std::vector<std::pair<unsigned int,std::string>> commandline_options::parse_to_uint_string_tuples_(const std::vector<std::string> &strings, char separator)
524 {
525 	std::vector<std::pair<unsigned int,std::string>> vec;
526 	const std::string& expected_format
527 			= std::string() + "UINT" + separator + "STRING";
528 
529 	for (const std::string &s : strings)
530 	{
531 		const std::vector<std::string> tokens = utils::split(s, separator);
532 		if(tokens.size() != 2) {
533 			throw bad_commandline_tuple(s, expected_format);
534 		}
535 
536 		unsigned int temp;
537 		try {
538 			temp = lexical_cast<unsigned int>(tokens[0]);
539 		} catch (const bad_lexical_cast &) {
540 			throw bad_commandline_tuple(s, expected_format);
541 		}
542 
543 		vec.emplace_back(temp, tokens[1]);
544 	}
545 	return vec;
546 }
547 
parse_to_uint_string_string_tuples_(const std::vector<std::string> & strings,char separator)548 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)
549 {
550 	std::vector<std::tuple<unsigned int,std::string,std::string>> vec;
551 	const std::string& expected_format
552 			= std::string() + "UINT" + separator + "STRING" + separator + "STRING";
553 
554 	for (const std::string &s : strings)
555 	{
556 		const std::vector<std::string> tokens = utils::split(s, separator);
557 		if(tokens.size() != 3) {
558 			throw bad_commandline_tuple(s, expected_format);
559 		}
560 
561 		unsigned int temp;
562 		try {
563 			temp = lexical_cast<unsigned int>(tokens[0]);
564 		} catch (const bad_lexical_cast &) {
565 			throw bad_commandline_tuple(s, expected_format);
566 		}
567 
568 		vec.emplace_back(temp, tokens[1], tokens[2]);
569 	}
570 	return vec;
571 }
572 
operator <<(std::ostream & os,const commandline_options & cmdline_opts)573 std::ostream& operator<<(std::ostream &os, const commandline_options& cmdline_opts)
574 {
575 	os << "Usage: " << cmdline_opts.args0_ << " [<options>] [<data-directory>]\n";
576 	os << cmdline_opts.visible_;
577 	return os;
578 }
579 
to_config() const580 config commandline_options::to_config() const {
581 	config ret;
582 	if (server) {
583 		ret["server"] = *server;
584 	}
585 	if (username) {
586 		ret["username"] = *username;
587 	}
588 	if (password) {
589 		ret["password"] = *password;
590 	}
591 	return ret;
592 }
593