1
0
mirror of synced 2024-12-12 07:51:05 +01:00
ImHex/lib/libimhex/source/helpers/utils.cpp

469 lines
13 KiB
C++
Raw Normal View History

#include <hex/helpers/utils.hpp>
2020-11-10 21:31:04 +01:00
#include <cstdio>
#include <codecvt>
sys/build: Properly support per-system metadata file paths (#181) * sys: Move away from metadata paths next to executable in the application Build system doesn't properly install / pack stuff yet * build: Updated README to contain better install instructions * sys: Search for imhex resource files in ~/Application Support * sys: MAX_PATH -> PATH_MAX * sys: Seach for imhex resource files in Application Support using NSFileManager (#180) * sys: Allow for multiple file search paths Also use install prefix instead of just /usr on Linux * build: Fixed IMHEX_INSTALL_PREFIX macro definition * build: Fix duplicate switch entry on Linux * docs: Updated readme to properly reflect new paths and dependencies * sys: Install files in their proper paths on linux (#183) * Install files in their proper paths on linux * Only create user directories * Follow the XDG specification on linux XDG specification specifies how to find config and data directories on linux systems. Specifically, it says this: - Data should be written to $XDG_DATA_HOME - Config should be written to $XDG_CONFIG_HOME - Data should be read from $XDG_DATA_HOME:$XDG_DATA_DIRS - Config should be read from $XDG_CONFIG_HOME:$XDG_CONFIG_DIRS The default values are this: - XDG_DATA_HOME: $HOME/.local/share - XDG_CONFIG_HOME: $HOME/.config - XDG_DATA_DIRS: /usr/share:/usr/local/share - XDG_CONFIG_DIRS: /etc/xdg Platforms with non-standard filesystems (like NixOS) will correctly set up those environment variables, allowing softwares to work unmodified. In order to make integration as simple as possible, we use a simple header-only dependency called XDGPP which does all the hard work for us to find the default directories. * Look for plugins in all Plugin Paths If the plugin folder was missing from one of the PluginPaths, we would immediately stop loading plugins. We now keep looking even if one of the path is missing. Co-authored-by: Nichole Mattera <me@nicholemattera.com> Co-authored-by: Robin Lambertz <unfiltered@roblab.la>
2021-03-01 08:56:49 +01:00
#include <hex/api/imhex_api.hpp>
2021-08-29 22:15:18 +02:00
#include <hex/helpers/fmt.hpp>
#include <imgui.h>
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui_internal.h>
2021-08-29 22:15:18 +02:00
#if defined(OS_WINDOWS)
#include <windows.h>
#elif defined(OS_LINUX)
#include <unistd.h>
#elif defined(OS_MACOS)
#include <hex/helpers/utils_macos.hpp>
#include <unistd.h>
#endif
2020-11-10 21:31:04 +01:00
namespace hex {
long double operator""_scaled(long double value) {
return value * ImHexApi::System::getGlobalScale();
}
long double operator""_scaled(unsigned long long value) {
return value * ImHexApi::System::getGlobalScale();
}
ImVec2 scaled(const ImVec2 &vector) {
return vector * ImHexApi::System::getGlobalScale();
}
std::string to_string(u128 value) {
char data[45] = { 0 };
u8 index = sizeof(data) - 2;
while (value != 0 && index != 0) {
data[index] = '0' + value % 10;
value /= 10;
index--;
}
return { data + index + 1 };
}
std::string to_string(i128 value) {
char data[45] = { 0 };
u128 unsignedValue = value < 0 ? -value : value;
u8 index = sizeof(data) - 2;
while (unsignedValue != 0 && index != 0) {
data[index] = '0' + unsignedValue % 10;
unsignedValue /= 10;
index--;
}
if (value < 0) {
data[index] = '-';
return { data + index };
} else
return { data + index + 1 };
}
std::string toByteString(u64 bytes) {
double value = bytes;
u8 unitIndex = 0;
while (value > 1024) {
value /= 1024;
unitIndex++;
if (unitIndex == 6)
break;
}
2022-06-30 11:29:20 +02:00
std::string result;
if (unitIndex == 0)
result = hex::format("{0:}", value);
else
result = hex::format("{0:.2f}", value);
switch (unitIndex) {
2022-02-01 22:09:44 +01:00
case 0:
2022-06-30 11:29:20 +02:00
result += ((value == 1) ? " Byte" : " Bytes");
2022-02-01 22:09:44 +01:00
break;
case 1:
2022-06-30 11:43:40 +02:00
result += " kiB";
2022-02-01 22:09:44 +01:00
break;
case 2:
2022-06-30 11:43:40 +02:00
result += " MiB";
2022-02-01 22:09:44 +01:00
break;
case 3:
2022-06-30 11:43:40 +02:00
result += " GiB";
2022-02-01 22:09:44 +01:00
break;
case 4:
2022-06-30 11:43:40 +02:00
result += " TiB";
2022-02-01 22:09:44 +01:00
break;
case 5:
2022-06-30 11:43:40 +02:00
result += " PiB";
2022-02-01 22:09:44 +01:00
break;
case 6:
2022-06-30 11:43:40 +02:00
result += " EiB";
2022-02-01 22:09:44 +01:00
break;
default:
result = "A lot!";
}
return result;
}
std::string makeStringPrintable(const std::string &string) {
std::string result;
for (char c : string) {
if (std::isprint(c))
result += c;
else
result += hex::format("\\x{0:02X}", u8(c));
}
return result;
}
std::string makePrintable(u8 c) {
switch (c) {
2022-02-01 22:09:44 +01:00
case 0:
return "NUL";
case 1:
return "SOH";
case 2:
return "STX";
case 3:
return "ETX";
case 4:
return "EOT";
case 5:
return "ENQ";
case 6:
return "ACK";
case 7:
return "BEL";
case 8:
return "BS";
case 9:
return "TAB";
case 10:
return "LF";
case 11:
return "VT";
case 12:
return "FF";
case 13:
return "CR";
case 14:
return "SO";
case 15:
return "SI";
case 16:
return "DLE";
case 17:
return "DC1";
case 18:
return "DC2";
case 19:
return "DC3";
case 20:
return "DC4";
case 21:
return "NAK";
case 22:
return "SYN";
case 23:
return "ETB";
case 24:
return "CAN";
case 25:
return "EM";
case 26:
return "SUB";
case 27:
return "ESC";
case 28:
return "FS";
case 29:
return "GS";
case 30:
return "RS";
case 31:
return "US";
case 32:
return "Space";
case 127:
return "DEL";
case 128 ... 255:
return " ";
default:
return std::string() + static_cast<char>(c);
}
}
std::vector<std::string> splitString(const std::string &string, const std::string &delimiter) {
size_t start = 0, end = 0;
std::string token;
std::vector<std::string> res;
while ((end = string.find(delimiter, start)) != std::string::npos) {
size_t size = end - start;
if (start + size > string.length())
break;
token = string.substr(start, end - start);
start = end + delimiter.length();
res.push_back(token);
}
res.emplace_back(string.substr(start));
return res;
}
std::string combineStrings(const std::vector<std::string> &strings, const std::string &delimiter) {
std::string result;
for (const auto &string : strings) {
result += string;
result += delimiter;
}
return result.substr(0, result.length() - delimiter.length());
}
std::string toEngineeringString(double value) {
2021-02-17 14:47:25 +01:00
constexpr std::array Suffixes = { "a", "f", "p", "n", "u", "m", "", "k", "M", "G", "T", "P", "E" };
2021-02-17 14:47:25 +01:00
int8_t suffixIndex = 6;
2021-02-17 14:47:25 +01:00
while (suffixIndex != 0 && suffixIndex != 12 && (value >= 1000 || value < 1) && value != 0) {
if (value >= 1000) {
value /= 1000;
2021-02-17 14:47:25 +01:00
suffixIndex++;
} else if (value < 1) {
value *= 1000;
2021-02-17 14:47:25 +01:00
suffixIndex--;
}
}
2021-02-17 14:47:25 +01:00
return std::to_string(value).substr(0, 5) + Suffixes[suffixIndex];
}
void runCommand(const std::string &command) {
#if defined(OS_WINDOWS)
auto result = system(hex::format("start {0}", command).c_str());
#elif defined(OS_MACOS)
auto result = system(hex::format("open {0}", command).c_str());
#elif defined(OS_LINUX)
auto result = system(hex::format("xdg-open {0}", command).c_str());
#endif
hex::unused(result);
}
void openWebpage(std::string url) {
if (url.find("://") == std::string::npos)
url = "https://" + url;
#if defined(OS_WINDOWS)
ShellExecute(nullptr, "open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
#elif defined(OS_MACOS)
openWebpageMacos(url.c_str());
#elif defined(OS_LINUX)
auto result = system(hex::format("xdg-open {0}", url).c_str());
hex::unused(result);
#else
#warning "Unknown OS, can't open webpages"
#endif
}
std::string encodeByteString(const std::vector<u8> &bytes) {
std::string result;
for (u8 byte : bytes) {
if (std::isprint(byte) && byte != '\\')
result += char(byte);
else {
switch (byte) {
case '\\':
result += "\\";
break;
case '\a':
result += "\\a";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
case '\v':
result += "\\v";
break;
default:
result += hex::format("\\x{:02X}", byte);
break;
}
}
}
return result;
}
std::vector<u8> decodeByteString(const std::string &string) {
u32 offset = 0;
std::vector<u8> result;
while (offset < string.length()) {
auto c = [&] { return string[offset]; };
if (c() == '\\') {
if ((offset + 2) >= string.length()) return {};
offset++;
char escapeChar = c();
offset++;
switch (escapeChar) {
case 'a':
result.push_back('\a');
break;
case 'b':
result.push_back('\b');
break;
case 'f':
result.push_back('\f');
break;
case 'n':
result.push_back('\n');
break;
case 'r':
result.push_back('\r');
break;
case 't':
result.push_back('\t');
break;
case 'v':
result.push_back('\v');
break;
case '\\':
result.push_back('\\');
break;
case 'x':
{
u8 byte = 0x00;
if ((offset + 1) >= string.length()) return {};
for (u8 i = 0; i < 2; i++) {
byte <<= 4;
if (c() >= '0' && c() <= '9')
byte |= 0x00 + (c() - '0');
else if (c() >= 'A' && c() <= 'F')
byte |= 0x0A + (c() - 'A');
else if (c() >= 'a' && c() <= 'f')
byte |= 0x0A + (c() - 'a');
else
return {};
offset++;
}
result.push_back(byte);
}
break;
default:
return {};
}
} else {
result.push_back(c());
offset++;
}
}
return result;
}
float float16ToFloat32(u16 float16) {
2022-02-01 22:09:44 +01:00
u32 sign = float16 >> 15;
u32 exponent = (float16 >> 10) & 0x1F;
u32 mantissa = float16 & 0x3FF;
u32 result = 0x00;
if (exponent == 0) {
if (mantissa == 0) {
// +- Zero
result = sign << 31;
} else {
// Subnormal value
exponent = 0x7F - 14;
while ((mantissa & (1 << 10)) == 0) {
exponent--;
mantissa <<= 1;
}
mantissa &= 0x3FF;
result = (sign << 31) | (exponent << 23) | (mantissa << 13);
}
} else if (exponent == 0x1F) {
// +-Inf or +-NaN
result = (sign << 31) | (0xFF << 23) | (mantissa << 13);
} else {
// Normal value
result = (sign << 31) | ((exponent + (0x7F - 15)) << 23) | (mantissa << 13);
}
float floatResult = 0;
std::memcpy(&floatResult, &result, sizeof(float));
return floatResult;
}
bool isProcessElevated() {
#if defined(OS_WINDOWS)
bool elevated = false;
2022-02-01 22:09:44 +01:00
HANDLE token = INVALID_HANDLE_VALUE;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) {
TOKEN_ELEVATION elevation;
DWORD elevationSize = sizeof(TOKEN_ELEVATION);
if (::GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &elevationSize))
elevated = elevation.TokenIsElevated;
}
if (token != INVALID_HANDLE_VALUE)
::CloseHandle(token);
return elevated;
#elif defined(OS_LINUX) || defined(OS_MACOS)
return getuid() == 0 || getuid() != geteuid();
#endif
}
std::optional<std::string> getEnvironmentVariable(const std::string &env) {
auto value = std::getenv(env.c_str());
if (value == nullptr)
return std::nullopt;
else
return value;
}
2020-11-10 21:31:04 +01:00
}