The Battle for Wesnoth  1.19.3+dev
preferences.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2024 - 2024
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Get and set user-preferences.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
23 
24 #include "cursor.hpp"
25 #include "game_board.hpp"
26 #include "game_display.hpp"
27 #include "formula/string_utils.hpp"
28 #include "game_config.hpp"
29 #include "game_data.hpp"
30 #include "gettext.hpp"
34 #include "hotkey/hotkey_item.hpp"
35 #include "log.hpp"
36 #include "map_settings.hpp"
37 #include "map/map.hpp"
38 #include "resources.hpp"
39 #include "serialization/parser.hpp"
40 #include "sound.hpp"
41 #include "units/unit.hpp"
42 #include "video.hpp"
43 
44 #include <sys/stat.h> // for setting the permissions of the preferences file
45 #include <boost/algorithm/string.hpp>
46 
47 #ifdef _WIN32
49 #include <boost/range/iterator_range.hpp>
50 #include <windows.h>
51 #endif
52 
53 #ifndef __APPLE__
54 #include <openssl/evp.h>
55 #include <openssl/err.h>
56 #else
57 #include <CommonCrypto/CommonCryptor.h>
58 #endif
59 
60 static lg::log_domain log_config("config");
61 #define ERR_CFG LOG_STREAM(err , log_config)
62 #define DBG_CFG LOG_STREAM(debug , log_config)
63 
64 static lg::log_domain log_filesystem("filesystem");
65 #define ERR_FS LOG_STREAM(err, log_filesystem)
66 
67 static lg::log_domain advanced_preferences("advanced_preferences");
68 #define ERR_ADV LOG_STREAM(err, advanced_preferences)
69 
71 : preferences_()
72 , fps_(false)
73 , completed_campaigns_()
74 , encountered_units_set_()
75 , encountered_terrains_set_()
76 , history_map_()
77 , acquaintances_()
78 , option_values_()
79 , options_initialized_(false)
80 , mp_modifications_()
81 , mp_modifications_initialized_(false)
82 , sp_modifications_()
83 , sp_modifications_initialized_(false)
84 , message_private_on_(false)
85 , credentials_()
86 , advanced_prefs_()
87 {
90 
91  // make sure this has a default set
92  if(!preferences_.has_attribute("scroll_threshold")) {
93  preferences_[prefs_list::scroll_threshold] = 10;
94  }
95 
96  for(const config& acfg : preferences_.child_range("acquaintance")) {
98  acquaintances_[ac.get_nick()] = ac;
99  }
100 }
101 
103 {
104  config campaigns;
105  for(const auto& elem : completed_campaigns_) {
106  config cmp;
107  cmp["name"] = elem.first;
108  cmp["difficulty_levels"] = utils::join(elem.second);
109  campaigns.add_child("campaign", cmp);
110  }
111 
112  set_child(prefs_list::completed_campaigns, campaigns);
113 
114  preferences_[prefs_list::encountered_units] = utils::join(encountered_units_set_);
116  preferences_[prefs_list::encountered_terrain_list] = t_translation::write_list(terrain);
117 
118  /* Structure of the history
119  [history]
120  [history_id]
121  [line]
122  message = foobar
123  [/line]
124  */
125  config history;
126  for(const auto& history_id : history_map_) {
127  config history_id_cfg; // [history_id]
128  for(const std::string& line : history_id.second) {
129  config cfg; // [line]
130 
131  cfg["message"] = line;
132  history_id_cfg.add_child("line", std::move(cfg));
133  }
134 
135  history.add_child(history_id.first, history_id_cfg);
136  }
137  set_child(prefs_list::history, history);
138 
139  preferences_.clear_children("acquaintance");
140 
141  for(auto& a : acquaintances_) {
142  config& item = preferences_.add_child("acquaintance");
143  a.second.save(item);
144  }
145 
146  history_map_.clear();
147  encountered_units_set_.clear();
149 
150  try {
151  if(!no_preferences_save) {
153  }
154  } catch (...) {
155  ERR_FS << "Failed to write preferences due to exception: " << utils::get_unknown_exception_type();
156  }
157 }
158 
160 {
161  for(const config& pref : gc.child_range("advanced_preference")) {
162  try {
163  advanced_prefs_.emplace_back(pref);
164  } catch(const std::invalid_argument& e) {
165  ERR_ADV << e.what();
166  continue;
167  }
168  }
169 
170  // show_deprecation has a different default on the dev branch
171  if(game_config::wesnoth_version.is_dev_version()) {
173  if(op.field == prefs_list::show_deprecation) {
174  op.cfg["default"] = true;
175  }
176  }
177  }
178 
179  std::sort(advanced_prefs_.begin(), advanced_prefs_.end(), [](const auto& lhs, const auto& rhs) { return translation::icompare(lhs.name, rhs.name) < 0; });
180 }
181 
182 void prefs::migrate_preferences(const std::string& migrate_prefs_file)
183 {
184  if(migrate_prefs_file != filesystem::get_synced_prefs_file() && filesystem::file_exists(migrate_prefs_file)) {
185  // if the file doesn't exist, just copy the file over
186  // else need to merge the preferences file
189  } else {
190  config current_cfg;
192  read(current_cfg, *current_stream);
193  config old_cfg;
194  filesystem::scoped_istream old_stream = filesystem::istream_file(migrate_prefs_file, false);
195  read(old_cfg, *old_stream);
196 
197  // when both files have the same attribute, use the one from whichever was most recently modified
198  bool current_prefs_are_older = filesystem::file_modified_time(filesystem::get_synced_prefs_file()) < filesystem::file_modified_time(migrate_prefs_file);
199  for(const config::attribute& val : old_cfg.attribute_range()) {
200  if(current_prefs_are_older || !current_cfg.has_attribute(val.first)) {
201  preferences_[val.first] = val.second;
202  }
203  }
204 
205  // don't touch child tags
206 
208  }
209  }
210 }
212 {
216 }
217 
218 std::set<std::string> prefs::all_attributes()
219 {
220  std::set<std::string> attrs;
221 
222  // attributes that exist in the preferences file
223  for(const auto& attr : preferences_.attribute_range()) {
224  attrs.emplace(attr.first);
225  }
226  // all mainline preference attributes, whether they're set or not
227  for(const auto attr : prefs_list::values) {
228  attrs.emplace(attr);
229  }
230 
231  return attrs;
232 }
233 
235 {
237  try{
238  config default_prefs;
239  config unsynced_prefs;
240  config synced_prefs;
241 #ifdef DEFAULT_PREFS_PATH
242  // NOTE: the system preferences file is only ever relevant for the first time wesnoth starts
243  // any default values will subsequently be written to the normal preferences files, which takes precedence over any values in the system preferences file
244  {
246  read(default_prefs, *stream);
247  }
248 #endif
249  {
251  read(unsynced_prefs, *stream);
252  }
253 
254  {
256  read(synced_prefs, *stream);
257  }
258 
259  preferences_.merge_with(default_prefs);
260  preferences_.merge_with(unsynced_prefs);
261  preferences_.merge_with(synced_prefs);
262 
263  // check for any unknown preferences
264  for(const auto& attr : synced_prefs.attribute_range()) {
265  if(std::find(synced_attributes_.begin(), synced_attributes_.end(), attr.first) == synced_attributes_.end()) {
266  unknown_synced_attributes_.insert(attr.first);
267  }
268  }
269  for(const auto& attr : unsynced_prefs.attribute_range()) {
270  if(std::find(unsynced_attributes_.begin(), unsynced_attributes_.end(), attr.first) == unsynced_attributes_.end()) {
271  unknown_unsynced_attributes_.insert(attr.first);
272  }
273  }
274 
275  for(const auto child : synced_prefs.all_children_range()) {
276  if(std::find(synced_children_.begin(), synced_children_.end(), child.key) == synced_children_.end()) {
277  unknown_synced_children_.insert(child.key);
278  }
279  }
280  for(const auto child : unsynced_prefs.all_children_range()) {
281  if(std::find(unsynced_children_.begin(), unsynced_children_.end(), child.key) == unsynced_children_.end()) {
282  unknown_unsynced_children_.insert(child.key);
283  }
284  }
285  } catch(const config::error& e) {
286  ERR_CFG << "Error loading preference, message: " << e.what();
287  }
288 
291 
292  /*
293  completed_campaigns = "A,B,C"
294  [completed_campaigns]
295  [campaign]
296  name = "A"
297  difficulty_levels = "EASY,MEDIUM"
298  [/campaign]
299  [/completed_campaigns]
300  */
301  // presumably for backwards compatibility?
302  // nothing actually sets the attribute, only the child tags
303  for(const std::string& c : utils::split(preferences_[prefs_list::completed_campaigns])) {
304  completed_campaigns_[c]; // create the elements
305  }
306 
307  if(auto ccc = get_child(prefs_list::completed_campaigns)) {
308  for(const config& cc : ccc->child_range("campaign")) {
309  std::set<std::string>& d = completed_campaigns_[cc["name"]];
310  std::vector<std::string> nd = utils::split(cc["difficulty_levels"]);
311  std::copy(nd.begin(), nd.end(), std::inserter(d, d.begin()));
312  }
313  }
314 
315  encountered_units_set_ = utils::split_set(preferences_[prefs_list::encountered_units].str());
316 
317  const t_translation::ter_list terrain(t_translation::read_list(preferences_[prefs_list::encountered_terrain_list].str()));
318  encountered_terrains_set_.insert(terrain.begin(), terrain.end());
319 
320  if(auto history = get_child(prefs_list::history)) {
321  /* Structure of the history
322  [history]
323  [history_id]
324  [line]
325  message = foobar
326  [/line]
327  */
328  for(const config::any_child h : history->all_children_range()) {
329  for(const config& l : h.cfg.child_range("line")) {
330  history_map_[h.key].push_back(l["message"]);
331  }
332  }
333  }
334 }
335 
337 {
338 #ifndef _WIN32
339  bool synced_prefs_file_existed = filesystem::file_exists(filesystem::get_synced_prefs_file());
340  bool unsynced_prefs_file_existed = filesystem::file_exists(filesystem::get_unsynced_prefs_file());
341 #endif
342 
343  config synced;
344  config unsynced;
345 
346  for(const char* attr : synced_attributes_) {
347  if(preferences_.has_attribute(attr)) {
348  synced[attr] = preferences_[attr];
349  }
350  }
351  for(const char* attr : synced_children_) {
352  for(const auto& child : preferences_.child_range(attr)) {
353  synced.add_child(attr, child);
354  }
355  }
356 
357  for(const char* attr : unsynced_attributes_) {
358  if(preferences_.has_attribute(attr)) {
359  unsynced[attr] = preferences_[attr];
360  }
361  }
362  for(const char* attr : unsynced_children_) {
363  for(const auto& child : preferences_.child_range(attr)) {
364  unsynced.add_child(attr, child);
365  }
366  }
367 
368  // write any unknown preferences back out
369  for(const std::string& attr : unknown_synced_attributes_) {
370  synced[attr] = preferences_[attr];
371  }
372  for(const std::string& attr : unknown_synced_children_) {
373  for(const auto& child : preferences_.child_range(attr)) {
374  synced.add_child(attr, child);
375  }
376  }
377 
378  for(const std::string& attr : unknown_unsynced_attributes_) {
379  unsynced[attr] = preferences_[attr];
380  }
381  for(const std::string& attr : unknown_unsynced_children_) {
382  for(const auto& child : preferences_.child_range(attr)) {
383  unsynced.add_child(attr, child);
384  }
385  }
386 
387  try {
389  write(*synced_prefs_file, synced);
390  } catch(const filesystem::io_exception&) {
391  ERR_FS << "error writing to synced preferences file '" << filesystem::get_synced_prefs_file() << "'";
392  }
393 
394  try {
396  write(*unsynced_prefs_file, unsynced);
397  } catch(const filesystem::io_exception&) {
398  ERR_FS << "error writing to unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
399  }
400 
402 
403 #ifndef _WIN32
404  if(!synced_prefs_file_existed) {
405  if(chmod(filesystem::get_synced_prefs_file().c_str(), 0600) == -1) {
406  ERR_FS << "error setting permissions of preferences file '" << filesystem::get_synced_prefs_file() << "'";
407  }
408  }
409  if(!unsynced_prefs_file_existed) {
410  if(chmod(filesystem::get_unsynced_prefs_file().c_str(), 0600) == -1) {
411  ERR_FS << "error setting permissions of unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
412  }
413  }
414 #endif
415 }
416 
418 {
419  // Zero them before clearing.
420  // Probably overly paranoid, but doesn't hurt?
421  for(auto& cred : credentials_) {
422  std::fill(cred.username.begin(), cred.username.end(), '\0');
423  std::fill(cred.server.begin(), cred.server.end(), '\0');
424  }
425  credentials_.clear();
426 }
427 
429 {
430  if(!remember_password()) {
431  return;
432  }
434  std::string cred_file = filesystem::get_credentials_file();
435  if(!filesystem::file_exists(cred_file)) {
436  return;
437  }
438  filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
439  // Credentials file is a binary blob, so use streambuf iterator
440  preferences::secure_buffer data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
442  if(data.empty() || data[0] != pref_constants::CREDENTIAL_SEPARATOR) {
443  ERR_CFG << "Invalid data in credentials file";
444  return;
445  }
446  for(const std::string& elem : utils::split(std::string(data.begin(), data.end()), pref_constants::CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
447  std::size_t at = elem.find_last_of('@');
448  std::size_t eq = elem.find_first_of('=', at + 1);
449  if(at != std::string::npos && eq != std::string::npos) {
450  preferences::secure_buffer key(elem.begin() + eq + 1, elem.end());
451  credentials_.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), unescape(key));
452  }
453  }
454 }
455 
457 {
458  if(!remember_password()) {
460  return;
461  }
462 
463 #ifndef _WIN32
464  bool creds_file_existed = filesystem::file_exists(filesystem::get_credentials_file());
465 #endif
466 
467  preferences::secure_buffer credentials_data;
468  for(const auto& cred : credentials_) {
469  credentials_data.push_back(pref_constants::CREDENTIAL_SEPARATOR);
470  credentials_data.insert(credentials_data.end(), cred.username.begin(), cred.username.end());
471  credentials_data.push_back('@');
472  credentials_data.insert(credentials_data.end(), cred.server.begin(), cred.server.end());
473  credentials_data.push_back('=');
474  preferences::secure_buffer key_escaped = escape(cred.key);
475  credentials_data.insert(credentials_data.end(), key_escaped.begin(), key_escaped.end());
476  }
477  try {
479  preferences::secure_buffer encrypted = aes_encrypt(credentials_data, build_key("global", get_system_username()));
480  credentials_file->write(reinterpret_cast<const char*>(encrypted.data()), encrypted.size());
481  } catch(const filesystem::io_exception&) {
482  ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'";
483  }
484 
485 #ifndef _WIN32
486  if(!creds_file_existed) {
487  if(chmod(filesystem::get_credentials_file().c_str(), 0600) == -1) {
488  ERR_FS << "error setting permissions of credentials file '" << filesystem::get_credentials_file() << "'";
489  }
490  }
491 #endif
492 }
493 
494 //
495 // helpers
496 //
497 void prefs::set_child(const std::string& key, const config& val) {
499  preferences_.add_child(key, val);
500 }
501 
503 {
504  return preferences_.optional_child(key);
505 }
506 
507 std::string prefs::get(const std::string& key, const std::string& def) {
508  return preferences_[key].empty() ? def : preferences_[key];
509 }
510 
512 {
513  return preferences_[key];
514 }
515 
516 //
517 // accessors
518 //
519 static std::string fix_orb_color_name(const std::string& color) {
520  if (color.substr(0,4) == "orb_") {
521  if(color[4] >= '0' && color[4] <= '9') {
522  return color.substr(5);
523  } else {
524  return color.substr(4);
525  }
526  }
527  return color;
528 }
529 
530 std::string prefs::allied_color() {
531  std::string ally_color = preferences_[prefs_list::ally_orb_color].str();
532  if (ally_color.empty())
534  return fix_orb_color_name(ally_color);
535 }
536 void prefs::set_allied_color(const std::string& color_id) {
538 }
539 
540 std::string prefs::enemy_color() {
542  if (enemy_color.empty())
545 }
546 void prefs::set_enemy_color(const std::string& color_id) {
548 }
549 
550 std::string prefs::moved_color() {
552  if (moved_color.empty())
555 }
556 void prefs::set_moved_color(const std::string& color_id) {
558 }
559 
560 std::string prefs::unmoved_color() {
562  if (unmoved_color.empty())
565 }
566 void prefs::set_unmoved_color(const std::string& color_id) {
568 }
569 
570 std::string prefs::partial_color() {
571  std::string partmoved_color = preferences_[prefs_list::partial_orb_color].str();
572  if (partmoved_color.empty())
574  return fix_orb_color_name(partmoved_color);
575 }
576 void prefs::set_partial_color(const std::string& color_id) {
578 }
579 
581 {
582  const unsigned x_res = preferences_[prefs_list::xresolution].to_unsigned();
583  const unsigned y_res = preferences_[prefs_list::yresolution].to_unsigned();
584 
585  // Either resolution was unspecified, return default.
586  if(x_res == 0 || y_res == 0) {
588  }
589 
590  return point(
591  std::max<unsigned>(x_res, pref_constants::min_window_width),
592  std::max<unsigned>(y_res, pref_constants::min_window_height)
593  );
594 }
595 
596 void prefs::set_resolution(const point& res)
597 {
598  preferences_[prefs_list::xresolution] = std::to_string(res.x);
599  preferences_[prefs_list::yresolution] = std::to_string(res.y);
600 }
601 
603 {
604  // For now this has a minimum value of 1 and a maximum of 4.
605  return std::max<int>(std::min<int>(preferences_[prefs_list::pixel_scale].to_int(1), pref_constants::max_pixel_scale), pref_constants::min_pixel_scale);
606 }
607 
609 {
611 }
612 
614 {
615  if(video::headless()) {
616  return true;
617  }
618 
619  return preferences_[prefs_list::turbo].to_bool();
620 }
621 
622 void prefs::set_turbo(bool ison)
623 {
624  preferences_[prefs_list::turbo] = ison;
625 }
626 
628 {
629  // Clip at 80 because if it's too low it'll cause crashes
630  return std::max<int>(std::min<int>(preferences_[prefs_list::font_scale].to_int(100), pref_constants::max_font_scaling), pref_constants::min_font_scaling);
631 }
632 
634 {
636 }
637 
639 {
640  return (size * font_scaling()) / 100;
641 }
642 
644 {
645  return preferences_[prefs_list::keepalive_timeout].to_int(20);
646 }
647 
648 void prefs::keepalive_timeout(int seconds)
649 {
650  preferences_[prefs_list::keepalive_timeout] = std::abs(seconds);
651 }
652 
654 {
655  // Sounds don't sound good on Windows unless the buffer size is 4k,
656  // but this seems to cause crashes on other systems...
657  #ifdef _WIN32
658  const std::size_t buf_size = 4096;
659  #else
660  const std::size_t buf_size = 1024;
661  #endif
662 
663  return preferences_[prefs_list::sound_buffer_size].to_int(buf_size);
664 }
665 
666 void prefs::save_sound_buffer_size(const std::size_t size)
667 {
668  const std::string new_size = std::to_string(size);
669  if (preferences_[prefs_list::sound_buffer_size] == new_size)
670  return;
671 
672  preferences_[prefs_list::sound_buffer_size] = new_size;
673 
675 }
676 
678 {
679  return preferences_[prefs_list::music_volume].to_int(100);
680 }
681 
683 {
684  if(music_volume() == vol) {
685  return;
686  }
687 
690 }
691 
693 {
694  return preferences_[prefs_list::sound_volume].to_int(100);
695 }
696 
698 {
699  if(sound_volume() == vol) {
700  return;
701  }
702 
705 }
706 
708 {
709  return preferences_[prefs_list::bell_volume].to_int(100);
710 }
711 
713 {
714  if(bell_volume() == vol) {
715  return;
716  }
717 
720 }
721 
722 // old pref name had uppercase UI
724 {
726  return preferences_[prefs_list::ui_volume].to_int(100);
727  } else {
728  return preferences_["UI_volume"].to_int(100);
729  }
730 }
731 
732 void prefs::set_ui_volume(int vol)
733 {
734  if(ui_volume() == vol) {
735  return;
736  }
737 
740 }
741 
743 {
744  return preferences_[prefs_list::turn_bell].to_bool(true);
745 }
746 
747 bool prefs::set_turn_bell(bool ison)
748 {
749  if(!turn_bell() && ison) {
751  if(!music_on() && !sound() && !ui_sound_on()) {
752  if(!sound::init_sound()) {
754  return false;
755  }
756  }
757  } else if(turn_bell() && !ison) {
760  if(!music_on() && !sound() && !ui_sound_on())
762  }
763  return true;
764 }
765 
766 // old pref name had uppercase UI
768 {
769  if(preferences_.has_attribute(prefs_list::ui_sound)) {
770  return preferences_[prefs_list::ui_sound].to_bool(true);
771  } else {
772  return preferences_["UI_sound"].to_bool(true);
773  }
774 }
775 
776 bool prefs::set_ui_sound(bool ison)
777 {
778  if(!ui_sound_on() && ison) {
779  preferences_[prefs_list::ui_sound] = true;
780  if(!music_on() && !sound() && !turn_bell()) {
781  if(!sound::init_sound()) {
782  preferences_[prefs_list::ui_sound] = false;
783  return false;
784  }
785  }
786  } else if(ui_sound_on() && !ison) {
787  preferences_[prefs_list::ui_sound] = false;
789  if(!music_on() && !sound() && !turn_bell())
791  }
792  return true;
793 }
794 
796 {
797  return preferences_[prefs_list::message_bell].to_bool(true);
798 }
799 
801 {
802  return preferences_[prefs_list::sound].to_bool(true);
803 }
804 
805 bool prefs::set_sound(bool ison) {
806  if(!sound() && ison) {
808  if(!music_on() && !turn_bell() && !ui_sound_on()) {
809  if(!sound::init_sound()) {
811  return false;
812  }
813  }
814  } else if(sound() && !ison) {
817  if(!music_on() && !turn_bell() && !ui_sound_on())
819  }
820  return true;
821 }
822 
824 {
825  return preferences_[prefs_list::music].to_bool(true);
826 }
827 
828 bool prefs::set_music(bool ison) {
829  if(!music_on() && ison) {
830  preferences_[prefs_list::music] = true;
831  if(!sound() && !turn_bell() && !ui_sound_on()) {
832  if(!sound::init_sound()) {
833  preferences_[prefs_list::music] = false;
834  return false;
835  }
836  }
837  else
839  } else if(music_on() && !ison) {
840  preferences_[prefs_list::music] = false;
841  if(!sound() && !turn_bell() && !ui_sound_on())
843  else
845  }
846  return true;
847 }
848 
850 {
851  return std::clamp<int>(preferences_[prefs_list::scroll].to_int(50), 1, 100);
852 }
853 
854 void prefs::set_scroll_speed(const int new_speed)
855 {
856  preferences_[prefs_list::scroll] = new_speed;
857 }
858 
860 {
861  return preferences_[prefs_list::middle_click_scrolls].to_bool(true);
862 }
863 
865 {
866  return preferences_[prefs_list::scroll_threshold].to_int(10);
867 }
868 
870 {
871  return fps_;
872 }
873 
874 void prefs::set_show_fps(bool value)
875 {
876  fps_ = value;
877 }
878 
880 {
882 }
883 
885 {
887 }
888 
890 {
892  preferences_.clear_children("hotkey");
893 }
894 
895 void prefs::add_alias(const std::string &alias, const std::string &command)
896 {
897  config &alias_list = preferences_.child_or_add("alias");
898  alias_list[alias] = command;
899 }
900 
901 
903 {
904  return get_child(prefs_list::alias);
905 }
906 
907 unsigned int prefs::sample_rate()
908 {
909  return preferences_[prefs_list::sample_rate].to_int(44100);
910 }
911 
912 void prefs::save_sample_rate(const unsigned int rate)
913 {
914  if (sample_rate() == rate)
915  return;
916 
917  preferences_[prefs_list::sample_rate] = rate;
918 
919  // If audio is open, we have to re set sample rate
921 }
922 
924 {
925  return preferences_[prefs_list::confirm_load_save_from_different_version].to_bool(true);
926 }
927 
929 {
930  return preferences_[prefs_list::use_twelve_hour_clock_format].to_bool();
931 }
932 
934 {
935  return sort_order::get_enum(preferences_[prefs_list::addon_manager_saved_order_direction]).value_or(sort_order::type::none);
936 }
937 
939 {
940  preferences_[prefs_list::addon_manager_saved_order_direction] = sort_order::get_string(value);
941 }
942 
943 bool prefs::achievement(const std::string& content_for, const std::string& id)
944 {
945  for(config& ach : preferences_.child_range(prefs_list::achievements))
946  {
947  if(ach["content_for"].str() == content_for)
948  {
949  std::vector<std::string> ids = utils::split(ach["ids"]);
950  return std::find(ids.begin(), ids.end(), id) != ids.end();
951  }
952  }
953  return false;
954 }
955 
956 void prefs::set_achievement(const std::string& content_for, const std::string& id)
957 {
958  for(config& ach : preferences_.child_range(prefs_list::achievements))
959  {
960  // if achievements already exist for this content and the achievement has not already been set, add it
961  if(ach["content_for"].str() == content_for)
962  {
963  std::vector<std::string> ids = utils::split(ach["ids"]);
964 
965  if(ids.empty())
966  {
967  ach["ids"] = id;
968  }
969  else if(std::find(ids.begin(), ids.end(), id) == ids.end())
970  {
971  ach["ids"] = ach["ids"].str() + "," + id;
972  }
973  ach.remove_children("in_progress", [&id](config cfg){return cfg["id"].str() == id;});
974  return;
975  }
976  }
977 
978  // else no achievements have been set for this content yet
979  config ach;
980  ach["content_for"] = content_for;
981  ach["ids"] = id;
982  preferences_.add_child(prefs_list::achievements, ach);
983 }
984 
985 int prefs::progress_achievement(const std::string& content_for, const std::string& id, int limit, int max_progress, int amount)
986 {
987  if(achievement(content_for, id))
988  {
989  return -1;
990  }
991 
992  for(config& ach : preferences_.child_range(prefs_list::achievements))
993  {
994  // if achievements already exist for this content and the achievement has not already been set, add it
995  if(ach["content_for"].str() == content_for)
996  {
997  // check if this achievement has progressed before - if so then increment it
998  for(config& in_progress : ach.child_range("in_progress"))
999  {
1000  if(in_progress["id"].str() == id)
1001  {
1002  // don't let using 'limit' decrease the achievement's current progress
1003  int starting_progress = in_progress["progress_at"].to_int();
1004  if(starting_progress >= limit) {
1005  return starting_progress;
1006  }
1007 
1008  in_progress["progress_at"] = std::clamp(starting_progress + amount, 0, std::min(limit, max_progress));
1009  return in_progress["progress_at"].to_int();
1010  }
1011  }
1012 
1013  // else this is the first time this achievement is progressing
1014  if(amount != 0)
1015  {
1016  config set_progress;
1017  set_progress["id"] = id;
1018  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1019 
1020  config& child = ach.add_child("in_progress", set_progress);
1021  return child["progress_at"].to_int();
1022  }
1023  return 0;
1024  }
1025  }
1026 
1027  // else not only has this achievement not progressed before, this is the first achievement for this achievement group to be added
1028  if(amount != 0)
1029  {
1030  config ach;
1031  config set_progress;
1032 
1033  set_progress["id"] = id;
1034  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1035 
1036  ach["content_for"] = content_for;
1037  ach["ids"] = "";
1038 
1039  config& child = ach.add_child("in_progress", set_progress);
1040  preferences_.add_child(prefs_list::achievements, ach);
1041  return child["progress_at"].to_int();
1042  }
1043  return 0;
1044 }
1045 
1046 bool prefs::sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1047 {
1048  // this achievement is already completed
1049  if(achievement(content_for, id))
1050  {
1051  return true;
1052  }
1053 
1054  for(config& ach : preferences_.child_range(prefs_list::achievements))
1055  {
1056  if(ach["content_for"].str() == content_for)
1057  {
1058  // check if the specific sub-achievement has been completed but the overall achievement is not completed
1059  for(const auto& in_progress : ach.child_range("in_progress"))
1060  {
1061  if(in_progress["id"] == id)
1062  {
1063  std::vector<std::string> sub_ids = utils::split(in_progress["sub_ids"]);
1064  return std::find(sub_ids.begin(), sub_ids.end(), sub_id) != sub_ids.end();
1065  }
1066  }
1067  }
1068  }
1069  return false;
1070 }
1071 
1072 void prefs::set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1073 {
1074  // this achievement is already completed
1075  if(achievement(content_for, id))
1076  {
1077  return;
1078  }
1079 
1080  for(config& ach : preferences_.child_range(prefs_list::achievements))
1081  {
1082  // if achievements already exist for this content and the achievement has not already been set, add it
1083  if(ach["content_for"].str() == content_for)
1084  {
1085  // check if this achievement has had sub-achievements set before
1086  for(config& in_progress : ach.child_range("in_progress"))
1087  {
1088  if(in_progress["id"].str() == id)
1089  {
1090  std::vector<std::string> sub_ids = utils::split(ach["ids"]);
1091 
1092  if(std::find(sub_ids.begin(), sub_ids.end(), sub_id) == sub_ids.end())
1093  {
1094  in_progress["sub_ids"] = in_progress["sub_ids"].str() + "," + sub_id;
1095  }
1096 
1097  in_progress["progress_at"] = sub_ids.size()+1;
1098  return;
1099  }
1100  }
1101 
1102  // else if this is the first sub-achievement being set
1103  config set_progress;
1104  set_progress["id"] = id;
1105  set_progress["sub_ids"] = sub_id;
1106  set_progress["progress_at"] = 1;
1107  ach.add_child("in_progress", set_progress);
1108  return;
1109  }
1110  }
1111 
1112  // else not only has this achievement not had a sub-achievement completed before, this is the first achievement for this achievement group to be added
1113  config ach;
1114  config set_progress;
1115 
1116  set_progress["id"] = id;
1117  set_progress["sub_ids"] = sub_id;
1118  set_progress["progress_at"] = 1;
1119 
1120  ach["content_for"] = content_for;
1121  ach["ids"] = "";
1122 
1123  ach.add_child("in_progress", set_progress);
1124  preferences_.add_child(prefs_list::achievements, ach);
1125 }
1126 
1128 {
1129  return preferences_[prefs_list::show_deprecation].to_bool(def);
1130 }
1131 
1133 {
1134  return preferences_[prefs_list::scroll_when_mouse_outside].to_bool(def);
1135 }
1136 
1138 {
1139  set_child(prefs_list::dir_bookmarks, cfg);
1140 }
1142 {
1143  return get_child(prefs_list::dir_bookmarks);
1144 }
1145 
1147 {
1148  return preferences_[prefs_list::lobby_auto_open_whisper_windows].to_bool(true);
1149 }
1150 
1152 {
1153  return std::max(std::size_t(1), preferences_[prefs_list::editor_max_recent_files].to_size_t(10));
1154 }
1155 
1156 //
1157 // NOTE: The MRU read/save functions enforce the entry count limit in
1158 // order to ensure the list on disk doesn't grow forever. Otherwise,
1159 // normally this would be the UI's responsibility instead.
1160 //
1161 
1162 std::vector<std::string> prefs::do_read_editor_mru()
1163 {
1164  auto cfg = get_child(prefs_list::editor_recent_files);
1165 
1166  std::vector<std::string> mru;
1167  if(!cfg) {
1168  return mru;
1169  }
1170 
1171  for(const config& child : cfg->child_range("entry"))
1172  {
1173  const std::string& entry = child["path"].str();
1174  if(!entry.empty()) {
1175  mru.push_back(entry);
1176  }
1177  }
1178 
1179  mru.resize(std::min(editor_mru_limit(), mru.size()));
1180 
1181  return mru;
1182 }
1183 
1184 void prefs::do_commit_editor_mru(const std::vector<std::string>& mru)
1185 {
1186  config cfg;
1187  unsigned n = 0;
1188 
1189  for(const std::string& entry : mru)
1190  {
1191  if(entry.empty()) {
1192  continue;
1193  }
1194 
1195  config& child = cfg.add_child("entry");
1196  child["path"] = entry;
1197 
1198  if(++n >= editor_mru_limit()) {
1199  break;
1200  }
1201  }
1202 
1203  set_child(prefs_list::editor_recent_files, cfg);
1204 }
1205 
1206 std::vector<std::string> prefs::recent_files()
1207 {
1208  return do_read_editor_mru();
1209 }
1210 
1211 void prefs::add_recent_files_entry(const std::string& path)
1212 {
1213  if(path.empty()) {
1214  return;
1215  }
1216 
1217  std::vector<std::string> mru = do_read_editor_mru();
1218 
1219  // Enforce uniqueness. Normally shouldn't do a thing unless somebody
1220  // has been tampering with the preferences file.
1221  mru.erase(std::remove(mru.begin(), mru.end(), path), mru.end());
1222 
1223  mru.insert(mru.begin(), path);
1224  mru.resize(std::min(editor_mru_limit(), mru.size()));
1225 
1226  do_commit_editor_mru(mru);
1227 }
1228 
1230 {
1231  return preferences_[prefs_list::color_cursors].to_bool(true);
1232 }
1233 
1235 {
1236  preferences_[prefs_list::color_cursors] = value;
1237 
1238  cursor::set();
1239 }
1240 
1242 {
1243  return preferences_[prefs_list::unit_standing_animations].to_bool(true);
1244 }
1245 
1247 {
1248  preferences_[prefs_list::unit_standing_animations] = value;
1249 
1250  if(display* d = display::get_singleton()) {
1251  d->reset_standing_animations();
1252  }
1253 }
1254 
1256 {
1257  std::vector<theme_info> themes = theme::get_basic_theme_info();
1258 
1259  if (themes.empty()) {
1261  _("No known themes. Try changing from within an existing game."));
1262 
1263  return false;
1264  }
1265 
1266  gui2::dialogs::theme_list dlg(themes);
1267 
1268  for (std::size_t k = 0; k < themes.size(); ++k) {
1269  if(themes[k].id == theme()) {
1270  dlg.set_selected_index(static_cast<int>(k));
1271  }
1272  }
1273 
1274  dlg.show();
1275  const int action = dlg.selected_index();
1276 
1277  if (action >= 0) {
1278  set_theme(themes[action].id);
1279  if(display::get_singleton() && resources::gamedata && resources::gamedata->get_theme().empty()) {
1280  display::get_singleton()->set_theme(themes[action].id);
1281  }
1282 
1283  return true;
1284  }
1285 
1286  return false;
1287 }
1288 
1290 {
1291  const std::string filename = filesystem::get_wesnothd_name();
1292 
1293  const std::string& old_path = filesystem::directory_name(get_mp_server_program_name());
1294  std::string path =
1295  !old_path.empty() && filesystem::is_directory(old_path)
1296  ? old_path : filesystem::get_exe_dir();
1297 
1298  const std::string msg = VGETTEXT("The <b>$filename</b> server application provides multiplayer server functionality and is required for hosting local network games. It will normally be found in the same folder as the game executable.", {{"filename", filename}});
1299 
1301 
1302  dlg.set_title(_("Find Server Application"))
1303  .set_message(msg)
1304  .set_ok_label(_("Select"))
1305  .set_read_only(true)
1307  .set_path(path);
1308 
1309  if(dlg.show()) {
1310  path = dlg.path();
1312  }
1313 }
1314 
1315 std::string prefs::theme()
1316 {
1317  if(video::headless()) {
1318  static const std::string null_theme = "null";
1319  return null_theme;
1320  }
1321 
1322  std::string res = preferences_[prefs_list::theme];
1323  if(res.empty()) {
1324  return "Default";
1325  }
1326 
1327  return res;
1328 }
1329 
1330 void prefs::set_theme(const std::string& theme)
1331 {
1332  if(theme != "null") {
1333  preferences_[prefs_list::theme] = theme;
1334  }
1335 }
1336 
1337 void prefs::set_mp_server_program_name(const std::string& path)
1338 {
1339  if(path.empty()) {
1340  preferences_.remove_attribute(prefs_list::mp_server_program_name);
1341  } else {
1342  preferences_[prefs_list::mp_server_program_name] = path;
1343  }
1344 }
1345 
1347 {
1348  return preferences_[prefs_list::mp_server_program_name].str();
1349 }
1350 
1351 const std::map<std::string, preferences::acquaintance>& prefs::get_acquaintances()
1352 {
1353  return acquaintances_;
1354 }
1355 
1356 const std::string prefs::get_ignored_delim()
1357 {
1358  std::vector<std::string> ignored;
1359 
1360  for(const auto& person : acquaintances_) {
1361  if(person.second.get_status() == "ignore") {
1362  ignored.push_back(person.second.get_nick());
1363  }
1364  }
1365 
1366  return utils::join(ignored);
1367 }
1368 
1369 // returns acquaintances in the form nick => notes where the status = filter
1370 std::map<std::string, std::string> prefs::get_acquaintances_nice(const std::string& filter)
1371 {
1372  std::map<std::string, std::string> ac_nice;
1373 
1374  for(const auto& a : acquaintances_) {
1375  if(a.second.get_status() == filter) {
1376  ac_nice[a.second.get_nick()] = a.second.get_notes();
1377  }
1378  }
1379 
1380  return ac_nice;
1381 }
1382 
1383 std::pair<preferences::acquaintance*, bool> prefs::add_acquaintance(const std::string& nick, const std::string& mode, const std::string& notes)
1384 {
1385  if(!utils::isvalid_wildcard(nick)) {
1386  return std::pair(nullptr, false);
1387  }
1388 
1389  preferences::acquaintance new_entry(nick, mode, notes);
1390  auto [iter, added_new] = acquaintances_.insert_or_assign(nick, new_entry);
1391 
1392  return std::pair(&iter->second, added_new);
1393 }
1394 
1395 bool prefs::remove_acquaintance(const std::string& nick)
1396 {
1398 
1399  // nick might include the notes, depending on how we're removing
1400  if(i == acquaintances_.end()) {
1401  std::size_t pos = nick.find_first_of(' ');
1402 
1403  if(pos != std::string::npos) {
1404  i = acquaintances_.find(nick.substr(0, pos));
1405  }
1406  }
1407 
1408  if(i == acquaintances_.end()) {
1409  return false;
1410  }
1411 
1412  acquaintances_.erase(i);
1413 
1414  return true;
1415 }
1416 
1417 bool prefs::is_friend(const std::string& nick)
1418 {
1419  const auto it = acquaintances_.find(nick);
1420 
1421  if(it == acquaintances_.end()) {
1422  return false;
1423  } else {
1424  return it->second.get_status() == "friend";
1425  }
1426 }
1427 
1428 bool prefs::is_ignored(const std::string& nick)
1429 {
1430  const auto it = acquaintances_.find(nick);
1431 
1432  if(it == acquaintances_.end()) {
1433  return false;
1434  } else {
1435  return it->second.get_status() == "ignore";
1436  }
1437 }
1438 
1439 void prefs::add_completed_campaign(const std::string& campaign_id, const std::string& difficulty_level)
1440 {
1441  completed_campaigns_[campaign_id].insert(difficulty_level);
1442 }
1443 
1444 bool prefs::is_campaign_completed(const std::string& campaign_id)
1445 {
1446  return completed_campaigns_.count(campaign_id) != 0;
1447 }
1448 
1449 bool prefs::is_campaign_completed(const std::string& campaign_id, const std::string& difficulty_level)
1450 {
1451  const auto it = completed_campaigns_.find(campaign_id);
1452  return it == completed_campaigns_.end() ? false : it->second.count(difficulty_level) != 0;
1453 }
1454 
1455 bool prefs::parse_should_show_lobby_join(const std::string& sender, const std::string& message)
1456 {
1457  // If it's actually not a lobby join or leave message return true (show it).
1458  if(sender != "server") {
1459  return true;
1460  }
1461 
1462  std::string::size_type pos = message.find(" has logged into the lobby");
1463  if(pos == std::string::npos) {
1464  pos = message.find(" has disconnected");
1465  if(pos == std::string::npos) {
1466  return true;
1467  }
1468  }
1469 
1472  return false;
1473  }
1474 
1476  return true;
1477  }
1478 
1479  return is_friend(message.substr(0, pos));
1480 }
1481 
1483 {
1484  std::string pref = preferences_[prefs_list::lobby_joins];
1485  if(pref == "friends") {
1487  } else if(pref == "all") {
1489  } else if(pref == "none") {
1491  } else {
1493  }
1494 }
1495 
1497 {
1504  }
1505 }
1506 
1507 const std::vector<game_config::server_info>& prefs::builtin_servers_list()
1508 {
1509  static std::vector<game_config::server_info> pref_servers = game_config::server_list;
1510  return pref_servers;
1511 }
1512 
1513 std::vector<game_config::server_info> prefs::user_servers_list()
1514 {
1515  std::vector<game_config::server_info> pref_servers;
1516 
1517  for(const config& server : preferences_.child_range("server")) {
1518  pref_servers.emplace_back();
1519  pref_servers.back().name = server["name"].str();
1520  pref_servers.back().address = server["address"].str();
1521  }
1522 
1523  return pref_servers;
1524 }
1525 
1526 void prefs::set_user_servers_list(const std::vector<game_config::server_info>& value)
1527 {
1528  preferences_.clear_children("server");
1529 
1530  for(const auto& svinfo : value) {
1531  config& sv_cfg = preferences_.add_child("server");
1532  sv_cfg["name"] = svinfo.name;
1533  sv_cfg["address"] = svinfo.address;
1534  }
1535 }
1536 
1537 std::string prefs::network_host()
1538 {
1539  const std::string res = preferences_[prefs_list::host];
1540  if(res.empty()) {
1541  return builtin_servers_list().front().address;
1542  } else {
1543  return res;
1544  }
1545 }
1546 
1547 void prefs::set_network_host(const std::string& host)
1548 {
1549  preferences_[prefs_list::host] = host;
1550 }
1551 
1553 {
1554  if(!preferences_[prefs_list::campaign_server].empty()) {
1555  return preferences_[prefs_list::campaign_server].str();
1556  } else {
1558  }
1559 }
1560 
1561 void prefs::set_campaign_server(const std::string& host)
1562 {
1563  preferences_[prefs_list::campaign_server] = host;
1564 }
1565 
1567 {
1568  return preferences_[prefs_list::show_combat].to_bool(true);
1569 }
1570 
1572 {
1573  if(options_initialized_) {
1574  return option_values_;
1575  }
1576 
1577  if(!get_child(prefs_list::options)) {
1578  // It may be an invalid config, which would cause problems in
1579  // multiplayer_create, so let's replace it with an empty but valid
1580  // config
1582  } else {
1583  option_values_ = *get_child(prefs_list::options);
1584  }
1585 
1586  options_initialized_ = true;
1587 
1588  return option_values_;
1589 }
1590 
1591 void prefs::set_options(const config& values)
1592 {
1593  set_child(prefs_list::options, values);
1594  options_initialized_ = false;
1595 }
1596 
1598 {
1599  return std::clamp<int>(preferences_[prefs_list::mp_countdown_init_time].to_int(240), 0, 1500);
1600 }
1601 
1603 {
1604  preferences_[prefs_list::mp_countdown_init_time] = value;
1605 }
1606 
1608 {
1609  preferences_.remove_attribute(prefs_list::mp_countdown_init_time);
1610 }
1611 
1613 {
1614  return std::clamp<int>(preferences_[prefs_list::mp_countdown_reservoir_time].to_int(360), 30, 1500);
1615 }
1616 
1618 {
1619  preferences_[prefs_list::mp_countdown_reservoir_time] = value;
1620 }
1621 
1623 {
1624  preferences_.remove_attribute(prefs_list::mp_countdown_reservoir_time);
1625 }
1626 
1628 {
1629  return std::clamp<int>(preferences_[prefs_list::mp_countdown_turn_bonus].to_int(240), 0, 300);
1630 }
1631 
1633 {
1634  preferences_[prefs_list::mp_countdown_turn_bonus] = value;
1635 }
1636 
1638 {
1639  preferences_.remove_attribute(prefs_list::mp_countdown_turn_bonus);
1640 }
1641 
1643 {
1644  return std::clamp<int>(preferences_[prefs_list::mp_countdown_action_bonus], 0, 30);
1645 }
1646 
1648 {
1649  preferences_[prefs_list::mp_countdown_action_bonus] = value;
1650 }
1651 
1653 {
1654  preferences_.remove_attribute(prefs_list::mp_countdown_action_bonus);
1655 }
1656 
1658 {
1659  return settings::get_village_gold(preferences_[prefs_list::mp_village_gold]);
1660 }
1661 
1663 {
1664  preferences_[prefs_list::mp_village_gold] = value;
1665 }
1666 
1668 {
1669  return settings::get_village_support(preferences_[prefs_list::mp_village_support]);
1670 }
1671 
1673 {
1674  preferences_[prefs_list::mp_village_support] = std::to_string(value);
1675 }
1676 
1678 {
1679  return settings::get_xp_modifier(preferences_[prefs_list::mp_xp_modifier]);
1680 }
1681 
1682 void prefs::set_xp_modifier(int value)
1683 {
1684  preferences_[prefs_list::mp_xp_modifier] = value;
1685 }
1686 
1687 const std::vector<std::string>& prefs::modifications(bool mp)
1688 {
1690  if(mp) {
1691  mp_modifications_ = utils::split(preferences_[prefs_list::mp_modifications].str(), ',');
1693  } else {
1694  sp_modifications_ = utils::split(preferences_[prefs_list::sp_modifications].str(), ',');
1696  }
1697  }
1698 
1700 }
1701 
1702 void prefs::set_modifications(const std::vector<std::string>& value, bool mp)
1703 {
1704  if(mp) {
1705  preferences_[prefs_list::mp_modifications] = utils::join(value, ",");
1707  } else {
1708  preferences_[prefs_list::sp_modifications] = utils::join(value, ",");
1710  }
1711 }
1712 
1714 {
1715  return message_private_on_;
1716 }
1717 
1719 {
1720  message_private_on_ = value;
1721 }
1722 
1724 {
1725  const std::string& choice = preferences_[prefs_list::compress_saves];
1726 
1727  // "yes" was used in 1.11.7 and earlier; the compress_saves
1728  // option used to be a toggle for gzip in those versions.
1729  if(choice.empty() || choice == "gzip" || choice == "yes") {
1731  } else if(choice == "bzip2") {
1733  } else if(choice == "none" || choice == "no") { // see above
1735  } /*else*/
1736 
1737  // In case the preferences file was created by a later version
1738  // supporting some algorithm we don't; although why would anyone
1739  // playing a game need more algorithms, really...
1741 }
1742 
1743 std::string prefs::get_chat_timestamp(const std::time_t& t)
1744 {
1745  if(chat_timestamp()) {
1746  if(use_twelve_hour_clock_format() == false) {
1747  return lg::get_timestamp(t, _("[%H:%M]")) + " ";
1748  } else {
1749  return lg::get_timestamp(t, _("[%I:%M %p]")) + " ";
1750  }
1751  }
1752 
1753  return "";
1754 }
1755 
1756 std::set<std::string>& prefs::encountered_units()
1757 {
1758  return encountered_units_set_;
1759 }
1760 
1761 std::set<t_translation::terrain_code>& prefs::encountered_terrains()
1762 {
1764 }
1765 
1766 /**
1767  * Returns a pointer to the history vector associated with given id
1768  * making a new one if it doesn't exist.
1769  *
1770  * @todo FIXME only used for gui2. Could be used for the above histories.
1771  */
1772 std::vector<std::string>* prefs::get_history(const std::string& id)
1773 {
1774  return &history_map_[id];
1775 }
1776 
1778 {
1779  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1780  return confirmation == "green" || confirmation == "yes";
1781 }
1782 
1784 {
1785  return preferences_[prefs_list::confirm_end_turn] == "yellow";
1786 }
1787 
1789 {
1790  // This is very non-intrusive so it is on by default
1791  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1792  return confirmation == "no_moves" || confirmation.empty();
1793 }
1794 
1795 void prefs::encounter_recruitable_units(const std::vector<team>& teams)
1796 {
1797  for(const team& help_team : teams) {
1798  help_team.log_recruitable();
1799  encountered_units_set_.insert(help_team.recruits().begin(), help_team.recruits().end());
1800  }
1801 }
1802 
1804 {
1805  for(const auto& help_unit : units) {
1806  encountered_units_set_.insert(help_unit.type_id());
1807  }
1808 }
1809 
1810 void prefs::encounter_recallable_units(const std::vector<team>& teams)
1811 {
1812  for(const team& t : teams) {
1813  for(const unit_const_ptr u : t.recall_list()) {
1814  encountered_units_set_.insert(u->type_id());
1815  }
1816  }
1817 }
1818 
1820 {
1821  map.for_each_loc([&](const map_location& loc) {
1822  const t_translation::terrain_code terrain = map.get_terrain(loc);
1823  encountered_terrains().insert(terrain);
1825  encountered_terrains().insert(t);
1826  }
1827  });
1828 }
1829 
1831 {
1832  encounter_recruitable_units(gameboard_.teams());
1833  encounter_start_units(gameboard_.units());
1834  encounter_recallable_units(gameboard_.teams());
1835  encounter_map_terrain(gameboard_.map());
1836 }
1837 
1839 {
1840  preferences_.remove_attribute(prefs_list::player_joins_sound);
1841  preferences_.remove_attribute(prefs_list::player_joins_notif);
1842  preferences_.remove_attribute(prefs_list::player_joins_lobby);
1843  preferences_.remove_attribute(prefs_list::player_leaves_sound);
1844  preferences_.remove_attribute(prefs_list::player_leaves_notif);
1845  preferences_.remove_attribute(prefs_list::player_leaves_lobby);
1846  preferences_.remove_attribute(prefs_list::private_message_sound);
1847  preferences_.remove_attribute(prefs_list::private_message_notif);
1848  preferences_.remove_attribute(prefs_list::private_message_lobby);
1849  preferences_.remove_attribute(prefs_list::friend_message_sound);
1850  preferences_.remove_attribute(prefs_list::friend_message_notif);
1851  preferences_.remove_attribute(prefs_list::friend_message_lobby);
1852  preferences_.remove_attribute(prefs_list::public_message_sound);
1853  preferences_.remove_attribute(prefs_list::public_message_notif);
1854  preferences_.remove_attribute(prefs_list::public_message_lobby);
1855  preferences_.remove_attribute(prefs_list::server_message_sound);
1856  preferences_.remove_attribute(prefs_list::server_message_notif);
1857  preferences_.remove_attribute(prefs_list::server_message_lobby);
1858  preferences_.remove_attribute(prefs_list::ready_for_start_sound);
1859  preferences_.remove_attribute(prefs_list::ready_for_start_notif);
1860  preferences_.remove_attribute(prefs_list::ready_for_start_lobby);
1861  preferences_.remove_attribute(prefs_list::game_has_begun_sound);
1862  preferences_.remove_attribute(prefs_list::game_has_begun_notif);
1863  preferences_.remove_attribute(prefs_list::game_has_begun_lobby);
1864  preferences_.remove_attribute(prefs_list::turn_changed_sound);
1865  preferences_.remove_attribute(prefs_list::turn_changed_notif);
1866  preferences_.remove_attribute(prefs_list::turn_changed_lobby);
1867  preferences_.remove_attribute(prefs_list::game_created_sound);
1868  preferences_.remove_attribute(prefs_list::game_created_notif);
1869  preferences_.remove_attribute(prefs_list::game_created_lobby);
1870 }
1871 
1873 {
1874  std::string res;
1875 #ifdef _WIN32
1876  wchar_t buffer[300];
1877  DWORD size = 300;
1878  if(GetUserNameW(buffer, &size)) {
1879  //size includes a terminating null character.
1880  assert(size > 0);
1881  res = unicode_cast<std::string>(boost::iterator_range<wchar_t*>(buffer, buffer + size - 1));
1882  }
1883 #else
1884  if(char* const login = getenv("USER")) {
1885  res = login;
1886  }
1887 #endif
1888  return res;
1889 }
1890 
1891 preferences::secure_buffer prefs::build_key(const std::string& server, const std::string& login)
1892 {
1893  std::string sysname = get_system_username();
1894  preferences::secure_buffer result(std::max<std::size_t>(server.size() + login.size() + sysname.size(), 32));
1895  unsigned char i = 0;
1896  std::generate(result.begin(), result.end(), [&i]() {return 'x' ^ i++;});
1897  std::copy(login.begin(), login.end(), result.begin());
1898  std::copy(sysname.begin(), sysname.end(), result.begin() + login.size());
1899  std::copy(server.begin(), server.end(), result.begin() + login.size() + sysname.size());
1900  return result;
1901 }
1902 
1904 {
1905 #ifndef __APPLE__
1906  int update_length;
1907  int extra_length;
1908  int total_length;
1909  // AES IV is generally 128 bits
1910  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1911  unsigned char encrypted_buffer[1024];
1912 
1913  if(plaintext.size() > 1008)
1914  {
1915  ERR_CFG << "Cannot encrypt data larger than 1008 bytes.";
1916  return preferences::secure_buffer();
1917  }
1918  DBG_CFG << "Encrypting data with length: " << plaintext.size();
1919 
1920  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1921  if(!ctx)
1922  {
1923  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
1924  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1925  return preferences::secure_buffer();
1926  }
1927 
1928  // TODO: use EVP_EncryptInit_ex2 once openssl 3.0 is more widespread
1929  if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
1930  {
1931  ERR_CFG << "AES EVP_EncryptInit_ex failed with error:";
1932  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1933  EVP_CIPHER_CTX_free(ctx);
1934  return preferences::secure_buffer();
1935  }
1936 
1937  if(EVP_EncryptUpdate(ctx, encrypted_buffer, &update_length, plaintext.data(), plaintext.size()) != 1)
1938  {
1939  ERR_CFG << "AES EVP_EncryptUpdate failed with error:";
1940  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1941  EVP_CIPHER_CTX_free(ctx);
1942  return preferences::secure_buffer();
1943  }
1944  DBG_CFG << "Update length: " << update_length;
1945 
1946  if(EVP_EncryptFinal_ex(ctx, encrypted_buffer + update_length, &extra_length) != 1)
1947  {
1948  ERR_CFG << "AES EVP_EncryptFinal failed with error:";
1949  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1950  EVP_CIPHER_CTX_free(ctx);
1951  return preferences::secure_buffer();
1952  }
1953  DBG_CFG << "Extra length: " << extra_length;
1954 
1955  EVP_CIPHER_CTX_free(ctx);
1956 
1957  total_length = update_length+extra_length;
1959  for(int i = 0; i < total_length; i++)
1960  {
1961  result.push_back(encrypted_buffer[i]);
1962  }
1963 
1964  DBG_CFG << "Successfully encrypted plaintext value of '" << utils::join(plaintext, "") << "' having length " << plaintext.size();
1965  DBG_CFG << "For a total encrypted length of: " << total_length;
1966 
1967  return result;
1968 #else
1969  size_t outWritten = 0;
1970  preferences::secure_buffer result(plaintext.size(), '\0');
1971 
1972  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
1973  kCCAlgorithmRC4,
1974  kCCOptionPKCS7Padding,
1975  key.data(),
1976  key.size(),
1977  nullptr,
1978  plaintext.data(),
1979  plaintext.size(),
1980  result.data(),
1981  result.size(),
1982  &outWritten);
1983 
1984  assert(ccStatus == kCCSuccess);
1985  assert(outWritten == plaintext.size());
1986 
1987  return result;
1988 #endif
1989 }
1990 
1992 {
1993 #ifndef __APPLE__
1994  int update_length;
1995  int extra_length;
1996  int total_length;
1997  // AES IV is generally 128 bits
1998  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1999  unsigned char plaintext_buffer[1024];
2000 
2001  if(encrypted.size() > 1024)
2002  {
2003  ERR_CFG << "Cannot decrypt data larger than 1024 bytes.";
2004  return preferences::secure_buffer();
2005  }
2006  DBG_CFG << "Decrypting data with length: " << encrypted.size();
2007 
2008  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
2009  if(!ctx)
2010  {
2011  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
2012  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2013  return preferences::secure_buffer();
2014  }
2015 
2016  // TODO: use EVP_DecryptInit_ex2 once openssl 3.0 is more widespread
2017  if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
2018  {
2019  ERR_CFG << "AES EVP_DecryptInit_ex failed with error:";
2020  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2021  EVP_CIPHER_CTX_free(ctx);
2022  return preferences::secure_buffer();
2023  }
2024 
2025  if(EVP_DecryptUpdate(ctx, plaintext_buffer, &update_length, encrypted.data(), encrypted.size()) != 1)
2026  {
2027  ERR_CFG << "AES EVP_DecryptUpdate failed with error:";
2028  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2029  EVP_CIPHER_CTX_free(ctx);
2030  return preferences::secure_buffer();
2031  }
2032  DBG_CFG << "Update length: " << update_length;
2033 
2034  if(EVP_DecryptFinal_ex(ctx, plaintext_buffer + update_length, &extra_length) != 1)
2035  {
2036  ERR_CFG << "AES EVP_DecryptFinal failed with error:";
2037  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2038  EVP_CIPHER_CTX_free(ctx);
2039  return preferences::secure_buffer();
2040  }
2041  DBG_CFG << "Extra length: " << extra_length;
2042 
2043  EVP_CIPHER_CTX_free(ctx);
2044 
2045  total_length = update_length+extra_length;
2047  for(int i = 0; i < total_length; i++)
2048  {
2049  result.push_back(plaintext_buffer[i]);
2050  }
2051 
2052  DBG_CFG << "Successfully decrypted data to the value: " << utils::join(result, "");
2053  DBG_CFG << "For a total decrypted length of: " << total_length;
2054 
2055  return result;
2056 #else
2057  size_t outWritten = 0;
2058  preferences::secure_buffer result(encrypted.size(), '\0');
2059 
2060  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
2061  kCCAlgorithmRC4,
2062  kCCOptionPKCS7Padding,
2063  key.data(),
2064  key.size(),
2065  nullptr,
2066  encrypted.data(),
2067  encrypted.size(),
2068  result.data(),
2069  result.size(),
2070  &outWritten);
2071 
2072  assert(ccStatus == kCCSuccess);
2073  assert(outWritten == encrypted.size());
2074 
2075  // the decrypted result is likely shorter than the encrypted data, so the extra padding needs to be removed.
2076  while(!result.empty() && result.back() == 0) {
2077  result.pop_back();
2078  }
2079 
2080  return result;
2081 #endif
2082 }
2083 
2085 {
2086  preferences::secure_buffer unescaped;
2087  unescaped.reserve(text.size());
2088  bool escaping = false;
2089  for(char c : text) {
2090  if(escaping) {
2091  if(c == '\xa') {
2092  unescaped.push_back('\xc');
2093  } else if(c == '.') {
2094  unescaped.push_back('@');
2095  } else {
2096  unescaped.push_back(c);
2097  }
2098  escaping = false;
2099  } else if(c == '\x1') {
2100  escaping = true;
2101  } else {
2102  unescaped.push_back(c);
2103  }
2104  }
2105  assert(!escaping);
2106  return unescaped;
2107 }
2108 
2110 {
2112  escaped.reserve(text.size());
2113  for(char c : text) {
2114  if(c == '\x1') {
2115  escaped.push_back('\x1');
2116  escaped.push_back('\x1');
2117  } else if(c == '\xc') {
2118  escaped.push_back('\x1');
2119  escaped.push_back('\xa');
2120  } else if(c == '@') {
2121  escaped.push_back('\x1');
2122  escaped.push_back('.');
2123  } else {
2124  escaped.push_back(c);
2125  }
2126  }
2127  return escaped;
2128 }
2129 
2131 {
2132  return preferences_[prefs_list::remember_password].to_bool();
2133 }
2134 
2135 void prefs::set_remember_password(bool remember)
2136 {
2137  preferences_[prefs_list::remember_password] = remember;
2138 
2139  if(remember) {
2140  load_credentials();
2141  } else {
2143  }
2144 }
2145 
2146 std::string prefs::login()
2147 {
2148  std::string name = get("login", pref_constants::EMPTY_LOGIN);
2149  if(name == pref_constants::EMPTY_LOGIN) {
2150  name = get_system_username();
2151  } else if(name.size() > 2 && name.front() == '@' && name.back() == '@') {
2152  name = name.substr(1, name.size() - 2);
2153  } else {
2154  ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)";
2155  }
2156  if(name.empty()) {
2157  return "player";
2158  }
2159  return name;
2160 }
2161 
2162 void prefs::set_login(const std::string& login)
2163 {
2164  auto login_clean = login;
2165  boost::trim(login_clean);
2166 
2167  preferences_[prefs_list::login] = '@' + login_clean + '@';
2168 }
2169 
2170 std::string prefs::password(const std::string& server, const std::string& login)
2171 {
2172  DBG_CFG << "Retrieving password for server: '" << server << "', login: '" << login << "'";
2173  auto login_clean = login;
2174  boost::trim(login_clean);
2175 
2176  if(!remember_password()) {
2177  if(!credentials_.empty() && credentials_[0].username == login_clean && credentials_[0].server == server) {
2178  auto temp = aes_decrypt(credentials_[0].key, build_key(server, login_clean));
2179  return std::string(temp.begin(), temp.end());
2180  } else {
2181  return "";
2182  }
2183  }
2184  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2185  return cred.server == server && cred.username == login_clean;
2186  });
2187  if(cred == credentials_.end()) {
2188  return "";
2189  }
2190  auto temp = aes_decrypt(cred->key, build_key(server, login_clean));
2191  return std::string(temp.begin(), temp.end());
2192 }
2193 
2194 void prefs::set_password(const std::string& server, const std::string& login, const std::string& key)
2195 {
2196  DBG_CFG << "Setting password for server: '" << server << "', login: '" << login << "'";
2197  auto login_clean = login;
2198  boost::trim(login_clean);
2199 
2200  preferences::secure_buffer temp(key.begin(), key.end());
2201  if(!remember_password()) {
2203  credentials_.emplace_back(login_clean, server, aes_encrypt(temp, build_key(server, login_clean)));
2204  return;
2205  }
2206  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2207  return cred.server == server && cred.username == login_clean;
2208  });
2209  if(cred == credentials_.end()) {
2210  // This is equivalent to emplace_back, but also returns the iterator to the new element
2211  cred = credentials_.emplace(credentials_.end(), login_clean, server);
2212  }
2213  cred->key = aes_encrypt(temp, build_key(server, login_clean));
2214 }
double t
Definition: astarsearch.cpp:63
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_attr_itors attribute_range() const
Definition: config.cpp:763
void clear_children(T... keys)
Definition: config.hpp:641
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1126
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
child_itors child_range(config_key_type key)
Definition: config.cpp:273
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:406
void remove_attribute(config_key_type key)
Definition: config.cpp:160
attribute_map::value_type attribute
Definition: config.hpp:299
bool empty() const
Definition: config.cpp:852
void clear()
Definition: config.cpp:831
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
config & add_child(config_key_type key)
Definition: config.cpp:441
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:89
void set_theme(const std::string &new_theme)
Definition: display.cpp:249
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:103
Game board class.
Definition: game_board.hpp:47
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
A class grating read only view to a vector of config objects, viewed as one config with all children ...
static game_config_view wrap(const config &cfg)
config_array_view child_range(config_key_type key) const
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:301
void for_each_loc(const F &f) const
Definition: map.hpp:136
Encapsulates the map of the game.
Definition: map.hpp:172
const t_translation::ter_list & underlying_union_terrain(const map_location &loc) const
Definition: map.cpp:58
file_dialog & set_ok_label(const std::string &value)
Sets the OK button label.
file_dialog & set_path(const std::string &value)
Sets the initial file selection.
file_dialog & set_title(const std::string &value)
Sets the current dialog title text.
Definition: file_dialog.hpp:59
file_dialog & set_read_only(bool value)
Whether to provide user interface elements for manipulating existing objects.
file_dialog & set_filename(const std::string &value)
Sets the initial file name input but not the path.
file_dialog & set_message(const std::string &value)
Sets the current dialog instructions/message text.
Definition: file_dialog.hpp:78
bool show(const unsigned auto_close_time=0)
Shows the window.
int selected_index() const
Returns the selected item index after displaying.
Definition: theme_list.hpp:35
void set_selected_index(int index)
Sets the initially selected item index (-1 by default).
Definition: theme_list.hpp:41
const std::string & get_nick() const
Definition: preferences.hpp:91
void set_lobby_joins(pref_constants::lobby_joins show)
std::string enemy_color()
config::attribute_value get_as_attribute(const std::string &key)
void set_network_host(const std::string &host)
std::set< t_translation::terrain_code > & encountered_terrains()
const config & options()
bool set_music(bool ison)
void set_sound_volume(int vol)
void set_remember_password(bool remember)
const std::map< std::string, preferences::acquaintance > & get_acquaintances()
optional_const_config get_alias()
bool show_theme_dialog()
void set_turbo(bool ison)
std::string get_system_username()
bool mp_modifications_initialized_
std::map< std::string, std::vector< std::string > > history_map_
void save_hotkeys()
int village_support()
void set_addon_manager_saved_order_direction(sort_order::type value)
bool middle_click_scrolls()
bool turn_bell()
int countdown_init_time()
std::set< std::string > & encountered_units()
void add_alias(const std::string &alias, const std::string &command)
void set_countdown_reservoir_time(int value)
void write_preferences()
void clear_preferences()
std::vector< std::string > sp_modifications_
void clear_mp_alert_prefs()
The most recently selected add-on id from the editor.
void set_village_gold(int value)
bool confirm_load_save_from_different_version()
std::string network_host()
void clear_countdown_init_time()
std::set< std::string > unknown_unsynced_attributes_
bool set_ui_sound(bool ison)
void set_login(const std::string &login)
static prefs & get()
const std::string get_ignored_delim()
std::map< std::string, preferences::acquaintance > acquaintances_
static constexpr std::array unsynced_children_
bool fps_
void set_countdown_action_bonus(int value)
int scroll_speed()
bool message_private()
void encounter_recallable_units(const std::vector< team > &teams)
std::map< std::string, std::set< std::string > > completed_campaigns_
bool show_combat()
std::string theme()
optional_const_config dir_bookmarks()
void set_theme(const std::string &theme)
void set_xp_modifier(int value)
void set_user_servers_list(const std::vector< game_config::server_info > &value)
preferences::secure_buffer build_key(const std::string &server, const std::string &login)
Fills a secure_buffer with 32 bytes of deterministically generated bytes, then overwrites it with the...
int countdown_action_bonus()
bool sound()
bool is_campaign_completed(const std::string &campaign_id)
void set_allied_color(const std::string &color_id)
bool get_scroll_when_mouse_outside(bool def)
void show_wesnothd_server_search()
bool green_confirm()
int bell_volume()
int mouse_scroll_threshold()
Gets the threshold for when to scroll.
bool is_ignored(const std::string &nick)
std::vector< std::string > mp_modifications_
bool achievement(const std::string &content_for, const std::string &id)
std::set< std::string > unknown_synced_attributes_
void set_enemy_color(const std::string &color_id)
void save_credentials()
void set_password(const std::string &server, const std::string &login, const std::string &key)
bool sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
void clear_credentials()
std::vector< preferences::login_info > credentials_
std::vector< std::string > do_read_editor_mru()
void encounter_recruitable_units(const std::vector< team > &teams)
bool parse_should_show_lobby_join(const std::string &sender, const std::string &message)
int progress_achievement(const std::string &content_for, const std::string &id, int limit=999999, int max_progress=999999, int amount=0)
Increments the achievement's current progress by amount if it hasn't already been completed.
sort_order::type addon_manager_saved_order_direction()
bool set_turn_bell(bool ison)
static constexpr std::array synced_attributes_
unsigned int sample_rate()
static constexpr std::array synced_children_
void set_options(const config &values)
void set_message_private(bool value)
std::vector< game_config::server_info > user_servers_list()
optional_const_config get_child(const std::string &key)
void load_credentials()
std::string unmoved_color()
void set_music_volume(int vol)
void load_advanced_prefs(const game_config_view &gc)
std::string allied_color()
void set_show_standing_animations(bool value)
void set_show_fps(bool value)
void set_pixel_scale(const int scale)
std::size_t editor_mru_limit()
void set_color_cursors(bool value)
std::vector< preferences::option > advanced_prefs_
void encounter_start_units(const unit_map &units)
void set_countdown_turn_bonus(int value)
pref_constants::lobby_joins get_lobby_joins()
bool show_fps()
void load_hotkeys()
void encounter_map_terrain(const gamemap &map)
int font_scaling()
void set_village_support(int value)
preferences::secure_buffer escape(const preferences::secure_buffer &text)
std::string moved_color()
bool confirm_no_moves()
void set_font_scaling(int scale)
std::size_t sound_buffer_size()
bool use_color_cursors()
bool yellow_confirm()
config option_values_
void migrate_preferences(const std::string &prefs_dir)
bool message_bell()
void set_dir_bookmarks(const config &cfg)
bool message_private_on_
std::set< t_translation::terrain_code > encountered_terrains_set_
bool set_sound(bool ison)
bool is_friend(const std::string &nick)
void set_achievement(const std::string &content_for, const std::string &id)
Marks the specified achievement as completed.
preferences::secure_buffer aes_decrypt(const preferences::secure_buffer &text, const preferences::secure_buffer &key)
Same as aes_encrypt(), except of course it takes encrypted data as an argument and returns decrypted ...
int village_gold()
config preferences_
void clear_countdown_turn_bonus()
std::string login()
bool music_on()
int font_scaled(int size)
void set_scroll_speed(const int scroll)
std::pair< preferences::acquaintance *, bool > add_acquaintance(const std::string &nick, const std::string &mode, const std::string &notes)
bool ui_sound_on()
bool options_initialized_
int music_volume()
int pixel_scale()
std::string partial_color()
preferences::secure_buffer unescape(const preferences::secure_buffer &text)
void set_resolution(const point &res)
void set_campaign_server(const std::string &host)
void set_countdown_init_time(int value)
void set_unmoved_color(const std::string &color_id)
bool remove_acquaintance(const std::string &nick)
void clear_countdown_reservoir_time()
void set_ui_volume(int vol)
bool turbo()
const std::vector< std::string > & modifications(bool mp=true)
void do_commit_editor_mru(const std::vector< std::string > &mru)
compression::format save_compression_format()
std::vector< std::string > recent_files()
Retrieves the list of recently opened files.
std::set< std::string > encountered_units_set_
void set_bell_volume(int vol)
std::string get_mp_server_program_name()
void set_partial_color(const std::string &color_id)
void set_child(const std::string &key, const config &val)
static constexpr std::array unsynced_attributes_
std::set< std::string > all_attributes()
int keepalive_timeout()
void set_mp_server_program_name(const std::string &)
bool remember_password()
int ui_volume()
void add_recent_files_entry(const std::string &path)
Adds an entry to the recent files list.
void set_moved_color(const std::string &color_id)
std::string get_chat_timestamp(const std::time_t &t)
point resolution()
void save_sample_rate(const unsigned int rate)
bool use_twelve_hour_clock_format()
static bool no_preferences_save
int sound_volume()
void set_modifications(const std::vector< std::string > &value, bool mp=true)
void set_sub_achievement(const std::string &content_for, const std::string &id, const std::string &sub_id)
Marks the specified sub-achievement as completed.
int countdown_reservoir_time()
std::set< std::string > unknown_unsynced_children_
const std::vector< game_config::server_info > & builtin_servers_list()
void reload_preferences()
std::string campaign_server()
std::set< std::string > unknown_synced_children_
int countdown_turn_bonus()
void add_completed_campaign(const std::string &campaign_id, const std::string &difficulty_level)
void clear_hotkeys()
void save_sound_buffer_size(const std::size_t size)
int xp_modifier()
void encounter_all_content(const game_board &gb)
std::string password(const std::string &server, const std::string &login)
bool sp_modifications_initialized_
std::vector< std::string > * get_history(const std::string &id)
Returns a pointer to the history vector associated with given id making a new one if it doesn't exist...
preferences::secure_buffer aes_encrypt(const preferences::secure_buffer &text, const preferences::secure_buffer &key)
Encrypts the value of text using key and a hard coded IV using AES.
bool auto_open_whisper_windows()
void load_preferences()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
bool get_show_deprecation(bool def)
bool show_standing_animations()
void clear_countdown_action_bonus()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
Definition: theme.hpp:44
static std::vector< theme_info > get_basic_theme_info(bool include_hidden=false)
Returns minimal info about saved themes, optionally including hidden ones.
Definition: theme.cpp:987
Container associating units to locations.
Definition: map.hpp:98
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:965
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:205
Standard logging facilities (interface).
General settings and defaults for scenarios.
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
void fill(const SDL_Rect &rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Fill an area with the given colour.
Definition: draw.cpp:50
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:202
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
Definition: draw.cpp:180
std::time_t file_modified_time(const std::string &fname)
Get the modification time of a file.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void copy_file(const std::string &src, const std::string &dest)
Read a file and then writes it back out.
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:325
std::string get_exe_dir()
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
std::string get_synced_prefs_file()
location of preferences file containing preferences that are synced between computers note that wesno...
std::string get_unsynced_prefs_file()
location of preferences file containing preferences that aren't synced between computers
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:53
std::string get_credentials_file()
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:54
std::string get_wesnothd_name()
std::string get_default_prefs_file()
std::string partial_orb_color
std::string moved_orb_color
std::string unmoved_orb_color
std::string ally_orb_color
std::string enemy_orb_color
std::string turn_bell
std::string path
Definition: filesystem.cpp:90
const version_info wesnoth_version(VERSION)
std::vector< server_info > server_list
Definition: game_config.cpp:73
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:65
void remove()
Removes a tip.
Definition: tooltip.cpp:95
static int bell_volume()
static int music_volume()
static bool sound()
static int ui_volume()
static int sound_volume()
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:411
void save_hotkeys(config &cfg)
Save the non-default hotkeys to the config.
void reset_default_hotkeys()
Reset all hotkeys to the defaults.
void load_custom_hotkeys(const game_config_view &cfg)
Registers all hotkeys present in this config, overwriting any matching default hotkeys.
std::string get_timestamp(const std::time_t &t, const std::string &format)
Definition: log.cpp:401
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:49
const int min_window_height
Definition: preferences.hpp:37
const int max_pixel_scale
Definition: preferences.hpp:49
const std::string EMPTY_LOGIN
Definition: preferences.hpp:57
const int min_pixel_scale
Definition: preferences.hpp:48
const std::string default_addons_server
Definition: preferences.hpp:61
const int def_window_width
Definition: preferences.hpp:39
const int min_font_scaling
Definition: preferences.hpp:45
const int min_window_width
Definition: preferences.hpp:36
const int max_font_scaling
Definition: preferences.hpp:46
const int def_window_height
Definition: preferences.hpp:40
const unsigned char CREDENTIAL_SEPARATOR
Definition: preferences.hpp:56
game_data * gamedata
Definition: resources.cpp:22
static std::string at(const std::string &file, int line)
int get_village_support(const std::string &value)
Gets the village unit level support.
int get_xp_modifier(const std::string &value)
Gets the xp modifier.
int get_village_gold(const std::string &value, const game_classification *classification)
Gets the village gold.
void set_bell_volume(int vol)
Definition: sound.cpp:1122
void reset_sound()
Definition: sound.cpp:524
bool init_sound()
Definition: sound.cpp:441
void close_sound()
Definition: sound.cpp:493
void play_music()
Definition: sound.cpp:615
void stop_music()
Definition: sound.cpp:555
void stop_UI_sound()
Definition: sound.cpp:590
void stop_bell()
Definition: sound.cpp:578
void set_music_volume(int vol)
Definition: sound.cpp:1082
void stop_sound()
Definition: sound.cpp:563
void set_UI_volume(int vol)
Definition: sound.cpp:1134
void set_sound_volume(int vol)
Definition: sound.cpp:1102
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
std::string write_list(const ter_list &list)
Writes a list of terrains to a string, only writes the new format.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
@ REMOVE_EMPTY
void trim(std::string_view &s)
bool isvalid_wildcard(const std::string &username)
Check if the username pattern contains only valid characters.
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
bool headless()
The game is running headless.
Definition: video.cpp:141
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
void scale(size_t factor, const uint32_t *src, uint32_t *trg, int srcWidth, int srcHeight, ColorFormat colFmt, const ScalerCfg &cfg=ScalerCfg(), int yFirst=0, int yLast=std::numeric_limits< int >::max())
Definition: xbrz.cpp:1170
std::string_view data
Definition: picture.cpp:178
static lg::log_domain log_filesystem("filesystem")
#define ERR_CFG
Definition: preferences.cpp:61
#define DBG_CFG
Definition: preferences.cpp:62
#define ERR_ADV
Definition: preferences.cpp:68
static lg::log_domain advanced_preferences("advanced_preferences")
static std::string fix_orb_color_name(const std::string &color)
#define ERR_FS
Definition: preferences.cpp:65
static lg::log_domain log_config("config")
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:623
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:760
std::string filename
Filename.
An exception object used when an IO error occurs.
Definition: filesystem.hpp:67
Encapsulates the map of the game.
Definition: location.hpp:38
Holds a 2D point.
Definition: point.hpp:25
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
mock_char c
static map_location::DIRECTION n
#define d
#define e
#define h