OpenTTD Source 20260621-master-g720d10536d
settings.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
23
24#include "stdafx.h"
25#include <charconv>
27#include "settings_table.h"
28#include "debug.h"
29#include "currency_func.h"
30#include "network/network.h"
32#include "network/core/config.h"
33#include "command_func.h"
34#include "console_func.h"
35#include "genworld.h"
36#include "string_func.h"
37#include "strings_func.h"
38#include "window_func.h"
39#include "company_func.h"
40#include "rev.h"
41#include "error.h"
42#include "gamelog.h"
43#include "settings_func.h"
44#include "ini_type.h"
45#include "ai/ai_config.hpp"
46#include "game/game_config.hpp"
47#include "newgrf_config.h"
48#include "picker_func.h"
49#include "newgrf_badge_config.h"
50#include "base_media_base.h"
51#include "base_media_graphics.h"
52#include "fios.h"
53#include "fileio_func.h"
54#include "settings_cmd.h"
55
56#include "table/strings.h"
57
58#include "safeguards.h"
59
64std::string _config_file;
65std::string _private_file;
66std::string _secrets_file;
67std::string _favs_file;
68
70
83{
84 static const SettingTable _generic_setting_tables[] = {
85 _difficulty_settings,
86 _economy_settings,
87 _game_settings,
88 _gui_settings,
89 _linkgraph_settings,
90 _locale_settings,
91 _multimedia_settings,
92 _network_settings,
93 _news_display_settings,
94 _pathfinding_settings,
95 _script_settings,
96 _world_settings,
97 };
98 return _generic_setting_tables;
99}
100
106{
107 static const SettingTable _private_setting_tables[] = {
108 _network_private_settings,
109 };
110 return _private_setting_tables;
111}
112
118{
119 static const SettingTable _secrets_setting_tables[] = {
120 _network_secrets_settings,
121 };
122 return _secrets_setting_tables;
123}
124
125using SettingDescProc = void(IniFile &ini, const SettingTable &desc, std::string_view grpname, void *object, bool only_startup);
126using SettingDescProcList = void(IniFile &ini, std::string_view grpname, StringList &list);
127
128static bool IsSignedVarMemType(VarType vt)
129{
130 switch (GetVarMemType(vt)) {
131 case SLE_VAR_I8:
132 case SLE_VAR_I16:
133 case SLE_VAR_I32:
134 case SLE_VAR_I64:
135 return true;
136 }
137 return false;
138}
139
143class ConfigIniFile : public IniFile {
144private:
145 static inline const IniGroupNameList list_group_names = {
146 "bans",
147 "newgrf",
148 "servers",
149 "server_bind_addresses",
150 "server_authorized_keys",
151 "rcon_authorized_keys",
152 "admin_authorized_keys"
153 };
154
155public:
156 ConfigIniFile(const std::string &filename) : IniFile(list_group_names)
157 {
158 this->LoadFromDisk(filename, Subdirectory::None);
159 }
160};
161
183
185
194std::optional<int32_t> IntSettingDesc::ParseSingleValue(std::string_view str, int32_t min, uint32_t max)
195{
196 StringConsumer consumer{str};
197 /* The actual settings value might be int32 or uint32. Read as int64 and just cast away the high bits. */
198 auto value = consumer.TryReadIntegerBase<int64_t>(10);
199 /* check if it's an integer */
200 if (!value.has_value()) return std::nullopt;
201
202 if (value < min || value > max) {
203 return std::nullopt;
204 }
205 return value;
206}
207
214std::optional<uint32_t> OneOfManySettingDesc::ParseSingleValue(std::string_view str, std::span<const std::string_view> many)
215{
216 StringConsumer consumer{str};
217 auto digit = consumer.TryReadIntegerBase<uint32_t>(10);
218 /* check if it's an integer */
219 if (digit.has_value()) return digit;
220
221 auto it = std::ranges::find(many, str);
222 if (it == many.end()) return std::nullopt;
223 return static_cast<uint32_t>(it - many.begin());
224}
225
232std::optional<bool> BoolSettingDesc::ParseSingleValue(std::string_view str)
233{
234 if (str == "true" || str == "on" || str == "1") return true;
235 if (str == "false" || str == "off" || str == "0") return false;
236
237 return std::nullopt;
238}
239
247static std::optional<uint32_t> LookupManyOfMany(std::span<const std::string_view> many, std::string_view str)
248{
249 static const std::string_view separators{" \t|"};
250
251 uint32_t res = 0;
252 StringConsumer consumer{str};
253 while (consumer.AnyBytesLeft()) {
254 /* skip "whitespace" */
255 consumer.SkipUntilCharNotIn(separators);
256
257 std::string_view value = consumer.ReadUntilCharIn(separators);
258 if (value.empty()) break;
259
260 auto r = OneOfManySettingDesc::ParseSingleValue(value, many);
261 if (!r.has_value()) return r;
262
263 SetBit(res, *r); // value found, set it
264 }
265 return res;
266}
267
273static std::optional<std::vector<uint32_t>> ParseIntList(std::string_view str)
274{
275 bool comma = false; // do we accept comma?
276 std::vector<uint32_t> result;
277
278 StringConsumer consumer{str};
279 for (;;) {
281 if (!consumer.AnyBytesLeft()) break;
282 if (comma && consumer.ReadIf(",")) {
283 /* commas are optional, but we only accept one between values */
284 comma = false;
285 continue;
286 }
287 auto v = consumer.TryReadIntegerBase<uint32_t>(10);
288 if (!v.has_value()) return std::nullopt;
289 result.push_back(*v);
290 comma = true;
291 }
292
293 /* If we have read comma but no number after it, fail.
294 * We have read comma when (n != 0) and comma is not allowed */
295 if (!result.empty() && !comma) return std::nullopt;
296
297 return result;
298}
299
308static bool LoadIntList(std::optional<std::string_view> str, void *array, int nelems, VarType type)
309{
310 size_t elem_size = SlVarSize(type);
311 std::byte *p = static_cast<std::byte *>(array);
312 if (!str.has_value()) {
313 std::fill_n(p, nelems * elem_size, static_cast<std::byte>(0));
314 return true;
315 }
316
317 auto opt_items = ParseIntList(*str);
318 if (!opt_items.has_value() || opt_items->size() != (size_t)nelems) return false;
319
320 for (auto item : *opt_items) {
321 WriteValue(p, type, item);
322 p += elem_size;
323 }
324 return true;
325}
326
333std::string ListSettingDesc::FormatValue(const void *object) const
334{
335 const uint8_t *p = static_cast<const uint8_t *>(GetVariableAddress(object, this->save));
336
337 std::string result;
338 for (size_t i = 0; i != this->save.length; i++) {
339 int64_t v;
340 switch (GetVarMemType(this->save.conv)) {
341 case SLE_VAR_BL:
342 case SLE_VAR_I8: v = *(const int8_t *)p; p += 1; break;
343 case SLE_VAR_U8: v = *(const uint8_t *)p; p += 1; break;
344 case SLE_VAR_I16: v = *(const int16_t *)p; p += 2; break;
345 case SLE_VAR_U16: v = *(const uint16_t *)p; p += 2; break;
346 case SLE_VAR_I32: v = *(const int32_t *)p; p += 4; break;
347 case SLE_VAR_U32: v = *(const uint32_t *)p; p += 4; break;
348 default: NOT_REACHED();
349 }
350 if (i != 0) result += ',';
351 format_append(result, "{}", v);
352 }
353 return result;
354}
355
356std::string OneOfManySettingDesc::FormatSingleValue(uint id) const
357{
358 if (id >= this->many.size()) {
359 return fmt::format("{}", id);
360 }
361 return std::string{this->many[id]};
362}
363
364std::string OneOfManySettingDesc::FormatValue(const void *object) const
365{
366 uint id = (uint)this->Read(object);
367 return this->FormatSingleValue(id);
368}
369
370std::string ManyOfManySettingDesc::FormatValue(const void *object) const
371{
372 uint bitmask = (uint)this->Read(object);
373 if (bitmask == 0) {
374 return {};
375 }
376
377 std::string result;
378 for (uint id : SetBitIterator(bitmask)) {
379 if (!result.empty()) result += '|';
380 result += this->FormatSingleValue(id);
381 }
382 return result;
383}
384
390int32_t IntSettingDesc::ParseValue(std::string_view str) const
391{
392 StringConsumer consumer{str};
393 /* The actual settings value might be int32 or uint32. Read as int64 and just cast away the high bits. */
394 auto value = consumer.TryReadIntegerBase<int64_t>(10);
395 if (!value.has_value()) {
396 _settings_error_list.emplace_back(
397 GetEncodedString(STR_CONFIG_ERROR),
398 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName()));
399 return this->GetDefaultValue();
400 }
401 if (consumer.AnyBytesLeft()) {
402 _settings_error_list.emplace_back(
403 GetEncodedString(STR_CONFIG_ERROR),
404 GetEncodedString(STR_CONFIG_ERROR_TRAILING_CHARACTERS, this->GetName()));
405 }
406 return static_cast<int32_t>(*value);
407}
408
409int32_t OneOfManySettingDesc::ParseValue(std::string_view str) const
410{
412 /* if the first attempt of conversion from string to the appropriate value fails,
413 * look if we have defined a converter from old value to new value. */
414 if (!r.has_value() && this->many_cnvt != nullptr) r = this->many_cnvt(str);
415 if (r.has_value()) return *r; // and here goes converted value
416
417 _settings_error_list.emplace_back(
418 GetEncodedString(STR_CONFIG_ERROR),
419 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName()));
420 return this->GetDefaultValue();
421}
422
423int32_t ManyOfManySettingDesc::ParseValue(std::string_view str) const
424{
425 auto r = LookupManyOfMany(this->many, str);
426 if (r.has_value()) return *r;
427
428 _settings_error_list.emplace_back(
429 GetEncodedString(STR_CONFIG_ERROR),
430 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName()));
431 return this->GetDefaultValue();
432}
433
434int32_t BoolSettingDesc::ParseValue(std::string_view str) const
435{
437 if (r.has_value()) return *r;
438
439 _settings_error_list.emplace_back(
440 GetEncodedString(STR_CONFIG_ERROR),
441 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName()));
442 return this->GetDefaultValue();
443}
444
451{
452 return this->get_title_cb != nullptr ? this->get_title_cb(*this) : this->str;
453}
454
460{
461 return this->get_help_cb != nullptr ? this->get_help_cb(*this) : this->str_help;
462}
463
469std::pair<StringParameter, StringParameter> IntSettingDesc::GetValueParams(int32_t value) const
470{
471 if (this->get_value_params_cb != nullptr) {
472 return this->get_value_params_cb(*this, value);
473 }
474
475 if (this->IsBoolSetting()) {
476 return {value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF, {}};
477 }
478
479 if (this->flags.Test(SettingFlag::GuiDropdown)) {
480 auto [min_val, _] = this->GetRange();
481 return {this->str_val - min_val + value, value};
482 }
483
484 return {this->str_val + ((value == 0 && this->flags.Test(SettingFlag::GuiZeroIsSpecial)) ? 1 : 0), value};
485}
486
492{
493 return this->get_def_cb != nullptr ? this->get_def_cb(*this) : this->def;
494}
495
500std::tuple<int32_t, uint32_t> IntSettingDesc::GetRange() const
501{
502 return this->get_range_cb != nullptr ? this->get_range_cb(*this) : std::tuple(this->min, this->max);
503}
504
511void IntSettingDesc::MakeValueValidAndWrite(const void *object, int32_t val) const
512{
513 this->MakeValueValid(val);
514 this->Write(object, val);
515}
516
526void IntSettingDesc::MakeValueValid(int32_t &val) const
527{
528 auto [min_val, max_val] = this->GetRange();
529 /* We need to take special care of the uint32_t type as we receive from the function
530 * a signed integer. While here also bail out on 64-bit settings as those are not
531 * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed
532 * 32-bit variable
533 * TODO: Support 64-bit settings/variables; requires 64 bit over command protocol! */
534 switch (GetVarMemType(this->save.conv)) {
535 case SLE_VAR_NULL: return;
536 case SLE_VAR_BL:
537 case SLE_VAR_I8:
538 case SLE_VAR_U8:
539 case SLE_VAR_I16:
540 case SLE_VAR_U16:
541 case SLE_VAR_I32: {
542 /* Override the minimum value. No value below this->min, except special value 0 */
543 if (!this->flags.Test(SettingFlag::GuiZeroIsSpecial) || val != 0) {
544 if (!this->flags.Test(SettingFlag::GuiDropdown)) {
545 /* Clamp value-type setting to its valid range */
546 val = Clamp(val, min_val, max_val);
547 } else if (val < min_val || val > static_cast<int32_t>(max_val)) {
548 /* Reset invalid discrete setting (where different values change gameplay) to its default value */
549 val = this->GetDefaultValue();
550 }
551 }
552 break;
553 }
554 case SLE_VAR_U32: {
555 /* Override the minimum value. No value below this->min, except special value 0 */
556 uint32_t uval = static_cast<uint32_t>(val);
557 if (!this->flags.Test(SettingFlag::GuiZeroIsSpecial) || uval != 0) {
558 if (!this->flags.Test(SettingFlag::GuiDropdown)) {
559 /* Clamp value-type setting to its valid range */
560 uval = ClampU(uval, min_val, max_val);
561 } else if (uval < static_cast<uint32_t>(min_val) || uval > max_val) {
562 /* Reset invalid discrete setting to its default value */
563 uval = static_cast<uint32_t>(this->GetDefaultValue());
564 }
565 }
566 val = static_cast<int32_t>(uval);
567 return;
568 }
569 case SLE_VAR_I64:
570 case SLE_VAR_U64:
571 default: NOT_REACHED();
572 }
573}
574
580void IntSettingDesc::Write(const void *object, int32_t val) const
581{
582 void *ptr = GetVariableAddress(object, this->save);
583 WriteValue(ptr, this->save.conv, (int64_t)val);
584}
585
591int32_t IntSettingDesc::Read(const void *object) const
592{
593 void *ptr = GetVariableAddress(object, this->save);
594 return (int32_t)ReadValue(ptr, this->save.conv);
595}
596
604void StringSettingDesc::MakeValueValid(std::string &str) const
605{
606 if (this->max_length == 0 || str.size() < this->max_length) return;
607
608 /* In case a maximum length is imposed by the setting, the length
609 * includes the '\0' termination for network transfer purposes.
610 * Also ensure the string is valid after chopping of some bytes. */
611 str.erase(this->max_length - 1, std::string::npos);
612 StrMakeValidInPlace(str, {});
613}
614
620void StringSettingDesc::Write(const void *object, std::string_view str) const
621{
622 reinterpret_cast<std::string *>(GetVariableAddress(object, this->save))->assign(str);
623}
624
630const std::string &StringSettingDesc::Read(const void *object) const
631{
632 return *reinterpret_cast<std::string *>(GetVariableAddress(object, this->save));
633}
634
644static void IniLoadSettings(IniFile &ini, const SettingTable &settings_table, std::string_view grpname, void *object, bool only_startup)
645{
646 const IniGroup *group;
647 const IniGroup *group_def = ini.GetGroup(grpname);
648
649 for (auto &desc : settings_table) {
650 const SettingDesc *sd = GetSettingDesc(desc);
652 if (sd->startup != only_startup) continue;
653
654 /* For settings.xx.yy load the settings from [xx] yy = ? */
655 std::string s{ sd->GetName() };
656 auto sc = s.find('.');
657 if (sc != std::string::npos) {
658 group = ini.GetGroup(s.substr(0, sc));
659 if (group == nullptr) group = group_def;
660 s = s.substr(sc + 1);
661 } else {
662 group = group_def;
663 }
664
665 const IniItem *item = nullptr;
666 if (group != nullptr) item = group->GetItem(s);
667 if (item == nullptr && group != group_def && group_def != nullptr) {
668 /* For settings.xx.yy load the settings from [settings] yy = ? in case the previous
669 * did not exist (e.g. loading old config files with a [settings] section */
670 item = group_def->GetItem(s);
671 }
672 if (item == nullptr) {
673 /* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous
674 * did not exist (e.g. loading old config files with a [yapf] section */
675 sc = s.find('.');
676 if (sc != std::string::npos) {
677 if (group = ini.GetGroup(s.substr(0, sc)); group != nullptr) item = group->GetItem(s.substr(sc + 1));
678 }
679 }
680
681 sd->ParseValue(item, object);
682 }
683}
684
685void IntSettingDesc::ParseValue(const IniItem *item, void *object) const
686{
687 int32_t val = (item != nullptr && item->value.has_value()) ? this->ParseValue(*item->value) : this->GetDefaultValue();
688 this->MakeValueValidAndWrite(object, val);
689}
690
691void StringSettingDesc::ParseValue(const IniItem *item, void *object) const
692{
693 std::string str{(item == nullptr) ? this->def : item->value.value_or("")};
694 this->MakeValueValid(str);
695 this->Write(object, str);
696}
697
698void ListSettingDesc::ParseValue(const IniItem *item, void *object) const
699{
700 std::optional<std::string_view> str;
701 if (item != nullptr) {
702 str = item->value;
703 } else if (!this->def.empty()) {
704 str = this->def;
705 }
706 void *ptr = GetVariableAddress(object, this->save);
707 if (!LoadIntList(str, ptr, this->save.length, GetVarMemType(this->save.conv))) {
708 _settings_error_list.emplace_back(
709 GetEncodedString(STR_CONFIG_ERROR),
710 GetEncodedString(STR_CONFIG_ERROR_ARRAY, this->GetName()));
711
712 /* Use default */
713 LoadIntList(this->def, ptr, this->save.length, GetVarMemType(this->save.conv));
714 }
715}
716
729static void IniSaveSettings(IniFile &ini, const SettingTable &settings_table, std::string_view grpname, void *object, bool)
730{
731 IniGroup *group_def = nullptr, *group;
732
733 for (auto &desc : settings_table) {
734 const SettingDesc *sd = GetSettingDesc(desc);
735 /* If the setting is not saved to the configuration
736 * file, just continue with the next setting */
738 if (sd->flags.Test(SettingFlag::NotInConfig)) continue;
739
740 /* XXX - wtf is this?? (group override?) */
741 std::string s{ sd->GetName() };
742 auto sc = s.find('.');
743 if (sc != std::string::npos) {
744 group = &ini.GetOrCreateGroup(s.substr(0, sc));
745 s = s.substr(sc + 1);
746 } else {
747 if (group_def == nullptr) group_def = &ini.GetOrCreateGroup(grpname);
748 group = group_def;
749 }
750
751 IniItem &item = group->GetOrCreateItem(s);
752
753 if (!item.value.has_value() || !sd->IsSameValue(&item, object)) {
754 /* The value is different, that means we have to write it to the ini */
755 item.value.emplace(sd->FormatValue(object));
756 }
757 }
758}
759
760std::string IntSettingDesc::FormatValue(const void *object) const
761{
762 int64_t i;
763 if (IsSignedVarMemType(this->save.conv)) {
764 i = this->Read(object);
765 } else {
766 i = (uint32_t)this->Read(object);
767 }
768 return fmt::format("{}", i);
769}
770
771std::string BoolSettingDesc::FormatValue(const void *object) const
772{
773 bool val = this->Read(object) != 0;
774 return val ? "true" : "false";
775}
776
777bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const
778{
779 int32_t item_value = static_cast<int32_t>(this->ParseValue(*item->value));
780 int32_t object_value = this->Read(object);
781 return item_value == object_value;
782}
783
784bool IntSettingDesc::IsDefaultValue(void *object) const
785{
786 int32_t object_value = this->Read(object);
787 return this->GetDefaultValue() == object_value;
788}
789
790void IntSettingDesc::ResetToDefault(void *object) const
791{
792 this->Write(object, this->GetDefaultValue());
793}
794
795std::string StringSettingDesc::FormatValue(const void *object) const
796{
797 const std::string &str = this->Read(object);
798 switch (GetVarMemType(this->save.conv)) {
799 case SLE_VAR_STR: return str;
800
801 case SLE_VAR_STRQ:
802 if (str.empty()) {
803 return str;
804 }
805 return fmt::format("\"{}\"", str);
806
807 default: NOT_REACHED();
808 }
809}
810
811bool StringSettingDesc::IsSameValue(const IniItem *item, void *object) const
812{
813 /* The ini parsing removes the quotes, which are needed to retain the spaces in STRQs,
814 * so those values are always different in the parsed ini item than they should be. */
815 if (GetVarMemType(this->save.conv) == SLE_VAR_STRQ) return false;
816
817 const std::string &str = this->Read(object);
818 return item->value->compare(str) == 0;
819}
820
821bool StringSettingDesc::IsDefaultValue(void *object) const
822{
823 const std::string &str = this->Read(object);
824 return this->def == str;
825}
826
827void StringSettingDesc::ResetToDefault(void *object) const
828{
829 this->Write(object, this->def);
830}
831
832bool ListSettingDesc::IsSameValue(const IniItem *, void *) const
833{
834 /* Checking for equality is way more expensive than just writing the value. */
835 return false;
836}
837
839{
840 /* Defaults of lists are often complicated, and hard to compare. */
841 return false;
842}
843
845{
846 /* Resetting a list to default is not supported. */
847 NOT_REACHED();
848}
849
859static void IniLoadSettingList(IniFile &ini, std::string_view grpname, StringList &list)
860{
861 const IniGroup *group = ini.GetGroup(grpname);
862
863 if (group == nullptr) return;
864
865 list.clear();
866
867 for (const IniItem &item : group->items) {
868 if (!item.name.empty()) list.push_back(item.name);
869 }
870}
871
881static void IniSaveSettingList(IniFile &ini, std::string_view grpname, StringList &list)
882{
883 IniGroup &group = ini.GetOrCreateGroup(grpname);
884 group.Clear();
885
886 for (const auto &iter : list) {
887 group.GetOrCreateItem(iter).SetValue("");
888 }
889}
890
897void IniLoadWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
898{
899 IniLoadSettings(ini, _window_settings, grpname, desc, false);
900}
901
908void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
909{
910 IniSaveSettings(ini, _window_settings, grpname, desc, false);
911}
912
918bool SettingDesc::IsEditable(bool do_command) const
919{
920 if (!do_command && !this->flags.Test(SettingFlag::NoNetworkSync) && _networking && !_network_server && !this->flags.Test(SettingFlag::PerCompany)) return false;
921 if (do_command && this->flags.Test(SettingFlag::NoNetworkSync)) return false;
922 if (this->flags.Test(SettingFlag::NetworkOnly) && !_networking && _game_mode != GameMode::Menu) return false;
923 if (this->flags.Test(SettingFlag::NoNetwork) && _networking) return false;
924 if (this->flags.Test(SettingFlag::NewgameOnly) &&
925 (_game_mode == GameMode::Normal ||
926 (_game_mode == GameMode::Editor && !this->flags.Test(SettingFlag::SceneditToo)))) return false;
927 if (this->flags.Test(SettingFlag::SceneditOnly) && _game_mode != GameMode::Editor) return false;
928 return true;
929}
930
936{
937 if (this->flags.Test(SettingFlag::PerCompany)) return ST_COMPANY;
938 return this->flags.Test(SettingFlag::NotInSave) ? ST_CLIENT : ST_GAME;
939}
940
946{
947 assert(this->IsIntSetting());
948 return static_cast<const IntSettingDesc *>(this);
949}
950
956{
957 assert(this->IsStringSetting());
958 return static_cast<const StringSettingDesc *>(this);
959}
960
962void HandleOldDiffCustom(bool savegame);
963
964
966static void ValidateSettings()
967{
968 /* Do not allow a custom sea level with the original land generator. */
969 if (_settings_newgame.game_creation.land_generator == LG_ORIGINAL &&
970 _settings_newgame.difficulty.quantity_sea_lakes == CUSTOM_SEA_LEVEL_NUMBER_DIFFICULTY) {
971 _settings_newgame.difficulty.quantity_sea_lakes = CUSTOM_SEA_LEVEL_MIN_PERCENTAGE;
972 }
973}
974
975static void AILoadConfig(const IniFile &ini, std::string_view grpname)
976{
977 const IniGroup *group = ini.GetGroup(grpname);
978
979 /* Clean any configured AI */
980 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
982 }
983
984 /* If no group exists, return */
985 if (group == nullptr) return;
986
987 CompanyID c = CompanyID::Begin();
988 for (const IniItem &item : group->items) {
990
991 config->Change(item.name);
992 if (!config->HasScript()) {
993 if (item.name != "none") {
994 Debug(script, 0, "The AI by the name '{}' was no longer found, and removed from the list.", item.name);
995 continue;
996 }
997 }
998 if (item.value.has_value()) config->StringToSettings(*item.value);
999 ++c;
1000 if (c >= MAX_COMPANIES) break;
1001 }
1002}
1003
1004static void GameLoadConfig(const IniFile &ini, std::string_view grpname)
1005{
1006 const IniGroup *group = ini.GetGroup(grpname);
1007
1008 /* Clean any configured GameScript */
1010
1011 /* If no group exists, return */
1012 if (group == nullptr || group->items.empty()) return;
1013
1014 const IniItem &item = group->items.front();
1015
1017
1018 config->Change(item.name);
1019 if (!config->HasScript()) {
1020 if (item.name != "none") {
1021 Debug(script, 0, "The GameScript by the name '{}' was no longer found, and removed from the list.", item.name);
1022 return;
1023 }
1024 }
1025 if (item.value.has_value()) config->StringToSettings(*item.value);
1026}
1027
1033{
1034 if (const IniGroup *group = ini.GetGroup("misc"); group != nullptr) {
1035 /* Load old setting first. */
1036 if (const IniItem *item = group->GetItem("graphicsset"); item != nullptr && item->value) BaseGraphics::ini_data.name = *item->value;
1037 }
1038
1039 if (const IniGroup *group = ini.GetGroup("graphicsset"); group != nullptr) {
1040 /* Load new settings. */
1041 if (const IniItem *item = group->GetItem("name"); item != nullptr && item->value) BaseGraphics::ini_data.name = *item->value;
1042
1043 if (const IniItem *item = group->GetItem("shortname"); item != nullptr && item->value && item->value->size() == 8) {
1044 auto val = ParseInteger<uint32_t>(*item->value, 16);
1045 if (val.has_value()) {
1046 BaseGraphics::ini_data.shortname = std::byteswap<uint32_t>(*val);
1047 } else {
1048 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1049 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, *item->value, BaseGraphics::ini_data.name),
1051 }
1052 }
1053
1054 if (const IniItem *item = group->GetItem("extra_version"); item != nullptr && item->value) {
1055 auto val = ParseInteger<uint32_t>(*item->value);
1056 if (val.has_value()) {
1057 BaseGraphics::ini_data.extra_version = *val;
1058 } else {
1059 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1060 GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, *item->value, BaseGraphics::ini_data.name),
1062 }
1063 }
1064
1065 if (const IniItem *item = group->GetItem("extra_params"); item != nullptr && item->value) {
1066 auto params = ParseIntList(*item->value);
1067 if (params.has_value()) {
1068 BaseGraphics::ini_data.extra_params = params.value();
1069 } else {
1070 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1071 GetEncodedString(STR_CONFIG_ERROR_ARRAY, BaseGraphics::ini_data.name),
1073 }
1074 }
1075 }
1076}
1077
1085static GRFConfigList GRFLoadConfig(const IniFile &ini, std::string_view grpname, bool is_static)
1086{
1087 const IniGroup *group = ini.GetGroup(grpname);
1088 GRFConfigList list;
1089
1090 if (group == nullptr) return list;
1091
1092 uint num_grfs = 0;
1093 for (const IniItem &item : group->items) {
1094 std::unique_ptr<GRFConfig> c{};
1095
1096 std::array<uint8_t, 4> grfid_buf;
1097 MD5Hash md5sum;
1098 std::string_view item_name = item.name;
1099 bool has_md5sum = false;
1100
1101 /* Try reading "<grfid>|" and on success, "<md5sum>|". */
1102 auto grfid_pos = item_name.find("|");
1103 if (grfid_pos != std::string_view::npos) {
1104 std::string_view grfid_str = item_name.substr(0, grfid_pos);
1105
1106 if (ConvertHexToBytes(grfid_str, grfid_buf)) {
1107 item_name = item_name.substr(grfid_pos + 1);
1108
1109 auto md5sum_pos = item_name.find("|");
1110 if (md5sum_pos != std::string_view::npos) {
1111 std::string_view md5sum_str = item_name.substr(0, md5sum_pos);
1112
1113 has_md5sum = ConvertHexToBytes(md5sum_str, md5sum);
1114 if (has_md5sum) item_name = item_name.substr(md5sum_pos + 1);
1115 }
1116
1117 uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
1118 if (has_md5sum) {
1119 const GRFConfig *s = FindGRFConfig(grfid, FindGRFConfigMode::Exact, &md5sum);
1120 if (s != nullptr) c = std::make_unique<GRFConfig>(*s);
1121 }
1122 if (c == nullptr && !FioCheckFileExists(std::string(item_name), Subdirectory::NewGrf)) {
1124 if (s != nullptr) c = std::make_unique<GRFConfig>(*s);
1125 }
1126 }
1127 }
1128 std::string filename = std::string(item_name);
1129
1130 if (c == nullptr) c = std::make_unique<GRFConfig>(filename);
1131
1132 /* Parse parameters */
1133 if (item.value.has_value() && !item.value->empty()) {
1134 auto params = ParseIntList(*item.value);
1135 if (params.has_value()) {
1136 c->SetParams(params.value());
1137 } else {
1138 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1139 GetEncodedString(STR_CONFIG_ERROR_ARRAY, filename),
1141 }
1142 }
1143
1144 /* Check if item is valid */
1145 if (!FillGRFDetails(*c, is_static) || c->flags.Test(GRFConfigFlag::Invalid)) {
1146 StringID reason;
1147 if (c->status == GRFStatus::NotFound) {
1148 reason = STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND;
1149 } else if (c->flags.Test(GRFConfigFlag::Unsafe)) {
1150 reason = STR_CONFIG_ERROR_INVALID_GRF_UNSAFE;
1151 } else if (c->flags.Test(GRFConfigFlag::System)) {
1152 reason = STR_CONFIG_ERROR_INVALID_GRF_SYSTEM;
1153 } else if (c->flags.Test(GRFConfigFlag::Invalid)) {
1154 reason = STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE;
1155 } else {
1156 reason = STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN;
1157 }
1158
1159 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1160 GetEncodedString(STR_CONFIG_ERROR_INVALID_GRF, filename.empty() ? item.name : filename, reason),
1162 continue;
1163 }
1164
1165 /* Check for duplicate GRFID (will also check for duplicate filenames) */
1166 auto found = std::ranges::find_if(list, [&c](const auto &gc) { return gc->ident.grfid == c->ident.grfid; });
1167 if (found != std::end(list)) {
1168 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1169 GetEncodedString(STR_CONFIG_ERROR_DUPLICATE_GRFID, c->filename, (*found)->filename),
1171 continue;
1172 }
1173
1174 if (is_static) {
1175 /* Mark file as static to avoid saving in savegame. */
1176 c->flags.Set(GRFConfigFlag::Static);
1177 } else if (++num_grfs > NETWORK_MAX_GRF_COUNT) {
1178 /* Check we will not load more non-static NewGRFs than allowed. This could trigger issues for game servers. */
1179 ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR),
1180 GetEncodedString(STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED), WarningLevel::Critical);
1181 break;
1182 }
1183
1184 /* Add item to list */
1185 list.push_back(std::move(c));
1186 }
1187
1188 return list;
1189}
1190
1191static IniFileVersion LoadVersionFromConfig(const IniFile &ini)
1192{
1193 const IniGroup *group = ini.GetGroup("version");
1194 if (group == nullptr) return IFV_0;
1195
1196 auto version_number = group->GetItem("ini_version");
1197 /* Older ini-file versions don't have this key yet. */
1198 if (version_number == nullptr || !version_number->value.has_value()) return IFV_0;
1199
1200 uint32_t version = 0;
1201 std::from_chars(version_number->value->data(), version_number->value->data() + version_number->value->size(), version);
1202
1203 return static_cast<IniFileVersion>(version);
1204}
1205
1206static void AISaveConfig(IniFile &ini, std::string_view grpname)
1207{
1208 IniGroup &group = ini.GetOrCreateGroup(grpname);
1209 group.Clear();
1210
1211 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
1213 std::string name;
1214 std::string value = config->SettingsToString();
1215
1216 if (config->HasScript()) {
1217 name = config->GetName();
1218 } else {
1219 name = "none";
1220 }
1221
1222 group.CreateItem(name).SetValue(value);
1223 }
1224}
1225
1226static void GameSaveConfig(IniFile &ini, std::string_view grpname)
1227{
1228 IniGroup &group = ini.GetOrCreateGroup(grpname);
1229 group.Clear();
1230
1232 std::string name;
1233 std::string value = config->SettingsToString();
1234
1235 if (config->HasScript()) {
1236 name = config->GetName();
1237 } else {
1238 name = "none";
1239 }
1240
1241 group.CreateItem(name).SetValue(value);
1242}
1243
1249{
1250 IniGroup &group = ini.GetOrCreateGroup("version");
1251 group.GetOrCreateItem("version_string").SetValue(_openttd_revision);
1252 group.GetOrCreateItem("version_number").SetValue(fmt::format("{:08X}", _openttd_newgrf_version));
1253 group.GetOrCreateItem("ini_version").SetValue(fmt::format("{}", INIFILE_VERSION));
1254}
1255
1261{
1262 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
1263 if (used_set == nullptr) return;
1264
1265 IniGroup &group = ini.GetOrCreateGroup("graphicsset");
1266 group.Clear();
1267
1268 group.GetOrCreateItem("name").SetValue(used_set->name);
1269 group.GetOrCreateItem("shortname").SetValue(fmt::format("{:08X}", std::byteswap(used_set->shortname)));
1270
1271 const GRFConfig *extra_cfg = used_set->GetExtraConfig();
1272 if (extra_cfg != nullptr && !extra_cfg->param.empty()) {
1273 group.GetOrCreateItem("extra_version").SetValue(fmt::format("{}", extra_cfg->version));
1274 group.GetOrCreateItem("extra_params").SetValue(GRFBuildParamList(*extra_cfg));
1275 }
1276}
1277
1278/* Save a GRF configuration to the given group name */
1279static void GRFSaveConfig(IniFile &ini, std::string_view grpname, const GRFConfigList &list)
1280{
1281 IniGroup &group = ini.GetOrCreateGroup(grpname);
1282 group.Clear();
1283
1284 for (const auto &c : list) {
1285 std::string key = fmt::format("{:08X}|{}|{}", std::byteswap(c->ident.grfid),
1286 FormatArrayAsHex(c->ident.md5sum), c->filename);
1288 }
1289}
1290
1291/* Common handler for saving/loading variables to the configuration file */
1292static void HandleSettingDescs(IniFile &generic_ini, IniFile &private_ini, IniFile &secrets_ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool only_startup = false)
1293{
1294 proc(generic_ini, _misc_settings, "misc", nullptr, only_startup);
1295#if defined(_WIN32) && !defined(DEDICATED)
1296 proc(generic_ini, _win32_settings, "win32", nullptr, only_startup);
1297#endif /* _WIN32 */
1298
1299 /* The name "patches" is a fallback, as every setting should sets its own group. */
1300
1301 for (auto &table : GenericSettingTables()) {
1302 proc(generic_ini, table, "patches", &_settings_newgame, only_startup);
1303 }
1304 for (auto &table : PrivateSettingTables()) {
1305 proc(private_ini, table, "patches", &_settings_newgame, only_startup);
1306 }
1307 for (auto &table : SecretSettingTables()) {
1308 proc(secrets_ini, table, "patches", &_settings_newgame, only_startup);
1309 }
1310
1311 proc(generic_ini, _currency_settings, "currency", &GetCustomCurrency(), only_startup);
1312 proc(generic_ini, _company_settings, "company", &_settings_client.company, only_startup);
1313
1314 if (!only_startup) {
1315 proc_list(private_ini, "server_bind_addresses", _network_bind_list);
1316 proc_list(private_ini, "servers", _network_host_list);
1317 proc_list(private_ini, "bans", _network_ban_list);
1318 proc_list(private_ini, "server_authorized_keys", _settings_client.network.server_authorized_keys);
1319 proc_list(private_ini, "rcon_authorized_keys", _settings_client.network.rcon_authorized_keys);
1320 proc_list(private_ini, "admin_authorized_keys", _settings_client.network.admin_authorized_keys);
1321 }
1322}
1323
1333static void RemoveEntriesFromIni(IniFile &ini, const SettingTable &table)
1334{
1335 for (auto &desc : table) {
1336 const SettingDesc *sd = GetSettingDesc(desc);
1337
1338 /* For settings.xx.yy load the settings from [xx] yy = ? */
1339 std::string s{ sd->GetName() };
1340 auto sc = s.find('.');
1341 if (sc == std::string::npos) continue;
1342
1343 IniGroup *group = ini.GetGroup(s.substr(0, sc));
1344 if (group == nullptr) continue;
1345 s = s.substr(sc + 1);
1346
1347 group->RemoveItem(s);
1348 }
1349}
1350
1374bool IsConversionNeeded(const ConfigIniFile &ini, const std::string &group, const std::string &old_var, const std::string &new_var, const IniItem **old_item)
1375{
1376 *old_item = nullptr;
1377
1378 const IniGroup *igroup = ini.GetGroup(group);
1379 /* If the group doesn't exist, there is nothing to convert. */
1380 if (igroup == nullptr) return false;
1381
1382 const IniItem *tmp_old_item = igroup->GetItem(old_var);
1383 const IniItem *new_item = igroup->GetItem(new_var);
1384
1385 /* If the old item doesn't exist, there is nothing to convert. */
1386 if (tmp_old_item == nullptr) return false;
1387
1388 /* If the new item exists, it means conversion was already done. We only
1389 * do the conversion the first time, and after that these settings are
1390 * independent. This allows users to freely change between older and
1391 * newer clients without breaking anything. */
1392 if (new_item != nullptr) return false;
1393
1394 *old_item = tmp_old_item;
1395 return true;
1396}
1397
1402void LoadFromConfig(bool startup)
1403{
1404 ConfigIniFile generic_ini(_config_file);
1405 ConfigIniFile private_ini(_private_file);
1406 ConfigIniFile secrets_ini(_secrets_file);
1407 ConfigIniFile favs_ini(_favs_file);
1408
1409 if (!startup) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one
1410
1411 IniFileVersion generic_version = LoadVersionFromConfig(generic_ini);
1412
1413 if (startup) {
1414 GraphicsSetLoadConfig(generic_ini);
1415 }
1416
1417 /* Before the split of private/secrets, we have to look in the generic for these settings. */
1418 if (generic_version < IFV_PRIVATE_SECRETS) {
1419 HandleSettingDescs(generic_ini, generic_ini, generic_ini, IniLoadSettings, IniLoadSettingList, startup);
1420 } else {
1421 HandleSettingDescs(generic_ini, private_ini, secrets_ini, IniLoadSettings, IniLoadSettingList, startup);
1422 }
1423
1424 /* Load basic settings only during bootstrap, load other settings not during bootstrap */
1425 if (!startup) {
1426 if (generic_version < IFV_LINKGRAPH_SECONDS) {
1427 _settings_newgame.linkgraph.recalc_interval *= CalendarTime::SECONDS_PER_DAY;
1428 _settings_newgame.linkgraph.recalc_time *= CalendarTime::SECONDS_PER_DAY;
1429 }
1430
1431 /* Move use_relay_service from generic_ini to private_ini. */
1432 if (generic_version < IFV_NETWORK_PRIVATE_SETTINGS) {
1433 const IniGroup *network = generic_ini.GetGroup("network");
1434 if (network != nullptr) {
1435 const IniItem *use_relay_service = network->GetItem("use_relay_service");
1436 if (use_relay_service != nullptr) {
1437 if (use_relay_service->value == "never") {
1438 _settings_client.network.use_relay_service = UseRelayService::Never;
1439 } else if (use_relay_service->value == "ask") {
1440 _settings_client.network.use_relay_service = UseRelayService::Ask;
1441 } else if (use_relay_service->value == "allow") {
1442 _settings_client.network.use_relay_service = UseRelayService::Allow;
1443 }
1444 }
1445 }
1446 }
1447
1448 const IniItem *old_item;
1449
1450 if (generic_version < IFV_GAME_TYPE && IsConversionNeeded(generic_ini, "network", "server_advertise", "server_game_type", &old_item)) {
1451 auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value);
1452 _settings_client.network.server_game_type = old_value.value_or(false) ? ServerGameType::Public : ServerGameType::Local;
1453 }
1454
1455 if (generic_version < IFV_AUTOSAVE_RENAME && IsConversionNeeded(generic_ini, "gui", "autosave", "autosave_interval", &old_item)) {
1456 static constexpr std::initializer_list<std::string_view> _old_autosave_interval{"off"sv, "monthly"sv, "quarterly"sv, "half year"sv, "yearly"sv};
1457 auto old_value = OneOfManySettingDesc::ParseSingleValue(*old_item->value, _old_autosave_interval).value_or(-1);
1458
1459 switch (old_value) {
1460 case 0: _settings_client.gui.autosave_interval = 0; break;
1461 case 1: _settings_client.gui.autosave_interval = 10; break;
1462 case 2: _settings_client.gui.autosave_interval = 30; break;
1463 case 3: _settings_client.gui.autosave_interval = 60; break;
1464 case 4: _settings_client.gui.autosave_interval = 120; break;
1465 default: break;
1466 }
1467 }
1468
1469 /* Persist the right click close option from older versions. */
1470 if (generic_version < IFV_RIGHT_CLICK_CLOSE && IsConversionNeeded(generic_ini, "gui", "right_mouse_wnd_close", "right_click_wnd_close", &old_item)) {
1471 auto old_value = BoolSettingDesc::ParseSingleValue(*old_item->value);
1472 _settings_client.gui.right_click_wnd_close = old_value.value_or(false) ? RightClickClose::Yes : RightClickClose::No;
1473 }
1474
1475 if (generic_version < IFV_DEFAULT_RAIL_ROAD && IsConversionNeeded(generic_ini, "gui", "default_rail_type", "default_rail_road_type", &old_item)) {
1476 auto old_value = IntSettingDesc::ParseSingleValue(*old_item->value, 0, 2);
1477 _settings_client.gui.default_rail_road_type = static_cast<DefaultRailRoadType>(old_value.value_or(0));
1478 }
1479
1480 _grfconfig_newgame = GRFLoadConfig(generic_ini, "newgrf", false);
1481 _grfconfig_static = GRFLoadConfig(generic_ini, "newgrf-static", true);
1482 AILoadConfig(generic_ini, "ai_players");
1483 GameLoadConfig(generic_ini, "game_scripts");
1484 PickerLoadConfig(favs_ini);
1485 BadgeClassLoadConfig(favs_ini);
1486
1488 IniLoadSettings(generic_ini, _old_gameopt_settings, "gameopt", &_settings_newgame, false);
1489 HandleOldDiffCustom(false);
1490
1493
1494 /* Display scheduled errors */
1496 if (FindWindowById(WindowClass::ErrorMessage, 0) == nullptr) ShowFirstError();
1497 }
1498}
1499
1502{
1503 ConfigIniFile generic_ini(_config_file);
1504 ConfigIniFile private_ini(_private_file);
1505 ConfigIniFile secrets_ini(_secrets_file);
1506 ConfigIniFile favs_ini(_favs_file);
1507
1508 IniFileVersion generic_version = LoadVersionFromConfig(generic_ini);
1509
1510 /* If we newly create the private/secrets file, add a dummy group on top
1511 * just so we can add a comment before it (that is how IniFile works).
1512 * This to explain what the file is about. After doing it once, never touch
1513 * it again, as otherwise we might be reverting user changes. */
1514 if (IniGroup *group = private_ini.GetGroup("private"); group != nullptr) group->comment = "; This file possibly contains private information which can identify you as person.\n";
1515 if (IniGroup *group = secrets_ini.GetGroup("secrets"); group != nullptr) group->comment = "; Do not share this file with others, not even if they claim to be technical support.\n; This file contains saved passwords and other secrets that should remain private to you!\n";
1516
1517 if (generic_version == IFV_0) {
1518 /* Remove some obsolete groups. These have all been loaded into other groups. */
1519 generic_ini.RemoveGroup("patches");
1520 generic_ini.RemoveGroup("yapf");
1521 generic_ini.RemoveGroup("gameopt");
1522
1523 /* Remove all settings from the generic ini that are now in the private ini. */
1524 generic_ini.RemoveGroup("server_bind_addresses");
1525 generic_ini.RemoveGroup("servers");
1526 generic_ini.RemoveGroup("bans");
1527 for (auto &table : PrivateSettingTables()) {
1528 RemoveEntriesFromIni(generic_ini, table);
1529 }
1530
1531 /* Remove all settings from the generic ini that are now in the secrets ini. */
1532 for (auto &table : SecretSettingTables()) {
1533 RemoveEntriesFromIni(generic_ini, table);
1534 }
1535 }
1536
1537 if (generic_version < IFV_REMOVE_GENERATION_SEED) {
1538 IniGroup *game_creation = generic_ini.GetGroup("game_creation");
1539 if (game_creation != nullptr) {
1540 game_creation->RemoveItem("generation_seed");
1541 }
1542 }
1543
1544 /* These variables are migrated from generic ini to private ini now. */
1545 if (generic_version < IFV_NETWORK_PRIVATE_SETTINGS) {
1546 IniGroup *network = generic_ini.GetGroup("network");
1547 if (network != nullptr) {
1548 network->RemoveItem("use_relay_service");
1549 }
1550 }
1551
1552 HandleSettingDescs(generic_ini, private_ini, secrets_ini, IniSaveSettings, IniSaveSettingList);
1553 GraphicsSetSaveConfig(generic_ini);
1554 GRFSaveConfig(generic_ini, "newgrf", _grfconfig_newgame);
1555 GRFSaveConfig(generic_ini, "newgrf-static", _grfconfig_static);
1556 AISaveConfig(generic_ini, "ai_players");
1557 GameSaveConfig(generic_ini, "game_scripts");
1558 PickerSaveConfig(favs_ini);
1559 BadgeClassSaveConfig(favs_ini);
1560
1561 SaveVersionInConfig(generic_ini);
1562 SaveVersionInConfig(private_ini);
1563 SaveVersionInConfig(secrets_ini);
1564 SaveVersionInConfig(favs_ini);
1565
1566 generic_ini.SaveToDisk(_config_file);
1567 private_ini.SaveToDisk(_private_file);
1568 secrets_ini.SaveToDisk(_secrets_file);
1569 favs_ini.SaveToDisk(_favs_file);
1570}
1571
1577{
1578 StringList list;
1579
1581 for (const IniGroup &group : ini.groups) {
1582 if (group.name.starts_with("preset-")) {
1583 list.push_back(group.name.substr(7));
1584 }
1585 }
1586
1587 return list;
1588}
1589
1596GRFConfigList LoadGRFPresetFromConfig(std::string_view config_name)
1597{
1598 std::string section("preset-");
1599 section += config_name;
1600
1602 GRFConfigList config = GRFLoadConfig(ini, section, false);
1603
1604 return config;
1605}
1606
1613void SaveGRFPresetToConfig(std::string_view config_name, GRFConfigList &config)
1614{
1615 std::string section("preset-");
1616 section += config_name;
1617
1619 GRFSaveConfig(ini, section, config);
1621}
1622
1627void DeleteGRFPresetFromConfig(std::string_view config_name)
1628{
1629 std::string section("preset-");
1630 section += config_name;
1631
1633 ini.RemoveGroup(section);
1635}
1636
1643void IntSettingDesc::ChangeValue(const void *object, int32_t newval) const
1644{
1645 int32_t oldval = this->Read(object);
1646 this->MakeValueValid(newval);
1647 if (this->pre_check != nullptr && !this->pre_check(newval)) return;
1648 if (oldval == newval) return;
1649
1650 this->Write(object, newval);
1651 if (this->post_callback != nullptr) this->post_callback(newval);
1652
1653 if (this->flags.Test(SettingFlag::NoNetwork) || this->flags.Test(SettingFlag::Sandbox)) {
1654 _gamelog.StartAction(GLAT_SETTING);
1655 _gamelog.Setting(this->GetName(), oldval, newval);
1656 _gamelog.StopAction();
1657 }
1658
1659 SetWindowClassesDirty(WindowClass::GameOptions);
1660 if (this->flags.Test(SettingFlag::Sandbox)) SetWindowClassesDirty(WindowClass::Cheat);
1661
1662 if (_save_config) SaveToConfig();
1663}
1664
1672static const SettingDesc *GetSettingFromName(std::string_view name, const SettingTable &settings)
1673{
1674 /* First check all full names */
1675 for (auto &desc : settings) {
1676 const SettingDesc *sd = GetSettingDesc(desc);
1677 if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
1678 if (sd->GetName() == name) return sd;
1679 }
1680
1681 /* Then check the shortcut variant of the name. */
1682 std::string short_name_suffix = std::string{ "." }.append(name);
1683 for (auto &desc : settings) {
1684 const SettingDesc *sd = GetSettingDesc(desc);
1685 if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
1686 if (sd->GetName().ends_with(short_name_suffix)) return sd;
1687 }
1688
1689 return nullptr;
1690}
1691
1697void GetSaveLoadFromSettingTable(SettingTable settings, std::vector<SaveLoad> &saveloads)
1698{
1699 for (auto &desc : settings) {
1700 const SettingDesc *sd = GetSettingDesc(desc);
1701 if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
1702 saveloads.push_back(sd->save);
1703 }
1704}
1705
1712{
1713 static const SettingTable saveload_settings_tables[] = {
1714 _difficulty_settings,
1715 _economy_settings,
1716 _game_settings,
1717 _linkgraph_settings,
1718 _locale_settings,
1719 _pathfinding_settings,
1720 _script_settings,
1721 _world_settings,
1722 };
1723 static std::vector<SettingVariant> settings_table;
1724
1725 if (settings_table.empty()) {
1726 for (auto &saveload_settings_table : saveload_settings_tables) {
1727 for (auto &saveload_setting : saveload_settings_table) {
1728 settings_table.push_back(saveload_setting);
1729 }
1730 }
1731 }
1732
1733 return settings_table;
1734}
1735
1742static const SettingDesc *GetCompanySettingFromName(std::string_view name)
1743{
1744 static const std::string_view company_prefix = "company.";
1745 if (name.starts_with(company_prefix)) name.remove_prefix(company_prefix.size());
1746 return GetSettingFromName(name, _company_settings);
1747}
1748
1755const SettingDesc *GetSettingFromName(std::string_view name)
1756{
1757 for (auto &table : GenericSettingTables()) {
1758 auto sd = GetSettingFromName(name, table);
1759 if (sd != nullptr) return sd;
1760 }
1761 for (auto &table : PrivateSettingTables()) {
1762 auto sd = GetSettingFromName(name, table);
1763 if (sd != nullptr) return sd;
1764 }
1765 for (auto &table : SecretSettingTables()) {
1766 auto sd = GetSettingFromName(name, table);
1767 if (sd != nullptr) return sd;
1768 }
1769
1770 return GetCompanySettingFromName(name);
1771}
1772
1778std::vector<const SettingDesc *> GetFilteredSettingCollection(std::function<bool(const SettingDesc &desc)> func)
1779{
1780 std::vector<const SettingDesc *> collection;
1781
1782 for (const auto &table : GenericSettingTables()) {
1783 for (const auto &desc : table) {
1784 const auto sd = GetSettingDesc(desc);
1785 if (!func(*sd)) continue;
1786
1787 collection.push_back(sd);
1788 }
1789 }
1790
1791 return collection;
1792}
1793
1803CommandCost CmdChangeSetting(DoCommandFlags flags, const std::string &name, int32_t value)
1804{
1805 if (name.empty()) return CMD_ERROR;
1806 const SettingDesc *sd = GetSettingFromName(name);
1807
1808 if (sd == nullptr) return CMD_ERROR;
1810 if (!sd->IsIntSetting()) return CMD_ERROR;
1811
1812 if (!sd->IsEditable(true)) return CMD_ERROR;
1813
1814 if (flags.Test(DoCommandFlag::Execute)) {
1815 sd->AsIntSetting()->ChangeValue(&GetGameSettings(), value);
1816 }
1817
1818 return CommandCost();
1819}
1820
1829CommandCost CmdChangeCompanySetting(DoCommandFlags flags, const std::string &name, int32_t value)
1830{
1831 if (name.empty()) return CMD_ERROR;
1832 const SettingDesc *sd = GetCompanySettingFromName(name);
1833
1834 if (sd == nullptr) return CMD_ERROR;
1835 if (!sd->IsIntSetting()) return CMD_ERROR;
1836
1837 if (flags.Test(DoCommandFlag::Execute)) {
1839 }
1840
1841 return CommandCost();
1842}
1843
1851bool SetSettingValue(const IntSettingDesc *sd, int32_t value, bool force_newgame)
1852{
1853 const IntSettingDesc *setting = sd->AsIntSetting();
1854 if (setting->flags.Test(SettingFlag::PerCompany)) {
1855 if (Company::IsValidID(_local_company) && _game_mode != GameMode::Menu) {
1856 return Command<Commands::ChangeCompanySetting>::Post(setting->GetName(), value);
1857 }
1858
1859 setting->ChangeValue(&_settings_client.company, value);
1860 return true;
1861 }
1862
1863 /* If an item is company-based, we do not send it over the network
1864 * (if any) to change. Also *hack*hack* we update the _newgame version
1865 * of settings because changing a company-based setting in a game also
1866 * changes its defaults. At least that is the convention we have chosen */
1867 if (setting->flags.Test(SettingFlag::NoNetworkSync)) {
1868 if (_game_mode != GameMode::Menu) {
1869 setting->ChangeValue(&_settings_newgame, value);
1870 }
1871 setting->ChangeValue(&GetGameSettings(), value);
1872 return true;
1873 }
1874
1875 if (force_newgame) {
1876 setting->ChangeValue(&_settings_newgame, value);
1877 return true;
1878 }
1879
1880 /* send non-company-based settings over the network */
1882 return Command<Commands::ChangeSetting>::Post(setting->GetName(), value);
1883 }
1884 return false;
1885}
1886
1891void SetDefaultCompanySettings(CompanyID cid)
1892{
1893 Company *c = Company::Get(cid);
1895 for (auto &desc : _company_settings) {
1896 const IntSettingDesc *int_setting = GetSettingDesc(desc)->AsIntSetting();
1897 int_setting->MakeValueValidAndWrite(&c->settings, int_setting->GetDefaultValue());
1898 }
1899}
1900
1905{
1906 const void *old_object = &Company::Get(_current_company)->settings;
1907 const void *new_object = &_settings_client.company;
1908 for (auto &desc : _company_settings) {
1909 const SettingDesc *sd = GetSettingDesc(desc);
1910 uint32_t old_value = (uint32_t)sd->AsIntSetting()->Read(old_object);
1911 uint32_t new_value = (uint32_t)sd->AsIntSetting()->Read(new_object);
1912 /*
1913 * This is called from a command, and since it contains local configuration information
1914 * that the rest of the clients do not know about, we need to circumvent the normal ::Post
1915 * local command validation and immediately send the command to the server.
1916 */
1917 if (old_value != new_value) Command<Commands::ChangeCompanySetting>::SendNet(STR_NULL, _local_company, sd->GetName(), new_value);
1918 }
1919}
1920
1929bool SetSettingValue(const StringSettingDesc *sd, std::string_view value, bool force_newgame)
1930{
1932
1933 if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ && value == "(null)") {
1934 value = {};
1935 }
1936
1937 const void *object = (_game_mode == GameMode::Menu || force_newgame) ? &_settings_newgame : &_settings_game;
1938 sd->AsStringSetting()->ChangeValue(object, std::string{value});
1939 return true;
1940}
1941
1948void StringSettingDesc::ChangeValue(const void *object, std::string &&newval) const
1949{
1950 this->MakeValueValid(newval);
1951 if (this->pre_check != nullptr && !this->pre_check(newval)) return;
1952
1953 this->Write(object, newval);
1954 if (this->post_callback != nullptr) this->post_callback(newval);
1955
1956 if (_save_config) SaveToConfig();
1957}
1958
1959/* Those 2 functions need to be here, else we have to make some stuff non-static
1960 * and besides, it is also better to keep stuff like this at the same place */
1961void IConsoleSetSetting(std::string_view name, std::string_view value, bool force_newgame)
1962{
1963 const SettingDesc *sd = GetSettingFromName(name);
1964 /* Company settings are not in "list_settings", so don't try to modify them. */
1965 if (sd == nullptr || sd->flags.Test(SettingFlag::PerCompany)) {
1966 IConsolePrint(CC_ERROR, "'{}' is an unknown setting.", name);
1967 return;
1968 }
1969
1970 bool success = true;
1971 if (sd->IsStringSetting()) {
1972 success = SetSettingValue(sd->AsStringSetting(), value, force_newgame);
1973 } else if (sd->IsIntSetting()) {
1974 const IntSettingDesc *isd = sd->AsIntSetting();
1975 size_t val = isd->ParseValue(value);
1976 if (!_settings_error_list.empty()) {
1977 IConsolePrint(CC_ERROR, "'{}' is not a valid value for this setting.", value);
1978 _settings_error_list.clear();
1979 return;
1980 }
1981 success = SetSettingValue(isd, (int32_t)val, force_newgame);
1982 }
1983
1984 if (!success) {
1985 if (_network_server) {
1986 IConsolePrint(CC_ERROR, "This command/variable is not available during network games.");
1987 } else {
1988 IConsolePrint(CC_ERROR, "This command/variable is only available to a network server.");
1989 }
1990 }
1991}
1992
1993void IConsoleSetSetting(std::string_view name, int value)
1994{
1995 const SettingDesc *sd = GetSettingFromName(name);
1996 assert(sd != nullptr);
1997 SetSettingValue(sd->AsIntSetting(), value);
1998}
1999
2005void IConsoleGetSetting(std::string_view name, bool force_newgame)
2006{
2007 const SettingDesc *sd = GetSettingFromName(name);
2008 /* Company settings are not in "list_settings", so don't try to read them. */
2009 if (sd == nullptr || sd->flags.Test(SettingFlag::PerCompany)) {
2010 IConsolePrint(CC_ERROR, "'{}' is an unknown setting.", name);
2011 return;
2012 }
2013
2014 const void *object = (_game_mode == GameMode::Menu || force_newgame) ? &_settings_newgame : &_settings_game;
2015
2016 if (sd->IsStringSetting()) {
2017 IConsolePrint(CC_INFO, "Current value for '{}' is '{}'.", sd->GetName(), sd->AsStringSetting()->Read(object));
2018 } else if (sd->IsIntSetting()) {
2019 std::string value = sd->FormatValue(object);
2020 const IntSettingDesc *int_setting = sd->AsIntSetting();
2021 auto [min_val, max_val] = int_setting->GetRange();
2022 auto def_val = int_setting->GetDefaultValue();
2023 IConsolePrint(CC_INFO, "Current value for '{}' is '{}' (min: {}{}, max: {}, def: {}).",
2024 sd->GetName(), value, sd->flags.Test(SettingFlag::GuiZeroIsSpecial) ? "(0) " : "", min_val, max_val, def_val);
2025 }
2026}
2027
2028static void IConsoleListSettingsTable(const SettingTable &table, std::string_view prefilter)
2029{
2030 for (auto &desc : table) {
2031 const SettingDesc *sd = GetSettingDesc(desc);
2032 if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2033 if (!prefilter.empty() && sd->GetName().find(prefilter) == std::string::npos) continue;
2034 IConsolePrint(CC_DEFAULT, "{} = {}", sd->GetName(), sd->FormatValue(&GetGameSettings()));
2035 }
2036}
2037
2043void IConsoleListSettings(std::string_view prefilter)
2044{
2045 IConsolePrint(CC_HELP, "All settings with their current value:");
2046
2047 for (auto &table : GenericSettingTables()) {
2048 IConsoleListSettingsTable(table, prefilter);
2049 }
2050 for (auto &table : PrivateSettingTables()) {
2051 IConsoleListSettingsTable(table, prefilter);
2052 }
2053 for (auto &table : SecretSettingTables()) {
2054 IConsoleListSettingsTable(table, prefilter);
2055 }
2056
2057 IConsolePrint(CC_HELP, "Use 'setting' command to change a value.");
2058}
2059
2060ScriptConfigSettings::ScriptConfigSettings()
2061{
2062 /* Instantiate here, because unique_ptr needs a complete type. */
2063}
2064
2067{
2068 /* Instantiate here, because unique_ptr needs a complete type. */
2069}
2070
2071ScriptConfigSettings::ScriptConfigSettings(const ScriptConfigSettings &other)
2072{
2073 *this = other;
2074}
2075
2076ScriptConfigSettings &ScriptConfigSettings::operator=(const ScriptConfigSettings &other)
2077{
2078 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
2079 if (other.ai[c] != nullptr) {
2080 this->ai[c] = std::make_unique<AIConfig>(*other.ai[c]);
2081 }
2082 }
2083 if (other.game != nullptr) {
2084 this->game = std::make_unique<GameConfig>(*other.game);
2085 }
2086 return *this;
2087}
AIConfig stores the configuration settings of every AI.
Generic functions for replacing base data (graphics, sounds).
Generic functions for replacing base graphics data.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr enable_if_t< is_integral_v< T >, T > byteswap(T x) noexcept
Custom implementation of std::byteswap; remove once we build with C++23.
AI instantion of script configuration.
Definition ai_config.hpp:17
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=ScriptSettingSource::Default)
Get the AI configuration of specific company.
Definition ai_config.cpp:20
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
static const GraphicsSet * GetUsedSet()
Common return value for all commands.
IniFile to store a configuration.
Definition settings.cpp:143
Game script instantion of script configuration.
static GameConfig * GetConfig(ScriptSettingSource source=ScriptSettingSource::Default)
Get the script configuration.
@ ForceNewGame
Get the newgame Script config.
bool HasScript() const
Is this config attached to an Script?
void Change(std::optional< std::string_view > name, int version=-1, bool force_exact_match=false)
Set another Script to be loaded in this slot.
void StringToSettings(std::string_view value)
Convert a string which is stored in the config file or savegames to custom settings of this Script.
const std::string & GetName() const
Get the name of the Script.
std::string SettingsToString() const
Convert the custom settings to a string that can be stored in the config file or savegames.
Parse data from a string / buffer.
std::optional< T > TryReadIntegerBase(int base, bool clamp=false)
Try to read and parse an integer in number 'base', and then advance the reader.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
static const std::string_view WHITESPACE_NO_NEWLINE
ASCII whitespace characters, excluding new-line.
bool ReadIf(std::string_view str)
Check whether the next data matches 'str', and skip it.
void SkipUntilCharNotIn(std::string_view chars)
Skip 8-bit chars, while they are in 'chars', until they are not.
std::string_view ReadUntilCharIn(std::string_view chars)
Read 8-bit chars, while they are not in 'chars', until they are; and advance reader.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
Configuration options of the network stuff.
static const uint NETWORK_MAX_GRF_COUNT
Maximum number of GRFs that can be sent.
Definition config.h:88
void IConsolePrint(ExtendedTextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Definition console.cpp:90
Console functions used outside of the console code.
static const TextColour CC_HELP
Colour for help lines.
static const TextColour CC_INFO
Colour for information lines.
static const TextColour CC_DEFAULT
Default colour of the console.
static const TextColour CC_ERROR
Colour for error lines.
void ResetCurrencies(bool preserve_custom)
Will fill _currency_specs array with default values from origin_currency_specs Called only from newgr...
Definition currency.cpp:162
Functions to handle different currencies.
CurrencySpec & GetCustomCurrency()
Get the custom currency.
void DebugReconsiderSendRemoteMessages()
Reconsider whether we need to send debug messages to either NetworkAdminConsole or IConsolePrint.
Definition debug.cpp:261
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Functions related to errors.
std::list< ErrorMessageData > ErrorList
Define a queue with errors.
Definition error.h:51
void ScheduleErrorMessage(ErrorList &datas)
Schedule a list of errors.
@ Critical
Critical errors, the MessageBox is shown in all cases.
Definition error.h:27
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
void ShowFirstError()
Show the first error of the queue.
bool FioCheckFileExists(std::string_view filename, Subdirectory subdir)
Check whether the given file exists.
Definition fileio.cpp:123
std::string _config_file
Configuration file of OpenTTD.
Definition settings.cpp:64
Functions for standard in/out file operations.
@ NewGrf
Subdirectory for all NewGRFs.
Definition fileio_type.h:97
@ None
A path without any base directory.
Declarations for savegames operations.
fluid_settings_t * settings
FluidSynth settings handle.
GameConfig stores the configuration settings of every Game.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
@ GLAT_SETTING
Setting changed.
Definition gamelog.h:21
Functions related to world/map generation.
@ LG_ORIGINAL
The original landscape generator.
Definition genworld.h:21
static const uint CUSTOM_SEA_LEVEL_NUMBER_DIFFICULTY
Value for custom sea level in difficulty settings.
Definition genworld.h:46
static const uint CUSTOM_SEA_LEVEL_MIN_PERCENTAGE
Minimum percentage a user can specify for custom sea level.
Definition genworld.h:47
Types related to reading/writing '*.ini' files.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
constexpr uint ClampU(const uint a, const uint min, const uint max)
Clamp an unsigned integer between an interval.
StringList _network_host_list
The servers we know.
Definition network.cpp:76
bool _networking
are we in networking mode?
Definition network.cpp:67
StringList _network_ban_list
The banned clients.
Definition network.cpp:77
bool _network_server
network-server is active
Definition network.cpp:68
StringList _network_bind_list
The addresses to bind on.
Definition network.cpp:75
Basic functions/variables used all over the place.
Network functions used by other parts of OpenTTD.
@ Public
The game is publicly accessible.
@ Local
Do not communicate with the game coordinator.
void BadgeClassSaveConfig(IniFile &ini)
Save badge column preferences.
void BadgeClassLoadConfig(const IniFile &ini)
Load badge column preferences.
Functions related to NewGRF badge configuration.
GRFConfigList _grfconfig_static
First item in list of static GRF set up.
std::string GRFBuildParamList(const GRFConfig &c)
Build a string containing space separated parameter values.
const GRFConfig * FindGRFConfig(uint32_t grfid, FindGRFConfigMode mode, const MD5Hash *md5sum, uint32_t desired_version)
Find a NewGRF in the scanned list.
GRFConfigList _grfconfig_newgame
First item in list of default GRF set up.
bool FillGRFDetails(GRFConfig &config, bool is_static, Subdirectory subdir)
Find the GRFID of a given grf, and calculate its md5sum.
Functions to find and configure NewGRFs.
@ NotFound
GRF file was not found in the local cache.
@ Invalid
GRF is unusable with this version of OpenTTD.
@ Static
GRF file is used statically (can be used in any MP game).
@ System
GRF file is an openttd-internal system grf.
@ Unsafe
GRF file is unsafe for static usage.
@ Exact
Only find Grfs matching md5sum.
@ NewestValid
Find newest Grf, ignoring Grfs with GRFConfigFlag::Invalid set.
@ Editor
In the scenario editor.
Definition openttd.h:21
@ Normal
Playing a game.
Definition openttd.h:20
@ Menu
In the main menu.
Definition openttd.h:19
Functions/types etc.
void PickerSaveConfig(IniFile &ini)
Save favourites of all registered Pickers to config.
void PickerLoadConfig(const IniFile &ini)
Load favourites of all registered Pickers from config.
Declaration of OTTD revision dependent variables.
A number of safeguards to prevent using unsafe methods.
void WriteValue(void *ptr, VarType conv, int64_t val)
Write the value of a setting.
Definition saveload.cpp:847
int64_t ReadValue(const void *ptr, VarType conv)
Return a signed-long version of the value of a setting.
Definition saveload.cpp:823
@ SLE_VAR_NULL
useful to write zeros in savegame.
Definition saveload.h:693
@ SLE_VAR_STR
string pointer
Definition saveload.h:694
@ SLE_VAR_STRQ
string pointer enclosed in quotes
Definition saveload.h:695
constexpr size_t SlVarSize(VarType type)
Return expect size in bytes of a VarType.
Definition saveload.h:822
void * GetVariableAddress(const void *object, const SaveLoad &sld)
Get the address of the variable.
Definition saveload.h:1333
bool SlIsObjectCurrentlyValid(SaveLoadVersion version_from, SaveLoadVersion version_to)
Checks if some version from/to combination falls within the range of the active savegame version.
Definition saveload.h:1319
constexpr VarType GetVarMemType(VarType type)
Get the NumberType of a setting.
Definition saveload.h:791
static void GraphicsSetLoadConfig(IniFile &ini)
Load BaseGraphics set selection and configuration.
void HandleOldDiffCustom(bool savegame)
Reading of the old diff_custom array and transforming it to the new format.
void SyncCompanySettings()
Sync all company settings in a multiplayer game.
void IniLoadWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Load a WindowDesc from config.
Definition settings.cpp:897
static void RemoveEntriesFromIni(IniFile &ini, const SettingTable &table)
Remove all entries from a settings table from an ini-file.
void SaveGRFPresetToConfig(std::string_view config_name, GRFConfigList &config)
Save a NewGRF configuration with a preset name.
StringList GetGRFPresetList()
Get the list of known NewGrf presets.
static void ValidateSettings()
Checks if any settings are set to incorrect values, and sets them to correct values in that case.
Definition settings.cpp:966
bool IsConversionNeeded(const ConfigIniFile &ini, const std::string &group, const std::string &old_var, const std::string &new_var, const IniItem **old_item)
Check whether a conversion should be done, and based on what old setting information.
void IConsoleGetSetting(std::string_view name, bool force_newgame)
Output value of a specific setting to the console.
std::string _secrets_file
Secrets configuration file of OpenTTD.
Definition settings.cpp:66
static GRFConfigList GRFLoadConfig(const IniFile &ini, std::string_view grpname, bool is_static)
Load a GRF configuration.
std::string _favs_file
Picker favourites configuration file of OpenTTD.
Definition settings.cpp:67
static ErrorList _settings_error_list
Errors while loading minimal settings.
Definition settings.cpp:69
void DeleteGRFPresetFromConfig(std::string_view config_name)
Delete a NewGRF configuration by preset name.
void SetDefaultCompanySettings(CompanyID cid)
Set the company settings for a new company to their default values.
static void IniLoadSettingList(IniFile &ini, std::string_view grpname, StringList &list)
Loads all items from a 'grpname' section into a list The list parameter can be a nullptr pointer,...
Definition settings.cpp:859
std::vector< const SettingDesc * > GetFilteredSettingCollection(std::function< bool(const SettingDesc &desc)> func)
Get a collection of settings matching a custom filter.
void LoadFromConfig(bool startup)
Load the values from the configuration files.
GRFConfigList LoadGRFPresetFromConfig(std::string_view config_name)
Load a NewGRF configuration by preset-name.
bool SetSettingValue(const IntSettingDesc *sd, int32_t value, bool force_newgame)
Top function to save the new value of an element of the Settings struct.
void SaveToConfig()
Save the values to the configuration file.
VehicleDefaultSettings _old_vds
Used for loading default vehicles settings from old savegames.
Definition settings.cpp:63
void PrepareOldDiffCustom()
Prepare for reading and old diff_custom by zero-ing the memory.
static void IniLoadSettings(IniFile &ini, const SettingTable &settings_table, std::string_view grpname, void *object, bool only_startup)
Load values from a group of an IniFile structure into the internal representation.
Definition settings.cpp:644
void GetSaveLoadFromSettingTable(SettingTable settings, std::vector< SaveLoad > &saveloads)
Get the SaveLoad for all settings in the settings table.
static void GraphicsSetSaveConfig(IniFile &ini)
Save BaseGraphics set selection and configuration.
static bool LoadIntList(std::optional< std::string_view > str, void *array, int nelems, VarType type)
Load parsed string-values into an integer-array (intlist).
Definition settings.cpp:308
static std::optional< std::vector< uint32_t > > ParseIntList(std::string_view str)
Parse a string into a vector of uint32s.
Definition settings.cpp:273
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
CommandCost CmdChangeCompanySetting(DoCommandFlags flags, const std::string &name, int32_t value)
Change one of the per-company settings.
IniFileVersion
Ini-file versions.
Definition settings.cpp:169
@ IFV_0
0 All versions prior to introduction.
Definition settings.cpp:170
@ IFV_AUTOSAVE_RENAME
5 PR#11143 Renamed values of autosave to be in minutes.
Definition settings.cpp:176
@ IFV_LINKGRAPH_SECONDS
3 PR#10610 Store linkgraph update intervals in seconds instead of days.
Definition settings.cpp:173
@ IFV_REMOVE_GENERATION_SEED
7 PR#11927 Remove "generation_seed" from configuration.
Definition settings.cpp:178
@ IFV_MAX_VERSION
Highest possible ini-file version.
Definition settings.cpp:181
@ IFV_NETWORK_PRIVATE_SETTINGS
4 PR#10762 Move use_relay_service to private settings.
Definition settings.cpp:174
@ IFV_DEFAULT_RAIL_ROAD
8 PR#15585 Update default rail type setting to support road and tram tiles
Definition settings.cpp:179
@ IFV_GAME_TYPE
2 PR#9515 Convert server_advertise to server_game_type.
Definition settings.cpp:172
@ IFV_RIGHT_CLICK_CLOSE
6 PR#10204 Add alternative right click to close windows setting.
Definition settings.cpp:177
@ IFV_PRIVATE_SECRETS
1 PR#9298 Moving of settings from openttd.cfg to private.cfg / secrets.cfg.
Definition settings.cpp:171
SettingTable GetSaveLoadSettingTable()
Create a single table with all settings that should be stored/loaded in the savegame.
void IConsoleListSettings(std::string_view prefilter)
List all settings and their value to the console.
static void IniSaveSettingList(IniFile &ini, std::string_view grpname, StringList &list)
Saves all items from a list into the 'grpname' section The list parameter can be a nullptr pointer,...
Definition settings.cpp:881
const uint16_t INIFILE_VERSION
Current ini-file version of OpenTTD.
Definition settings.cpp:184
static void IniSaveSettings(IniFile &ini, const SettingTable &settings_table, std::string_view grpname, void *object, bool)
Save the values of settings to the inifile.
Definition settings.cpp:729
static auto & SecretSettingTables()
List of all the secrets setting tables.
Definition settings.cpp:117
std::string _private_file
Private configuration file of OpenTTD.
Definition settings.cpp:65
GameSettings _settings_newgame
Game settings for new games (updated from the intro screen).
Definition settings.cpp:62
static auto & PrivateSettingTables()
List of all the private setting tables.
Definition settings.cpp:105
static void SaveVersionInConfig(IniFile &ini)
Save the version of OpenTTD to the ini file.
static std::optional< uint32_t > LookupManyOfMany(std::span< const std::string_view > many, std::string_view str)
Find the set-integer value MANYofMANY type in a string.
Definition settings.cpp:247
static const SettingDesc * GetSettingFromName(std::string_view name, const SettingTable &settings)
Given a name of setting, return a setting description from the table.
static auto & GenericSettingTables()
List of all the generic setting tables.
Definition settings.cpp:82
void IniSaveWindowSettings(IniFile &ini, std::string_view grpname, WindowDesc *desc)
Save a WindowDesc to config.
Definition settings.cpp:908
static const SettingDesc * GetCompanySettingFromName(std::string_view name)
Given a name of setting, return a company setting description of it.
CommandCost CmdChangeSetting(DoCommandFlags flags, const std::string &name, int32_t value)
Network-safe changing of settings (server-only).
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Command definitions related to settings.
Functions related to setting/changing the settings.
@ NotInSave
Do not save with savegame, basically client-based.
@ Sandbox
This setting is a sandbox setting.
@ SceneditOnly
This setting can only be changed in the scenario editor.
@ PerCompany
This setting can be different for each company (saved in company struct).
@ NewgameOnly
This setting cannot be changed in a game.
@ GuiZeroIsSpecial
A value of zero is possible and has a custom string (the one after "strval").
@ NoNetwork
This setting does not apply to network games; it may not be changed during the game.
@ NotInConfig
Do not save to config file.
@ GuiDropdown
The value represents a limited number of string-options (internally integer) presented as dropdown.
@ SceneditToo
This setting can be changed in the scenario editor (only makes sense when SettingFlag::NewgameOnly is...
@ NoNetworkSync
Do not synchronize over network (but it is saved if SettingFlag::NotInSave is not set).
@ NetworkOnly
This setting only applies to network games.
SettingType
Type of settings for filtering.
@ ST_CLIENT
Client setting.
@ ST_GAME
Game setting.
@ ST_COMPANY
Company setting.
static constexpr const SettingDesc * GetSettingDesc(const SettingVariant &desc)
Helper to convert the type of the iterated settings description to a pointer to it.
Definition of the configuration tables of the settings.
GameSettings & GetGameSettings()
Get the settings-object applicable for the current situation: the newgame settings when we're in the ...
DefaultRailRoadType
How to select the default rail/road types.
Definition of base types and functions in a cross-platform compatible way.
bool ConvertHexToBytes(std::string_view hex, std::span< uint8_t > bytes)
Convert a hex-string to a byte-array, while validating it was actually hex.
Definition string.cpp:572
void StrMakeValidInPlace(char *str, StringValidationSettings settings)
Scans the string for invalid characters and replaces them with a question mark '?
Definition string.cpp:157
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
Parse strings.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.
Functions related to low-level strings.
std::vector< std::string > StringList
Type for a list of strings.
Definition string_type.h:61
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
std::string name
The name of the base set.
uint32_t shortname
Four letter short variant of the name.
std::string FormatValue(const void *object) const override
Format the value of the setting associated with this object.
Definition settings.cpp:771
static std::optional< bool > ParseSingleValue(std::string_view str)
Find whether a string was a boolean true or a boolean false.
Definition settings.cpp:232
int32_t ParseValue(std::string_view str) const override
Convert a string representation (external) of an integer-like setting to an integer.
Definition settings.cpp:434
All settings that are only important for the local client.
CompanySettings settings
settings specific for each company
Information about GRF, used in the game and (part of it) in savegames.
uint32_t version
NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown.
std::vector< uint32_t > param
GRF parameters.
All settings together for the game.
All data of a graphics set.
Ini file that supports both loading and saving.
Definition ini_type.h:87
bool SaveToDisk(const std::string &filename)
Save the Ini file's data to the disk.
Definition ini.cpp:42
IniFile(const IniGroupNameList &list_group_names={})
Create a new ini file with given group names.
Definition ini.cpp:33
A group within an ini file.
Definition ini_type.h:34
const IniItem * GetItem(std::string_view name) const
Get the item with the given name.
Definition ini_load.cpp:50
std::string comment
comment for group
Definition ini_type.h:38
void Clear()
Clear all items in the group.
Definition ini_load.cpp:96
void RemoveItem(std::string_view name)
Remove the item with the given name.
Definition ini_load.cpp:88
std::string name
name of group
Definition ini_type.h:37
IniItem & CreateItem(std::string_view name)
Create an item with the given name.
Definition ini_load.cpp:79
IniItem & GetOrCreateItem(std::string_view name)
Get the item with the given name, and if it doesn't exist create a new item.
Definition ini_load.cpp:64
std::list< IniItem > items
all items in the group
Definition ini_type.h:35
A single "line" in an ini file.
Definition ini_type.h:23
std::optional< std::string > value
The value of this item.
Definition ini_type.h:25
std::string name
The name of this item.
Definition ini_type.h:24
void SetValue(std::string_view value)
Replace the current value with another value.
Definition ini_load.cpp:30
std::list< IniGroup > groups
all groups in the ini
Definition ini_type.h:53
void RemoveGroup(std::string_view name)
Remove the group with the given name.
Definition ini_load.cpp:173
const IniGroup * GetGroup(std::string_view name) const
Get the group with the given name.
Definition ini_load.cpp:117
void LoadFromDisk(std::string_view filename, Subdirectory subdir)
Load the Ini file's data from the disk.
Definition ini_load.cpp:184
IniGroup & GetOrCreateGroup(std::string_view name)
Get the group with the given name, and if it doesn't exist create a new group.
Definition ini_load.cpp:145
Base integer type, including boolean, settings.
static std::optional< int32_t > ParseSingleValue(std::string_view str, int32_t min, uint32_t max)
Find whether a string was a valid int setting.
Definition settings.cpp:194
StringID str_help
(Translated) string with help text; gui only.
StringID str_val
(Translated) first string describing the value.
void MakeValueValid(int32_t &value) const
Make the value valid given the limitations of this setting.
Definition settings.cpp:526
void ResetToDefault(void *object) const override
Reset the setting to its default value.
Definition settings.cpp:790
std::string FormatValue(const void *object) const override
Format the value of the setting associated with this object.
Definition settings.cpp:760
int32_t def
default value given when none is present
std::tuple< int32_t, uint32_t > GetRange() const
Get the min/max range for the setting.
Definition settings.cpp:500
StringID GetTitle() const
Get the title of the setting.
Definition settings.cpp:450
void ChangeValue(const void *object, int32_t newvalue) const
Handle changing a value.
uint32_t max
maximum values
GetDefaultValueCallback * get_def_cb
Callback to set the correct default value.
bool IsSameValue(const IniItem *item, void *object) const override
Check whether the value in the Ini item is the same as is saved in this setting in the object.
Definition settings.cpp:777
int32_t min
minimum values
PreChangeCheck * pre_check
Callback to check for the validity of the setting.
int32_t GetDefaultValue() const
Get the default value of the setting.
Definition settings.cpp:491
void Write(const void *object, int32_t value) const
Set the value of a setting.
Definition settings.cpp:580
StringID GetHelp() const
Get the help text of the setting.
Definition settings.cpp:459
virtual bool IsBoolSetting() const
Check whether this setting is a boolean type setting.
PostChangeCallback * post_callback
Callback when the setting has been changed.
StringID str
(translated) string with descriptive text; gui and console
void MakeValueValidAndWrite(const void *object, int32_t value) const
Make the value valid and then write it to the setting.
Definition settings.cpp:511
std::pair< StringParameter, StringParameter > GetValueParams(int32_t value) const
Get parameters for drawing the value of the setting.
Definition settings.cpp:469
int32_t Read(const void *object) const
Read the integer from the the actual setting.
Definition settings.cpp:591
virtual int32_t ParseValue(std::string_view str) const
Convert a string representation (external) of an integer-like setting to an integer.
Definition settings.cpp:390
bool IsDefaultValue(void *object) const override
Check whether the value is the same as the default value.
Definition settings.cpp:784
void ParseValue(const IniItem *item, void *object) const override
Parse/read the value from the Ini item into the setting associated with this object.
Definition settings.cpp:698
void ResetToDefault(void *object) const override
Reset the setting to its default value.
Definition settings.cpp:844
bool IsSameValue(const IniItem *item, void *object) const override
Check whether the value in the Ini item is the same as is saved in this setting in the object.
Definition settings.cpp:832
std::string_view def
default value given when none is present
std::string FormatValue(const void *object) const override
Convert a list to a string representation.
Definition settings.cpp:333
bool IsDefaultValue(void *object) const override
Check whether the value is the same as the default value.
Definition settings.cpp:838
int32_t ParseValue(std::string_view str) const override
Convert a string representation (external) of an integer-like setting to an integer.
Definition settings.cpp:423
std::string FormatValue(const void *object) const override
Format the value of the setting associated with this object.
Definition settings.cpp:370
OnConvert * many_cnvt
callback procedure when loading value mechanism fails
std::string FormatValue(const void *object) const override
Format the value of the setting associated with this object.
Definition settings.cpp:364
static std::optional< uint32_t > ParseSingleValue(std::string_view str, std::span< const std::string_view > many)
Find the index value of a ONEofMANY type in a string.
Definition settings.cpp:214
std::vector< std::string_view > many
possible values for this type
int32_t ParseValue(std::string_view str) const override
Convert a string representation (external) of an integer-like setting to an integer.
Definition settings.cpp:409
static Company * Get(auto index)
SaveLoadVersion version_to
Save/load the variable before this savegame version.
Definition saveload.h:763
VarType conv
Type of the variable to be saved; this field combines both FileVarType and MemVarType.
Definition saveload.h:760
SaveLoadVersion version_from
Save/load the variable starting from this savegame version.
Definition saveload.h:762
Container for AI and Game script configuration.
std::unique_ptr< class GameConfig > game
settings for gamescript
TypedIndexContainer< std::array< std::unique_ptr< class AIConfig >, MAX_COMPANIES >, CompanyID > ai
settings per company
~ScriptConfigSettings()
Needs to be manually defined due to incomplete definition of types in the header.
Iterable ensemble of each set bit in a value.
Properties of config file settings.
virtual void ParseValue(const IniItem *item, void *object) const =0
Parse/read the value from the Ini item into the setting associated with this object.
bool IsEditable(bool do_command=false) const
Check whether the setting is editable in the current gamemode.
Definition settings.cpp:918
SettingFlags flags
Handles how a setting would show up in the GUI (text/currency, etc.).
virtual bool IsStringSetting() const
Check whether this setting is an string type setting.
SettingType GetType() const
Return the type of the setting.
Definition settings.cpp:935
constexpr const std::string & GetName() const
Get the name of this setting.
bool startup
Setting has to be loaded directly at startup?.
virtual std::string FormatValue(const void *object) const =0
Format the value of the setting associated with this object.
const struct StringSettingDesc * AsStringSetting() const
Get the setting description of this setting as a string setting.
Definition settings.cpp:955
virtual bool IsSameValue(const IniItem *item, void *object) const =0
Check whether the value in the Ini item is the same as is saved in this setting in the object.
SaveLoad save
Internal structure (going to savegame, parts to config).
virtual bool IsIntSetting() const
Check whether this setting is an integer type setting.
const struct IntSettingDesc * AsIntSetting() const
Get the setting description of this setting as an integer setting.
Definition settings.cpp:945
String settings.
const std::string & Read(const void *object) const
Read the string from the the actual setting.
Definition settings.cpp:630
std::string_view def
Default value given when none is present.
void Write(const void *object, std::string_view str) const
Write a string to the actual setting.
Definition settings.cpp:620
uint32_t max_length
Maximum length of the string, 0 means no maximum length.
PreChangeCheck * pre_check
Callback to check for the validity of the setting.
void ChangeValue(const void *object, std::string &&newval) const
Handle changing a string value.
void MakeValueValid(std::string &str) const
Make the value valid given the limitations of this setting.
Definition settings.cpp:604
bool IsSameValue(const IniItem *item, void *object) const override
Check whether the value in the Ini item is the same as is saved in this setting in the object.
Definition settings.cpp:811
bool IsDefaultValue(void *object) const override
Check whether the value is the same as the default value.
Definition settings.cpp:821
PostChangeCallback * post_callback
Callback when the setting has been changed.
void ResetToDefault(void *object) const override
Reset the setting to its default value.
Definition settings.cpp:827
std::string FormatValue(const void *object) const override
Format the value of the setting associated with this object.
Definition settings.cpp:795
void ParseValue(const IniItem *item, void *object) const override
Parse/read the value from the Ini item into the setting associated with this object.
Definition settings.cpp:691
Default settings for vehicles.
High level window description.
Definition window_gui.h:172
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3223
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition window.cpp:1158
Window functions not directly related to making/drawing windows.