#include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #include #elif defined(OS_LINUX) #include #include #include #elif defined(OS_MACOS) #include #include #include #elif defined(OS_WEB) #include "emscripten.h" #endif namespace hex { float operator""_scaled(long double value) { return value * ImHexApi::System::getGlobalScale(); } float operator""_scaled(unsigned long long value) { return value * ImHexApi::System::getGlobalScale(); } ImVec2 scaled(const ImVec2 &vector) { return vector * ImHexApi::System::getGlobalScale(); } ImVec2 scaled(float x, float y) { return ImVec2(x, y) * 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 toLower(std::string string) { for (char &c : string) c = std::tolower(c); return string; } std::string toUpper(std::string string) { for (char &c : string) c = std::toupper(c); return string; } std::vector parseHexString(std::string string) { if (string.empty()) return { }; // Remove common hex prefixes and commas string = hex::replaceStrings(string, "0x", ""); string = hex::replaceStrings(string, "0X", ""); string = hex::replaceStrings(string, ",", ""); // Check for non-hex characters bool isValidHexString = std::find_if(string.begin(), string.end(), [](char c) { return !std::isxdigit(c) && !std::isspace(c); }) == string.end(); if (!isValidHexString) return { }; // Remove all whitespace std::erase_if(string, [](char c) { return std::isspace(c); }); // Only parse whole bytes if (string.length() % 2 != 0) return { }; // Convert hex string to bytes return crypt::decode16(string); } std::optional parseBinaryString(const std::string &string) { if (string.empty()) return std::nullopt; u8 byte = 0x00; for (char c : string) { byte <<= 1; if (c == '1') byte |= 0b01; else if (c == '0') byte |= 0b00; else return std::nullopt; } return byte; } std::string toByteString(u64 bytes) { double value = bytes; u8 unitIndex = 0; while (value > 1024) { value /= 1024; unitIndex++; if (unitIndex == 6) break; } std::string result; if (unitIndex == 0) result = hex::format("{0:}", value); else result = hex::format("{0:.2f}", value); switch (unitIndex) { case 0: result += ((value == 1) ? " Byte" : " Bytes"); break; case 1: result += " kiB"; break; case 2: result += " MiB"; break; case 3: result += " GiB"; break; case 4: result += " TiB"; break; case 5: result += " PiB"; break; case 6: result += " EiB"; 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) { 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"; default: if (c >= 128) return " "; else return std::string() + static_cast(c); } } std::vector splitString(const std::string &string, const std::string &delimiter) { size_t start = 0, end = 0; std::vector res; while ((end = string.find(delimiter, start)) != std::string::npos) { size_t size = end - start; if (start + size > string.length()) break; std::string 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 &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 replaceStrings(std::string string, const std::string &search, const std::string &replace) { if (search.empty()) return string; std::size_t pos; while ((pos = string.find(search)) != std::string::npos) string.replace(pos, search.size(), replace); return string; } std::string toEngineeringString(double value) { constexpr static std::array Suffixes = { "a", "f", "p", "n", "u", "m", "", "k", "M", "G", "T", "P", "E" }; int8_t suffixIndex = 6; while (suffixIndex != 0 && suffixIndex != 12 && (value >= 1000 || value < 1) && value != 0) { if (value >= 1000) { value /= 1000; suffixIndex++; } else if (value < 1) { value *= 1000; suffixIndex--; } } return std::to_string(value).substr(0, 5) + Suffixes[suffixIndex]; } void startProgram(const std::string &command) { #if defined(OS_WINDOWS) hex::unused(system(hex::format("start {0}", command).c_str())); #elif defined(OS_MACOS) hex::unused(system(hex::format("open {0}", command).c_str())); #elif defined(OS_LINUX) executeCmd({"xdg-open", command}); #elif defined(OS_WEB) hex::unused(command); #endif } int executeCommand(const std::string &command) { return ::system(command.c_str()); } void openWebpage(std::string url) { if (!url.contains("://")) 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) executeCmd({"xdg-open", url}); #elif defined(OS_WEB) EM_ASM({ window.open(UTF8ToString($0), '_blank'); }, url.c_str()); #else #warning "Unknown OS, can't open webpages" #endif } std::optional hexCharToValue(char c) { if (std::isdigit(c)) return c - '0'; else if (std::isxdigit(c)) return std::toupper(c) - 'A' + 0x0A; else return { }; } std::string encodeByteString(const std::vector &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 decodeByteString(const std::string &string) { u32 offset = 0; std::vector 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 (auto hexValue = hexCharToValue(c()); hexValue.has_value()) byte |= hexValue.value(); else return {}; offset++; } result.push_back(byte); } break; default: return {}; } } else { result.push_back(c()); offset++; } } return result; } std::wstring utf8ToUtf16(const std::string& utf8) { std::vector unicodes; for (size_t byteIndex = 0; byteIndex < utf8.size();) { u32 unicode = 0; size_t unicodeSize = 0; u8 ch = utf8[byteIndex]; byteIndex += 1; if (ch <= 0x7F) { unicode = ch; unicodeSize = 0; } else if (ch <= 0xBF) { return { }; } else if (ch <= 0xDF) { unicode = ch&0x1F; unicodeSize = 1; } else if (ch <= 0xEF) { unicode = ch&0x0F; unicodeSize = 2; } else if (ch <= 0xF7) { unicode = ch&0x07; unicodeSize = 3; } else { return { }; } for (size_t unicodeByteIndex = 0; unicodeByteIndex < unicodeSize; unicodeByteIndex += 1) { if (byteIndex == utf8.size()) return { }; u8 byte = utf8[byteIndex]; if (byte < 0x80 || byte > 0xBF) return { }; unicode <<= 6; unicode += byte & 0x3F; byteIndex += 1; } if (unicode >= 0xD800 && unicode <= 0xDFFF) return { }; if (unicode > 0x10FFFF) return { }; unicodes.push_back(unicode); } std::wstring utf16; for (auto unicode : unicodes) { if (unicode <= 0xFFFF) { utf16 += static_cast(unicode); } else { unicode -= 0x10000; utf16 += static_cast(((unicode >> 10) + 0xD800)); utf16 += static_cast(((unicode & 0x3FF) + 0xDC00)); } } return utf16; } std::string utf16ToUtf8(const std::wstring& utf16) { std::vector unicodes; for (size_t index = 0; index < utf16.size();) { u32 unicode = 0; wchar_t wch = utf16[index]; index += 1; if (wch < 0xD800 || wch > 0xDFFF) { unicode = static_cast(wch); } else if (wch >= 0xD800 && wch <= 0xDBFF) { if (index == utf16.size()) return ""; wchar_t nextWch = utf16[index]; index += 1; if (nextWch < 0xDC00 || nextWch > 0xDFFF) return ""; unicode = static_cast(((wch - 0xD800) << 10) + (nextWch - 0xDC00) + 0x10000); } else { return ""; } unicodes.push_back(unicode); } std::string utf8; for (auto unicode : unicodes) { if (unicode <= 0x7F) { utf8 += static_cast(unicode); } else if (unicode <= 0x7FF) { utf8 += static_cast(0xC0 | ((unicode >> 6) & 0x1F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else if (unicode <= 0xFFFF) { utf8 += static_cast(0xE0 | ((unicode >> 12) & 0x0F)); utf8 += static_cast(0x80 | ((unicode >> 6) & 0x3F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else if (unicode <= 0x10FFFF) { utf8 += static_cast(0xF0 | ((unicode >> 18) & 0x07)); utf8 += static_cast(0x80 | ((unicode >> 12) & 0x3F)); utf8 += static_cast(0x80 | ((unicode >> 6) & 0x3F)); utf8 += static_cast(0x80 | (unicode & 0x3F)); } else { return ""; } } return utf8; } float float16ToFloat32(u16 float16) { 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; 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(); #else return false; #endif } std::optional getEnvironmentVariable(const std::string &env) { auto value = std::getenv(env.c_str()); if (value == nullptr) return std::nullopt; else return value; } static std::optional s_fileToOpen; extern "C" void openFile(const char *path) { log::info("Opening file: {0}", path); s_fileToOpen = path; } std::optional getInitialFilePath() { return s_fileToOpen; } static std::map s_fonts; extern "C" void registerFont(const char *fontName, const char *fontPath) { s_fonts[fontPath] = fontName; } const std::map& getFonts() { return s_fonts; } namespace { std::string generateHexViewImpl(u64 offset, auto begin, auto end) { constexpr static auto HeaderLine = "Hex View 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"; std::string result; const auto size = std::distance(begin, end); result.reserve(std::string(HeaderLine).size() * size / 0x10); result += HeaderLine; u64 address = offset & ~u64(0x0F); std::string asciiRow; for (auto it = begin; it != end; ++it) { u8 byte = *it; if ((address % 0x10) == 0) { result += hex::format(" {}", asciiRow); result += hex::format("\n{0:08X} ", address); asciiRow.clear(); if (address == (offset & ~u64(0x0F))) { for (u64 i = 0; i < (offset - address); i++) { result += " "; asciiRow += " "; } if (offset - address >= 8) result += " "; address = offset; } } result += hex::format("{0:02X} ", byte); asciiRow += std::isprint(byte) ? char(byte) : '.'; if ((address % 0x10) == 0x07) result += " "; address++; } if ((address % 0x10) != 0x00) for (u32 i = 0; i < (0x10 - (address % 0x10)); i++) result += " "; result += hex::format(" {}", asciiRow); return result; } } std::string generateHexView(u64 offset, u64 size, prv::Provider *provider) { auto reader = prv::ProviderReader(provider); reader.seek(offset); reader.setEndAddress((offset + size) - 1); return generateHexViewImpl(offset, reader.begin(), reader.end()); } std::string generateHexView(u64 offset, const std::vector &data) { return generateHexViewImpl(offset, data.begin(), data.end()); } std::string formatSystemError(i32 error) { #if defined(OS_WINDOWS) wchar_t *message = nullptr; auto wLength = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (wchar_t*)&message, 0, nullptr ); ON_SCOPE_EXIT { LocalFree(message); }; auto length = ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, nullptr, 0, nullptr, nullptr); std::string result(length, '\x00'); ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, result.data(), length, nullptr, nullptr); return result; #else return std::system_category().message(error); #endif } void* getContainingModule(void* symbol) { #if defined(OS_WINDOWS) MEMORY_BASIC_INFORMATION mbi; if (VirtualQuery(symbol, &mbi, sizeof(mbi))) return mbi.AllocationBase; return nullptr; #elif !defined(OS_WEB) Dl_info info = {}; if (dladdr(symbol, &info) == 0) return nullptr; return dlopen(info.dli_fname, RTLD_LAZY); #else hex::unused(symbol); return nullptr; #endif } }