#pragma once // ----------------------------------------------------------------------------- // Разбор строчных текстовых форматов движка: каталог сущностей и файлы комнат. // // Свой микро-парсер, а не YAML/JSON: оба формата — это «слово и несколько // значений в строке», и внешняя библиотека ради них тянула бы в движок // зависимость тяжелее всего остального движка вместе взятого. Оба файла // правятся руками и лежат в git, поэтому формат обязан быть построчным и // diff-читаемым — это требование к формату, а не следствие лени. // ----------------------------------------------------------------------------- #include #include #include namespace text { // Комментарий — от '#' до конца строки. Внутри значений '#' не встречается: // в тайловой сетке комнаты решётка означает стену, но сетка читается целыми // строками и через этот разбор не проходит. inline std::string StripComment(const std::string& line) { const size_t hash = line.find('#'); return (hash == std::string::npos) ? line : line.substr(0, hash); } inline std::string Trim(const std::string& s) { size_t a = 0; size_t b = s.size(); while (a < b && (s[a] == ' ' || s[a] == '\t' || s[a] == '\r')) ++a; while (b > a && (s[b - 1] == ' ' || s[b - 1] == '\t' || s[b - 1] == '\r')) --b; return s.substr(a, b - a); } // Разбить по пробелам. Пустых лексем не возвращает. inline std::vector Split(const std::string& s) { std::vector out; size_t i = 0; while (i < s.size()) { while (i < s.size() && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r')) ++i; const size_t start = i; while (i < s.size() && s[i] != ' ' && s[i] != '\t' && s[i] != '\r') ++i; if (i > start) out.push_back(s.substr(start, i - start)); } return out; } // Разбить надвое по разделителю. false — разделителя нет. // // Разделителей два, и они РАЗНЫЕ намеренно: `ключ=значение` — это свойство // записи (каталог сущностей), `имя:вес` — элемент таблицы (веса точки спавна). // Один символ на оба случая читался бы одинаково в двух разных ролях, и // таблица весов внутри строки свойств стала бы неразличима. inline bool SplitAt(const std::string& s, char sep, std::string& key, std::string& value) { const size_t at = s.find(sep); if (at == std::string::npos) return false; key = s.substr(0, at); value = s.substr(at + 1); return true; } // `имя:вес` — элемент таблицы. inline bool SplitPair(const std::string& s, std::string& key, std::string& value) { return SplitAt(s, ':', key, value); } // `ключ=значение` — свойство записи. inline bool SplitKeyValue(const std::string& s, std::string& key, std::string& value) { return SplitAt(s, '=', key, value); } inline int ToInt(const std::string& s, int fallback = 0) { if (s.empty()) return fallback; char* end = nullptr; const long v = std::strtol(s.c_str(), &end, 10); return (end && *end == '\0') ? int(v) : fallback; } inline float ToFloat(const std::string& s, float fallback = 0.0f) { if (s.empty()) return fallback; char* end = nullptr; const double v = std::strtod(s.c_str(), &end); return (end && *end == '\0') ? float(v) : fallback; } // Склеить хвост лексем обратно в строку — для полей, где значение с пробелами // (подпись в палитре, теги комнаты). inline std::string JoinFrom(const std::vector& parts, size_t first) { std::string out; for (size_t i = first; i < parts.size(); ++i) { if (!out.empty()) out += ' '; out += parts[i]; } return out; } } // namespace text