aboutsummaryrefslogtreecommitdiff
path: root/src/game/RLE_reader.cpp
blob: df2823c5078579452a30a5fde1c33a0038410ee4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "RLE_reader.hpp"

#include <ctre.hpp>
#include <cctype>
#include <string>
#include <vector>

#include "engine/data/bounds.hpp"
#include "engine/data/vector2.hpp"
#include "errors/RLE_reader.hpp"
#include "util/algorithm_impl.hpp"
#include "util/io_impl.hpp"
#include "util/string_impl.hpp"

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;
}