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

Return-To-The-Roots / s25client / 19550882615

20 Nov 2025 08:47PM UTC coverage: 50.483% (+0.01%) from 50.469%
19550882615

push

github

web-flow
Merge pull request #1829 from Flamefire/speed

Make all game speeds available to "v" key

13 of 35 new or added lines in 7 files covered. (37.14%)

4 existing lines in 1 file now uncovered.

22515 of 44599 relevant lines covered (50.48%)

35056.13 hits per line

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

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

5
#include "iwSave.h"
6
#include "ListDir.h"
7
#include "Loader.h"
8
#include "RttrConfig.h"
9
#include "RttrLobbyClient.hpp"
10
#include "Savegame.h"
11
#include "Settings.h"
12
#include "WindowManager.h"
13
#include "controls/ctrlComboBox.h"
14
#include "controls/ctrlEdit.h"
15
#include "controls/ctrlTable.h"
16
#include "controls/ctrlText.h"
17
#include "desktops/dskLobby.h"
18
#include "files.h"
19
#include "helpers/make_array.h"
20
#include "helpers/toString.h"
21
#include "iwConnecting.h"
22
#include "network/GameClient.h"
23
#include "gameData/GameConsts.h"
24
#include "gameData/const_gui_ids.h"
25
#include "liblobby/LobbyClient.h"
26
#include "s25util/Log.h"
27
#include <boost/range/adaptors.hpp>
28
#include <utility>
29

30
namespace {
31
enum
32
{
33
    ID_tblSaveGames,
34
    ID_edtFilename,
35
    ID_btSaveOrLoad,
36
    ID_txtAutoSave,
37
    ID_cbAutoSaveInterval,
38
    ID_txtSaveFolder,
39
};
40

41
using namespace std::chrono_literals;
42
constexpr std::array AUTO_SAVE_INTERVALS{1min, 5min, 10min, 15min, 30min, 60min, 90min};
43
} // namespace
44

45
iwSaveLoad::iwSaveLoad(const std::string& window_title, ITexture* btImg, const unsigned addHeight)
×
46
    : IngameWindow(CGI_SAVE, IngameWindow::posLastOrCenter, Extent(600, 400 + addHeight), window_title,
×
47
                   LOADER.GetImageN("resource", 41))
×
48
{
49
    using SRT = ctrlTable::SortType;
50
    AddTable(ID_tblSaveGames, DrawPoint(20, 30), Extent(560, 300), TextureColor::Green2, NormalFont,
×
51
             ctrlTable::Columns{{_("Filename"), 270, SRT::String},
×
52
                                {_("Map"), 250, SRT::String},
53
                                {_("Time"), 250, SRT::Date},
54
                                {_("Game Time"), 320, SRT::Time},
55
                                {}});
×
56

57
    AddText(ID_txtSaveFolder, DrawPoint(20, 333), RTTRCONFIG.ExpandPath(s25::folders::save).string(), COLOR_YELLOW,
×
58
            FontStyle::TOP, SmallFont)
×
59
      ->setMaxWidth(510);
×
60
    AddEdit(ID_edtFilename, DrawPoint(20, 350), Extent(510, 22), TextureColor::Green2, NormalFont);
×
61
    AddImageButton(ID_btSaveOrLoad, DrawPoint(540, 341), Extent(40, 40), TextureColor::Green2, btImg);
×
62
    // Initially fill the table
63
    RefreshTable();
×
64
}
×
65

66
void iwSaveLoad::Msg_EditEnter(const unsigned /*ctrl_id*/)
×
67
{
68
    SaveLoad();
×
69
}
×
70

71
void iwSaveLoad::Msg_ButtonClick(const unsigned ctrl_id)
×
72
{
73
    RTTR_Assert(ctrl_id == ID_btSaveOrLoad);
×
74
    SaveLoad();
×
75
}
×
76

77
void iwSaveLoad::Msg_TableSelectItem(const unsigned /*ctrl_id*/, const boost::optional<unsigned>& selection)
×
78
{
79
    // On selecting a table entry put the filename into the edit control
80
    GetCtrl<ctrlEdit>(ID_edtFilename)
81
      ->SetText(selection ? GetCtrl<ctrlTable>(ID_tblSaveGames)->GetItemText(*selection, 0) : "");
×
82
}
×
83

84
void iwSaveLoad::RefreshTable()
×
85
{
86
    static bool loadedOnce = false;
87

88
    auto* table = GetCtrl<ctrlTable>(ID_tblSaveGames);
×
89

90
    table->DeleteAllItems();
×
91

92
    std::vector<boost::filesystem::path> saveFiles = ListDir(RTTRCONFIG.ExpandPath(s25::folders::save), "sav");
×
93
    for(const auto& saveFile : saveFiles)
×
94
    {
95
        Savegame save;
×
96
        if(!save.Load(saveFile, SaveGameDataToLoad::Header))
×
97
        {
98
            // Show errors only first time this is loaded
99
            if(!loadedOnce)
×
100
            {
101
                LOG.write(_("Invalid Savegame %1%! Reason: %2%\n")) % saveFile
×
102
                  % (save.GetLastErrorMsg().empty() ? _("Unknown") : save.GetLastErrorMsg());
×
103
            }
104
            continue;
×
105
        }
106

107
        const auto fileName = saveFile.stem().string();
×
108
        const std::string dateStr = s25util::Time::FormatTime("%d.%m.%Y - %H:%i", save.GetSaveTime());
×
109
        const std::string gameTime = GAMECLIENT.FormatGFTime(save.start_gf);
×
110

111
        table->AddRow({fileName, save.GetMapName(), dateStr, gameTime, saveFile.string()});
×
112
    }
113

114
    // Sort by time
115
    table->SortRows(2, TableSortDir::Descending);
×
116
    loadedOnce = true;
×
117
}
×
118

119
void iwSave::SaveLoad()
×
120
{
121
    const boost::filesystem::path savePath =
122
      RTTRCONFIG.ExpandPath(s25::folders::save) / (GetCtrl<ctrlEdit>(ID_edtFilename)->GetText() + ".sav");
×
123
    GAMECLIENT.SaveToFile(savePath);
×
124

125
    RefreshTable();
×
126
    GetCtrl<ctrlEdit>(ID_edtFilename)->SetText("");
×
127
}
×
128

129
iwSave::iwSave() : iwSaveLoad(_("Save game!"), LOADER.GetTextureN("io", 47), 30)
×
130
{
131
    const auto* fileNameEdit = GetCtrl<ctrlEdit>(ID_edtFilename);
×
132
    DrawPoint pos(GetSize().x / 2, fileNameEdit->GetPos().y + fileNameEdit->GetSize().y + 10);
×
133

134
    ctrlComboBox* combo =
135
      AddComboBox(ID_cbAutoSaveInterval, pos, Extent(130, 22), TextureColor::Green2, NormalFont, 100);
×
136
    pos += DrawPoint(-5, 11);
×
137
    AddText(ID_txtAutoSave, pos, _("Auto-Save every:"), 0xFFFFFF00, FontStyle::RIGHT | FontStyle::VCENTER, NormalFont);
×
138

139
    // Add intervals
140
    combo->AddString(_("Disabled"));
×
141
    for(const std::chrono::minutes interval : AUTO_SAVE_INTERVALS)
×
142
        combo->AddString((boost::format(_("%1% min")) % interval.count()).str());
×
143
    // Last entry is only for debugging
144
    if(SETTINGS.global.debugMode)
×
145
        combo->AddString(_("Every GF"));
×
146

147
    // Select interval
148
    combo->SetSelection(0); // Use disabled by default and change if possible
×
149
    if(SETTINGS.interface.autosave_interval == 1)
×
150
        combo->SetSelection(AUTO_SAVE_INTERVALS.size() + 1);
×
151
    else
152
    {
153
        // Start selection index at 1, 0 is "disabled"
154
        for(const auto& i : AUTO_SAVE_INTERVALS | boost::adaptors::indexed(1))
×
155
        {
NEW
156
            if(SETTINGS.interface.autosave_interval == duration_to_gfs(i.value()))
×
157
            {
158
                combo->SetSelection(static_cast<unsigned>(i.index()));
×
159
                break;
×
160
            }
161
        }
162
    }
163
}
×
164

165
void iwSave::Msg_ComboSelectItem(const unsigned /*ctrl_id*/, const unsigned selection)
×
166
{
167
    if(selection == 0) // First entry is "disabled"
×
168
        SETTINGS.interface.autosave_interval = 0;
×
169
    else if(selection > AUTO_SAVE_INTERVALS.size()) // Last entry is "every GF" (in debug mode)
×
170
        SETTINGS.interface.autosave_interval = 1;
×
171
    else
172
    {
173
        // selection is the index into the array ignoring the first ("disabled") entry
174
        RTTR_Assert(selection >= 1 && selection <= AUTO_SAVE_INTERVALS.size());
×
NEW
175
        SETTINGS.interface.autosave_interval = duration_to_gfs(AUTO_SAVE_INTERVALS[selection - 1]);
×
176
    }
177
}
×
178

179
iwLoad::iwLoad(CreateServerInfo csi) : iwSaveLoad(_("Load game!"), LOADER.GetTextureN("io", 48)), csi(std::move(csi)) {}
×
180

181
void iwLoad::SaveLoad()
×
182
{
183
    const auto* table = GetCtrl<ctrlTable>(ID_tblSaveGames);
×
184
    if(!table->GetSelection())
×
185
        return;
×
186

187
    if(GAMECLIENT.HostGame(csi, {table->GetItemText(*table->GetSelection(), 4), MapType::Savegame}))
×
188
    {
189
        std::unique_ptr<ILobbyClient> lobbyClient;
×
190
        if(csi.type == ServerType::Lobby)
×
191
            lobbyClient = std::make_unique<RttrLobbyClient>(LOBBYCLIENT);
×
192
        WINDOWMANAGER.Show(std::make_unique<iwConnecting>(csi.type, std::move(lobbyClient)));
×
193
    } else
194
    {
195
        if(LOBBYCLIENT.IsLoggedIn())
×
196
            WINDOWMANAGER.Switch(std::make_unique<dskLobby>());
×
197
        else
198
            Close();
×
199
    }
200
}
201

202
/// Handle double click on the table
203
void iwLoad::Msg_TableChooseItem(const unsigned /*ctrl_id*/, const unsigned /*selection*/)
×
204
{
205
    SaveLoad();
×
206
}
×
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