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