1 #include "views/view_strings.hpp"
2 
3 #include <hex/providers/provider.hpp>
4 #include <hex/helpers/utils.hpp>
5 
6 #include <cstring>
7 
8 #include <llvm/Demangle/Demangle.h>
9 
10 using namespace std::literals::string_literals;
11 
12 namespace hex {
13 
ViewStrings()14     ViewStrings::ViewStrings() : View("hex.view.strings.title"_lang) {
15         View::subscribeEvent(Events::DataChanged, [this](auto){
16             this->m_foundStrings.clear();
17         });
18 
19         this->m_filter.resize(0xFFFF, 0x00);
20     }
21 
~ViewStrings()22     ViewStrings::~ViewStrings() {
23         View::unsubscribeEvent(Events::DataChanged);
24     }
25 
26 
createStringContextMenu(const FoundString & foundString)27     void ViewStrings::createStringContextMenu(const FoundString &foundString) {
28         if (ImGui::TableGetColumnFlags(2) == ImGuiTableColumnFlags_IsHovered && ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
29             ImGui::OpenPopup("StringContextMenu");
30             this->m_selectedString = foundString.string;
31         }
32         if (ImGui::BeginPopup("StringContextMenu")) {
33             if (ImGui::MenuItem("hex.view.strings.copy"_lang)) {
34                 ImGui::SetClipboardText(this->m_selectedString.c_str());
35             }
36             ImGui::Separator();
37             if (ImGui::MenuItem("hex.view.strings.demangle"_lang)) {
38                 this->m_demangledName = llvm::demangle(this->m_selectedString);
39                 if (!this->m_demangledName.empty())
40                     View::doLater([]{ ImGui::OpenPopup("hex.view.strings.demangle.title"_lang); });
41             }
42             ImGui::EndPopup();
43         }
44     }
45 
46 
drawContent()47     void ViewStrings::drawContent() {
48         auto provider = SharedData::currentProvider;
49 
50         if (this->m_shouldInvalidate) {
51             this->m_shouldInvalidate = false;
52 
53             this->m_foundStrings.clear();
54 
55             std::vector<u8> buffer(1024, 0x00);
56             u32 foundCharacters = 0;
57 
58             for (u64 offset = 0; offset < provider->getSize(); offset += buffer.size()) {
59                 size_t readSize = std::min(u64(buffer.size()), provider->getSize() - offset);
60                 provider->read(offset,  buffer.data(), readSize);
61 
62                 for (u32 i = 0; i < readSize; i++) {
63                     if (buffer[i] >= 0x20 && buffer[i] <= 0x7E)
64                         foundCharacters++;
65                     else {
66                         if (foundCharacters >= this->m_minimumLength) {
67                             FoundString foundString;
68 
69                             foundString.offset = offset + i - foundCharacters;
70                             foundString.size = foundCharacters;
71                             foundString.string.reserve(foundCharacters);
72                             foundString.string.resize(foundCharacters);
73                             provider->read(foundString.offset, foundString.string.data(), foundCharacters);
74 
75                             this->m_foundStrings.push_back(foundString);
76                         }
77 
78                         foundCharacters = 0;
79                     }
80                 }
81             }
82         }
83 
84 
85         if (ImGui::Begin("Strings", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
86             if (provider != nullptr && provider->isReadable()) {
87                 if (ImGui::InputInt("hex.view.strings.min_length"_lang, &this->m_minimumLength, 1, 0))
88                     this->m_shouldInvalidate = true;
89 
90                 ImGui::InputText("hex.view.strings.filter"_lang, this->m_filter.data(), this->m_filter.size());
91                 if (ImGui::Button("hex.view.strings.extract"_lang))
92                     this->m_shouldInvalidate = true;
93 
94                 ImGui::Separator();
95                 ImGui::NewLine();
96 
97                 if (ImGui::BeginTable("##strings", 3,
98                                       ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
99                                       ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
100                     ImGui::TableSetupScrollFreeze(0, 1);
101                     ImGui::TableSetupColumn("hex.view.strings.offset"_lang, 0, -1, ImGui::GetID("offset"));
102                     ImGui::TableSetupColumn("hex.view.strings.size"_lang, 0, -1, ImGui::GetID("size"));
103                     ImGui::TableSetupColumn("hex.view.strings.string"_lang, 0, -1, ImGui::GetID("string"));
104 
105                     auto sortSpecs = ImGui::TableGetSortSpecs();
106 
107                     if (sortSpecs->SpecsDirty) {
108                         std::sort(this->m_foundStrings.begin(), this->m_foundStrings.end(),
109                                   [&sortSpecs](FoundString &left, FoundString &right) -> bool {
110                                       if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
111                                           if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
112                                               return left.offset > right.offset;
113                                           else
114                                               return left.offset < right.offset;
115                                       } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
116                                           if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
117                                               return left.size > right.size;
118                                           else
119                                               return left.size < right.size;
120                                       } else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("string")) {
121                                           if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
122                                               return left.string > right.string;
123                                           else
124                                               return left.string < right.string;
125                                       }
126 
127                                       return false;
128                                   });
129 
130                         sortSpecs->SpecsDirty = false;
131                     }
132 
133                     ImGui::TableHeadersRow();
134 
135                     ImGuiListClipper clipper;
136                     clipper.Begin(this->m_foundStrings.size());
137 
138                     while (clipper.Step()) {
139                         for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
140                             auto &foundString = this->m_foundStrings[i];
141 
142                             if (strlen(this->m_filter.data()) != 0 &&
143                                 foundString.string.find(this->m_filter.data()) == std::string::npos)
144                                 continue;
145 
146                             ImGui::TableNextRow();
147                             ImGui::TableNextColumn();
148                             if (ImGui::Selectable(("##StringLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
149                                 Region selectRegion = { foundString.offset, foundString.size };
150                                 View::postEvent(Events::SelectionChangeRequest, selectRegion);
151                             }
152                             ImGui::PushID(i + 1);
153                             createStringContextMenu(foundString);
154                             ImGui::PopID();
155                             ImGui::SameLine();
156                             ImGui::Text("0x%08lx : 0x%08lx", foundString.offset, foundString.offset + foundString.size);
157                             ImGui::TableNextColumn();
158                             ImGui::Text("0x%04lx", foundString.size);
159                             ImGui::TableNextColumn();
160                             ImGui::Text("%s", foundString.string.c_str());
161                         }
162                     }
163                     clipper.End();
164 
165                     ImGui::EndTable();
166                 }
167             }
168         }
169         ImGui::End();
170 
171         if (ImGui::BeginPopup("hex.view.strings.demangle.title"_lang)) {
172             if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
173                 ImGui::TextUnformatted("hex.view.strings.demangle.title"_lang);
174                 ImGui::Separator();
175                 ImGui::TextWrapped("%s", this->m_demangledName.c_str());
176                 ImGui::EndChild();
177                 ImGui::NewLine();
178                 if (ImGui::Button("hex.view.strings.demangle.copy"_lang))
179                     ImGui::SetClipboardText(this->m_demangledName.c_str());
180             }
181             ImGui::EndPopup();
182         }
183     }
184 
drawMenu()185     void ViewStrings::drawMenu() {
186 
187     }
188 
189 }