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 "machine/serialization.h"
29
30 // include headers that implement a archive in simple text format
31 #include <boost/archive/text_oarchive.hpp>
32 #include <boost/archive/text_iarchive.hpp>
33 #include <boost/serialization/split_free.hpp>
34 #include <boost/serialization/vector.hpp>
35 #include <boost/serialization/set.hpp>
36 #include <boost/serialization/map.hpp>
37 #include <boost/filesystem/path.hpp>
38 #include <boost/filesystem/fstream.hpp>
39 #include <boost/filesystem/operations.hpp>
40 #include <boost/iostreams/filtering_stream.hpp>
41 #include <boost/iostreams/filter/zlib.hpp>
42 #include <fstream>
43 #include <sstream>
44 #include <iostream>
45
46 #include "libreallive/intmemref.h"
47 #include "machine/memory.h"
48 #include "machine/rlmachine.h"
49 #include "systems/base/event_system.h"
50 #include "systems/base/graphics_system.h"
51 #include "systems/base/sound_system.h"
52 #include "systems/base/system.h"
53 #include "systems/base/text_system.h"
54 #include "utilities/dynamic_bitset_serialize.h"
55 #include "utilities/exception.h"
56 #include "utilities/gettext.h"
57
58 namespace fs = boost::filesystem;
59
60 namespace Serialization {
61
62 // - Was at 2 was most of rlvm's lifetime.
63 // - Was changed to 3 during the 0.7 release because boost 1.35 had a serious
64 // bug in its implementation of vectors of primitive types which made
65 // archives not-backwards (or forwards) compatible. Thankfully, the save
66 // games themselves don't use that feature.
67 const int CURRENT_GLOBAL_VERSION = 3;
68
buildGlobalMemoryFilename(RLMachine & machine)69 fs::path buildGlobalMemoryFilename(RLMachine& machine) {
70 return machine.system().GameSaveDirectory() / "global.sav.gz";
71 }
72
saveGlobalMemory(RLMachine & machine)73 void saveGlobalMemory(RLMachine& machine) {
74 fs::path home = buildGlobalMemoryFilename(machine);
75 fs::ofstream file(home, std::ios::binary);
76 if (!file) {
77 throw rlvm::Exception(_("Could not open global memory file."));
78 }
79
80 saveGlobalMemoryTo(file, machine);
81 }
82
saveGlobalMemoryTo(std::ostream & oss,RLMachine & machine)83 void saveGlobalMemoryTo(std::ostream& oss, RLMachine& machine) {
84 boost::iostreams::filtering_stream<boost::iostreams::output> filtered_output;
85 filtered_output.push(boost::iostreams::zlib_compressor());
86 filtered_output.push(oss);
87
88 boost::archive::text_oarchive oa(filtered_output);
89 System& sys = machine.system();
90
91 oa << CURRENT_GLOBAL_VERSION
92 << const_cast<const GlobalMemory&>(machine.memory().global())
93 << const_cast<const SystemGlobals&>(sys.globals())
94 << const_cast<const GraphicsSystemGlobals&>(sys.graphics().globals())
95 << const_cast<const EventSystemGlobals&>(sys.event().globals())
96 << const_cast<const TextSystemGlobals&>(sys.text().globals())
97 << const_cast<const SoundSystemGlobals&>(sys.sound().globals());
98 }
99
loadGlobalMemory(RLMachine & machine)100 void loadGlobalMemory(RLMachine& machine) {
101 fs::path home = buildGlobalMemoryFilename(machine);
102 fs::ifstream file(home, std::ios::binary);
103
104 // If we were able to open the file for reading, load it. Don't
105 // complain if we're unable to, since this may be the first run on
106 // this certain game and it may not exist yet.
107 if (file) {
108 try {
109 loadGlobalMemoryFrom(file, machine);
110 }
111 catch (...) {
112 // Swallow ALL exceptions during file reading. If loading the global
113 // memory file fails in any way, something is EXTREMELY wrong. Either
114 // we're trying to read an incompatible old version's files or the global
115 // data is corrupted. Either way, we can't safely do ANYTHING with this
116 // game's entire save data so move it out of the way.
117 fs::path save_dir = machine.system().GameSaveDirectory();
118 fs::path dest_save_dir = save_dir.parent_path() /
119 (save_dir.filename() / ".old_corrupted_data");
120
121 if (fs::exists(dest_save_dir))
122 fs::remove_all(dest_save_dir);
123 fs::rename(save_dir, dest_save_dir);
124
125 std::cerr << "WARNING: Unable to read saved global memory file. Moving "
126 << save_dir << " to " << dest_save_dir << std::endl;
127 }
128 }
129 }
130
loadGlobalMemoryFrom(std::istream & iss,RLMachine & machine)131 void loadGlobalMemoryFrom(std::istream& iss, RLMachine& machine) {
132 boost::iostreams::filtering_stream<boost::iostreams::input> filtered_input;
133 filtered_input.push(boost::iostreams::zlib_decompressor());
134 filtered_input.push(iss);
135
136 boost::archive::text_iarchive ia(filtered_input);
137 System& sys = machine.system();
138 int version;
139 ia >> version;
140
141 // Load global memory.
142 ia >> machine.memory().global();
143
144 // When Karmic Koala came out, support for all boost earlier than 1.36 was
145 // dropped. For years, I had used boost 1.35 on Ubuntu. It turns out that
146 // boost 1.35 had a serious bug in it, where it wouldn't save vectors of
147 // primitive data types correctly. These global data files no longer load
148 // correctly.
149 //
150 // After flirting with moving to Google protobuf (can't; doesn't handle
151 // complex object graphs like GraphicsObject and its copy-on-write stuff),
152 // and then trying to fix the problem in a forked copy of the serialization
153 // headers which was unsuccessful, I'm just saying to hell with the user's
154 // settings. Most people don't change these values and save games and global
155 // memory still work (per above.)
156 if (version == CURRENT_GLOBAL_VERSION) {
157 ia >> sys.globals() >> sys.graphics().globals() >> sys.event().globals() >>
158 sys.text().globals() >> sys.sound().globals();
159
160 // Restore options which may have System specific implementations. (This
161 // will probably expand as more of RealLive is implemented).
162 sys.sound().RestoreFromGlobals();
163 }
164 }
165
166 } // namespace Serialization
167