Mercurial > minori
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