/
gamesessionwriter.cpp
120 lines (102 loc) · 3.92 KB
/
gamesessionwriter.cpp
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
/** @file gamesessionwriter.cpp Serializing game state to a saved session.
*
* @authors Copyright © 2003-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
* @authors Copyright © 2005-2013 Daniel Swanson <danij@dengine.net>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program 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; either version 2 of the License, or (at your
* option) any later version. This program 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. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA</small>
*/
#include "common.h"
#include "gamesessionwriter.h"
#include "g_common.h"
#include "mapstatewriter.h"
#include "p_saveio.h"
#include <de/App>
#include <de/game/SavedSessionRepository>
#include <de/Time>
#include <de/Writer>
#include <de/ZipArchive>
using namespace de;
DENG2_PIMPL_NOREF(GameSessionWriter)
{
String savePath;
inline de::game::SavedSessionRepository &saveRepo() {
return G_SavedSessionRepository();
}
String composeInfo(SessionMetadata const &metadata)
{
String info;
QTextStream os(&info);
os.setCodec("UTF-8");
// Write header and misc info.
Time now;
os << "# Doomsday Engine saved game session package.\n#"
<< "\n# Generator: GameSessionWriter (libcommon)"
<< "\n# Generation Date: " + now.asDateTime().toString(Qt::SystemLocaleShortDate);
// Write metadata.
os << "\n\n" + metadata.asTextWithInfoSyntax() + "\n";
return info;
}
void writeACScriptState(ZipArchive &arch)
{
#if __JHEXEN__
Block data;
de::Writer writer(data);
Game_ACScriptInterpreter().writeWorldState(writer);
de::Writer(arch.entryBlock("ACScriptState")).withHeader() << data;
#endif
}
void writeMapState(ZipArchive &arch, String const &mapUriStr)
{
Block data;
SV_OpenFileForWrite(data);
writer_s *writer = SV_NewWriter();
MapStateWriter().write(writer);
arch.add(String("maps") / mapUriStr + "State", data);
Writer_Delete(writer);
SV_CloseFile();
}
};
GameSessionWriter::GameSessionWriter(String const &savePath) : d(new Instance())
{
DENG2_ASSERT(!savePath.isEmpty());
d->savePath = savePath;
}
void GameSessionWriter::write(SessionMetadata const &metadata)
{
LOG_AS("GameSessionWriter");
LOG_RES_VERBOSE("Serializing to \"%s\"...") << d->savePath;
// Serialize the game state to a new .save package.
ZipArchive arch;
arch.add("Info", d->composeInfo(metadata).toUtf8());
d->writeACScriptState(arch);
d->writeMapState(arch, Str_Text(Uri_Compose(gameMapUri))); // current map.
d->saveRepo().remove(d->savePath);
// Write the new package to /home/savegames/<game-id>/<session-name>.save
Folder &folder = DENG2_APP->rootFolder().locate<Folder>(d->savePath.fileNamePath());
if(SavedSession *existing = folder.tryLocate<SavedSession>(d->savePath.fileName()))
{
existing->setMode(File::Write);
}
File &save = folder.replaceFile(d->savePath.fileName());
de::Writer(save) << arch;
save.setMode(File::ReadOnly);
LOG_RES_MSG("Wrote ") << save.description();
// We can now reinterpret and populate the contents of the archive.
File *updated = save.reinterpret();
updated->as<Folder>().populate();
SavedSession &session = updated->as<SavedSession>();
//session.cacheMetadata(metadata); // Avoid immediately reopening the .save package.
d->saveRepo().add(session);
}