• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 24635933460

19 Apr 2026 06:21PM UTC coverage: 50.203% (-0.2%) from 50.362%
24635933460

Pull #1890

github

web-flow
Merge 3877a723b into 7b5704a17
Pull Request #1890: Add support for borderless Windows

167 of 641 new or added lines in 44 files covered. (26.05%)

26 existing lines in 9 files now uncovered.

23078 of 45969 relevant lines covered (50.2%)

43616.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.25
/libs/s25main/Settings.cpp
1
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "Settings.h"
6
#include "DrawPoint.h"
7
#include "RTTR_Version.h"
8
#include "RttrConfig.h"
9
#include "driver/VideoInterface.h"
10
#include "drivers/AudioDriverWrapper.h"
11
#include "drivers/VideoDriverWrapper.h"
12
#include "enum_cast.hpp"
13
#include "files.h"
14
#include "helpers/MaxEnumValue.h"
15
#include "helpers/strUtils.h"
16
#include "languages.h"
17
#include "gameData/PortraitConsts.h"
18
#include "gameData/const_gui_ids.h"
19
#include "libsiedler2/ArchivItem_Ini.h"
20
#include "libsiedler2/ArchivItem_Text.h"
21
#include "libsiedler2/libsiedler2.h"
22
#include "s25util/StringConversion.h"
23
#include "s25util/System.h"
24
#include "s25util/error.h"
25
#include <boost/filesystem/operations.hpp>
26

27
namespace {
28
#ifdef __ANDROID__
29
constexpr bool SHARED_TEXTURES_DEFAULT = false;
30
constexpr MapScrollMode MAP_SCROLL_MODE_DEFAULT = MapScrollMode::GrabAndDrag;
31
#else
32
constexpr bool SHARED_TEXTURES_DEFAULT = true;
33
constexpr MapScrollMode MAP_SCROLL_MODE_DEFAULT = MapScrollMode::ScrollOpposite;
34
#endif
35
} // namespace
36

37
const int Settings::VERSION = 13;
38
const std::array<std::string, 10> Settings::SECTION_NAMES = {
39
  {"global", "video", "language", "driver", "sound", "lobby", "server", "proxy", "interface", "addons"}};
40

41
const std::array<short, 13> Settings::SCREEN_REFRESH_RATES = {
42
  {-1, 25, 30, 50, 60, 75, 80, 100, 120, 150, 180, 200, 240}};
43

44
const std::map<GUI_ID, std::string> persistentWindows = {{CGI_CHAT, "wnd_chat"},
45
                                                         {CGI_POSTOFFICE, "wnd_postoffice"},
46
                                                         {CGI_DISTRIBUTION, "wnd_distribution"},
47
                                                         {CGI_BUILDORDER, "wnd_buildorder"},
48
                                                         {CGI_TRANSPORT, "wnd_transport"},
49
                                                         {CGI_MILITARY, "wnd_military"},
50
                                                         {CGI_TOOLS, "wnd_tools"},
51
                                                         {CGI_INVENTORY, "wnd_inventory"},
52
                                                         {CGI_MINIMAP, "wnd_minimap"},
53
                                                         {CGI_BUILDINGS, "wnd_buildings"},
54
                                                         {CGI_BUILDINGSPRODUCTIVITY, "wnd_buildingsproductivity"},
55
                                                         {CGI_MUSICPLAYER, "wnd_musicplayer"},
56
                                                         {CGI_STATISTICS, "wnd_statistics"},
57
                                                         {CGI_ECONOMICPROGRESS, "wnd_economicprogress"},
58
                                                         {CGI_DIPLOMACY, "wnd_diplomacy"},
59
                                                         {CGI_SHIP, "wnd_ship"},
60
                                                         {CGI_MERCHANDISE_STATISTICS, "wnd_merchandise_statistics"}};
61

62
namespace validate {
63
boost::optional<uint16_t> checkPort(const std::string& port)
9✔
64
{
65
    int32_t iPort;
66
    if((helpers::tryFromString(port, iPort) || s25util::tryFromStringClassic(port, iPort)) && checkPort(iPort))
9✔
67
        return static_cast<uint16_t>(iPort);
3✔
68
    else
69
        return boost::none;
6✔
70
}
71
bool checkPort(int port)
7✔
72
{
73
    // Disallow port 0 as it may cause problems
74
    return port > 0 && port <= 65535;
7✔
75
}
76
} // namespace validate
77

78
Settings::Settings() //-V730
5✔
79
{
80
    LoadDefaults();
5✔
81
}
5✔
82

83
void Settings::LoadDefaults()
5✔
84
{
85
    // global
86
    // {
87
    global.submitDebugData = SubmitDebugData::AskAtStart;
5✔
88
    global.useUPNP = false;
5✔
89
    global.smartCursor = true;
5✔
90
    global.debugMode = false;
5✔
91
    global.showGFInfo = false;
5✔
92
    // }
93

94
    // video
95
    // {
96
    if(VIDEODRIVER.IsLoaded())
5✔
97
    {
98
        video.fullscreenSize = (VIDEODRIVER.GetDisplayMode() == DisplayMode::Fullscreen) ? VIDEODRIVER.GetWindowSize() :
3✔
99
                                                                                           VIDEODRIVER.MinWindowSize;
3✔
100
        video.windowedSize = (VIDEODRIVER.GetDisplayMode() == DisplayMode::Windowed) ? VIDEODRIVER.GetWindowSize() :
3✔
NEW
101
                                                                                       VIDEODRIVER.MinWindowSize;
×
102
        video.displayMode = VIDEODRIVER.GetDisplayMode();
3✔
103
    } else
104
    {
105
        video.windowedSize = video.fullscreenSize = VIDEODRIVER.MinWindowSize;
2✔
106
        video.displayMode = DisplayMode::Windowed;
2✔
107
    }
108
    video.framerate = 0; // Special value for HW vsync
5✔
109
    video.vbo = true;
5✔
110
    video.sharedTextures = SHARED_TEXTURES_DEFAULT;
5✔
111
    video.guiScale = 0; // special value indicating automatic selection
5✔
112
    // }
113

114
    // language
115
    // {
116
    language.language.clear();
5✔
117
    // }
118

119
    LANGUAGES.setLanguage(language.language);
5✔
120

121
    // driver
122
    // {
123
    driver.audio = AUDIODRIVER.GetName();
5✔
124
    driver.video = VIDEODRIVER.GetName();
5✔
125
    // }
126

127
    // sound
128
    // {
129
    sound.musicEnabled = false;
5✔
130
    sound.musicVolume = 30;
5✔
131
    sound.effectsEnabled = true;
5✔
132
    sound.effectsVolume = 75;
5✔
133
    sound.birdsEnabled = true;
5✔
134
    sound.playlist = s25::files::defaultPlaylist;
5✔
135
    // }
136

137
    // lobby
138
    // {
139

140
    lobby.name = System::getUserName();
5✔
141
    lobby.portraitIndex = 0;
5✔
142
    lobby.password.clear();
5✔
143
    lobby.savePassword = false;
5✔
144
    // }
145

146
    // server
147
    // {
148
    server.lastIP.clear();
5✔
149
    server.localPort = 3665;
5✔
150
    server.ipv6 = false;
5✔
151
    // }
152

153
    proxy = ProxySettings();
5✔
154
    proxy.port = 1080;
5✔
155

156
    // interface
157
    // {
158
    interface.autosaveInterval = 0;
5✔
159
    interface.mapScrollMode = MAP_SCROLL_MODE_DEFAULT;
5✔
160
    interface.enableWindowPinning = false;
5✔
161
    interface.windowSnapDistance = 8;
5✔
162
    // }
163

164
    // addons
165
    // {
166
    addons.configuration.clear();
5✔
167
    // }
168

169
    LoadIngameDefaults();
5✔
170
}
5✔
171

172
void Settings::LoadIngameDefaults()
5✔
173
{
174
    // ingame
175
    // {
176
    ingame.scaleStatistics = false;
5✔
177
    ingame.showBQ = false;
5✔
178
    ingame.showNames = false;
5✔
179
    ingame.showProductivity = false;
5✔
180
    ingame.minimapExtended = false;
5✔
181
    // }
182

183
    // windows
184
    // {
185
    for(const auto& window : persistentWindows)
90✔
186
        windows.persistentSettings[window.first] = PersistentWindowSettings();
85✔
187
    // }
188
}
5✔
189

190
///////////////////////////////////////////////////////////////////////////////
191
// Routine for loading the configuration
192
void Settings::Load()
×
193
{
194
    libsiedler2::Archiv settings;
×
195
    const auto settingsPath = RTTRCONFIG.ExpandPath(s25::resources::config);
×
196
    try
197
    {
198
        if(libsiedler2::Load(settingsPath, settings) != 0 || settings.size() < SECTION_NAMES.size())
×
199
            throw std::runtime_error("File missing or invalid");
×
200

201
        const libsiedler2::ArchivItem_Ini* iniGlobal =
202
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("global"));
×
203
        const libsiedler2::ArchivItem_Ini* iniVideo = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("video"));
×
204
        const libsiedler2::ArchivItem_Ini* iniLanguage =
205
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("language"));
×
206
        const libsiedler2::ArchivItem_Ini* iniDriver =
207
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("driver"));
×
208
        const libsiedler2::ArchivItem_Ini* iniSound = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("sound"));
×
209
        const libsiedler2::ArchivItem_Ini* iniLobby = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("lobby"));
×
210
        const libsiedler2::ArchivItem_Ini* iniServer =
211
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("server"));
×
212
        const libsiedler2::ArchivItem_Ini* iniProxy = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("proxy"));
×
213
        const libsiedler2::ArchivItem_Ini* iniInterface =
214
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("interface"));
×
215
        const libsiedler2::ArchivItem_Ini* iniAddons =
216
          static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("addons"));
×
217

218
        // Is one of the categories missing?
219
        if(!iniGlobal || !iniVideo || !iniLanguage || !iniDriver || !iniSound || !iniLobby || !iniServer || !iniProxy
×
220
           || !iniInterface || !iniAddons)
×
221
        {
222
            throw std::runtime_error("Missing section");
×
223
        }
224
        // Is the settings version correct?
225
        if(iniGlobal->getValue("version", 0) != VERSION)
×
226
            throw std::runtime_error("Wrong version");
×
227

228
        // global
229
        // {
230
        if(iniGlobal->getValue("gameversion") != rttr::version::GetRevision())
×
231
            s25util::warning("Your application version has changed - please recheck your settings!");
×
232

NEW
233
        unsigned submitDebugData = iniGlobal->getIntValue("submit_debug_data");
×
NEW
234
        if(submitDebugData <= helpers::MaxEnumValue_v<SubmitDebugData>)
×
NEW
235
            global.submitDebugData = static_cast<SubmitDebugData>(submitDebugData);
×
236
        else
NEW
237
            global.submitDebugData = SubmitDebugData::AskAtStart;
×
NEW
238
        global.useUPNP = iniGlobal->getBoolValue("use_upnp");
×
239
        global.smartCursor = iniGlobal->getValue("smartCursor", true);
×
240
        global.debugMode = iniGlobal->getValue("debugMode", false);
×
241
        global.showGFInfo = iniGlobal->getValue("showGFInfo", false);
×
242
        // };
243

244
        // video
245
        // {
246
        video.windowedSize.width = iniVideo->getIntValue("windowed_width");
×
247
        video.windowedSize.height = iniVideo->getIntValue("windowed_height");
×
248
        video.fullscreenSize.width = iniVideo->getIntValue("fullscreen_width");
×
249
        video.fullscreenSize.height = iniVideo->getIntValue("fullscreen_height");
×
NEW
250
        const auto displayMode = iniVideo->getValue("displayMode", -1);
×
NEW
251
        if(displayMode >= 0 && static_cast<unsigned>(displayMode) <= helpers::MaxEnumValue_v<DisplayMode::Type>)
×
NEW
252
            video.displayMode = DisplayMode(displayMode);
×
253
        else
254
        {
255
            // Compatibility with prior settings format
NEW
256
            video.displayMode =
×
NEW
257
              iniVideo->getValue("fullscreen", false) ? DisplayMode::Fullscreen : DisplayMode::Windowed;
×
258
        }
NEW
259
        video.displayMode.resizeable = !iniVideo->getValue("lock_window_size", false);
×
260
        video.framerate = iniVideo->getValue("framerate", 0);
×
261
        video.vbo = iniVideo->getBoolValue("vbo");
×
NEW
262
        video.sharedTextures = iniVideo->getBoolValue("shared_textures");
×
263
        video.guiScale = iniVideo->getValue("gui_scale", 0);
×
264
        // };
265

266
        if(video.fullscreenSize.width == 0 || video.fullscreenSize.height == 0 || video.windowedSize.width == 0
×
267
           || video.windowedSize.height == 0)
×
268
            throw std::runtime_error("Invalid video settings");
×
269

270
        // language
271
        // {
272
        language.language = iniLanguage->getValue("language");
×
273
        // }
274

275
        LANGUAGES.setLanguage(language.language);
×
276

277
        // driver
278
        // {
279
        driver.video = iniDriver->getValue("video");
×
280
        driver.audio = iniDriver->getValue("audio");
×
281
        // }
282

283
        // sound
284
        // {
285
        sound.musicEnabled = iniSound->getBoolValue("musik");
×
286
        sound.musicVolume = iniSound->getIntValue("musik_volume");
×
287
        sound.effectsEnabled = iniSound->getBoolValue("effekte");
×
288
        sound.effectsVolume = iniSound->getIntValue("effekte_volume");
×
289
        sound.birdsEnabled = iniSound->getValue("bird_sounds", true);
×
290
        sound.playlist = iniSound->getValue("playlist");
×
291
        // }
292

293
        // lobby
294
        // {
295
        lobby.name = iniLobby->getValue("name");
×
296
        lobby.portraitIndex = iniLobby->getIntValue("portrait_index");
×
297
        lobby.password = iniLobby->getValue("password");
×
NEW
298
        lobby.savePassword = iniLobby->getBoolValue("save_password");
×
299
        // }
300

301
        if(lobby.name.empty())
×
302
            lobby.name = System::getUserName();
×
303

304
        if(lobby.portraitIndex >= Portraits.size())
×
305
        {
306
            lobby.portraitIndex = 0;
×
307
        }
308

309
        // server
310
        // {
NEW
311
        server.lastIP = iniServer->getValue("last_ip");
×
312
        boost::optional<uint16_t> port = validate::checkPort(iniServer->getValue("local_port"));
×
313
        server.localPort = port.value_or(3665);
×
314
        server.ipv6 = iniServer->getBoolValue("ipv6");
×
315
        // }
316

317
        // proxy
318
        // {
319
        proxy.hostname = iniProxy->getValue("proxy");
×
320
        port = validate::checkPort(iniProxy->getValue("port"));
×
321
        proxy.port = port.value_or(1080);
×
322
        proxy.type = ProxyType(iniProxy->getIntValue("typ"));
×
323
        // }
324

325
        // Empty proxy address completely disables proxy.
326
        // Disabled proxy removes proxy address.
327
        if(proxy.hostname.empty() || (proxy.type != ProxyType::Socks4 && proxy.type != ProxyType::Socks5))
×
328
        {
329
            proxy.type = ProxyType::None;
×
330
            proxy.hostname.clear();
×
331
        }
332
        // Enabled Socks v4 disables IPv6
333
        else if(proxy.type == ProxyType::Socks4 && server.ipv6)
×
334
            server.ipv6 = false;
×
335

336
        // interface
337
        // {
NEW
338
        interface.autosaveInterval = iniInterface->getIntValue("autosave_interval");
×
339
        try
340
        {
341
            interface.mapScrollMode = static_cast<MapScrollMode>(iniInterface->getIntValue("map_scroll_mode"));
×
342
        } catch(const std::runtime_error&)
×
343
        {
344
            interface.mapScrollMode =
×
345
              iniInterface->getBoolValue("invert_mouse") ? MapScrollMode::ScrollSame : MapScrollMode::ScrollOpposite;
×
346
            s25util::warning(
×
347
              "Value 'map_scroll_mode' not found! Using 'invert_mouse' instead - please recheck your settings!");
348
        }
349
        interface.enableWindowPinning = iniInterface->getValue("enable_window_pinning", false);
×
350
        interface.windowSnapDistance = iniInterface->getValue("window_snap_distance", 8);
×
351
        // }
352

353
        // addons
354
        // {
355
        for(unsigned addon = 0; addon < iniAddons->size(); ++addon)
×
356
        {
357
            const auto* item = dynamic_cast<const libsiedler2::ArchivItem_Text*>(iniAddons->get(addon));
×
358

359
            if(item)
×
360
                addons.configuration.insert(std::make_pair(s25util::fromStringClassic<unsigned>(item->getName()),
×
361
                                                           s25util::fromStringClassic<unsigned>(item->getText())));
×
362
        }
363

364
        LoadIngame();
×
365
        // }
366
    } catch(const std::runtime_error& e)
×
367
    {
368
        s25util::warning(std::string("Could not use settings from \"") + settingsPath.string()
×
369
                         + "\", using default values. Reason: " + e.what());
×
370
        LoadDefaults();
×
371
        Save();
×
372
    }
373
}
×
374

375
void Settings::LoadIngame()
×
376
{
377
    libsiedler2::Archiv settingsIngame;
×
378
    const auto settingsPathIngame = RTTRCONFIG.ExpandPath(s25::resources::ingameOptions);
×
379
    try
380
    {
381
        if(libsiedler2::Load(settingsPathIngame, settingsIngame) != 0)
×
382
            throw std::runtime_error("File missing");
×
383

384
        const libsiedler2::ArchivItem_Ini* iniIngame =
385
          static_cast<libsiedler2::ArchivItem_Ini*>(settingsIngame.find("ingame"));
×
386
        if(!iniIngame)
×
387
            throw std::runtime_error("Missing section");
×
388
        // ingame
389
        // {
NEW
390
        ingame.scaleStatistics = iniIngame->getBoolValue("scale_statistics");
×
391
        ingame.showBQ = iniIngame->getBoolValue("show_building_quality");
×
392
        ingame.showNames = iniIngame->getBoolValue("show_names");
×
393
        ingame.showProductivity = iniIngame->getBoolValue("show_productivity");
×
394
        ingame.minimapExtended = iniIngame->getBoolValue("minimap_extended");
×
395
        // }
396
        // ingame windows
397
        for(const auto& window : persistentWindows)
×
398
        {
399
            const auto* iniWindow = static_cast<const libsiedler2::ArchivItem_Ini*>(settingsIngame.find(window.second));
×
400
            if(!iniWindow)
×
401
                continue;
×
402
            auto& settings = windows.persistentSettings[window.first];
×
403
            const auto lastPos = settings.lastPos =
×
404
              DrawPoint(iniWindow->getIntValue("pos_x"), iniWindow->getIntValue("pos_y"));
×
405
            settings.restorePos = DrawPoint(iniWindow->getValue("restore_pos_x", lastPos.x),
×
406
                                            iniWindow->getValue("restore_pos_y", lastPos.y));
×
407
            settings.isOpen = iniWindow->getIntValue("is_open");
×
408
            settings.isPinned = iniWindow->getValue("is_pinned", false);
×
409
            settings.isMinimized = iniWindow->getValue("is_minimized", false);
×
410
        }
411
    } catch(const std::runtime_error& e)
×
412
    {
413
        s25util::warning(std::string("Could not use ingame settings from \"") + settingsPathIngame.string()
×
414
                         + "\", using default values. Reason: " + e.what());
×
415
        LoadIngameDefaults();
×
416
        SaveIngame();
×
417
    }
418
}
×
419

420
///////////////////////////////////////////////////////////////////////////////
421
// Routine for saving the configuration
422
void Settings::Save()
×
423
{
424
    libsiedler2::Archiv settings;
×
425
    settings.alloc(SECTION_NAMES.size());
×
426
    for(unsigned i = 0; i < SECTION_NAMES.size(); ++i)
×
427
        settings.set(i, std::make_unique<libsiedler2::ArchivItem_Ini>(SECTION_NAMES[i]));
×
428

429
    libsiedler2::ArchivItem_Ini* iniGlobal = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("global"));
×
430
    libsiedler2::ArchivItem_Ini* iniVideo = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("video"));
×
431
    libsiedler2::ArchivItem_Ini* iniLanguage = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("language"));
×
432
    libsiedler2::ArchivItem_Ini* iniDriver = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("driver"));
×
433
    libsiedler2::ArchivItem_Ini* iniSound = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("sound"));
×
434
    libsiedler2::ArchivItem_Ini* iniLobby = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("lobby"));
×
435
    libsiedler2::ArchivItem_Ini* iniServer = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("server"));
×
436
    libsiedler2::ArchivItem_Ini* iniProxy = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("proxy"));
×
437
    libsiedler2::ArchivItem_Ini* iniInterface = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("interface"));
×
438
    libsiedler2::ArchivItem_Ini* iniAddons = static_cast<libsiedler2::ArchivItem_Ini*>(settings.find("addons"));
×
439

440
    // Is one of the categories missing?
441
    RTTR_Assert(iniGlobal && iniVideo && iniLanguage && iniDriver && iniSound && iniLobby && iniServer && iniProxy
×
442
                && iniInterface && iniAddons);
443

444
    // global
445
    // {
446
    iniGlobal->setValue("version", VERSION);
×
447
    iniGlobal->setValue("gameversion", rttr::version::GetRevision());
×
NEW
448
    iniGlobal->setValue("submit_debug_data", rttr::enum_cast(global.submitDebugData));
×
NEW
449
    iniGlobal->setValue("use_upnp", global.useUPNP);
×
450
    iniGlobal->setValue("smartCursor", global.smartCursor);
×
451
    iniGlobal->setValue("debugMode", global.debugMode);
×
452
    iniGlobal->setValue("showGFInfo", global.showGFInfo);
×
453
    // };
454

455
    // video
456
    // {
457
    iniVideo->setValue("fullscreen_width", video.fullscreenSize.width);
×
458
    iniVideo->setValue("fullscreen_height", video.fullscreenSize.height);
×
459
    iniVideo->setValue("windowed_width", video.windowedSize.width);
×
460
    iniVideo->setValue("windowed_height", video.windowedSize.height);
×
NEW
461
    iniVideo->setValue("displayMode", rttr::enum_cast(video.displayMode.type));
×
NEW
462
    iniVideo->setValue("lock_window_size", !video.displayMode.resizeable);
×
463
    iniVideo->setValue("framerate", video.framerate);
×
464
    iniVideo->setValue("vbo", video.vbo);
×
NEW
465
    iniVideo->setValue("shared_textures", video.sharedTextures);
×
466
    iniVideo->setValue("gui_scale", video.guiScale);
×
467
    // };
468

469
    // language
470
    // {
471
    iniLanguage->setValue("language", language.language);
×
472
    // }
473

474
    // driver
475
    // {
476
    iniDriver->setValue("video", driver.video);
×
477
    iniDriver->setValue("audio", driver.audio);
×
478
    // }
479

480
    // sound
481
    // {
482
    iniSound->setValue("musik", sound.musicEnabled);
×
483
    iniSound->setValue("musik_volume", sound.musicVolume);
×
484
    iniSound->setValue("effekte", sound.effectsEnabled);
×
485
    iniSound->setValue("effekte_volume", sound.effectsVolume);
×
486
    iniSound->setValue("bird_sounds", sound.birdsEnabled);
×
487
    iniSound->setValue("playlist", sound.playlist);
×
488
    // }
489

490
    // lobby
491
    // {
492
    iniLobby->setValue("name", lobby.name);
×
493
    iniLobby->setValue("portrait_index", lobby.portraitIndex);
×
494
    iniLobby->setValue("password", lobby.password);
×
NEW
495
    iniLobby->setValue("save_password", lobby.savePassword);
×
496
    // }
497

498
    // server
499
    // {
NEW
500
    iniServer->setValue("last_ip", server.lastIP);
×
501
    iniServer->setValue("local_port", server.localPort);
×
502
    iniServer->setValue("ipv6", server.ipv6);
×
503
    // }
504

505
    // proxy
506
    // {
507
    iniProxy->setValue("proxy", proxy.hostname);
×
508
    iniProxy->setValue("port", proxy.port);
×
509
    iniProxy->setValue("typ", static_cast<int>(proxy.type));
×
510
    // }
511

512
    // interface
513
    // {
NEW
514
    iniInterface->setValue("autosave_interval", interface.autosaveInterval);
×
515
    iniInterface->setValue("map_scroll_mode", static_cast<int>(interface.mapScrollMode));
×
516
    iniInterface->setValue("enable_window_pinning", interface.enableWindowPinning);
×
517
    iniInterface->setValue("window_snap_distance", interface.windowSnapDistance);
×
518
    // }
519

520
    // addons
521
    // {
522
    iniAddons->clear();
×
523
    for(const auto& it : addons.configuration)
×
524
        iniAddons->setValue(s25util::toStringClassic(it.first), s25util::toStringClassic(it.second));
×
525
    // }
526

527
    bfs::path settingsPath = RTTRCONFIG.ExpandPath(s25::resources::config);
×
528
    if(libsiedler2::Write(settingsPath, settings) == 0)
×
529
        bfs::permissions(settingsPath, bfs::owner_read | bfs::owner_write);
×
530

531
    SaveIngame();
×
532
}
×
533

534
void Settings::SaveIngame()
×
535
{
536
    libsiedler2::Archiv settingsIngame;
×
537
    settingsIngame.alloc(1 + persistentWindows.size());
×
538
    settingsIngame.set(0, std::make_unique<libsiedler2::ArchivItem_Ini>("ingame"));
×
539
    unsigned i = 1;
×
540
    for(const auto& window : persistentWindows)
×
541
    {
542
        settingsIngame.set(i, std::make_unique<libsiedler2::ArchivItem_Ini>(window.second));
×
543
        i++;
×
544
    }
545

546
    auto* iniIngame = static_cast<libsiedler2::ArchivItem_Ini*>(settingsIngame.find("ingame"));
×
547

548
    RTTR_Assert(iniIngame);
×
549

550
    // ingame
551
    // {
NEW
552
    iniIngame->setValue("scale_statistics", ingame.scaleStatistics);
×
553
    iniIngame->setValue("show_building_quality", ingame.showBQ);
×
554
    iniIngame->setValue("show_names", ingame.showNames);
×
555
    iniIngame->setValue("show_productivity", ingame.showProductivity);
×
556
    iniIngame->setValue("minimap_extended", ingame.minimapExtended);
×
557
    // }
558

559
    // ingame windows
560
    for(const auto& window : persistentWindows)
×
561
    {
562
        auto* iniWindow = static_cast<libsiedler2::ArchivItem_Ini*>(settingsIngame.find(window.second));
×
563
        if(!iniWindow)
×
564
            continue;
×
565
        const auto& settings = windows.persistentSettings[window.first];
×
566
        iniWindow->setValue("pos_x", settings.lastPos.x);
×
567
        iniWindow->setValue("pos_y", settings.lastPos.y);
×
568
        if(settings.restorePos != settings.lastPos)
×
569
        {
570
            // only save if different; defaults to lastPos on load
571
            iniWindow->setValue("restore_pos_x", settings.restorePos.x);
×
572
            iniWindow->setValue("restore_pos_y", settings.restorePos.y);
×
573
        }
574
        iniWindow->setValue("is_open", settings.isOpen);
×
575
        iniWindow->setValue("is_pinned", settings.isPinned);
×
576
        iniWindow->setValue("is_minimized", settings.isMinimized);
×
577
    }
578

579
    bfs::path settingsPathIngame = RTTRCONFIG.ExpandPath(s25::resources::ingameOptions);
×
580
    if(libsiedler2::Write(settingsPathIngame, settingsIngame) == 0)
×
581
        bfs::permissions(settingsPathIngame, bfs::owner_read | bfs::owner_write);
×
582
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc