Mercurial > minori
view dep/animone/src/player.cc @ 342:adb79bdde329
dep/animone: fix tons of issues
for example, the window ID stuff was just... completely wrong. since we're
supporting multiple different window systems, it *has* to be a union rather
than just a single integer type. HWND is also not a DWORD, it's a pointer(!),
so now it's stored as a std::uintptr_t.
(this probably breaks things)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 20 Jun 2024 03:03:05 -0400 |
parents | a7d4e5107531 |
children |
line wrap: on
line source
#include "animone/player.h" #include "animone/util.h" #include <map> #include <sstream> #include <string> #include <vector> #include <optional> #include <iostream> namespace animone { namespace internal::parser { struct State { enum class Name { ExpectPlayerName, ExpectSection, ExpectWindowPlatform, ExpectExecutablePlatform, ExpectWindow, ExpectExecutable, ExpectStrategy, ExpectType, ExpectWindowTitle, }; Name state = Name::ExpectPlayerName; WindowPlatform window_platform = WindowPlatform::Unknown; ExecutablePlatform executable_platform = ExecutablePlatform::Unknown; }; 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 = [](const State::Name& state) -> size_t { switch (state) { default: case State::Name::ExpectPlayerName: return 0; case State::Name::ExpectSection: return 1; case State::Name::ExpectWindowPlatform: case State::Name::ExpectExecutablePlatform: case State::Name::ExpectStrategy: case State::Name::ExpectType: return 2; case State::Name::ExpectWindow: case State::Name::ExpectExecutable: case State::Name::ExpectWindowTitle: return 3; } }(state.state); if (current > expected) { std::cerr << "animone: excessive indentation found" << std::endl; return false; // Disallow excessive indentation } if (current < expected) { const std::optional<State::Name> st = [current, state]() -> std::optional<State::Name> { switch (current) { case 0: return State::Name::ExpectPlayerName; default: case 1: return State::Name::ExpectSection; case 2: switch (state.state) { case State::Name::ExpectWindow: return State::Name::ExpectWindowPlatform; case State::Name::ExpectExecutable: return State::Name::ExpectExecutablePlatform; default: return std::nullopt; } } }(); if (!st.has_value()) return false; switch (state.state) { case State::Name::ExpectWindow: if (players.back().windows.empty()) return false; state.state = st.value(); break; case State::Name::ExpectExecutable: if (players.back().executables.empty()) return false; state.state = st.value(); break; case State::Name::ExpectStrategy: if (players.back().strategies.empty()) return false; state.state = st.value(); break; case State::Name::ExpectType: state.state = st.value(); break; case State::Name::ExpectWindowTitle: return false; } } return true; } bool HandleState(std::string& line, std::vector<Player>& players, State& state) { switch (state.state) { case State::Name::ExpectPlayerName: players.push_back(Player()); players.back().name = line; state.state = State::Name::ExpectSection; break; case State::Name::ExpectSection: { static const std::map<std::string, State::Name> sections = { {"windows", State::Name::ExpectWindowPlatform}, {"executables", State::Name::ExpectExecutablePlatform}, {"strategies", State::Name::ExpectStrategy }, {"type", State::Name::ExpectType }, }; util::TrimRight(line, ":"); const auto it = sections.find(line); if (it == sections.end()) return false; state.state = it->second; break; } case State::Name::ExpectWindowPlatform: { static const std::map<std::string, WindowPlatform> platforms = { {"quartz", WindowPlatform::Quartz}, {"win32", WindowPlatform::Win32}, {"x11", WindowPlatform::X11}, }; util::TrimRight(line, ":"); const auto it = platforms.find(line); if (it == platforms.end()) return false; state.state = State::Name::ExpectWindow; state.window_platform = it->second; break; } case State::Name::ExpectExecutablePlatform: { static const std::map<std::string, ExecutablePlatform> platforms = { {"posix", ExecutablePlatform::Posix}, {"win32", ExecutablePlatform::Win32}, {"macosx", ExecutablePlatform::Xnu}, }; util::TrimRight(line, ":"); const auto it = platforms.find(line); if (it == platforms.end()) return false; state.state = State::Name::ExpectExecutable; state.executable_platform = it->second; break; } case State::Name::ExpectWindow: players.back().windows[state.window_platform].push_back(line); break; case State::Name::ExpectExecutable: players.back().executables[state.executable_platform].push_back(line); break; case State::Name::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 = State::Name::ExpectWindowTitle; break; } break; } case State::Name::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::Name::ExpectWindowTitle: players.back().window_title_format = line; state.state = State::Name::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; internal::parser::State state; int ln = 1; for (; std::getline(stream, line, '\n'); ln++) { 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)) { std::cerr << "animone: indentation: failed on line " << ln << std::endl; return false; } if (!internal::parser::HandleState(line, players, state)) { std::cerr << "animone: state: failed on line " << ln << std::endl; 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