663 lines
17 KiB
C++
663 lines
17 KiB
C++
// Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
|
|
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
|
|
|
#include "utils.h"
|
|
#include "DateTime.h"
|
|
#include "FileSystem.h"
|
|
#include "Lang.h"
|
|
#include "StringF.h"
|
|
#include "gameconsts.h"
|
|
#include "graphics/Graphics.h"
|
|
#include "gui/Gui.h"
|
|
#include "libs.h"
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
std::string format_money(double cents, bool showCents)
|
|
{
|
|
char *end; // for error checking
|
|
size_t groupDigits = strtol(Lang::NUMBER_GROUP_NUM, &end, 10);
|
|
assert(*end == 0);
|
|
|
|
double money = showCents ? 0.01 * cents : roundf(0.01 * cents);
|
|
|
|
const char *format = (money < 0) ? "-$%.2f" : "$%.2f";
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), format, fabs(money));
|
|
std::string result(buf);
|
|
|
|
size_t pos = result.find_first_of('.'); // pos to decimal point
|
|
|
|
if (showCents) // replace decimal point
|
|
result.replace(pos, 1, Lang::NUMBER_DECIMAL_POINT);
|
|
else // or just remove frac. part
|
|
result.erase(result.begin() + pos, result.end());
|
|
|
|
size_t groupMin = strtol(Lang::NUMBER_GROUP_MIN, &end, 10);
|
|
assert(*end == 0);
|
|
|
|
if (groupDigits != 0 && fabs(money) >= groupMin) {
|
|
|
|
std::string groupSep = std::string(Lang::NUMBER_GROUP_SEP) == " " ?
|
|
"\u00a0" :
|
|
Lang::NUMBER_GROUP_SEP; // space should be fixed space
|
|
|
|
size_t skip = (money < 0) ? 2 : 1; // compensate for "$" or "-$"
|
|
while (pos - skip > groupDigits) { // insert thousand seperator
|
|
pos = pos - groupDigits;
|
|
result.insert(pos, groupSep);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const char *const MONTH_NAMES[] = {
|
|
Lang::MONTH_JAN,
|
|
Lang::MONTH_FEB,
|
|
Lang::MONTH_MAR,
|
|
Lang::MONTH_APR,
|
|
Lang::MONTH_MAY,
|
|
Lang::MONTH_JUN,
|
|
Lang::MONTH_JUL,
|
|
Lang::MONTH_AUG,
|
|
Lang::MONTH_SEP,
|
|
Lang::MONTH_OCT,
|
|
Lang::MONTH_NOV,
|
|
Lang::MONTH_DEC
|
|
};
|
|
|
|
std::string format_date(double t)
|
|
{
|
|
const Time::DateTime dt(t);
|
|
int year, month, day, hour, minute, second;
|
|
dt.GetDateParts(&year, &month, &day);
|
|
dt.GetTimeParts(&hour, &minute, &second);
|
|
|
|
char buf[32];
|
|
snprintf(buf, sizeof(buf), "%02d:%02d:%02d %d %s %d",
|
|
hour, minute, second, day, MONTH_NAMES[month - 1], year);
|
|
return buf;
|
|
}
|
|
|
|
std::string format_date_only(double t)
|
|
{
|
|
const Time::DateTime dt(t);
|
|
int year, month, day;
|
|
dt.GetDateParts(&year, &month, &day);
|
|
|
|
char buf[16];
|
|
snprintf(buf, sizeof(buf), "%d %s %d", day, MONTH_NAMES[month - 1], year);
|
|
return buf;
|
|
}
|
|
|
|
std::string string_join(std::vector<std::string> &v, std::string sep)
|
|
{
|
|
std::vector<std::string>::iterator i = v.begin();
|
|
std::string out;
|
|
|
|
while (i != v.end()) {
|
|
out += *i;
|
|
++i;
|
|
if (i != v.end()) out += sep;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::string format_duration(double seconds)
|
|
{
|
|
std::ostringstream ss;
|
|
int duration = seconds;
|
|
int secs = duration % 60;
|
|
int minutes = (duration / 60) % 60;
|
|
int hours = (duration / 60 / 60) % 24;
|
|
int days = (duration / 60 / 60 / 24) % 7;
|
|
int weeks = (duration / 60 / 60 / 24 / 7);
|
|
if (weeks != 0)
|
|
ss << weeks << Lang::UNIT_WEEKS;
|
|
if (days != 0)
|
|
ss << days << Lang::UNIT_DAYS;
|
|
if (hours != 0)
|
|
ss << hours << Lang::UNIT_HOURS;
|
|
if (minutes != 0)
|
|
ss << minutes << Lang::UNIT_MINUTES;
|
|
// do not show seconds unless the largest unit shown is minutes
|
|
if (weeks == 0 && days == 0 && hours == 0)
|
|
if (minutes == 0 || secs != 0)
|
|
ss << secs << Lang::UNIT_SECONDS;
|
|
return ss.str();
|
|
}
|
|
|
|
std::string format_distance(double dist, int precision)
|
|
{
|
|
std::ostringstream ss;
|
|
ss.setf(std::ios::fixed, std::ios::floatfield);
|
|
if (dist < 1e3) {
|
|
ss.precision(0);
|
|
ss << dist << " m";
|
|
} else {
|
|
const float LY = 9.4607e15f;
|
|
ss.precision(precision);
|
|
|
|
if (dist < 1e6)
|
|
ss << (dist * 1e-3) << " km";
|
|
else if (dist < AU * 0.01)
|
|
ss << (dist * 1e-6) << " Mm";
|
|
else if (dist < LY * 0.1)
|
|
ss << (dist / AU) << " " << Lang::UNIT_AU;
|
|
else
|
|
ss << (dist / LY) << " " << Lang::UNIT_LY;
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
// strcasestr() adapted from gnulib
|
|
// (c) 2005 FSF. GPL2+
|
|
|
|
#define TOLOWER(c) (isupper(static_cast<unsigned char>(c)) ? tolower(static_cast<unsigned char>(c)) : (static_cast<unsigned char>(c)))
|
|
|
|
const char *pi_strcasestr(const char *haystack, const char *needle)
|
|
{
|
|
if (!*needle)
|
|
return haystack;
|
|
|
|
// cache the first character for speed
|
|
char b = TOLOWER(*needle);
|
|
|
|
needle++;
|
|
for (;; haystack++) {
|
|
if (!*haystack)
|
|
return 0;
|
|
|
|
if (TOLOWER(*haystack) == b) {
|
|
const char *rhaystack = haystack + 1;
|
|
const char *rneedle = needle;
|
|
|
|
for (;; rhaystack++, rneedle++) {
|
|
if (!*rneedle)
|
|
return haystack;
|
|
|
|
if (!*rhaystack)
|
|
return 0;
|
|
|
|
if (TOLOWER(*rhaystack) != TOLOWER(*rneedle))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> SplitString(const std::string &source, const std::string &delim)
|
|
{
|
|
bool stringSplitted = false;
|
|
std::vector<std::string> splitted;
|
|
|
|
size_t startPos = 0;
|
|
do {
|
|
// try to find delim
|
|
size_t delimPos = source.find(delim, startPos);
|
|
|
|
// if delim found
|
|
if (delimPos != std::string::npos) {
|
|
std::string element = source.substr(startPos, delimPos);
|
|
splitted.push_back(element);
|
|
|
|
// prepare next loop
|
|
startPos = delimPos + delim.length();
|
|
} else {
|
|
// push tail and exit
|
|
splitted.push_back(source.substr(startPos));
|
|
stringSplitted = true;
|
|
}
|
|
|
|
} while (!stringSplitted);
|
|
|
|
return splitted;
|
|
}
|
|
|
|
//#define USE_HEX_FLOATS
|
|
#ifndef USE_HEX_FLOATS
|
|
union fu32 {
|
|
fu32() {}
|
|
fu32(float fIn) :
|
|
f(fIn) {}
|
|
fu32(uint32_t uIn) :
|
|
u(uIn) {}
|
|
float f;
|
|
uint32_t u;
|
|
};
|
|
union fu64 {
|
|
fu64() {}
|
|
fu64(double dIn) :
|
|
d(dIn) {}
|
|
fu64(uint64_t uIn) :
|
|
u(uIn) {}
|
|
double d;
|
|
uint64_t u;
|
|
};
|
|
#endif // USE_HEX_FLOATS
|
|
|
|
std::string FloatToStr(float val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
char hex[32]; // Probably don't need such a large char array.
|
|
std::sprintf(hex, "%a", val);
|
|
return hex;
|
|
#else
|
|
// Exact representation (but not human readable).
|
|
static_assert(sizeof(float) == 4, "float isn't 4bytes");
|
|
fu32 uval(val);
|
|
char str[64];
|
|
SDL_itoa(uval.u, str, 10);
|
|
return str;
|
|
#endif
|
|
}
|
|
|
|
std::string DoubleToStr(double val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
char hex[64]; // Probably don't need such a large char array.
|
|
std::sprintf(hex, "%la", val);
|
|
return hex;
|
|
#else
|
|
// Exact representation (but not human readable).
|
|
static_assert(sizeof(double) == 8, "double isn't 8 bytes");
|
|
fu64 uval(val);
|
|
char str[128];
|
|
SDL_ulltoa(uval.u, str, 10);
|
|
return str;
|
|
#endif
|
|
}
|
|
|
|
void Vector3fToStr(const vector3f &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(vector3f) == 12, "vector3f isn't 12 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
const int amt = std::sprintf(out, "%a,%a,%a", val.x, val.y, val.z);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu32 a(val.x);
|
|
fu32 b(val.y);
|
|
fu32 c(val.z);
|
|
const int amt = sprintf(out, "(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")", a.u, b.u, c.u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
void Vector3dToStr(const vector3d &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(vector3d) == 24, "vector3d isn't 24 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
const int amt = std::sprintf(out, "%la,%la,%la", val.x, val.y, val.z);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu64 a(val.x);
|
|
fu64 b(val.y);
|
|
fu64 c(val.z);
|
|
const int amt = sprintf(out, "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")", a.u, b.u, c.u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
void Matrix3x3fToStr(const matrix3x3f &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(matrix3x3f) == 36, "matrix3x3f isn't 36 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu32 fuvals[9];
|
|
for (int i = 0; i < 9; i++)
|
|
fuvals[i].f = val[i];
|
|
const int amt = sprintf(out,
|
|
"(%" PRIu32 ",%" PRIu32 ",%" PRIu32
|
|
",%" PRIu32 ",%" PRIu32 ",%" PRIu32
|
|
",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")",
|
|
fuvals[0].u, fuvals[1].u, fuvals[2].u,
|
|
fuvals[3].u, fuvals[4].u, fuvals[5].u,
|
|
fuvals[6].u, fuvals[7].u, fuvals[8].u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
void Matrix3x3dToStr(const matrix3x3d &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(matrix3x3d) == 72, "matrix3x3d isn't 72 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu64 fuvals[9];
|
|
for (int i = 0; i < 9; i++)
|
|
fuvals[i].d = val[i];
|
|
const int amt = sprintf(out,
|
|
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")",
|
|
fuvals[0].u, fuvals[1].u, fuvals[2].u,
|
|
fuvals[3].u, fuvals[4].u, fuvals[5].u,
|
|
fuvals[6].u, fuvals[7].u, fuvals[8].u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
void Matrix4x4fToStr(const matrix4x4f &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(matrix4x4f) == 64, "matrix4x4f isn't 64 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu32 fuvals[16];
|
|
for (int i = 0; i < 16; i++)
|
|
fuvals[i].f = val[i];
|
|
const int amt = sprintf(out,
|
|
"(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
|
|
",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
|
|
",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32
|
|
",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ")",
|
|
fuvals[0].u, fuvals[1].u, fuvals[2].u, fuvals[3].u,
|
|
fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u,
|
|
fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u,
|
|
fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
void Matrix4x4dToStr(const matrix4x4d &val, char *out, size_t size)
|
|
{
|
|
PROFILE_SCOPED()
|
|
static_assert(sizeof(matrix4x4d) == 128, "matrix4x4d isn't 128 bytes");
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#else
|
|
fu64 fuvals[16];
|
|
for (int i = 0; i < 16; i++)
|
|
fuvals[i].d = val[i];
|
|
const int amt = sprintf(out,
|
|
"(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
|
|
",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ")",
|
|
fuvals[0].u, fuvals[1].u, fuvals[2].u, fuvals[3].u,
|
|
fuvals[4].u, fuvals[5].u, fuvals[6].u, fuvals[7].u,
|
|
fuvals[8].u, fuvals[9].u, fuvals[10].u, fuvals[11].u,
|
|
fuvals[12].u, fuvals[13].u, fuvals[14].u, fuvals[15].u);
|
|
assert(static_cast<size_t>(amt) <= size);
|
|
(void)amt;
|
|
#endif
|
|
}
|
|
|
|
std::string AutoToStr(Sint32 val)
|
|
{
|
|
char str[64];
|
|
sprintf(str, "%" PRId32, val);
|
|
return str;
|
|
}
|
|
|
|
std::string AutoToStr(Sint64 val)
|
|
{
|
|
char str[128];
|
|
sprintf(str, "%" PRId64, val);
|
|
return str;
|
|
}
|
|
|
|
std::string AutoToStr(float val)
|
|
{
|
|
return FloatToStr(val);
|
|
}
|
|
|
|
std::string AutoToStr(double val)
|
|
{
|
|
return DoubleToStr(val);
|
|
}
|
|
|
|
Sint64 StrToSInt64(const std::string &str)
|
|
{
|
|
Sint64 val;
|
|
sscanf(str.c_str(), "%" SCNd64, &val);
|
|
return val;
|
|
}
|
|
|
|
Uint64 StrToUInt64(const std::string &str)
|
|
{
|
|
Uint64 val;
|
|
sscanf(str.c_str(), "%" SCNu64, &val);
|
|
return val;
|
|
}
|
|
|
|
float StrToFloat(const std::string &str)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
float val;
|
|
std::sscanf(str.c_str(), "%a", &val);
|
|
return val;
|
|
#else
|
|
// Exact representation (but not human readable).
|
|
static_assert(sizeof(float) == 4, "float isn't 4 bytes");
|
|
fu32 uval;
|
|
const int amt = sscanf(str.c_str(), "%" SCNu32, &uval.u);
|
|
assert(amt == 1);
|
|
(void)amt;
|
|
return uval.f;
|
|
#endif
|
|
}
|
|
|
|
double StrToDouble(const std::string &str)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
double val;
|
|
std::sscanf(str.c_str(), "%la", &val);
|
|
return val;
|
|
#else
|
|
// Exact representation (but not human readable).
|
|
static_assert(sizeof(double) == 8, "double isn't 8 bytes");
|
|
static_assert(sizeof(long long) == sizeof(uint64_t), "long long isn't equal in size to uint64_t");
|
|
fu64 uval;
|
|
const int amt = sscanf(str.c_str(), "%" SCNu64, &uval.u);
|
|
(void)amt;
|
|
assert(amt == 1);
|
|
return uval.d;
|
|
#endif
|
|
}
|
|
|
|
void StrToAuto(Sint32 *pVal, const std::string &str)
|
|
{
|
|
sscanf(str.c_str(), "%" SCNd32, pVal);
|
|
}
|
|
|
|
void StrToAuto(Sint64 *pVal, const std::string &str)
|
|
{
|
|
sscanf(str.c_str(), "%" SCNd64, pVal);
|
|
}
|
|
|
|
void StrToAuto(float *pVal, const std::string &str)
|
|
{
|
|
*pVal = StrToFloat(str);
|
|
}
|
|
|
|
void StrToAuto(double *pVal, const std::string &str)
|
|
{
|
|
*pVal = StrToDouble(str);
|
|
}
|
|
|
|
void StrToVector3f(const char *str, vector3f &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
const int amt = std::sscanf(str, "%a,%a,%a", &val.x, &val.y, &val.z);
|
|
assert(amt == 3);
|
|
(void)amt;
|
|
#else
|
|
fu32 a, b, c;
|
|
const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")", &a.u, &b.u, &c.u);
|
|
assert(amt == 3);
|
|
(void)amt;
|
|
val.x = a.f;
|
|
val.y = b.f;
|
|
val.z = c.f;
|
|
#endif
|
|
}
|
|
|
|
void StrToVector3d(const char *str, vector3d &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
const int amt = std::sscanf(str, "%la,%la,%la", &val.x, &val.y, &val.z);
|
|
assert(amt == 3);
|
|
(void)amt;
|
|
#else
|
|
fu64 a, b, c;
|
|
const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")", &a.u, &b.u, &c.u);
|
|
assert(amt == 3);
|
|
(void)amt;
|
|
val.x = a.d;
|
|
val.y = b.d;
|
|
val.z = c.d;
|
|
#endif
|
|
}
|
|
|
|
void StrToMatrix3x3f(const char *str, matrix3x3f &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(amt == 9);
|
|
(void)amt;
|
|
#else
|
|
fu32 fu[9];
|
|
const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")",
|
|
&fu[0].u, &fu[1].u, &fu[2].u,
|
|
&fu[3].u, &fu[4].u, &fu[5].u,
|
|
&fu[6].u, &fu[7].u, &fu[8].u);
|
|
assert(amt == 9);
|
|
(void)amt;
|
|
for (int i = 0; i < 9; i++)
|
|
val[i] = fu[i].f;
|
|
#endif
|
|
}
|
|
|
|
void StrToMatrix3x3d(const char *str, matrix3x3d &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(amt == 9);
|
|
(void)amt;
|
|
#else
|
|
fu64 fu[9];
|
|
const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")",
|
|
&fu[0].u, &fu[1].u, &fu[2].u,
|
|
&fu[3].u, &fu[4].u, &fu[5].u,
|
|
&fu[6].u, &fu[7].u, &fu[8].u);
|
|
assert(amt == 9);
|
|
(void)amt;
|
|
for (int i = 0; i < 9; i++)
|
|
val[i] = fu[i].d;
|
|
#endif
|
|
}
|
|
|
|
void StrToMatrix4x4f(const char *str, matrix4x4f &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(amt == 16);
|
|
#else
|
|
fu32 fu[16];
|
|
const int amt = std::sscanf(str, "(%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ")",
|
|
&fu[0].u, &fu[1].u, &fu[2].u, &fu[3].u,
|
|
&fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u,
|
|
&fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u,
|
|
&fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u);
|
|
assert(amt == 16);
|
|
(void)amt;
|
|
for (int i = 0; i < 16; i++)
|
|
val[i] = fu[i].f;
|
|
#endif
|
|
}
|
|
|
|
void StrToMatrix4x4d(const char *str, matrix4x4d &val)
|
|
{
|
|
PROFILE_SCOPED()
|
|
#ifdef USE_HEX_FLOATS
|
|
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]);
|
|
assert(amt == 16);
|
|
#else
|
|
fu64 fu[16];
|
|
const int amt = std::sscanf(str, "(%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ",%" SCNu64 ")",
|
|
&fu[0].u, &fu[1].u, &fu[2].u, &fu[3].u,
|
|
&fu[4].u, &fu[5].u, &fu[6].u, &fu[7].u,
|
|
&fu[8].u, &fu[9].u, &fu[10].u, &fu[11].u,
|
|
&fu[12].u, &fu[13].u, &fu[14].u, &fu[15].u);
|
|
assert(amt == 16);
|
|
(void)amt;
|
|
for (int i = 0; i < 16; i++)
|
|
val[i] = fu[i].d;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Converts geographic coordinates from decimal to degree/minutes/seconds format
|
|
and returns a string.
|
|
*/
|
|
std::string DecimalToDegMinSec(float dec)
|
|
{
|
|
int degrees = dec;
|
|
int minutes = 60 * (dec - degrees);
|
|
int seconds = 3600 * ((dec - degrees) - static_cast<float>(minutes) / 60);
|
|
std::string str = stringf("%0° %1' %2\"", degrees, std::abs(minutes), std::abs(seconds));
|
|
return str;
|
|
}
|
|
|
|
static const int HEXDUMP_CHUNK = 16;
|
|
void hexdump(const unsigned char *buf, int len)
|
|
{
|
|
int count;
|
|
|
|
for (int i = 0; i < len; i += HEXDUMP_CHUNK) {
|
|
Output("0x%06x ", i);
|
|
|
|
count = ((len - i) > HEXDUMP_CHUNK ? HEXDUMP_CHUNK : len - i);
|
|
|
|
for (int j = 0; j < count; j++) {
|
|
if (j == HEXDUMP_CHUNK / 2) Output(" ");
|
|
Output("%02x ", buf[i + j]);
|
|
}
|
|
|
|
for (int j = count; j < HEXDUMP_CHUNK; j++) {
|
|
if (j == HEXDUMP_CHUNK / 2) Output(" ");
|
|
Output(" ");
|
|
}
|
|
|
|
Output(" ");
|
|
|
|
for (int j = 0; j < count; j++)
|
|
Output("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
|
|
|
|
Output("\n");
|
|
}
|
|
}
|