diff --git a/.gitmodules b/.gitmodules index 4e7bbee3d..d1a32ab3a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,6 +21,8 @@ [submodule "lib/external/capstone"] path = lib/external/capstone url = https://github.com/capstone-engine/capstone + ignore = dirty [submodule "lib/external/libromfs"] path = lib/external/libromfs url = https://github.com/WerWolv/libromfs + ignore = dirty diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index fa785d985..001a232f3 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -41,6 +41,10 @@ namespace hex { void runCommand(const std::string &command); void openWebpage(std::string url); + std::string encodeByteString(const std::vector &bytes); + std::vector decodeByteString(const std::string &string); + + [[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const hex::unsigned_integral auto &value) { if (from < to) std::swap(from, to); @@ -247,7 +251,8 @@ namespace hex { trimRight(s); } - enum class DialogMode { + enum class DialogMode + { Open, Save, Folder @@ -307,7 +312,9 @@ namespace hex { ScopeGuard &operator=(ScopeGuard &&) = delete; }; - enum class ScopeGuardOnExit { }; + enum class ScopeGuardOnExit + { + }; template constexpr ScopeGuard operator+(ScopeGuardOnExit, F &&f) { @@ -328,7 +335,9 @@ namespace hex { FirstTimeExecute &operator=(FirstTimeExecute &&) = delete; }; - enum class FirstTimeExecutor { }; + enum class FirstTimeExecutor + { + }; template constexpr FirstTimeExecute operator+(FirstTimeExecutor, F &&f) { @@ -352,7 +361,9 @@ namespace hex { FinalCleanupExecute &operator=(FinalCleanupExecute &&) = delete; }; - enum class FinalCleanupExecutor { }; + enum class FinalCleanupExecutor + { + }; template constexpr FinalCleanupExecute operator+(FinalCleanupExecutor, F &&f) { diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 2d5dabc82..c940f08a6 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -276,6 +276,124 @@ namespace hex { #endif } + 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 (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; + } + + void openFileBrowser(const std::string &title, DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath) { NFD::Init(); diff --git a/plugins/builtin/source/content/data_inspector.cpp b/plugins/builtin/source/content/data_inspector.cpp index 798bcfed8..83152f6f4 100644 --- a/plugins/builtin/source/content/data_inspector.cpp +++ b/plugins/builtin/source/content/data_inspector.cpp @@ -13,6 +13,7 @@ #include #include +#include namespace hex::plugin::builtin { @@ -39,7 +40,7 @@ namespace hex::plugin::builtin { template static std::vector stringToSigned(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) { - i64 result = std::strtoull(value.c_str(), nullptr, 0); + i64 result = std::strtoll(value.c_str(), nullptr, 0); if (result > std::numeric_limits::max() || result < std::numeric_limits::min()) return {}; std::vector bytes(sizeof(T), 0x00); @@ -74,12 +75,13 @@ namespace hex::plugin::builtin { return {}; } + // clang-format off void registerDataInspectorEntries() { using Style = ContentRegistry::DataInspector::NumberDisplayStyle; - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.binary", sizeof(u8), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.binary", sizeof(u8), + [](auto buffer, auto endian, auto style) { std::string binary = "0b"; for (u8 i = 0; i < 8; i++) binary += ((buffer[0] << i) & 0x80) == 0 ? '0' : '1'; @@ -87,7 +89,8 @@ namespace hex::plugin::builtin { return [binary] { ImGui::TextUnformatted(binary.c_str()); return binary; - }; }, [](std::string value, std::endian endian) -> std::vector { + }; + }, [](std::string value, std::endian endian) -> std::vector { if (value.starts_with("0b")) value = value.substr(2); @@ -104,20 +107,23 @@ namespace hex::plugin::builtin { return { }; } - return { byte }; }); + return { byte }; + } + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.u8", sizeof(u8), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.u8", sizeof(u8), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:02X}" : "0o{0:03o}"); auto value = hex::format(format, *reinterpret_cast(buffer.data())); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.i8", sizeof(i8), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.i8", sizeof(i8), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:02X}" : "{0}0o{1:03o}"); auto number = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); @@ -126,20 +132,22 @@ namespace hex::plugin::builtin { return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.u16", sizeof(u16), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.u16", sizeof(u16), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:04X}" : "0o{0:06o}"); auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.i16", sizeof(i16), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.i16", sizeof(i16), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:04X}" : "{0}0o{1:06o}"); auto number = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); @@ -148,20 +156,22 @@ namespace hex::plugin::builtin { return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.u32", sizeof(u32), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.u32", sizeof(u32), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:08X}" : "0o{0:011o}"); auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.i32", sizeof(i32), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.i32", sizeof(i32), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:08X}" : "{0}0o{1:011o}"); auto number = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); @@ -170,20 +180,22 @@ namespace hex::plugin::builtin { return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.u64", sizeof(u64), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.u64", sizeof(u64), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:016X}" : "0o{0:022o}"); auto value = hex::format(format, hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.i64", sizeof(i64), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.i64", sizeof(i64), + [](auto buffer, auto endian, auto style) { auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:016X}" : "{0}0o{1:022o}"); auto number = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); @@ -192,98 +204,118 @@ namespace hex::plugin::builtin { return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToInteger); + stringToInteger + ); - ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16), [](auto buffer, auto endian, auto style) { - auto value = hex::format("{0:G}", hex::changeEndianess(float16ToFloat32(*reinterpret_cast(buffer.data())), endian)); - return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; - }); + ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16), + [](auto buffer, auto endian, auto style) { + auto value = hex::format("{0:G}", hex::changeEndianess(float16ToFloat32(*reinterpret_cast(buffer.data())), endian)); + return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; + } + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.float", sizeof(float), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.float", sizeof(float), + [](auto buffer, auto endian, auto style) { auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToFloat); + stringToFloat + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.double", sizeof(double), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.double", sizeof(double), + [](auto buffer, auto endian, auto style) { auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToFloat); + stringToFloat + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.long_double", sizeof(long double), [](auto buffer, auto endian, auto style) { + ContentRegistry::DataInspector::add("hex.builtin.inspector.long_double", sizeof(long double), + [](auto buffer, auto endian, auto style) { auto value = hex::format("{0:G}", hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, - stringToFloat); + stringToFloat + ); - ContentRegistry::DataInspector::add( - "hex.builtin.inspector.ascii", sizeof(char8_t), [](auto buffer, auto endian, auto style) { - auto value = hex::format("'{0}'", makePrintable(*reinterpret_cast(buffer.data())).c_str()); - return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, [](const std::string &value, std::endian endian) -> std::vector { - if (value.length() > 3) return { }; + ContentRegistry::DataInspector::add("hex.builtin.inspector.ascii", sizeof(char8_t), + [](auto buffer, auto endian, auto style) { + auto value = makePrintable(*reinterpret_cast(buffer.data())); + return [value] { ImGui::TextFormatted("'{0}'", value.c_str()); return value; }; + }, + [](const std::string &value, std::endian endian) -> std::vector { + if (value.length() > 1) return { }; - u8 character = 0x00; - if (value.length() == 3 && value.starts_with('\'') && value.ends_with('\'')) - character = value[1]; - else if (value.length() == 1) - character = value[0]; - else - return { }; + return { u8(value[0]) }; + } + ); - return { character }; }); + ContentRegistry::DataInspector::add("hex.builtin.inspector.wide", sizeof(wchar_t), + [](auto buffer, auto endian, auto style) { + wchar_t wideChar = '\x00'; + std::memcpy(&wideChar, buffer.data(), std::min(sizeof(wchar_t), buffer.size())); - ContentRegistry::DataInspector::add("hex.builtin.inspector.wide", sizeof(wchar_t), [](auto buffer, auto endian, auto style) { - wchar_t wideChar = '\x00'; - std::memcpy(&wideChar, buffer.data(), std::min(sizeof(wchar_t), buffer.size())); + auto c = hex::changeEndianess(wideChar, endian); - auto c = hex::changeEndianess(wideChar, endian); + std::wstring_convert> converter("Invalid"); - std::wstring_convert> converter("Invalid"); + auto value = hex::format("{0}", c <= 255 ? makePrintable(c) : converter.to_bytes(c)); + return [value] { ImGui::TextFormatted("'{0}'", value.c_str()); return value; }; + }, + [](const std::string &value, std::endian endian) -> std::vector { + std::wstring_convert> converter(""); - auto value = hex::format("'{0}'", c <= 255 ? makePrintable(c) : converter.to_bytes(c)); - return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; - }); + std::vector bytes; + auto wideString = converter.from_bytes(value.c_str()); + std::copy(wideString.begin(), wideString.end(), std::back_inserter(bytes)); - ContentRegistry::DataInspector::add("hex.builtin.inspector.utf8", sizeof(char8_t) * 4, [](auto buffer, auto endian, auto style) { - char utf8Buffer[5] = { 0 }; - char codepointString[5] = { 0 }; - u32 codepoint = 0; + if (endian != std::endian::native) + std::reverse(bytes.begin(), bytes.end()); - std::memcpy(utf8Buffer, reinterpret_cast(buffer.data()), 4); - u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4); + return bytes; + } + ); - std::memcpy(codepointString, utf8Buffer, std::min(codepointSize, u8(4))); - auto value = hex::format("'{0}' (U+0x{1:04X})", - codepoint == 0xFFFD ? "Invalid" : (codepointSize == 1 ? makePrintable(codepointString[0]) : codepointString), - codepoint); + ContentRegistry::DataInspector::add("hex.builtin.inspector.utf8", sizeof(char8_t) * 4, + [](auto buffer, auto endian, auto style) { + char utf8Buffer[5] = { 0 }; + char codepointString[5] = { 0 }; + u32 codepoint = 0; - return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; - }); + std::memcpy(utf8Buffer, reinterpret_cast(buffer.data()), 4); + u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4); - ContentRegistry::DataInspector::add("hex.builtin.inspector.string", 1, [](auto buffer, auto endian, auto style) { - Region currSelection = { 0 }; - EventManager::post(currSelection); + std::memcpy(codepointString, utf8Buffer, std::min(codepointSize, u8(4))); + auto value = hex::format("'{0}' (U+0x{1:04X})", + codepoint == 0xFFFD ? "Invalid" : (codepointSize == 1 ? makePrintable(codepointString[0]) : codepointString), + codepoint); - constexpr static auto MaxStringLength = 32; + return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; + } + ); - std::string stringBuffer(std::min(MaxStringLength, currSelection.size), 0x00); - ImHexApi::Provider::get()->read(currSelection.address, stringBuffer.data(), stringBuffer.size()); - if (currSelection.size > MaxStringLength) - stringBuffer += "..."; + ContentRegistry::DataInspector::add("hex.builtin.inspector.string", 1, + [](auto buffer, auto endian, auto style) { + Region currSelection = { 0 }; + EventManager::post(currSelection); - for (auto &c : stringBuffer) - if (c < 0x20) - c = ' '; + constexpr static auto MaxStringLength = 32; + std::vector stringBuffer(std::min(MaxStringLength, currSelection.size), 0x00); + ImHexApi::Provider::get()->read(currSelection.address, stringBuffer.data(), stringBuffer.size()); - auto value = hex::format("\"{0}\"", stringBuffer); + auto value = hex::encodeByteString(stringBuffer); + auto copyValue = hex::encodeByteString(buffer); - return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; - }); + if (currSelection.size > MaxStringLength) + value += "..."; + + return [value, copyValue] { ImGui::TextFormatted("\"{0}\"", value.c_str()); return copyValue; }; + }, + [](const std::string &value, std::endian endian) -> std::vector { + return hex::decodeByteString(value); + } + ); #if defined(OS_WINDOWS) && defined(ARCH_64_BIT) @@ -331,7 +363,7 @@ namespace hex::plugin::builtin { #endif ContentRegistry::DataInspector::add("hex.builtin.inspector.guid", sizeof(GUID), [](auto buffer, auto endian, auto style) { - GUID guid; + GUID guid = { 0 }; std::memcpy(&guid, buffer.data(), sizeof(GUID)); auto value = hex::format("{}{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}", (hex::changeEndianess(guid.data3, endian) >> 12) <= 5 && ((guid.data4[0] >> 4) >= 8 || (guid.data4[0] >> 4) == 0) ? "" : "Invalid ", @@ -353,13 +385,14 @@ namespace hex::plugin::builtin { ContentRegistry::DataInspector::add("hex.builtin.inspector.rgba8", sizeof(u32), [](auto buffer, auto endian, auto style) { ImColor value(hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); - auto stringValue = hex::format("(0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X})", u8(0xFF * (value.Value.x)), u8(0xFF * (value.Value.y)), u8(0xFF * (value.Value.z)), u8(0xFF * (value.Value.w))); + auto copyValue = hex::format("#{:02X}{:02X}{:02X}{:02X}", u8(0xFF * (value.Value.x)), u8(0xFF * (value.Value.y)), u8(0xFF * (value.Value.z)), u8(0xFF * (value.Value.w))); - return [value, stringValue] { + return [value, copyValue] { ImGui::ColorButton("##inspectorColor", value, ImGuiColorEditFlags_None, ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeight())); - return stringValue; + return copyValue; }; }); } + // clang-format on } \ No newline at end of file