1// SuperTux
2// Copyright (C) 2015 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "supertux/level_parser.hpp"
18
19#include <physfs.h>
20#include <sstream>
21
22#include "supertux/level.hpp"
23#include "supertux/sector.hpp"
24#include "supertux/sector_parser.hpp"
25#include "util/log.hpp"
26#include "util/reader.hpp"
27#include "util/reader_document.hpp"
28#include "util/reader_mapping.hpp"
29
30std::string
31LevelParser::get_level_name(const std::string& filename)
32{
33 try
34 {
35 register_translation_directory(filename);
36 auto doc = ReaderDocument::from_file(filename);
37 auto root = doc.get_root();
38
39 if (root.get_name() != "supertux-level") {
40 return "";
41 } else {
42 auto mapping = root.get_mapping();
43 std::string name;
44 mapping.get("name", name);
45 return name;
46 }
47 }
48 catch(const std::exception& e)
49 {
50 log_warning << "Problem getting name of '" << filename << "': "
51 << e.what() << std::endl;
52 return "";
53 }
54}
55
56std::unique_ptr<Level>
57LevelParser::from_stream(std::istream& stream, const std::string& context, bool worldmap, bool editable)
58{
59 auto level = std::make_unique<Level>(worldmap);
60 LevelParser parser(*level, worldmap, editable);
61 parser.load(stream, context);
62 return level;
63}
64
65std::unique_ptr<Level>
66LevelParser::from_file(const std::string& filename, bool worldmap, bool editable)
67{
68 auto level = std::make_unique<Level>(worldmap);
69 LevelParser parser(*level, worldmap, editable);
70 parser.load(filename);
71 return level;
72}
73
74std::unique_ptr<Level>
75LevelParser::from_nothing(const std::string& basedir)
76{
77 auto level = std::make_unique<Level>(false);
78 LevelParser parser(*level, false, false);
79
80 // Find a free level filename
81 std::string level_file;
82 int num = 0;
83 do {
84 num++;
85 level_file = basedir + "/level" + std::to_string(num) + ".stl";
86 } while ( PHYSFS_exists(level_file.c_str()) );
87 std::string level_name = "Level " + std::to_string(num);
88 level_file = "level" + std::to_string(num) + ".stl";
89
90 parser.create(level_file, level_name);
91 return level;
92}
93
94std::unique_ptr<Level>
95LevelParser::from_nothing_worldmap(const std::string& basedir, const std::string& name)
96{
97 auto level = std::make_unique<Level>(true);
98 LevelParser parser(*level, true, false);
99
100 // Find a free level filename
101 std::string level_file = basedir + "/worldmap.stwm";
102 if (PHYSFS_exists(level_file.c_str())) {
103 int num = 0;
104 do {
105 num++;
106 level_file = basedir + "/worldmap" + std::to_string(num) + ".stwm";
107 } while ( PHYSFS_exists(level_file.c_str()) );
108 level_file = "worldmap" + std::to_string(num) + ".stwm";
109 } else {
110 level_file = "worldmap.stwm";
111 }
112
113 parser.create(level_file, name);
114 return level;
115}
116
117LevelParser::LevelParser(Level& level, bool worldmap, bool editable) :
118 m_level(level),
119 m_worldmap(worldmap),
120 m_editable(editable)
121{
122}
123
124void
125LevelParser::load(std::istream& stream, const std::string& context)
126{
127 auto doc = ReaderDocument::from_stream(stream, context);
128 load(doc);
129}
130
131void
132LevelParser::load(const std::string& filepath)
133{
134 m_level.m_filename = filepath;
135 register_translation_directory(filepath);
136 try {
137 auto doc = ReaderDocument::from_file(filepath);
138 load(doc);
139 } catch(std::exception& e) {
140 std::stringstream msg;
141 msg << "Problem when reading level '" << filepath << "': " << e.what();
142 throw std::runtime_error(msg.str());
143 }
144}
145
146void
147LevelParser::load(const ReaderDocument& doc)
148{
149 auto root = doc.get_root();
150
151 if (root.get_name() != "supertux-level")
152 throw std::runtime_error("file is not a supertux-level file.");
153
154 auto level = root.get_mapping();
155
156 int version = 1;
157 level.get("version", version);
158 if (version == 1) {
159 log_info << "[" << doc.get_filename() << "] level uses old format: version 1" << std::endl;
160 load_old_format(level);
161 } else if (version == 2 || version == 3) {
162 level.get("tileset", m_level.m_tileset);
163
164 level.get("name", m_level.m_name);
165 level.get("author", m_level.m_author);
166 level.get("contact", m_level.m_contact);
167 level.get("license", m_level.m_license);
168 level.get("target-time", m_level.m_target_time);
169
170 auto iter = level.get_iter();
171 while (iter.next()) {
172 if (iter.get_key() == "sector") {
173 auto sector = SectorParser::from_reader(m_level, iter.as_mapping(), m_editable);
174 m_level.add_sector(std::move(sector));
175 }
176 }
177
178 if (m_level.m_license.empty()) {
179 log_warning << "[" << doc.get_filename() << "] The level author \"" << m_level.m_author
180 << "\" did not specify a license for this level \""
181 << m_level.m_name << "\". You might not be allowed to share it."
182 << std::endl;
183 }
184 } else {
185 log_warning << "[" << doc.get_filename() << "] level format version " << version << " is not supported" << std::endl;
186 }
187
188 m_level.m_stats.init(m_level);
189}
190
191void
192LevelParser::load_old_format(const ReaderMapping& reader)
193{
194 reader.get("name", m_level.m_name);
195 reader.get("author", m_level.m_author);
196
197 auto sector = SectorParser::from_reader_old_format(m_level, reader, m_editable);
198 m_level.add_sector(std::move(sector));
199}
200
201void
202LevelParser::create(const std::string& filepath, const std::string& levelname)
203{
204 m_level.m_filename = filepath;
205 m_level.m_name = levelname;
206 m_level.m_license = "CC-BY-SA 4.0 International";
207 m_level.m_tileset = m_worldmap ? "images/worldmap.strf" : "images/tiles.strf";
208
209 auto sector = SectorParser::from_nothing(m_level);
210 sector->set_name("main");
211 m_level.add_sector(std::move(sector));
212}
213
214/* EOF */
215