1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
#ifndef GAMEBRYOSAVEGAME_H
#define GAMEBRYOSAVEGAME_H
#include "isavegame.h"
#include "memoizedlock.h"
#include <QDateTime>
#include <QFile>
#include <QImage>
#include <QString>
#include <QStringList>
#include <cstdint>
#include <stddef.h>
#include <stdexcept>
#ifdef _WIN32
struct _SYSTEMTIME;
#else
// Windows SYSTEMTIME struct - used in Bethesda savegame binary format
struct _SYSTEMTIME {
uint16_t wYear;
uint16_t wMonth;
uint16_t wDayOfWeek;
uint16_t wDay;
uint16_t wHour;
uint16_t wMinute;
uint16_t wSecond;
uint16_t wMilliseconds;
};
#endif
namespace MOBase
{
class IPluginGame;
} // namespace MOBase
class GameGamebryo;
class GamebryoSaveGame : public MOBase::ISaveGame
{
public:
GamebryoSaveGame(QString const& file, GameGamebryo const* game,
bool const lightEnabled = false, bool const mediumEnabled = false);
virtual ~GamebryoSaveGame();
public: // ISaveGame interface
virtual QString getFilepath() const override;
virtual QDateTime getCreationTime() const override;
virtual QString getName() const override;
virtual QString getSaveGroupIdentifier() const override;
virtual QStringList allFiles() const override;
public:
bool hasScriptExtenderFile() const;
// Simple getters
virtual QString getPCName() const { return m_PCName; }
virtual unsigned short getPCLevel() const { return m_PCLevel; }
virtual QString getPCLocation() const { return m_PCLocation; }
virtual uint32_t getSaveNumber() const { return m_SaveNumber; }
QStringList const& getPlugins() const { return m_DataFields.value()->Plugins; }
QStringList const& getMediumPlugins() const
{
return m_DataFields.value()->MediumPlugins;
}
QStringList const& getLightPlugins() const
{
return m_DataFields.value()->LightPlugins;
}
QImage const& getScreenshot() const { return m_DataFields.value()->Screenshot; }
bool isMediumEnabled() const { return m_MediumEnabled; }
bool isLightEnabled() const { return m_LightEnabled; }
enum class StringType
{
TYPE_BZSTRING,
TYPE_BSTRING,
TYPE_WSTRING
};
enum class StringFormat
{
UTF8,
LOCAL8BIT
};
protected:
friend class FileWrapper;
class FileWrapper
{
public:
/**
* @brief Construct the save file information.
*
* @param filepath The path to the save file.
* @params expected Expecte bytes at start of file.
*
**/
FileWrapper(QString const& filepath, QString const& expected);
/** Set this for save games that have a marker at the end of each
* field. Specifically fallout
**/
void setHasFieldMarkers(bool);
/** Set bz string mode (1 byte length, null terminated)
**/
void setPluginString(StringType);
/** Set string format (utf-8, windows local 8 bit strings)
**/
void setPluginStringFormat(StringFormat);
template <typename T>
void skip(int count = 1)
{
if (!m_File.seek(m_File.pos() + count * sizeof(T))) {
throw std::runtime_error("unexpected end of file");
}
}
template <typename T>
void read(T& value)
{
int read = m_File.read(reinterpret_cast<char*>(&value), sizeof(T));
if (read != sizeof(T)) {
throw std::runtime_error("unexpected end of file");
}
if (m_HasFieldMarkers) {
skip<char>();
}
}
void seek(unsigned long pos)
{
if (!m_File.seek(pos)) {
throw std::runtime_error("unexpected end of file");
}
}
void read(void* buff, std::size_t length);
/* Reads RGB image from save
* Assumes picture dimentions come immediately before the save
*/
QImage readImage(int scale = 0, bool alpha = false);
/* Reads RGB image from save */
QImage readImage(uint32_t width, uint32_t height, int scale = 0,
bool alpha = false);
/* Sets the compression type. */
void setCompressionType(uint16_t type);
/* uncompress the begining of the compressed block */
bool openCompressedData(int bytesToIgnore = 0);
/* read the next compressed block */
bool readNextChunk();
/* frees the uncompressed block */
void closeCompressedData();
/* Read the save game version in the compressed block */
uint8_t readChar(int bytesToIgnore = 0);
uint16_t readShort(int bytesToIgnore = 0);
uint32_t readInt(int bytesToIgnore = 0);
uint64_t readLong(int bytesToIgnore = 0);
float_t readFloat(int bytesToIgnore = 0);
/* Read the plugin list */
QStringList readPlugins(int bytesToIgnore = 0, int extraData = 0,
const QStringList& corePlugins = {});
/* Read the light plugin list */
QStringList readLightPlugins(int bytesToIgnore = 0, int extraData = 0,
const QStringList& corePlugins = {});
/* Read the medium plugin list */
QStringList readMediumPlugins(int bytesToIgnore = 0, int extraData = 0,
const QStringList& corePlugins = {});
void close();
private:
QFile m_File;
uint64_t m_NextChunk;
uint64_t m_UncompressedSize;
bool m_HasFieldMarkers;
StringType m_PluginString;
StringFormat m_PluginStringFormat;
QDataStream* m_Data;
uint16_t m_CompressionType = 0;
private:
template <typename T>
void readQDataStream(QDataStream& data, T& value);
void readQDataStream(QDataStream& data, void* buff, std::size_t length);
void skipQDataStream(QDataStream& data, std::size_t length);
QStringList readPluginData(uint32_t count, int extraData,
const QStringList corePlugins);
};
void setCreationTime(_SYSTEMTIME const& time);
GameGamebryo const* m_Game;
bool m_MediumEnabled;
bool m_LightEnabled;
QString m_FileName;
QString m_PCName;
unsigned short m_PCLevel;
QString m_PCLocation;
uint32_t m_SaveNumber;
QDateTime m_CreationTime;
// Those three fields are usually much slower to fetch than
// the other, so we do not fetch them if not needed.
//
// This is virtual so child class can add fields if those are
// hard to access.
struct DataFields
{
QStringList Plugins;
QStringList LightPlugins;
QStringList MediumPlugins;
QImage Screenshot;
// We need this constructor.
DataFields() {}
virtual ~DataFields() {}
};
MOBase::MemoizedLocked<std::unique_ptr<DataFields>> m_DataFields;
// Fetch the field.
virtual std::unique_ptr<DataFields> fetchDataFields() const = 0;
};
// Explicit template specialization must be at namespace scope (GCC requirement)
template <>
void GamebryoSaveGame::FileWrapper::read<QString>(QString& value);
#endif // GAMEBRYOSAVEGAME_H
|