#pragma once #include #include #include #include "util/math/vector2.h" #include "util/math/vector3.h" #ifdef __clang__ #include "Common/unix/fast_float.h" #endif template constexpr auto to_underlying(TType v) noexcept { return static_cast>(v); } // wrapper to allow reverse iteration with range-based loops before C++20 template class reverse_itr { private: T& iterable_; public: explicit reverse_itr(T& iterable) : iterable_{ iterable } {} auto begin() const { return std::rbegin(iterable_); } auto end() const { return std::rend(iterable_); } }; #ifndef M_PI #define M_PI 3.14159265358979323846 #endif template T deg2rad(T v) { return v * static_cast(M_PI) / static_cast(180); } template T rad2deg(T v) { return v * static_cast(180) / static_cast(M_PI); } template Vector3 deg2rad(const Vector3& v) { return { deg2rad(v.x), deg2rad(v.y), deg2rad(v.z) }; } template Vector3 rad2deg(const Vector3& v) { return { rad2deg(v.x), rad2deg(v.y), rad2deg(v.z) }; } template Vector2 deg2rad(const Vector2& v) { return { deg2rad(v.x), deg2rad(v.y) }; } template Vector2 rad2deg(const Vector2& v) { return { rad2deg(v.x), rad2deg(v.y) }; } uint32_t GetPhysicalCoreCount(); // Creates a temporary file to test for write access bool TestWriteAccess(const fs::path& p); fs::path MakeRelativePath(const fs::path& path); #ifdef HAS_DIRECTINPUT bool GUIDFromString(const char* string, GUID& guid); std::string StringFromGUID(const GUID& guid); std::wstring WStringFromGUID(const GUID& guid); #endif std::vector TokenizeView(std::string_view string, char delimiter); std::vector Tokenize(std::string_view string, char delimiter); std::string ltrim_copy(const std::string& s); std::string rtrim_copy(const std::string& s); std::string& ltrim(std::string& str, const std::string& chars = "\t\n\v\f\r "); std::string& rtrim(std::string& str, const std::string& chars = "\t\n\v\f\r "); std::string& trim(std::string& str, const std::string& chars = "\t\n\v\f\r "); std::string_view& ltrim(std::string_view& str, const std::string& chars = "\t\n\v\f\r "); std::string_view& rtrim(std::string_view& str, const std::string& chars = "\t\n\v\f\r "); std::string_view& trim(std::string_view& str, const std::string& chars = "\t\n\v\f\r "); std::string GenerateRandomString(size_t length); std::string GenerateRandomString(size_t length, std::string_view characters); std::wstring GetSystemErrorMessageW(); std::wstring GetSystemErrorMessageW(DWORD error_code); std::string GetSystemErrorMessage(); std::string GetSystemErrorMessage(DWORD error_code); std::string GetSystemErrorMessage(const std::exception& ex); std::string GetSystemErrorMessage(const std::error_code& ec); template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...)->overloaded; template bool equals(T v1, T v2) { /* return std::fabs(x-y) <= std::numeric_limits::epsilon() * std::fabs(x+y) * ulp // unless the result is subnormal || std::fabs(x-y) < std::numeric_limits::min(); */ if constexpr (std::is_floating_point_v) return std::abs(v1 - v2) < (T)0.000001; else if constexpr (std::is_same_v) return strcmp(v1, v2) == 0; else return v1 == v2; } template T ConvertString(std::string_view str, sint32 base) { if (str.empty()) return {}; static_assert(std::is_integral_v); T result; ltrim(str); // from_chars cant deal with hex numbers starting with "0x" if (base == 16) { const sint32 index = str[0] == '-' ? 1 : 0; if (str.size() >= 2 && str[index+0] == '0' && tolower(str[index+1]) == 'x') str = str.substr(index + 2); if (std::from_chars(str.data(), str.data() + str.size(), result, base).ec == std::errc()) { if (index == 1) { if constexpr(std::is_unsigned_v) result = static_cast(-static_cast>(result)); else result = -result; } return result; } return {}; } if(std::from_chars(str.data(), str.data() + str.size(), result, base).ec == std::errc()) return result; return {}; } template T ConvertString(std::string_view str) { if (str.empty()) return {}; T result; ltrim(str); if constexpr (std::is_same_v) { return str == "1" || boost::iequals(str, "true"); } else if constexpr(std::is_floating_point_v) { // from_chars can't deal with float conversation starting with "+" ltrim(str, "+"); #ifdef __clang__ if (fast_float::from_chars(str.data(), str.data() + str.size(), result).ec == std::errc()) return result; #else if (std::from_chars(str.data(), str.data() + str.size(), result).ec == std::errc()) return result; #endif return {}; } else if constexpr(std::is_enum_v) { return (T)ConvertString>(str); } else { const sint32 index = str[0] == '-' ? 1 : 0; if (str.size() >= 2 && str[index + 0] == '0' && tolower(str[index + 1]) == 'x') result = ConvertString(str, 16); else result = ConvertString(str, 10); } return result; } template constexpr T DegToRad(T deg) { return (T)((double)deg * M_PI / 180); } template constexpr T RadToDeg(T rad) { return (T)((double)rad * 180 / M_PI); } template std::string ToString(T value) { std::ostringstream str; str.imbue(std::locale("C")); str << value; return str.str(); } template T FromString(std::string value) { std::istringstream str(value); str.imbue(std::locale("C")); T tmp; str >> tmp; return tmp; } template size_t RemoveDuplicatesKeepOrder(std::vector& vec) { std::set tmp; auto new_end = std::remove_if(vec.begin(), vec.end(), [&tmp](const T& value) { if (tmp.find(value) != std::end(tmp)) return true; tmp.insert(value); return false; }); vec.erase(new_end, vec.end()); return vec.size(); } void SetThreadName(const char* name); inline uint64 MakeU64(uint32 high, uint32 low) { return ((uint64)high << 32) | ((uint64)low); } // MAJOR; MINOR std::pair GetWindowsVersion(); bool IsWindows81OrGreater(); bool IsWindows10OrGreater(); fs::path GetParentProcess();