#include #include #include #include #include #include #include #include #include #include #include #include namespace hex::plugin::builtin { using Style = ContentRegistry::DataInspector::NumberDisplayStyle; struct GUID { u32 data1; u16 data2; u16 data3; u8 data4[8]; }; template static std::vector stringToUnsigned(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) { u64 result = std::strtoull(value.c_str(), nullptr, 0); if (result > std::numeric_limits::max()) return {}; std::vector bytes(Size, 0x00); std::memcpy(bytes.data(), &result, bytes.size()); if (endian != std::endian::native) std::reverse(bytes.begin(), bytes.end()); return bytes; } template static std::vector stringToSigned(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) { i64 result = std::strtoll(value.c_str(), nullptr, 0); if (result > std::numeric_limits::max() || result < std::numeric_limits::min()) return {}; std::vector bytes(Size, 0x00); std::memcpy(bytes.data(), &result, bytes.size()); if (endian != std::endian::native) std::reverse(bytes.begin(), bytes.end()); return bytes; } template static std::vector stringToFloat(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(long double)) { T result = std::strtold(value.c_str(), nullptr); std::vector bytes(sizeof(T), 0x00); std::memcpy(bytes.data(), &result, bytes.size()); if (endian != std::endian::native) std::reverse(bytes.begin(), bytes.end()); return bytes; } template static std::vector stringToInteger(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) { if constexpr (std::unsigned_integral) return stringToUnsigned(value, endian); else if constexpr (std::signed_integral) return stringToSigned(value, endian); else return {}; } template static std::string unsignedToString(const std::vector &buffer, std::endian endian, Style style) requires(sizeof(T) <= sizeof(u64)) { if (buffer.size() < Size) return { }; auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? hex::format("0x{{0:0{}X}}", Size * 2) : hex::format("0o{{0:0{}o}}", Size * 3)); T value = 0x00; std::memcpy(&value, buffer.data(), std::min(sizeof(T), Size)); return hex::format(format, hex::changeEndianess(value, Size, endian)); } template static std::string signedToString(const std::vector &buffer, std::endian endian, Style style) requires(sizeof(T) <= sizeof(u64)) { if (buffer.size() < Size) return { }; auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? hex::format("{{0}}0x{{1:0{}X}}", Size * 2) : hex::format("{{0}}0o{{1:0{}o}}", Size * 3)); T value = 0x00; std::memcpy(&value, buffer.data(), std::min(sizeof(T), Size)); auto number = hex::changeEndianess(value, Size, endian); if (Size != sizeof(T)) number = hex::signExtend(Size * 8, number); bool negative = number < 0; return hex::format(format, negative ? "-" : "", std::abs(number)); } template static std::string integerToString(const std::vector &buffer, std::endian endian, Style style) requires(sizeof(T) <= sizeof(u64)) { if constexpr (std::unsigned_integral) return unsignedToString(buffer, endian, style); else if constexpr (std::signed_integral) return signedToString(buffer, endian, style); else return {}; } template static hex::ContentRegistry::DataInspector::impl::GeneratorFunction drawString(T func) { return [func](const std::vector &buffer, std::endian endian, Style style) { return [value = func(buffer, endian, style)]() -> std::string { ImGui::TextUnformatted(value.c_str()); return value; }; }; } // clang-format off void registerDataInspectorEntries() { ContentRegistry::DataInspector::add("hex.builtin.inspector.binary", sizeof(u8), [](auto buffer, auto endian, auto style) { hex::unused(endian, style); std::string binary = hex::format("0b{:08b}", buffer[0]); return [binary] { ImGui::TextUnformatted(binary.c_str()); return binary; }; }, [](const std::string &value, std::endian endian) -> std::vector { hex::unused(endian); std::string binary = value; if (binary.starts_with("0b")) binary = binary.substr(2); if (binary.size() > 8) return { }; if (auto result = hex::parseBinaryString(binary); result.has_value()) return { result.value() }; else return { }; } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u8", sizeof(u8), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i8", sizeof(i8), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u16", sizeof(u16), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i16", sizeof(i16), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u24", 3, drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i24", 3, drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u32", sizeof(u32), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i32", sizeof(i32), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u48", 6, drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i48", 6, drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.u64", sizeof(u64), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.i64", sizeof(i64), drawString(integerToString), stringToInteger ); ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16), [](auto buffer, auto endian, auto style) { u16 result = 0; std::memcpy(&result, buffer.data(), sizeof(u16)); const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}"; auto value = hex::format(formatString, float16ToFloat32(hex::changeEndianess(result, 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) { float result = 0; std::memcpy(&result, buffer.data(), sizeof(float)); const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}"; auto value = hex::format(formatString, hex::changeEndianess(result, endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, stringToFloat ); ContentRegistry::DataInspector::add("hex.builtin.inspector.double", sizeof(double), [](auto buffer, auto endian, auto style) { double result = 0; std::memcpy(&result, buffer.data(), sizeof(double)); const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}"; auto value = hex::format(formatString, hex::changeEndianess(result, endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, stringToFloat ); ContentRegistry::DataInspector::add("hex.builtin.inspector.long_double", sizeof(long double), [](auto buffer, auto endian, auto style) { long double result = 0; std::memcpy(&result, buffer.data(), sizeof(long double)); const auto formatString = style == Style::Hexadecimal ? "{0:a}" : "{0:G}"; auto value = hex::format(formatString, hex::changeEndianess(result, endian)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, stringToFloat ); ContentRegistry::DataInspector::add("hex.builtin.inspector.sleb128", 1, (sizeof(i128) * 8 / 7) + 1, [](auto buffer, auto endian, auto style) { hex::unused(endian); auto format = (style == Style::Decimal) ? "{0}{1:d}" : ((style == Style::Hexadecimal) ? "{0}0x{1:X}" : "{0}0o{1:o}"); auto number = hex::crypt::decodeSleb128(buffer); bool negative = number < 0; auto value = hex::format(format, negative ? "-" : "", negative ? -number : number); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, [](const std::string &value, std::endian endian) -> std::vector { hex::unused(endian); return hex::crypt::encodeSleb128(std::strtoll(value.c_str(), nullptr, 0)); } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.uleb128", 1, (sizeof(u128) * 8 / 7) + 1, [](auto buffer, auto endian, auto style) { hex::unused(endian); auto format = (style == Style::Decimal) ? "{0:d}" : ((style == Style::Hexadecimal) ? "0x{0:X}" : "0o{0:o}"); auto value = hex::format(format, hex::crypt::decodeUleb128(buffer)); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }, [](const std::string &value, std::endian endian) -> std::vector { hex::unused(endian); return hex::crypt::encodeUleb128(std::strtoull(value.c_str(), nullptr, 0)); } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.bool", sizeof(bool), [](auto buffer, auto endian, auto style) { hex::unused(endian, style); std::string value = [buffer] { switch (buffer[0]) { case false: return "false"; case true: return "true"; default: return "Invalid"; } }(); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.ascii", sizeof(char8_t), [](auto buffer, auto endian, auto style) { hex::unused(endian, 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 { hex::unused(endian); if (value.length() > 1) return { }; return { u8(value[0]) }; } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.wide", sizeof(wchar_t), [](auto buffer, auto endian, auto style) { hex::unused(style); wchar_t wideChar = '\x00'; std::memcpy(&wideChar, buffer.data(), std::min(sizeof(wchar_t), buffer.size())); auto c = hex::changeEndianess(wideChar, endian); 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("Invalid"); std::vector bytes; auto wideString = converter.from_bytes(value.c_str()); std::copy(wideString.begin(), wideString.end(), std::back_inserter(bytes)); if (endian != std::endian::native) std::reverse(bytes.begin(), bytes.end()); return bytes; } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.utf8", sizeof(char8_t) * 4, [](auto buffer, auto endian, auto style) { hex::unused(endian, style); char utf8Buffer[5] = { 0 }; char codepointString[5] = { 0 }; u32 codepoint = 0; std::memcpy(utf8Buffer, reinterpret_cast(buffer.data()), 4); u8 codepointSize = ImTextCharFromUtf8(&codepoint, utf8Buffer, utf8Buffer + 4); 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); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; } ); constexpr static auto MaxStringLength = 32; ContentRegistry::DataInspector::add("hex.builtin.inspector.string", 1, [](auto buffer, auto endian, auto style) { hex::unused(buffer, endian, style); auto currSelection = ImHexApi::HexEditor::getSelection(); std::string value, copyValue; if (currSelection.has_value()) { std::vector stringBuffer(std::min(currSelection->size, 0x1000), 0x00); ImHexApi::Provider::get()->read(currSelection->address, stringBuffer.data(), stringBuffer.size()); value = copyValue = hex::encodeByteString(stringBuffer); if (value.size() > MaxStringLength) { value.resize(MaxStringLength); value += "..."; } } else { value = ""; copyValue = ""; } return [value, copyValue] { ImGui::TextFormatted("\"{0}\"", value.c_str()); return copyValue; }; }, [](const std::string &value, std::endian endian) -> std::vector { hex::unused(endian); return hex::decodeByteString(value); } ); ContentRegistry::DataInspector::add("hex.builtin.inspector.string16", 2, [](auto buffer, auto endian, auto style) { hex::unused(buffer, endian, style); auto currSelection = ImHexApi::HexEditor::getSelection(); std::string value, copyValue; if (currSelection.has_value()) { std::u16string stringBuffer(std::min(currSelection->size, 0x1000), 0x00); ImHexApi::Provider::get()->read(currSelection->address, stringBuffer.data(), stringBuffer.size()); for (auto &c : stringBuffer) c = hex::changeEndianess(c, endian); auto it = std::remove_if(buffer.begin(), buffer.end(), [](auto c) { return c == 0x00; }); buffer.erase(it, buffer.end()); auto string = std::wstring_convert, char16_t>("Invalid").to_bytes(stringBuffer.data()); value = copyValue = hex::encodeByteString({ string.begin(), string.end() }); if (value.size() > MaxStringLength) { value.resize(MaxStringLength); value += "..."; } } else { value = ""; copyValue = ""; } return [value, copyValue] { ImGui::TextFormatted("L\"{0}\"", value.c_str()); return copyValue; }; }, [](const std::string &value, std::endian endian) -> std::vector { hex::unused(endian); return hex::decodeByteString(value); } ); #if defined(OS_WINDOWS) && defined(ARCH_64_BIT) ContentRegistry::DataInspector::add("hex.builtin.inspector.time32", sizeof(u32), [](auto buffer, auto endian, auto style) { hex::unused(style); auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); std::string value; try { value = hex::format("{0:%a, %d.%m.%Y %H:%M:%S}", fmt::localtime(endianAdjustedTime)); } catch (fmt::format_error &) { value = "Invalid"; } return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); ContentRegistry::DataInspector::add("hex.builtin.inspector.time64", sizeof(u64), [](auto buffer, auto endian, auto style) { hex::unused(style); auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); std::string value; try { value = hex::format("{0:%a, %d.%m.%Y %H:%M:%S}", fmt::localtime(endianAdjustedTime)); } catch (fmt::format_error &) { value = "Invalid"; } return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); #else ContentRegistry::DataInspector::add("hex.builtin.inspector.time", sizeof(time_t), [](auto buffer, auto endian, auto style) { hex::unused(style); auto endianAdjustedTime = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); std::string value; try { value = hex::format("{0:%a, %d.%m.%Y %H:%M:%S}", fmt::localtime(endianAdjustedTime)); } catch (fmt::format_error &e) { value = "Invalid"; } return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); #endif struct DOSDate { unsigned day : 5; unsigned month : 4; unsigned year : 7; }; struct DOSTime { unsigned seconds : 5; unsigned minutes : 6; unsigned hours : 5; }; ContentRegistry::DataInspector::add("hex.builtin.inspector.dos_date", sizeof(DOSDate), [](auto buffer, auto endian, auto style) { hex::unused(style); DOSDate date = { }; std::memcpy(&date, buffer.data(), sizeof(DOSDate)); date = hex::changeEndianess(date, endian); auto value = hex::format("{}/{}/{}", date.day, date.month, date.year + 1980); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); ContentRegistry::DataInspector::add("hex.builtin.inspector.dos_time", sizeof(DOSTime), [](auto buffer, auto endian, auto style) { hex::unused(style); DOSTime time = { }; std::memcpy(&time, buffer.data(), sizeof(DOSTime)); time = hex::changeEndianess(time, endian); auto value = hex::format("{:02}:{:02}:{:02}", time.hours, time.minutes, time.seconds * 2); return [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); ContentRegistry::DataInspector::add("hex.builtin.inspector.guid", sizeof(GUID), [](auto buffer, auto endian, auto style) { hex::unused(style); GUID guid = { }; 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 ", hex::changeEndianess(guid.data1, endian), hex::changeEndianess(guid.data2, endian), hex::changeEndianess(guid.data3, endian), 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 [value] { ImGui::TextUnformatted(value.c_str()); return value; }; }); ContentRegistry::DataInspector::add("hex.builtin.inspector.rgba8", sizeof(u32), [](auto buffer, auto endian, auto style) { hex::unused(style); ImColor value(hex::changeEndianess(*reinterpret_cast(buffer.data()), endian)); 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, copyValue] { ImGui::ColorButton("##inspectorColor", value, ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); return copyValue; }; }); ContentRegistry::DataInspector::add("hex.builtin.inspector.rgb565", sizeof(u16), [](auto buffer, auto endian, auto style) { hex::unused(style); auto value = hex::changeEndianess(*reinterpret_cast(buffer.data()), endian); ImColor color((value & 0x1F) << 3, ((value >> 5) & 0x3F) << 2, ((value >> 11) & 0x1F) << 3, 0xFF); auto copyValue = hex::format("#{:02X}{:02X}{:02X}", u8(0xFF * (color.Value.x)), u8(0xFF * (color.Value.y)), u8(0xFF * (color.Value.z)), 0xFF); return [color, copyValue] { ImGui::ColorButton("##inspectorColor", color, ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); return copyValue; }; }); } // clang-format on }