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

Return-To-The-Roots / s25client / 21632345767

03 Feb 2026 01:35PM UTC coverage: 50.746% (-0.008%) from 50.754%
21632345767

Pull #1885

github

web-flow
Merge fb01a4f33 into dc0ce04f6
Pull Request #1885: Allow changing color of unused player slots

1 of 8 new or added lines in 2 files covered. (12.5%)

5 existing lines in 2 files now uncovered.

22797 of 44924 relevant lines covered (50.75%)

42380.89 hits per line

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

80.34
/libs/s25main/SavedFile.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 "SavedFile.h"
6
#include "BasePlayerInfo.h"
7
#include "RTTR_Version.h"
8
#include "enum_cast.hpp"
9
#include "helpers/format.hpp"
10
#include "gameData/MaxPlayers.h"
11
#include "libendian/ConvertEndianess.h"
12
#include "s25util/BinaryFile.h"
13
#include "s25util/Serializer.h"
14
#include <boost/format.hpp>
15
#include <algorithm>
16
#include <mygettext/mygettext.h>
17
#include <stdexcept>
18

19
SavedFile::SavedFile() : saveTime_(0)
18✔
20
{
21
    const std::string rev = rttr::version::GetRevision();
18✔
22
    std::copy(rev.begin(), rev.begin() + revision.size(), revision.begin());
18✔
23
}
18✔
24

25
SavedFile::~SavedFile() = default;
18✔
26

27
uint8_t SavedFile::GetMinorVersion() const
27✔
28
{
29
    return minorVersion_.value();
27✔
30
}
31

32
uint8_t SavedFile::GetMajorVersion() const
×
33
{
34
    return majorVersion_.value();
×
35
}
36

37
void SavedFile::WriteFileHeader(BinaryFile& file) const
7✔
38
{
39
    // Signature
40
    const std::string signature = GetSignature();
14✔
41
    file.WriteRawData(signature.c_str(), signature.length());
7✔
42
    // Format version
43
    file.WriteUnsignedChar(GetLatestMajorVersion());
7✔
44
    file.WriteUnsignedChar(GetLatestMinorVersion());
7✔
45
}
7✔
46

47
void SavedFile::WriteExtHeader(BinaryFile& file, const std::string& mapName)
6✔
48
{
49
    // Store data in struct
50
    saveTime_ = s25util::Time::CurrentTime();
6✔
51
    mapName_ = mapName;
6✔
52

53
    // Program version
54
    file.WriteRawData(revision.data(), revision.size());
6✔
55
    s25util::time64_t tmpTime = libendian::ConvertEndianess<false>::fromNative(saveTime_);
6✔
56
    file.WriteRawData(&tmpTime, sizeof(tmpTime));
6✔
57
    file.WriteShortString(mapName);
6✔
58
    file.WriteUnsignedInt(playerNames_.size());
6✔
59
    for(const std::string& name : playerNames_)
24✔
60
        file.WriteShortString(name);
18✔
61
}
6✔
62

63
bool SavedFile::ReadFileHeader(BinaryFile& file)
12✔
64
{
65
    lastErrorMsg.clear();
12✔
66

67
    const std::string signature = GetSignature();
24✔
68
    std::array<char, 32> read_signature;
69
    if(signature.size() > read_signature.size())
12✔
70
        throw std::range_error("Program signature is to long!");
×
71
    try
72
    {
73
        file.ReadRawData(read_signature.data(), signature.size());
12✔
74

75
        // Signatur überprüfen
76
        if(!std::equal(signature.begin(), signature.end(), read_signature.begin()))
12✔
77
        {
78
            lastErrorMsg = _("File is not in a valid format!");
×
79
            return false;
×
80
        }
81

82
        // Version überprüfen
83
        majorVersion_ = file.ReadUnsignedChar();
12✔
84
        minorVersion_ = file.ReadUnsignedChar();
12✔
85
        if(majorVersion_ != GetLatestMajorVersion() || minorVersion_ > GetLatestMinorVersion())
12✔
86
        {
87
            boost::format fmt =
88
              boost::format((majorVersion_ < GetLatestMajorVersion()) ?
×
89
                              _("File has an old version and cannot be used (version: %1%.%2%, expected: %3%.%4%)!") :
×
90
                              _("File was created with more recent program and cannot be used (version: %1%.%2%, "
×
91
                                "expected: %3%.%4%)!"));
×
92
            lastErrorMsg =
93
              (fmt % majorVersion_.value() % minorVersion_.value() % GetLatestMajorVersion() % GetLatestMinorVersion())
×
94
                .str();
×
95
            return false;
×
96
        }
97
    } catch(const std::runtime_error& e)
×
98
    {
99
        lastErrorMsg = e.what();
×
100
        return false;
×
101
    }
102

103
    return true;
12✔
104
}
105

106
bool SavedFile::ReadExtHeader(BinaryFile& file)
10✔
107
{
108
    file.ReadRawData(revision.data(), revision.size());
10✔
109
    file.ReadRawData(&saveTime_, sizeof(saveTime_));
10✔
110
    saveTime_ = libendian::ConvertEndianess<false>::toNative(saveTime_);
10✔
111
    mapName_ = file.ReadShortString();
10✔
112
    playerNames_.resize(file.ReadUnsignedInt());
10✔
113
    for(std::string& name : playerNames_)
40✔
114
        name = file.ReadShortString();
30✔
115
    return true;
10✔
116
}
117

118
void SavedFile::WriteAllHeaderData(BinaryFile& file, const std::string& mapName)
6✔
119
{
120
    // Versionszeug schreiben
121
    WriteFileHeader(file);
6✔
122
    WriteExtHeader(file, mapName);
6✔
123
}
6✔
124

125
bool SavedFile::ReadAllHeaderData(BinaryFile& file)
10✔
126
{
127
    if(!ReadFileHeader(file))
10✔
128
        return false;
×
129
    if(!ReadExtHeader(file))
10✔
130
        return false;
×
131
    return true;
10✔
132
}
133

134
void SavedFile::WritePlayerData(BinaryFile& file)
6✔
135
{
136
    Serializer ser;
12✔
137
    ser.PushUnsignedChar(players.size());
6✔
138
    for(const auto& player : players)
29✔
139
        player.Serialize(ser, true);
23✔
140

141
    ser.WriteToFile(file);
6✔
142
}
6✔
143

144
void SavedFile::ReadPlayerData(BinaryFile& file)
7✔
145
{
146
    ClearPlayers();
7✔
147
    Serializer ser;
14✔
148
    ser.ReadFromFile(file);
7✔
149
    const unsigned playerCt = ser.PopUnsignedChar();
7✔
150
    if(playerCt > MAX_PLAYERS)
7✔
151
    {
NEW
152
        throw std::length_error(
×
NEW
153
          helpers::format(_("Number of players (%1%) exceeds maximum allowed of %2%!"), playerCt, MAX_PLAYERS));
×
154
    }
155
    players.reserve(playerCt);
7✔
156
    for(unsigned i = 0; i < playerCt; i++)
34✔
157
    {
158
        // TODO(Replay) TODO(Savegame) minor versions will change their meaning when major is bumped
159
        BasePlayerInfo player(ser, GetMinorVersion() >= 1 ? 1 : 0, true);
54✔
160
        // Temporary workaround: The random team was stored in the file but should not anymore, see PR #1331
161
        if(player.team > Team::Team4)
27✔
162
            player.team = Team(rttr::enum_cast(player.team) - 3); // Was random team 2-4
×
163
        else if(player.team == Team::Random)
27✔
164
            player.team = Team::Team1; // Was random team 1
×
165
        AddPlayer(player);
27✔
166
    }
167
}
7✔
168

169
/**
170
 *  schreibt die GlobalGameSettings in die Datei.
171
 */
172
void SavedFile::WriteGGS(BinaryFile& file) const
6✔
173
{
174
    Serializer ser;
12✔
175
    ggs.Serialize(ser);
6✔
176
    ser.WriteToFile(file);
6✔
177
}
6✔
178

179
/**
180
 *  liest die GlobalGameSettings aus der Datei.
181
 */
182
void SavedFile::ReadGGS(BinaryFile& file)
7✔
183
{
184
    Serializer ser;
14✔
185
    ser.ReadFromFile(file);
7✔
186
    ggs.Deserialize(ser);
7✔
187
}
7✔
188

189
const BasePlayerInfo& SavedFile::GetPlayer(unsigned idx) const
27✔
190
{
191
    return players[idx];
27✔
192
}
193

194
unsigned SavedFile::GetNumPlayers()
9✔
195
{
196
    return players.size();
9✔
197
}
198

199
void SavedFile::AddPlayer(const BasePlayerInfo& player)
50✔
200
{
201
    players.push_back(player);
50✔
202
    if(player.isUsed())
50✔
203
        playerNames_.push_back(player.name);
39✔
204
}
50✔
205

206
void SavedFile::ClearPlayers()
17✔
207
{
208
    players.clear();
17✔
209
    playerNames_.clear();
17✔
210
}
17✔
211

212
std::string SavedFile::GetRevision() const
×
213
{
214
    return std::string(revision.begin(), revision.end());
×
215
}
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