diff --git a/external/ImGui/include/imgui_imhex_extensions.h b/external/ImGui/include/imgui_imhex_extensions.h index 99138a453..325018317 100644 --- a/external/ImGui/include/imgui_imhex_extensions.h +++ b/external/ImGui/include/imgui_imhex_extensions.h @@ -15,6 +15,8 @@ namespace ImGui { void Disabled(const std::function &widgets, bool disabled); void TextSpinner(const char* label); + void Header(const char *label, bool firstEntry = false); + enum ImGuiCustomCol { ImGuiCustomCol_DescButton, ImGuiCustomCol_DescButtonHovered, diff --git a/external/ImGui/source/imgui_imhex_extensions.cpp b/external/ImGui/source/imgui_imhex_extensions.cpp index 1707b22d7..06e6f9615 100644 --- a/external/ImGui/source/imgui_imhex_extensions.cpp +++ b/external/ImGui/source/imgui_imhex_extensions.cpp @@ -157,6 +157,13 @@ namespace ImGui { ImGui::Text("[%c] %s", "|/-\\"[ImU32(ImGui::GetTime() * 20) % 4], label); } + void Header(const char *label, bool firstEntry) { + if (!firstEntry) + ImGui::NewLine(); + ImGui::TextUnformatted(label); + ImGui::Separator(); + } + ImU32 GetCustomColorU32(ImGuiCustomCol idx, float alpha_mul) { auto& customData = *static_cast(GImGui->IO.UserData); ImVec4 c = customData.Colors[idx]; diff --git a/include/views/view_information.hpp b/include/views/view_information.hpp index 2431bd78b..3210a131c 100644 --- a/include/views/view_information.hpp +++ b/include/views/view_information.hpp @@ -26,7 +26,7 @@ namespace hex { float m_highestBlockEntropy = 0; std::vector m_blockEntropy; - std::array m_valueCounts = { 0 }; + std::array m_valueCounts = { 0 }; bool m_analyzing = false; std::pair m_analyzedRegion = { 0, 0 }; diff --git a/plugins/builtin/source/lang/en_US.cpp b/plugins/builtin/source/lang/en_US.cpp index 9733eba5c..bd7a71e2f 100644 --- a/plugins/builtin/source/lang/en_US.cpp +++ b/plugins/builtin/source/lang/en_US.cpp @@ -207,7 +207,7 @@ namespace hex::plugin::builtin { { "hex.view.information.distribution", "Byte distribution" }, { "hex.view.information.entropy", "Entropy" }, { "hex.view.information.block_size", "Block size" }, - { "hex.view.information.block_size.desc", "2046 blocks of %lu bytes" }, + { "hex.view.information.block_size.desc", "%lu blocks of %lu bytes" }, { "hex.view.information.file_entropy", "File entropy" }, { "hex.view.information.highest_entropy", "Highest entropy block" }, { "hex.view.information.encrypted", "This data is most likely encrypted or compressed!" }, diff --git a/source/views/view_information.cpp b/source/views/view_information.cpp index 5821dd1e1..d65490763 100644 --- a/source/views/view_information.cpp +++ b/source/views/view_information.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include namespace hex { @@ -34,14 +36,20 @@ namespace hex { View::unsubscribeEvent(Events::DataChanged); } - static float calculateEntropy(std::array &valueCounts, size_t numBytes) { + static float calculateEntropy(std::array &valueCounts, size_t numBytes) { float entropy = 0; - for (u16 i = 0; i < 256; i++) { - valueCounts[i] /= numBytes; + if (numBytes == 0) + return 0.0F; - if (valueCounts[i] > 0) - entropy -= (valueCounts[i] * std::log2(valueCounts[i])); + std::array floatValueCounts{ 0 }; + std::copy(valueCounts.begin(), valueCounts.end(), floatValueCounts.begin()); + + for (u16 i = 0; i < 256; i++) { + floatValueCounts[i] /= float(numBytes); + + if (floatValueCounts[i] > 0) + entropy -= (floatValueCounts[i] * std::log2(floatValueCounts[i])); } return entropy / 8; @@ -56,13 +64,13 @@ namespace hex { this->m_analyzedRegion = { provider->getBaseAddress(), provider->getBaseAddress() + provider->getSize() }; { - this->m_blockSize = std::ceil(provider->getSize() / 2048.0F); + this->m_blockSize = std::max(std::ceil(provider->getSize() / 2048.0F), 256); std::vector buffer(this->m_blockSize, 0x00); std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32)); this->m_blockEntropy.clear(); for (u64 i = 0; i < provider->getSize(); i += this->m_blockSize) { - std::array blockValueCounts = { 0 }; + std::array blockValueCounts = { 0 }; provider->read(i, buffer.data(), std::min(u64(this->m_blockSize), provider->getSize() - i)); for (size_t j = 0; j < this->m_blockSize; j++) { @@ -177,23 +185,46 @@ namespace hex { ImGui::TextUnformatted("hex.view.information.info_analysis"_lang); ImGui::Separator(); - ImGui::Text("hex.view.information.distribution"_lang); - ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100)); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg)); + + ImGui::TextUnformatted("hex.view.information.distribution"_lang); + ImPlot::SetNextPlotLimits(0, 256, 0, float(*std::max_element(this->m_valueCounts.begin(), this->m_valueCounts.end())) * 1.1F, ImGuiCond_Always); + if (ImPlot::BeginPlot("##distribution", "Address", "Count", ImVec2(-1,0), ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect, ImPlotAxisFlags_Lock, ImPlotAxisFlags_Lock)) { + constexpr static auto x = []{ + std::array result{ 0 }; + std::iota(result.begin(), result.end(), 0); + return result; + }(); + + ImPlot::PlotBars("##bytes", x.data(), this->m_valueCounts.data(), x.size(), 0.67); + ImPlot::EndPlot(); + } ImGui::NewLine(); - ImGui::Text("hex.view.information.entropy"_lang); - ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100)); + ImGui::TextUnformatted("hex.view.information.entropy"_lang); + + ImPlot::SetNextPlotLimits(0, this->m_blockEntropy.size(), -0.1, 1.1, ImGuiCond_Always); + if (ImPlot::BeginPlot("##entropy", "Address", "Entropy", ImVec2(-1,0), ImPlotFlags_CanvasOnly, ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoTickLabels, ImPlotAxisFlags_Lock)) { + ImPlot::PlotLine("##entropy_line", this->m_blockEntropy.data(), this->m_blockEntropy.size()); + + if (ImGui::IsItemClicked()) + View::postEvent(Events::SelectionChangeRequest, Region{ u64(ImPlot::GetPlotMousePos().x) * this->m_blockSize, 1 }); + + ImPlot::EndPlot(); + } + + ImGui::PopStyleColor(); ImGui::NewLine(); - ImGui::LabelText("hex.view.information.block_size"_lang, "hex.view.information.block_size.desc"_lang, this->m_blockSize); + ImGui::LabelText("hex.view.information.block_size"_lang, "hex.view.information.block_size.desc"_lang, this->m_blockEntropy.size(), this->m_blockSize); ImGui::LabelText("hex.view.information.file_entropy"_lang, "%.8f", this->m_averageEntropy); ImGui::LabelText("hex.view.information.highest_entropy"_lang, "%.8f", this->m_highestBlockEntropy); if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) { ImGui::NewLine(); - ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "hex.view.information.encrypted"_lang); + ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "%s", static_cast("hex.view.information.encrypted"_lang)); } }