level_parser.cpp 6.3 KB

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