world.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  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/world.hpp"
  17. #include <physfs.h>
  18. #include <sstream>
  19. #include "physfs/util.hpp"
  20. #include "util/file_system.hpp"
  21. #include "util/log.hpp"
  22. #include "util/reader.hpp"
  23. #include "util/reader_document.hpp"
  24. #include "util/reader_mapping.hpp"
  25. #include "util/writer.hpp"
  26. std::unique_ptr<World>
  27. World::from_directory(const std::string& directory)
  28. {
  29. std::unique_ptr<World> world(new World(directory));
  30. std::string info_filename = FileSystem::join(directory, "info");
  31. try
  32. {
  33. register_translation_directory(info_filename);
  34. auto doc = ReaderDocument::from_file(info_filename);
  35. auto root = doc.get_root();
  36. if (root.get_name() != "supertux-world" &&
  37. root.get_name() != "supertux-level-subset")
  38. {
  39. throw std::runtime_error("File is not a world or levelsubset file");
  40. }
  41. auto info = root.get_mapping();
  42. info.get("title", world->m_title);
  43. info.get("description", world->m_description);
  44. info.get("levelset", world->m_is_levelset, true);
  45. info.get("hide-from-contribs", world->m_hide_from_contribs, false);
  46. info.get("contrib-type", world->m_contrib_type, "user");
  47. info.get("title-level", world->m_title_level);
  48. }
  49. catch (const std::exception& err)
  50. {
  51. log_warning << "Failed to load " << info_filename << ":" << err.what() << std::endl;
  52. world->m_hide_from_contribs = true;
  53. }
  54. return world;
  55. }
  56. std::unique_ptr<World>
  57. World::create(const std::string& title, const std::string& desc)
  58. {
  59. // Limit the charset to numbers and alphabet.
  60. std::string base = title;
  61. for (size_t i = 0; i < base.length(); i++) {
  62. if (!isalnum(base[i])) {
  63. base[i] = '_';
  64. }
  65. }
  66. base = FileSystem::join("levels", base);
  67. // Find a non-existing fitting directory name
  68. std::string dirname = base;
  69. if (PHYSFS_exists(dirname.c_str())) {
  70. int num = 1;
  71. do {
  72. num++;
  73. dirname = base + std::to_string(num);
  74. } while (PHYSFS_exists(dirname.c_str()));
  75. }
  76. std::unique_ptr<World> world(new World(dirname));
  77. world->m_title = title;
  78. world->m_description = desc;
  79. return world;
  80. }
  81. World::World(const std::string& directory) :
  82. m_title(),
  83. m_description(),
  84. m_is_levelset(true),
  85. m_basedir(directory),
  86. m_hide_from_contribs(false),
  87. m_contrib_type(),
  88. m_title_level()
  89. {
  90. }
  91. std::string
  92. World::get_basename() const
  93. {
  94. return FileSystem::basename(m_basedir);
  95. }
  96. void
  97. World::save(bool retry)
  98. {
  99. std::string filepath = FileSystem::join(m_basedir, "/info");
  100. try
  101. {
  102. { // make sure the levelset directory exists
  103. std::string dirname = FileSystem::dirname(filepath);
  104. if (!PHYSFS_exists(dirname.c_str()))
  105. {
  106. if (!PHYSFS_mkdir(dirname.c_str()))
  107. {
  108. std::ostringstream msg;
  109. msg << "Couldn't create directory for levelset '"
  110. << dirname << "': " <<physfsutil::get_last_error();
  111. throw std::runtime_error(msg.str());
  112. }
  113. }
  114. if (!physfsutil::is_directory(dirname))
  115. {
  116. std::ostringstream msg;
  117. msg << "Levelset path '" << dirname << "' is not a directory";
  118. throw std::runtime_error(msg.str());
  119. }
  120. }
  121. Writer writer(filepath);
  122. writer.start_list("supertux-level-subset");
  123. writer.write("title", m_title, true);
  124. writer.write("description", m_description, true);
  125. writer.write("levelset", m_is_levelset);
  126. writer.write("contrib-type", "user");
  127. writer.write("hide-from-contribs", m_hide_from_contribs);
  128. writer.write("title-level", m_title_level);
  129. writer.end_list("supertux-level-subset");
  130. log_warning << "Levelset info saved as " << filepath << "." << std::endl;
  131. }
  132. catch(std::exception& e)
  133. {
  134. if (retry) {
  135. std::stringstream msg;
  136. msg << "Problem when saving levelset info '" << filepath << "': " << e.what();
  137. throw std::runtime_error(msg.str());
  138. } else {
  139. log_warning << "Failed to save the levelset info, retrying..." << std::endl;
  140. { // create the levelset directory again
  141. std::string dirname = FileSystem::dirname(filepath);
  142. if (!PHYSFS_mkdir(dirname.c_str()))
  143. {
  144. std::ostringstream msg;
  145. msg << "Couldn't create directory for levelset '"
  146. << dirname << "': " <<physfsutil::get_last_error();
  147. throw std::runtime_error(msg.str());
  148. }
  149. }
  150. save(true);
  151. }
  152. }
  153. }
  154. std::string
  155. World::get_worldmap_filename() const
  156. {
  157. return FileSystem::join(m_basedir, "worldmap.stwm");
  158. }
  159. /* EOF */