1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27
28 // include headers that implement a archive in simple text format
29 #include <boost/archive/text_oarchive.hpp>
30 #include <boost/archive/text_iarchive.hpp>
31 #include <boost/serialization/split_free.hpp>
32 #include <boost/serialization/vector.hpp>
33 #include <boost/serialization/export.hpp>
34 #include <boost/date_time/posix_time/time_serialize.hpp>
35 #include <boost/filesystem/path.hpp>
36 #include <boost/filesystem/fstream.hpp>
37 #include <boost/iostreams/filtering_stream.hpp>
38 #include <boost/iostreams/filter/zlib.hpp>
39 #include <fstream>
40 #include <sstream>
41 #include <iostream>
42 #include <exception>
43 #include <stdexcept>
44 #include <string>
45
46 #include "libreallive/archive.h"
47 #include "libreallive/intmemref.h"
48 #include "machine/memory.h"
49 #include "machine/rlmachine.h"
50 #include "machine/save_game_header.h"
51 #include "machine/serialization.h"
52 #include "machine/stack_frame.h"
53 #include "systems/base/anm_graphics_object_data.h"
54 #include "systems/base/event_system.h"
55 #include "systems/base/gan_graphics_object_data.h"
56 #include "systems/base/graphics_object.h"
57 #include "systems/base/graphics_object_of_file.h"
58 #include "systems/base/graphics_stack_frame.h"
59 #include "systems/base/graphics_system.h"
60 #include "systems/base/sound_system.h"
61 #include "systems/base/system.h"
62 #include "systems/base/text_system.h"
63 #include "utilities/exception.h"
64 #include "utilities/gettext.h"
65
66 namespace fs = boost::filesystem;
67
68 namespace Serialization {
69
70 RLMachine* g_current_machine = NULL;
71
72 const int CURRENT_LOCAL_VERSION = 2;
73
74 } // namespace Serialization
75
76 namespace {
77
78 template <typename TYPE>
checkInFileOpened(TYPE & file,const fs::path & home)79 void checkInFileOpened(TYPE& file, const fs::path& home) {
80 if (!file) {
81 throw rlvm::Exception(
82 str(format(_("Could not open save game file %1%")) % home));
83 }
84 }
85
86 } // namespace
87
88 namespace Serialization {
89
saveGameForSlot(RLMachine & machine,int slot)90 void saveGameForSlot(RLMachine& machine, int slot) {
91 fs::path path = buildSaveGameFilename(machine, slot);
92 fs::ofstream file(path, std::ios::binary);
93 checkInFileOpened(file, path);
94
95 saveGameTo(file, machine);
96 }
97
saveGameTo(std::ostream & oss,RLMachine & machine)98 void saveGameTo(std::ostream& oss, RLMachine& machine) {
99 boost::iostreams::filtering_stream<boost::iostreams::output> filtered_output;
100 filtered_output.push(boost::iostreams::zlib_compressor());
101 filtered_output.push(oss);
102
103 const SaveGameHeader header(machine.system().graphics().window_subtitle());
104
105 g_current_machine = &machine;
106
107 try {
108 boost::archive::text_oarchive oa(filtered_output);
109 oa << CURRENT_LOCAL_VERSION << header
110 << const_cast<const LocalMemory&>(machine.memory().local())
111 << const_cast<const RLMachine&>(machine)
112 << const_cast<const System&>(machine.system())
113 << const_cast<const GraphicsSystem&>(machine.system().graphics())
114 << const_cast<const TextSystem&>(machine.system().text())
115 << const_cast<const SoundSystem&>(machine.system().sound());
116 }
117 catch (std::exception& e) {
118 std::cerr << "--- WARNING: ERROR DURING SAVING FILE: " << e.what() << " ---"
119 << std::endl;
120
121 g_current_machine = NULL;
122 throw e;
123 }
124
125 g_current_machine = NULL;
126 }
127
buildSaveGameFilename(RLMachine & machine,int slot)128 fs::path buildSaveGameFilename(RLMachine& machine, int slot) {
129 std::ostringstream oss;
130 oss << "save" << std::setw(3) << std::setfill('0') << slot << ".sav.gz";
131
132 return machine.system().GameSaveDirectory() / oss.str();
133 }
134
loadHeaderForSlot(RLMachine & machine,int slot)135 SaveGameHeader loadHeaderForSlot(RLMachine& machine, int slot) {
136 fs::path path = buildSaveGameFilename(machine, slot);
137 fs::ifstream file(path, std::ios::binary);
138 checkInFileOpened(file, path);
139
140 return loadHeaderFrom(file);
141 }
142
loadHeaderFrom(std::istream & iss)143 SaveGameHeader loadHeaderFrom(std::istream& iss) {
144 boost::iostreams::filtering_stream<boost::iostreams::input> filtered_input;
145 filtered_input.push(boost::iostreams::zlib_decompressor());
146 filtered_input.push(iss);
147
148 int version;
149 SaveGameHeader header;
150
151 // Only load the header
152 boost::archive::text_iarchive ia(filtered_input);
153 ia >> version >> header;
154
155 return header;
156 }
157
loadLocalMemoryForSlot(RLMachine & machine,int slot,Memory & memory)158 void loadLocalMemoryForSlot(RLMachine& machine, int slot, Memory& memory) {
159 fs::path path = buildSaveGameFilename(machine, slot);
160 fs::ifstream file(path, std::ios::binary);
161 checkInFileOpened(file, path);
162
163 loadLocalMemoryFrom(file, memory);
164 }
165
loadLocalMemoryFrom(std::istream & iss,Memory & memory)166 void loadLocalMemoryFrom(std::istream& iss, Memory& memory) {
167 boost::iostreams::filtering_stream<boost::iostreams::input> filtered_input;
168 filtered_input.push(boost::iostreams::zlib_decompressor());
169 filtered_input.push(iss);
170
171 int version;
172 SaveGameHeader header;
173
174 // Only load the header
175 boost::archive::text_iarchive ia(filtered_input);
176 ia >> version >> header >> memory.local();
177 }
178
loadGameForSlot(RLMachine & machine,int slot)179 void loadGameForSlot(RLMachine& machine, int slot) {
180 fs::path path = buildSaveGameFilename(machine, slot);
181 fs::ifstream file(path, std::ios::binary);
182 checkInFileOpened(file, path);
183
184 loadGameFrom(file, machine);
185 }
186
loadGameFrom(std::istream & iss,RLMachine & machine)187 void loadGameFrom(std::istream& iss, RLMachine& machine) {
188 boost::iostreams::filtering_stream<boost::iostreams::input> filtered_input;
189 filtered_input.push(boost::iostreams::zlib_decompressor());
190 filtered_input.push(iss);
191
192 int version;
193 SaveGameHeader header;
194
195 g_current_machine = &machine;
196
197 try {
198 // Must clear the stack before reseting the System because LongOperations
199 // often hold references to objects in the System heiarchy.
200 machine.Reset();
201
202 boost::archive::text_iarchive ia(filtered_input);
203 ia >> version >> header >> machine.memory().local() >> machine >>
204 machine.system() >> machine.system().graphics() >>
205 machine.system().text() >> machine.system().sound();
206
207 machine.system().graphics().ReplayGraphicsStack(machine);
208
209 machine.system().graphics().ForceRefresh();
210 }
211 catch (std::exception& e) {
212 std::cerr << "--- WARNING: ERROR DURING LOADING FILE: " << e.what()
213 << " ---" << std::endl;
214
215 g_current_machine = NULL;
216 throw e;
217 }
218
219 g_current_machine = NULL;
220 }
221
222 } // namespace Serialization
223