1 /*
2 Copyright © 2011-2012 Clint Bellanger
3 Copyright © 2012 Stefan Beller
4 Copyright © 2013 Henrik Andersson
5 Copyright © 2012-2015 Justin Jacobs
6
7 This file is part of FLARE.
8
9 FLARE is free software: you can redistribute it and/or modify it under the terms
10 of the GNU General Public License as published by the Free Software Foundation,
11 either version 3 of the License, or (at your option) any later version.
12
13 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 PARTICULAR PURPOSE. See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along with
18 FLARE. If not, see http://www.gnu.org/licenses/
19 */
20
21 #include "CommonIncludes.h"
22 #include "FontEngine.h"
23 #include "ItemManager.h"
24 #include "Settings.h"
25 #include "SharedResources.h"
26 #include "UtilsParsing.h"
27 #include "WidgetLabel.h"
28
29 #include <cstdlib>
30 #include <math.h>
31 #include <typeinfo>
32
trim(const std::string & s,const std::string & delimiters)33 std::string Parse::trim(const std::string& s, const std::string& delimiters) {
34 std::string tmp = s;
35 tmp.erase(tmp.find_last_not_of(delimiters) + 1); // trim right side
36 return tmp.erase(0, tmp.find_first_not_of(delimiters)); // trim left side
37 }
38
getSectionTitle(const std::string & s)39 std::string Parse::getSectionTitle(const std::string& s) {
40 size_t bracket = s.find_first_of(']');
41 if (bracket == std::string::npos) return ""; // not found
42 return s.substr(1, bracket-1);
43 }
44
getKeyPair(const std::string & s,std::string & key,std::string & val)45 void Parse::getKeyPair(const std::string& s, std::string &key, std::string &val) {
46 size_t separator = s.find_first_of('=');
47 if (separator == std::string::npos) {
48 key = "";
49 val = "";
50 return; // not found
51 }
52 key = s.substr(0, separator);
53 val = s.substr(separator+1, s.length());
54 key = trim(key);
55 val = trim(val);
56 }
57
58 // strip carriage return if exists
stripCarriageReturn(const std::string & line)59 std::string Parse::stripCarriageReturn(const std::string& line) {
60 if (line.length() > 0) {
61 if ('\r' == line.at(line.length()-1)) {
62 return line.substr(0, line.length()-1);
63 }
64 }
65 return line;
66 }
67
getLine(std::ifstream & infile)68 std::string Parse::getLine(std::ifstream &infile) {
69 std::string line;
70 // This is the standard way to check whether a read failed.
71 if (!getline(infile, line))
72 return "";
73 line = stripCarriageReturn(line);
74 return line;
75 }
76
tryParseValue(const std::type_info & type,const std::string & value,void * output)77 bool Parse::tryParseValue(const std::type_info & type, const std::string & value, void * output) {
78
79 std::stringstream stream(value);
80
81 if (type == typeid(bool)) {
82 stream >> *(static_cast<bool*>(output));
83 }
84 else if (type == typeid(int)) {
85 stream >> *(static_cast<int*>(output));
86 }
87 else if (type == typeid(unsigned int)) {
88 stream >> *(static_cast<unsigned int*>(output));
89 }
90 else if (type == typeid(short)) {
91 stream >> *(static_cast<short*>(output));
92 }
93 else if (type == typeid(unsigned short)) {
94 stream >> *(static_cast<unsigned short*>(output));
95 }
96 else if (type == typeid(char)) {
97 stream >> *(static_cast<char*>(output));
98 }
99 else if (type == typeid(unsigned char)) {
100 stream >> *(static_cast<unsigned char*>(output));
101 }
102 else if (type == typeid(float)) {
103 stream >> *(static_cast<float*>(output));
104 }
105 else if (type == typeid(std::string)) {
106 *(static_cast<std::string*>(output)) = value;
107 }
108 else {
109 Utils::logError("UtilsParsing: %s: a required type is not defined!", __FUNCTION__);
110 return false;
111 }
112
113 return !stream.fail();
114 }
115
toString(const std::type_info & type,void * value)116 std::string Parse::toString(const std::type_info & type, void * value) {
117
118 std::stringstream stream;
119
120 if (type == typeid(bool)) {
121 stream << *(static_cast<bool*>(value));
122 }
123 else if (type == typeid(int)) {
124 stream << *(static_cast<int*>(value));
125 }
126 else if (type == typeid(unsigned int)) {
127 stream << *(static_cast<unsigned int*>(value));
128 }
129 else if (type == typeid(short)) {
130 stream << *(static_cast<short*>(value));
131 }
132 else if (type == typeid(unsigned short)) {
133 stream << *(static_cast<unsigned short*>(value));
134 }
135 else if (type == typeid(char)) {
136 stream << *(static_cast<char*>(value));
137 }
138 else if (type == typeid(unsigned char)) {
139 stream << *(static_cast<unsigned char*>(value));
140 }
141 else if (type == typeid(float)) {
142 stream << *(static_cast<float*>(value));
143 }
144 else if (type == typeid(std::string)) {
145 return *(static_cast<std::string*>(value));
146 }
147 else {
148 Utils::logError("UtilsParsing: %s: a required type is not defined!", __FUNCTION__);
149 return "";
150 }
151
152 return stream.str();
153 }
154
toInt(const std::string & s,int default_value)155 int Parse::toInt(const std::string& s, int default_value) {
156 int result;
157 if (!(std::stringstream(s) >> result))
158 result = default_value;
159 return result;
160 }
161
toFloat(const std::string & s,float default_value)162 float Parse::toFloat(const std::string& s, float default_value) {
163 float result;
164 if (!(std::stringstream(s) >> result))
165 result = default_value;
166 return result;
167 }
168
toUnsignedLong(const std::string & s,unsigned long default_value)169 unsigned long Parse::toUnsignedLong(const std::string& s, unsigned long default_value) {
170 unsigned long result;
171 if (!(std::stringstream(s) >> result))
172 result = default_value;
173 return result;
174 }
175
toSizeT(const std::string & s,size_t default_value)176 size_t Parse::toSizeT(const std::string& s, size_t default_value) {
177 size_t result;
178 if (!(std::stringstream(s) >> result))
179 result = default_value;
180 return result;
181 }
182
toItemID(const std::string & s,ItemID default_value)183 ItemID Parse::toItemID(const std::string& s, ItemID default_value) {
184 return Parse::toSizeT(s, default_value);
185 }
186
toPowerID(const std::string & s,PowerID default_value)187 PowerID Parse::toPowerID(const std::string& s, PowerID default_value) {
188 return Parse::toSizeT(s, default_value);
189 }
190
toBool(std::string value)191 bool Parse::toBool(std::string value) {
192 trim(value);
193
194 std::transform(value.begin(), value.end(), value.begin(), ::tolower);
195 if (value == "true") return true;
196 if (value == "yes") return true;
197 if (value == "1") return true;
198 if (value == "false") return false;
199 if (value == "no") return false;
200 if (value == "0") return false;
201
202 Utils::logError("UtilsParsing: %s %s doesn't know how to handle %s", __FILE__, __FUNCTION__, value.c_str());
203 return false;
204 }
205
toPoint(std::string value)206 Point Parse::toPoint(std::string value) {
207 Point p;
208 p.x = popFirstInt(value);
209 p.y = popFirstInt(value);
210 return p;
211 }
212
toRect(std::string value)213 Rect Parse::toRect(std::string value) {
214 Rect r;
215 r.x = popFirstInt(value);
216 r.y = popFirstInt(value);
217 r.w = popFirstInt(value);
218 r.h = popFirstInt(value);
219 return r;
220 }
221
toRGB(std::string value)222 Color Parse::toRGB(std::string value) {
223 Color c;
224 c.r = static_cast<Uint8>(popFirstInt(value));
225 c.g = static_cast<Uint8>(popFirstInt(value));
226 c.b = static_cast<Uint8>(popFirstInt(value));
227 return c;
228 }
229
toRGBA(std::string value)230 Color Parse::toRGBA(std::string value) {
231 Color c;
232 c.r = static_cast<Uint8>(popFirstInt(value));
233 c.g = static_cast<Uint8>(popFirstInt(value));
234 c.b = static_cast<Uint8>(popFirstInt(value));
235 c.a = static_cast<Uint8>(popFirstInt(value));
236 return c;
237 }
238
239 /**
240 * Parse a duration string and return duration in frames.
241 */
toDuration(const std::string & s)242 int Parse::toDuration(const std::string& s) {
243 int val = 0;
244 std::string suffix = "";
245 std::stringstream ss;
246 ss.str(s);
247 ss >> val;
248 ss >> suffix;
249
250 if (val == 0)
251 return val;
252 else if (suffix == "s")
253 val *= settings->max_frames_per_sec;
254 else {
255 if (suffix != "ms")
256 Utils::logError("UtilsParsing: Duration of '%d' does not have a suffix. Assuming 'ms'.", val);
257 val = static_cast<int>(floorf((static_cast<float>(val * settings->max_frames_per_sec) / 1000.f) + 0.5f));
258 }
259
260 // round back up to 1 if we rounded down to 0 for ms
261 if (val < 1) val = 1;
262
263 return val;
264 }
265
toDirection(const std::string & s)266 int Parse::toDirection(const std::string& s) {
267 int dir;
268
269 if (s == "N")
270 dir = 3;
271 else if (s == "NE")
272 dir = 4;
273 else if (s == "E")
274 dir = 5;
275 else if (s == "SE")
276 dir = 6;
277 else if (s == "S")
278 dir = 7;
279 else if (s == "SW")
280 dir = 0;
281 else if (s == "W")
282 dir = 1;
283 else if (s == "NW")
284 dir = 2;
285 else {
286 dir = Parse::toInt(s);
287 if (dir < 0 || dir > 7) {
288 Utils::logError("UtilsParsing: Direction '%d' is not within range 0-7.");
289 dir = 0;
290 }
291 }
292
293 return dir;
294 }
295
toAlignment(const std::string & s,int default_value)296 int Parse::toAlignment(const std::string &s, int default_value) {
297 int align = default_value;
298
299 if (s == "topleft")
300 align = Utils::ALIGN_TOPLEFT;
301 else if (s == "top")
302 align = Utils::ALIGN_TOP;
303 else if (s == "topright")
304 align = Utils::ALIGN_TOPRIGHT;
305 else if (s == "left")
306 align = Utils::ALIGN_LEFT;
307 else if (s == "center")
308 align = Utils::ALIGN_CENTER;
309 else if (s == "right")
310 align = Utils::ALIGN_RIGHT;
311 else if (s == "bottomleft")
312 align = Utils::ALIGN_BOTTOMLEFT;
313 else if (s == "bottom")
314 align = Utils::ALIGN_BOTTOM;
315 else if (s == "bottomright")
316 align = Utils::ALIGN_BOTTOMRIGHT;
317 else if (s == "frame_topleft")
318 align = Utils::ALIGN_FRAME_TOPLEFT;
319 else if (s == "frame_top")
320 align = Utils::ALIGN_FRAME_TOP;
321 else if (s == "frame_topright")
322 align = Utils::ALIGN_FRAME_TOPRIGHT;
323 else if (s == "frame_left")
324 align = Utils::ALIGN_FRAME_LEFT;
325 else if (s == "frame_center")
326 align = Utils::ALIGN_FRAME_CENTER;
327 else if (s == "frame_right")
328 align = Utils::ALIGN_FRAME_RIGHT;
329 else if (s == "frame_bottomleft")
330 align = Utils::ALIGN_FRAME_BOTTOMLEFT;
331 else if (s == "frame_bottom")
332 align = Utils::ALIGN_FRAME_BOTTOM;
333 else if (s == "frame_bottomright")
334 align = Utils::ALIGN_FRAME_BOTTOMRIGHT;
335
336 return align;
337 }
338
339 /**
340 * Given a string that starts with a decimal number then a comma
341 * Return that int, and modify the string to remove the num and comma
342 *
343 * This is basically a really lazy "split" replacement
344 */
popFirstInt(std::string & s,char separator)345 int Parse::popFirstInt(std::string &s, char separator) {
346 return Parse::toInt(popFirstString(s, separator));
347 }
348
popFirstString(std::string & s,char separator)349 std::string Parse::popFirstString(std::string &s, char separator) {
350 std::string outs = "";
351 size_t seppos;
352
353 if (separator == 0) {
354 seppos = s.find_first_of(',');
355 size_t alt_seppos = s.find_first_of(';');
356
357 if (alt_seppos != std::string::npos && alt_seppos < seppos) {
358 seppos = alt_seppos; // return the first ',' or ';'
359 }
360 }
361 else {
362 seppos = s.find_first_of(separator);
363 }
364
365 if (seppos == std::string::npos) {
366 outs = s;
367 s = "";
368 }
369 else {
370 outs = s.substr(0, seppos);
371 s = s.substr(seppos+1, s.length());
372 }
373 return outs;
374 }
375
popLabelInfo(std::string val)376 LabelInfo Parse::popLabelInfo(std::string val) {
377 LabelInfo info;
378 std::string justify,valign,style;
379
380 std::string tmp = popFirstString(val);
381 if (tmp == "hidden") {
382 info.hidden = true;
383 }
384 else {
385 info.hidden = false;
386 info.x = Parse::toInt(tmp);
387 info.y = popFirstInt(val);
388 justify = popFirstString(val);
389 valign = popFirstString(val);
390 style = popFirstString(val);
391
392 if (justify == "left") info.justify = FontEngine::JUSTIFY_LEFT;
393 else if (justify == "center") info.justify = FontEngine::JUSTIFY_CENTER;
394 else if (justify == "right") info.justify = FontEngine::JUSTIFY_RIGHT;
395
396 if (valign == "top") info.valign = LabelInfo::VALIGN_TOP;
397 else if (valign == "center") info.valign = LabelInfo::VALIGN_CENTER;
398 else if (valign == "bottom") info.valign = LabelInfo::VALIGN_BOTTOM;
399
400 if (style != "") info.font_style = style;
401 }
402
403 return info;
404 }
405
toItemQuantityPair(std::string value,bool * check_pair)406 ItemStack Parse::toItemQuantityPair(std::string value, bool* check_pair) {
407 ItemStack r;
408
409 if (check_pair) {
410 *check_pair = (value.find_first_of(':') != std::string::npos);
411 }
412
413 value += ':';
414 r.item = toItemID(popFirstString(value, ':'));
415 r.quantity = popFirstInt(value, ':');
416
417 // quantity is always >= 1
418 if (r.quantity == 0)
419 r.quantity = 1;
420
421 return r;
422 }
423