diff --git a/include/views/view_command_palette.hpp b/include/views/view_command_palette.hpp index 49fed0411..493f47507 100644 --- a/include/views/view_command_palette.hpp +++ b/include/views/view_command_palette.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include diff --git a/include/views/view_help.hpp b/include/views/view_help.hpp index 37f76c695..88f7b9da9 100644 --- a/include/views/view_help.hpp +++ b/include/views/view_help.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include diff --git a/include/views/view_hexeditor.hpp b/include/views/view_hexeditor.hpp index 54a00c7d0..0b3dd6a57 100644 --- a/include/views/view_hexeditor.hpp +++ b/include/views/view_hexeditor.hpp @@ -10,8 +10,6 @@ #include #include -#include - namespace hex { namespace prv { class Provider; } diff --git a/include/views/view_pattern_data.hpp b/include/views/view_pattern_data.hpp index 2aea0372d..6e207027f 100644 --- a/include/views/view_pattern_data.hpp +++ b/include/views/view_pattern_data.hpp @@ -4,7 +4,6 @@ #include #include -#include #include #include @@ -13,6 +12,7 @@ namespace hex { namespace prv { class Provider; } + namespace lang { class PatternData; } class ViewPatternData : public View { public: diff --git a/plugins/libimhex/include/hex/lang/evaluator.hpp b/plugins/libimhex/include/hex/lang/evaluator.hpp index 51ace555e..eec6b3552 100644 --- a/plugins/libimhex/include/hex/lang/evaluator.hpp +++ b/plugins/libimhex/include/hex/lang/evaluator.hpp @@ -82,6 +82,8 @@ namespace hex::lang { PatternData* evaluateType(ASTNodeTypeDecl *node); PatternData* evaluateVariable(ASTNodeVariableDecl *node); PatternData* evaluateArray(ASTNodeArrayVariableDecl *node); + PatternData* evaluateStaticArray(ASTNodeArrayVariableDecl *node); + PatternData* evaluateDynamicArray(ASTNodeArrayVariableDecl *node); PatternData* evaluatePointer(ASTNodePointerVariableDecl *node); }; diff --git a/plugins/libimhex/include/hex/lang/pattern_data.hpp b/plugins/libimhex/include/hex/lang/pattern_data.hpp index 36141a2d3..3de117555 100644 --- a/plugins/libimhex/include/hex/lang/pattern_data.hpp +++ b/plugins/libimhex/include/hex/lang/pattern_data.hpp @@ -55,12 +55,15 @@ namespace hex::lang { if (SharedData::patternPaletteOffset >= (sizeof(Palette) / sizeof(u32))) SharedData::patternPaletteOffset = 0; } + + PatternData(const PatternData &other) = default; + virtual ~PatternData() = default; virtual PatternData* clone() = 0; [[nodiscard]] u64 getOffset() const { return this->m_offset; } - void setOffset(u64 offset) { this->m_offset = offset; } + virtual void setOffset(u64 offset) { this->m_offset = offset; } [[nodiscard]] size_t getSize() const { return this->m_size; } void setSize(size_t size) { this->m_size = size; } @@ -104,6 +107,10 @@ namespace hex::lang { return this->m_highlightedAddresses; } + virtual void clearHighlightedAddresses() { + this->m_highlightedAddresses.clear(); + } + virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) { } static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, lang::PatternData* left, lang::PatternData* right) { @@ -253,7 +260,7 @@ namespace hex::lang { : PatternData(offset, size, color), m_pointedAt(nullptr) { } - PatternDataPointer(const PatternDataPointer &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + PatternDataPointer(const PatternDataPointer &other) : PatternData(other) { this->m_pointedAt = other.m_pointedAt->clone(); } @@ -577,13 +584,13 @@ namespace hex::lang { } }; - class PatternDataArray : public PatternData { + class PatternDataDynamicArray : public PatternData { public: - PatternDataArray(u64 offset, size_t size, u32 color = 0) + PatternDataDynamicArray(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color) { } - PatternDataArray(const PatternDataArray &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + PatternDataDynamicArray(const PatternDataDynamicArray &other) : PatternData(other) { std::vector entries; for (const auto &entry : other.m_entries) entries.push_back(entry->clone()); @@ -591,13 +598,21 @@ namespace hex::lang { this->setEntries(entries); } - ~PatternDataArray() override { + ~PatternDataDynamicArray() override { for (const auto &entry : this->m_entries) delete entry; } PatternData* clone() override { - return new PatternDataArray(*this); + return new PatternDataDynamicArray(*this); + } + + void setOffset(u64 offset) override { + for (auto &entry : this->m_entries) { + entry->setOffset(offset + (entry->getOffset() - this->getOffset())); + } + + PatternData::setOffset(offset); } void createEntry(prv::Provider* &provider) override { @@ -654,6 +669,12 @@ namespace hex::lang { return this->m_highlightedAddresses; } + void clearHighlightedAddresses() override { + for (auto &entry : this->m_entries) + entry->clearHighlightedAddresses(); + PatternData::clearHighlightedAddresses(); + } + [[nodiscard]] std::string getFormattedName() const override { return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]"; } @@ -675,14 +696,137 @@ namespace hex::lang { std::vector m_entries; }; + class PatternDataStaticArray : public PatternData { + public: + PatternDataStaticArray(u64 offset, size_t size, u32 color = 0) + : PatternData(offset, size, color) { + } + + PatternDataStaticArray(const PatternDataStaticArray &other) : PatternData(other) { + this->setEntries(other.getTemplate()->clone(), other.getEntryCount()); + } + + ~PatternDataStaticArray() override { + delete this->m_template; + } + + PatternData* clone() override { + return new PatternDataStaticArray(*this); + } + + void createEntry(prv::Provider* &provider) override { + if (this->getEntryCount() == 0) + return; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap); + this->drawCommentTooltip(); + ImGui::TableNextColumn(); + ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight())); + ImGui::TableNextColumn(); + ImGui::Text("0x%08llX : 0x%08llX", this->getOffset(), this->getOffset() + this->getSize() - 1); + ImGui::TableNextColumn(); + ImGui::Text("0x%04llX", this->getSize()); + ImGui::TableNextColumn(); + ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->m_template->getTypeName().c_str()); + ImGui::SameLine(0, 0); + + ImGui::TextUnformatted("["); + ImGui::SameLine(0, 0); + ImGui::TextColored(ImColor(0xFF00FF00), "%llu", this->m_entryCount); + ImGui::SameLine(0, 0); + ImGui::TextUnformatted("]"); + + ImGui::TableNextColumn(); + ImGui::Text("%s", "{ ... }"); + + if (open) { + auto entry = this->m_template->clone(); + for (u64 index = 0; index < this->m_entryCount; index++) { + entry->setVariableName(hex::format("[{0}]", index)); + entry->setOffset(this->getOffset() + index * this->m_template->getSize()); + entry->draw(provider); + } + delete entry; + + ImGui::TreePop(); + } + } + + std::optional highlightBytes(size_t offset) override{ + auto entry = this->m_template->clone(); + + for (u64 address = this->getOffset(); address < this->getOffset() + this->getSize(); address += this->m_template->getSize()) { + entry->setOffset(address); + if (auto color = entry->highlightBytes(offset); color.has_value()) + return color.value(); + } + + delete entry; + + return { }; + } + + std::map getHighlightedAddresses() override { + if (this->m_highlightedAddresses.empty()) { + auto entry = this->m_template->clone(); + + for (u64 address = this->getOffset(); address < this->getOffset() + this->getSize(); address += this->m_template->getSize()) { + entry->setOffset(address); + entry->clearHighlightedAddresses(); + this->m_highlightedAddresses.merge(entry->getHighlightedAddresses()); + } + + delete entry; + } + + return this->m_highlightedAddresses; + } + + void clearHighlightedAddresses() override { + this->m_template->clearHighlightedAddresses(); + PatternData::clearHighlightedAddresses(); + } + + [[nodiscard]] std::string getFormattedName() const override { + return this->m_template->getTypeName() + "[" + std::to_string(this->m_entryCount) + "]"; + } + + [[nodiscard]] PatternData* getTemplate() const { + return this->m_template; + } + + [[nodiscard]] size_t getEntryCount() const { + return this->m_entryCount; + } + + void setEntryCount(size_t count) { + this->m_entryCount = count; + } + + void setEntries(PatternData* templ, size_t count) { + this->m_template = templ; + this->m_entryCount = count; + + this->m_template->setColor(this->getColor()); + this->m_template->setParent(this); + } + + private: + PatternData *m_template; + size_t m_entryCount; + }; + class PatternDataStruct : public PatternData { public: PatternDataStruct(u64 offset, size_t size, u32 color = 0) : PatternData(offset, size, color){ } - PatternDataStruct(const PatternDataStruct &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + PatternDataStruct(const PatternDataStruct &other) : PatternData(other) { for (const auto &member : other.m_members) this->m_members.push_back(member->clone()); + this->m_sortedMembers = this->m_members; } ~PatternDataStruct() override { @@ -694,6 +838,14 @@ namespace hex::lang { return new PatternDataStruct(*this); } + void setOffset(u64 offset) override { + for (auto &member : this->m_members) { + member->setOffset(offset + (member->getOffset() - this->getOffset())); + } + + PatternData::setOffset(offset); + } + void createEntry(prv::Provider* &provider) override { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -737,6 +889,12 @@ namespace hex::lang { return this->m_highlightedAddresses; } + void clearHighlightedAddresses() override { + for (auto &member : this->m_members) + member->clearHighlightedAddresses(); + PatternData::clearHighlightedAddresses(); + } + void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override { this->m_sortedMembers = this->m_members; @@ -780,9 +938,10 @@ namespace hex::lang { } - PatternDataUnion(const PatternDataUnion &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) { + PatternDataUnion(const PatternDataUnion &other) : PatternData(other) { for (const auto &member : other.m_members) this->m_members.push_back(member->clone()); + this->m_sortedMembers = this->m_members; } ~PatternDataUnion() override { @@ -794,6 +953,14 @@ namespace hex::lang { return new PatternDataUnion(*this); } + void setOffset(u64 offset) override { + for (auto &member : this->m_members) { + member->setOffset(offset + (member->getOffset() - this->getOffset())); + } + + PatternData::setOffset(offset); + } + void createEntry(prv::Provider* &provider) override { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -838,6 +1005,12 @@ namespace hex::lang { return this->m_highlightedAddresses; } + void clearHighlightedAddresses() override { + for (auto &member : this->m_members) + member->clearHighlightedAddresses(); + PatternData::clearHighlightedAddresses(); + } + void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override { this->m_sortedMembers = this->m_members; diff --git a/plugins/libimhex/source/lang/evaluator.cpp b/plugins/libimhex/source/lang/evaluator.cpp index 21cf1dd73..67cce0eb8 100644 --- a/plugins/libimhex/source/lang/evaluator.cpp +++ b/plugins/libimhex/source/lang/evaluator.cpp @@ -61,8 +61,12 @@ namespace hex::lang { else if (auto bitfieldPattern = dynamic_cast(currPattern); bitfieldPattern != nullptr) { currMembers = bitfieldPattern->getFields(); } - else if (auto arrayPattern = dynamic_cast(currPattern); arrayPattern != nullptr) { - currMembers = arrayPattern->getEntries(); + else if (auto dynamicArrayPattern = dynamic_cast(currPattern); dynamicArrayPattern != nullptr) { + currMembers = dynamicArrayPattern->getEntries(); + continue; + } + else if (auto staticArrayPattern = dynamic_cast(currPattern); staticArrayPattern != nullptr) { + currMembers = { staticArrayPattern->getTemplate() }; continue; } else @@ -84,15 +88,30 @@ namespace hex::lang { ON_SCOPE_EXIT { delete arrayIndexNode; }; if (currPattern != nullptr) { - if (auto arrayPattern = dynamic_cast(currPattern); arrayPattern != nullptr) { + if (auto dynamicArrayPattern = dynamic_cast(currPattern); dynamicArrayPattern != nullptr) { std::visit([this](auto &&arrayIndex) { if (std::is_floating_point_v) this->getConsole().abortEvaluation("cannot use float to index into array"); }, arrayIndexNode->getValue()); std::visit([&](auto &&arrayIndex){ - if (arrayIndex >= 0 && arrayIndex < arrayPattern->getEntries().size()) - currPattern = arrayPattern->getEntries()[arrayIndex]; + if (arrayIndex >= 0 && arrayIndex < dynamicArrayPattern->getEntries().size()) + currPattern = dynamicArrayPattern->getEntries()[arrayIndex]; + else + this->getConsole().abortEvaluation(hex::format("tried to access out of bounds index {} of '{}'", arrayIndex, currPattern->getVariableName())); + }, arrayIndexNode->getValue()); + + } else if (auto staticArrayPattern = dynamic_cast(currPattern); staticArrayPattern != nullptr) { + std::visit([this](auto &&arrayIndex) { + if (std::is_floating_point_v) + this->getConsole().abortEvaluation("cannot use float to index into array"); + }, arrayIndexNode->getValue()); + + std::visit([&](auto &&arrayIndex){ + if (arrayIndex >= 0 && arrayIndex < staticArrayPattern->getEntryCount()) { + currPattern = staticArrayPattern->getTemplate(); + currPattern->setOffset(staticArrayPattern->getOffset() + arrayIndex * staticArrayPattern->getSize()); + } else this->getConsole().abortEvaluation(hex::format("tried to access out of bounds index {} of '{}'", arrayIndex, currPattern->getVariableName())); }, arrayIndexNode->getValue()); @@ -960,7 +979,6 @@ namespace hex::lang { } PatternData* Evaluator::evaluateArray(ASTNodeArrayVariableDecl *node) { - // Evaluate placement of array if (auto offset = dynamic_cast(node->getPlacementOffset()); offset != nullptr) { auto valueNode = evaluateMathematicalExpression(offset); @@ -982,6 +1000,112 @@ namespace hex::lang { return nullptr; } + + auto type = static_cast(node->getType())->getType(); + + if (dynamic_cast(type) != nullptr) + return this->evaluateStaticArray(node); + + auto attributes = dynamic_cast(type)->getAttributes(); + + bool isStaticType = std::any_of(attributes.begin(), attributes.end(), [](ASTNodeAttribute *attribute) { + return attribute->getAttribute() == "static" && !attribute->getValue().has_value(); + }); + + if (isStaticType) + return this->evaluateStaticArray(node); + else + return this->evaluateDynamicArray(node); + } + + PatternData* Evaluator::evaluateStaticArray(ASTNodeArrayVariableDecl *node) { + std::optional color; + + size_t arraySize = 0; + + auto startOffset = this->m_currOffset; + PatternData *templatePattern; + if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) + templatePattern = this->evaluateType(typeDecl); + else if (auto builtinTypeDecl = dynamic_cast(node->getType()); builtinTypeDecl != nullptr) + templatePattern = this->evaluateBuiltinType(builtinTypeDecl); + else + this->getConsole().abortEvaluation("ASTNodeVariableDecl had an invalid type. This is a bug!"); + + auto entrySize = this->m_currOffset - startOffset; + + ON_SCOPE_EXIT { delete templatePattern; }; + + auto sizeNode = node->getSize(); + if (auto numericExpression = dynamic_cast(sizeNode); numericExpression != nullptr) { + // Parse explicit size of array + auto valueNode = this->evaluateMathematicalExpression(numericExpression); + ON_SCOPE_EXIT { delete valueNode; }; + + arraySize = std::visit([this] (auto &&value) { + using Type = std::remove_cvref_t; + if constexpr (std::is_floating_point_v) + this->getConsole().abortEvaluation("bitfield entry size must be an integer value"); + return static_cast(value); + }, valueNode->getValue()); + } else if (auto whileLoopExpression = dynamic_cast(sizeNode); whileLoopExpression != nullptr) { + // Parse while loop based size of array + auto conditionNode = this->evaluateMathematicalExpression(static_cast(whileLoopExpression->getCondition())); + ON_SCOPE_EXIT { delete conditionNode; }; + + while (std::visit([](auto &&value) { return value != 0; }, conditionNode->getValue())) { + arraySize++; + + delete conditionNode; + conditionNode = this->evaluateMathematicalExpression(static_cast(whileLoopExpression->getCondition())); + } + } else { + // Parse unsized array + + if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) { + if (auto builtinType = dynamic_cast(typeDecl->getType()); builtinType != nullptr) { + std::vector bytes(Token::getTypeSize(builtinType->getType()), 0x00); + u64 offset = startOffset; + + do { + this->m_provider->read(offset, bytes.data(), bytes.size()); + offset += bytes.size(); + arraySize++; + } while (!std::all_of(bytes.begin(), bytes.end(), [](u8 byte){ return byte == 0x00; }) && offset < this->m_provider->getSize()); + } + } + } + + if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) { + if (auto builtinType = dynamic_cast(typeDecl->getType()); builtinType != nullptr) { + if (builtinType->getType() == Token::ValueType::Padding) + return new PatternDataPadding(startOffset, entrySize * arraySize); + } + } + + PatternData *pattern; + if (dynamic_cast(templatePattern) != nullptr) + pattern = new PatternDataString(startOffset, entrySize * arraySize, color.value_or(0)); + else if (dynamic_cast(templatePattern) != nullptr) + pattern = new PatternDataString16(startOffset, entrySize * arraySize, color.value_or(0)); + else { + auto arrayPattern = new PatternDataStaticArray(startOffset, entrySize * arraySize, color.value_or(0)); + arrayPattern->setTypeName(templatePattern->getTypeName()); + arrayPattern->setEntries(templatePattern->clone(), arraySize); + + pattern = arrayPattern; + } + + pattern->setVariableName(node->getName().data()); + pattern->setEndian(this->getCurrentEndian()); + + this->m_currOffset = startOffset + entrySize * arraySize; + + return this->evaluateAttributes(node, pattern); + + } + + PatternData* Evaluator::evaluateDynamicArray(ASTNodeArrayVariableDecl *node) { auto startOffset = this->m_currOffset; std::vector entries; @@ -991,8 +1115,6 @@ namespace hex::lang { PatternData *entry; if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) entry = this->evaluateType(typeDecl); - else if (auto builtinTypeDecl = dynamic_cast(node->getType()); builtinTypeDecl != nullptr) - entry = this->evaluateBuiltinType(builtinTypeDecl); else this->getConsole().abortEvaluation("ASTNodeVariableDecl had an invalid type. This is a bug!"); @@ -1042,23 +1164,6 @@ namespace hex::lang { delete conditionNode; conditionNode = this->evaluateMathematicalExpression(static_cast(whileLoopExpression->getCondition())); } - } else { - // Parse unsized array - - if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) { - if (auto builtinType = dynamic_cast(typeDecl->getType()); builtinType != nullptr) { - std::vector bytes(Token::getTypeSize(builtinType->getType()), 0x00); - u64 offset = startOffset; - - u64 index = 0; - do { - this->m_provider->read(offset, bytes.data(), bytes.size()); - offset += bytes.size(); - addEntry(index); - index++; - } while (!std::all_of(bytes.begin(), bytes.end(), [](u8 byte){ return byte == 0x00; }) && offset < this->m_provider->getSize()); - } - } } auto deleteEntries = SCOPE_GUARD { @@ -1066,31 +1171,13 @@ namespace hex::lang { delete entry; }; - if (auto typeDecl = dynamic_cast(node->getType()); typeDecl != nullptr) { - if (auto builtinType = dynamic_cast(typeDecl->getType()); builtinType != nullptr) { - if (builtinType->getType() == Token::ValueType::Padding) - return new PatternDataPadding(startOffset, this->m_currOffset - startOffset); - } - } + if (node->getSize() == nullptr) + this->getConsole().abortEvaluation("no bounds provided for array"); + auto pattern = new PatternDataDynamicArray(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); - PatternData *pattern; - if (entries.empty()) - pattern = new PatternDataPadding(startOffset, 0); - else if (dynamic_cast(entries[0]) != nullptr) - pattern = new PatternDataString(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); - else if (dynamic_cast(entries[0]) != nullptr) - pattern = new PatternDataString16(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); - else { - if (node->getSize() == nullptr) - this->getConsole().abortEvaluation("no bounds provided for array"); - auto arrayPattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), color.value_or(0)); - - arrayPattern->setEntries(entries); - deleteEntries.release(); - - pattern = arrayPattern; - } + deleteEntries.release(); + pattern->setEntries(entries); pattern->setVariableName(node->getName().data()); pattern->setEndian(this->getCurrentEndian()); diff --git a/plugins/libimhex/source/lang/pattern_language.cpp b/plugins/libimhex/source/lang/pattern_language.cpp index e24c21958..3529fe239 100644 --- a/plugins/libimhex/source/lang/pattern_language.cpp +++ b/plugins/libimhex/source/lang/pattern_language.cpp @@ -7,12 +7,13 @@ #include #include #include -#include #include namespace hex::lang { + class PatternData; + PatternLanguage::PatternLanguage() { this->m_preprocessor = new Preprocessor(); this->m_lexer = new Lexer(); diff --git a/source/init/tasks.cpp b/source/init/tasks.cpp index ca8b50421..9bcbab126 100644 --- a/source/init/tasks.cpp +++ b/source/init/tasks.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include diff --git a/source/views/view_command_palette.cpp b/source/views/view_command_palette.cpp index bbfd954a5..25852fb07 100644 --- a/source/views/view_command_palette.cpp +++ b/source/views/view_command_palette.cpp @@ -1,6 +1,6 @@ #include "views/view_command_palette.hpp" -#include +#include namespace hex { diff --git a/source/views/view_help.cpp b/source/views/view_help.cpp index 92c1c22a7..e72cf205f 100644 --- a/source/views/view_help.cpp +++ b/source/views/view_help.cpp @@ -3,6 +3,9 @@ #include +#include +#include + namespace hex { ViewHelp::ViewHelp() : View("hex.view.help.about.name") { diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp index 7aadbe77a..aa25c466b 100644 --- a/source/views/view_hexeditor.cpp +++ b/source/views/view_hexeditor.cpp @@ -1,8 +1,9 @@ #include "views/view_hexeditor.hpp" -#include #include +#include #include +#include #include "providers/file_provider.hpp" #include "helpers/patches.hpp" diff --git a/source/views/view_pattern_editor.cpp b/source/views/view_pattern_editor.cpp index 08533b968..924d6781f 100644 --- a/source/views/view_pattern_editor.cpp +++ b/source/views/view_pattern_editor.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include