1
// -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
// vi:tw=80:et:ts=2:sts=2
4
// -----------------------------------------------------------------------
6
// This file is part of RLVM, a RealLive virtual machine clone.
8
// -----------------------------------------------------------------------
10
// Copyright (C) 2007 Elliot Glaysher
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.
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.
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.
26
// -----------------------------------------------------------------------
28
#include "Modules/Module_Sys_Save.hpp"
31
#include <boost/algorithm/string/predicate.hpp>
32
#include <boost/bind.hpp>
33
#include <boost/filesystem/fstream.hpp>
34
#include <boost/filesystem/operations.hpp>
35
#include <boost/filesystem/path.hpp>
36
#include <boost/lexical_cast.hpp>
37
#include <boost/shared_ptr.hpp>
41
#include "LongOperations/LoadGameLongOperation.hpp"
42
#include "MachineBase/GeneralOperations.hpp"
43
#include "MachineBase/LongOperation.hpp"
44
#include "MachineBase/Memory.hpp"
45
#include "MachineBase/RLMachine.hpp"
46
#include "MachineBase/RLModule.hpp"
47
#include "MachineBase/RLOperation.hpp"
48
#include "MachineBase/RLOperation/Argc_T.hpp"
49
#include "MachineBase/RLOperation/Complex_T.hpp"
50
#include "MachineBase/RLOperation/RLOp_Store.hpp"
51
#include "MachineBase/RLOperation/References.hpp"
52
#include "MachineBase/RLOperation/Special_T.hpp"
53
#include "MachineBase/SaveGameHeader.hpp"
54
#include "MachineBase/Serialization.hpp"
55
#include "Systems/Base/Colour.hpp"
56
#include "Systems/Base/Surface.hpp"
57
#include "Systems/Base/System.hpp"
58
#include "libReallive/intmemref.h"
59
#include "utf8cpp/utf8.h"
61
// For copy_n, which isn't part of the C++ standard and doesn't come on
63
#include <boost/multi_array/algorithm.hpp>
65
using namespace libReallive;
66
using boost::lexical_cast;
67
using boost::starts_with;
68
using boost::ends_with;
70
using boost::shared_ptr;
71
namespace fs = boost::filesystem;
73
// -----------------------------------------------------------------------
77
struct SaveExists : public RLOp_Store_1< IntConstant_T > {
78
int operator()(RLMachine& machine, int slot) {
79
fs::path saveFile = Serialization::buildSaveGameFilename(machine, slot);
80
return fs::exists(saveFile) ? 1 : 0;
85
: public RLOp_Store_5<IntConstant_T, IntReference_T, IntReference_T,
86
IntReference_T, IntReference_T> {
87
int operator()(RLMachine& machine, int slot,
88
IntReferenceIterator yIt, IntReferenceIterator mIt,
89
IntReferenceIterator dIt, IntReferenceIterator wdIt) {
90
int fileExists = SaveExists()(machine, slot);
93
SaveGameHeader header = Serialization::loadHeaderForSlot(machine, slot);
95
*yIt = header.save_time.date().year();
96
*mIt = header.save_time.date().month();
97
*dIt = header.save_time.date().day();
98
*wdIt = header.save_time.date().day_of_week();
106
: public RLOp_Store_5<IntConstant_T, IntReference_T, IntReference_T,
107
IntReference_T, IntReference_T> {
108
int operator()(RLMachine& machine, int slot,
109
IntReferenceIterator hhIt, IntReferenceIterator mmIt,
110
IntReferenceIterator ssIt, IntReferenceIterator msIt) {
111
int fileExists = SaveExists()(machine, slot);
114
SaveGameHeader header = Serialization::loadHeaderForSlot(machine, slot);
116
*hhIt = header.save_time.time_of_day().hours();
117
*mmIt = header.save_time.time_of_day().minutes();
118
*ssIt = header.save_time.time_of_day().seconds();
119
*msIt = header.save_time.time_of_day().fractional_seconds();
126
struct SaveDateTime : public RLOp_Store_9<
127
IntConstant_T, IntReference_T, IntReference_T, IntReference_T, IntReference_T,
128
IntReference_T, IntReference_T, IntReference_T, IntReference_T > {
129
int operator()(RLMachine& machine, int slot,
130
IntReferenceIterator yIt, IntReferenceIterator mIt,
131
IntReferenceIterator dIt, IntReferenceIterator wdIt,
132
IntReferenceIterator hhIt, IntReferenceIterator mmIt,
133
IntReferenceIterator ssIt, IntReferenceIterator msIt) {
134
int fileExists = SaveExists()(machine, slot);
137
SaveGameHeader header = Serialization::loadHeaderForSlot(machine, slot);
139
*yIt = header.save_time.date().year();
140
*mIt = header.save_time.date().month();
141
*dIt = header.save_time.date().day();
142
*wdIt = header.save_time.date().day_of_week();
143
*hhIt = header.save_time.time_of_day().hours();
144
*mmIt = header.save_time.time_of_day().minutes();
145
*ssIt = header.save_time.time_of_day().seconds();
146
*msIt = header.save_time.time_of_day().fractional_seconds();
154
: public RLOp_Store_10<IntConstant_T, IntReference_T, IntReference_T,
155
IntReference_T, IntReference_T, IntReference_T,
156
IntReference_T, IntReference_T, IntReference_T,
158
int operator()(RLMachine& machine, int slot,
159
IntReferenceIterator yIt, IntReferenceIterator mIt,
160
IntReferenceIterator dIt, IntReferenceIterator wdIt,
161
IntReferenceIterator hhIt, IntReferenceIterator mmIt,
162
IntReferenceIterator ssIt, IntReferenceIterator msIt,
163
StringReferenceIterator titleIt) {
164
int fileExists = SaveExists()(machine, slot);
167
SaveGameHeader header = Serialization::loadHeaderForSlot(machine, slot);
169
*yIt = header.save_time.date().year();
170
*mIt = header.save_time.date().month();
171
*dIt = header.save_time.date().day();
172
*wdIt = header.save_time.date().day_of_week();
173
*hhIt = header.save_time.time_of_day().hours();
174
*mmIt = header.save_time.time_of_day().minutes();
175
*ssIt = header.save_time.time_of_day().seconds();
176
*msIt = header.save_time.time_of_day().fractional_seconds();
178
// Convert the UTF-8 string to the memory internal CP932
179
*titleIt = header.title;
188
Complex3_T<IntReference_T, IntReference_T, IntConstant_T>,
189
Complex3_T<StrReference_T, StrReference_T, IntConstant_T> > >
192
// Retrieves the values of variables from saved games. If slot is
193
// empty, returns 0 and does nothing further; if slot contains a saved
194
// game, returns 1 and processes the list of structures. For each
195
// entry in the list, count values are copied to a block of variables
196
// starting with dst, reading from src: the values copied are those
197
// that are stored in the saved game in slot.
199
// For example, an RPG that stored the player's level in F[100], the
200
// player's hit points in F[101], and the name of the player's class
201
// in S[10], could retrieve these values from saved games to display
202
// them in a custom load menu as follows:
205
// for (int i = 0) (i < length(menu_line)) (i += 1):
206
// int (block) level, hp
208
// GetSaveFlag(i, {intF[100], level, 2}, {strS[10], class, 1})
209
// menu_line[i] = 'Level \i{level} \s{class}, \i{hp} HP';
210
struct GetSaveFlag : public RLOp_Store_2<
211
IntConstant_T, GetSaveFlagList> {
213
int operator()(RLMachine& machine, int slot, GetSaveFlagList::type flagList) {
214
int fileExists = SaveExists()(machine, slot);
218
Memory overlayedMemory(machine, slot);
219
Serialization::loadLocalMemoryForSlot(machine, slot, overlayedMemory);
221
using boost::detail::multi_array::copy_n;
222
for (GetSaveFlagList::type::iterator it = flagList.begin();
223
it != flagList.end(); ++it) {
226
IntReferenceIterator jt = it->first.get<0>()
227
.changeMemoryTo(&overlayedMemory);
228
copy_n(jt, it->first.get<2>(), it->first.get<1>());
232
StringReferenceIterator jt = it->second.get<0>()
233
.changeMemoryTo(&overlayedMemory);
234
copy_n(jt, it->second.get<2>(), it->second.get<1>());
238
throw rlvm::Exception("Illegal value in Special_T in GetSaveFlag");
247
// Returns the slot most recently saved to, or −1 if no games have
249
struct LatestSave : public RLOp_Store_Void {
250
int operator()(RLMachine& machine) {
251
fs::path saveDir = machine.system().gameSaveDirectory();
253
time_t latestTime = std::numeric_limits<time_t>::min();
255
if (fs::exists(saveDir)) {
256
fs::directory_iterator end;
257
for (fs::directory_iterator it(saveDir); it != end; ++it) {
258
string filename = it->leaf();
259
if (starts_with(filename, "save") && ends_with(filename, ".sav.gz")) {
260
time_t mtime = fs::last_write_time(*it);
262
if (mtime > latestTime) {
264
latestSlot = lexical_cast<int>(filename.substr(4, 3));
274
struct save : public RLOp_Void_1< IntConstant_T > {
275
void operator()(RLMachine& machine, int slot) {
276
Serialization::saveGlobalMemory(machine);
277
Serialization::saveGameForSlot(machine, slot);
281
struct LoadingGameFromSlot : public LoadGameLongOperation {
283
explicit LoadingGameFromSlot(RLMachine& machine, int slot)
284
: LoadGameLongOperation(machine), slot_(slot) {}
286
virtual void load(RLMachine& machine) {
287
Serialization::loadGameForSlot(machine, slot_);
293
// -----------------------------------------------------------------------
295
void Sys_load::operator()(RLMachine& machine, int slot) {
296
// LoadGameLongOperation will add self to |machine|'s stack.
297
new LoadingGameFromSlot(machine, slot);
300
// -----------------------------------------------------------------------
302
void addSysSaveOpcodes(RLModule& m) {
303
m.addOpcode(1409, 0, "SaveExists", new SaveExists);
304
m.addOpcode(1410, 0, "SaveDate", new SaveDate);
305
m.addOpcode(1411, 0, "SaveTime", new SaveTime);
306
m.addOpcode(1412, 0, "SaveDateTime", new SaveDateTime);
307
m.addOpcode(1413, 0, "SaveInfo", new SaveInfo);
308
m.addOpcode(1414, 0, "GetSaveFlag", new GetSaveFlag);
309
m.addOpcode(1421, 0, "LatestSave", new LatestSave);
311
m.addOpcode(2053, 0, "SetConfirmSaveLoad",
312
callFunction(&System::setConfirmSaveLoad));
313
m.addOpcode(2003, 0, "ConfirmSaveLoad",
314
returnIntValue(&System::confirmSaveLoad));
316
m.addOpcode(3000, 0, "menu_save", new InvokeSyscomAsOp(0));
317
m.addOpcode(3001, 0, "menu_load", new InvokeSyscomAsOp(1));
319
m.addOpcode(3007, 0, "save", new save);
320
m.addOpcode(3107, 0, "save_always", new save);
322
m.addOpcode(3009, 0, "load", new Sys_load);
323
m.addOpcode(3109, 0, "load_always", new Sys_load);
325
m.addOpcode(3100, 0, "menu_save_always", new InvokeSyscomAsOp(0));
326
m.addOpcode(3101, 0, "menu_load_always", new InvokeSyscomAsOp(1));
328
m.addOpcode(3500, 0, "Savepoint",
329
callFunction(&RLMachine::markSavepoint));
330
m.addOpcode(3501, 0, "EnableAutoSavepoints",
331
callFunctionWith(&RLMachine::setMarkSavepoints, 1));
332
m.addOpcode(3502, 0, "DisableAutoSavepoints",
333
callFunctionWith(&RLMachine::setMarkSavepoints, 0));