diff --git a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h index fa0b17ecc..13997b7bf 100644 --- a/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h +++ b/lib/libimhex/include/hex/ui/imgui_imhex_extensions.h @@ -314,7 +314,7 @@ namespace ImGuiExt { bool BitCheckbox(const char* label, bool* v); - bool DimmedButton(const char* label, ImVec2 size = ImVec2(0, 0)); + bool DimmedButton(const char* label, ImVec2 size = ImVec2(0, 0), ImGuiButtonFlags flags = ImGuiButtonFlags_None); bool DimmedIconButton(const char *symbol, ImVec4 color, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0)); bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0)); bool DimmedIconToggle(const char *icon, bool *v); diff --git a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp index 62d8136de..f8058dc40 100644 --- a/lib/libimhex/source/ui/imgui_imhex_extensions.cpp +++ b/lib/libimhex/source/ui/imgui_imhex_extensions.cpp @@ -1169,14 +1169,14 @@ namespace ImGuiExt { return pressed; } - bool DimmedButton(const char* label, ImVec2 size){ + bool DimmedButton(const char* label, ImVec2 size, ImGuiButtonFlags flags){ PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered)); PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton)); PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive)); PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive)); PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1); - bool res = Button(label, size); + bool res = ButtonEx(label, size, flags); PopStyleColor(4); PopStyleVar(1); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index e95a5c635..650fc758c 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -938,6 +938,11 @@ "hex.builtin.information_section.info_analysis.encrypted": "This data is most likely encrypted or compressed!", "hex.builtin.information_section.info_analysis.entropy": "Entropy", "hex.builtin.information_section.magic.extension": "File Extension", + "hex.builtin.information_section.magic.pattern_info": "Advanced Pattern Info", + "hex.builtin.information_section.magic.pattern_info.no_description": "No further information available.", + "hex.builtin.information_section.magic.pattern_info.add_description": "Add a 'fn get_data_description()' function to this pattern to provide more information about the matched data.", + "hex.builtin.information_section.magic.pattern_info.load_pattern": "Load this Pattern", + "hex.builtin.information_section.magic.pattern_info.load_pattern_failed": "Pattern file could not be loaded", "hex.builtin.information_section.info_analysis.file_entropy": "Overall entropy", "hex.builtin.information_section.info_analysis.highest_entropy": "Highest block entropy", "hex.builtin.information_section.info_analysis.lowest_entropy": "Lowest block entropy", diff --git a/plugins/builtin/source/content/data_information_sections.cpp b/plugins/builtin/source/content/data_information_sections.cpp index 946d4bc31..745fe59b2 100644 --- a/plugins/builtin/source/content/data_information_sections.cpp +++ b/plugins/builtin/source/content/data_information_sections.cpp @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -80,10 +84,34 @@ namespace hex::plugin::builtin { std::vector data(region.getSize()); provider->read(region.getStartAddress(), data.data(), data.size()); - m_dataDescription = magic::getDescription(data); - m_dataMimeType = magic::getMIMEType(data); - m_dataAppleCreatorType = magic::getAppleCreatorType(data); - m_dataExtensions = magic::getExtensions(data); + m_dataDescription = magic::getDescription(data); + m_dataMimeType = magic::getMIMEType(data); + m_dataAppleCreatorType = magic::getAppleCreatorType(data); + m_dataExtensions = magic::getExtensions(data); + m_patternDataDescription = std::nullopt; + + const auto foundPatterns = magic::findViablePatterns(provider); + if (!foundPatterns.empty()) { + pl::PatternLanguage runtime; + ContentRegistry::PatternLanguage::configureRuntime(runtime, provider); + + const auto &pattern = foundPatterns.front(); + + m_foundPattern = pattern; + + constexpr static auto DataDescriptionFunction = "get_data_description"; + if (runtime.executeFile(pattern.patternFilePath)) { + const auto &evaluator = runtime.getInternals().evaluator; + const auto &functions = evaluator->getCustomFunctions(); + if (const auto function = functions.find(DataDescriptionFunction); function != functions.end()) { + if (const auto value = function->second.func(evaluator.get(), {}); value.has_value()) { + if (value->isString()) { + m_patternDataDescription = ui::Markdown(value->toString()); + } + } + } + } + } } catch (const std::bad_alloc &) { hex::log::error("Failed to allocate enough memory for full file magic analysis!"); @@ -144,10 +172,51 @@ namespace hex::plugin::builtin { ImGuiExt::TextFormattedSelectable("{}", m_dataExtensions); } - ImGui::EndTable(); - } + if (m_foundPattern.has_value()) { + ImGui::TableNextColumn(); + ImGui::TextUnformatted("hex.builtin.information_section.magic.pattern_info"_lang); + ImGui::TableNextColumn(); + } - ImGui::NewLine(); + ImGui::EndTable(); + + // Explicitly draw this part outside the table so we can have the sub window take the full width + // of the parent window + if (m_foundPattern.has_value()) { + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0F); + if (ImGui::BeginChild("##pattern_information", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY, ImGuiWindowFlags_MenuBar)) { + if (ImGui::BeginMenuBar()) { + ImGui::TextUnformatted(m_foundPattern->description.c_str()); + ImGui::TextUnformatted(ICON_VS_ARROW_RIGHT); + ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0F); + if (ImGuiExt::DimmedButton("hex.builtin.information_section.magic.pattern_info.load_pattern"_lang, {}, ImGuiButtonFlags_AlignTextBaseLine)) { + wolv::io::File patternFile(m_foundPattern->patternFilePath, wolv::io::File::Mode::Read); + if (patternFile.isValid()) { + const auto patternCode = patternFile.readString(); + RequestSetPatternLanguageCode::post(patternCode); + RequestTriggerPatternEvaluation::post(); + } else { + ui::ToastError::open("hex.builtin.information_section.magic.pattern_info.load_pattern_failed"_lang); + } + } + ImGui::PopStyleVar(); + ImGui::EndMenuBar(); + } + + if (m_patternDataDescription.has_value()) + m_patternDataDescription->draw(); + else { + ImGui::BeginDisabled(); + ImGuiExt::TextFormattedCenteredHorizontal("hex.builtin.information_section.magic.pattern_info.no_description"_lang); + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGuiExt::HelpHover("hex.builtin.information_section.magic.pattern_info.add_description"_lang, ICON_VS_INFO); + } + } + ImGui::EndChild(); + ImGui::PopStyleVar(); + } + } } } @@ -163,6 +232,9 @@ namespace hex::plugin::builtin { std::string m_dataMimeType; std::string m_dataAppleCreatorType; std::string m_dataExtensions; + + std::optional m_foundPattern; + std::optional m_patternDataDescription; }; class InformationByteAnalysis : public ContentRegistry::DataInformation::InformationSection {