diff dep/animia/src/player.cc @ 138:28842a8d0c6b

dep/animia: huge refactor (again...) but this time, it actually compiles! and it WORKS! (on win32... not sure about other platforms...) configuring players is still not supported: at some point I'll prune something up...
author Paper <mrpapersonic@gmail.com>
date Sun, 12 Nov 2023 04:53:19 -0500
parents 69db40272acd
children d8a61e7e2a36
line wrap: on
line diff
--- a/dep/animia/src/player.cc	Fri Nov 10 13:52:47 2023 -0500
+++ b/dep/animia/src/player.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,198 @@
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "animia/player.h"
+#include "animia/util.h"
+
+namespace animia {
+
+namespace internal::parser {
+
+enum class State {
+	ExpectPlayerName,
+	ExpectSection,
+	ExpectWindow,
+	ExpectExecutable,
+	ExpectStrategy,
+	ExpectType,
+	ExpectWindowTitle,
+};
+
+size_t GetIndentation(const std::string& line) {
+	return line.find_first_not_of('\t');
+}
+
+bool HandleIndentation(const size_t current,
+                       const std::vector<Player>& players,
+                       State& state) {
+	// Each state has a definitive expected indentation
+	const auto expected = [&state]() -> size_t {
+		switch (state) {
+			default:
+			case State::ExpectPlayerName:
+				return 0;
+			case State::ExpectSection:
+				return 1;
+			case State::ExpectWindow:
+			case State::ExpectExecutable:
+			case State::ExpectStrategy:
+			case State::ExpectType:
+				return 2;
+			case State::ExpectWindowTitle:
+				return 3;
+		}
+	}();
+
+	if (current > expected)
+		return false;  // Disallow excessive indentation
+
+	if (current < expected) {
+		auto fix_state = [&]() {
+			state = !current ? State::ExpectPlayerName : State::ExpectSection;
+		};
+		switch (state) {
+			case State::ExpectWindow:
+				if (players.back().windows.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectExecutable:
+				if (players.back().executables.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectStrategy:
+				if (players.back().strategies.empty())
+					return false;
+				fix_state();
+				break;
+			case State::ExpectType:
+				fix_state();
+				break;
+			case State::ExpectWindowTitle:
+				return false;
+		}
+	}
+
+	return true;
+}
+
+bool HandleState(std::string& line, std::vector<Player>& players, State& state) {
+	switch (state) {
+		case State::ExpectPlayerName:
+			players.push_back(Player());
+			players.back().name = line;
+			state = State::ExpectSection;
+			break;
+
+		case State::ExpectSection: {
+			static const std::map<std::string, State> sections = {
+				{"windows", State::ExpectWindow},
+				{"executables", State::ExpectExecutable},
+				{"strategies", State::ExpectStrategy},
+				{"type", State::ExpectType},
+			};
+			util::TrimRight(line, ":");
+			const auto it = sections.find(line);
+			if (it == sections.end())
+				return false;
+			state = it->second;
+			break;
+		}
+
+		case State::ExpectWindow:
+			players.back().windows.push_back(line);
+			break;
+
+		case State::ExpectExecutable:
+			players.back().executables.push_back(line);
+			break;
+
+		case State::ExpectStrategy: {
+			static const std::map<std::string, Strategy> strategies = {
+				{"window_title", Strategy::WindowTitle},
+				{"open_files", Strategy::OpenFiles},
+				{"ui_automation", Strategy::UiAutomation},
+			};
+			util::TrimRight(line, ":");
+			const auto it = strategies.find(line);
+			if (it == strategies.end())
+				return false;
+			const auto strategy = it->second;
+			players.back().strategies.push_back(strategy);
+			switch (strategy) {
+				case Strategy::WindowTitle:
+					state = State::ExpectWindowTitle;
+					break;
+			}
+			break;
+		}
+
+		case State::ExpectType: {
+			static const std::map<std::string, PlayerType> types = {
+				{"default", PlayerType::Default},
+				{"web_browser", PlayerType::WebBrowser},
+			};
+			const auto it = types.find(line);
+			if (it == types.end())
+				return false;
+			players.back().type = it->second;
+			break;
+		}
+
+		case State::ExpectWindowTitle:
+			players.back().window_title_format = line;
+			state = State::ExpectStrategy;
+			break;
+	}
+
+	return true;
+}
+
+}  // namespace internal::parser
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool ParsePlayersData(const std::string& data, std::vector<Player>& players) {
+	if (data.empty())
+		return false;
+
+	std::istringstream stream(data);
+	std::string line;
+	size_t indentation = 0;
+	auto state = internal::parser::State::ExpectPlayerName;
+
+	while (std::getline(stream, line, '\n')) {
+		if (line.empty())
+			continue;  // Ignore empty lines
+
+		indentation = internal::parser::GetIndentation(line);
+
+		internal::util::TrimLeft(line, "\t");
+		internal::util::TrimRight(line, "\n\r");
+
+		if (line.empty() || line.front() == '#')
+			continue;  // Ignore empty lines and comments
+
+		if (!internal::parser::HandleIndentation(indentation, players, state))
+			return false;
+
+		if (!internal::parser::HandleState(line, players, state))
+			return false;
+	}
+
+	return !players.empty();
+}
+
+bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) {
+	std::string data;
+
+	if (!internal::util::ReadFile(path, data))
+		return false;
+
+	return ParsePlayersData(data, players);
+}
+
+}  // namespace anisthesia
\ No newline at end of file