diff dep/animone/src/player.cc @ 258:862d0d8619f6

*: HUUUGE changes animia has been renamed to animone, so instead of thinking of a health condition, you think of a beautiful flower :) I've also edited some of the code for animone, but I have no idea if it even works or not because I don't have a mac or windows machine lying around. whoops! ... anyway, all of the changes divergent from Anisthesia are now licensed under BSD. it's possible that I could even rewrite most of the code to where I don't even have to keep the MIT license, but that's thinking too far into the future I've been slacking off on implementing the anime seasons page, mostly out of laziness. I think I'd have to create another db file specifically for the seasons anyway, this code is being pushed *primarily* because the hard drive it's on is failing! yay :)
author Paper <paper@paper.us.eu.org>
date Mon, 01 Apr 2024 02:43:44 -0400
parents
children b1f625b0227c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/player.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,181 @@
+#include "animone/player.h"
+#include "animone/util.h"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace animone {
+
+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 animone