diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e37df93..20c87f822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ add_executable(ImHex source/views/view_pattern.cpp source/views/view_pattern_data.cpp source/views/view_hashes.cpp - source/views/view_entropy.cpp + source/views/view_information.cpp libs/glad/source/glad.c @@ -36,6 +36,6 @@ add_executable(ImHex res.rc ) -target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a) +target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libwinpthread.a) diff --git a/include/event.hpp b/include/event.hpp new file mode 100644 index 000000000..e735177cb --- /dev/null +++ b/include/event.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace hex { + + enum class Events { + DataChanged + }; + + struct EventHandler { + void *sender; + Events eventType; + std::function callback; + }; + + class EventManager { + public: + + void post(Events eventType, void *userData) { + for (auto &handler : this->m_eventHandlers) + if (eventType == handler.eventType) + handler.callback(userData); + } + + void subscribe(Events eventType, void *sender, std::function callback) { + for (auto &handler : this->m_eventHandlers) + if (eventType == handler.eventType && sender == handler.sender) + return; + + this->m_eventHandlers.push_back(EventHandler { sender, eventType, callback }); + } + + void unsubscribe(Events eventType, void *sender) { + std::erase_if(this->m_eventHandlers, [&eventType, &sender](EventHandler handler) { + return eventType == handler.eventType && sender == handler.sender; + }); + } + + private: + std::vector m_eventHandlers; + }; + +} \ No newline at end of file diff --git a/include/views/view.hpp b/include/views/view.hpp index 994b933ef..ed52e20c4 100644 --- a/include/views/view.hpp +++ b/include/views/view.hpp @@ -4,6 +4,8 @@ #include "imgui.h" +#include "event.hpp" + namespace hex { class View { @@ -14,6 +16,22 @@ namespace hex { virtual void createView() = 0; virtual void createMenu() { } virtual bool handleShortcut(int key, int mods) { return false; } + + protected: + void subscribeEvent(Events eventType, std::function callback) { + View::s_eventManager.subscribe(eventType, this, callback); + } + + void unsubscribeEvent(Events eventType) { + View::s_eventManager.unsubscribe(eventType, this); + } + + void postEvent(Events eventType, void *userData = nullptr) { + View::s_eventManager.post(eventType, userData); + } + + private: + static inline EventManager s_eventManager; }; } \ No newline at end of file diff --git a/include/views/view_entropy.hpp b/include/views/view_entropy.hpp deleted file mode 100644 index a7a0db042..000000000 --- a/include/views/view_entropy.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "views/view.hpp" - -#include - -namespace hex { - - namespace prv { class Provider; } - - class ViewEntropy : public View { - public: - explicit ViewEntropy(prv::Provider* &dataProvider); - ~ViewEntropy() override; - - void createView() override; - void createMenu() override; - - private: - prv::Provider* &m_dataProvider; - bool m_windowOpen = true; - - double m_entropy = 0; - float m_valueCounts[256] = { 0 }; - bool m_shouldInvalidate = true; - }; - -} \ No newline at end of file diff --git a/include/views/view_hashes.hpp b/include/views/view_hashes.hpp index d692e45a7..6881edf7e 100644 --- a/include/views/view_hashes.hpp +++ b/include/views/view_hashes.hpp @@ -20,6 +20,7 @@ namespace hex { prv::Provider* &m_dataProvider; bool m_windowOpen = true; + bool m_shouldInvalidate = true; int m_currHashFunction = 0; int m_hashStart = 0, m_hashEnd = 0; static constexpr const char* HashFunctionNames[] = { "CRC16", "CRC32", "MD5", "SHA-1", "SHA-256" }; diff --git a/include/views/view_hexeditor.hpp b/include/views/view_hexeditor.hpp index 631cf293b..c5a38cb63 100644 --- a/include/views/view_hexeditor.hpp +++ b/include/views/view_hexeditor.hpp @@ -36,6 +36,11 @@ namespace hex { char m_searchBuffer[0xFFFF] = { 0 }; s64 m_lastSearchIndex = 0; std::vector> m_lastSearch; + u64 m_gotoAddress = 0; + + + void drawSearchPopup(); + void drawGotoPopup(); }; } \ No newline at end of file diff --git a/include/views/view_information.hpp b/include/views/view_information.hpp new file mode 100644 index 000000000..31dcf1c0e --- /dev/null +++ b/include/views/view_information.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "views/view.hpp" + +#include +#include +#include +#include + +namespace hex { + + namespace prv { class Provider; } + + class ViewInformation : public View { + public: + explicit ViewInformation(prv::Provider* &dataProvider); + ~ViewInformation() override; + + void createView() override; + void createMenu() override; + + private: + prv::Provider* &m_dataProvider; + bool m_windowOpen = true; + + float m_averageEntropy = 0; + float m_highestBlockEntropy = 0; + std::vector m_blockEntropy; + + std::array m_valueCounts = { 0 }; + bool m_shouldInvalidate = true; + + std::string m_fileDescription; + std::string m_mimeType; + }; + +} \ No newline at end of file diff --git a/libs/ImGui/include/imgui_memory_editor.h b/libs/ImGui/include/imgui_memory_editor.h index 0ef9c508a..7fa68242e 100644 --- a/libs/ImGui/include/imgui_memory_editor.h +++ b/libs/ImGui/include/imgui_memory_editor.h @@ -180,9 +180,6 @@ struct MemoryEditor // Standalone Memory Editor window void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000) { - if (!Open) - return; - Sizes s; CalcSizes(s, mem_size, base_display_addr); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); @@ -208,13 +205,17 @@ struct MemoryEditor if (Cols < 1) Cols = 1; - if (mem_size == 0) - return; - ImU8* mem_data = (ImU8*)mem_data_void; Sizes s; CalcSizes(s, mem_size, base_display_addr); ImGuiStyle& style = ImGui::GetStyle(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + if (mem_size == 0x00) { + constexpr const char *noDataString = "No data loaded!"; + draw_list->AddText(ImVec2(ImGui::GetWindowWidth() / 2 - 55, ImGui::GetWindowHeight() / 2), 0xFFFFFFFF, noDataString); + return; + } // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window. // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move. @@ -225,11 +226,12 @@ struct MemoryEditor if (OptShowDataPreview) footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3; ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + + // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function. const int line_total_count = (int)((mem_size + Cols - 1) / Cols); ImGuiListClipper clipper; diff --git a/source/main.cpp b/source/main.cpp index d0dbfdaa0..01b270805 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -5,7 +5,7 @@ #include "views/view_pattern.hpp" #include "views/view_pattern_data.hpp" #include "views/view_hashes.hpp" -#include "views/view_entropy.hpp" +#include "views/view_information.hpp" #include "providers/provider.hpp" @@ -23,12 +23,9 @@ int main() { window.addView(highlights); window.addView(dataProvider, highlights); window.addView(dataProvider); - window.addView(dataProvider); + window.addView(dataProvider); window.loop(); - if (dataProvider != nullptr) - delete dataProvider; - return 0; } diff --git a/source/views/view_entropy.cpp b/source/views/view_entropy.cpp deleted file mode 100644 index 168ec52bf..000000000 --- a/source/views/view_entropy.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "views/view_entropy.hpp" - -#include "providers/provider.hpp" - -#include "utils.hpp" - -#include -#include -#include - -namespace hex { - - ViewEntropy::ViewEntropy(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) { - - } - - ViewEntropy::~ViewEntropy() { - - } - - - void ViewEntropy::createView() { - if (!this->m_windowOpen) - return; - - if (ImGui::Begin("Entropy", &this->m_windowOpen)) { - ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); - - if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) { - if (this->m_shouldInvalidate) { - std::vector buffer(512, 0x00); - std::memset(this->m_valueCounts, 0x00, sizeof(this->m_valueCounts)); - - for (u64 i = 0; i < this->m_dataProvider->getSize(); i += 512) { - this->m_dataProvider->read(i, buffer.data(), std::min(512ULL, this->m_dataProvider->getSize() - i)); - - for (u16 j = 0; j < 512; j++) - this->m_valueCounts[buffer[j]]++; - } - - size_t numBytes = this->m_dataProvider->getSize(); - this->m_entropy = 0; - for (u16 i = 0; i < 256; i++) { - this->m_valueCounts[i] /= numBytes; - - if (this->m_valueCounts[i] > 0) - this->m_entropy -= this->m_valueCounts[i] * std::log2(this->m_valueCounts[i]); - } - - this->m_entropy /= 8; - - this->m_shouldInvalidate = false; - } - - ImGui::LabelText("Entropy", "%.8f", this->m_entropy); - - if (this->m_entropy > 0.99) - ImGui::TextUnformatted("This file is most likely encrypted or compressed!"); - - ImGui::NewLine(); - ImGui::Separator(); - ImGui::NewLine(); - ImGui::PlotHistogram("Byte Distribution", this->m_valueCounts, 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100)); - - ImGui::NewLine(); - ImGui::Separator(); - ImGui::NewLine(); - - if (ImGui::Button("Invalidate")) - this->m_shouldInvalidate = true; - } - - ImGui::EndChild(); - } - ImGui::End(); - } - - void ViewEntropy::createMenu() { - if (ImGui::BeginMenu("View")) { - ImGui::MenuItem("Entropy View", "", &this->m_windowOpen); - ImGui::EndMenu(); - } - } - -} \ No newline at end of file diff --git a/source/views/view_hashes.cpp b/source/views/view_hashes.cpp index f308d6b62..1069a52a8 100644 --- a/source/views/view_hashes.cpp +++ b/source/views/view_hashes.cpp @@ -9,17 +9,17 @@ namespace hex { ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) { - + View::subscribeEvent(Events::DataChanged, [this](void*){ + this->m_shouldInvalidate = true; + }); } ViewHashes::~ViewHashes() { - + View::unsubscribeEvent(Events::DataChanged); } void ViewHashes::createView() { - static bool invalidate = false; - if (!this->m_windowOpen) return; @@ -29,16 +29,18 @@ namespace hex { if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) { - ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames, - sizeof(HashFunctionNames) / sizeof(const char *)); + ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *)); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; ImGui::NewLine(); ImGui::Separator(); ImGui::NewLine(); ImGui::InputInt("Begin", &this->m_hashStart, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; ImGui::InputInt("End", &this->m_hashEnd, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; size_t dataSize = this->m_dataProvider->getSize(); if (this->m_hashEnd >= dataSize) @@ -52,7 +54,7 @@ namespace hex { std::vector buffer; - if (invalidate) { + if (this->m_shouldInvalidate) { buffer = std::vector(this->m_hashEnd - this->m_hashStart + 1, 0x00); this->m_dataProvider->read(this->m_hashStart, buffer.data(), buffer.size()); } @@ -63,7 +65,10 @@ namespace hex { static int polynomial = 0, init = 0; ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; + ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; ImGui::NewLine(); ImGui::Separator(); @@ -71,7 +76,7 @@ namespace hex { static u16 result = 0; - if (invalidate) + if (this->m_shouldInvalidate) result = crc16(buffer.data(), buffer.size(), polynomial, init); ImGui::LabelText("##nolabel", "%X", result); @@ -82,7 +87,10 @@ namespace hex { static int polynomial = 0, init = 0; ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; + ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true; ImGui::NewLine(); ImGui::Separator(); @@ -90,7 +98,7 @@ namespace hex { static u32 result = 0; - if (invalidate) + if (this->m_shouldInvalidate) result = crc32(buffer.data(), buffer.size(), polynomial, init); ImGui::LabelText("##nolabel", "%X", result); @@ -100,7 +108,7 @@ namespace hex { { static std::array result; - if (invalidate) + if (this->m_shouldInvalidate) result = md5(buffer.data(), buffer.size()); ImGui::LabelText("##nolabel", "%08X%08X%08X%08X", @@ -114,12 +122,7 @@ namespace hex { } - invalidate = false; - - ImGui::SameLine(); - if (ImGui::Button("Hash")) - invalidate = true; - + this->m_shouldInvalidate = false; } ImGui::EndChild(); } diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp index 7bc00cd64..fcc508a3e 100644 --- a/source/views/view_hexeditor.cpp +++ b/source/views/view_hexeditor.cpp @@ -29,6 +29,7 @@ namespace hex { return; _this->m_dataProvider->write(off, &d, sizeof(ImU8)); + _this->postEvent(Events::DataChanged); }; this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool { @@ -50,9 +51,13 @@ namespace hex { }; } - ViewHexEditor::~ViewHexEditor() {} + ViewHexEditor::~ViewHexEditor() { + if (this->m_dataProvider != nullptr) + delete this->m_dataProvider; + this->m_dataProvider = nullptr; + } - auto findString(prv::Provider* &provider, std::string string) { + static auto findString(prv::Provider* &provider, std::string string) { std::vector> results; u32 foundCharacters = 0; @@ -76,24 +81,76 @@ namespace hex { } } - - return results; } - void ViewHexEditor::createView() { - this->m_memoryEditor.DrawWindow("Hex Editor", this, (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize()); + void ViewHexEditor::createView() { + if (!this->m_memoryEditor.Open) + return; + + size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize(); + + this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize); + + if (dataSize != 0x00) { + this->drawSearchPopup(); + this->drawGotoPopup(); + } + + } + + void ViewHexEditor::createMenu() { + + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open File...")) { + auto filePath = openFileDialog(); + if (filePath.has_value()) { + if (this->m_dataProvider != nullptr) + delete this->m_dataProvider; + + this->m_dataProvider = new prv::FileProvider(filePath.value()); + View::postEvent(Events::DataChanged); + } + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("View")) { + ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open); + ImGui::EndMenu(); + } + } + + bool ViewHexEditor::handleShortcut(int key, int mods) { + if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_F) { + ImGui::OpenPopup("Search"); + return true; + } else if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_G) { + ImGui::OpenPopup("Goto"); + return true; + } + + return false; + } + + + void ViewHexEditor::drawSearchPopup() { if (ImGui::BeginPopup("Search")) { ImGui::TextUnformatted("Search"); - ImGui::InputText("##nolabel", this->m_searchBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackEdit, - [](ImGuiInputTextCallbackData* data) -> int { - auto lastSearch = static_cast>*>(data->UserData); + ImGui::InputText("##nolabel", this->m_searchBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackCompletion, + [](ImGuiInputTextCallbackData* data) -> int { + auto _this = static_cast(data->UserData); - lastSearch->clear(); + _this->m_lastSearch = findString(_this->m_dataProvider, _this->m_searchBuffer); + _this->m_lastSearchIndex = 0; - return 0; - }, &this->m_lastSearch); + if (_this->m_lastSearch.size() > 0) + _this->m_memoryEditor.GotoAddrAndHighlight(_this->m_lastSearch[0].first, _this->m_lastSearch[0].second); + + return 0; + }, this); if (ImGui::Button("Find")) { @@ -135,35 +192,20 @@ namespace hex { } } - void ViewHexEditor::createMenu() { - if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("Open File...")) { - auto filePath = openFileDialog(); - if (filePath.has_value()) { - if (this->m_dataProvider != nullptr) - delete this->m_dataProvider; + void ViewHexEditor::drawGotoPopup() { + if (ImGui::BeginPopup("Goto")) { + ImGui::TextUnformatted("Goto"); + ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal); - this->m_dataProvider = new prv::FileProvider(filePath.value()); - } + if (this->m_gotoAddress >= this->m_dataProvider->getSize()) + this->m_gotoAddress = this->m_dataProvider->getSize() - 1; + if (ImGui::Button("Goto")) { + this->m_memoryEditor.GotoAddr = this->m_gotoAddress; } - ImGui::EndMenu(); + ImGui::EndPopup(); } - - if (ImGui::BeginMenu("View")) { - ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open); - ImGui::EndMenu(); - } - } - - bool ViewHexEditor::handleShortcut(int key, int mods) { - if (mods & GLFW_MOD_CONTROL && key == GLFW_KEY_F) { - ImGui::OpenPopup("Search"); - return true; - } - - return false; } } \ No newline at end of file diff --git a/source/views/view_information.cpp b/source/views/view_information.cpp new file mode 100644 index 000000000..40174f5ab --- /dev/null +++ b/source/views/view_information.cpp @@ -0,0 +1,139 @@ +#include "views/view_information.hpp" + +#include "providers/provider.hpp" + +#include "utils.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace hex { + + ViewInformation::ViewInformation(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) { + View::subscribeEvent(Events::DataChanged, [this](void*) { + this->m_shouldInvalidate = true; + }); + } + + ViewInformation::~ViewInformation() { + View::unsubscribeEvent(Events::DataChanged); + } + + static float calculateEntropy(std::array &valueCounts, size_t numBytes) { + float entropy = 0; + + for (u16 i = 0; i < 256; i++) { + valueCounts[i] /= numBytes; + + if (valueCounts[i] > 0) + entropy -= (valueCounts[i] * std::log2(valueCounts[i])); + } + + return entropy / 8; + } + + void ViewInformation::createView() { + if (!this->m_windowOpen) + return; + + if (ImGui::Begin("File Information", &this->m_windowOpen)) { + ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); + + if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) { + if (this->m_shouldInvalidate) { + + { + std::vector buffer(512, 0x00); + std::memset(this->m_valueCounts.data(), 0x00, this->m_valueCounts.size() * sizeof(u32)); + this->m_blockEntropy.clear(); + + for (u64 i = 0; i < this->m_dataProvider->getSize(); i += 512) { + std::array blockValueCounts = { 0 }; + this->m_dataProvider->read(i, buffer.data(), std::min(512ULL, this->m_dataProvider->getSize() - i)); + + for (u16 j = 0; j < 512; j++) { + blockValueCounts[buffer[j]]++; + this->m_valueCounts[buffer[j]]++; + } + this->m_blockEntropy.push_back(calculateEntropy(blockValueCounts, 512)); + } + + this->m_averageEntropy = calculateEntropy(this->m_valueCounts, this->m_dataProvider->getSize()); + this->m_highestBlockEntropy = *std::max_element(this->m_blockEntropy.begin(), this->m_blockEntropy.end()); + } + + { + std::vector buffer(this->m_dataProvider->getSize(), 0x00); + this->m_dataProvider->read(0x00, buffer.data(), buffer.size()); + + { + magic_t cookie = magic_open(MAGIC_NONE); + magic_load(cookie, "magic"); + this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size()); + magic_close(cookie); + } + + { + magic_t cookie = magic_open(MAGIC_MIME); + magic_load(cookie, "magic"); + this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size()); + magic_close(cookie); + } + + this->m_shouldInvalidate = false; + } + } + + ImGui::NewLine(); + + ImGui::Text("Byte Distribution"); + ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100)); + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + ImGui::Text("Entropy"); + ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100)); + + ImGui::NewLine(); + + ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy); + ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy); + + ImGui::NewLine(); + + if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) + ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "This data is most likely encrypted or compressed!"); + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + ImGui::TextUnformatted("Description:"); + ImGui::TextWrapped("%s", this->m_fileDescription.c_str()); + ImGui::NewLine(); + ImGui::TextUnformatted("MIME Type:"); + ImGui::TextWrapped("%s", this->m_mimeType.c_str()); + + ImGui::NewLine(); + } + + ImGui::EndChild(); + } + ImGui::End(); + } + + void ViewInformation::createMenu() { + if (ImGui::BeginMenu("View")) { + ImGui::MenuItem("Entropy View", "", &this->m_windowOpen); + ImGui::EndMenu(); + } + } + +} \ No newline at end of file