mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 06:19:19 +02:00
669 lines
19 KiB
C++
669 lines
19 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
/*
|
|
MaSzyna EU07 - SPKS
|
|
Brakes.
|
|
Copyright (C) 2007-2014 Maciej Cierniak
|
|
*/
|
|
//#include "stdafx.h"
|
|
//
|
|
//#include <sys/types.h>
|
|
//#include <sys/stat.h>
|
|
#include <charconv>
|
|
#include <ranges>
|
|
#include <format>
|
|
//#ifndef WIN32
|
|
//#include <unistd.h>
|
|
//#endif
|
|
//
|
|
//#ifdef WIN32
|
|
//#define stat _stat
|
|
//#endif
|
|
|
|
#include "utilities/utilities.h"
|
|
#include "utilities/Globals.h"
|
|
#include "utilities/parser.h"
|
|
#include "utilities/U8.h"
|
|
#include "utilities/Logs.h"
|
|
|
|
|
|
|
|
// TODO: This shouldn't be in Globals?
|
|
bool DebugModeFlag = false;
|
|
bool FreeFlyModeFlag = false;
|
|
bool EditorModeFlag = false;
|
|
bool DebugCameraFlag = false;
|
|
bool DebugTractionFlag = false;
|
|
|
|
std::string Now()
|
|
{
|
|
#if defined(__APPLE__)
|
|
auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
|
std::tm tm{};
|
|
localtime_r(&t, &tm);
|
|
char buf[64];
|
|
std::strftime(buf, sizeof(buf), "%c", &tm);
|
|
return std::string(buf);
|
|
#else
|
|
auto now = std::chrono::system_clock::now();
|
|
auto local = std::chrono::current_zone()->to_local(now);
|
|
return std::format("{:%c}", local);
|
|
#endif
|
|
}
|
|
|
|
std::filesystem::path user_config_path(const std::string &filename)
|
|
{
|
|
namespace fs = std::filesystem;
|
|
#if defined(_WIN32)
|
|
if (const char *appdata = std::getenv("APPDATA"))
|
|
return fs::path(appdata) / "MaSzyna" / filename;
|
|
#elif defined(__APPLE__)
|
|
if (const char *home = std::getenv("HOME"))
|
|
return fs::path(home) / "Library" / "Application Support" / "MaSzyna" / filename;
|
|
#else
|
|
if (const char *home = std::getenv("HOME"))
|
|
return fs::path(home) / ".config" / "MaSzyna" / filename;
|
|
#endif
|
|
return {}; // env var missing → caller falls back to CWD-relative filename
|
|
}
|
|
|
|
// zwraca różnicę czasu
|
|
// jeśli pierwsza jest aktualna, a druga rozkładowa, to ujemna oznacza opóżnienie
|
|
// na dłuższą metę trzeba uwzględnić datę, jakby opóżnienia miały przekraczać 12h (towarowych)
|
|
double CompareTime(double t1h, double t1m, double t2h, double t2m)
|
|
{
|
|
|
|
if (t2h < 0)
|
|
return 0;
|
|
else
|
|
{
|
|
auto t = (t2h - t1h) * 60 + t2m - t1m; // jeśli t2=00:05, a t1=23:50, to różnica wyjdzie ujemna
|
|
if (t < -720) // jeśli różnica przekracza 12h na minus
|
|
t = t + 1440; // to dodanie doby minut;else
|
|
if (t > 720) // jeśli przekracza 12h na plus
|
|
t = t - 1440; // to odjęcie doby minut
|
|
return t;
|
|
}
|
|
}
|
|
|
|
bool SetFlag(int &Flag, int const Value)
|
|
{
|
|
|
|
if (Value > 0)
|
|
{
|
|
if (false == TestFlag(Flag, Value))
|
|
{
|
|
Flag |= Value;
|
|
return true; // true, gdy było wcześniej 0 i zostało ustawione
|
|
}
|
|
}
|
|
else if (Value < 0)
|
|
{
|
|
// Value jest ujemne, czyli zerowanie flagi
|
|
return ClearFlag(Flag, -Value);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClearFlag(int &Flag, int const Value)
|
|
{
|
|
|
|
if (true == TestFlag(Flag, Value))
|
|
{
|
|
Flag &= ~Value;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
double Random(double min, double max)
|
|
{
|
|
if (max < min) { std::swap(min, max); } // std::uniform_real_distribution requires min <= max (inverted bounds are UB)
|
|
std::uniform_real_distribution dist(min, max);
|
|
return dist(Global.random_engine);
|
|
}
|
|
|
|
int Random(int min, int max)
|
|
{
|
|
if (max < min) { std::swap(min, max); } // std::uniform_int_distribution requires min <= max (inverted bounds are UB)
|
|
std::uniform_int_distribution dist(min, max);
|
|
return dist(Global.random_engine);
|
|
}
|
|
|
|
std::uint64_t true_random_seed()
|
|
{
|
|
try
|
|
{
|
|
std::random_device rd;
|
|
if (rd.entropy() > 0.0)
|
|
{
|
|
// NOTE: both halves have to be 64 bit wide before the shift - shifting a std::uint32_t
|
|
// by 32 is undefined behaviour, and drops the high half of the seed
|
|
std::uint64_t const high{rd()};
|
|
std::uint64_t const low{rd()};
|
|
return (high << 32) | low;
|
|
}
|
|
}
|
|
catch (std::exception const &Error)
|
|
{
|
|
// std::random_device throws when the machine has no usable hardware entropy source;
|
|
// the clock below stands in for it, which is worth saying out loud since it is a
|
|
// weaker seed than the caller asked for
|
|
WriteLog(std::format("random: no hardware entropy source ({}), seeding from the clock instead", Error.what()));
|
|
}
|
|
return static_cast<std::uint64_t>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
|
}
|
|
|
|
std::uint32_t seed_of(std::string const &Text)
|
|
{
|
|
if (std::uint32_t number{}; std::from_chars(Text.data(), Text.data() + Text.size(), number).ec == std::errc{})
|
|
{
|
|
return number;
|
|
}
|
|
std::uint32_t hash{};
|
|
for (auto const character : Text)
|
|
{
|
|
hash = hash * 31u + static_cast<std::uint32_t>(static_cast<unsigned char>(character));
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
std::string generate_uuid_v4()
|
|
{
|
|
std::uniform_int_distribution dist(0, 255);
|
|
|
|
std::array<uint8_t, 16> bytes;
|
|
for (auto &b : bytes)
|
|
b = static_cast<uint8_t>(dist(Global.random_engine));
|
|
|
|
// UUID v4 (RFC 4122)
|
|
bytes[6] = bytes[6] & 0x0F | 0x40;
|
|
bytes[8] = bytes[8] & 0x3F | 0x80;
|
|
|
|
return std::format("{:02x}{:02x}{:02x}{:02x}-"
|
|
"{:02x}{:02x}-"
|
|
"{:02x}{:02x}-"
|
|
"{:02x}{:02x}-"
|
|
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
|
|
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
|
|
}
|
|
|
|
double LocalRandom(double a, double b)
|
|
{
|
|
if (b < a) { std::swap(a, b); } // std::uniform_real_distribution requires a <= b (inverted bounds are UB)
|
|
std::uniform_real_distribution dist(a, b);
|
|
return dist(Global.local_random_engine);
|
|
}
|
|
|
|
bool FuzzyLogic(double Test, double Threshold, double Probability)
|
|
{
|
|
if (Test > Threshold && !DebugModeFlag)
|
|
return Random() < Probability * Threshold * 1.0 / Test /*im wiekszy Test tym wieksza szansa*/;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool FuzzyLogicAI(double Test, double Threshold, double Probability)
|
|
{
|
|
if (Test > Threshold)
|
|
return Random() < Probability * Threshold * 1.0 / Test /*im wiekszy Test tym wieksza szansa*/;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> Split(std::string_view s, char delim)
|
|
{ // dzieli tekst na wektor tekstow
|
|
std::vector<std::string> out;
|
|
for (const auto& part : s | std::ranges::views::split(delim))
|
|
out.emplace_back(part.begin(), part.end());
|
|
return out;
|
|
}
|
|
|
|
std::pair<std::string, int> split_string_and_number(std::string const &Key)
|
|
{
|
|
|
|
auto const indexstart{Key.find_first_of("-1234567890")};
|
|
auto const indexend{Key.find_first_not_of("-1234567890", indexstart)};
|
|
if (indexstart != std::string::npos)
|
|
{
|
|
return {Key.substr(0, indexstart), std::stoi(Key.substr(indexstart, indexend - indexstart))};
|
|
}
|
|
return {Key, 0};
|
|
}
|
|
|
|
std::string to_string(int Value, int width)
|
|
{
|
|
std::ostringstream o;
|
|
o << std::setw(width) << Value;
|
|
return std::move(o).str();
|
|
};
|
|
|
|
std::string to_string(double Value, int precision)
|
|
{
|
|
std::ostringstream o;
|
|
o << std::fixed << std::setprecision(precision) << Value;
|
|
return std::move(o).str();
|
|
};
|
|
|
|
std::string to_string(double const Value, int const Precision, int const Width)
|
|
{
|
|
std::ostringstream o;
|
|
o << std::setw(Width) << std::fixed << std::setprecision(Precision) << Value;
|
|
return std::move(o).str();
|
|
};
|
|
|
|
std::string to_hex_str(int const Value, int const Width)
|
|
{
|
|
std::ostringstream o;
|
|
o << "0x" << std::uppercase << std::setfill('0') << std::setw(Width) << std::hex << Value;
|
|
return o.str();
|
|
};
|
|
|
|
std::string const fractionlabels[] = {U8(" "), U8("¹"), U8("²"), U8("³"), U8("⁴"), U8("⁵"), U8("⁶"), U8("⁷"), U8("⁸"), U8("⁹")};
|
|
|
|
std::string to_minutes_str(float const Minutes, bool const Leadingzero, int const Width)
|
|
{
|
|
|
|
float minutesintegral;
|
|
auto const minutesfractional{std::modf(Minutes, &minutesintegral)};
|
|
auto const width{Width - 1};
|
|
auto minutes = std::string(width - 1, ' ') + (Leadingzero ? std::to_string(100 + minutesintegral).substr(1, 2) : to_string(minutesintegral, 0));
|
|
return minutes.substr(minutes.size() - width, width) + fractionlabels[static_cast<int>(std::floor(minutesfractional * 10 + 0.1))];
|
|
}
|
|
|
|
int stol_def(const std::string &str, const int &DefaultValue)
|
|
{
|
|
|
|
int result{DefaultValue};
|
|
std::stringstream converter;
|
|
converter << str;
|
|
converter >> result;
|
|
return result;
|
|
}
|
|
|
|
std::string ToLower(std::string const &text)
|
|
{
|
|
|
|
auto lowercase{text};
|
|
std::ranges::transform(text, std::begin(lowercase), [](unsigned char c) { return std::tolower(c); });
|
|
return lowercase;
|
|
}
|
|
|
|
std::string ToUpper(std::string const &text)
|
|
{
|
|
|
|
auto uppercase{text};
|
|
std::ranges::transform(text, std::begin(uppercase), [](unsigned char c) { return std::toupper(c); });
|
|
return uppercase;
|
|
}
|
|
|
|
// replaces polish letters with basic ascii
|
|
void win1250_to_ascii(std::string &Input)
|
|
{
|
|
|
|
std::unordered_map<char, char> const charmap{{165, 'A'}, {198, 'C'}, {202, 'E'}, {163, 'L'}, {209, 'N'}, {211, 'O'}, {140, 'S'}, {143, 'Z'}, {175, 'Z'},
|
|
{185, 'a'}, {230, 'c'}, {234, 'e'}, {179, 'l'}, {241, 'n'}, {243, 'o'}, {156, 's'}, {159, 'z'}, {191, 'z'}};
|
|
std::unordered_map<char, char>::const_iterator lookup;
|
|
for (auto &input : Input)
|
|
{
|
|
if ((lookup = charmap.find(input)) != charmap.end())
|
|
input = lookup->second;
|
|
}
|
|
}
|
|
|
|
std::string win1250_to_utf8(const std::string &Input)
|
|
{
|
|
std::unordered_map<char, std::string> const charmap{{165, "Ą"}, {198, "Ć"}, {202, "Ę"}, {163, "Ł"}, {209, "Ń"}, {211, "Ó"}, {140, "Ś"}, {143, "Ź"}, {175, "Ż"},
|
|
{185, "ą"}, {230, "ć"}, {234, "ę"}, {179, "ł"}, {241, "ń"}, {243, "ó"}, {156, "ś"}, {159, "ź"}, {191, "ż"}};
|
|
std::string output;
|
|
std::unordered_map<char, std::string>::const_iterator lookup;
|
|
for (auto &input : Input)
|
|
{
|
|
if ((lookup = charmap.find(input)) != charmap.end())
|
|
output += lookup->second;
|
|
else
|
|
output += input;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
// unicode code points of the upper half of the windows-1250 code page; 0 marks the unassigned bytes
|
|
std::array<char32_t, 128> const win1250_upperhalf{
|
|
0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
|
|
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
|
|
0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
|
|
0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
|
|
0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
|
|
0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
|
|
0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
|
|
0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9};
|
|
|
|
// returns number of bytes of the utf-8 sequence started by provided byte, or 0 if it can't start one
|
|
std::size_t utf8_sequence_length(std::byte const Lead)
|
|
{
|
|
if ((Lead & std::byte{0x80}) == std::byte{0x00})
|
|
{
|
|
return 1;
|
|
}
|
|
if ((Lead & std::byte{0xE0}) == std::byte{0xC0})
|
|
{
|
|
return 2;
|
|
}
|
|
if ((Lead & std::byte{0xF0}) == std::byte{0xE0})
|
|
{
|
|
return 3;
|
|
}
|
|
if ((Lead & std::byte{0xF8}) == std::byte{0xF0})
|
|
{
|
|
return 4;
|
|
}
|
|
return 0; // a continuation byte, or a lead of a sequence utf-8 doesn't allow
|
|
}
|
|
} // namespace
|
|
|
|
bool utf8_to_utf32(std::string const &Text, std::u32string &Output)
|
|
{
|
|
// lowest code point which needs given number of bytes; shorter values are overlong forms
|
|
static std::array<char32_t, 5> const minimum{0, 0, 0x80, 0x800, 0x10000};
|
|
|
|
Output.clear();
|
|
auto const size{Text.size()};
|
|
for (std::size_t idx = 0; idx < size;)
|
|
{
|
|
auto const lead{static_cast<std::byte>(Text[idx])};
|
|
auto const length{utf8_sequence_length(lead)};
|
|
if (length == 0 || idx + length > size)
|
|
{
|
|
return false;
|
|
}
|
|
auto codepoint{std::to_integer<char32_t>(lead & (std::byte{0xFF} >> (length > 1 ? length + 1 : 1)))};
|
|
for (std::size_t offset = 1; offset < length; ++offset)
|
|
{
|
|
auto const next{static_cast<std::byte>(Text[idx + offset])};
|
|
if ((next & std::byte{0xC0}) != std::byte{0x80})
|
|
{
|
|
return false;
|
|
}
|
|
codepoint = (codepoint << 6) | std::to_integer<char32_t>(next & std::byte{0x3F});
|
|
}
|
|
if (codepoint < minimum[length] || codepoint > 0x10FFFF || (codepoint >= 0xD800 && codepoint <= 0xDFFF))
|
|
{
|
|
return false;
|
|
}
|
|
Output += codepoint;
|
|
idx += length;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void win1250_to_utf32(std::string const &Text, std::u32string &Output)
|
|
{
|
|
Output.clear();
|
|
for (auto const character : Text)
|
|
{
|
|
auto const value{std::to_integer<std::size_t>(static_cast<std::byte>(character))};
|
|
Output += (value < 0x80 ? static_cast<char32_t>(value) : win1250_upperhalf[value - 0x80]);
|
|
}
|
|
}
|
|
|
|
// Ra: tymczasowe rozwiązanie kwestii zagranicznych (czeskich) napisów
|
|
char const charsetconversiontable[] = "E?,?\"_++?%S<STZZ?`'\"\".--??s>stzz"
|
|
" ^^L$A|S^CS<--RZo±,l'uP.,as>L\"lz"
|
|
"RAAAALCCCEEEEIIDDNNOOOOxRUUUUYTB"
|
|
"raaaalccceeeeiiddnnoooo-ruuuuyt?";
|
|
|
|
// wycięcie liter z ogonkami
|
|
std::string Bezogonkow(std::string Input, bool const Underscorestospaces)
|
|
{
|
|
|
|
char const extendedcharsetbit{static_cast<char>(0x80)};
|
|
char const space{' '};
|
|
char const underscore{'_'};
|
|
|
|
for (auto &input : Input)
|
|
{
|
|
if (input & extendedcharsetbit)
|
|
{
|
|
input = charsetconversiontable[input ^ extendedcharsetbit];
|
|
}
|
|
else if (input < space)
|
|
{
|
|
input = space;
|
|
}
|
|
else if (Underscorestospaces && input == underscore)
|
|
{
|
|
input = space;
|
|
}
|
|
}
|
|
|
|
return Input;
|
|
}
|
|
|
|
template <> bool extract_value(bool &Variable, std::string const &Key, std::string const &Input, std::string const &Default)
|
|
{
|
|
|
|
auto value = extract_value(Key, Input);
|
|
if (false == value.empty())
|
|
{
|
|
// set the specified variable to retrieved value
|
|
Variable = ToLower(value) == "yes";
|
|
return true; // located the variable
|
|
}
|
|
else
|
|
{
|
|
// set the variable to provided default value
|
|
if (false == Default.empty())
|
|
{
|
|
Variable = ToLower(Default) == "yes";
|
|
}
|
|
return false; // couldn't locate the variable in provided input
|
|
}
|
|
}
|
|
|
|
bool FileExists(std::string const &Filename)
|
|
{
|
|
return std::filesystem::exists(Filename);
|
|
}
|
|
|
|
std::pair<std::string, std::string> FileExists(std::vector<std::string> const &Names, std::vector<std::string> const &Extensions)
|
|
{
|
|
|
|
for (auto const &name : Names)
|
|
{
|
|
for (auto const &extension : Extensions)
|
|
{
|
|
if (FileExists(name + extension))
|
|
{
|
|
return {name, extension};
|
|
}
|
|
}
|
|
}
|
|
// nothing found
|
|
return {{}, {}};
|
|
}
|
|
|
|
// returns time of last modification for specified file
|
|
std::time_t last_modified(std::string const &Filename)
|
|
{
|
|
std::string fn = Filename;
|
|
struct stat filestat;
|
|
if (::stat(fn.c_str(), &filestat) == 0)
|
|
return filestat.st_mtime;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
std::tm local_tm(std::time_t Time)
|
|
{
|
|
std::tm tm{};
|
|
#ifdef _WIN32
|
|
localtime_s(&tm, &Time);
|
|
#else
|
|
localtime_r(&Time, &tm);
|
|
#endif
|
|
return tm;
|
|
}
|
|
|
|
std::tm utc_tm(std::time_t Time)
|
|
{
|
|
std::tm tm{};
|
|
#ifdef _WIN32
|
|
gmtime_s(&tm, &Time);
|
|
#else
|
|
gmtime_r(&Time, &tm);
|
|
#endif
|
|
return tm;
|
|
}
|
|
|
|
// potentially erases file extension from provided file name. returns: true if extension was removed, false otherwise
|
|
bool erase_extension(std::string &Filename)
|
|
{
|
|
|
|
auto const extensionpos{Filename.rfind('.')};
|
|
|
|
if (extensionpos == std::string::npos)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (extensionpos != Filename.rfind("..") + 1)
|
|
{
|
|
// we can get extension for .mat or, in legacy files, some image format. just trim it and set it to material file extension
|
|
Filename.erase(extensionpos);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void erase_leading_slashes(std::string &Filename)
|
|
{
|
|
auto pos = Filename.find_first_not_of('/');
|
|
Filename.erase(0, pos);
|
|
}
|
|
|
|
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
|
void replace_slashes(std::string &Filename)
|
|
{
|
|
std::ranges::replace(Filename, '\\', '/');
|
|
}
|
|
|
|
// returns potential path part from provided file name
|
|
std::string substr_path(std::string const &Filename)
|
|
{
|
|
// String::substr returns new string so substr_path has to return std::string
|
|
if (auto pos = Filename.rfind('/'); pos != std::string::npos)
|
|
return Filename.substr(0, pos + 1);
|
|
return {};
|
|
}
|
|
|
|
// returns length of common prefix between two provided strings
|
|
std::ptrdiff_t len_common_prefix(std::string_view a, std::string_view b)
|
|
{
|
|
auto [it1, it2] = std::ranges::mismatch(a, b);
|
|
return std::distance(a.begin(), it1);
|
|
}
|
|
|
|
// returns true if provided string contains another provided string
|
|
bool contains(std::string_view const String, std::string_view Substring)
|
|
{
|
|
// To be replaced with string::contains in C++ 23
|
|
return String.find(Substring) != std::string::npos;
|
|
}
|
|
|
|
bool contains(std::string_view const String, char Character)
|
|
{
|
|
// To be replaced with string::contains in C++ 23
|
|
return String.find(Character) != std::string::npos;
|
|
}
|
|
|
|
// helper, restores content of a 3d vector from provided input stream
|
|
// TODO: review and clean up the helper routines, there's likely some redundant ones
|
|
|
|
glm::dvec3 LoadPoint(cParser &Input)
|
|
{
|
|
// pobranie współrzędnych punktu
|
|
glm::dvec3 point;
|
|
Input.getTokens(3);
|
|
Input >> point.x >> point.y >> point.z;
|
|
return point;
|
|
}
|
|
|
|
// extracts a group of tokens from provided data stream, returns one of them picked randomly
|
|
std::string deserialize_random_set(cParser &Input, char const *Break)
|
|
{
|
|
|
|
auto token{Input.getToken<std::string>(true, Break)};
|
|
std::ranges::replace(token, '\\', '/');
|
|
if (token != "[")
|
|
{
|
|
// simple case, single token
|
|
return token;
|
|
}
|
|
// if instead of a single token we've encountered '[' this marks a beginning of a random set
|
|
// we retrieve all entries, then return a random one
|
|
std::vector<std::string> tokens;
|
|
while ((token = deserialize_random_set(Input, Break)) != "" && token != "]")
|
|
{
|
|
tokens.emplace_back(token);
|
|
}
|
|
if (false == tokens.empty())
|
|
{
|
|
std::ranges::shuffle(tokens, Global.random_engine);
|
|
return tokens.front();
|
|
}
|
|
else
|
|
{
|
|
// shouldn't ever get here but, eh
|
|
return "";
|
|
}
|
|
}
|
|
|
|
// returns hash of provided string, combined with provided seed
|
|
std::uint32_t hash_string(std::string const &String, std::uint32_t const Seed)
|
|
{
|
|
// FNV-1a, 32 bit, with the seed mixed into the standard offset basis
|
|
std::uint32_t hash{2166136261u ^ Seed};
|
|
for (auto const character : String)
|
|
{
|
|
hash ^= static_cast<std::uint32_t>(static_cast<unsigned char>(character));
|
|
hash *= 16777619u;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
// extracts a group of tokens from provided data stream, returns all of them
|
|
std::vector<std::string> deserialize_set(cParser &Input, char const *Break)
|
|
{
|
|
|
|
std::vector<std::string> tokens;
|
|
|
|
auto token{Input.getToken<std::string>(true, Break)};
|
|
std::ranges::replace(token, '\\', '/');
|
|
if (token != "[")
|
|
{
|
|
// simple case, single token
|
|
if (false == token.empty())
|
|
{
|
|
tokens.emplace_back(token);
|
|
}
|
|
return tokens;
|
|
}
|
|
// '[' marks a beginning of a set, retrieve all entries until it's closed
|
|
while ((token = deserialize_random_set(Input, Break)) != "" && token != "]")
|
|
{
|
|
tokens.emplace_back(token);
|
|
}
|
|
return tokens;
|
|
}
|