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 | |
30 | std::string |
31 | LevelParser::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 | |
56 | std::unique_ptr<Level> |
57 | LevelParser::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 | |
65 | std::unique_ptr<Level> |
66 | LevelParser::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 | |
74 | std::unique_ptr<Level> |
75 | LevelParser::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 | |
94 | std::unique_ptr<Level> |
95 | LevelParser::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 | |
117 | LevelParser::LevelParser(Level& level, bool worldmap, bool editable) : |
118 | m_level(level), |
119 | m_worldmap(worldmap), |
120 | m_editable(editable) |
121 | { |
122 | } |
123 | |
124 | void |
125 | LevelParser::load(std::istream& stream, const std::string& context) |
126 | { |
127 | auto doc = ReaderDocument::from_stream(stream, context); |
128 | load(doc); |
129 | } |
130 | |
131 | void |
132 | LevelParser::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 | |
146 | void |
147 | LevelParser::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 | |
191 | void |
192 | LevelParser::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 | |
201 | void |
202 | LevelParser::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 | |