diff --git a/external/ImGui/include/imgui_imhex_extensions.h b/external/ImGui/include/imgui_imhex_extensions.h index c3bc2dc91..5646e6876 100644 --- a/external/ImGui/include/imgui_imhex_extensions.h +++ b/external/ImGui/include/imgui_imhex_extensions.h @@ -61,7 +61,8 @@ namespace ImGui { void InfoTooltip(const char *text); bool TitleBarButton(const char* label, ImVec2 size_arg); - bool ToolBarButton(const char* symbol, ImVec4 color, ImVec2 size_arg); + bool ToolBarButton(const char* symbol, ImVec4 color); + bool IconButton(const char* symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0)); inline bool HasSecondPassed() { return static_cast(ImGui::GetTime() * 100) % 100 <= static_cast(ImGui::GetIO().DeltaTime * 100); diff --git a/external/ImGui/source/imgui_imhex_extensions.cpp b/external/ImGui/source/imgui_imhex_extensions.cpp index e98eb387f..c8da2c2b2 100644 --- a/external/ImGui/source/imgui_imhex_extensions.cpp +++ b/external/ImGui/source/imgui_imhex_extensions.cpp @@ -401,7 +401,7 @@ namespace ImGui { return pressed; } - bool ToolBarButton(const char* symbol, ImVec4 color, ImVec2 size_arg) { + bool ToolBarButton(const char* symbol, ImVec4 color) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; @@ -415,7 +415,7 @@ namespace ImGui { ImVec2 pos = window->DC.CursorPos; - ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + ImVec2 size = CalcItemSize(ImVec2(1, 1) * ImGui::GetCurrentWindow()->MenuBarHeight(), label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); const ImRect bb(pos, pos + size); ItemSize(size, style.FramePadding.y); @@ -443,4 +443,46 @@ namespace ImGui { return pressed; } + bool IconButton(const char* symbol, ImVec4 color, ImVec2 size_arg) { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + color.w = 1.0F; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(symbol); + const ImVec2 label_size = CalcTextSize(symbol, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(size, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + PushStyleColor(ImGuiCol_Text, color); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, col, false, style.FrameRounding); + RenderTextClipped(bb.Min + style.FramePadding * ImVec2(1, 2), bb.Max - style.FramePadding, symbol, NULL, &label_size, style.ButtonTextAlign, &bb); + + PopStyleColor(); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + return pressed; + } + } \ No newline at end of file diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index d0ce49506..d494fc7c7 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -26,19 +26,17 @@ namespace hex::plugin::builtin { void addToolbarItems() { ContentRegistry::Interface::addToolbarItem([] { - const static auto buttonSize = ImVec2(ImGui::GetCurrentWindow()->MenuBarHeight(), ImGui::GetCurrentWindow()->MenuBarHeight()); - auto provider = ImHexApi::Provider::get(); // Undo ImGui::Disabled([&provider] { - if (ImGui::ToolBarButton(ICON_VS_DISCARD, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_DISCARD, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->undo(); }, !ImHexApi::Provider::isValid() || !provider->canUndo()); // Redo ImGui::Disabled([&provider] { - if (ImGui::ToolBarButton(ICON_VS_REDO, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_REDO, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->redo(); }, !ImHexApi::Provider::isValid() || !provider->canRedo()); @@ -46,11 +44,11 @@ namespace hex::plugin::builtin { ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); // Create new file - if (ImGui::ToolBarButton(ICON_VS_FILE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGray), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_FILE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGray))) EventManager::post("Create File"); // Open file - if (ImGui::ToolBarButton(ICON_VS_FOLDER_OPENED, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBrown), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_FOLDER_OPENED, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBrown))) EventManager::post("Open File"); @@ -58,13 +56,13 @@ namespace hex::plugin::builtin { // Save file ImGui::Disabled([&provider] { - if (ImGui::ToolBarButton(ICON_VS_SAVE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_SAVE, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) provider->save(); }, !ImHexApi::Provider::isValid() || !provider->isWritable() || !provider->isSavable()); // Save file as ImGui::Disabled([&provider] { - if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue), buttonSize)) + if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue))) hex::openFileBrowser("hex.view.hexeditor.save_as"_lang, DialogMode::Save, { }, [&provider](auto path) { provider->saveAs(path); }); @@ -76,7 +74,7 @@ namespace hex::plugin::builtin { // Create bookmark ImGui::Disabled([] { - if (ImGui::ToolBarButton(ICON_VS_BOOKMARK, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen), buttonSize)) { + if (ImGui::ToolBarButton(ICON_VS_BOOKMARK, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) { Region region = { 0 }; EventManager::post(region); diff --git a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp index bd9ca0672..79fbe2796 100644 --- a/plugins/libimhex/include/hex/pattern_language/ast_node.hpp +++ b/plugins/libimhex/include/hex/pattern_language/ast_node.hpp @@ -516,7 +516,10 @@ namespace hex::pl { FunctionResult execute(Evaluator *evaluator) override { + u64 loopIterations = 0; while (evaluateCondition(evaluator)) { + evaluator->handleAbort(); + auto variables = *evaluator->getScope(0).scope; u32 startVariableCount = variables.size(); ON_SCOPE_EXIT { @@ -538,6 +541,12 @@ namespace hex::pl { return { true, result }; } } + + loopIterations++; + if (loopIterations >= evaluator->getLoopLimit()) + LogConsole::abortEvaluation(hex::format("loop iterations exceeded limit of {}", evaluator->getLoopLimit()), this); + + evaluator->handleAbort(); } return { false, { } }; @@ -807,6 +816,7 @@ namespace hex::pl { while (whileStatement->evaluateCondition(evaluator)) { entryCount++; evaluator->dataOffset() += templatePattern->getSize(); + evaluator->handleAbort(); } } } else { @@ -829,6 +839,7 @@ namespace hex::pl { } if (reachedEnd) break; + evaluator->handleAbort(); } } @@ -901,6 +912,8 @@ namespace hex::pl { entries.push_back(pattern); size += pattern->getSize(); + + evaluator->handleAbort(); } } else if (auto whileStatement = dynamic_cast(sizeNode)) { while (whileStatement->evaluateCondition(evaluator)) { @@ -917,6 +930,8 @@ namespace hex::pl { entryCount++; size += pattern->getSize(); + + evaluator->handleAbort(); } } } else { @@ -951,6 +966,7 @@ namespace hex::pl { } if (reachedEnd) break; + evaluator->handleAbort(); } } @@ -1380,6 +1396,10 @@ namespace hex::pl { std::visit(overloaded { [&](std::string &assignmentValue) { }, + [&](s128 assignmentValue) { + std::memcpy(&value, &assignmentValue, pattern->getSize()); + value = hex::signExtend(pattern->getSize() * 8, value); + }, [&](auto &&assignmentValue) { std::memcpy(&value, &assignmentValue, pattern->getSize()); } }, literal); } diff --git a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp index 3ceb007e8..0d5c80a91 100644 --- a/plugins/libimhex/include/hex/pattern_language/evaluator.hpp +++ b/plugins/libimhex/include/hex/pattern_language/evaluator.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -31,8 +32,10 @@ namespace hex::pl { struct Scope { PatternData *parent; std::vector* scope; }; void pushScope(PatternData *parent, std::vector &scope) { - if (this->m_scopes.size() > this->m_evalDepth) - LogConsole::abortEvaluation(hex::format("recursion limit of {} reached", this->m_evalDepth)); + if (this->m_scopes.size() > this->getEvaluationDepth()) + LogConsole::abortEvaluation(hex::format("evaluation depth exceeded set limit of {}", this->getEvaluationDepth())); + + this->handleAbort(); this->m_scopes.push_back({ parent, &scope }); } @@ -70,38 +73,47 @@ namespace hex::pl { return this->m_defaultEndian; } - void setEvaluationDepth(u32 evalDepth) { + void setEvaluationDepth(u64 evalDepth) { this->m_evalDepth = evalDepth; } [[nodiscard]] - u32 getEvaluationDepth() const { + u64 getEvaluationDepth() const { return this->m_evalDepth; } - void setArrayLimit(u32 arrayLimit) { + void setArrayLimit(u64 arrayLimit) { this->m_arrayLimit = arrayLimit; } [[nodiscard]] - u32 getArrayLimit() const { + u64 getArrayLimit() const { return this->m_arrayLimit; } - void setPatternLimit(u32 limit) { + void setPatternLimit(u64 limit) { this->m_patternLimit = limit; } [[nodiscard]] - u32 getPatternLimit() { + u64 getPatternLimit() { return this->m_patternLimit; } [[nodiscard]] - u32 getPatternCount() { + u64 getPatternCount() { return this->m_currPatternCount; } + void setLoopLimit(u64 limit) { + this->m_loopLimit = limit; + } + + [[nodiscard]] + u64 getLoopLimit() { + return this->m_loopLimit; + } + u64& dataOffset() { return this->m_currOffset; } bool addCustomFunction(const std::string &name, u32 numParams, const ContentRegistry::PatternLanguageFunctions::Callback &function) { @@ -123,6 +135,15 @@ namespace hex::pl { void createVariable(const std::string &name, ASTNode *type, const std::optional &value = std::nullopt); void setVariable(const std::string &name, const Token::Literal& value); + void abort() { + this->m_aborted = true; + } + + void handleAbort() { + if (this->m_aborted) + LogConsole::abortEvaluation("evaluation aborted by user"); + } + private: void patternCreated(); @@ -134,11 +155,14 @@ namespace hex::pl { LogConsole m_console; std::endian m_defaultEndian = std::endian::native; - u32 m_evalDepth; - u32 m_arrayLimit; - u32 m_patternLimit; + u64 m_evalDepth; + u64 m_arrayLimit; + u64 m_patternLimit; + u64 m_loopLimit; - u32 m_currPatternCount; + u64 m_currPatternCount; + + std::atomic m_aborted; std::vector m_scopes; std::map m_customFunctions; diff --git a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp index 31ccfe85c..167dab106 100644 --- a/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp +++ b/plugins/libimhex/include/hex/pattern_language/pattern_language.hpp @@ -31,6 +31,8 @@ namespace hex::pl { std::optional> executeString(prv::Provider *provider, const std::string &string); std::optional> executeFile(prv::Provider *provider, const std::string &path); + void abort(); + const std::vector>& getConsoleLog(); const std::optional>& getError(); diff --git a/plugins/libimhex/source/pattern_language/evaluator.cpp b/plugins/libimhex/source/pattern_language/evaluator.cpp index 8adb12fb4..80656aebf 100644 --- a/plugins/libimhex/source/pattern_language/evaluator.cpp +++ b/plugins/libimhex/source/pattern_language/evaluator.cpp @@ -106,6 +106,7 @@ namespace hex::pl { this->m_stack.clear(); this->m_customFunctions.clear(); this->m_scopes.clear(); + this->m_aborted = false; this->dataOffset() = 0x00; this->m_currPatternCount = 0; diff --git a/plugins/libimhex/source/pattern_language/pattern_language.cpp b/plugins/libimhex/source/pattern_language/pattern_language.cpp index 0597d4ce7..775bd9f77 100644 --- a/plugins/libimhex/source/pattern_language/pattern_language.cpp +++ b/plugins/libimhex/source/pattern_language/pattern_language.cpp @@ -67,6 +67,16 @@ namespace hex::pl { return true; }); + this->m_preprocessor->addPragmaHandler("loop_limit", [this](const std::string &value) { + auto limit = strtol(value.c_str(), nullptr, 0); + + if (limit <= 0) + return false; + + this->m_evaluator->setLoopLimit(limit); + return true; + }); + this->m_preprocessor->addPragmaHandler("base_address", [](const std::string &value) { auto baseAddress = strtoull(value.c_str(), nullptr, 0); @@ -93,6 +103,7 @@ namespace hex::pl { this->m_evaluator->setEvaluationDepth(32); this->m_evaluator->setArrayLimit(0x1000); this->m_evaluator->setPatternLimit(0x2000); + this->m_evaluator->setLoopLimit(0x1000); for (auto &node : this->m_currAST) delete node; @@ -133,6 +144,10 @@ namespace hex::pl { return this->executeString(provider, file.readString()); } + void PatternLanguage::abort() { + this->m_evaluator->abort(); + } + const std::vector>& PatternLanguage::getConsoleLog() { return this->m_evaluator->getConsole().getLog(); diff --git a/source/views/view_pattern_editor.cpp b/source/views/view_pattern_editor.cpp index 1badd1fb8..7526c848e 100644 --- a/source/views/view_pattern_editor.cpp +++ b/source/views/view_pattern_editor.cpp @@ -245,16 +245,18 @@ namespace hex { ImGui::EndChild(); ImGui::PopStyleColor(1); - ImGui::Disabled([this] { - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(ImColor(0x20, 0x85, 0x20))); - ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); - if (ImGui::ArrowButton("evaluate", ImGuiDir_Right)) + if (this->m_evaluatorRunning) { + if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) + this->m_patternLanguageRuntime->abort(); + } else { + if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) this->parsePattern(this->m_textEditor.GetText().data()); + } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - }, this->m_evaluatorRunning); + + ImGui::PopStyleVar(); ImGui::SameLine(); if (this->m_evaluatorRunning)