Merge branch 'feature/savegame'
[supertux.git] / src / supertux / world_state.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //                2014 Ingo Ruhnke <grumbel@gmx.de>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "supertux/world_state.hpp"
19
20 #include "lisp/lisp.hpp"
21 #include "lisp/parser.hpp"
22 #include "lisp/writer.hpp"
23 #include "physfs/ifile_streambuf.hpp"
24 #include "scripting/serialize.hpp"
25 #include "scripting/squirrel_util.hpp"
26 #include "supertux/player_status.hpp"
27 #include "util/file_system.hpp"
28 #include "util/log.hpp"
29 #include "worldmap/worldmap.hpp"
30
31 WorldState::WorldState() :
32   m_player_status(new PlayerStatus)
33 {
34 }
35
36 void
37 WorldState::load(const std::string& filename)
38 {
39   if(!PHYSFS_exists(filename.c_str()))
40   {
41     log_info << filename << ": doesn't exist, not loading state" << std::endl;
42   }
43   else
44   {
45     try
46     {
47       HSQUIRRELVM vm = scripting::global_vm;
48
49       lisp::Parser parser;
50       const lisp::Lisp* root = parser.parse(filename);
51
52       const lisp::Lisp* lisp = root->get_lisp("supertux-savegame");
53       if(lisp == NULL)
54       {
55         throw std::runtime_error("file is not a supertux-savegame file");
56       }
57       else
58       {
59         int version = 1;
60         lisp->get("version", version);
61         if(version != 1)
62         {
63           throw std::runtime_error("incompatible savegame version");
64         }
65         else
66         {
67           const lisp::Lisp* tux = lisp->get_lisp("tux");
68           if(tux == NULL)
69           {
70             throw std::runtime_error("No tux section in savegame");
71           }
72           {
73             m_player_status->read(*tux);
74           }
75
76           const lisp::Lisp* state = lisp->get_lisp("state");
77           if(state == NULL)
78           {
79             throw std::runtime_error("No state section in savegame");
80           }
81           else
82           {
83             sq_pushroottable(vm);
84             sq_pushstring(vm, "state", -1);
85             if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
86               sq_pop(vm, 1);
87
88             sq_pushstring(vm, "state", -1);
89             sq_newtable(vm);
90             scripting::load_squirrel_table(vm, -1, *state);
91             if(SQ_FAILED(sq_createslot(vm, -3)))
92               throw std::runtime_error("Couldn't create state table");
93             sq_pop(vm, 1);
94           }
95         }
96       }
97     }
98     catch(const std::exception& e)
99     {
100       log_fatal << "Couldn't load savegame: " << e.what() << std::endl;
101     }
102   }
103 }
104
105 void
106 WorldState::save(const std::string& filename)
107 {
108   { // make sure the savegame directory exists
109     std::string dirname = FileSystem::dirname(filename);
110     if(!PHYSFS_exists(dirname.c_str()))
111     {
112       if(!PHYSFS_mkdir(dirname.c_str()))
113       {
114         std::ostringstream msg;
115         msg << "Couldn't create directory for savegames '"
116             << dirname << "': " <<PHYSFS_getLastError();
117         throw std::runtime_error(msg.str());
118       }
119     }
120
121     if(!PHYSFS_isDirectory(dirname.c_str()))
122     {
123       std::ostringstream msg;
124       msg << "Savegame path '" << dirname << "' is not a directory";
125       throw std::runtime_error(msg.str());
126     }
127   }
128
129   HSQUIRRELVM vm = scripting::global_vm;
130
131   lisp::Writer writer(filename);
132
133   writer.start_list("supertux-savegame");
134   writer.write("version", 1);
135
136   using namespace worldmap;
137   if(WorldMap::current() != NULL)
138   {
139     std::ostringstream title;
140     title << WorldMap::current()->get_title();
141     title << " (" << WorldMap::current()->solved_level_count()
142           << "/" << WorldMap::current()->level_count() << ")";
143     writer.write("title", title.str());
144   }
145
146   writer.start_list("tux");
147   m_player_status->write(writer);
148   writer.end_list("tux");
149
150   writer.start_list("state");
151
152   sq_pushroottable(vm);
153   sq_pushstring(vm, "state", -1);
154   if(SQ_SUCCEEDED(sq_get(vm, -2)))
155   {
156     scripting::save_squirrel_table(vm, -1, writer);
157     sq_pop(vm, 1);
158   }
159   sq_pop(vm, 1);
160   writer.end_list("state");
161
162   writer.end_list("supertux-savegame");
163 }
164
165 /* EOF */