1 #include "WorldSave.hpp"
3 #include "filesystem.hpp"
18 WorldSave::WorldSave(const string &path)
20 , world_conf_path(path + "world.conf")
21 , gen_conf_path(path + "gen.conf")
22 , chunk_path(path + "chunks/%d/%d/%d.gz")
23 , chunk_bufsiz(chunk_path.length() + 3 * std::numeric_limits<int>::digits10)
24 , chunk_buf(new char[chunk_bufsiz]) {
29 bool WorldSave::Exists() const noexcept {
30 return is_dir(root_path) && is_file(world_conf_path);
34 // TODO: better implementation of config files
35 void WorldSave::Read(World::Config &conf) const {
36 ifstream in(world_conf_path);
38 throw runtime_error("failed to open world config");
41 constexpr char spaces[] = "\n\r\t ";
44 while (getline(in, line)) {
45 if (line.empty() || line[0] == '#') continue;
46 auto equals_pos = line.find_first_of('=');
48 auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces));
49 auto name_end = equals_pos - 1;
50 while (name_end > name_begin && isspace(line[name_end])) {
54 auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces));
55 auto value_end = line.length() - 1;
56 while (value_end > value_begin && isspace(line[value_end])) {
60 string name(line, name_begin, name_end - name_begin + 1);
61 string value(line, value_begin, value_end - value_begin + 1);
63 // if (name == "seed") {
64 // conf.gen.seed = stoul(value);
66 throw runtime_error("unknown world option: " + name);
70 throw runtime_error("IO error reading world config");
74 void WorldSave::Write(const World::Config &conf) const {
75 if (!make_dirs(root_path)) {
76 throw runtime_error("failed to create world save directory");
79 ofstream out(world_conf_path);
80 //out << "seed = " << conf.gen.seed << endl;
84 throw runtime_error("failed to write world config");
89 void WorldSave::Read(Generator::Config &conf) const {
90 ifstream in(gen_conf_path);
92 throw runtime_error("failed to open generator config");
95 constexpr char spaces[] = "\n\r\t ";
98 while (getline(in, line)) {
99 if (line.empty() || line[0] == '#') continue;
100 auto equals_pos = line.find_first_of('=');
102 auto name_begin = line.find_first_not_of(spaces, 0, sizeof(spaces));
103 auto name_end = equals_pos - 1;
104 while (name_end > name_begin && isspace(line[name_end])) {
108 auto value_begin = line.find_first_not_of(spaces, equals_pos + 1, sizeof(spaces));
109 auto value_end = line.length() - 1;
110 while (value_end > value_begin && isspace(line[value_end])) {
114 string name(line, name_begin, name_end - name_begin + 1);
115 string value(line, value_begin, value_end - value_begin + 1);
117 if (name == "seed") {
118 conf.seed = stoul(value);
120 throw runtime_error("unknown generator option: " + name);
124 throw runtime_error("IO error reading world config");
128 void WorldSave::Write(const Generator::Config &conf) const {
129 if (!make_dirs(root_path)) {
130 throw runtime_error("failed to create world save directory");
133 ofstream out(gen_conf_path);
134 out << "seed = " << conf.seed << endl;
138 throw runtime_error("failed to write generator config");
143 bool WorldSave::Exists(const Chunk::Pos &pos) const noexcept {
144 return is_file(ChunkPath(pos));
147 void WorldSave::Read(Chunk &chunk) const {
148 const char *path = ChunkPath(chunk.Position());
149 gzFile file = gzopen(path, "r");
151 throw runtime_error("failed to open chunk file");
153 if (gzread(file, chunk.BlockData(), Chunk::BlockSize()) != Chunk::BlockSize()) {
154 throw runtime_error("failed to read chunk from file");
156 if (gzclose(file) != Z_OK) {
157 throw runtime_error("failed to read chunk file");
159 chunk.InvalidateModel();
163 void WorldSave::Write(Chunk &chunk) const {
164 const char *path = ChunkPath(chunk.Position());
165 gzFile file = gzopen(path, "w");
167 // check if it's because of a missing path component
168 if (errno != ENOENT) {
170 throw runtime_error(strerror(errno));
172 string dir_path(path);
173 dir_path.erase(dir_path.find_last_of("\\/"));
174 if (!make_dirs(dir_path)) {
175 throw runtime_error("failed to create dir for chunk file");
177 file = gzopen(path, "w");
179 throw runtime_error("failed to open chunk file");
182 if (gzwrite(file, chunk.BlockData(), Chunk::BlockSize()) == 0) {
183 gzclose(file); // if this fails, it can't be helped
184 throw runtime_error("failed to write chunk to file");
186 if (gzclose(file) != Z_OK) {
187 throw runtime_error("failed to write chunk file");
193 const char *WorldSave::ChunkPath(const Chunk::Pos &pos) const {
194 snprintf(chunk_buf.get(), chunk_bufsiz, chunk_path.c_str(), pos.x, pos.y, pos.z);
195 return chunk_buf.get();