2020-11-10 21:31:04 +01:00
|
|
|
#include "views/view_hexeditor.hpp"
|
|
|
|
|
2020-11-11 10:47:02 +01:00
|
|
|
#include "providers/provider.hpp"
|
2020-11-11 09:18:35 +01:00
|
|
|
#include "providers/file_provider.hpp"
|
|
|
|
|
2020-11-11 14:42:01 +01:00
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
|
2020-11-10 21:31:04 +01:00
|
|
|
namespace hex {
|
|
|
|
|
2020-11-19 11:36:52 +01:00
|
|
|
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
2020-11-14 14:42:21 +01:00
|
|
|
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
2020-11-10 21:31:04 +01:00
|
|
|
|
|
|
|
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
|
|
|
|
ViewHexEditor *_this = (ViewHexEditor *) data;
|
|
|
|
|
2020-11-11 09:18:35 +01:00
|
|
|
if (!_this->m_dataProvider->isAvailable() || !_this->m_dataProvider->isReadable())
|
2020-11-10 21:31:04 +01:00
|
|
|
return 0x00;
|
|
|
|
|
|
|
|
ImU8 byte;
|
2020-11-11 09:18:35 +01:00
|
|
|
_this->m_dataProvider->read(off, &byte, sizeof(ImU8));
|
2020-11-10 21:31:04 +01:00
|
|
|
|
|
|
|
return byte;
|
|
|
|
};
|
|
|
|
|
|
|
|
this->m_memoryEditor.WriteFn = [](ImU8 *data, size_t off, ImU8 d) -> void {
|
|
|
|
ViewHexEditor *_this = (ViewHexEditor *) data;
|
|
|
|
|
2020-11-11 09:18:35 +01:00
|
|
|
if (!_this->m_dataProvider->isAvailable() || !_this->m_dataProvider->isWritable())
|
2020-11-10 21:31:04 +01:00
|
|
|
return;
|
|
|
|
|
2020-11-11 09:18:35 +01:00
|
|
|
_this->m_dataProvider->write(off, &d, sizeof(ImU8));
|
2020-11-12 09:38:52 +01:00
|
|
|
_this->postEvent(Events::DataChanged);
|
2020-11-10 21:31:04 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
this->m_memoryEditor.HighlightFn = [](const ImU8 *data, size_t off, bool next) -> bool {
|
|
|
|
ViewHexEditor *_this = (ViewHexEditor *) data;
|
|
|
|
|
2020-11-14 14:42:21 +01:00
|
|
|
for (auto& pattern : _this->m_patternData) {
|
2020-11-19 11:36:52 +01:00
|
|
|
if (next && pattern->highlightBytes(off - 1) != pattern->highlightBytes(off)) {
|
2020-11-10 21:31:04 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-19 11:36:52 +01:00
|
|
|
if (auto color = pattern->highlightBytes(off); color.has_value()) {
|
|
|
|
_this->m_memoryEditor.HighlightColor = color.value();
|
2020-11-10 21:31:04 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-19 22:34:56 +01:00
|
|
|
_this->m_memoryEditor.HighlightColor = 0x60C08080;
|
2020-11-10 21:31:04 +01:00
|
|
|
return false;
|
|
|
|
};
|
2020-11-17 13:58:50 +01:00
|
|
|
|
|
|
|
View::subscribeEvent(Events::FileDropped, [this](const void *userData) {
|
|
|
|
auto filePath = static_cast<const char*>(userData);
|
|
|
|
|
|
|
|
if (filePath != nullptr)
|
|
|
|
this->openFile(filePath);
|
|
|
|
});
|
2020-11-10 21:31:04 +01:00
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
ViewHexEditor::~ViewHexEditor() {
|
|
|
|
if (this->m_dataProvider != nullptr)
|
|
|
|
delete this->m_dataProvider;
|
|
|
|
this->m_dataProvider = nullptr;
|
|
|
|
}
|
2020-11-10 21:31:04 +01:00
|
|
|
|
|
|
|
void ViewHexEditor::createView() {
|
2020-11-12 09:38:52 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-11-17 15:38:24 +01:00
|
|
|
if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
|
|
|
|
this->openFile(this->m_fileBrowser.selected_path);
|
2020-11-12 21:20:51 +01:00
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
}
|
|
|
|
|
2020-11-17 13:58:50 +01:00
|
|
|
void ViewHexEditor::openFile(std::string path) {
|
|
|
|
if (this->m_dataProvider != nullptr)
|
|
|
|
delete this->m_dataProvider;
|
|
|
|
|
|
|
|
this->m_dataProvider = new prv::FileProvider(path);
|
|
|
|
View::postEvent(Events::DataChanged);
|
|
|
|
}
|
|
|
|
|
2020-11-16 00:07:42 +01:00
|
|
|
void ViewHexEditor::copyBytes() {
|
2020-11-17 02:36:12 +01:00
|
|
|
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
|
|
|
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
|
|
|
|
|
|
|
size_t copySize = (end - start) + 1;
|
2020-11-16 00:07:42 +01:00
|
|
|
|
|
|
|
std::vector<u8> buffer(copySize, 0x00);
|
2020-11-17 02:36:12 +01:00
|
|
|
this->m_dataProvider->read(start, buffer.data(), buffer.size());
|
2020-11-16 00:07:42 +01:00
|
|
|
|
|
|
|
std::string str;
|
|
|
|
for (const auto &byte : buffer)
|
|
|
|
str += hex::format("%x ", byte);
|
|
|
|
str.pop_back();
|
|
|
|
|
|
|
|
ImGui::SetClipboardText(str.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewHexEditor::copyString() {
|
2020-11-17 02:36:12 +01:00
|
|
|
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
|
|
|
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
|
|
|
|
|
|
|
size_t copySize = (end - start) + 1;
|
2020-11-16 00:07:42 +01:00
|
|
|
|
|
|
|
std::string buffer;
|
|
|
|
buffer.reserve(copySize + 1);
|
2020-11-17 02:36:12 +01:00
|
|
|
this->m_dataProvider->read(start, buffer.data(), copySize);
|
2020-11-16 00:07:42 +01:00
|
|
|
|
|
|
|
ImGui::SetClipboardText(buffer.c_str());
|
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
void ViewHexEditor::createMenu() {
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
if (ImGui::BeginMenu("File")) {
|
2020-11-16 00:07:42 +01:00
|
|
|
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
|
2020-11-17 15:38:24 +01:00
|
|
|
View::doLater([]{ ImGui::OpenPopup("Open File"); });
|
2020-11-12 09:38:52 +01:00
|
|
|
}
|
|
|
|
|
2020-11-16 00:07:42 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
if (ImGui::MenuItem("Search", "CTRL + F")) {
|
|
|
|
View::doLater([]{ ImGui::OpenPopup("Search"); });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::MenuItem("Goto", "CTRL + G")) {
|
|
|
|
View::doLater([]{ ImGui::OpenPopup("Goto"); });
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::BeginMenu("Edit")) {
|
|
|
|
if (ImGui::MenuItem("Copy bytes", "CTRL + ALT + C")) {
|
|
|
|
this->copyBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::MenuItem("Copy string", "CTRL + SHIFT + C")) {
|
|
|
|
this->copyString();
|
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::BeginMenu("View")) {
|
|
|
|
ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open);
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ViewHexEditor::handleShortcut(int key, int mods) {
|
2020-11-16 00:07:42 +01:00
|
|
|
if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
|
2020-11-12 09:38:52 +01:00
|
|
|
ImGui::OpenPopup("Search");
|
|
|
|
return true;
|
2020-11-16 00:07:42 +01:00
|
|
|
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
|
2020-11-12 09:38:52 +01:00
|
|
|
ImGui::OpenPopup("Goto");
|
|
|
|
return true;
|
2020-11-16 00:07:42 +01:00
|
|
|
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
|
2020-11-17 15:38:24 +01:00
|
|
|
ImGui::OpenPopup("Open File");
|
2020-11-16 00:07:42 +01:00
|
|
|
return true;
|
|
|
|
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
|
|
|
|
this->copyBytes();
|
|
|
|
return true;
|
|
|
|
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_C) {
|
|
|
|
this->copyString();
|
|
|
|
return true;
|
2020-11-12 09:38:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
static std::vector<std::pair<u64, u64>> findString(prv::Provider* &provider, std::string string) {
|
|
|
|
std::vector<std::pair<u64, u64>> results;
|
|
|
|
|
|
|
|
u32 foundCharacters = 0;
|
|
|
|
|
|
|
|
std::vector<u8> buffer(1024, 0x00);
|
|
|
|
size_t dataSize = provider->getSize();
|
|
|
|
for (u64 offset = 0; offset < dataSize; offset += 1024) {
|
|
|
|
size_t usedBufferSize = std::min(buffer.size(), dataSize - offset);
|
|
|
|
provider->read(offset, buffer.data(), usedBufferSize);
|
|
|
|
|
|
|
|
for (u64 i = 0; i < usedBufferSize; i++) {
|
|
|
|
if (buffer[i] == string[foundCharacters])
|
|
|
|
foundCharacters++;
|
|
|
|
else
|
|
|
|
foundCharacters = 0;
|
|
|
|
|
|
|
|
if (foundCharacters == string.size()) {
|
|
|
|
results.emplace_back(offset + i - foundCharacters + 1, offset + i + 1);
|
|
|
|
foundCharacters = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::pair<u64, u64>> findHex(prv::Provider* &provider, std::string string) {
|
|
|
|
std::vector<std::pair<u64, u64>> results;
|
|
|
|
|
|
|
|
if ((string.size() % 2) == 1)
|
|
|
|
string = "0" + string;
|
|
|
|
|
|
|
|
std::vector<u8> hex;
|
|
|
|
hex.reserve(string.size() / 2);
|
|
|
|
|
|
|
|
for (u32 i = 0; i < string.size(); i += 2) {
|
|
|
|
char byte[3] = { string[i], string[i + 1], 0 };
|
|
|
|
hex.push_back(strtoul(byte, nullptr, 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 foundCharacters = 0;
|
|
|
|
|
|
|
|
std::vector<u8> buffer(1024, 0x00);
|
|
|
|
size_t dataSize = provider->getSize();
|
|
|
|
for (u64 offset = 0; offset < dataSize; offset += 1024) {
|
|
|
|
size_t usedBufferSize = std::min(buffer.size(), dataSize - offset);
|
|
|
|
provider->read(offset, buffer.data(), usedBufferSize);
|
|
|
|
|
|
|
|
for (u64 i = 0; i < usedBufferSize; i++) {
|
|
|
|
if (buffer[i] == hex[foundCharacters])
|
|
|
|
foundCharacters++;
|
|
|
|
else
|
|
|
|
foundCharacters = 0;
|
|
|
|
|
|
|
|
if (foundCharacters == hex.size()) {
|
|
|
|
results.emplace_back(offset + i - foundCharacters + 1, offset + i + 1);
|
|
|
|
foundCharacters = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
void ViewHexEditor::drawSearchPopup() {
|
2020-11-15 23:04:46 +01:00
|
|
|
static auto InputCallback = [](ImGuiInputTextCallbackData* data) -> int {
|
|
|
|
auto _this = static_cast<ViewHexEditor*>(data->UserData);
|
2020-11-12 09:38:52 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
*_this->m_lastSearchBuffer = _this->m_searchFunction(_this->m_dataProvider, data->Buf);
|
|
|
|
_this->m_lastSearchIndex = 0;
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (_this->m_lastSearchBuffer->size() > 0)
|
|
|
|
_this->m_memoryEditor.GotoAddrAndHighlight((*_this->m_lastSearchBuffer)[0].first, (*_this->m_lastSearchBuffer)[0].second);
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
return 0;
|
|
|
|
};
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
static auto Find = [this](char *buffer) {
|
|
|
|
*this->m_lastSearchBuffer = this->m_searchFunction(this->m_dataProvider, buffer);
|
|
|
|
this->m_lastSearchIndex = 0;
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (this->m_lastSearchBuffer->size() > 0)
|
|
|
|
this->m_memoryEditor.GotoAddrAndHighlight((*this->m_lastSearchBuffer)[0].first, (*this->m_lastSearchBuffer)[0].second);
|
|
|
|
};
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
static auto FindNext = [this]() {
|
|
|
|
if (this->m_lastSearchBuffer->size() > 0) {
|
|
|
|
++this->m_lastSearchIndex %= this->m_lastSearchBuffer->size();
|
|
|
|
this->m_memoryEditor.GotoAddrAndHighlight((*this->m_lastSearchBuffer)[this->m_lastSearchIndex].first,
|
|
|
|
(*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second);
|
2020-11-11 14:42:01 +01:00
|
|
|
}
|
2020-11-15 23:04:46 +01:00
|
|
|
};
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
static auto FindPrevious = [this]() {
|
|
|
|
if (this->m_lastSearchBuffer->size() > 0) {
|
|
|
|
this->m_lastSearchIndex--;
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (this->m_lastSearchIndex < 0)
|
|
|
|
this->m_lastSearchIndex = this->m_lastSearchBuffer->size() - 1;
|
|
|
|
|
|
|
|
this->m_lastSearchIndex %= this->m_lastSearchBuffer->size();
|
|
|
|
|
|
|
|
this->m_memoryEditor.GotoAddrAndHighlight((*this->m_lastSearchBuffer)[this->m_lastSearchIndex].first,
|
|
|
|
(*this->m_lastSearchBuffer)[this->m_lastSearchIndex].second);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (ImGui::BeginPopup("Search")) {
|
|
|
|
ImGui::TextUnformatted("Search");
|
|
|
|
if (ImGui::BeginTabBar("searchTabs")) {
|
|
|
|
char *currBuffer;
|
|
|
|
if (ImGui::BeginTabItem("String")) {
|
|
|
|
this->m_searchFunction = findString;
|
|
|
|
this->m_lastSearchBuffer = &this->m_lastStringSearch;
|
|
|
|
currBuffer = this->m_searchStringBuffer;
|
|
|
|
|
|
|
|
ImGui::InputText("##nolabel", currBuffer, 0xFFFF, ImGuiInputTextFlags_CallbackCompletion,
|
|
|
|
InputCallback, this);
|
|
|
|
ImGui::EndTabItem();
|
2020-11-11 14:42:01 +01:00
|
|
|
}
|
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (ImGui::BeginTabItem("Hex")) {
|
|
|
|
this->m_searchFunction = findHex;
|
|
|
|
this->m_lastSearchBuffer = &this->m_lastHexSearch;
|
|
|
|
currBuffer = this->m_searchHexBuffer;
|
|
|
|
|
|
|
|
ImGui::InputText("##nolabel", currBuffer, 0xFFFF,
|
|
|
|
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CallbackCompletion,
|
|
|
|
InputCallback, this);
|
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (ImGui::Button("Find"))
|
|
|
|
Find(currBuffer);
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if (this->m_lastSearchBuffer->size() > 0) {
|
|
|
|
if ((ImGui::Button("Find Next")))
|
|
|
|
FindNext();
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
ImGui::SameLine();
|
2020-11-11 14:42:01 +01:00
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
if ((ImGui::Button("Find Prev")))
|
|
|
|
FindPrevious();
|
2020-11-11 14:42:01 +01:00
|
|
|
}
|
2020-11-15 23:04:46 +01:00
|
|
|
|
|
|
|
ImGui::EndTabBar();
|
2020-11-11 14:42:01 +01:00
|
|
|
}
|
|
|
|
|
2020-11-15 23:04:46 +01:00
|
|
|
|
2020-11-11 14:42:01 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
2020-11-10 21:31:04 +01:00
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
void ViewHexEditor::drawGotoPopup() {
|
|
|
|
if (ImGui::BeginPopup("Goto")) {
|
|
|
|
ImGui::TextUnformatted("Goto");
|
|
|
|
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
2020-11-10 21:31:04 +01:00
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
if (this->m_gotoAddress >= this->m_dataProvider->getSize())
|
|
|
|
this->m_gotoAddress = this->m_dataProvider->getSize() - 1;
|
2020-11-10 21:31:04 +01:00
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
if (ImGui::Button("Goto")) {
|
|
|
|
this->m_memoryEditor.GotoAddr = this->m_gotoAddress;
|
2020-11-10 21:31:04 +01:00
|
|
|
}
|
|
|
|
|
2020-11-12 09:38:52 +01:00
|
|
|
ImGui::EndPopup();
|
2020-11-11 14:42:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 21:31:04 +01:00
|
|
|
}
|