level_parser.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. #include "supertux/level_parser.hpp"
  17. #include <physfs.h>
  18. #include <sstream>
  19. #include "supertux/constants.hpp"
  20. #include "supertux/level.hpp"
  21. #include "supertux/sector.hpp"
  22. #include "supertux/sector_parser.hpp"
  23. #include "util/log.hpp"
  24. #include "util/reader.hpp"
  25. #include "util/reader_document.hpp"
  26. #include "util/reader_mapping.hpp"
  27. std::string
  28. LevelParser::get_level_name(const std::string& filename)
  29. {
  30. try
  31. {
  32. register_translation_directory(filename);
  33. auto doc = ReaderDocument::from_file(filename);
  34. auto root = doc.get_root();
  35. if (root.get_name() != "supertux-level") {
  36. return "";
  37. } else {
  38. auto mapping = root.get_mapping();
  39. std::string name;
  40. mapping.get("name", name);
  41. return name;
  42. }
  43. }
  44. catch(const std::exception& e)
  45. {
  46. log_warning << "Problem getting name of '" << filename << "': "
  47. << e.what() << std::endl;
  48. return "";
  49. }
  50. }
  51. std::unique_ptr<Level>
  52. LevelParser::from_stream(std::istream& stream, const std::string& context, bool worldmap, bool editable)
  53. {
  54. auto level = std::make_unique<Level>(worldmap);
  55. LevelParser parser(*level, worldmap, editable);
  56. parser.load(stream, context);
  57. return level;
  58. }
  59. std::unique_ptr<Level>
  60. LevelParser::from_file(const std::string& filename, bool worldmap, bool editable)
  61. {
  62. auto level = std::make_unique<Level>(worldmap);
  63. LevelParser parser(*level, worldmap, editable);
  64. parser.load(filename);
  65. return level;
  66. }
  67. std::unique_ptr<Level>
  68. LevelParser::from_nothing(const std::string& basedir)
  69. {
  70. auto level = std::make_unique<Level>(false);
  71. LevelParser parser(*level, false, false);
  72. // Find a free level filename
  73. std::string level_file;
  74. int num = 0;
  75. do {
  76. num++;
  77. level_file = basedir + "/level" + std::to_string(num) + ".stl";
  78. } while ( PHYSFS_exists(level_file.c_str()) );
  79. std::string level_name = "Level " + std::to_string(num);
  80. level_file = "level" + std::to_string(num) + ".stl";
  81. parser.create(level_file, level_name);
  82. return level;
  83. }
  84. std::unique_ptr<Level>
  85. LevelParser::from_nothing_worldmap(const std::string& basedir, const std::string& name)
  86. {
  87. auto level = std::make_unique<Level>(true);
  88. LevelParser parser(*level, true, false);
  89. // Find a free level filename
  90. std::string level_file = basedir + "/worldmap.stwm";
  91. if (PHYSFS_exists(level_file.c_str())) {
  92. int num = 0;
  93. do {
  94. num++;
  95. level_file = basedir + "/worldmap" + std::to_string(num) + ".stwm";
  96. } while ( PHYSFS_exists(level_file.c_str()) );
  97. level_file = "worldmap" + std::to_string(num) + ".stwm";
  98. } else {
  99. level_file = "worldmap.stwm";
  100. }
  101. parser.create(level_file, name);
  102. return level;
  103. }
  104. LevelParser::LevelParser(Level& level, bool worldmap, bool editable) :
  105. m_level(level),
  106. m_worldmap(worldmap),
  107. m_editable(editable)
  108. {
  109. }
  110. void
  111. LevelParser::load(std::istream& stream, const std::string& context)
  112. {
  113. auto doc = ReaderDocument::from_stream(stream, context);
  114. load(doc);
  115. }
  116. void
  117. LevelParser::load(const std::string& filepath)
  118. {
  119. m_level.m_filename = filepath;
  120. register_translation_directory(filepath);
  121. try {
  122. auto doc = ReaderDocument::from_file(filepath);
  123. load(doc);
  124. } catch(std::exception& e) {
  125. std::stringstream msg;
  126. msg << "Problem when reading level '" << filepath << "': " << e.what();
  127. throw std::runtime_error(msg.str());
  128. }
  129. }
  130. void
  131. LevelParser::load(const ReaderDocument& doc)
  132. {
  133. auto root = doc.get_root();
  134. if (root.get_name() != "supertux-level")
  135. throw std::runtime_error("file is not a supertux-level file.");
  136. auto level = root.get_mapping();
  137. int version = 1;
  138. level.get("version", version);
  139. if (version == 1) {
  140. log_info << "[" << doc.get_filename() << "] level uses old format: version 1" << std::endl;
  141. load_old_format(level);
  142. } else if (version == 2 || version == 3) {
  143. level.get("tileset", m_level.m_tileset);
  144. level.get("name", m_level.m_name);
  145. level.get("author", m_level.m_author);
  146. level.get("contact", m_level.m_contact);
  147. level.get("license", m_level.m_license);
  148. level.get("target-time", m_level.m_target_time);
  149. level.get("suppress-pause-menu", m_level.m_suppress_pause_menu);
  150. level.get("note", m_level.m_note);
  151. level.get("icon", m_level.m_icon);
  152. level.get("icon-locked", m_level.m_icon_locked);
  153. level.get("bkg", m_level.m_wmselect_bkg);
  154. auto iter = level.get_iter();
  155. while (iter.next())
  156. {
  157. if (iter.get_key() == "sector")
  158. {
  159. auto sector = SectorParser::from_reader(m_level, iter.as_mapping(), m_editable);
  160. m_level.add_sector(std::move(sector));
  161. }
  162. }
  163. if (m_level.m_license.empty()) {
  164. log_warning << "[" << doc.get_filename() << "] The level author \"" << m_level.m_author
  165. << "\" did not specify a license for this level \""
  166. << m_level.m_name << "\". You might not be allowed to share it."
  167. << std::endl;
  168. }
  169. } else {
  170. log_warning << "[" << doc.get_filename() << "] level format version " << version << " is not supported" << std::endl;
  171. }
  172. m_level.initialize();
  173. }
  174. void
  175. LevelParser::load_old_format(const ReaderMapping& reader)
  176. {
  177. reader.get("name", m_level.m_name);
  178. reader.get("author", m_level.m_author);
  179. auto sector = SectorParser::from_reader_old_format(m_level, reader, m_editable);
  180. m_level.add_sector(std::move(sector));
  181. m_level.initialize();
  182. }
  183. void
  184. LevelParser::create(const std::string& filepath, const std::string& levelname)
  185. {
  186. m_level.m_filename = filepath;
  187. m_level.m_name = levelname;
  188. m_level.m_license = "CC-BY-SA 4.0 International";
  189. m_level.m_tileset = m_worldmap ? "images/ice_world.strf" : "images/tiles.strf";
  190. auto sector = SectorParser::from_nothing(m_level);
  191. sector->set_name(DEFAULT_SECTOR_NAME);
  192. m_level.add_sector(std::move(sector));
  193. }
  194. /* EOF */