From 4928c044af8d737f312528a0413f225881bd0add Mon Sep 17 00:00:00 2001 From: Polshakov Dmitry Date: Fri, 8 Apr 2022 17:08:02 +0300 Subject: [PATCH] patterns: Move pattern drawer into builtin plugin (#482) Co-authored-by: Dmitry Polshakov --- .../hex/pattern_language/pattern_drawer.hpp | 477 ------------------ plugins/builtin/CMakeLists.txt | 1 + .../content/views/view_pattern_data.hpp | 5 +- plugins/builtin/include/pattern_drawer.hpp | 79 +++ plugins/builtin/source/pattern_drawer.cpp | 469 +++++++++++++++++ 5 files changed, 552 insertions(+), 479 deletions(-) delete mode 100644 lib/libimhex/include/hex/pattern_language/pattern_drawer.hpp create mode 100644 plugins/builtin/include/pattern_drawer.hpp create mode 100644 plugins/builtin/source/pattern_drawer.cpp diff --git a/lib/libimhex/include/hex/pattern_language/pattern_drawer.hpp b/lib/libimhex/include/hex/pattern_language/pattern_drawer.hpp deleted file mode 100644 index 92f6ced1e..000000000 --- a/lib/libimhex/include/hex/pattern_language/pattern_drawer.hpp +++ /dev/null @@ -1,477 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace { - constexpr auto DisplayEndDefault = 50u; - constexpr auto DisplayEndStep = 50u; - - template - concept ArrayPattern = requires(T pattern, std::function fn) { - { pattern.forEachArrayEntry(fn) } -> std::same_as; - }; -}; - -namespace hex::pl { - - class PatternDrawer : public PatternVisitor - { - public: - PatternDrawer() - : m_provider{nullptr} - { } - - void setProvider(prv::Provider *provider) { - m_provider = provider; - } - - void visit(PatternArrayDynamic& pattern) override { - this->drawArray(pattern); - } - - void visit(PatternArrayStatic& pattern) override { - this->drawArray(pattern); - } - - void visit(PatternBitfieldField& pattern) override { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - drawNameColumn(pattern); - makeSelectable(pattern); - drawColorColumn(pattern); - - auto byteAddr = pattern.getOffset() + pattern.getBitOffset() / 8; - auto firstBitIdx = pattern.getBitOffset() % 8; - auto lastBitIdx = firstBitIdx + (pattern.getBitSize() - 1) % 8; - if (firstBitIdx == lastBitIdx) - ImGui::TextFormatted("0x{0:08X} bit {1}", byteAddr, firstBitIdx); - else - ImGui::TextFormatted("0x{0:08X} bits {1} - {2}", byteAddr, firstBitIdx, lastBitIdx); - ImGui::TableNextColumn(); - if (pattern.getBitSize() == 1) - ImGui::TextFormatted("{0} bit", pattern.getBitSize()); - else - ImGui::TextFormatted("{0} bits", pattern.getBitSize()); - ImGui::TableNextColumn(); - ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits"); - ImGui::TableNextColumn(); - - u64 extractedValue = pattern.getValue(m_provider); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), &pattern)); - } - - void visit(PatternBitfield& pattern) override { - std::vector value = pattern.getValue(m_provider); - - bool open = true; - if (!pattern.isInlined()) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - open = createTreeNode(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - drawCommentTooltip(pattern); - ImGui::TableNextColumn(); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - drawTypenameColumn(pattern, "bitfield"); - - std::string valueString = "{ "; - for (auto i : value) - valueString += hex::format("{0:02X} ", i); - valueString += "}"; - - ImGui::TextFormatted("{}", pattern.formatDisplayValue(valueString, &pattern)); - } else { - ImGui::SameLine(); - ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); - } - - if (open) { - pattern.forEachMember([&] (auto &field) { - this->draw(field); - }); - - ImGui::TreePop(); - } - } - - void visit(PatternBoolean& pattern) override { - u8 boolean = pattern.getValue(m_provider); - - if (boolean == 0) - this->createDefaultEntry(pattern, "false", false); - else if (boolean == 1) - this->createDefaultEntry(pattern, "true", true); - else - this->createDefaultEntry(pattern, "true*", true); - } - - void visit(PatternCharacter& pattern) override { - char character = pattern.getValue(m_provider); - this->createDefaultEntry(pattern, hex::format("'{0}'", character), character); - } - - void visit(PatternEnum& pattern) override { - u64 value = pattern.getValue(m_provider); - - std::string valueString = pattern.getTypeName() + "::"; - - bool foundValue = false; - for (auto &[entryValueLiteral, entryName] : pattern.getEnumValues()) { - auto visitor = overloaded { - [&, name = entryName](auto &entryValue) { - if (static_cast(value) == entryValue) { - valueString += name; - foundValue = true; - return true; - } - - return false; - }, - [](const std::string &) { return false; }, - [](Pattern *) { return false; }, - }; - - bool matches = std::visit(visitor, entryValueLiteral); - if (matches) - break; - } - - if (!foundValue) - valueString += "???"; - - ImGui::TableNextRow(); - createLeafNode(pattern); - drawCommentTooltip(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - ImGui::SameLine(); - drawNameColumn(pattern); - drawColorColumn(pattern); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - drawTypenameColumn(pattern, "enum"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, pattern.getSize() * 2), &pattern)); - } - - void visit(PatternFloat& pattern) override { - if (pattern.getSize() == 4) { - float f32 = static_cast(pattern.getValue(m_provider)); - u32 integerResult = 0; - std::memcpy(&integerResult, &f32, sizeof(float)); - this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f32, integerResult, pattern.getSize() * 2), f32); - } else if (pattern.getSize() == 8) { - double f64 = pattern.getValue(m_provider); - u64 integerResult = 0; - std::memcpy(&integerResult, &f64, sizeof(double)); - this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f64, integerResult, pattern.getSize() * 2), f64); - } - } - - void visit(PatternPadding& pattern) override { - // Do nothing - hex::unused(pattern); - } - - void visit(PatternPointer& pattern) override { - u64 data = pattern.getValue(m_provider); - - bool open = true; - - if (!pattern.isInlined()) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - open = createTreeNode(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - drawCommentTooltip(pattern); - ImGui::SameLine(0, 0); - drawColorColumn(pattern); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getFormattedName()); - ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("*(0x{0:X})", data), u128(data))); - } else { - ImGui::SameLine(); - ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); - } - - if (open) { - pattern.getPointedAtPattern()->accept(*this); - - ImGui::TreePop(); - } - } - - void visit(PatternSigned& pattern) override { - i128 data = pattern.getValue(m_provider); - this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data); - } - - void visit(PatternString& pattern) override { - auto size = std::min(pattern.getSize(), 0x7F); - - if (size == 0) - return; - - std::string displayString = pattern.getValue(m_provider, size); - this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", displayString, size > pattern.getSize() ? "(truncated)" : ""), displayString); - } - - void visit(PatternStruct& pattern) override { - bool open = true; - - if (!pattern.isInlined()) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - open = createTreeNode(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - drawCommentTooltip(pattern); - ImGui::TableNextColumn(); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - drawTypenameColumn(pattern, "struct"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); - } else { - ImGui::SameLine(); - ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); - } - - if (open) { - pattern.forEachMember([&](auto &member){ - this->draw(member); - }); - - ImGui::TreePop(); - } - } - - void visit(PatternUnion& pattern) override { - bool open = true; - - if (!pattern.isInlined()) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - open = createTreeNode(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - drawCommentTooltip(pattern); - ImGui::TableNextColumn(); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - drawTypenameColumn(pattern, "union"); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); - } else { - ImGui::SameLine(); - ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); - } - - if (open) { - pattern.forEachMember([&](auto &member) { - this->draw(member); - }); - - ImGui::TreePop(); - } - } - - void visit(PatternUnsigned& pattern) override { - u128 data = pattern.getValue(m_provider); - this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, pattern.getSize() * 2), data); - } - - void visit(PatternWideCharacter& pattern) override { - char16_t character = pattern.getValue(m_provider); - u128 literal = character; - auto str = std::wstring_convert, char16_t> {}.to_bytes(character); - this->createDefaultEntry(pattern, hex::format("'{0}'", str), literal); - } - - void visit(PatternWideString& pattern) override { - auto size = std::min(pattern.getSize(), 0x100); - - if (size == 0) - return; - - std::string utf8String = pattern.getValue(m_provider, size); - - this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", utf8String, size > pattern.getSize() ? "(truncated)" : ""), utf8String); - } - - private: - void createDefaultEntry(const Pattern &pattern, const std::string &value, Token::Literal &&literal) const { - ImGui::TableNextRow(); - createLeafNode(pattern); - ImGui::TableNextColumn(); - - makeSelectable(pattern); - - drawCommentTooltip(pattern); - ImGui::SameLine(); - drawNameColumn(pattern); - drawColorColumn(pattern); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getTypeName().empty() ? pattern.getFormattedName() : pattern.getTypeName()); - ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue(value, literal)); - } - - static void makeSelectable(const Pattern &pattern) { - ImGui::PushID(static_cast(pattern.getOffset())); - ImGui::PushID(pattern.getVariableName().c_str()); - if (ImGui::Selectable("##PatternLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { - ImHexApi::HexEditor::setSelection(pattern.getOffset(), pattern.getSize()); - } - ImGui::SameLine(); - ImGui::PopID(); - ImGui::PopID(); - } - - static void drawCommentTooltip(const Pattern &pattern) { - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && pattern.getComment().has_value()) { - ImGui::BeginTooltip(); - ImGui::TextUnformatted(pattern.getComment()->c_str()); - ImGui::EndTooltip(); - } - } - - void draw(Pattern& pattern) { - if (pattern.isHidden()) - return; - - pattern.accept(*this); - } - - template - void drawArray(T& pattern) { - if (pattern.getEntryCount() == 0) - return; - - bool open = true; - if (!pattern.isInlined()) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - open = createTreeNode(pattern); - ImGui::TableNextColumn(); - makeSelectable(pattern); - drawCommentTooltip(pattern); - ImGui::TableNextColumn(); - drawOffsetColumn(pattern); - drawSizeColumn(pattern); - ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", pattern.getTypeName()); - ImGui::SameLine(0, 0); - - ImGui::TextUnformatted("["); - ImGui::SameLine(0, 0); - ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", pattern.getEntryCount()); - ImGui::SameLine(0, 0); - ImGui::TextUnformatted("]"); - - ImGui::TableNextColumn(); - ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); - } else { - ImGui::SameLine(); - ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); - } - - auto& displayEnd = this->getDisplayEnd(pattern); - if (open) { - pattern.forEachArrayEntry([&] (u64 idx, auto &entry){ - u64 lastVisible = displayEnd - 1; - - ImGui::PushID(entry.getOffset()); - - if (idx < lastVisible) { - this->draw(entry); - } else if (idx == lastVisible) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns); - if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - displayEnd += DisplayEndStep; - } - - ImGui::PopID(); - }); - - ImGui::TreePop(); - } else { - displayEnd = DisplayEndDefault; - } - } - - static void createLeafNode(const Pattern& pattern) { - ImGui::TreeNodeEx(pattern.getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | - ImGuiTreeNodeFlags_NoTreePushOnOpen | - ImGuiTreeNodeFlags_SpanFullWidth | - ImGuiTreeNodeFlags_AllowItemOverlap); - } - - static bool createTreeNode(const Pattern& pattern) { - return ImGui::TreeNodeEx(pattern.getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth); - } - - static void drawTypenameColumn(const Pattern& pattern, const std::string& pattern_name) { - ImGui::TextFormattedColored(ImColor(0xFFD69C56), pattern_name); - ImGui::SameLine(); - ImGui::TextUnformatted(pattern.getTypeName().c_str()); - ImGui::TableNextColumn(); - } - - static void drawNameColumn(const Pattern& pattern) { - ImGui::TextUnformatted(pattern.getDisplayName().c_str()); - ImGui::TableNextColumn(); - } - - static void drawColorColumn(const Pattern& pattern) { - ImGui::ColorButton("color", ImColor(pattern.getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); - ImGui::TableNextColumn(); - } - - static void drawOffsetColumn(const Pattern& pattern) { - ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", pattern.getOffset(), pattern.getOffset() + pattern.getSize() - (pattern.getSize() == 0 ? 0 : 1)); - ImGui::TableNextColumn(); - } - - static void drawSizeColumn(const Pattern& pattern) { - ImGui::TextFormatted("0x{0:04X}", pattern.getSize()); - ImGui::TableNextColumn(); - } - - u64& getDisplayEnd(const Pattern& pattern) { - auto it = m_displayEnd.find(&pattern); - if (it != m_displayEnd.end()) { - return it->second; - } - - auto [inserted, success] = m_displayEnd.emplace(&pattern, DisplayEndDefault); - return inserted->second; - } - - private: - prv::Provider *m_provider; - std::map m_displayEnd; - }; -}; \ No newline at end of file diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index ae8b0f14e..91ffc3ec9 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(${PROJECT_NAME} SHARED source/math_evaluator.cpp + source/pattern_drawer.cpp source/lang/en_US.cpp source/lang/de_DE.cpp diff --git a/plugins/builtin/include/content/views/view_pattern_data.hpp b/plugins/builtin/include/content/views/view_pattern_data.hpp index 284761e64..085b5406e 100644 --- a/plugins/builtin/include/content/views/view_pattern_data.hpp +++ b/plugins/builtin/include/content/views/view_pattern_data.hpp @@ -1,9 +1,10 @@ #pragma once +#include "pattern_drawer.hpp" + #include #include -#include #include #include @@ -21,7 +22,7 @@ namespace hex::plugin::builtin { private: std::map>> m_sortedPatterns; - hex::pl::PatternDrawer m_drawer; + hex::PatternDrawer m_drawer; }; } \ No newline at end of file diff --git a/plugins/builtin/include/pattern_drawer.hpp b/plugins/builtin/include/pattern_drawer.hpp new file mode 100644 index 000000000..5462bf921 --- /dev/null +++ b/plugins/builtin/include/pattern_drawer.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +namespace hex { + + template + concept ArrayPattern = requires(T pattern, std::function fn) { + { pattern.forEachArrayEntry(fn) } -> std::same_as; + }; + + class PatternDrawer : public pl::PatternVisitor + { + public: + PatternDrawer(); + + void setProvider(prv::Provider *provider); + + void visit(pl::PatternArrayDynamic& pattern) override; + void visit(pl::PatternArrayStatic& pattern) override; + void visit(pl::PatternBitfieldField& pattern) override; + void visit(pl::PatternBitfield& pattern) override; + void visit(pl::PatternBoolean& pattern) override; + void visit(pl::PatternCharacter& pattern) override; + void visit(pl::PatternEnum& pattern) override; + void visit(pl::PatternFloat& pattern) override; + void visit(pl::PatternPadding& pattern) override; + void visit(pl::PatternPointer& pattern) override; + void visit(pl::PatternSigned& pattern) override; + void visit(pl::PatternString& pattern) override; + void visit(pl::PatternStruct& pattern) override; + void visit(pl::PatternUnion& pattern) override; + void visit(pl::PatternUnsigned& pattern) override; + void visit(pl::PatternWideCharacter& pattern) override; + void visit(pl::PatternWideString& pattern) override; + + private: + void createDefaultEntry(const pl::Pattern &pattern, const std::string &value, pl::Token::Literal &&literal) const; + void createLeafNode(const pl::Pattern& pattern) const; + bool createTreeNode(const pl::Pattern& pattern) const; + + void makeSelectable(const pl::Pattern &pattern) const; + + void draw(pl::Pattern& pattern); + + template + void drawArray(T& pattern) { + bool opened = this->drawArrayRoot(pattern, pattern.getEntryCount(), pattern.isInlined()); + + if (opened) { + auto& displayEnd = this->getDisplayEnd(pattern); + pattern.forEachArrayEntry([&] (u64 idx, auto &entry){ + this->drawArrayNode(idx, displayEnd, entry); + }); + } + + this->drawArrayEnd(pattern, opened); + } + + bool drawArrayRoot(pl::Pattern& pattern, size_t entryCount, bool isInlined); + void drawArrayNode(u64 idx, u64 displayEnd, pl::Pattern& pattern); + void drawArrayEnd(pl::Pattern& pattern, bool opened); + + void drawCommentTooltip(const pl::Pattern &pattern) const; + void drawTypenameColumn(const pl::Pattern& pattern, const std::string& pattern_name) const; + void drawNameColumn(const pl::Pattern& pattern) const; + void drawColorColumn(const pl::Pattern& pattern) const; + void drawOffsetColumn(const pl::Pattern& pattern) const; + void drawSizeColumn(const pl::Pattern& pattern) const; + + u64& getDisplayEnd(const pl::Pattern& pattern); + + private: + prv::Provider *m_provider; + std::map m_displayEnd; + }; +}; \ No newline at end of file diff --git a/plugins/builtin/source/pattern_drawer.cpp b/plugins/builtin/source/pattern_drawer.cpp new file mode 100644 index 000000000..90a6865a5 --- /dev/null +++ b/plugins/builtin/source/pattern_drawer.cpp @@ -0,0 +1,469 @@ +#include "pattern_drawer.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + constexpr static auto DisplayEndDefault = 50u; + constexpr static auto DisplayEndStep = 50u; + + using namespace ::std::literals::string_literals; +}; + +namespace hex { + + PatternDrawer::PatternDrawer() + : m_provider{nullptr} + { } + + void PatternDrawer::setProvider(prv::Provider *provider) { + m_provider = provider; + } + + void PatternDrawer::visit(pl::PatternArrayDynamic& pattern) { + this->drawArray(pattern); + } + + void PatternDrawer::visit(pl::PatternArrayStatic& pattern) { + this->drawArray(pattern); + } + + void PatternDrawer::visit(pl::PatternBitfieldField& pattern) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + drawNameColumn(pattern); + makeSelectable(pattern); + drawColorColumn(pattern); + + auto byteAddr = pattern.getOffset() + pattern.getBitOffset() / 8; + auto firstBitIdx = pattern.getBitOffset() % 8; + auto lastBitIdx = firstBitIdx + (pattern.getBitSize() - 1) % 8; + if (firstBitIdx == lastBitIdx) + ImGui::TextFormatted("0x{0:08X} bit {1}", byteAddr, firstBitIdx); + else + ImGui::TextFormatted("0x{0:08X} bits {1} - {2}", byteAddr, firstBitIdx, lastBitIdx); + ImGui::TableNextColumn(); + if (pattern.getBitSize() == 1) + ImGui::TextFormatted("{0} bit", pattern.getBitSize()); + else + ImGui::TextFormatted("{0} bits", pattern.getBitSize()); + ImGui::TableNextColumn(); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits"); + ImGui::TableNextColumn(); + + u64 extractedValue = pattern.getValue(m_provider); + ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), &pattern)); + } + + void PatternDrawer::visit(pl::PatternBitfield& pattern) { + std::vector value = pattern.getValue(m_provider); + + bool open = true; + if (!pattern.isInlined()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + open = createTreeNode(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + drawCommentTooltip(pattern); + ImGui::TableNextColumn(); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + drawTypenameColumn(pattern, "bitfield"); + + std::string valueString = "{ "; + for (auto i : value) + valueString += hex::format("{0:02X} ", i); + valueString += "}"; + + ImGui::TextFormatted("{}", pattern.formatDisplayValue(valueString, &pattern)); + } else { + ImGui::SameLine(); + ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); + } + + if (open) { + pattern.forEachMember([&] (auto &field) { + this->draw(field); + }); + + ImGui::TreePop(); + } + } + + void PatternDrawer::visit(pl::PatternBoolean& pattern) { + u8 boolean = pattern.getValue(m_provider); + + if (boolean == 0) + this->createDefaultEntry(pattern, "false", false); + else if (boolean == 1) + this->createDefaultEntry(pattern, "true", true); + else + this->createDefaultEntry(pattern, "true*", true); + } + + void PatternDrawer::visit(pl::PatternCharacter& pattern) { + char character = pattern.getValue(m_provider); + this->createDefaultEntry(pattern, hex::format("'{0}'", character), character); + } + + void PatternDrawer::visit(pl::PatternEnum& pattern) { + u64 value = pattern.getValue(m_provider); + + std::string valueString = pattern.getTypeName() + "::"; + + bool foundValue = false; + for (auto &[entryValueLiteral, entryName] : pattern.getEnumValues()) { + auto visitor = overloaded { + [&, name = entryName](auto &entryValue) { + if (static_cast(value) == entryValue) { + valueString += name; + foundValue = true; + return true; + } + + return false; + }, + [](const std::string &) { return false; }, + [](pl::Pattern *) { return false; }, + }; + + bool matches = std::visit(visitor, entryValueLiteral); + if (matches) + break; + } + + if (!foundValue) + valueString += "???"; + + ImGui::TableNextRow(); + createLeafNode(pattern); + drawCommentTooltip(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + ImGui::SameLine(); + drawNameColumn(pattern); + drawColorColumn(pattern); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + drawTypenameColumn(pattern, "enum"); + ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, pattern.getSize() * 2), &pattern)); + } + + void PatternDrawer::visit(pl::PatternFloat& pattern) { + if (pattern.getSize() == 4) { + float f32 = static_cast(pattern.getValue(m_provider)); + u32 integerResult = 0; + std::memcpy(&integerResult, &f32, sizeof(float)); + this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f32, integerResult, pattern.getSize() * 2), f32); + } else if (pattern.getSize() == 8) { + double f64 = pattern.getValue(m_provider); + u64 integerResult = 0; + std::memcpy(&integerResult, &f64, sizeof(double)); + this->createDefaultEntry(pattern, hex::format("{:e} (0x{:0{}X})", f64, integerResult, pattern.getSize() * 2), f64); + } + } + + void PatternDrawer::visit(pl::PatternPadding& pattern) { + // Do nothing + hex::unused(pattern); + } + + void PatternDrawer::visit(pl::PatternPointer& pattern) { + u64 data = pattern.getValue(m_provider); + + bool open = true; + + if (!pattern.isInlined()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + open = createTreeNode(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + drawCommentTooltip(pattern); + ImGui::SameLine(0, 0); + drawColorColumn(pattern); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getFormattedName()); + ImGui::TableNextColumn(); + ImGui::TextFormatted("{}", pattern.formatDisplayValue(hex::format("*(0x{0:X})", data), u128(data))); + } else { + ImGui::SameLine(); + ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); + } + + if (open) { + pattern.getPointedAtPattern()->accept(*this); + + ImGui::TreePop(); + } + } + + void PatternDrawer::visit(pl::PatternSigned& pattern) { + i128 data = pattern.getValue(m_provider); + this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data); + } + + void PatternDrawer::visit(pl::PatternString& pattern) { + auto size = std::min(pattern.getSize(), 0x7F); + + if (size == 0) + return; + + std::string displayString = pattern.getValue(m_provider, size); + this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", displayString, size > pattern.getSize() ? "(truncated)" : ""), displayString); + } + + void PatternDrawer::visit(pl::PatternStruct& pattern) { + bool open = true; + + if (!pattern.isInlined()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + open = createTreeNode(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + drawCommentTooltip(pattern); + ImGui::TableNextColumn(); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + drawTypenameColumn(pattern, "struct"); + ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + } else { + ImGui::SameLine(); + ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); + } + + if (open) { + pattern.forEachMember([&](auto &member){ + this->draw(member); + }); + + ImGui::TreePop(); + } + } + + void PatternDrawer::visit(pl::PatternUnion& pattern) { + bool open = true; + + if (!pattern.isInlined()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + open = createTreeNode(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + drawCommentTooltip(pattern); + ImGui::TableNextColumn(); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + drawTypenameColumn(pattern, "union"); + ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + } else { + ImGui::SameLine(); + ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); + } + + if (open) { + pattern.forEachMember([&](auto &member) { + this->draw(member); + }); + + ImGui::TreePop(); + } + } + + void PatternDrawer::visit(pl::PatternUnsigned& pattern) { + u128 data = pattern.getValue(m_provider); + this->createDefaultEntry(pattern, hex::format("{:d} (0x{:0{}X})", data, data, pattern.getSize() * 2), data); + } + + void PatternDrawer::visit(pl::PatternWideCharacter& pattern) { + char16_t character = pattern.getValue(m_provider); + u128 literal = character; + auto str = std::wstring_convert, char16_t> {}.to_bytes(character); + this->createDefaultEntry(pattern, hex::format("'{0}'", str), literal); + } + + void PatternDrawer::visit(pl::PatternWideString& pattern) { + auto size = std::min(pattern.getSize(), 0x100); + + if (size == 0) + return; + + std::string utf8String = pattern.getValue(m_provider, size); + + this->createDefaultEntry(pattern, hex::format("\"{0}\" {1}", utf8String, size > pattern.getSize() ? "(truncated)" : ""), utf8String); + } + + void PatternDrawer::createDefaultEntry(const pl::Pattern &pattern, const std::string &value, pl::Token::Literal &&literal) const { + ImGui::TableNextRow(); + createLeafNode(pattern); + ImGui::TableNextColumn(); + + makeSelectable(pattern); + + drawCommentTooltip(pattern); + ImGui::SameLine(); + drawNameColumn(pattern); + drawColorColumn(pattern); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", pattern.getTypeName().empty() ? pattern.getFormattedName() : pattern.getTypeName()); + ImGui::TableNextColumn(); + ImGui::TextFormatted("{}", pattern.formatDisplayValue(value, literal)); + } + + void PatternDrawer::makeSelectable(const pl::Pattern &pattern) const { + ImGui::PushID(static_cast(pattern.getOffset())); + ImGui::PushID(pattern.getVariableName().c_str()); + if (ImGui::Selectable("##PatternLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) { + ImHexApi::HexEditor::setSelection(pattern.getOffset(), pattern.getSize()); + } + ImGui::SameLine(); + ImGui::PopID(); + ImGui::PopID(); + } + + + void PatternDrawer::drawCommentTooltip(const pl::Pattern &pattern) const { + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && pattern.getComment().has_value()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(pattern.getComment()->c_str()); + ImGui::EndTooltip(); + } + } + + void PatternDrawer::draw(pl::Pattern& pattern) { + if (pattern.isHidden()) + return; + + pattern.accept(*this); + } + + bool PatternDrawer::drawArrayRoot(pl::Pattern& pattern, size_t entryCount, bool isInlined) { + if (entryCount == 0) + return false; + + bool open = true; + if (!isInlined) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + open = createTreeNode(pattern); + ImGui::TableNextColumn(); + makeSelectable(pattern); + drawCommentTooltip(pattern); + ImGui::TableNextColumn(); + drawOffsetColumn(pattern); + drawSizeColumn(pattern); + ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", pattern.getTypeName()); + ImGui::SameLine(0, 0); + + ImGui::TextUnformatted("["); + ImGui::SameLine(0, 0); + ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", entryCount); + ImGui::SameLine(0, 0); + ImGui::TextUnformatted("]"); + + ImGui::TableNextColumn(); + ImGui::TextFormatted("{}", pattern.formatDisplayValue("{ ... }", &pattern)); + } else { + ImGui::SameLine(); + ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf); + } + + return open; + } + + void PatternDrawer::drawArrayNode(u64 idx, u64 displayEnd, pl::Pattern& pattern) { + u64 lastVisible = displayEnd - 1; + + ImGui::PushID(pattern.getOffset()); + + if (idx < lastVisible) { + this->draw(pattern); + } else if (idx == lastVisible) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns); + if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) + displayEnd += DisplayEndStep; + } + + ImGui::PopID(); + } + + void PatternDrawer::drawArrayEnd(pl::Pattern& pattern, bool opened) { + if (opened) { + ImGui::TreePop(); + } else { + auto& displayEnd = this->getDisplayEnd(pattern); + displayEnd = DisplayEndDefault; + } + } + + void PatternDrawer::createLeafNode(const pl::Pattern& pattern) const { + ImGui::TreeNodeEx(pattern.getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | + ImGuiTreeNodeFlags_NoTreePushOnOpen | + ImGuiTreeNodeFlags_SpanFullWidth | + ImGuiTreeNodeFlags_AllowItemOverlap); + } + + bool PatternDrawer::createTreeNode(const pl::Pattern& pattern) const { + return ImGui::TreeNodeEx(pattern.getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth); + } + + void PatternDrawer::drawTypenameColumn(const pl::Pattern& pattern, const std::string& pattern_name) const { + ImGui::TextFormattedColored(ImColor(0xFFD69C56), pattern_name); + ImGui::SameLine(); + ImGui::TextUnformatted(pattern.getTypeName().c_str()); + ImGui::TableNextColumn(); + } + + void PatternDrawer::drawNameColumn(const pl::Pattern& pattern) const { + ImGui::TextUnformatted(pattern.getDisplayName().c_str()); + ImGui::TableNextColumn(); + } + + void PatternDrawer::drawColorColumn(const pl::Pattern& pattern) const { + ImGui::ColorButton("color", ImColor(pattern.getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + ImGui::TableNextColumn(); + } + + void PatternDrawer::drawOffsetColumn(const pl::Pattern& pattern) const { + ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", pattern.getOffset(), pattern.getOffset() + pattern.getSize() - (pattern.getSize() == 0 ? 0 : 1)); + ImGui::TableNextColumn(); + } + + void PatternDrawer::drawSizeColumn(const pl::Pattern& pattern) const { + ImGui::TextFormatted("0x{0:04X}", pattern.getSize()); + ImGui::TableNextColumn(); + } + + u64& PatternDrawer::getDisplayEnd(const pl::Pattern& pattern) { + auto it = m_displayEnd.find(&pattern); + if (it != m_displayEnd.end()) { + return it->second; + } + + auto [inserted, success] = m_displayEnd.emplace(&pattern, DisplayEndDefault); + return inserted->second; + } +};