1 #include "WorldSave.hpp"
3 #include "filesystem.hpp"
17 WorldSave::WorldSave(const string &path)
19 , conf_path(path + "world.conf")
20 , chunk_path(path + "chunks/%d/%d/%d.gz")
21 , chunk_bufsiz(chunk_path.length() + 3 * std::numeric_limits<int>::digits10)
22 , chunk_buf(new char[chunk_bufsiz]) {
27 bool WorldSave::Exists() const noexcept {
28 return is_dir(root_path) && is_file(conf_path);
32 void WorldSave::Read(World::Config &conf) const {
33 ifstream in(conf_path);
35 throw runtime_error("failed to open world config");
38 constexpr char spaces[] = "\n\r\t ";
41 while (getline(in, line)) {
42 if (line.empty() || line[0] == '#') continue;
43 auto equals_pos = line.find_first_of('=');
45 auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces));
46 auto name_end = equals_pos - 1;
47 while (name_end > name_begin && isspace(line[name_end])) {
51 auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces));
52 auto value_end = line.length() - 1;
53 while (value_end > value_begin && isspace(line[value_end])) {
57 string name(line, name_begin, name_end - name_begin + 1);
58 string value(line, value_begin, value_end - value_begin + 1);
61 conf.gen.seed = stoul(value);
63 throw runtime_error("unknown world option: " + name);
67 throw runtime_error("IO error reading world config");
71 void WorldSave::Write(const World::Config &conf) const {
72 if (!make_dirs(root_path)) {
73 throw runtime_error("failed to create world save directory");
76 ofstream out(conf_path);
77 out << "seed = " << conf.gen.seed << endl;
81 throw runtime_error("failed to write world config");
86 bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept {
87 return is_file(ChunkPath(pos));
90 void WorldSave::Read(Chunk &chunk) const {
91 const char *path = ChunkPath(chunk.Position());
92 gzFile file = gzopen(path, "r");
94 throw runtime_error("failed to open chunk file");
96 if (gzread(file, chunk.BlockData(), Chunk::BlockSize()) != Chunk::BlockSize()) {
97 throw runtime_error("failed to read chunk from file");
99 if (gzclose(file) != Z_OK) {
100 throw runtime_error("failed to read chunk file");
102 chunk.InvalidateModel();
106 void WorldSave::Write(Chunk &chunk) const {
107 const char *path = ChunkPath(chunk.Position());
108 gzFile file = gzopen(path, "w");
110 // check if it's because of a missing path component
111 if (errno != ENOENT) {
113 throw runtime_error(strerror(errno));
115 string dir_path(path);
116 dir_path.erase(dir_path.find_last_of("\\/"));
117 if (!make_dirs(dir_path)) {
118 throw runtime_error("failed to create dir for chunk file");
120 file = gzopen(path, "w");
122 throw runtime_error("failed to open chunk file");
125 if (gzwrite(file, chunk.BlockData(), Chunk::BlockSize()) == 0) {
126 gzclose(file); // if this fails, it can't be helped
127 throw runtime_error("failed to write chunk to file");
129 if (gzclose(file) != Z_OK) {
130 throw runtime_error("failed to write chunk file");
136 const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const {
137 snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z);
138 return chunk_buf.get();