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