Cemu/src/util/helpers/helpers.cpp
ArtemisX64 1cb2d4e5ee
Proper fix for static asserts (#64)
Replace std::uniform_int_distribution with boost::random::uniform_int_distribution
2022-08-26 14:28:50 +02:00

443 lines
10 KiB
C++

#include "helpers.h"
#include <algorithm>
#include <functional>
#include <cctype>
#include <random>
#include <wx/translation.h>
#include "config/ActiveSettings.h"
#include <boost/random/uniform_int.hpp>
#if BOOST_OS_WINDOWS
#include <TlHelp32.h>
#endif
std::string& ltrim(std::string& str, const std::string& chars)
{
str.erase(0, str.find_first_not_of(chars));
return str;
}
std::string& rtrim(std::string& str, const std::string& chars)
{
str.erase(str.find_last_not_of(chars) + 1);
return str;
}
std::string& trim(std::string& str, const std::string& chars)
{
return ltrim(rtrim(str, chars), chars);
}
std::string_view& ltrim(std::string_view& str, const std::string& chars)
{
str.remove_prefix(std::min(str.find_first_not_of(chars), str.size()));
return str;
}
std::string_view& rtrim(std::string_view& str, const std::string& chars)
{
str.remove_suffix(std::max(str.size() - str.find_last_not_of(chars) - 1, (size_t)0));
return str;
}
std::string_view& trim(std::string_view& str, const std::string& chars)
{
return ltrim(rtrim(str, chars), chars);
}
#if BOOST_OS_WINDOWS
std::wstring GetSystemErrorMessageW()
{
return GetSystemErrorMessageW(GetLastError());
}
std::wstring GetSystemErrorMessageW(DWORD error_code)
{
if(error_code == ERROR_SUCCESS)
return {};
LPWSTR lpMsgBuf = nullptr;
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, 0, (LPWSTR)&lpMsgBuf, 0, nullptr);
if (lpMsgBuf)
{
std::wstring str = fmt::format(L"{}: {}", _("Error").ToStdWstring(), lpMsgBuf); // TRANSLATE
LocalFree(lpMsgBuf);
return str;
}
return fmt::format(L"{}: {:#x}", _("Error code").ToStdWstring(), error_code);
}
std::string GetSystemErrorMessage(DWORD error_code)
{
if(error_code == ERROR_SUCCESS)
return {};
LPSTR lpMsgBuf = nullptr;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error_code, 0, (LPSTR)&lpMsgBuf, 0, nullptr);
if (lpMsgBuf)
{
std::string str = fmt::format("{}: {}", _("Error").ToStdString(), lpMsgBuf); // TRANSLATE
LocalFree(lpMsgBuf);
return str;
}
return fmt::format("{}: {:#x}", _("Error code").ToStdString(), error_code);
}
std::string GetSystemErrorMessage()
{
return GetSystemErrorMessage(GetLastError());
}
#else
std::string GetSystemErrorMessage()
{
return "";
}
#endif
std::string GetSystemErrorMessage(const std::exception& ex)
{
const std::string msg = GetSystemErrorMessage();
if(msg.empty())
return ex.what();
return fmt::format("{}\n{}",msg, ex.what());
}
std::string GetSystemErrorMessage(const std::error_code& ec)
{
const std::string msg = GetSystemErrorMessage();
if(msg.empty())
return ec.message();
return fmt::format("{}\n{}",msg, ec.message());
}
#if BOOST_OS_WINDOWS
const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
#endif
void SetThreadName(const char* name)
{
#if BOOST_OS_WINDOWS
#ifndef _PUBLIC_RELEASE
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = GetCurrentThreadId();
info.dwFlags = 0;
#pragma warning(push)
#pragma warning(disable: 6320 6322)
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
}
#pragma warning(pop)
#endif
#elif BOOST_OS_MACOS
pthread_setname_np(name);
#else
pthread_setname_np(pthread_self(), name);
#endif
}
#if BOOST_OS_WINDOWS
std::pair<DWORD, DWORD> GetWindowsVersion()
{
using RtlGetVersion_t = LONG(*)(POSVERSIONINFOEXW);
static RtlGetVersion_t pRtlGetVersion = nullptr;
if(!pRtlGetVersion)
pRtlGetVersion = (RtlGetVersion_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
cemu_assert(pRtlGetVersion);
OSVERSIONINFOEXW version_info{};
pRtlGetVersion(&version_info);
return { version_info.dwMajorVersion, version_info.dwMinorVersion };
}
bool IsWindows81OrGreater()
{
const auto [major, minor] = GetWindowsVersion();
return major > 6 || (major == 6 && minor >= 3);
}
bool IsWindows10OrGreater()
{
const auto [major, minor] = GetWindowsVersion();
return major >= 10;
}
#endif
fs::path GetParentProcess()
{
fs::path result;
#if BOOST_OS_WINDOWS
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapshot != INVALID_HANDLE_VALUE)
{
DWORD pid = GetCurrentProcessId();
PROCESSENTRY32 pe{};
pe.dwSize = sizeof(pe);
for(BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
{
if(pe.th32ProcessID == pid)
{
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ParentProcessID);
if(hProcess)
{
wchar_t tmp[MAX_PATH];
DWORD size = std::size(tmp);
if (QueryFullProcessImageNameW(hProcess, 0, tmp, &size) && size > 0)
result = tmp;
CloseHandle(hProcess);
}
break;
}
}
CloseHandle(hSnapshot);
}
#else
assert_dbg();
#endif
return result;
}
std::string ltrim_copy(const std::string& s)
{
std::string result = s;
ltrim(result);
return result;
}
std::string rtrim_copy(const std::string& s)
{
std::string result = s;
rtrim(result);
return result;
}
uint32_t GetPhysicalCoreCount()
{
static uint32_t s_core_count = 0;
if (s_core_count != 0)
return s_core_count;
#if BOOST_OS_WINDOWS
auto core_count = std::thread::hardware_concurrency();
// Get physical cores
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = nullptr;
DWORD returnLength = 0;
GetLogicalProcessorInformation(buffer, &returnLength);
if (returnLength > 0)
{
buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
if (GetLogicalProcessorInformation(buffer, &returnLength))
{
uint32_t counter = 0;
for (DWORD i = 0; i < returnLength / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); ++i)
{
if (buffer[i].Relationship == RelationProcessorCore)
++counter;
}
if (counter > 0 && counter < core_count)
core_count = counter;
}
free(buffer);
}
s_core_count = core_count;
return core_count;
#else
return std::thread::hardware_concurrency();
#endif
}
bool TestWriteAccess(const fs::path& p)
{
// must be path and must exist
if (!fs::exists(p) || !fs::is_directory(p))
return false;
// retry 3 times
for (int i = 0; i < 3; ++i)
{
const auto filename = p / fmt::format("_{}.tmp", GenerateRandomString(8));
if (fs::exists(filename))
continue;
std::ofstream file(filename);
if (!file.is_open()) // file couldn't be created
break;
file.close();
std::error_code ec;
fs::remove(filename, ec);
return true;
}
return false;
}
// make path relative to Cemu directory
fs::path MakeRelativePath(const fs::path& path)
{
try
{
const fs::path base = ActiveSettings::GetPath();
return fs::relative(path, base);
}
catch (const std::exception&)
{
return path;
}
}
#ifdef HAS_DIRECTINPUT
bool GUIDFromString(const char* string, GUID& guid)
{
unsigned long p0;
int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
const sint32 count = sscanf_s(string, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10);
if (count != 11)
return false;
guid.Data1 = p0;
guid.Data2 = p1;
guid.Data3 = p2;
guid.Data4[0] = p3;
guid.Data4[1] = p4;
guid.Data4[2] = p5;
guid.Data4[3] = p6;
guid.Data4[4] = p7;
guid.Data4[5] = p8;
guid.Data4[6] = p9;
guid.Data4[7] = p10;
return count == 11;
}
std::string StringFromGUID(const GUID& guid)
{
char temp[256];
sprintf(temp, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::string(temp);
}
std::wstring WStringFromGUID(const GUID& guid)
{
wchar_t temp[256];
swprintf_s(temp, L"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::wstring(temp);
}
#endif
std::vector<std::string_view> TokenizeView(std::string_view str, char delimiter)
{
std::vector<std::string_view> result;
size_t last_token_index = 0;
for (auto index = str.find(delimiter); index != std::string_view::npos; index = str.find(delimiter, index + 1))
{
const auto token = str.substr(last_token_index, index - last_token_index);
result.emplace_back(token);
last_token_index = index + 1;
}
try
{
const auto token = str.substr(last_token_index);
result.emplace_back(token);
}
catch (const std::invalid_argument&) {}
return result;
}
std::vector<std::string> Tokenize(std::string_view str, char delimiter)
{
std::vector<std::string> result;
size_t last_token_index = 0;
for (auto index = str.find(delimiter); index != std::string_view::npos; index = str.find(delimiter, index + 1))
{
const auto token = str.substr(last_token_index, index - last_token_index);
result.emplace_back(token);
last_token_index = index + 1;
}
try
{
const auto token = str.substr(last_token_index);
result.emplace_back(token);
}
catch (const std::invalid_argument&) {}
return result;
}
std::string GenerateRandomString(size_t length)
{
const std::string kCharacters{
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"1234567890" };
return GenerateRandomString(length, kCharacters);
}
std::string GenerateRandomString(const size_t length, const std::string_view characters)
{
assert(!characters.empty());
std::string result;
result.resize(length);
std::random_device rd;
std::mt19937 gen(rd());
// workaround for static asserts using boost
boost::random::uniform_int_distribution<decltype(characters.size())> index_dist(0, characters.size() - 1);
std::generate_n(
result.begin(),
length,
[&] { return characters[index_dist(gen)]; }
);
return result;
}