1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #include "utils.h"
5 #include "DateTime.h"
6 #include "FileSystem.h"
7 #include "Lang.h"
8 #include "StringF.h"
9 #include "gameconsts.h"
10 #include "graphics/Graphics.h"
11 #include "gui/Gui.h"
12 #include "libs.h"
13 #include <cmath>
14 #include <cstdio>
15 #include <fstream>
16 #include <sstream>
17 
format_money(double cents,bool showCents)18 std::string format_money(double cents, bool showCents)
19 {
20 	char *end; // for  error checking
21 	size_t groupDigits = strtol(Lang::NUMBER_GROUP_NUM, &end, 10);
22 	assert(*end == 0);
23 
24 	double money = showCents ? 0.01 * cents : roundf(0.01 * cents);
25 
26 	const char *format = (money < 0) ? "-$%.2f" : "$%.2f";
27 	char buf[64];
28 	snprintf(buf, sizeof(buf), format, fabs(money));
29 	std::string result(buf);
30 
31 	size_t pos = result.find_first_of('.'); // pos to decimal point
32 
33 	if (showCents) // replace decimal point
34 		result.replace(pos, 1, Lang::NUMBER_DECIMAL_POINT);
35 	else // or just remove frac. part
36 		result.erase(result.begin() + pos, result.end());
37 
38 	size_t groupMin = strtol(Lang::NUMBER_GROUP_MIN, &end, 10);
39 	assert(*end == 0);
40 
41 	if (groupDigits != 0 && fabs(money) >= groupMin) {
42 
43 		std::string groupSep = std::string(Lang::NUMBER_GROUP_SEP) == " " ?
44 			"\u00a0" :
45 			Lang::NUMBER_GROUP_SEP; // space should be fixed space
46 
47 		size_t skip = (money < 0) ? 2 : 1; // compensate for "$" or "-$"
48 		while (pos - skip > groupDigits) { // insert thousand seperator
49 			pos = pos - groupDigits;
50 			result.insert(pos, groupSep);
51 		}
52 	}
53 	return result;
54 }
55 
56 static const char *const MONTH_NAMES[] = {
57 	Lang::MONTH_JAN,
58 	Lang::MONTH_FEB,
59 	Lang::MONTH_MAR,
60 	Lang::MONTH_APR,
61 	Lang::MONTH_MAY,
62 	Lang::MONTH_JUN,
63 	Lang::MONTH_JUL,
64 	Lang::MONTH_AUG,
65 	Lang::MONTH_SEP,
66 	Lang::MONTH_OCT,
67 	Lang::MONTH_NOV,
68 	Lang::MONTH_DEC
69 };
70 
format_date(double t)71 std::string format_date(double t)
72 {
73 	const Time::DateTime dt(t);
74 	int year, month, day, hour, minute, second;
75 	dt.GetDateParts(&year, &month, &day);
76 	dt.GetTimeParts(&hour, &minute, &second);
77 
78 	char buf[32];
79 	snprintf(buf, sizeof(buf), "%02d:%02d:%02d %d %s %d",
80 		hour, minute, second, day, MONTH_NAMES[month - 1], year);
81 	return buf;
82 }
83 
format_date_only(double t)84 std::string format_date_only(double t)
85 {
86 	const Time::DateTime dt(t);
87 	int year, month, day;
88 	dt.GetDateParts(&year, &month, &day);
89 
90 	char buf[16];
91 	snprintf(buf, sizeof(buf), "%d %s %d", day, MONTH_NAMES[month - 1], year);
92 	return buf;
93 }
94 
string_join(std::vector<std::string> & v,std::string sep)95 std::string string_join(std::vector<std::string> &v, std::string sep)
96 {
97 	std::vector<std::string>::iterator i = v.begin();
98 	std::string out;
99 
100 	while (i != v.end()) {
101 		out += *i;
102 		++i;
103 		if (i != v.end()) out += sep;
104 	}
105 	return out;
106 }
107 
format_duration(double seconds)108 std::string format_duration(double seconds)
109 {
110 	std::ostringstream ss;
111 	int duration = seconds;
112 	int secs = duration % 60;
113 	int minutes = (duration / 60) % 60;
114 	int hours = (duration / 60 / 60) % 24;
115 	int days = (duration / 60 / 60 / 24) % 7;
116 	int weeks = (duration / 60 / 60 / 24 / 7);
117 	if (weeks != 0)
118 		ss << weeks << Lang::UNIT_WEEKS;
119 	if (days != 0)
120 		ss << days << Lang::UNIT_DAYS;
121 	if (hours != 0)
122 		ss << hours << Lang::UNIT_HOURS;
123 	if (minutes != 0)
124 		ss << minutes << Lang::UNIT_MINUTES;
125 	// do not show seconds unless the largest unit shown is minutes
126 	if (weeks == 0 && days == 0 && hours == 0)
127 		if (minutes == 0 || secs != 0)
128 			ss << secs << Lang::UNIT_SECONDS;
129 	return ss.str();
130 }
131 
format_distance(double dist,int precision)132 std::string format_distance(double dist, int precision)
133 {
134 	std::ostringstream ss;
135 	ss.setf(std::ios::fixed, std::ios::floatfield);
136 	if (dist < 1e3) {
137 		ss.precision(0);
138 		ss << dist << " m";
139 	} else {
140 		const float LY = 9.4607e15f;
141 		ss.precision(precision);
142 
143 		if (dist < 1e6)
144 			ss << (dist * 1e-3) << " km";
145 		else if (dist < AU * 0.01)
146 			ss << (dist * 1e-6) << " Mm";
147 		else if (dist < LY * 0.1)
148 			ss << (dist / AU) << " " << Lang::UNIT_AU;
149 		else
150 			ss << (dist / LY) << " " << Lang::UNIT_LY;
151 	}
152 	return ss.str();
153 }
154 
155 // strcasestr() adapted from gnulib
156 // (c) 2005 FSF. GPL2+
157 
158 #define TOLOWER(c) (isupper(static_cast<unsigned char>(c)) ? tolower(static_cast<unsigned char>(c)) : (static_cast<unsigned char>(c)))
159 
pi_strcasestr(const char * haystack,const char * needle)160 const char *pi_strcasestr(const char *haystack, const char *needle)
161 {
162 	if (!*needle)
163 		return haystack;
164 
165 	// cache the first character for speed
166 	char b = TOLOWER(*needle);
167 
168 	needle++;
169 	for (;; haystack++) {
170 		if (!*haystack)
171 			return 0;
172 
173 		if (TOLOWER(*haystack) == b) {
174 			const char *rhaystack = haystack + 1;
175 			const char *rneedle = needle;
176 
177 			for (;; rhaystack++, rneedle++) {
178 				if (!*rneedle)
179 					return haystack;
180 
181 				if (!*rhaystack)
182 					return 0;
183 
184 				if (TOLOWER(*rhaystack) != TOLOWER(*rneedle))
185 					break;
186 			}
187 		}
188 	}
189 }
190 
SplitString(const std::string & source,const std::string & delim)191 std::vector<std::string> SplitString(const std::string &source, const std::string &delim)
192 {
193 	bool stringSplitted = false;
194 	std::vector<std::string> splitted;
195 
196 	size_t startPos = 0;
197 	do {
198 		// try to find delim
199 		size_t delimPos = source.find(delim, startPos);
200 
201 		// if delim found
202 		if (delimPos != std::string::npos) {
203 			std::string element = source.substr(startPos, delimPos);
204 			splitted.push_back(element);
205 
206 			// prepare next loop
207 			startPos = delimPos + delim.length();
208 		} else {
209 			// push tail and exit
210 			splitted.push_back(source.substr(startPos));
211 			stringSplitted = true;
212 		}
213 
214 	} while (!stringSplitted);
215 
216 	return splitted;
217 }
218 
219 //#define USE_HEX_FLOATS
220 #ifndef USE_HEX_FLOATS
221 union fu32 {
fu32()222 	fu32() {}
fu32(float fIn)223 	fu32(float fIn) :
224 		f(fIn) {}
fu32(uint32_t uIn)225 	fu32(uint32_t uIn) :
226 		u(uIn) {}
227 	float f;
228 	uint32_t u;
229 };
230 union fu64 {
fu64()231 	fu64() {}
fu64(double dIn)232 	fu64(double dIn) :
233 		d(dIn) {}
fu64(uint64_t uIn)234 	fu64(uint64_t uIn) :
235 		u(uIn) {}
236 	double d;
237 	uint64_t u;
238 };
239 #endif // USE_HEX_FLOATS
240 
FloatToStr(float val)241 std::string FloatToStr(float val)
242 {
243 	PROFILE_SCOPED()
244 #ifdef USE_HEX_FLOATS
245 	char hex[32]; // Probably don't need such a large char array.
246 	std::sprintf(hex, "%a", val);
247 	return hex;
248 #else
249 	// Exact representation (but not human readable).
250 	static_assert(sizeof(float) == 4, "float isn't 4bytes");
251 	fu32 uval(val);
252 	char str[64];
253 	SDL_itoa(uval.u, str, 10);
254 	return str;
255 #endif
256 }
257 
DoubleToStr(double val)258 std::string DoubleToStr(double val)
259 {
260 	PROFILE_SCOPED()
261 #ifdef USE_HEX_FLOATS
262 	char hex[64]; // Probably don't need such a large char array.
263 	std::sprintf(hex, "%la", val);
264 	return hex;
265 #else
266 	// Exact representation (but not human readable).
267 	static_assert(sizeof(double) == 8, "double isn't 8 bytes");
268 	fu64 uval(val);
269 	char str[128];
270 	SDL_ulltoa(uval.u, str, 10);
271 	return str;
272 #endif
273 }
274 
Vector3fToStr(const vector3f & val,char * out,size_t size)275 void Vector3fToStr(const vector3f &val, char *out, size_t size)
276 {
277 	PROFILE_SCOPED()
278 	static_assert(sizeof(vector3f) == 12, "vector3f isn't 12 bytes");
279 #ifdef USE_HEX_FLOATS
280 	const int amt = std::sprintf(out, "%a,%a,%a", val.x, val.y, val.z);
281 	assert(static_cast<size_t>(amt) <= size);
282 	(void)amt;
283 #else
284 	fu32 a(val.x);
285 	fu32 b(val.y);
286 	fu32 c(val.z);
287 	const int amt = sprintf(out, "(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")", a.u, b.u, c.u);
288 	assert(static_cast<size_t>(amt) <= size);
289 	(void)amt;
290 #endif
291 }
292 
Vector3dToStr(const vector3d & val,char * out,size_t size)293 void Vector3dToStr(const vector3d &val, char *out, size_t size)
294 {
295 	PROFILE_SCOPED()
296 	static_assert(sizeof(vector3d) == 24, "vector3d isn't 24 bytes");
297 #ifdef USE_HEX_FLOATS
298 	const int amt = std::sprintf(out, "%la,%la,%la", val.x, val.y, val.z);
299 	assert(static_cast<size_t>(amt) <= size);
300 	(void)amt;
301 #else
302 	fu64 a(val.x);
303 	fu64 b(val.y);
304 	fu64 c(val.z);
305 	const int amt = sprintf(out, "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")", a.u, b.u, c.u);
306 	assert(static_cast<size_t>(amt) <= size);
307 	(void)amt;
308 #endif
309 }
310 
Matrix3x3fToStr(const matrix3x3f & val,char * out,size_t size)311 void Matrix3x3fToStr(const matrix3x3f &val, char *out, size_t size)
312 {
313 	PROFILE_SCOPED()
314 	static_assert(sizeof(matrix3x3f) == 36, "matrix3x3f isn't 36 bytes");
315 #ifdef USE_HEX_FLOATS
316 	const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8]);
317 	assert(static_cast<size_t>(amt) <= size);
318 	(void)amt;
319 #else
320 	fu32 fuvals[9];
321 	for (int i = 0; i < 9; i++)
322 		fuvals[i].f = val[i];
323 	const int amt = sprintf(out,
324 		"(%" PRIu32 ",%" PRIu32 ",%" PRIu32
325 		",%" PRIu32 ",%" PRIu32 ",%" PRIu32
326 		",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")",
327 		fuvals[0].u, fuvals[1].u, fuvals[2].u,
328 		fuvals[3].u, fuvals[4].u, fuvals[5].u,
329 		fuvals[6].u, fuvals[7].u, fuvals[8].u);
330 	assert(static_cast<size_t>(amt) <= size);
331 	(void)amt;
332 #endif
333 }
334 
Matrix3x3dToStr(const matrix3x3d & val,char * out,size_t size)335 void Matrix3x3dToStr(const matrix3x3d &val, char *out, size_t size)
336 {
337 	PROFILE_SCOPED()
338 	static_assert(sizeof(matrix3x3d) == 72, "matrix3x3d isn't 72 bytes");
339 #ifdef USE_HEX_FLOATS
340 	const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8]);
341 	assert(static_cast<size_t>(amt) <= size);
342 	(void)amt;
343 #else
344 	fu64 fuvals[9];
345 	for (int i = 0; i < 9; i++)
346 		fuvals[i].d = val[i];
347 	const int amt = sprintf(out,
348 		"(%" PRIu64 ",%" PRIu64 ",%" PRIu64
349 		",%" PRIu64 ",%" PRIu64 ",%" PRIu64
350 		",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")",
351 		fuvals[0].u, fuvals[1].u, fuvals[2].u,
352 		fuvals[3].u, fuvals[4].u, fuvals[5].u,
353 		fuvals[6].u, fuvals[7].u, fuvals[8].u);
354 	assert(static_cast<size_t>(amt) <= size);
355 	(void)amt;
356 #endif
357 }
358 
Matrix4x4fToStr(const matrix4x4f & val,char * out,size_t size)359 void Matrix4x4fToStr(const matrix4x4f &val, char *out, size_t size)
360 {
361 	PROFILE_SCOPED()
362 	static_assert(sizeof(matrix4x4f) == 64, "matrix4x4f isn't 64 bytes");
363 #ifdef USE_HEX_FLOATS
364 	const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
365 	assert(static_cast<size_t>(amt) <= size);
366 	(void)amt;
367 #else
368 	fu32 fuvals[16];
369 	for (int i = 0; i < 16; i++)
370 		fuvals[i].f = val[i];
371 	const int amt = sprintf(out,
372 		"(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
373 		",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
374 		",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
375 		",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")",
376 		fuvals[0].u, fuvals[1].u, fuvals[2].u, fuvals[3].u,
377 		fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u,
378 		fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u,
379 		fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u);
380 	assert(static_cast<size_t>(amt) <= size);
381 	(void)amt;
382 #endif
383 }
384 
Matrix4x4dToStr(const matrix4x4d & val,char * out,size_t size)385 void Matrix4x4dToStr(const matrix4x4d &val, char *out, size_t size)
386 {
387 	PROFILE_SCOPED()
388 	static_assert(sizeof(matrix4x4d) == 128, "matrix4x4d isn't 128 bytes");
389 #ifdef USE_HEX_FLOATS
390 	const int amt = std::sprintf(out, "%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
391 	assert(static_cast<size_t>(amt) <= size);
392 	(void)amt;
393 #else
394 	fu64 fuvals[16];
395 	for (int i = 0; i < 16; i++)
396 		fuvals[i].d = val[i];
397 	const int amt = sprintf(out,
398 		"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
399 		",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
400 		",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
401 		",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")",
402 		fuvals[0].u, fuvals[1].u, fuvals[2].u, fuvals[3].u,
403 		fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u,
404 		fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u,
405 		fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u);
406 	assert(static_cast<size_t>(amt) <= size);
407 	(void)amt;
408 #endif
409 }
410 
AutoToStr(Sint32 val)411 std::string AutoToStr(Sint32 val)
412 {
413 	char str[64];
414 	sprintf(str, "%" PRId32, val);
415 	return str;
416 }
417 
AutoToStr(Sint64 val)418 std::string AutoToStr(Sint64 val)
419 {
420 	char str[128];
421 	sprintf(str, "%" PRId64, val);
422 	return str;
423 }
424 
AutoToStr(float val)425 std::string AutoToStr(float val)
426 {
427 	return FloatToStr(val);
428 }
429 
AutoToStr(double val)430 std::string AutoToStr(double val)
431 {
432 	return DoubleToStr(val);
433 }
434 
StrToSInt64(const std::string & str)435 Sint64 StrToSInt64(const std::string &str)
436 {
437 	Sint64 val;
438 	sscanf(str.c_str(), "%" SCNd64, &val);
439 	return val;
440 }
441 
StrToUInt64(const std::string & str)442 Uint64 StrToUInt64(const std::string &str)
443 {
444 	Uint64 val;
445 	sscanf(str.c_str(), "%" SCNu64, &val);
446 	return val;
447 }
448 
StrToFloat(const std::string & str)449 float StrToFloat(const std::string &str)
450 {
451 	PROFILE_SCOPED()
452 #ifdef USE_HEX_FLOATS
453 	float val;
454 	std::sscanf(str.c_str(), "%a", &val);
455 	return val;
456 #else
457 	// Exact representation (but not human readable).
458 	static_assert(sizeof(float) == 4, "float isn't 4 bytes");
459 	fu32 uval;
460 	const int amt = sscanf(str.c_str(), "%" SCNu32, &uval.u);
461 	assert(amt == 1);
462 	(void)amt;
463 	return uval.f;
464 #endif
465 }
466 
StrToDouble(const std::string & str)467 double StrToDouble(const std::string &str)
468 {
469 	PROFILE_SCOPED()
470 #ifdef USE_HEX_FLOATS
471 	double val;
472 	std::sscanf(str.c_str(), "%la", &val);
473 	return val;
474 #else
475 	// Exact representation (but not human readable).
476 	static_assert(sizeof(double) == 8, "double isn't 8 bytes");
477 	static_assert(sizeof(long long) == sizeof(uint64_t), "long long isn't equal in size to uint64_t");
478 	fu64 uval;
479 	const int amt = sscanf(str.c_str(), "%" SCNu64, &uval.u);
480 	(void)amt;
481 	assert(amt == 1);
482 	return uval.d;
483 #endif
484 }
485 
StrToAuto(Sint32 * pVal,const std::string & str)486 void StrToAuto(Sint32 *pVal, const std::string &str)
487 {
488 	sscanf(str.c_str(), "%" SCNd32, pVal);
489 }
490 
StrToAuto(Sint64 * pVal,const std::string & str)491 void StrToAuto(Sint64 *pVal, const std::string &str)
492 {
493 	sscanf(str.c_str(), "%" SCNd64, pVal);
494 }
495 
StrToAuto(float * pVal,const std::string & str)496 void StrToAuto(float *pVal, const std::string &str)
497 {
498 	*pVal = StrToFloat(str);
499 }
500 
StrToAuto(double * pVal,const std::string & str)501 void StrToAuto(double *pVal, const std::string &str)
502 {
503 	*pVal = StrToDouble(str);
504 }
505 
StrToVector3f(const char * str,vector3f & val)506 void StrToVector3f(const char *str, vector3f &val)
507 {
508 	PROFILE_SCOPED()
509 #ifdef USE_HEX_FLOATS
510 	const int amt = std::sscanf(str, "%a,%a,%a", &val.x, &val.y, &val.z);
511 	assert(amt == 3);
512 	(void)amt;
513 #else
514 	fu32 a, b, c;
515 	const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")", &a.u, &b.u, &c.u);
516 	assert(amt == 3);
517 	(void)amt;
518 	val.x = a.f;
519 	val.y = b.f;
520 	val.z = c.f;
521 #endif
522 }
523 
StrToVector3d(const char * str,vector3d & val)524 void StrToVector3d(const char *str, vector3d &val)
525 {
526 	PROFILE_SCOPED()
527 #ifdef USE_HEX_FLOATS
528 	const int amt = std::sscanf(str, "%la,%la,%la", &val.x, &val.y, &val.z);
529 	assert(amt == 3);
530 	(void)amt;
531 #else
532 	fu64 a, b, c;
533 	const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")", &a.u, &b.u, &c.u);
534 	assert(amt == 3);
535 	(void)amt;
536 	val.x = a.d;
537 	val.y = b.d;
538 	val.z = c.d;
539 #endif
540 }
541 
StrToMatrix3x3f(const char * str,matrix3x3f & val)542 void StrToMatrix3x3f(const char *str, matrix3x3f &val)
543 {
544 	PROFILE_SCOPED()
545 #ifdef USE_HEX_FLOATS
546 	const int amt = std::sscanf(str, "%a,%a,%a,%a,%a,%a,%a,%a,%a", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8]);
547 	assert(amt == 9);
548 	(void)amt;
549 #else
550 	fu32 fu[9];
551 	const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")",
552 		&fu[0].u, &fu[1].u, &fu[2].u,
553 		&fu[3].u, &fu[4].u, &fu[5].u,
554 		&fu[6].u, &fu[7].u, &fu[8].u);
555 	assert(amt == 9);
556 	(void)amt;
557 	for (int i = 0; i < 9; i++)
558 		val[i] = fu[i].f;
559 #endif
560 }
561 
StrToMatrix3x3d(const char * str,matrix3x3d & val)562 void StrToMatrix3x3d(const char *str, matrix3x3d &val)
563 {
564 	PROFILE_SCOPED()
565 #ifdef USE_HEX_FLOATS
566 	const int amt = std::sscanf(str, "%la,%la,%la,%la,%la,%la,%la,%la,%la", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8]);
567 	assert(amt == 9);
568 	(void)amt;
569 #else
570 	fu64 fu[9];
571 	const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")",
572 		&fu[0].u, &fu[1].u, &fu[2].u,
573 		&fu[3].u, &fu[4].u, &fu[5].u,
574 		&fu[6].u, &fu[7].u, &fu[8].u);
575 	assert(amt == 9);
576 	(void)amt;
577 	for (int i = 0; i < 9; i++)
578 		val[i] = fu[i].d;
579 #endif
580 }
581 
StrToMatrix4x4f(const char * str,matrix4x4f & val)582 void StrToMatrix4x4f(const char *str, matrix4x4f &val)
583 {
584 	PROFILE_SCOPED()
585 #ifdef USE_HEX_FLOATS
586 	const int amt = std::sscanf(str, "%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a,%a", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8], &val[9], &val[10], &val[11], &val[12], &val[13], &val[14], &val[15]);
587 	assert(amt == 16);
588 #else
589 	fu32 fu[16];
590 	const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")",
591 		&fu[0].u, &fu[1].u, &fu[2].u, &fu[3].u,
592 		&fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u,
593 		&fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u,
594 		&fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u);
595 	assert(amt == 16);
596 	(void)amt;
597 	for (int i = 0; i < 16; i++)
598 		val[i] = fu[i].f;
599 #endif
600 }
601 
StrToMatrix4x4d(const char * str,matrix4x4d & val)602 void StrToMatrix4x4d(const char *str, matrix4x4d &val)
603 {
604 	PROFILE_SCOPED()
605 #ifdef USE_HEX_FLOATS
606 	const int amt = std::sscanf(str, "%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la,%la", &val[0], &val[1], &val[2], &val[3], &val[4], &val[5], &val[6], &val[7], &val[8], &val[9], &val[10], &val[11], &val[12], &val[13], &val[14], &val[15]);
607 	assert(amt == 16);
608 #else
609 	fu64 fu[16];
610 	const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")",
611 		&fu[0].u, &fu[1].u, &fu[2].u, &fu[3].u,
612 		&fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u,
613 		&fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u,
614 		&fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u);
615 	assert(amt == 16);
616 	(void)amt;
617 	for (int i = 0; i < 16; i++)
618 		val[i] = fu[i].d;
619 #endif
620 }
621 
622 /**
623     Converts geographic coordinates from decimal to degree/minutes/seconds format
624     and returns a string.
625 */
DecimalToDegMinSec(float dec)626 std::string DecimalToDegMinSec(float dec)
627 {
628 	int degrees = dec;
629 	int minutes = 60 * (dec - degrees);
630 	int seconds = 3600 * ((dec - degrees) - static_cast<float>(minutes) / 60);
631 	std::string str = stringf("%0° %1' %2\"", degrees, std::abs(minutes), std::abs(seconds));
632 	return str;
633 }
634 
635 static const int HEXDUMP_CHUNK = 16;
hexdump(const unsigned char * buf,int len)636 void hexdump(const unsigned char *buf, int len)
637 {
638 	int count;
639 
640 	for (int i = 0; i < len; i += HEXDUMP_CHUNK) {
641 		Output("0x%06x  ", i);
642 
643 		count = ((len - i) > HEXDUMP_CHUNK ? HEXDUMP_CHUNK : len - i);
644 
645 		for (int j = 0; j < count; j++) {
646 			if (j == HEXDUMP_CHUNK / 2) Output(" ");
647 			Output("%02x ", buf[i + j]);
648 		}
649 
650 		for (int j = count; j < HEXDUMP_CHUNK; j++) {
651 			if (j == HEXDUMP_CHUNK / 2) Output(" ");
652 			Output("   ");
653 		}
654 
655 		Output(" ");
656 
657 		for (int j = 0; j < count; j++)
658 			Output("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
659 
660 		Output("\n");
661 	}
662 }
663