feat(editor): writer surgical del bloc data: al YAML del tilemap
This commit is contained in:
1 parent
459d18b49c
commit
6d8423764d
4 files changed
+345
No files matched your search
@@ -0,0 +1,118 @@
|
||||
#include "game/map/tilemap_writer.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace TilemapWriter {
|
||||
|
||||
namespace {
|
||||
|
||||
// Retorna la posició de l'inici de la línia que conté "data:" a
|
||||
// columna 0. La detecció requereix que els 5 caràcters siguin al
|
||||
// principi d'una línia i que el següent siga un terminador típic
|
||||
// (fi de línia, espai, tabulador o comentari). Així evitem trigger
|
||||
// amb claus com "data_version:" o valors escalars que comencin
|
||||
// amb "data:".
|
||||
auto findDataLine(const std::string& content) -> std::size_t {
|
||||
std::size_t pos = 0;
|
||||
while (pos < content.size()) {
|
||||
const bool AT_LINE_START = (pos == 0 || content[pos - 1] == '\n');
|
||||
if (AT_LINE_START && content.compare(pos, 5, "data:") == 0) {
|
||||
const std::size_t AFTER = pos + 5;
|
||||
if (AFTER >= content.size() || content[AFTER] == '\n' || content[AFTER] == '\r' || content[AFTER] == ' ' || content[AFTER] == '\t' || content[AFTER] == '#') {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
const auto NL = content.find('\n', pos);
|
||||
if (NL == std::string::npos) { break; }
|
||||
pos = NL + 1;
|
||||
}
|
||||
return std::string::npos;
|
||||
}
|
||||
|
||||
// Avança mentre trobe línies indentades (espai/tab). S'atura al
|
||||
// primer caràcter a columna 0 (nova clau top-level) o davant una
|
||||
// línia buida (separadors de seccions). Així els comentaris i el
|
||||
// blanc que precedeixen a la pròxima secció queden fora del bloc.
|
||||
auto advancePastIndented(const std::string& content, std::size_t pos) -> std::size_t {
|
||||
while (pos < content.size()) {
|
||||
const char C = content[pos];
|
||||
if (C != ' ' && C != '\t') { break; }
|
||||
const auto NL = content.find('\n', pos);
|
||||
pos = (NL == std::string::npos) ? content.size() : NL + 1;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
auto buildBlock(const std::vector<std::vector<int>>& data) -> std::string {
|
||||
int max_width = 1;
|
||||
for (const auto& row : data) {
|
||||
for (const int V : row) {
|
||||
const int W = static_cast<int>(std::to_string(V).size());
|
||||
max_width = std::max(max_width, W);
|
||||
}
|
||||
}
|
||||
|
||||
std::string out;
|
||||
for (const auto& row : data) {
|
||||
out += " - [";
|
||||
for (std::size_t i = 0; i < row.size(); ++i) {
|
||||
if (i > 0) { out += ","; }
|
||||
const auto S = std::to_string(row[i]);
|
||||
const int PAD = max_width - static_cast<int>(S.size());
|
||||
out.append(static_cast<std::size_t>(PAD), ' ');
|
||||
out += S;
|
||||
}
|
||||
out += "]\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto saveDataBlock(const std::string& path, const std::vector<std::vector<int>>& data) -> bool {
|
||||
std::ifstream in(path, std::ios::binary);
|
||||
if (!in) { return false; }
|
||||
std::ostringstream ss;
|
||||
ss << in.rdbuf();
|
||||
const std::string CONTENT = ss.str();
|
||||
in.close();
|
||||
|
||||
const auto DATA_POS = findDataLine(CONTENT);
|
||||
if (DATA_POS == std::string::npos) { return false; }
|
||||
|
||||
const auto LINE_END = CONTENT.find('\n', DATA_POS);
|
||||
const auto AFTER_DATA = (LINE_END == std::string::npos) ? CONTENT.size() : LINE_END + 1;
|
||||
const auto BLOCK_END = advancePastIndented(CONTENT, AFTER_DATA);
|
||||
|
||||
const std::string NEW_CONTENT =
|
||||
CONTENT.substr(0, AFTER_DATA) + buildBlock(data) + CONTENT.substr(BLOCK_END);
|
||||
|
||||
const auto TMP_PATH = path + ".tmp";
|
||||
{
|
||||
std::ofstream out(TMP_PATH, std::ios::binary | std::ios::trunc);
|
||||
if (!out) { return false; }
|
||||
out.write(NEW_CONTENT.data(), static_cast<std::streamsize>(NEW_CONTENT.size()));
|
||||
out.close();
|
||||
if (!out) {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(TMP_PATH, ec);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::rename(TMP_PATH, path, ec);
|
||||
if (ec) {
|
||||
std::filesystem::remove(TMP_PATH, ec);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TilemapWriter
|
||||
Reference in new issue
Block a user