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