The Battle for Wesnoth  1.19.5+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 <windows.h>
50 #endif
51 
52 #ifndef __APPLE__
53 #include <openssl/evp.h>
54 #include <openssl/err.h>
55 #else
56 #include <CommonCrypto/CommonCryptor.h>
57 #endif
58 
59 static lg::log_domain log_config("config");
60 #define ERR_CFG LOG_STREAM(err , log_config)
61 #define DBG_CFG LOG_STREAM(debug , log_config)
62 
63 static lg::log_domain log_filesystem("filesystem");
64 #define ERR_FS LOG_STREAM(err, log_filesystem)
65 
66 static lg::log_domain advanced_preferences("advanced_preferences");
67 #define ERR_ADV LOG_STREAM(err, advanced_preferences)
68 
70 : preferences_()
71 , fps_(false)
72 , completed_campaigns_()
73 , encountered_units_set_()
74 , encountered_terrains_set_()
75 , history_map_()
76 , acquaintances_()
77 , option_values_()
78 , options_initialized_(false)
79 , mp_modifications_()
80 , mp_modifications_initialized_(false)
81 , sp_modifications_()
82 , sp_modifications_initialized_(false)
83 , message_private_on_(false)
84 , credentials_()
85 , advanced_prefs_()
86 {
89 
90  // make sure this has a default set
91  if(!preferences_.has_attribute("scroll_threshold")) {
92  preferences_[prefs_list::scroll_threshold] = 10;
93  }
94 
95  for(const config& acfg : preferences_.child_range("acquaintance")) {
97  acquaintances_[ac.get_nick()] = ac;
98  }
99 }
100 
102 {
103  config campaigns;
104  for(const auto& elem : completed_campaigns_) {
105  config cmp;
106  cmp["name"] = elem.first;
107  cmp["difficulty_levels"] = utils::join(elem.second);
108  campaigns.add_child("campaign", cmp);
109  }
110 
111  set_child(prefs_list::completed_campaigns, campaigns);
112 
113  preferences_[prefs_list::encountered_units] = utils::join(encountered_units_set_);
115  preferences_[prefs_list::encountered_terrain_list] = t_translation::write_list(terrain);
116 
117  /* Structure of the history
118  [history]
119  [history_id]
120  [line]
121  message = foobar
122  [/line]
123  */
124  config history;
125  for(const auto& history_id : history_map_) {
126  config history_id_cfg; // [history_id]
127  for(const std::string& line : history_id.second) {
128  config cfg; // [line]
129 
130  cfg["message"] = line;
131  history_id_cfg.add_child("line", std::move(cfg));
132  }
133 
134  history.add_child(history_id.first, history_id_cfg);
135  }
136  set_child(prefs_list::history, history);
137 
138  preferences_.clear_children("acquaintance");
139 
140  for(auto& a : acquaintances_) {
141  config& item = preferences_.add_child("acquaintance");
142  a.second.save(item);
143  }
144 
145  history_map_.clear();
146  encountered_units_set_.clear();
148 
149  try {
150  if(!no_preferences_save) {
152  }
153  } catch (...) {
154  ERR_FS << "Failed to write preferences due to exception: " << utils::get_unknown_exception_type();
155  }
156 }
157 
159 {
160  for(const config& pref : gc.child_range("advanced_preference")) {
161  try {
162  advanced_prefs_.emplace_back(pref);
163  } catch(const std::invalid_argument& e) {
164  ERR_ADV << e.what();
165  continue;
166  }
167  }
168 
169  // show_deprecation has a different default on the dev branch
170  if(game_config::wesnoth_version.is_dev_version()) {
172  if(op.field == prefs_list::show_deprecation) {
173  op.cfg["default"] = true;
174  }
175  }
176  }
177 
178  std::sort(advanced_prefs_.begin(), advanced_prefs_.end(), [](const auto& lhs, const auto& rhs) { return translation::icompare(lhs.name, rhs.name) < 0; });
179 }
180 
181 void prefs::migrate_preferences(const std::string& migrate_prefs_file)
182 {
183  if(migrate_prefs_file != filesystem::get_synced_prefs_file() && filesystem::file_exists(migrate_prefs_file)) {
184  // if the file doesn't exist, just copy the file over
185  // else need to merge the preferences file
188  } else {
189  config current_cfg;
191  read(current_cfg, *current_stream);
192  config old_cfg;
193  filesystem::scoped_istream old_stream = filesystem::istream_file(migrate_prefs_file, false);
194  read(old_cfg, *old_stream);
195 
196  // when both files have the same attribute, use the one from whichever was most recently modified
197  bool current_prefs_are_older = filesystem::file_modified_time(filesystem::get_synced_prefs_file()) < filesystem::file_modified_time(migrate_prefs_file);
198  for(const auto& [key, value] : old_cfg.attribute_range()) {
199  if(current_prefs_are_older || !current_cfg.has_attribute(key)) {
200  preferences_[key] = value;
201  }
202  }
203 
204  // don't touch child tags
205 
207  }
208  }
209 }
211 {
215 }
216 
217 std::set<std::string> prefs::all_attributes()
218 {
219  std::set<std::string> attrs;
220 
221  // attributes that exist in the preferences file
222  for(const auto& attr : preferences_.attribute_range()) {
223  attrs.emplace(attr.first);
224  }
225  // all mainline preference attributes, whether they're set or not
226  for(const auto attr : prefs_list::values) {
227  attrs.emplace(attr);
228  }
229 
230  return attrs;
231 }
232 
234 {
236  try{
237  config default_prefs;
238  config unsynced_prefs;
239  config synced_prefs;
240 #ifdef DEFAULT_PREFS_PATH
241  // NOTE: the system preferences file is only ever relevant for the first time wesnoth starts
242  // any default values will subsequently be written to the normal preferences files, which takes precedence over any values in the system preferences file
243  {
245  read(default_prefs, *stream);
246  }
247 #endif
248  {
250  read(unsynced_prefs, *stream);
251  }
252 
253  {
255  read(synced_prefs, *stream);
256  }
257 
258  preferences_.merge_with(default_prefs);
259  preferences_.merge_with(unsynced_prefs);
260  preferences_.merge_with(synced_prefs);
261 
262  // check for any unknown preferences
263  for(const auto& [key, _] : synced_prefs.attribute_range()) {
264  if(std::find(synced_attributes_.begin(), synced_attributes_.end(), key) == synced_attributes_.end()) {
265  unknown_synced_attributes_.insert(key);
266  }
267  }
268  for(const auto& [key, _] : unsynced_prefs.attribute_range()) {
269  if(std::find(unsynced_attributes_.begin(), unsynced_attributes_.end(), key) == unsynced_attributes_.end()) {
270  unknown_unsynced_attributes_.insert(key);
271  }
272  }
273 
274  for(const auto [key, _] : synced_prefs.all_children_view()) {
275  if(std::find(synced_children_.begin(), synced_children_.end(), key) == synced_children_.end()) {
276  unknown_synced_children_.insert(key);
277  }
278  }
279  for(const auto [key, _] : unsynced_prefs.all_children_view()) {
280  if(std::find(unsynced_children_.begin(), unsynced_children_.end(), key) == unsynced_children_.end()) {
281  unknown_unsynced_children_.insert(key);
282  }
283  }
284  } catch(const config::error& e) {
285  ERR_CFG << "Error loading preference, message: " << e.what();
286  }
287 
290 
291  /*
292  completed_campaigns = "A,B,C"
293  [completed_campaigns]
294  [campaign]
295  name = "A"
296  difficulty_levels = "EASY,MEDIUM"
297  [/campaign]
298  [/completed_campaigns]
299  */
300  // presumably for backwards compatibility?
301  // nothing actually sets the attribute, only the child tags
302  for(const std::string& c : utils::split(preferences_[prefs_list::completed_campaigns])) {
303  completed_campaigns_[c]; // create the elements
304  }
305 
306  if(auto ccc = get_child(prefs_list::completed_campaigns)) {
307  for(const config& cc : ccc->child_range("campaign")) {
308  std::set<std::string>& d = completed_campaigns_[cc["name"]];
309  std::vector<std::string> nd = utils::split(cc["difficulty_levels"]);
310  std::copy(nd.begin(), nd.end(), std::inserter(d, d.begin()));
311  }
312  }
313 
314  encountered_units_set_ = utils::split_set(preferences_[prefs_list::encountered_units].str());
315 
316  const t_translation::ter_list terrain(t_translation::read_list(preferences_[prefs_list::encountered_terrain_list].str()));
317  encountered_terrains_set_.insert(terrain.begin(), terrain.end());
318 
319  if(auto history = get_child(prefs_list::history)) {
320  /* Structure of the history
321  [history]
322  [history_id]
323  [line]
324  message = foobar
325  [/line]
326  */
327  for(const auto [key, cfg] : history->all_children_view()) {
328  for(const config& l : cfg.child_range("line")) {
329  history_map_[key].push_back(l["message"]);
330  }
331  }
332  }
333 }
334 
336 {
337 #ifndef _WIN32
338  bool synced_prefs_file_existed = filesystem::file_exists(filesystem::get_synced_prefs_file());
339  bool unsynced_prefs_file_existed = filesystem::file_exists(filesystem::get_unsynced_prefs_file());
340 #endif
341 
342  config synced;
343  config unsynced;
344 
345  for(const char* attr : synced_attributes_) {
346  if(preferences_.has_attribute(attr)) {
347  synced[attr] = preferences_[attr];
348  }
349  }
350  for(const char* attr : synced_children_) {
351  for(const auto& child : preferences_.child_range(attr)) {
352  synced.add_child(attr, child);
353  }
354  }
355 
356  for(const char* attr : unsynced_attributes_) {
357  if(preferences_.has_attribute(attr)) {
358  unsynced[attr] = preferences_[attr];
359  }
360  }
361  for(const char* attr : unsynced_children_) {
362  for(const auto& child : preferences_.child_range(attr)) {
363  unsynced.add_child(attr, child);
364  }
365  }
366 
367  // write any unknown preferences back out
368  for(const std::string& attr : unknown_synced_attributes_) {
369  synced[attr] = preferences_[attr];
370  }
371  for(const std::string& attr : unknown_synced_children_) {
372  for(const auto& child : preferences_.child_range(attr)) {
373  synced.add_child(attr, child);
374  }
375  }
376 
377  for(const std::string& attr : unknown_unsynced_attributes_) {
378  unsynced[attr] = preferences_[attr];
379  }
380  for(const std::string& attr : unknown_unsynced_children_) {
381  for(const auto& child : preferences_.child_range(attr)) {
382  unsynced.add_child(attr, child);
383  }
384  }
385 
386  try {
388  write(*synced_prefs_file, synced);
389  } catch(const filesystem::io_exception&) {
390  ERR_FS << "error writing to synced preferences file '" << filesystem::get_synced_prefs_file() << "'";
391  }
392 
393  try {
395  write(*unsynced_prefs_file, unsynced);
396  } catch(const filesystem::io_exception&) {
397  ERR_FS << "error writing to unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
398  }
399 
401 
402 #ifndef _WIN32
403  if(!synced_prefs_file_existed) {
404  if(chmod(filesystem::get_synced_prefs_file().c_str(), 0600) == -1) {
405  ERR_FS << "error setting permissions of preferences file '" << filesystem::get_synced_prefs_file() << "'";
406  }
407  }
408  if(!unsynced_prefs_file_existed) {
409  if(chmod(filesystem::get_unsynced_prefs_file().c_str(), 0600) == -1) {
410  ERR_FS << "error setting permissions of unsynced preferences file '" << filesystem::get_unsynced_prefs_file() << "'";
411  }
412  }
413 #endif
414 }
415 
417 {
418  // Zero them before clearing.
419  // Probably overly paranoid, but doesn't hurt?
420  for(auto& cred : credentials_) {
421  std::fill(cred.username.begin(), cred.username.end(), '\0');
422  std::fill(cred.server.begin(), cred.server.end(), '\0');
423  }
424  credentials_.clear();
425 }
426 
428 {
429  if(!remember_password()) {
430  return;
431  }
433  std::string cred_file = filesystem::get_credentials_file();
434  if(!filesystem::file_exists(cred_file)) {
435  return;
436  }
437  filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
438  // Credentials file is a binary blob, so use streambuf iterator
439  preferences::secure_buffer data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
441  if(data.empty() || data[0] != pref_constants::CREDENTIAL_SEPARATOR) {
442  ERR_CFG << "Invalid data in credentials file";
443  return;
444  }
445  for(const std::string& elem : utils::split(std::string(data.begin(), data.end()), pref_constants::CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
446  std::size_t at = elem.find_last_of('@');
447  std::size_t eq = elem.find_first_of('=', at + 1);
448  if(at != std::string::npos && eq != std::string::npos) {
449  preferences::secure_buffer key(elem.begin() + eq + 1, elem.end());
450  credentials_.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), unescape(key));
451  }
452  }
453 }
454 
456 {
457  if(!remember_password()) {
459  return;
460  }
461 
462 #ifndef _WIN32
463  bool creds_file_existed = filesystem::file_exists(filesystem::get_credentials_file());
464 #endif
465 
466  preferences::secure_buffer credentials_data;
467  for(const auto& cred : credentials_) {
468  credentials_data.push_back(pref_constants::CREDENTIAL_SEPARATOR);
469  credentials_data.insert(credentials_data.end(), cred.username.begin(), cred.username.end());
470  credentials_data.push_back('@');
471  credentials_data.insert(credentials_data.end(), cred.server.begin(), cred.server.end());
472  credentials_data.push_back('=');
473  preferences::secure_buffer key_escaped = escape(cred.key);
474  credentials_data.insert(credentials_data.end(), key_escaped.begin(), key_escaped.end());
475  }
476  try {
478  preferences::secure_buffer encrypted = aes_encrypt(credentials_data, build_key("global", get_system_username()));
479  credentials_file->write(reinterpret_cast<const char*>(encrypted.data()), encrypted.size());
480  } catch(const filesystem::io_exception&) {
481  ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'";
482  }
483 
484 #ifndef _WIN32
485  if(!creds_file_existed) {
486  if(chmod(filesystem::get_credentials_file().c_str(), 0600) == -1) {
487  ERR_FS << "error setting permissions of credentials file '" << filesystem::get_credentials_file() << "'";
488  }
489  }
490 #endif
491 }
492 
493 //
494 // helpers
495 //
496 void prefs::set_child(const std::string& key, const config& val) {
498  preferences_.add_child(key, val);
499 }
500 
502 {
503  return preferences_.optional_child(key);
504 }
505 
506 std::string prefs::get(const std::string& key, const std::string& def) {
507  return preferences_[key].empty() ? def : preferences_[key];
508 }
509 
511 {
512  return preferences_[key];
513 }
514 
515 //
516 // accessors
517 //
518 static std::string fix_orb_color_name(const std::string& color) {
519  if (color.substr(0,4) == "orb_") {
520  if(color[4] >= '0' && color[4] <= '9') {
521  return color.substr(5);
522  } else {
523  return color.substr(4);
524  }
525  }
526  return color;
527 }
528 
529 std::string prefs::allied_color() {
530  std::string ally_color = preferences_[prefs_list::ally_orb_color].str();
531  if (ally_color.empty())
533  return fix_orb_color_name(ally_color);
534 }
535 void prefs::set_allied_color(const std::string& color_id) {
537 }
538 
539 std::string prefs::enemy_color() {
541  if (enemy_color.empty())
544 }
545 void prefs::set_enemy_color(const std::string& color_id) {
547 }
548 
549 std::string prefs::moved_color() {
551  if (moved_color.empty())
554 }
555 void prefs::set_moved_color(const std::string& color_id) {
557 }
558 
559 std::string prefs::unmoved_color() {
561  if (unmoved_color.empty())
564 }
565 void prefs::set_unmoved_color(const std::string& color_id) {
567 }
568 
569 std::string prefs::partial_color() {
570  std::string partmoved_color = preferences_[prefs_list::partial_orb_color].str();
571  if (partmoved_color.empty())
573  return fix_orb_color_name(partmoved_color);
574 }
575 void prefs::set_partial_color(const std::string& color_id) {
577 }
578 
580 {
581  const unsigned x_res = preferences_[prefs_list::xresolution].to_unsigned();
582  const unsigned y_res = preferences_[prefs_list::yresolution].to_unsigned();
583 
584  // Either resolution was unspecified, return default.
585  if(x_res == 0 || y_res == 0) {
587  }
588 
589  return point(
590  std::max<unsigned>(x_res, pref_constants::min_window_width),
591  std::max<unsigned>(y_res, pref_constants::min_window_height)
592  );
593 }
594 
595 void prefs::set_resolution(const point& res)
596 {
597  preferences_[prefs_list::xresolution] = std::to_string(res.x);
598  preferences_[prefs_list::yresolution] = std::to_string(res.y);
599 }
600 
602 {
603  // For now this has a minimum value of 1 and a maximum of 4.
604  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);
605 }
606 
608 {
610 }
611 
613 {
614  if(video::headless()) {
615  return true;
616  }
617 
618  return preferences_[prefs_list::turbo].to_bool();
619 }
620 
621 void prefs::set_turbo(bool ison)
622 {
623  preferences_[prefs_list::turbo] = ison;
624 }
625 
627 {
628  // Clip at 80 because if it's too low it'll cause crashes
629  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);
630 }
631 
633 {
635 }
636 
638 {
639  return (size * font_scaling()) / 100;
640 }
641 
643 {
644  return preferences_[prefs_list::keepalive_timeout].to_int(20);
645 }
646 
647 void prefs::keepalive_timeout(int seconds)
648 {
649  preferences_[prefs_list::keepalive_timeout] = std::abs(seconds);
650 }
651 
653 {
654  // Sounds don't sound good on Windows unless the buffer size is 4k,
655  // but this seems to cause crashes on other systems...
656  #ifdef _WIN32
657  const std::size_t buf_size = 4096;
658  #else
659  const std::size_t buf_size = 1024;
660  #endif
661 
662  return preferences_[prefs_list::sound_buffer_size].to_int(buf_size);
663 }
664 
665 void prefs::save_sound_buffer_size(const std::size_t size)
666 {
667  const std::string new_size = std::to_string(size);
668  if (preferences_[prefs_list::sound_buffer_size] == new_size)
669  return;
670 
671  preferences_[prefs_list::sound_buffer_size] = new_size;
672 
674 }
675 
677 {
678  return preferences_[prefs_list::music_volume].to_int(100);
679 }
680 
682 {
683  if(music_volume() == vol) {
684  return;
685  }
686 
689 }
690 
692 {
693  return preferences_[prefs_list::sound_volume].to_int(100);
694 }
695 
697 {
698  if(sound_volume() == vol) {
699  return;
700  }
701 
704 }
705 
707 {
708  return preferences_[prefs_list::bell_volume].to_int(100);
709 }
710 
712 {
713  if(bell_volume() == vol) {
714  return;
715  }
716 
719 }
720 
721 // old pref name had uppercase UI
723 {
725  return preferences_[prefs_list::ui_volume].to_int(100);
726  } else {
727  return preferences_["UI_volume"].to_int(100);
728  }
729 }
730 
731 void prefs::set_ui_volume(int vol)
732 {
733  if(ui_volume() == vol) {
734  return;
735  }
736 
739 }
740 
742 {
743  return preferences_[prefs_list::turn_bell].to_bool(true);
744 }
745 
746 bool prefs::set_turn_bell(bool ison)
747 {
748  if(!turn_bell() && ison) {
750  if(!music_on() && !sound() && !ui_sound_on()) {
751  if(!sound::init_sound()) {
753  return false;
754  }
755  }
756  } else if(turn_bell() && !ison) {
759  if(!music_on() && !sound() && !ui_sound_on())
761  }
762  return true;
763 }
764 
765 // old pref name had uppercase UI
767 {
768  if(preferences_.has_attribute(prefs_list::ui_sound)) {
769  return preferences_[prefs_list::ui_sound].to_bool(true);
770  } else {
771  return preferences_["UI_sound"].to_bool(true);
772  }
773 }
774 
775 bool prefs::set_ui_sound(bool ison)
776 {
777  if(!ui_sound_on() && ison) {
778  preferences_[prefs_list::ui_sound] = true;
779  if(!music_on() && !sound() && !turn_bell()) {
780  if(!sound::init_sound()) {
781  preferences_[prefs_list::ui_sound] = false;
782  return false;
783  }
784  }
785  } else if(ui_sound_on() && !ison) {
786  preferences_[prefs_list::ui_sound] = false;
788  if(!music_on() && !sound() && !turn_bell())
790  }
791  return true;
792 }
793 
795 {
796  return preferences_[prefs_list::message_bell].to_bool(true);
797 }
798 
800 {
801  return preferences_[prefs_list::sound].to_bool(true);
802 }
803 
804 bool prefs::set_sound(bool ison) {
805  if(!sound() && ison) {
807  if(!music_on() && !turn_bell() && !ui_sound_on()) {
808  if(!sound::init_sound()) {
810  return false;
811  }
812  }
813  } else if(sound() && !ison) {
816  if(!music_on() && !turn_bell() && !ui_sound_on())
818  }
819  return true;
820 }
821 
823 {
824  return preferences_[prefs_list::music].to_bool(true);
825 }
826 
827 bool prefs::set_music(bool ison) {
828  if(!music_on() && ison) {
829  preferences_[prefs_list::music] = true;
830  if(!sound() && !turn_bell() && !ui_sound_on()) {
831  if(!sound::init_sound()) {
832  preferences_[prefs_list::music] = false;
833  return false;
834  }
835  }
836  else
838  } else if(music_on() && !ison) {
839  preferences_[prefs_list::music] = false;
840  if(!sound() && !turn_bell() && !ui_sound_on())
842  else
844  }
845  return true;
846 }
847 
849 {
850  return std::clamp<int>(preferences_[prefs_list::scroll].to_int(50), 1, 100);
851 }
852 
853 void prefs::set_scroll_speed(const int new_speed)
854 {
855  preferences_[prefs_list::scroll] = new_speed;
856 }
857 
859 {
860  return preferences_[prefs_list::middle_click_scrolls].to_bool(true);
861 }
862 
864 {
865  return preferences_[prefs_list::scroll_threshold].to_int(10);
866 }
867 
869 {
870  return fps_;
871 }
872 
873 void prefs::set_show_fps(bool value)
874 {
875  fps_ = value;
876 }
877 
879 {
881 }
882 
884 {
886 }
887 
889 {
891  preferences_.clear_children("hotkey");
892 }
893 
894 void prefs::add_alias(const std::string &alias, const std::string &command)
895 {
896  config &alias_list = preferences_.child_or_add("alias");
897  alias_list[alias] = command;
898 }
899 
900 
902 {
903  return get_child(prefs_list::alias);
904 }
905 
906 unsigned int prefs::sample_rate()
907 {
908  return preferences_[prefs_list::sample_rate].to_int(44100);
909 }
910 
911 void prefs::save_sample_rate(const unsigned int rate)
912 {
913  if (sample_rate() == rate)
914  return;
915 
916  preferences_[prefs_list::sample_rate] = rate;
917 
918  // If audio is open, we have to re set sample rate
920 }
921 
923 {
924  return preferences_[prefs_list::confirm_load_save_from_different_version].to_bool(true);
925 }
926 
928 {
929  return preferences_[prefs_list::use_twelve_hour_clock_format].to_bool();
930 }
931 
933 {
934  return sort_order::get_enum(preferences_[prefs_list::addon_manager_saved_order_direction].to_int()).value_or(sort_order::type::none);
935 }
936 
938 {
939  preferences_[prefs_list::addon_manager_saved_order_direction] = sort_order::get_string(value);
940 }
941 
942 bool prefs::achievement(const std::string& content_for, const std::string& id)
943 {
944  for(config& ach : preferences_.child_range(prefs_list::achievements))
945  {
946  if(ach["content_for"].str() == content_for)
947  {
948  std::vector<std::string> ids = utils::split(ach["ids"]);
949  return std::find(ids.begin(), ids.end(), id) != ids.end();
950  }
951  }
952  return false;
953 }
954 
955 void prefs::set_achievement(const std::string& content_for, const std::string& id)
956 {
957  for(config& ach : preferences_.child_range(prefs_list::achievements))
958  {
959  // if achievements already exist for this content and the achievement has not already been set, add it
960  if(ach["content_for"].str() == content_for)
961  {
962  std::vector<std::string> ids = utils::split(ach["ids"]);
963 
964  if(ids.empty())
965  {
966  ach["ids"] = id;
967  }
968  else if(std::find(ids.begin(), ids.end(), id) == ids.end())
969  {
970  ach["ids"] = ach["ids"].str() + "," + id;
971  }
972  ach.remove_children("in_progress", [&id](config cfg){return cfg["id"].str() == id;});
973  return;
974  }
975  }
976 
977  // else no achievements have been set for this content yet
978  config ach;
979  ach["content_for"] = content_for;
980  ach["ids"] = id;
981  preferences_.add_child(prefs_list::achievements, ach);
982 }
983 
984 int prefs::progress_achievement(const std::string& content_for, const std::string& id, int limit, int max_progress, int amount)
985 {
986  if(achievement(content_for, id))
987  {
988  return -1;
989  }
990 
991  for(config& ach : preferences_.child_range(prefs_list::achievements))
992  {
993  // if achievements already exist for this content and the achievement has not already been set, add it
994  if(ach["content_for"].str() == content_for)
995  {
996  // check if this achievement has progressed before - if so then increment it
997  for(config& in_progress : ach.child_range("in_progress"))
998  {
999  if(in_progress["id"].str() == id)
1000  {
1001  // don't let using 'limit' decrease the achievement's current progress
1002  int starting_progress = in_progress["progress_at"].to_int();
1003  if(starting_progress >= limit) {
1004  return starting_progress;
1005  }
1006 
1007  in_progress["progress_at"] = std::clamp(starting_progress + amount, 0, std::min(limit, max_progress));
1008  return in_progress["progress_at"].to_int();
1009  }
1010  }
1011 
1012  // else this is the first time this achievement is progressing
1013  if(amount != 0)
1014  {
1015  config set_progress;
1016  set_progress["id"] = id;
1017  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1018 
1019  config& child = ach.add_child("in_progress", set_progress);
1020  return child["progress_at"].to_int();
1021  }
1022  return 0;
1023  }
1024  }
1025 
1026  // else not only has this achievement not progressed before, this is the first achievement for this achievement group to be added
1027  if(amount != 0)
1028  {
1029  config ach;
1030  config set_progress;
1031 
1032  set_progress["id"] = id;
1033  set_progress["progress_at"] = std::clamp(amount, 0, std::min(limit, max_progress));
1034 
1035  ach["content_for"] = content_for;
1036  ach["ids"] = "";
1037 
1038  config& child = ach.add_child("in_progress", set_progress);
1039  preferences_.add_child(prefs_list::achievements, ach);
1040  return child["progress_at"].to_int();
1041  }
1042  return 0;
1043 }
1044 
1045 bool prefs::sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1046 {
1047  // this achievement is already completed
1048  if(achievement(content_for, id))
1049  {
1050  return true;
1051  }
1052 
1053  for(config& ach : preferences_.child_range(prefs_list::achievements))
1054  {
1055  if(ach["content_for"].str() == content_for)
1056  {
1057  // check if the specific sub-achievement has been completed but the overall achievement is not completed
1058  for(const auto& in_progress : ach.child_range("in_progress"))
1059  {
1060  if(in_progress["id"] == id)
1061  {
1062  std::vector<std::string> sub_ids = utils::split(in_progress["sub_ids"]);
1063  return std::find(sub_ids.begin(), sub_ids.end(), sub_id) != sub_ids.end();
1064  }
1065  }
1066  }
1067  }
1068  return false;
1069 }
1070 
1071 void prefs::set_sub_achievement(const std::string& content_for, const std::string& id, const std::string& sub_id)
1072 {
1073  // this achievement is already completed
1074  if(achievement(content_for, id))
1075  {
1076  return;
1077  }
1078 
1079  for(config& ach : preferences_.child_range(prefs_list::achievements))
1080  {
1081  // if achievements already exist for this content and the achievement has not already been set, add it
1082  if(ach["content_for"].str() == content_for)
1083  {
1084  // check if this achievement has had sub-achievements set before
1085  for(config& in_progress : ach.child_range("in_progress"))
1086  {
1087  if(in_progress["id"].str() == id)
1088  {
1089  std::vector<std::string> sub_ids = utils::split(ach["ids"]);
1090 
1091  if(std::find(sub_ids.begin(), sub_ids.end(), sub_id) == sub_ids.end())
1092  {
1093  in_progress["sub_ids"] = in_progress["sub_ids"].str() + "," + sub_id;
1094  }
1095 
1096  in_progress["progress_at"] = sub_ids.size()+1;
1097  return;
1098  }
1099  }
1100 
1101  // else if this is the first sub-achievement being set
1102  config set_progress;
1103  set_progress["id"] = id;
1104  set_progress["sub_ids"] = sub_id;
1105  set_progress["progress_at"] = 1;
1106  ach.add_child("in_progress", set_progress);
1107  return;
1108  }
1109  }
1110 
1111  // 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
1112  config ach;
1113  config set_progress;
1114 
1115  set_progress["id"] = id;
1116  set_progress["sub_ids"] = sub_id;
1117  set_progress["progress_at"] = 1;
1118 
1119  ach["content_for"] = content_for;
1120  ach["ids"] = "";
1121 
1122  ach.add_child("in_progress", set_progress);
1123  preferences_.add_child(prefs_list::achievements, ach);
1124 }
1125 
1127 {
1128  return preferences_[prefs_list::show_deprecation].to_bool(def);
1129 }
1130 
1132 {
1133  return preferences_[prefs_list::scroll_when_mouse_outside].to_bool(def);
1134 }
1135 
1137 {
1138  set_child(prefs_list::dir_bookmarks, cfg);
1139 }
1141 {
1142  return get_child(prefs_list::dir_bookmarks);
1143 }
1144 
1146 {
1147  return preferences_[prefs_list::lobby_auto_open_whisper_windows].to_bool(true);
1148 }
1149 
1151 {
1152  return std::max(std::size_t(1), preferences_[prefs_list::editor_max_recent_files].to_size_t(10));
1153 }
1154 
1155 //
1156 // NOTE: The MRU read/save functions enforce the entry count limit in
1157 // order to ensure the list on disk doesn't grow forever. Otherwise,
1158 // normally this would be the UI's responsibility instead.
1159 //
1160 
1161 std::vector<std::string> prefs::do_read_editor_mru()
1162 {
1163  auto cfg = get_child(prefs_list::editor_recent_files);
1164 
1165  std::vector<std::string> mru;
1166  if(!cfg) {
1167  return mru;
1168  }
1169 
1170  for(const config& child : cfg->child_range("entry"))
1171  {
1172  const std::string& entry = child["path"].str();
1173  if(!entry.empty()) {
1174  mru.push_back(entry);
1175  }
1176  }
1177 
1178  mru.resize(std::min(editor_mru_limit(), mru.size()));
1179 
1180  return mru;
1181 }
1182 
1183 void prefs::do_commit_editor_mru(const std::vector<std::string>& mru)
1184 {
1185  config cfg;
1186  unsigned n = 0;
1187 
1188  for(const std::string& entry : mru)
1189  {
1190  if(entry.empty()) {
1191  continue;
1192  }
1193 
1194  config& child = cfg.add_child("entry");
1195  child["path"] = entry;
1196 
1197  if(++n >= editor_mru_limit()) {
1198  break;
1199  }
1200  }
1201 
1202  set_child(prefs_list::editor_recent_files, cfg);
1203 }
1204 
1205 std::vector<std::string> prefs::recent_files()
1206 {
1207  return do_read_editor_mru();
1208 }
1209 
1210 void prefs::add_recent_files_entry(const std::string& path)
1211 {
1212  if(path.empty()) {
1213  return;
1214  }
1215 
1216  std::vector<std::string> mru = do_read_editor_mru();
1217 
1218  // Enforce uniqueness. Normally shouldn't do a thing unless somebody
1219  // has been tampering with the preferences file.
1220  utils::erase(mru, path);
1221 
1222  mru.insert(mru.begin(), path);
1223  mru.resize(std::min(editor_mru_limit(), mru.size()));
1224 
1225  do_commit_editor_mru(mru);
1226 }
1227 
1229 {
1230  return preferences_[prefs_list::color_cursors].to_bool(true);
1231 }
1232 
1234 {
1235  preferences_[prefs_list::color_cursors] = value;
1236 
1237  cursor::set();
1238 }
1239 
1241 {
1242  return preferences_[prefs_list::unit_standing_animations].to_bool(true);
1243 }
1244 
1246 {
1247  preferences_[prefs_list::unit_standing_animations] = value;
1248 
1249  if(display* d = display::get_singleton()) {
1250  d->reset_standing_animations();
1251  }
1252 }
1253 
1255 {
1256  std::vector<theme_info> themes = theme::get_basic_theme_info();
1257 
1258  if (themes.empty()) {
1260  _("No known themes. Try changing from within an existing game."));
1261 
1262  return false;
1263  }
1264 
1265  gui2::dialogs::theme_list dlg(themes);
1266 
1267  for (std::size_t k = 0; k < themes.size(); ++k) {
1268  if(themes[k].id == theme()) {
1269  dlg.set_selected_index(static_cast<int>(k));
1270  }
1271  }
1272 
1273  dlg.show();
1274  const int action = dlg.selected_index();
1275 
1276  if (action >= 0) {
1277  set_theme(themes[action].id);
1278  if(display::get_singleton() && resources::gamedata && resources::gamedata->get_theme().empty()) {
1279  display::get_singleton()->set_theme(themes[action].id);
1280  }
1281 
1282  return true;
1283  }
1284 
1285  return false;
1286 }
1287 
1289 {
1290  const std::string filename = filesystem::get_wesnothd_name();
1291 
1292  const std::string& old_path = filesystem::directory_name(get_mp_server_program_name());
1293  std::string path =
1294  !old_path.empty() && filesystem::is_directory(old_path)
1295  ? old_path : filesystem::get_exe_dir();
1296 
1297  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}});
1298 
1300 
1301  dlg.set_title(_("Find Server Application"))
1302  .set_message(msg)
1303  .set_ok_label(_("Select"))
1304  .set_read_only(true)
1306  .set_path(path);
1307 
1308  if(dlg.show()) {
1309  path = dlg.path();
1311  }
1312 }
1313 
1314 std::string prefs::theme()
1315 {
1316  if(video::headless()) {
1317  static const std::string null_theme = "null";
1318  return null_theme;
1319  }
1320 
1321  std::string res = preferences_[prefs_list::theme];
1322  if(res.empty()) {
1323  return "Default";
1324  }
1325 
1326  return res;
1327 }
1328 
1329 void prefs::set_theme(const std::string& theme)
1330 {
1331  if(theme != "null") {
1332  preferences_[prefs_list::theme] = theme;
1333  }
1334 }
1335 
1336 void prefs::set_mp_server_program_name(const std::string& path)
1337 {
1338  if(path.empty()) {
1339  preferences_.remove_attribute(prefs_list::mp_server_program_name);
1340  } else {
1341  preferences_[prefs_list::mp_server_program_name] = path;
1342  }
1343 }
1344 
1346 {
1347  return preferences_[prefs_list::mp_server_program_name].str();
1348 }
1349 
1350 const std::map<std::string, preferences::acquaintance>& prefs::get_acquaintances()
1351 {
1352  return acquaintances_;
1353 }
1354 
1355 const std::string prefs::get_ignored_delim()
1356 {
1357  std::vector<std::string> ignored;
1358 
1359  for(const auto& person : acquaintances_) {
1360  if(person.second.get_status() == "ignore") {
1361  ignored.push_back(person.second.get_nick());
1362  }
1363  }
1364 
1365  return utils::join(ignored);
1366 }
1367 
1368 // returns acquaintances in the form nick => notes where the status = filter
1369 std::map<std::string, std::string> prefs::get_acquaintances_nice(const std::string& filter)
1370 {
1371  std::map<std::string, std::string> ac_nice;
1372 
1373  for(const auto& a : acquaintances_) {
1374  if(a.second.get_status() == filter) {
1375  ac_nice[a.second.get_nick()] = a.second.get_notes();
1376  }
1377  }
1378 
1379  return ac_nice;
1380 }
1381 
1382 std::pair<preferences::acquaintance*, bool> prefs::add_acquaintance(const std::string& nick, const std::string& mode, const std::string& notes)
1383 {
1384  if(!utils::isvalid_wildcard(nick)) {
1385  return std::pair(nullptr, false);
1386  }
1387 
1388  preferences::acquaintance new_entry(nick, mode, notes);
1389  auto [iter, added_new] = acquaintances_.insert_or_assign(nick, new_entry);
1390 
1391  return std::pair(&iter->second, added_new);
1392 }
1393 
1394 bool prefs::remove_acquaintance(const std::string& nick)
1395 {
1397 
1398  // nick might include the notes, depending on how we're removing
1399  if(i == acquaintances_.end()) {
1400  std::size_t pos = nick.find_first_of(' ');
1401 
1402  if(pos != std::string::npos) {
1403  i = acquaintances_.find(nick.substr(0, pos));
1404  }
1405  }
1406 
1407  if(i == acquaintances_.end()) {
1408  return false;
1409  }
1410 
1411  acquaintances_.erase(i);
1412 
1413  return true;
1414 }
1415 
1416 bool prefs::is_friend(const std::string& nick)
1417 {
1418  const auto it = acquaintances_.find(nick);
1419 
1420  if(it == acquaintances_.end()) {
1421  return false;
1422  } else {
1423  return it->second.get_status() == "friend";
1424  }
1425 }
1426 
1427 bool prefs::is_ignored(const std::string& nick)
1428 {
1429  const auto it = acquaintances_.find(nick);
1430 
1431  if(it == acquaintances_.end()) {
1432  return false;
1433  } else {
1434  return it->second.get_status() == "ignore";
1435  }
1436 }
1437 
1438 void prefs::add_completed_campaign(const std::string& campaign_id, const std::string& difficulty_level)
1439 {
1440  completed_campaigns_[campaign_id].insert(difficulty_level);
1441 }
1442 
1443 bool prefs::is_campaign_completed(const std::string& campaign_id)
1444 {
1445  return completed_campaigns_.count(campaign_id) != 0;
1446 }
1447 
1448 bool prefs::is_campaign_completed(const std::string& campaign_id, const std::string& difficulty_level)
1449 {
1450  const auto it = completed_campaigns_.find(campaign_id);
1451  return it == completed_campaigns_.end() ? false : it->second.count(difficulty_level) != 0;
1452 }
1453 
1454 bool prefs::parse_should_show_lobby_join(const std::string& sender, const std::string& message)
1455 {
1456  // If it's actually not a lobby join or leave message return true (show it).
1457  if(sender != "server") {
1458  return true;
1459  }
1460 
1461  std::string::size_type pos = message.find(" has logged into the lobby");
1462  if(pos == std::string::npos) {
1463  pos = message.find(" has disconnected");
1464  if(pos == std::string::npos) {
1465  return true;
1466  }
1467  }
1468 
1471  return false;
1472  }
1473 
1475  return true;
1476  }
1477 
1478  return is_friend(message.substr(0, pos));
1479 }
1480 
1482 {
1483  std::string pref = preferences_[prefs_list::lobby_joins];
1484  if(pref == "friends") {
1486  } else if(pref == "all") {
1488  } else if(pref == "none") {
1490  } else {
1492  }
1493 }
1494 
1496 {
1503  }
1504 }
1505 
1506 const std::vector<game_config::server_info>& prefs::builtin_servers_list()
1507 {
1508  static std::vector<game_config::server_info> pref_servers = game_config::server_list;
1509  return pref_servers;
1510 }
1511 
1512 std::vector<game_config::server_info> prefs::user_servers_list()
1513 {
1514  std::vector<game_config::server_info> pref_servers;
1515 
1516  for(const config& server : preferences_.child_range("server")) {
1517  pref_servers.emplace_back();
1518  pref_servers.back().name = server["name"].str();
1519  pref_servers.back().address = server["address"].str();
1520  }
1521 
1522  return pref_servers;
1523 }
1524 
1525 void prefs::set_user_servers_list(const std::vector<game_config::server_info>& value)
1526 {
1527  preferences_.clear_children("server");
1528 
1529  for(const auto& svinfo : value) {
1530  config& sv_cfg = preferences_.add_child("server");
1531  sv_cfg["name"] = svinfo.name;
1532  sv_cfg["address"] = svinfo.address;
1533  }
1534 }
1535 
1536 std::string prefs::network_host()
1537 {
1538  const std::string res = preferences_[prefs_list::host];
1539  if(res.empty()) {
1540  return builtin_servers_list().front().address;
1541  } else {
1542  return res;
1543  }
1544 }
1545 
1546 void prefs::set_network_host(const std::string& host)
1547 {
1548  preferences_[prefs_list::host] = host;
1549 }
1550 
1552 {
1553  if(!preferences_[prefs_list::campaign_server].empty()) {
1554  return preferences_[prefs_list::campaign_server].str();
1555  } else {
1557  }
1558 }
1559 
1560 void prefs::set_campaign_server(const std::string& host)
1561 {
1562  preferences_[prefs_list::campaign_server] = host;
1563 }
1564 
1566 {
1567  return preferences_[prefs_list::show_combat].to_bool(true);
1568 }
1569 
1571 {
1572  if(options_initialized_) {
1573  return option_values_;
1574  }
1575 
1576  if(!get_child(prefs_list::options)) {
1577  // It may be an invalid config, which would cause problems in
1578  // multiplayer_create, so let's replace it with an empty but valid
1579  // config
1581  } else {
1582  option_values_ = *get_child(prefs_list::options);
1583  }
1584 
1585  options_initialized_ = true;
1586 
1587  return option_values_;
1588 }
1589 
1590 void prefs::set_options(const config& values)
1591 {
1592  set_child(prefs_list::options, values);
1593  options_initialized_ = false;
1594 }
1595 
1597 {
1598  return std::clamp<int>(preferences_[prefs_list::mp_countdown_init_time].to_int(240), 0, 1500);
1599 }
1600 
1602 {
1603  preferences_[prefs_list::mp_countdown_init_time] = value;
1604 }
1605 
1607 {
1608  preferences_.remove_attribute(prefs_list::mp_countdown_init_time);
1609 }
1610 
1612 {
1613  return std::clamp<int>(preferences_[prefs_list::mp_countdown_reservoir_time].to_int(360), 30, 1500);
1614 }
1615 
1617 {
1618  preferences_[prefs_list::mp_countdown_reservoir_time] = value;
1619 }
1620 
1622 {
1623  preferences_.remove_attribute(prefs_list::mp_countdown_reservoir_time);
1624 }
1625 
1627 {
1628  return std::clamp<int>(preferences_[prefs_list::mp_countdown_turn_bonus].to_int(240), 0, 300);
1629 }
1630 
1632 {
1633  preferences_[prefs_list::mp_countdown_turn_bonus] = value;
1634 }
1635 
1637 {
1638  preferences_.remove_attribute(prefs_list::mp_countdown_turn_bonus);
1639 }
1640 
1642 {
1643  return std::clamp<int>(preferences_[prefs_list::mp_countdown_action_bonus].to_int(), 0, 30);
1644 }
1645 
1647 {
1648  preferences_[prefs_list::mp_countdown_action_bonus] = value;
1649 }
1650 
1652 {
1653  preferences_.remove_attribute(prefs_list::mp_countdown_action_bonus);
1654 }
1655 
1657 {
1658  return settings::get_village_gold(preferences_[prefs_list::mp_village_gold]);
1659 }
1660 
1662 {
1663  preferences_[prefs_list::mp_village_gold] = value;
1664 }
1665 
1667 {
1668  return settings::get_village_support(preferences_[prefs_list::mp_village_support]);
1669 }
1670 
1672 {
1673  preferences_[prefs_list::mp_village_support] = std::to_string(value);
1674 }
1675 
1677 {
1678  return settings::get_xp_modifier(preferences_[prefs_list::mp_xp_modifier]);
1679 }
1680 
1681 void prefs::set_xp_modifier(int value)
1682 {
1683  preferences_[prefs_list::mp_xp_modifier] = value;
1684 }
1685 
1686 const std::vector<std::string>& prefs::modifications(bool mp)
1687 {
1689  if(mp) {
1690  mp_modifications_ = utils::split(preferences_[prefs_list::mp_modifications].str(), ',');
1692  } else {
1693  sp_modifications_ = utils::split(preferences_[prefs_list::sp_modifications].str(), ',');
1695  }
1696  }
1697 
1699 }
1700 
1701 void prefs::set_modifications(const std::vector<std::string>& value, bool mp)
1702 {
1703  if(mp) {
1704  preferences_[prefs_list::mp_modifications] = utils::join(value, ",");
1706  } else {
1707  preferences_[prefs_list::sp_modifications] = utils::join(value, ",");
1709  }
1710 }
1711 
1713 {
1714  return message_private_on_;
1715 }
1716 
1718 {
1719  message_private_on_ = value;
1720 }
1721 
1723 {
1724  const std::string& choice = preferences_[prefs_list::compress_saves];
1725 
1726  // "yes" was used in 1.11.7 and earlier; the compress_saves
1727  // option used to be a toggle for gzip in those versions.
1728  if(choice.empty() || choice == "gzip" || choice == "yes") {
1730  } else if(choice == "bzip2") {
1732  } else if(choice == "none" || choice == "no") { // see above
1734  } /*else*/
1735 
1736  // In case the preferences file was created by a later version
1737  // supporting some algorithm we don't; although why would anyone
1738  // playing a game need more algorithms, really...
1740 }
1741 
1742 std::string prefs::get_chat_timestamp(const std::time_t& t)
1743 {
1744  if(chat_timestamp()) {
1745  if(use_twelve_hour_clock_format() == false) {
1746  return lg::get_timestamp(t, _("[%H:%M]")) + " ";
1747  } else {
1748  return lg::get_timestamp(t, _("[%I:%M %p]")) + " ";
1749  }
1750  }
1751 
1752  return "";
1753 }
1754 
1755 std::set<std::string>& prefs::encountered_units()
1756 {
1757  return encountered_units_set_;
1758 }
1759 
1760 std::set<t_translation::terrain_code>& prefs::encountered_terrains()
1761 {
1763 }
1764 
1765 /**
1766  * Returns a pointer to the history vector associated with given id
1767  * making a new one if it doesn't exist.
1768  *
1769  * @todo FIXME only used for gui2. Could be used for the above histories.
1770  */
1771 std::vector<std::string>* prefs::get_history(const std::string& id)
1772 {
1773  return &history_map_[id];
1774 }
1775 
1777 {
1778  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1779  return confirmation == "green" || confirmation == "yes";
1780 }
1781 
1783 {
1784  return preferences_[prefs_list::confirm_end_turn] == "yellow";
1785 }
1786 
1788 {
1789  // This is very non-intrusive so it is on by default
1790  const std::string confirmation = preferences_[prefs_list::confirm_end_turn];
1791  return confirmation == "no_moves" || confirmation.empty();
1792 }
1793 
1794 void prefs::encounter_recruitable_units(const std::vector<team>& teams)
1795 {
1796  for(const team& help_team : teams) {
1797  help_team.log_recruitable();
1798  encountered_units_set_.insert(help_team.recruits().begin(), help_team.recruits().end());
1799  }
1800 }
1801 
1803 {
1804  for(const auto& help_unit : units) {
1805  encountered_units_set_.insert(help_unit.type_id());
1806  }
1807 }
1808 
1809 void prefs::encounter_recallable_units(const std::vector<team>& teams)
1810 {
1811  for(const team& t : teams) {
1812  for(const unit_const_ptr u : t.recall_list()) {
1813  encountered_units_set_.insert(u->type_id());
1814  }
1815  }
1816 }
1817 
1819 {
1820  map.for_each_loc([&](const map_location& loc) {
1821  const t_translation::terrain_code terrain = map.get_terrain(loc);
1822  encountered_terrains().insert(terrain);
1824  encountered_terrains().insert(t);
1825  }
1826  });
1827 }
1828 
1830 {
1831  encounter_recruitable_units(gameboard_.teams());
1832  encounter_start_units(gameboard_.units());
1833  encounter_recallable_units(gameboard_.teams());
1834  encounter_map_terrain(gameboard_.map());
1835 }
1836 
1838 {
1839  preferences_.remove_attribute(prefs_list::player_joins_sound);
1840  preferences_.remove_attribute(prefs_list::player_joins_notif);
1841  preferences_.remove_attribute(prefs_list::player_joins_lobby);
1842  preferences_.remove_attribute(prefs_list::player_leaves_sound);
1843  preferences_.remove_attribute(prefs_list::player_leaves_notif);
1844  preferences_.remove_attribute(prefs_list::player_leaves_lobby);
1845  preferences_.remove_attribute(prefs_list::private_message_sound);
1846  preferences_.remove_attribute(prefs_list::private_message_notif);
1847  preferences_.remove_attribute(prefs_list::private_message_lobby);
1848  preferences_.remove_attribute(prefs_list::friend_message_sound);
1849  preferences_.remove_attribute(prefs_list::friend_message_notif);
1850  preferences_.remove_attribute(prefs_list::friend_message_lobby);
1851  preferences_.remove_attribute(prefs_list::public_message_sound);
1852  preferences_.remove_attribute(prefs_list::public_message_notif);
1853  preferences_.remove_attribute(prefs_list::public_message_lobby);
1854  preferences_.remove_attribute(prefs_list::server_message_sound);
1855  preferences_.remove_attribute(prefs_list::server_message_notif);
1856  preferences_.remove_attribute(prefs_list::server_message_lobby);
1857  preferences_.remove_attribute(prefs_list::ready_for_start_sound);
1858  preferences_.remove_attribute(prefs_list::ready_for_start_notif);
1859  preferences_.remove_attribute(prefs_list::ready_for_start_lobby);
1860  preferences_.remove_attribute(prefs_list::game_has_begun_sound);
1861  preferences_.remove_attribute(prefs_list::game_has_begun_notif);
1862  preferences_.remove_attribute(prefs_list::game_has_begun_lobby);
1863  preferences_.remove_attribute(prefs_list::turn_changed_sound);
1864  preferences_.remove_attribute(prefs_list::turn_changed_notif);
1865  preferences_.remove_attribute(prefs_list::turn_changed_lobby);
1866  preferences_.remove_attribute(prefs_list::game_created_sound);
1867  preferences_.remove_attribute(prefs_list::game_created_notif);
1868  preferences_.remove_attribute(prefs_list::game_created_lobby);
1869 }
1870 
1872 {
1873 #ifdef _WIN32
1874  wchar_t buffer[300];
1875  DWORD size = 300;
1876  if(GetUserNameW(buffer, &size)) {
1877  //size includes a terminating null character.
1878  assert(size > 0);
1879  return unicode_cast<std::string>(std::wstring_view{buffer});
1880  }
1881 #else
1882  if(char* const login = getenv("USER")) {
1883  return login;
1884  }
1885 #endif
1886  return {};
1887 }
1888 
1889 preferences::secure_buffer prefs::build_key(const std::string& server, const std::string& login)
1890 {
1891  std::string sysname = get_system_username();
1892  preferences::secure_buffer result(std::max<std::size_t>(server.size() + login.size() + sysname.size(), 32));
1893  unsigned char i = 0;
1894  std::generate(result.begin(), result.end(), [&i]() {return 'x' ^ i++;});
1895  std::copy(login.begin(), login.end(), result.begin());
1896  std::copy(sysname.begin(), sysname.end(), result.begin() + login.size());
1897  std::copy(server.begin(), server.end(), result.begin() + login.size() + sysname.size());
1898  return result;
1899 }
1900 
1902 {
1903 #ifndef __APPLE__
1904  int update_length;
1905  int extra_length;
1906  int total_length;
1907  // AES IV is generally 128 bits
1908  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1909  unsigned char encrypted_buffer[1024];
1910 
1911  if(plaintext.size() > 1008)
1912  {
1913  ERR_CFG << "Cannot encrypt data larger than 1008 bytes.";
1914  return preferences::secure_buffer();
1915  }
1916  DBG_CFG << "Encrypting data with length: " << plaintext.size();
1917 
1918  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1919  if(!ctx)
1920  {
1921  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
1922  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1923  return preferences::secure_buffer();
1924  }
1925 
1926  // TODO: use EVP_EncryptInit_ex2 once openssl 3.0 is more widespread
1927  if(EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
1928  {
1929  ERR_CFG << "AES EVP_EncryptInit_ex failed with error:";
1930  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1931  EVP_CIPHER_CTX_free(ctx);
1932  return preferences::secure_buffer();
1933  }
1934 
1935  if(EVP_EncryptUpdate(ctx, encrypted_buffer, &update_length, plaintext.data(), plaintext.size()) != 1)
1936  {
1937  ERR_CFG << "AES EVP_EncryptUpdate failed with error:";
1938  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1939  EVP_CIPHER_CTX_free(ctx);
1940  return preferences::secure_buffer();
1941  }
1942  DBG_CFG << "Update length: " << update_length;
1943 
1944  if(EVP_EncryptFinal_ex(ctx, encrypted_buffer + update_length, &extra_length) != 1)
1945  {
1946  ERR_CFG << "AES EVP_EncryptFinal failed with error:";
1947  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
1948  EVP_CIPHER_CTX_free(ctx);
1949  return preferences::secure_buffer();
1950  }
1951  DBG_CFG << "Extra length: " << extra_length;
1952 
1953  EVP_CIPHER_CTX_free(ctx);
1954 
1955  total_length = update_length+extra_length;
1957  for(int i = 0; i < total_length; i++)
1958  {
1959  result.push_back(encrypted_buffer[i]);
1960  }
1961 
1962  DBG_CFG << "Successfully encrypted plaintext value of '" << utils::join(plaintext, "") << "' having length " << plaintext.size();
1963  DBG_CFG << "For a total encrypted length of: " << total_length;
1964 
1965  return result;
1966 #else
1967  size_t outWritten = 0;
1968  preferences::secure_buffer result(plaintext.size(), '\0');
1969 
1970  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
1971  kCCAlgorithmRC4,
1972  kCCOptionPKCS7Padding,
1973  key.data(),
1974  key.size(),
1975  nullptr,
1976  plaintext.data(),
1977  plaintext.size(),
1978  result.data(),
1979  result.size(),
1980  &outWritten);
1981 
1982  assert(ccStatus == kCCSuccess);
1983  assert(outWritten == plaintext.size());
1984 
1985  return result;
1986 #endif
1987 }
1988 
1990 {
1991 #ifndef __APPLE__
1992  int update_length;
1993  int extra_length;
1994  int total_length;
1995  // AES IV is generally 128 bits
1996  const unsigned char iv[] = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};
1997  unsigned char plaintext_buffer[1024];
1998 
1999  if(encrypted.size() > 1024)
2000  {
2001  ERR_CFG << "Cannot decrypt data larger than 1024 bytes.";
2002  return preferences::secure_buffer();
2003  }
2004  DBG_CFG << "Decrypting data with length: " << encrypted.size();
2005 
2006  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
2007  if(!ctx)
2008  {
2009  ERR_CFG << "AES EVP_CIPHER_CTX_new failed with error:";
2010  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2011  return preferences::secure_buffer();
2012  }
2013 
2014  // TODO: use EVP_DecryptInit_ex2 once openssl 3.0 is more widespread
2015  if(EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key.data(), iv) != 1)
2016  {
2017  ERR_CFG << "AES EVP_DecryptInit_ex failed with error:";
2018  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2019  EVP_CIPHER_CTX_free(ctx);
2020  return preferences::secure_buffer();
2021  }
2022 
2023  if(EVP_DecryptUpdate(ctx, plaintext_buffer, &update_length, encrypted.data(), encrypted.size()) != 1)
2024  {
2025  ERR_CFG << "AES EVP_DecryptUpdate failed with error:";
2026  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2027  EVP_CIPHER_CTX_free(ctx);
2028  return preferences::secure_buffer();
2029  }
2030  DBG_CFG << "Update length: " << update_length;
2031 
2032  if(EVP_DecryptFinal_ex(ctx, plaintext_buffer + update_length, &extra_length) != 1)
2033  {
2034  ERR_CFG << "AES EVP_DecryptFinal failed with error:";
2035  ERR_CFG << ERR_error_string(ERR_get_error(), NULL);
2036  EVP_CIPHER_CTX_free(ctx);
2037  return preferences::secure_buffer();
2038  }
2039  DBG_CFG << "Extra length: " << extra_length;
2040 
2041  EVP_CIPHER_CTX_free(ctx);
2042 
2043  total_length = update_length+extra_length;
2045  for(int i = 0; i < total_length; i++)
2046  {
2047  result.push_back(plaintext_buffer[i]);
2048  }
2049 
2050  DBG_CFG << "Successfully decrypted data to the value: " << utils::join(result, "");
2051  DBG_CFG << "For a total decrypted length of: " << total_length;
2052 
2053  return result;
2054 #else
2055  size_t outWritten = 0;
2056  preferences::secure_buffer result(encrypted.size(), '\0');
2057 
2058  CCCryptorStatus ccStatus = CCCrypt(kCCDecrypt,
2059  kCCAlgorithmRC4,
2060  kCCOptionPKCS7Padding,
2061  key.data(),
2062  key.size(),
2063  nullptr,
2064  encrypted.data(),
2065  encrypted.size(),
2066  result.data(),
2067  result.size(),
2068  &outWritten);
2069 
2070  assert(ccStatus == kCCSuccess);
2071  assert(outWritten == encrypted.size());
2072 
2073  // the decrypted result is likely shorter than the encrypted data, so the extra padding needs to be removed.
2074  while(!result.empty() && result.back() == 0) {
2075  result.pop_back();
2076  }
2077 
2078  return result;
2079 #endif
2080 }
2081 
2083 {
2084  preferences::secure_buffer unescaped;
2085  unescaped.reserve(text.size());
2086  bool escaping = false;
2087  for(char c : text) {
2088  if(escaping) {
2089  if(c == '\xa') {
2090  unescaped.push_back('\xc');
2091  } else if(c == '.') {
2092  unescaped.push_back('@');
2093  } else {
2094  unescaped.push_back(c);
2095  }
2096  escaping = false;
2097  } else if(c == '\x1') {
2098  escaping = true;
2099  } else {
2100  unescaped.push_back(c);
2101  }
2102  }
2103  assert(!escaping);
2104  return unescaped;
2105 }
2106 
2108 {
2110  escaped.reserve(text.size());
2111  for(char c : text) {
2112  if(c == '\x1') {
2113  escaped.push_back('\x1');
2114  escaped.push_back('\x1');
2115  } else if(c == '\xc') {
2116  escaped.push_back('\x1');
2117  escaped.push_back('\xa');
2118  } else if(c == '@') {
2119  escaped.push_back('\x1');
2120  escaped.push_back('.');
2121  } else {
2122  escaped.push_back(c);
2123  }
2124  }
2125  return escaped;
2126 }
2127 
2129 {
2130  return preferences_[prefs_list::remember_password].to_bool();
2131 }
2132 
2133 void prefs::set_remember_password(bool remember)
2134 {
2135  preferences_[prefs_list::remember_password] = remember;
2136 
2137  if(remember) {
2138  load_credentials();
2139  } else {
2141  }
2142 }
2143 
2144 std::string prefs::login()
2145 {
2146  std::string name = get("login", pref_constants::EMPTY_LOGIN);
2147  if(name == pref_constants::EMPTY_LOGIN) {
2148  name = get_system_username();
2149  } else if(name.size() > 2 && name.front() == '@' && name.back() == '@') {
2150  name = name.substr(1, name.size() - 2);
2151  } else {
2152  ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)";
2153  }
2154  if(name.empty()) {
2155  return "player";
2156  }
2157  return name;
2158 }
2159 
2160 void prefs::set_login(const std::string& login)
2161 {
2162  auto login_clean = login;
2163  boost::trim(login_clean);
2164 
2165  preferences_[prefs_list::login] = '@' + login_clean + '@';
2166 }
2167 
2168 std::string prefs::password(const std::string& server, const std::string& login)
2169 {
2170  DBG_CFG << "Retrieving password for server: '" << server << "', login: '" << login << "'";
2171  auto login_clean = login;
2172  boost::trim(login_clean);
2173 
2174  if(!remember_password()) {
2175  if(!credentials_.empty() && credentials_[0].username == login_clean && credentials_[0].server == server) {
2176  auto temp = aes_decrypt(credentials_[0].key, build_key(server, login_clean));
2177  return std::string(temp.begin(), temp.end());
2178  } else {
2179  return "";
2180  }
2181  }
2182  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2183  return cred.server == server && cred.username == login_clean;
2184  });
2185  if(cred == credentials_.end()) {
2186  return "";
2187  }
2188  auto temp = aes_decrypt(cred->key, build_key(server, login_clean));
2189  return std::string(temp.begin(), temp.end());
2190 }
2191 
2192 void prefs::set_password(const std::string& server, const std::string& login, const std::string& key)
2193 {
2194  DBG_CFG << "Setting password for server: '" << server << "', login: '" << login << "'";
2195  auto login_clean = login;
2196  boost::trim(login_clean);
2197 
2198  preferences::secure_buffer temp(key.begin(), key.end());
2199  if(!remember_password()) {
2201  credentials_.emplace_back(login_clean, server, aes_encrypt(temp, build_key(server, login_clean)));
2202  return;
2203  }
2204  auto cred = std::find_if(credentials_.begin(), credentials_.end(), [&](const preferences::login_info& cred) {
2205  return cred.server == server && cred.username == login_clean;
2206  });
2207  if(cred == credentials_.end()) {
2208  // This is equivalent to emplace_back, but also returns the iterator to the new element
2209  cred = credentials_.emplace(credentials_.end(), login_clean, server);
2210  }
2211  cred->key = aes_encrypt(temp, build_key(server, login_clean));
2212 }
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:172
const_attr_itors attribute_range() const
Definition: config.cpp:760
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
void clear_children(T... keys)
Definition: config.hpp:616
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1123
child_itors child_range(config_key_type key)
Definition: config.cpp:272
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:405
void remove_attribute(config_key_type key)
Definition: config.cpp:162
bool empty() const
Definition: config.cpp:849
void clear()
Definition: config.cpp:828
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:384
config & add_child(config_key_type key)
Definition: config.cpp:440
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:97
void set_theme(const std::string &new_theme)
Definition: display.cpp:247
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:111
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:302
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:59
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:1023
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:198
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:64
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.
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:402
Main entry points of multiplayer mode.
Definition: lobby_data.cpp:50
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::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:111
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:139
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:60
#define DBG_CFG
Definition: preferences.cpp:61
#define ERR_ADV
Definition: preferences.cpp:67
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:64
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:622
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:759
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:45
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