1 #include "WorldSave.hpp"
3 #include "filesystem.hpp"
18 WorldSave::WorldSave(const string &path)
20 , conf_path(path + "world.conf")
21 , chunk_path(path + "chunks/%d/%d/%d.gz")
22 , chunk_bufsiz(chunk_path.length() + 3 * std::numeric_limits<int>::digits10)
23 , chunk_buf(new char[chunk_bufsiz]) {
28 bool WorldSave::Exists() const noexcept {
29 return is_dir(root_path) && is_file(conf_path);
33 void WorldSave::Read(World::Config &conf) const {
34 ifstream in(conf_path);
36 throw runtime_error("failed to open world config");
39 constexpr char spaces[] = "\n\r\t ";
42 while (getline(in, line)) {
43 if (line.empty() || line[0] == '#') continue;
44 auto equals_pos = line.find_first_of('=');
46 auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces));
47 auto name_end = equals_pos - 1;
48 while (name_end > name_begin && isspace(line[name_end])) {
52 auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces));
53 auto value_end = line.length() - 1;
54 while (value_end > value_begin && isspace(line[value_end])) {
58 string name(line, name_begin, name_end - name_begin + 1);
59 string value(line, value_begin, value_end - value_begin + 1);
62 conf.gen.seed = stoul(value);
64 throw runtime_error("unknown world option: " + name);
68 throw runtime_error("IO error reading world config");
72 void WorldSave::Write(const World::Config &conf) const {
73 if (!make_dirs(root_path)) {
74 throw runtime_error("failed to create world save directory");
77 ofstream out(conf_path);
78 out << "seed = " << conf.gen.seed << endl;
82 throw runtime_error("failed to write world config");
87 bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept {
88 return is_file(ChunkPath(pos));
91 void WorldSave::Read(Chunk &chunk) const {
92 const char *path = ChunkPath(chunk.Position());
93 gzFile file = gzopen(path, "r");
95 throw runtime_error("failed to open chunk file");
97 if (gzread(file, chunk.BlockData(), Chunk::BlockSize()) != Chunk::BlockSize()) {
98 throw runtime_error("failed to read chunk from file");
100 if (gzclose(file) != Z_OK) {
101 throw runtime_error("failed to read chunk file");
103 chunk.InvalidateModel();
107 void WorldSave::Write(Chunk &chunk) const {
108 const char *path = ChunkPath(chunk.Position());
109 gzFile file = gzopen(path, "w");
111 // check if it's because of a missing path component
112 if (errno != ENOENT) {
114 throw runtime_error(strerror(errno));
116 string dir_path(path);
117 dir_path.erase(dir_path.find_last_of("\\/"));
118 if (!make_dirs(dir_path)) {
119 throw runtime_error("failed to create dir for chunk file");
121 file = gzopen(path, "w");
123 throw runtime_error("failed to open chunk file");
126 if (gzwrite(file, chunk.BlockData(), Chunk::BlockSize()) == 0) {
127 gzclose(file); // if this fails, it can't be helped
128 throw runtime_error("failed to write chunk to file");
130 if (gzclose(file) != Z_OK) {
131 throw runtime_error("failed to write chunk file");
137 const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const {
138 snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z);
139 return chunk_buf.get();