aboutsummaryrefslogtreecommitdiff
path: root/src/game/RLE_reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/RLE_reader.cpp')
-rw-r--r--src/game/RLE_reader.cpp133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/game/RLE_reader.cpp b/src/game/RLE_reader.cpp
new file mode 100644
index 0000000..f971966
--- /dev/null
+++ b/src/game/RLE_reader.cpp
@@ -0,0 +1,133 @@
+#include "RLE_reader.hpp"
+
+#include "engine/data/bounds.hpp"
+#include "engine/data/vector2.hpp"
+#include "errors/RLE_reader.hpp"
+#include "util/algorithm.hpp"
+#include "util/io.hpp"
+#include "util/string.hpp"
+
+#include <ctre.hpp>
+
+#include <cctype>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <iostream>
+
+RLEReader::RLEReader(const IMatrixFactory<MatrixElement> &matrix_factory) noexcept
+ : _matrix_factory(matrix_factory)
+{
+}
+
+auto RLEReader::read_RLE_file(const std::filesystem::path &path) const
+ -> std::unique_ptr<IMatrix<MatrixElement>>
+{
+ auto content_lines = read_file_lines<std::vector<std::string>>(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 = container_find(
+ content_lines,
+ [](const std::string &line)
+ {
+ return ctre::starts_with<"x = \\d+, y = \\d+">(line);
+ // return ctre::match<"x = \\d+, y = \\d+(, rule = [a-zA-Z0-9/]+)?">(line);
+ });
+
+ if (header_line_iter == content_lines.end())
+ {
+ throw InvalidRLEFileError(path, "No header line");
+ }
+
+ const auto &header_line = *header_line_iter;
+
+ const auto header_parts = split_string<std::vector<std::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<Bounds::Value>(std::stoul(header_x_part.substr(4)));
+
+ const auto pattern_height =
+ static_cast<Bounds::Value>(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 first_pattern_line_iter = header_line_iter + 1;
+
+ for (auto pattern_line_iter = first_pattern_line_iter;
+ pattern_line_iter != content_lines.end();
+ ++pattern_line_iter)
+ {
+ if (!ctre::match<"[bo$0-9!]+">(*pattern_line_iter))
+ {
+ throw InvalidRLEFileError(path, "Invalid pattern line");
+ }
+ }
+
+ auto pattern = std::string();
+
+ for (auto pattern_line_iter = first_pattern_line_iter;
+ pattern_line_iter != content_lines.end();
+ ++pattern_line_iter)
+ {
+ pattern.append(*pattern_line_iter);
+ }
+
+ auto run_count_str = std::string();
+
+ for (const auto &character : pattern)
+ {
+ if (static_cast<bool>(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 (auto run_index = 0; run_index < run_count; run_index++)
+ {
+ 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;
+}
+