#include "RLE_reader.hpp" #include #include #include #include #include #include #include #include #include #include #include "engine/data/bounds.hpp" #include "engine/data/vector2.hpp" #include "errors/RLE_reader.hpp" #include "util/io_impl.hpp" #include "util/string_impl.hpp" RLEReader::RLEReader(const IMatrixFactory &matrix_factory) noexcept : _matrix_factory(matrix_factory) { } auto RLEReader::read_RLE_file(const std::filesystem::path &path) const -> std::unique_ptr> { auto content_lines = read_file_lines>(path); if (content_lines.empty()) { throw InvalidRLEFileError(path, "File is empty"); } if (content_lines.back().empty()) { content_lines.pop_back(); } const auto header_line_iter = ranges::find_if( content_lines, [](const std::string &line) { return ctre::starts_with<"x = \\d+, y = \\d+">(line); }); if (header_line_iter == ranges::end(content_lines)) { throw InvalidRLEFileError(path, "No header line"); } const auto &header_line = *header_line_iter; const auto header_parts = split_string>(header_line, ','); const auto &header_x_part = header_parts[0]; const auto &header_y_part = header_parts[1].substr(1); const auto pattern_width = static_cast(std::stoul(header_x_part.substr(4))); const auto pattern_height = static_cast(std::stoul(header_y_part.substr(4))); const auto pattern_size = Bounds({.width = pattern_width, .height = pattern_height}); auto pattern_matrix = _matrix_factory(pattern_size); auto pattern_pos = Vector2({.x = 0, .y = 0}); const auto header_line_index = static_cast(header_line_iter - content_lines.begin()); const auto pattern_lines = content_lines | ranges::views::drop(header_line_index + 1) | ranges::to(); for (const auto &pattern_line : pattern_lines) { if (!ctre::match<"[bo$0-9!]+">(pattern_line)) { throw InvalidRLEFileError(path, "Invalid pattern line"); } } auto pattern = ranges::fold_left( pattern_lines, std::string(), [](const std::string &acc, const std::string &line) { return acc + line; }); auto run_count_str = std::string(); for (const auto &character : pattern) { if (static_cast(isdigit(character))) { run_count_str += character; continue; } if (character == '!') { break; } const auto run_count = run_count_str.empty() ? 1 : std::stoi(run_count_str); run_count_str.clear(); if (character == '$') { pattern_pos.set_x(0); pattern_pos += Vector2({.x = 0, .y = run_count}); continue; } for (const auto &run_index : ranges::views::iota(0, run_count)) { if (pattern_size.validate_coords(pattern_pos) != CoordsValidation::VALID) { throw InvalidRLEFileError(path, "Pattern goes out of bounds"); } pattern_matrix->set(pattern_pos, character == 'o' ? 'x' : ' '); pattern_pos += Vector2::right(); } } return pattern_matrix; }