2021-12-07 22:47:41 +01:00
|
|
|
#include "content/views/view_strings.hpp"
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2021-01-13 17:28:27 +01:00
|
|
|
#include <hex/providers/provider.hpp>
|
2021-11-30 21:02:37 +01:00
|
|
|
#include <hex/helpers/fmt.hpp>
|
2020-11-15 01:42:43 +01:00
|
|
|
|
|
|
|
#include <cstring>
|
2021-02-22 10:16:58 +01:00
|
|
|
#include <thread>
|
2021-11-25 08:46:42 +01:00
|
|
|
#include <regex>
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2020-11-24 18:12:08 +01:00
|
|
|
#include <llvm/Demangle/Demangle.h>
|
2021-12-31 01:10:06 +01:00
|
|
|
#include <hex/ui/imgui_imhex_extensions.h>
|
2020-11-24 18:12:08 +01:00
|
|
|
|
2020-11-23 13:10:14 +01:00
|
|
|
using namespace std::literals::string_literals;
|
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
namespace hex::plugin::builtin {
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
ViewStrings::ViewStrings() : View("hex.builtin.view.strings.name") {
|
2021-03-27 11:36:36 +01:00
|
|
|
EventManager::subscribe<EventDataChanged>(this, [this]() {
|
2020-11-22 19:43:35 +01:00
|
|
|
this->m_foundStrings.clear();
|
2020-11-15 01:42:43 +01:00
|
|
|
});
|
|
|
|
|
2021-09-06 22:45:55 +02:00
|
|
|
this->m_filter.reserve(0xFFFF);
|
|
|
|
std::memset(this->m_filter.data(), 0x00, this->m_filter.capacity());
|
2021-12-05 22:09:43 +01:00
|
|
|
|
|
|
|
EventManager::subscribe<EventFileUnloaded>(this, [this]{
|
|
|
|
this->m_foundStrings.clear();
|
|
|
|
});
|
2020-11-15 01:42:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ViewStrings::~ViewStrings() {
|
2021-03-27 11:36:36 +01:00
|
|
|
EventManager::unsubscribe<EventDataChanged>(this);
|
2021-12-05 22:09:43 +01:00
|
|
|
EventManager::unsubscribe<EventFileUnloaded>(this);
|
2020-11-15 01:42:43 +01:00
|
|
|
}
|
|
|
|
|
2021-09-06 22:45:55 +02:00
|
|
|
std::string readString(const FoundString &foundString) {
|
|
|
|
std::string string(foundString.size + 1, '\0');
|
2021-09-21 02:29:54 +02:00
|
|
|
ImHexApi::Provider::get()->read(foundString.offset, string.data(), foundString.size);
|
2021-09-06 22:45:55 +02:00
|
|
|
|
|
|
|
return string;
|
|
|
|
}
|
2020-11-24 02:59:49 +01:00
|
|
|
|
2020-11-24 18:12:08 +01:00
|
|
|
void ViewStrings::createStringContextMenu(const FoundString &foundString) {
|
2021-01-21 23:09:43 +01:00
|
|
|
if (ImGui::TableGetColumnFlags(2) == ImGuiTableColumnFlags_IsHovered && ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
2020-11-24 02:59:49 +01:00
|
|
|
ImGui::OpenPopup("StringContextMenu");
|
2021-09-06 22:45:55 +02:00
|
|
|
this->m_selectedString = readString(foundString);
|
2020-11-24 18:12:08 +01:00
|
|
|
}
|
2020-11-24 02:59:49 +01:00
|
|
|
if (ImGui::BeginPopup("StringContextMenu")) {
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::MenuItem("hex.builtin.view.strings.copy"_lang)) {
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::SetClipboardText(this->m_selectedString.c_str());
|
|
|
|
}
|
2020-11-24 02:59:49 +01:00
|
|
|
ImGui::Separator();
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::MenuItem("hex.builtin.view.strings.demangle"_lang)) {
|
2020-11-24 18:12:08 +01:00
|
|
|
this->m_demangledName = llvm::demangle(this->m_selectedString);
|
|
|
|
if (!this->m_demangledName.empty())
|
2021-12-07 22:47:41 +01:00
|
|
|
View::doLater([]{ ImGui::OpenPopup("hex.builtin.view.strings.demangle.name"_lang); });
|
2020-11-24 18:12:08 +01:00
|
|
|
}
|
2020-11-24 02:59:49 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:16:58 +01:00
|
|
|
void ViewStrings::searchStrings() {
|
|
|
|
this->m_foundStrings.clear();
|
2021-09-06 22:45:55 +02:00
|
|
|
this->m_filterIndices.clear();
|
2021-02-22 10:16:58 +01:00
|
|
|
this->m_searching = true;
|
2020-11-24 02:59:49 +01:00
|
|
|
|
2021-02-22 10:16:58 +01:00
|
|
|
std::thread([this] {
|
2021-09-21 02:29:54 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2021-12-16 23:48:52 +01:00
|
|
|
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.strings.searching", provider->getActualSize());
|
2020-11-15 01:42:43 +01:00
|
|
|
|
|
|
|
std::vector<u8> buffer(1024, 0x00);
|
|
|
|
u32 foundCharacters = 0;
|
2020-12-27 15:39:06 +01:00
|
|
|
|
2021-12-16 23:48:52 +01:00
|
|
|
for (u64 offset = 0; offset < provider->getActualSize(); offset += buffer.size()) {
|
|
|
|
task.update(offset);
|
|
|
|
|
|
|
|
size_t readSize = std::min(u64(buffer.size()), provider->getActualSize() - offset);
|
2021-12-09 21:10:24 +01:00
|
|
|
provider->read(offset + provider->getBaseAddress(), buffer.data(), readSize);
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2020-11-23 16:19:31 +01:00
|
|
|
for (u32 i = 0; i < readSize; i++) {
|
2021-12-16 23:48:52 +01:00
|
|
|
if (buffer[i] >= ' ' && buffer[i] <= '~' && offset < provider->getActualSize() - 1)
|
2020-11-15 01:42:43 +01:00
|
|
|
foundCharacters++;
|
|
|
|
else {
|
|
|
|
if (foundCharacters >= this->m_minimumLength) {
|
2021-09-06 22:45:55 +02:00
|
|
|
FoundString foundString = {
|
|
|
|
offset + i - foundCharacters + provider->getBaseAddress(),
|
|
|
|
foundCharacters
|
|
|
|
};
|
2020-11-15 01:42:43 +01:00
|
|
|
|
|
|
|
this->m_foundStrings.push_back(foundString);
|
2021-09-06 22:45:55 +02:00
|
|
|
this->m_filterIndices.push_back(this->m_foundStrings.size() - 1);
|
2020-11-15 01:42:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foundCharacters = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 10:16:58 +01:00
|
|
|
this->m_searching = false;
|
|
|
|
}).detach();
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2021-02-22 10:16:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewStrings::drawContent() {
|
2021-09-21 02:29:54 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2021-02-22 10:16:58 +01:00
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::Begin(View::toWindowName("hex.builtin.view.strings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
2021-09-21 02:29:54 +02:00
|
|
|
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
2021-02-22 10:16:58 +01:00
|
|
|
ImGui::Disabled([this]{
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::InputInt("hex.builtin.view.strings.min_length"_lang, &this->m_minimumLength, 1, 0))
|
2021-02-22 10:16:58 +01:00
|
|
|
this->m_foundStrings.clear();
|
|
|
|
|
2021-11-25 08:46:42 +01:00
|
|
|
ImGui::Checkbox("Regex", &this->m_regex);
|
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
ImGui::InputText("hex.builtin.view.strings.filter"_lang, this->m_filter.data(), this->m_filter.capacity(), ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) {
|
2021-09-06 22:45:55 +02:00
|
|
|
auto &view = *static_cast<ViewStrings*>(data->UserData);
|
|
|
|
view.m_filter.resize(data->BufTextLen);
|
|
|
|
view.m_filterIndices.clear();
|
2021-11-25 08:46:42 +01:00
|
|
|
std::regex pattern;
|
|
|
|
if (view.m_regex) {
|
|
|
|
try {
|
|
|
|
pattern = std::regex(data->Buf);
|
|
|
|
view.m_pattern_parsed = true;
|
|
|
|
} catch (std::regex_error &e) {
|
|
|
|
view.m_pattern_parsed = false;
|
|
|
|
}
|
|
|
|
}
|
2021-09-06 22:45:55 +02:00
|
|
|
for (u64 i = 0; i < view.m_foundStrings.size(); i++) {
|
2021-11-30 21:02:37 +01:00
|
|
|
if (view.m_regex) {
|
2021-11-25 08:46:42 +01:00
|
|
|
if(view.m_pattern_parsed && std::regex_search(readString(view.m_foundStrings[i]), pattern))
|
|
|
|
view.m_filterIndices.push_back(i);
|
|
|
|
}
|
2021-11-30 21:02:37 +01:00
|
|
|
else if (readString(view.m_foundStrings[i]).find(data->Buf) != std::string::npos) {
|
2021-09-06 22:45:55 +02:00
|
|
|
view.m_filterIndices.push_back(i);
|
2021-11-25 08:46:42 +01:00
|
|
|
}
|
2021-09-06 22:45:55 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}, this);
|
2021-11-30 21:02:37 +01:00
|
|
|
if (this->m_regex && !this->m_pattern_parsed) {
|
2022-01-15 14:14:53 +01:00
|
|
|
ImGui::TextFormattedColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "{}", "hex.builtin.view.strings.regex_error"_lang);
|
2021-11-25 08:46:42 +01:00
|
|
|
}
|
2021-09-06 22:45:55 +02:00
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::Button("hex.builtin.view.strings.extract"_lang))
|
2021-02-22 10:16:58 +01:00
|
|
|
this->searchStrings();
|
|
|
|
}, this->m_searching);
|
|
|
|
|
|
|
|
if (this->m_searching) {
|
|
|
|
ImGui::SameLine();
|
2021-12-07 22:47:41 +01:00
|
|
|
ImGui::TextSpinner("hex.builtin.view.strings.searching"_lang);
|
2021-02-22 10:16:58 +01:00
|
|
|
}
|
2021-11-30 21:02:37 +01:00
|
|
|
else if (this->m_foundStrings.size() > 0) {
|
|
|
|
ImGui::SameLine();
|
2021-12-31 01:10:06 +01:00
|
|
|
ImGui::TextFormatted("hex.builtin.view.strings.results"_lang, this->m_filterIndices.size());
|
2021-11-30 21:02:37 +01:00
|
|
|
}
|
2020-11-15 03:51:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
if (ImGui::BeginTable("##strings", 3,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
2020-11-23 15:22:26 +01:00
|
|
|
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
|
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
2021-12-07 22:47:41 +01:00
|
|
|
ImGui::TableSetupColumn("hex.builtin.view.strings.offset"_lang, 0, -1, ImGui::GetID("offset"));
|
|
|
|
ImGui::TableSetupColumn("hex.builtin.view.strings.size"_lang, 0, -1, ImGui::GetID("size"));
|
|
|
|
ImGui::TableSetupColumn("hex.builtin.view.strings.string"_lang, 0, -1, ImGui::GetID("string"));
|
2020-11-15 03:51:59 +01:00
|
|
|
|
|
|
|
auto sortSpecs = ImGui::TableGetSortSpecs();
|
|
|
|
|
|
|
|
if (sortSpecs->SpecsDirty) {
|
|
|
|
std::sort(this->m_foundStrings.begin(), this->m_foundStrings.end(),
|
2020-11-15 16:06:10 +01:00
|
|
|
[&sortSpecs](FoundString &left, FoundString &right) -> bool {
|
2021-02-17 14:47:25 +01:00
|
|
|
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
2020-11-15 03:51:59 +01:00
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
|
|
|
return left.offset > right.offset;
|
|
|
|
else
|
|
|
|
return left.offset < right.offset;
|
2021-02-17 14:47:25 +01:00
|
|
|
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
2020-11-15 03:51:59 +01:00
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
|
|
|
return left.size > right.size;
|
|
|
|
else
|
|
|
|
return left.size < right.size;
|
2021-02-17 14:47:25 +01:00
|
|
|
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("string")) {
|
2020-11-15 03:51:59 +01:00
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
2021-09-06 22:45:55 +02:00
|
|
|
return readString(left) > readString(right);
|
2020-11-15 03:51:59 +01:00
|
|
|
else
|
2021-09-06 22:45:55 +02:00
|
|
|
return readString(left) < readString(right);
|
2020-11-15 03:51:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
sortSpecs->SpecsDirty = false;
|
|
|
|
}
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2020-11-15 03:51:59 +01:00
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
|
|
|
|
ImGuiListClipper clipper;
|
2021-09-06 22:45:55 +02:00
|
|
|
clipper.Begin(this->m_filterIndices.size());
|
2020-11-20 11:56:37 +01:00
|
|
|
|
|
|
|
while (clipper.Step()) {
|
|
|
|
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
2021-09-06 22:45:55 +02:00
|
|
|
auto &foundString = this->m_foundStrings[this->m_filterIndices[i]];
|
|
|
|
auto string = readString(foundString);
|
2020-11-20 11:56:37 +01:00
|
|
|
|
2020-11-23 13:08:24 +01:00
|
|
|
ImGui::TableNextRow();
|
2020-11-20 11:56:37 +01:00
|
|
|
ImGui::TableNextColumn();
|
2020-11-23 13:10:14 +01:00
|
|
|
if (ImGui::Selectable(("##StringLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
2021-03-27 11:36:36 +01:00
|
|
|
EventManager::post<RequestSelectionChange>(Region { foundString.offset, foundString.size });
|
2020-11-23 13:10:14 +01:00
|
|
|
}
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::PushID(i + 1);
|
|
|
|
createStringContextMenu(foundString);
|
|
|
|
ImGui::PopID();
|
2020-11-23 13:10:14 +01:00
|
|
|
ImGui::SameLine();
|
2021-12-31 01:10:06 +01:00
|
|
|
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", foundString.offset, foundString.offset + foundString.size);
|
2020-11-20 11:56:37 +01:00
|
|
|
ImGui::TableNextColumn();
|
2021-12-31 01:10:06 +01:00
|
|
|
ImGui::TextFormatted("0x{0:04X}", foundString.size);
|
2020-11-20 11:56:37 +01:00
|
|
|
ImGui::TableNextColumn();
|
2021-09-06 22:45:55 +02:00
|
|
|
|
2021-12-31 01:10:06 +01:00
|
|
|
ImGui::TextUnformatted(string.c_str());
|
2020-11-20 11:56:37 +01:00
|
|
|
}
|
2020-11-15 03:51:59 +01:00
|
|
|
}
|
|
|
|
clipper.End();
|
2020-11-15 01:42:43 +01:00
|
|
|
|
2020-11-15 03:51:59 +01:00
|
|
|
ImGui::EndTable();
|
2020-11-15 01:42:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
2020-11-24 18:12:08 +01:00
|
|
|
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::BeginPopup("hex.builtin.view.strings.demangle.title"_lang)) {
|
2020-11-24 18:12:08 +01:00
|
|
|
if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
|
2021-12-07 22:47:41 +01:00
|
|
|
ImGui::TextUnformatted("hex.builtin.view.strings.demangle.title"_lang);
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::Separator();
|
2022-01-15 14:14:53 +01:00
|
|
|
ImGui::TextFormattedWrapped("{}", this->m_demangledName.c_str());
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::NewLine();
|
2021-12-07 22:47:41 +01:00
|
|
|
if (ImGui::Button("hex.builtin.view.strings.demangle.copy"_lang))
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::SetClipboardText(this->m_demangledName.c_str());
|
2021-08-28 14:22:02 +02:00
|
|
|
|
2020-11-24 18:12:08 +01:00
|
|
|
}
|
2021-08-28 16:02:53 +02:00
|
|
|
ImGui::EndChild();
|
2020-11-24 18:12:08 +01:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
2020-11-15 01:42:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|