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