Mercurial > minori
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 137:69db40272acd | 138:28842a8d0c6b |
|---|---|
| 1 #include <map> | |
| 2 #include <sstream> | |
| 3 #include <string> | |
| 4 #include <vector> | |
| 5 | |
| 6 #include "animia/player.h" | |
| 7 #include "animia/util.h" | |
| 8 | |
| 9 namespace animia { | |
| 10 | |
| 11 namespace internal::parser { | |
| 12 | |
| 13 enum class State { | |
| 14 ExpectPlayerName, | |
| 15 ExpectSection, | |
| 16 ExpectWindow, | |
| 17 ExpectExecutable, | |
| 18 ExpectStrategy, | |
| 19 ExpectType, | |
| 20 ExpectWindowTitle, | |
| 21 }; | |
| 22 | |
| 23 size_t GetIndentation(const std::string& line) { | |
| 24 return line.find_first_not_of('\t'); | |
| 25 } | |
| 26 | |
| 27 bool HandleIndentation(const size_t current, | |
| 28 const std::vector<Player>& players, | |
| 29 State& state) { | |
| 30 // Each state has a definitive expected indentation | |
| 31 const auto expected = [&state]() -> size_t { | |
| 32 switch (state) { | |
| 33 default: | |
| 34 case State::ExpectPlayerName: | |
| 35 return 0; | |
| 36 case State::ExpectSection: | |
| 37 return 1; | |
| 38 case State::ExpectWindow: | |
| 39 case State::ExpectExecutable: | |
| 40 case State::ExpectStrategy: | |
| 41 case State::ExpectType: | |
| 42 return 2; | |
| 43 case State::ExpectWindowTitle: | |
| 44 return 3; | |
| 45 } | |
| 46 }(); | |
| 47 | |
| 48 if (current > expected) | |
| 49 return false; // Disallow excessive indentation | |
| 50 | |
| 51 if (current < expected) { | |
| 52 auto fix_state = [&]() { | |
| 53 state = !current ? State::ExpectPlayerName : State::ExpectSection; | |
| 54 }; | |
| 55 switch (state) { | |
| 56 case State::ExpectWindow: | |
| 57 if (players.back().windows.empty()) | |
| 58 return false; | |
| 59 fix_state(); | |
| 60 break; | |
| 61 case State::ExpectExecutable: | |
| 62 if (players.back().executables.empty()) | |
| 63 return false; | |
| 64 fix_state(); | |
| 65 break; | |
| 66 case State::ExpectStrategy: | |
| 67 if (players.back().strategies.empty()) | |
| 68 return false; | |
| 69 fix_state(); | |
| 70 break; | |
| 71 case State::ExpectType: | |
| 72 fix_state(); | |
| 73 break; | |
| 74 case State::ExpectWindowTitle: | |
| 75 return false; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 return true; | |
| 80 } | |
| 81 | |
| 82 bool HandleState(std::string& line, std::vector<Player>& players, State& state) { | |
| 83 switch (state) { | |
| 84 case State::ExpectPlayerName: | |
| 85 players.push_back(Player()); | |
| 86 players.back().name = line; | |
| 87 state = State::ExpectSection; | |
| 88 break; | |
| 89 | |
| 90 case State::ExpectSection: { | |
| 91 static const std::map<std::string, State> sections = { | |
| 92 {"windows", State::ExpectWindow}, | |
| 93 {"executables", State::ExpectExecutable}, | |
| 94 {"strategies", State::ExpectStrategy}, | |
| 95 {"type", State::ExpectType}, | |
| 96 }; | |
| 97 util::TrimRight(line, ":"); | |
| 98 const auto it = sections.find(line); | |
| 99 if (it == sections.end()) | |
| 100 return false; | |
| 101 state = it->second; | |
| 102 break; | |
| 103 } | |
| 104 | |
| 105 case State::ExpectWindow: | |
| 106 players.back().windows.push_back(line); | |
| 107 break; | |
| 108 | |
| 109 case State::ExpectExecutable: | |
| 110 players.back().executables.push_back(line); | |
| 111 break; | |
| 112 | |
| 113 case State::ExpectStrategy: { | |
| 114 static const std::map<std::string, Strategy> strategies = { | |
| 115 {"window_title", Strategy::WindowTitle}, | |
| 116 {"open_files", Strategy::OpenFiles}, | |
| 117 {"ui_automation", Strategy::UiAutomation}, | |
| 118 }; | |
| 119 util::TrimRight(line, ":"); | |
| 120 const auto it = strategies.find(line); | |
| 121 if (it == strategies.end()) | |
| 122 return false; | |
| 123 const auto strategy = it->second; | |
| 124 players.back().strategies.push_back(strategy); | |
| 125 switch (strategy) { | |
| 126 case Strategy::WindowTitle: | |
| 127 state = State::ExpectWindowTitle; | |
| 128 break; | |
| 129 } | |
| 130 break; | |
| 131 } | |
| 132 | |
| 133 case State::ExpectType: { | |
| 134 static const std::map<std::string, PlayerType> types = { | |
| 135 {"default", PlayerType::Default}, | |
| 136 {"web_browser", PlayerType::WebBrowser}, | |
| 137 }; | |
| 138 const auto it = types.find(line); | |
| 139 if (it == types.end()) | |
| 140 return false; | |
| 141 players.back().type = it->second; | |
| 142 break; | |
| 143 } | |
| 144 | |
| 145 case State::ExpectWindowTitle: | |
| 146 players.back().window_title_format = line; | |
| 147 state = State::ExpectStrategy; | |
| 148 break; | |
| 149 } | |
| 150 | |
| 151 return true; | |
| 152 } | |
| 153 | |
| 154 } // namespace internal::parser | |
| 155 | |
| 156 //////////////////////////////////////////////////////////////////////////////// | |
| 157 | |
| 158 bool ParsePlayersData(const std::string& data, std::vector<Player>& players) { | |
| 159 if (data.empty()) | |
| 160 return false; | |
| 161 | |
| 162 std::istringstream stream(data); | |
| 163 std::string line; | |
| 164 size_t indentation = 0; | |
| 165 auto state = internal::parser::State::ExpectPlayerName; | |
| 166 | |
| 167 while (std::getline(stream, line, '\n')) { | |
| 168 if (line.empty()) | |
| 169 continue; // Ignore empty lines | |
| 170 | |
| 171 indentation = internal::parser::GetIndentation(line); | |
| 172 | |
| 173 internal::util::TrimLeft(line, "\t"); | |
| 174 internal::util::TrimRight(line, "\n\r"); | |
| 175 | |
| 176 if (line.empty() || line.front() == '#') | |
| 177 continue; // Ignore empty lines and comments | |
| 178 | |
| 179 if (!internal::parser::HandleIndentation(indentation, players, state)) | |
| 180 return false; | |
| 181 | |
| 182 if (!internal::parser::HandleState(line, players, state)) | |
| 183 return false; | |
| 184 } | |
| 185 | |
| 186 return !players.empty(); | |
| 187 } | |
| 188 | |
| 189 bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) { | |
| 190 std::string data; | |
| 191 | |
| 192 if (!internal::util::ReadFile(path, data)) | |
| 193 return false; | |
| 194 | |
| 195 return ParsePlayersData(data, players); | |
| 196 } | |
| 197 | |
| 198 } // namespace anisthesia |
