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 cout << "reading world save" << endl;
35 ifstream in(conf_path);
37 throw runtime_error("failed to open world config");
40 constexpr char spaces[] = "\n\r\t ";
43 while (getline(in, line)) {
44 if (line.empty() || line[0] == '#') continue;
45 auto equals_pos = line.find_first_of('=');
47 auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces));
48 auto name_end = equals_pos - 1;
49 while (name_end > name_begin && isspace(line[name_end])) {
53 auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces));
54 auto value_end = line.length() - 1;
55 while (value_end > value_begin && isspace(line[value_end])) {
59 string name(line, name_begin, name_end - name_begin + 1);
60 string value(line, value_begin, value_end - value_begin + 1);
63 conf.gen.seed = stoul(value);
65 throw runtime_error("unknown world option: " + name);
69 throw runtime_error("IO error reading world config");
73 void WorldSave::Write(const World::Config &conf) const {
74 cout << "writing world save" << endl;
76 if (!make_dirs(root_path)) {
77 throw runtime_error("failed to create world save directory");
80 ofstream out(conf_path);
81 out << "seed = " << conf.gen.seed << endl;
85 throw runtime_error("failed to write world config");
90 bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept {
91 return is_file(ChunkPath(pos));
94 void WorldSave::Read(Chunk &chunk) const {
95 const char *path = ChunkPath(chunk.Position());
96 gzFile file = gzopen(path, "r");
98 throw runtime_error("failed to open chunk file");
100 if (gzread(file, chunk.BlockData(), Chunk::BlockSize()) != Chunk::BlockSize()) {
101 throw runtime_error("failed to read chunk from file");
103 if (gzclose(file) != Z_OK) {
104 throw runtime_error("failed to read chunk file");
106 chunk.InvalidateModel();
110 void WorldSave::Write(Chunk &chunk) const {
111 const char *path = ChunkPath(chunk.Position());
112 gzFile file = gzopen(path, "w");
114 // check if it's because of a missing path component
115 if (errno != ENOENT) {
117 throw runtime_error(strerror(errno));
119 string dir_path(path);
120 dir_path.erase(dir_path.find_last_of("\\/"));
121 if (!make_dirs(dir_path)) {
122 throw runtime_error("failed to create dir for chunk file");
124 file = gzopen(path, "w");
126 throw runtime_error("failed to open chunk file");
129 if (gzwrite(file, chunk.BlockData(), Chunk::BlockSize()) == 0) {
130 gzclose(file); // if this fails, it can't be helped
131 throw runtime_error("failed to write chunk to file");
133 if (gzclose(file) != Z_OK) {
134 throw runtime_error("failed to write chunk file");
140 const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const {
141 snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z);
142 return chunk_buf.get();