mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Added event system and make use of it for data invalidation
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
|
||||
45
include/event.hpp
Normal file
45
include/event.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace hex {
|
||||
|
||||
enum class Events {
|
||||
DataChanged
|
||||
};
|
||||
|
||||
struct EventHandler {
|
||||
void *sender;
|
||||
Events eventType;
|
||||
std::function<void(void*)> 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<void(void*)> 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<EventHandler> m_eventHandlers;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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<void(void*)> 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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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" };
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace hex {
|
||||
char m_searchBuffer[0xFFFF] = { 0 };
|
||||
s64 m_lastSearchIndex = 0;
|
||||
std::vector<std::pair<u64, u64>> m_lastSearch;
|
||||
u64 m_gotoAddress = 0;
|
||||
|
||||
|
||||
void drawSearchPopup();
|
||||
void drawGotoPopup();
|
||||
};
|
||||
|
||||
}
|
||||
37
include/views/view_information.hpp
Normal file
37
include/views/view_information.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<float> m_blockEntropy;
|
||||
|
||||
std::array<float, 256> m_valueCounts = { 0 };
|
||||
bool m_shouldInvalidate = true;
|
||||
|
||||
std::string m_fileDescription;
|
||||
std::string m_mimeType;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<hex::ViewPattern>(highlights);
|
||||
window.addView<hex::ViewPatternData>(dataProvider, highlights);
|
||||
window.addView<hex::ViewHashes>(dataProvider);
|
||||
window.addView<hex::ViewEntropy>(dataProvider);
|
||||
window.addView<hex::ViewInformation>(dataProvider);
|
||||
|
||||
window.loop();
|
||||
|
||||
if (dataProvider != nullptr)
|
||||
delete dataProvider;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "views/view_entropy.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
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<u8> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<u8> buffer;
|
||||
|
||||
if (invalidate) {
|
||||
if (this->m_shouldInvalidate) {
|
||||
buffer = std::vector<u8>(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<u32, 4> 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();
|
||||
}
|
||||
|
||||
@@ -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<std::pair<u64, u64>> 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<std::vector<std::pair<u64, u64>>*>(data->UserData);
|
||||
ImGui::InputText("##nolabel", this->m_searchBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackCompletion,
|
||||
[](ImGuiInputTextCallbackData* data) -> int {
|
||||
auto _this = static_cast<ViewHexEditor*>(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;
|
||||
}
|
||||
|
||||
}
|
||||
139
source/views/view_information.cpp
Normal file
139
source/views/view_information.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "views/view_information.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <magic.h>
|
||||
|
||||
#include <span>
|
||||
|
||||
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<float, 256> &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<u8> 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<float, 256> 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<u8> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user