2022-07-29 13:59:57 +02:00
|
|
|
#include "content/views/view_find.hpp"
|
|
|
|
|
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-08-06 21:33:15 +02:00
|
|
|
#include <hex/api/achievement_manager.hpp>
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
#include <hex/providers/buffered_reader.hpp>
|
|
|
|
|
2024-01-28 22:14:59 +01:00
|
|
|
#include <fonts/codicons_font.h>
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
#include <array>
|
2023-12-19 14:34:35 +01:00
|
|
|
#include <ranges>
|
2022-07-29 13:59:57 +02:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
|
2024-07-11 20:38:22 +02:00
|
|
|
#include <content/helpers/demangle.hpp>
|
2024-06-07 23:12:18 +02:00
|
|
|
#include <boost/regex.hpp>
|
2022-07-29 13:59:57 +02:00
|
|
|
|
|
|
|
namespace hex::plugin::builtin {
|
|
|
|
|
2024-01-08 21:51:48 +01:00
|
|
|
ViewFind::ViewFind() : View::Window("hex.builtin.view.find.name", ICON_VS_SEARCH) {
|
2023-11-16 22:24:06 +01:00
|
|
|
const static auto HighlightColor = [] { return (ImGuiExt::GetCustomColorU32(ImGuiCustomCol_FindHighlight) & 0x00FFFFFF) | 0x70000000; };
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2022-09-28 15:01:43 +02:00
|
|
|
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size, bool) -> std::optional<color_t> {
|
2022-07-29 13:59:57 +02:00
|
|
|
hex::unused(data, size);
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_searchTask.isRunning())
|
2022-09-19 10:34:57 +02:00
|
|
|
return { };
|
|
|
|
|
2023-12-19 14:34:35 +01:00
|
|
|
if (!m_occurrenceTree->overlapping({ address, address }).empty())
|
2022-07-29 13:59:57 +02:00
|
|
|
return HighlightColor();
|
|
|
|
else
|
|
|
|
return std::nullopt;
|
|
|
|
});
|
|
|
|
|
|
|
|
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8* data, size_t size) {
|
|
|
|
hex::unused(data, size);
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_searchTask.isRunning())
|
2022-09-19 10:34:57 +02:00
|
|
|
return;
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
auto occurrences = m_occurrenceTree->overlapping({ address, address + size });
|
2023-05-11 09:27:23 +02:00
|
|
|
if (occurrences.empty())
|
2022-07-29 13:59:57 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
ImGui::BeginTooltip();
|
|
|
|
|
2022-08-06 12:57:47 +02:00
|
|
|
for (const auto &occurrence : occurrences) {
|
|
|
|
ImGui::PushID(&occurrence);
|
|
|
|
if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
|
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2022-08-06 12:57:47 +02:00
|
|
|
{
|
2023-05-11 09:27:23 +02:00
|
|
|
auto region = occurrence.value.region;
|
|
|
|
const auto value = this->decodeValue(ImHexApi::Provider::get(), occurrence.value, 256);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2022-08-06 12:57:47 +02:00
|
|
|
ImGui::ColorButton("##color", ImColor(HighlightColor()));
|
|
|
|
ImGui::SameLine(0, 10);
|
2023-11-16 22:24:06 +01:00
|
|
|
ImGuiExt::TextFormatted("{} ", value);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2022-08-06 12:57:47 +02:00
|
|
|
if (ImGui::GetIO().KeyShift) {
|
|
|
|
ImGui::Indent();
|
|
|
|
if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
|
2022-07-29 13:59:57 +02:00
|
|
|
|
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2023-12-23 21:09:41 +01:00
|
|
|
ImGuiExt::TextFormatted("{}: ", "hex.ui.common.region"_lang);
|
2022-07-29 13:59:57 +02:00
|
|
|
ImGui::TableNextColumn();
|
2023-11-16 22:24:06 +01:00
|
|
|
ImGuiExt::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", region.getStartAddress(), region.getEndAddress());
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2024-07-11 20:38:22 +02:00
|
|
|
auto demangledValue = hex::plugin::builtin::demangle(value);
|
2022-08-06 12:57:47 +02:00
|
|
|
|
|
|
|
if (value != demangledValue) {
|
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2023-11-16 22:24:06 +01:00
|
|
|
ImGuiExt::TextFormatted("{}: ", "hex.builtin.view.find.demangled"_lang);
|
2022-08-06 12:57:47 +02:00
|
|
|
ImGui::TableNextColumn();
|
2023-11-16 22:24:06 +01:00
|
|
|
ImGuiExt::TextFormatted("{}", demangledValue);
|
2022-08-06 12:57:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
ImGui::Unindent();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-06 12:57:47 +02:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_TableRowBg, HighlightColor());
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, HighlightColor());
|
|
|
|
ImGui::EndTable();
|
|
|
|
ImGui::PopStyleColor(2);
|
|
|
|
}
|
|
|
|
ImGui::PopID();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndTooltip();
|
|
|
|
});
|
2023-05-28 11:50:10 +02:00
|
|
|
|
2023-11-17 14:46:21 +01:00
|
|
|
ShortcutManager::addShortcut(this, CTRLCMD + Keys::A, "hex.builtin.view.find.shortcut.select_all", [this] {
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_filterTask.isRunning())
|
2023-05-28 11:50:10 +02:00
|
|
|
return;
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_searchTask.isRunning())
|
2023-05-28 11:50:10 +02:00
|
|
|
return;
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
for (auto &occurrence : *m_sortedOccurrences)
|
2023-05-28 11:50:10 +02:00
|
|
|
occurrence.selected = true;
|
|
|
|
});
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
2022-09-19 10:34:57 +02:00
|
|
|
template<typename Type, typename StorageType>
|
|
|
|
static std::tuple<bool, std::variant<u64, i64, float, double>, size_t> parseNumericValue(const std::string &string) {
|
|
|
|
static_assert(sizeof(StorageType) >= sizeof(Type));
|
|
|
|
|
2023-01-21 18:44:12 +01:00
|
|
|
StorageType value;
|
|
|
|
|
|
|
|
std::size_t processed = 0;
|
|
|
|
try {
|
|
|
|
if constexpr (std::floating_point<Type>)
|
|
|
|
value = std::stod(string, &processed);
|
|
|
|
else if constexpr (std::signed_integral<Type>)
|
|
|
|
value = std::stoll(string, &processed, 0);
|
|
|
|
else
|
|
|
|
value = std::stoull(string, &processed, 0);
|
|
|
|
} catch (std::exception &) {
|
|
|
|
return { false, { }, 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (processed != string.size())
|
2022-09-19 10:34:57 +02:00
|
|
|
return { false, { }, 0 };
|
|
|
|
|
2022-12-18 14:17:57 +01:00
|
|
|
if (value < std::numeric_limits<Type>::lowest() || value > std::numeric_limits<Type>::max())
|
2022-09-19 10:34:57 +02:00
|
|
|
return { false, { }, 0 };
|
|
|
|
|
|
|
|
return { true, value, sizeof(Type) };
|
|
|
|
}
|
|
|
|
|
|
|
|
std::tuple<bool, std::variant<u64, i64, float, double>, size_t> ViewFind::parseNumericValueInput(const std::string &input, SearchSettings::Value::Type type) {
|
|
|
|
switch (type) {
|
|
|
|
using enum SearchSettings::Value::Type;
|
|
|
|
|
|
|
|
case U8: return parseNumericValue<u8, u64>(input);
|
|
|
|
case U16: return parseNumericValue<u16, u64>(input);
|
|
|
|
case U32: return parseNumericValue<u32, u64>(input);
|
|
|
|
case U64: return parseNumericValue<u64, u64>(input);
|
|
|
|
case I8: return parseNumericValue<i8, i64>(input);
|
|
|
|
case I16: return parseNumericValue<i16, i64>(input);
|
|
|
|
case I32: return parseNumericValue<i32, i64>(input);
|
|
|
|
case I64: return parseNumericValue<i64, i64>(input);
|
|
|
|
case F32: return parseNumericValue<float, float>(input);
|
|
|
|
case F64: return parseNumericValue<double, double>(input);
|
|
|
|
default: return { false, { }, 0 };
|
|
|
|
}
|
2022-10-02 17:30:26 +02:00
|
|
|
}
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2022-09-19 10:34:57 +02:00
|
|
|
template<typename T>
|
2023-11-25 19:40:29 +01:00
|
|
|
static std::string formatBytes(const std::vector<u8> &bytes, std::endian endian) {
|
2022-09-19 10:34:57 +02:00
|
|
|
if (bytes.size() > sizeof(T))
|
|
|
|
return { };
|
|
|
|
|
|
|
|
T value = 0x00;
|
|
|
|
std::memcpy(&value, bytes.data(), bytes.size());
|
|
|
|
|
2024-03-28 22:25:28 +01:00
|
|
|
value = hex::changeEndianness(value, bytes.size(), endian);
|
2023-11-25 19:40:29 +01:00
|
|
|
|
2022-09-19 10:34:57 +02:00
|
|
|
if (std::signed_integral<T>)
|
2023-05-28 14:30:33 +02:00
|
|
|
value = hex::signExtend(bytes.size() * 8, value);
|
2022-09-19 10:34:57 +02:00
|
|
|
|
|
|
|
return hex::format("{}", value);
|
|
|
|
}
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
std::vector<hex::ContentRegistry::DataFormatter::impl::FindOccurrence> ViewFind::searchStrings(Task &task, prv::Provider *provider, hex::Region searchRegion, const SearchSettings::Strings &settings) {
|
2023-03-17 08:16:13 +01:00
|
|
|
using enum SearchSettings::StringType;
|
2022-08-03 11:38:36 +02:00
|
|
|
|
|
|
|
std::vector<Occurrence> results;
|
|
|
|
|
|
|
|
if (settings.type == ASCII_UTF16BE || settings.type == ASCII_UTF16LE) {
|
|
|
|
auto newSettings = settings;
|
|
|
|
|
|
|
|
newSettings.type = ASCII;
|
|
|
|
auto asciiResults = searchStrings(task, provider, searchRegion, newSettings);
|
|
|
|
std::copy(asciiResults.begin(), asciiResults.end(), std::back_inserter(results));
|
|
|
|
|
|
|
|
if (settings.type == ASCII_UTF16BE) {
|
|
|
|
newSettings.type = UTF16BE;
|
|
|
|
auto utf16Results = searchStrings(task, provider, searchRegion, newSettings);
|
|
|
|
std::copy(utf16Results.begin(), utf16Results.end(), std::back_inserter(results));
|
|
|
|
} else if (settings.type == ASCII_UTF16LE) {
|
|
|
|
newSettings.type = UTF16LE;
|
|
|
|
auto utf16Results = searchStrings(task, provider, searchRegion, newSettings);
|
|
|
|
std::copy(utf16Results.begin(), utf16Results.end(), std::back_inserter(results));
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-03-12 18:27:29 +01:00
|
|
|
auto reader = prv::ProviderReader(provider);
|
2022-07-29 13:59:57 +02:00
|
|
|
reader.seek(searchRegion.getStartAddress());
|
|
|
|
reader.setEndAddress(searchRegion.getEndAddress());
|
|
|
|
|
2022-09-19 10:34:57 +02:00
|
|
|
const auto [decodeType, endian] = [&] -> std::pair<Occurrence::DecodeType, std::endian> {
|
2022-08-03 11:38:36 +02:00
|
|
|
if (settings.type == ASCII)
|
2022-09-19 10:34:57 +02:00
|
|
|
return { Occurrence::DecodeType::ASCII, std::endian::native };
|
2023-03-17 08:16:13 +01:00
|
|
|
else if (settings.type == SearchSettings::StringType::UTF16BE)
|
2022-09-19 10:34:57 +02:00
|
|
|
return { Occurrence::DecodeType::UTF16, std::endian::big };
|
2023-03-17 08:16:13 +01:00
|
|
|
else if (settings.type == SearchSettings::StringType::UTF16LE)
|
2022-09-19 10:34:57 +02:00
|
|
|
return { Occurrence::DecodeType::UTF16, std::endian::little };
|
2022-08-03 11:38:36 +02:00
|
|
|
else
|
2022-09-19 10:34:57 +02:00
|
|
|
return { Occurrence::DecodeType::Binary, std::endian::native };
|
2022-08-03 11:38:36 +02:00
|
|
|
}();
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
size_t countedCharacters = 0;
|
|
|
|
u64 startAddress = reader.begin().getAddress();
|
2023-03-11 14:39:50 +01:00
|
|
|
u64 endAddress = reader.end().getAddress();
|
2023-03-21 15:37:49 +01:00
|
|
|
|
|
|
|
u64 progress = 0;
|
2022-07-29 13:59:57 +02:00
|
|
|
for (u8 byte : reader) {
|
|
|
|
bool validChar =
|
2023-03-17 08:16:13 +01:00
|
|
|
(settings.lowerCaseLetters && std::islower(byte)) ||
|
|
|
|
(settings.upperCaseLetters && std::isupper(byte)) ||
|
|
|
|
(settings.numbers && std::isdigit(byte)) ||
|
|
|
|
(settings.spaces && std::isspace(byte) && byte != '\r' && byte != '\n') ||
|
|
|
|
(settings.underscores && byte == '_') ||
|
|
|
|
(settings.symbols && std::ispunct(byte) && !std::isspace(byte)) ||
|
|
|
|
(settings.lineFeeds && (byte == '\r' || byte == '\n'));
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2022-08-03 11:38:36 +02:00
|
|
|
if (settings.type == UTF16LE) {
|
2022-07-29 13:59:57 +02:00
|
|
|
// Check if second byte of UTF-16 encoded string is 0x00
|
|
|
|
if (countedCharacters % 2 == 1)
|
2023-03-31 22:20:00 +02:00
|
|
|
validChar = byte == 0x00;
|
2022-08-03 11:38:36 +02:00
|
|
|
} else if (settings.type == UTF16BE) {
|
2022-07-29 13:59:57 +02:00
|
|
|
// Check if first byte of UTF-16 encoded string is 0x00
|
|
|
|
if (countedCharacters % 2 == 0)
|
2023-03-31 22:20:00 +02:00
|
|
|
validChar = byte == 0x00;
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:37:49 +01:00
|
|
|
task.update(progress);
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
if (validChar)
|
|
|
|
countedCharacters++;
|
2023-03-11 14:39:50 +01:00
|
|
|
if (!validChar || startAddress + countedCharacters == endAddress) {
|
2022-07-29 13:59:57 +02:00
|
|
|
if (countedCharacters >= size_t(settings.minLength)) {
|
2023-05-28 11:50:10 +02:00
|
|
|
if (!settings.nullTermination || byte == 0x00) {
|
|
|
|
results.push_back(Occurrence { Region { startAddress, countedCharacters }, decodeType, endian, false });
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
startAddress += countedCharacters + 1;
|
|
|
|
countedCharacters = 0;
|
2023-03-21 15:37:49 +01:00
|
|
|
progress = startAddress - searchRegion.getStartAddress();
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
std::vector<hex::ContentRegistry::DataFormatter::impl::FindOccurrence> ViewFind::searchSequence(Task &task, prv::Provider *provider, hex::Region searchRegion, const SearchSettings::Sequence &settings) {
|
2022-08-03 11:38:36 +02:00
|
|
|
std::vector<Occurrence> results;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-03-12 18:27:29 +01:00
|
|
|
auto reader = prv::ProviderReader(provider);
|
2022-07-29 13:59:57 +02:00
|
|
|
reader.seek(searchRegion.getStartAddress());
|
|
|
|
reader.setEndAddress(searchRegion.getEndAddress());
|
|
|
|
|
2023-12-19 14:34:35 +01:00
|
|
|
auto input = hex::decodeByteString(settings.sequence);
|
|
|
|
if (input.empty())
|
2022-09-13 14:05:48 +02:00
|
|
|
return { };
|
|
|
|
|
2023-12-19 14:34:35 +01:00
|
|
|
std::vector<u8> bytes;
|
2023-12-27 16:33:49 +01:00
|
|
|
auto decodeType = Occurrence::DecodeType::Binary;
|
2023-12-19 14:34:35 +01:00
|
|
|
std::endian endian;
|
|
|
|
switch (settings.type) {
|
|
|
|
default:
|
|
|
|
case SearchSettings::StringType::ASCII:
|
|
|
|
bytes = input;
|
|
|
|
decodeType = Occurrence::DecodeType::ASCII;
|
|
|
|
endian = std::endian::native;
|
|
|
|
break;
|
|
|
|
case SearchSettings::StringType::UTF16LE: {
|
|
|
|
auto wString = hex::utf8ToUtf16({ input.begin(), input.end() });
|
|
|
|
|
|
|
|
bytes.resize(wString.size() * 2);
|
|
|
|
std::memcpy(bytes.data(), wString.data(), bytes.size());
|
|
|
|
decodeType = Occurrence::DecodeType::UTF16;
|
|
|
|
endian = std::endian::little;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SearchSettings::StringType::UTF16BE: {
|
|
|
|
auto wString = hex::utf8ToUtf16({ input.begin(), input.end() });
|
|
|
|
|
|
|
|
bytes.resize(wString.size() * 2);
|
|
|
|
std::memcpy(bytes.data(), wString.data(), bytes.size());
|
|
|
|
decodeType = Occurrence::DecodeType::UTF16;
|
|
|
|
endian = std::endian::big;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < bytes.size(); i += 2)
|
|
|
|
std::swap(bytes[i], bytes[i + 1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
auto occurrence = reader.begin();
|
2023-03-21 15:37:49 +01:00
|
|
|
u64 progress = 0;
|
2023-12-19 14:34:35 +01:00
|
|
|
|
|
|
|
auto searchPredicate = [&] -> bool(*)(u8, u8) {
|
|
|
|
if (!settings.ignoreCase)
|
|
|
|
return [](u8 left, u8 right) -> bool {
|
|
|
|
return left == right;
|
|
|
|
};
|
|
|
|
else
|
|
|
|
return [](u8 left, u8 right) -> bool {
|
|
|
|
if (std::isupper(left))
|
|
|
|
left = std::tolower(left);
|
|
|
|
if (std::isupper(right))
|
|
|
|
right = std::tolower(right);
|
|
|
|
|
|
|
|
return left == right;
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
while (true) {
|
2023-03-21 15:37:49 +01:00
|
|
|
task.update(progress);
|
|
|
|
|
2023-12-19 14:34:35 +01:00
|
|
|
occurrence = std::search(reader.begin(), reader.end(), std::default_searcher(bytes.begin(), bytes.end(), searchPredicate));
|
2022-07-29 13:59:57 +02:00
|
|
|
if (occurrence == reader.end())
|
|
|
|
break;
|
|
|
|
|
2022-07-29 18:49:43 +02:00
|
|
|
auto address = occurrence.getAddress();
|
2022-08-06 12:57:47 +02:00
|
|
|
reader.seek(address + 1);
|
2023-12-19 14:34:35 +01:00
|
|
|
results.push_back(Occurrence{ Region { address, bytes.size() }, decodeType, endian, false });
|
2023-03-21 15:37:49 +01:00
|
|
|
progress = address - searchRegion.getStartAddress();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
std::vector<hex::ContentRegistry::DataFormatter::impl::FindOccurrence> ViewFind::searchRegex(Task &task, prv::Provider *provider, hex::Region searchRegion, const SearchSettings::Regex &settings) {
|
2022-08-03 11:38:36 +02:00
|
|
|
auto stringOccurrences = searchStrings(task, provider, searchRegion, SearchSettings::Strings {
|
2023-03-17 08:16:13 +01:00
|
|
|
.minLength = settings.minLength,
|
|
|
|
.nullTermination = settings.nullTermination,
|
|
|
|
.type = settings.type,
|
|
|
|
.lowerCaseLetters = true,
|
|
|
|
.upperCaseLetters = true,
|
|
|
|
.numbers = true,
|
|
|
|
.underscores = true,
|
|
|
|
.symbols = true,
|
|
|
|
.spaces = true,
|
|
|
|
.lineFeeds = true
|
2022-07-29 13:59:57 +02:00
|
|
|
});
|
|
|
|
|
2022-08-03 11:38:36 +02:00
|
|
|
std::vector<Occurrence> result;
|
2024-06-07 23:12:18 +02:00
|
|
|
boost::regex regex(settings.pattern);
|
2022-08-03 11:38:36 +02:00
|
|
|
for (const auto &occurrence : stringOccurrences) {
|
|
|
|
std::string string(occurrence.region.getSize(), '\x00');
|
|
|
|
provider->read(occurrence.region.getStartAddress(), string.data(), occurrence.region.getSize());
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-03-21 15:37:49 +01:00
|
|
|
task.update();
|
|
|
|
|
2022-09-13 14:06:08 +02:00
|
|
|
if (settings.fullMatch) {
|
2024-06-07 23:12:18 +02:00
|
|
|
if (boost::regex_match(string, regex))
|
2022-09-13 14:06:08 +02:00
|
|
|
result.push_back(occurrence);
|
|
|
|
} else {
|
2024-06-07 23:12:18 +02:00
|
|
|
if (boost::regex_search(string, regex))
|
2022-09-13 14:06:08 +02:00
|
|
|
result.push_back(occurrence);
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
std::vector<hex::ContentRegistry::DataFormatter::impl::FindOccurrence> ViewFind::searchBinaryPattern(Task &task, prv::Provider *provider, hex::Region searchRegion, const SearchSettings::BinaryPattern &settings) {
|
2022-08-03 11:38:36 +02:00
|
|
|
std::vector<Occurrence> results;
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2023-03-12 18:27:29 +01:00
|
|
|
auto reader = prv::ProviderReader(provider);
|
2022-08-03 10:19:34 +02:00
|
|
|
reader.seek(searchRegion.getStartAddress());
|
|
|
|
reader.setEndAddress(searchRegion.getEndAddress());
|
|
|
|
|
2023-05-22 00:01:58 +02:00
|
|
|
const size_t patternSize = settings.pattern.getSize();
|
2022-08-06 12:57:47 +02:00
|
|
|
|
2023-04-09 15:28:31 +02:00
|
|
|
if (settings.alignment == 1) {
|
2023-04-08 20:59:33 +02:00
|
|
|
u32 matchedBytes = 0;
|
|
|
|
for (auto it = reader.begin(); it < reader.end(); it += 1) {
|
|
|
|
auto byte = *it;
|
2022-08-06 12:57:47 +02:00
|
|
|
|
2023-04-08 20:59:33 +02:00
|
|
|
task.update(it.getAddress());
|
2023-05-22 00:01:58 +02:00
|
|
|
if (settings.pattern.matchesByte(byte, matchedBytes)) {
|
2023-04-08 20:59:33 +02:00
|
|
|
matchedBytes++;
|
2023-05-22 00:01:58 +02:00
|
|
|
if (matchedBytes == settings.pattern.getSize()) {
|
2023-04-08 20:59:33 +02:00
|
|
|
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
|
|
|
|
|
2023-05-28 11:50:10 +02:00
|
|
|
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false });
|
2023-04-08 20:59:33 +02:00
|
|
|
it.setAddress(occurrenceAddress);
|
|
|
|
matchedBytes = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (matchedBytes > 0)
|
|
|
|
it -= matchedBytes;
|
2022-08-03 10:19:34 +02:00
|
|
|
matchedBytes = 0;
|
|
|
|
}
|
2023-04-08 20:59:33 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
std::vector<u8> data(patternSize);
|
2023-04-09 15:28:31 +02:00
|
|
|
for (u64 address = searchRegion.getStartAddress(); address < searchRegion.getEndAddress(); address += settings.alignment) {
|
2023-04-08 20:59:33 +02:00
|
|
|
reader.read(address, data.data(), data.size());
|
|
|
|
|
|
|
|
task.update(address);
|
|
|
|
|
|
|
|
bool match = true;
|
|
|
|
for (u32 i = 0; i < patternSize; i++) {
|
2023-10-22 20:59:32 +02:00
|
|
|
if (!settings.pattern.matchesByte(data[i], i)) {
|
2023-04-08 20:59:33 +02:00
|
|
|
match = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match)
|
2023-05-28 11:50:10 +02:00
|
|
|
results.push_back(Occurrence { Region { address, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false });
|
2022-08-03 10:19:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
std::vector<hex::ContentRegistry::DataFormatter::impl::FindOccurrence> ViewFind::searchValue(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Value &settings) {
|
2022-09-19 10:34:57 +02:00
|
|
|
std::vector<Occurrence> results;
|
|
|
|
|
2023-03-12 18:27:29 +01:00
|
|
|
auto reader = prv::ProviderReader(provider);
|
2022-09-19 10:34:57 +02:00
|
|
|
reader.seek(searchRegion.getStartAddress());
|
|
|
|
reader.setEndAddress(searchRegion.getEndAddress());
|
|
|
|
|
2023-04-08 20:59:33 +02:00
|
|
|
auto inputMin = settings.inputMin;
|
|
|
|
auto inputMax = settings.inputMax;
|
|
|
|
|
|
|
|
if (inputMax.empty())
|
|
|
|
inputMax = inputMin;
|
|
|
|
|
|
|
|
const auto [validMin, min, sizeMin] = parseNumericValueInput(inputMin, settings.type);
|
|
|
|
const auto [validMax, max, sizeMax] = parseNumericValueInput(inputMax, settings.type);
|
2022-09-19 10:34:57 +02:00
|
|
|
|
|
|
|
if (!validMin || !validMax || sizeMin != sizeMax)
|
|
|
|
return { };
|
|
|
|
|
|
|
|
const auto size = sizeMin;
|
|
|
|
|
2023-04-08 20:59:33 +02:00
|
|
|
const auto advance = settings.aligned ? size : 1;
|
|
|
|
|
|
|
|
for (u64 address = searchRegion.getStartAddress(); address < searchRegion.getEndAddress(); address += advance) {
|
|
|
|
task.update(address);
|
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
auto result = std::visit([&]<typename T>(T) {
|
|
|
|
using DecayedType = std::remove_cvref_t<std::decay_t<T>>;
|
2023-04-08 20:59:33 +02:00
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
auto minValue = std::get<DecayedType>(min);
|
|
|
|
auto maxValue = std::get<DecayedType>(max);
|
2023-04-08 20:59:33 +02:00
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
DecayedType value = 0;
|
2023-04-08 20:59:33 +02:00
|
|
|
reader.read(address, reinterpret_cast<u8*>(&value), size);
|
2024-03-28 22:25:28 +01:00
|
|
|
value = hex::changeEndianness(value, size, settings.endian);
|
2023-04-08 20:59:33 +02:00
|
|
|
|
|
|
|
return value >= minValue && value <= maxValue;
|
|
|
|
}, min);
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
Occurrence::DecodeType decodeType = [&]{
|
|
|
|
switch (settings.type) {
|
|
|
|
using enum SearchSettings::Value::Type;
|
|
|
|
using enum Occurrence::DecodeType;
|
|
|
|
|
|
|
|
case U8:
|
|
|
|
case U16:
|
|
|
|
case U32:
|
|
|
|
case U64:
|
|
|
|
return Unsigned;
|
|
|
|
case I8:
|
|
|
|
case I16:
|
|
|
|
case I32:
|
|
|
|
case I64:
|
|
|
|
return Signed;
|
|
|
|
case F32:
|
|
|
|
return Float;
|
|
|
|
case F64:
|
|
|
|
return Double;
|
|
|
|
default:
|
|
|
|
return Binary;
|
|
|
|
}
|
|
|
|
}();
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-05-28 11:50:10 +02:00
|
|
|
results.push_back(Occurrence { Region { address, size }, decodeType, settings.endian, false });
|
2022-09-19 10:34:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
void ViewFind::runSearch() {
|
2023-12-19 13:10:25 +01:00
|
|
|
Region searchRegion = m_searchSettings.region;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-27 16:33:49 +01:00
|
|
|
if (m_searchSettings.mode == SearchSettings::Mode::Strings) {
|
2023-08-06 21:33:15 +02:00
|
|
|
AchievementManager::unlockAchievement("hex.builtin.achievement.find", "hex.builtin.achievement.find.find_strings.name");
|
2023-12-27 16:33:49 +01:00
|
|
|
} else if (m_searchSettings.mode == SearchSettings::Mode::Sequence) {
|
2023-08-06 21:33:15 +02:00
|
|
|
AchievementManager::unlockAchievement("hex.builtin.achievement.find", "hex.builtin.achievement.find.find_specific_string.name");
|
2023-12-27 16:33:49 +01:00
|
|
|
} else if (m_searchSettings.mode == SearchSettings::Mode::Value) {
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_searchSettings.value.inputMin == "250" && m_searchSettings.value.inputMax == "1000")
|
2023-09-12 22:24:12 +02:00
|
|
|
AchievementManager::unlockAchievement("hex.builtin.achievement.find", "hex.builtin.achievement.find.find_numeric.name");
|
2023-08-06 21:33:15 +02:00
|
|
|
}
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_occurrenceTree->clear();
|
2023-12-08 10:29:44 +01:00
|
|
|
EventHighlightingChanged::post();
|
2023-08-06 21:33:15 +02:00
|
|
|
|
2024-07-27 14:09:52 +02:00
|
|
|
m_searchTask = TaskManager::createTask("hex.builtin.view.find.searching"_lang, searchRegion.getSize(), [this, settings = m_searchSettings, searchRegion](auto &task) {
|
2022-07-29 13:59:57 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
|
|
|
|
|
|
|
switch (settings.mode) {
|
|
|
|
using enum SearchSettings::Mode;
|
|
|
|
case Strings:
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences.get(provider) = searchStrings(task, provider, searchRegion, settings.strings);
|
2022-07-29 13:59:57 +02:00
|
|
|
break;
|
|
|
|
case Sequence:
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences.get(provider) = searchSequence(task, provider, searchRegion, settings.bytes);
|
2022-07-29 13:59:57 +02:00
|
|
|
break;
|
|
|
|
case Regex:
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences.get(provider) = searchRegex(task, provider, searchRegion, settings.regex);
|
2022-07-29 13:59:57 +02:00
|
|
|
break;
|
2022-08-03 10:19:34 +02:00
|
|
|
case BinaryPattern:
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences.get(provider) = searchBinaryPattern(task, provider, searchRegion, settings.binaryPattern);
|
2022-08-03 10:19:34 +02:00
|
|
|
break;
|
2022-09-19 10:34:57 +02:00
|
|
|
case Value:
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences.get(provider) = searchValue(task, provider, searchRegion, settings.value);
|
2022-09-19 10:34:57 +02:00
|
|
|
break;
|
2022-08-17 16:15:36 +02:00
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_sortedOccurrences.get(provider) = m_foundOccurrences.get(provider);
|
2024-03-21 23:50:13 +01:00
|
|
|
m_lastSelectedOccurrence = nullptr;
|
2022-08-06 12:57:47 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
for (const auto &occurrence : m_foundOccurrences.get(provider))
|
|
|
|
m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence);
|
2023-09-12 22:23:47 +02:00
|
|
|
|
|
|
|
TaskManager::doLater([] {
|
2023-12-08 10:29:44 +01:00
|
|
|
EventHighlightingChanged::post();
|
2023-09-12 22:23:47 +02:00
|
|
|
});
|
2022-08-17 16:15:36 +02:00
|
|
|
});
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
std::string ViewFind::decodeValue(prv::Provider *provider, const Occurrence &occurrence, size_t maxBytes) const {
|
2023-04-01 11:04:07 +02:00
|
|
|
std::vector<u8> bytes(std::min<size_t>(occurrence.region.getSize(), maxBytes));
|
2022-08-03 11:38:36 +02:00
|
|
|
provider->read(occurrence.region.getStartAddress(), bytes.data(), bytes.size());
|
2022-07-29 13:59:57 +02:00
|
|
|
|
|
|
|
std::string result;
|
2023-12-19 13:10:25 +01:00
|
|
|
switch (m_decodeSettings.mode) {
|
2022-07-29 13:59:57 +02:00
|
|
|
using enum SearchSettings::Mode;
|
|
|
|
|
2022-09-19 10:34:57 +02:00
|
|
|
case Value:
|
2022-07-29 13:59:57 +02:00
|
|
|
case Strings:
|
2023-12-19 14:34:35 +01:00
|
|
|
case Sequence:
|
|
|
|
case Regex:
|
2022-07-29 13:59:57 +02:00
|
|
|
{
|
2022-08-03 11:38:36 +02:00
|
|
|
switch (occurrence.decodeType) {
|
|
|
|
using enum Occurrence::DecodeType;
|
|
|
|
case Binary:
|
2022-07-29 13:59:57 +02:00
|
|
|
case ASCII:
|
|
|
|
result = hex::encodeByteString(bytes);
|
|
|
|
break;
|
2022-09-19 10:34:57 +02:00
|
|
|
case UTF16:
|
2023-03-31 22:20:00 +02:00
|
|
|
for (size_t i = occurrence.endian == std::endian::little ? 0 : 1; i < bytes.size(); i += 2)
|
2022-07-29 13:59:57 +02:00
|
|
|
result += hex::encodeByteString({ bytes[i] });
|
|
|
|
break;
|
2022-09-19 10:34:57 +02:00
|
|
|
case Unsigned:
|
2023-11-25 19:40:29 +01:00
|
|
|
result += formatBytes<u64>(bytes, occurrence.endian);
|
2022-09-19 10:34:57 +02:00
|
|
|
break;
|
|
|
|
case Signed:
|
2023-11-25 19:40:29 +01:00
|
|
|
result += formatBytes<i64>(bytes, occurrence.endian);
|
2022-09-19 10:34:57 +02:00
|
|
|
break;
|
|
|
|
case Float:
|
2023-11-25 19:40:29 +01:00
|
|
|
result += formatBytes<float>(bytes, occurrence.endian);
|
2022-09-19 10:34:57 +02:00
|
|
|
break;
|
|
|
|
case Double:
|
2023-11-25 19:40:29 +01:00
|
|
|
result += formatBytes<double>(bytes, occurrence.endian);
|
2022-07-29 13:59:57 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2022-08-03 10:19:34 +02:00
|
|
|
case BinaryPattern:
|
2022-07-29 13:59:57 +02:00
|
|
|
result = hex::encodeByteString(bytes);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-04-01 11:04:07 +02:00
|
|
|
if (occurrence.region.getSize() > maxBytes)
|
|
|
|
result += "...";
|
|
|
|
|
2022-07-29 13:59:57 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-05-28 11:50:10 +02:00
|
|
|
void ViewFind::drawContextMenu(Occurrence &target, const std::string &value) {
|
2022-07-29 13:59:57 +02:00
|
|
|
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) && ImGui::IsItemHovered()) {
|
|
|
|
ImGui::OpenPopup("FindContextMenu");
|
2023-05-28 11:50:10 +02:00
|
|
|
target.selected = true;
|
2023-12-19 13:10:25 +01:00
|
|
|
m_replaceBuffer.clear();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::BeginPopup("FindContextMenu")) {
|
2022-07-29 14:05:30 +02:00
|
|
|
if (ImGui::MenuItem("hex.builtin.view.find.context.copy"_lang))
|
2022-07-29 13:59:57 +02:00
|
|
|
ImGui::SetClipboardText(value.c_str());
|
|
|
|
if (ImGui::MenuItem("hex.builtin.view.find.context.copy_demangle"_lang))
|
2024-07-11 20:38:22 +02:00
|
|
|
ImGui::SetClipboardText(hex::plugin::builtin::demangle(value).c_str());
|
2023-05-28 11:50:10 +02:00
|
|
|
if (ImGui::BeginMenu("hex.builtin.view.find.context.replace"_lang)) {
|
|
|
|
if (ImGui::BeginTabBar("##replace_tabs")) {
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.context.replace.hex"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGuiExt::InputTextIcon("##replace_input", ICON_VS_SYMBOL_NAMESPACE, m_replaceBuffer);
|
2023-05-28 11:50:10 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui::BeginDisabled(m_replaceBuffer.empty());
|
2023-05-28 11:50:10 +02:00
|
|
|
if (ImGui::Button("hex.builtin.view.find.context.replace"_lang)) {
|
2023-05-28 14:30:33 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2023-12-19 13:10:25 +01:00
|
|
|
auto bytes = parseHexString(m_replaceBuffer);
|
2023-05-28 14:30:33 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
for (const auto &occurrence : *m_sortedOccurrences) {
|
2023-05-28 11:50:10 +02:00
|
|
|
if (occurrence.selected) {
|
|
|
|
size_t size = std::min<size_t>(occurrence.region.size, bytes.size());
|
|
|
|
provider->write(occurrence.region.getStartAddress(), bytes.data(), size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.context.replace.ascii"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGuiExt::InputTextIcon("##replace_input", ICON_VS_SYMBOL_KEY, m_replaceBuffer);
|
2023-05-28 11:50:10 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui::BeginDisabled(m_replaceBuffer.empty());
|
2023-05-28 11:50:10 +02:00
|
|
|
if (ImGui::Button("hex.builtin.view.find.context.replace"_lang)) {
|
2023-05-28 14:30:33 +02:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2023-12-19 13:10:25 +01:00
|
|
|
auto bytes = decodeByteString(m_replaceBuffer);
|
2023-05-28 14:30:33 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
for (const auto &occurrence : *m_sortedOccurrences) {
|
2023-05-28 11:50:10 +02:00
|
|
|
if (occurrence.selected) {
|
2023-05-28 14:30:33 +02:00
|
|
|
size_t size = std::min<size_t>(occurrence.region.size, bytes.size());
|
|
|
|
provider->write(occurrence.region.getStartAddress(), bytes.data(), size);
|
2023-05-28 11:50:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndTabBar();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewFind::drawContent() {
|
2023-11-21 13:47:50 +01:00
|
|
|
auto provider = ImHexApi::Provider::get();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui::BeginDisabled(m_searchTask.isRunning());
|
2023-11-21 13:47:50 +01:00
|
|
|
{
|
2023-12-19 13:10:25 +01:00
|
|
|
ui::regionSelectionPicker(&m_searchSettings.region, provider, &m_searchSettings.range, true, true);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::NewLine();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (ImGui::BeginTabBar("SearchMethods")) {
|
|
|
|
const std::array<std::string, 5> StringTypes = {
|
2023-12-23 21:09:41 +01:00
|
|
|
"hex.ui.common.encoding.ascii"_lang,
|
|
|
|
"hex.ui.common.encoding.utf16le"_lang,
|
|
|
|
"hex.ui.common.encoding.utf16be"_lang,
|
|
|
|
hex::format("{} + {}", "hex.ui.common.encoding.ascii"_lang, "hex.ui.common.encoding.utf16le"_lang),
|
|
|
|
hex::format("{} + {}", "hex.ui.common.encoding.ascii"_lang, "hex.ui.common.encoding.utf16be"_lang)
|
2023-11-21 13:47:50 +01:00
|
|
|
};
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &mode = m_searchSettings.mode;
|
2023-11-21 13:47:50 +01:00
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.strings"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &settings = m_searchSettings.strings;
|
2023-11-21 13:47:50 +01:00
|
|
|
mode = SearchSettings::Mode::Strings;
|
|
|
|
|
|
|
|
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
|
|
|
if (settings.minLength < 1)
|
|
|
|
settings.minLength = 1;
|
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
if (ImGui::BeginCombo("hex.ui.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
2023-11-21 13:47:50 +01:00
|
|
|
for (size_t i = 0; i < StringTypes.size(); i++) {
|
|
|
|
auto type = static_cast<SearchSettings::StringType>(i);
|
|
|
|
|
|
|
|
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
|
|
|
settings.type = type;
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (ImGui::CollapsingHeader("hex.builtin.view.find.strings.match_settings"_lang)) {
|
|
|
|
ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGuiExt::Header("hex.builtin.view.find.strings.chars"_lang);
|
|
|
|
ImGui::Checkbox(hex::format("{} [a-z]", "hex.builtin.view.find.strings.lower_case"_lang.get()).c_str(), &settings.lowerCaseLetters);
|
|
|
|
ImGui::Checkbox(hex::format("{} [A-Z]", "hex.builtin.view.find.strings.upper_case"_lang.get()).c_str(), &settings.upperCaseLetters);
|
|
|
|
ImGui::Checkbox(hex::format("{} [0-9]", "hex.builtin.view.find.strings.numbers"_lang.get()).c_str(), &settings.numbers);
|
|
|
|
ImGui::Checkbox(hex::format("{} [_]", "hex.builtin.view.find.strings.underscores"_lang.get()).c_str(), &settings.underscores);
|
|
|
|
ImGui::Checkbox(hex::format("{} [!\"#$%...]", "hex.builtin.view.find.strings.symbols"_lang.get()).c_str(), &settings.symbols);
|
|
|
|
ImGui::Checkbox(hex::format("{} [ \\f\\t\\v]", "hex.builtin.view.find.strings.spaces"_lang.get()).c_str(), &settings.spaces);
|
|
|
|
ImGui::Checkbox(hex::format("{} [\\r\\n]", "hex.builtin.view.find.strings.line_feeds"_lang.get()).c_str(), &settings.lineFeeds);
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = true;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.sequences"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &settings = m_searchSettings.bytes;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
mode = SearchSettings::Mode::Sequence;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
ImGuiExt::InputTextIcon("hex.ui.common.value"_lang, ICON_VS_SYMBOL_KEY, settings.sequence);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
if (ImGui::BeginCombo("hex.ui.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
2023-12-19 14:34:35 +01:00
|
|
|
for (size_t i = 0; i < StringTypes.size() - 2; i++) {
|
|
|
|
auto type = static_cast<SearchSettings::StringType>(i);
|
|
|
|
|
|
|
|
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
|
|
|
settings.type = type;
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Checkbox("hex.builtin.view.find.sequences.ignore_case"_lang, &settings.ignoreCase);
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = !settings.sequence.empty() && !hex::decodeByteString(settings.sequence).empty();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.regex"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &settings = m_searchSettings.regex;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
mode = SearchSettings::Mode::Regex;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
|
|
|
if (settings.minLength < 1)
|
|
|
|
settings.minLength = 1;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
if (ImGui::BeginCombo("hex.ui.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
2023-11-21 13:47:50 +01:00
|
|
|
for (size_t i = 0; i < StringTypes.size(); i++) {
|
|
|
|
auto type = static_cast<SearchSettings::StringType>(i);
|
|
|
|
|
|
|
|
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
|
|
|
settings.type = type;
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::NewLine();
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGuiExt::InputTextIcon("hex.builtin.view.find.regex.pattern"_lang, ICON_VS_REGEX, settings.pattern);
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
try {
|
2024-06-07 23:12:18 +02:00
|
|
|
boost::regex regex(settings.pattern);
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = true;
|
2024-06-07 23:12:18 +02:00
|
|
|
} catch (const boost::regex_error &) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = false;
|
2023-11-21 13:47:50 +01:00
|
|
|
}
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (settings.pattern.empty())
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = false;
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::Checkbox("hex.builtin.view.find.regex.full_match"_lang, &settings.fullMatch);
|
2023-03-17 08:16:13 +01:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &settings = m_searchSettings.binaryPattern;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
mode = SearchSettings::Mode::BinaryPattern;
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGuiExt::InputTextIcon("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, settings.input);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
constexpr static u32 min = 1, max = 0x1000;
|
|
|
|
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
|
2022-09-13 14:06:08 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
settings.pattern = hex::BinaryPattern(settings.input);
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = settings.pattern.isValid() && settings.alignment > 0;
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTabItem();
|
|
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("hex.builtin.view.find.value"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &settings = m_searchSettings.value;
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
mode = SearchSettings::Mode::Value;
|
2023-04-09 15:28:31 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
bool edited = false;
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (settings.range) {
|
|
|
|
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.min"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) edited = true;
|
|
|
|
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.max"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMax)) edited = true;
|
|
|
|
} else {
|
2023-12-23 21:09:41 +01:00
|
|
|
if (ImGuiExt::InputTextIcon("hex.ui.common.value"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) {
|
2023-11-21 13:47:50 +01:00
|
|
|
edited = true;
|
|
|
|
settings.inputMax = settings.inputMin;
|
|
|
|
}
|
2022-08-03 10:19:34 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::BeginDisabled();
|
|
|
|
ImGuiExt::InputTextIcon("##placeholder_value", ICON_VS_SYMBOL_NUMERIC, settings.inputMax);
|
|
|
|
ImGui::EndDisabled();
|
2022-08-03 10:19:34 +02:00
|
|
|
}
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-12-17 23:38:10 +01:00
|
|
|
if (ImGui::Checkbox("hex.builtin.view.find.value.range"_lang, &settings.range)) {
|
|
|
|
settings.inputMax = settings.inputMin;
|
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::NewLine();
|
|
|
|
|
|
|
|
const std::array<std::string, 10> InputTypes = {
|
2023-12-23 21:09:41 +01:00
|
|
|
"hex.ui.common.type.u8"_lang,
|
|
|
|
"hex.ui.common.type.u16"_lang,
|
|
|
|
"hex.ui.common.type.u32"_lang,
|
|
|
|
"hex.ui.common.type.u64"_lang,
|
|
|
|
"hex.ui.common.type.i8"_lang,
|
|
|
|
"hex.ui.common.type.i16"_lang,
|
|
|
|
"hex.ui.common.type.i32"_lang,
|
|
|
|
"hex.ui.common.type.i64"_lang,
|
|
|
|
"hex.ui.common.type.f32"_lang,
|
|
|
|
"hex.ui.common.type.f64"_lang
|
2023-11-21 13:47:50 +01:00
|
|
|
};
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
if (ImGui::BeginCombo("hex.ui.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) {
|
2023-11-21 13:47:50 +01:00
|
|
|
for (size_t i = 0; i < InputTypes.size(); i++) {
|
|
|
|
auto type = static_cast<SearchSettings::Value::Type>(i);
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) {
|
|
|
|
settings.type = type;
|
2023-04-08 20:59:33 +02:00
|
|
|
edited = true;
|
|
|
|
}
|
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2023-04-08 20:59:33 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
{
|
|
|
|
int selection = [&] {
|
|
|
|
switch (settings.endian) {
|
|
|
|
default:
|
|
|
|
case std::endian::little: return 0;
|
|
|
|
case std::endian::big: return 1;
|
2022-09-19 10:34:57 +02:00
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
}();
|
|
|
|
|
2023-12-23 21:09:41 +01:00
|
|
|
std::array options = { "hex.ui.common.little"_lang, "hex.ui.common.big"_lang };
|
|
|
|
if (ImGui::SliderInt("hex.ui.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
2023-11-21 13:47:50 +01:00
|
|
|
edited = true;
|
|
|
|
switch (selection) {
|
|
|
|
default:
|
|
|
|
case 0: settings.endian = std::endian::little; break;
|
|
|
|
case 1: settings.endian = std::endian::big; break;
|
2022-09-19 10:34:57 +02:00
|
|
|
}
|
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
}
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::Checkbox("hex.builtin.view.find.value.aligned"_lang, &settings.aligned);
|
2022-09-19 10:34:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (edited) {
|
|
|
|
auto [minValid, min, minSize] = parseNumericValueInput(settings.inputMin, settings.type);
|
|
|
|
auto [maxValid, max, maxSize] = parseNumericValueInput(settings.inputMax, settings.type);
|
2023-05-13 17:51:16 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = minValid && maxValid && minSize == maxSize;
|
2022-09-19 10:34:57 +02:00
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
if (settings.inputMin.empty())
|
2023-12-19 13:10:25 +01:00
|
|
|
m_settingsValid = false;
|
2023-11-21 13:47:50 +01:00
|
|
|
|
|
|
|
ImGui::EndTabItem();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTabBar();
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::NewLine();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui::BeginDisabled(!m_settingsValid);
|
2023-11-21 13:47:50 +01:00
|
|
|
{
|
|
|
|
if (ImGui::Button("hex.builtin.view.find.search"_lang)) {
|
|
|
|
this->runSearch();
|
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
m_decodeSettings = m_searchSettings;
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::SameLine();
|
2022-09-13 14:22:18 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
ImGui::BeginDisabled(m_foundOccurrences->empty());
|
2023-11-21 13:47:50 +01:00
|
|
|
{
|
|
|
|
if (ImGui::Button("hex.builtin.view.find.search.reset"_lang)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_foundOccurrences->clear();
|
|
|
|
m_sortedOccurrences->clear();
|
|
|
|
m_occurrenceTree->clear();
|
2024-03-21 23:50:13 +01:00
|
|
|
m_lastSelectedOccurrence = nullptr;
|
2023-09-12 22:23:47 +02:00
|
|
|
|
2023-12-08 10:29:44 +01:00
|
|
|
EventHighlightingChanged::post();
|
2022-09-13 14:22:18 +02:00
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
2024-06-18 22:57:55 +02:00
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
|
|
|
ImGuiExt::TextFormatted("hex.builtin.view.find.search.entries"_lang, m_foundOccurrences->size());
|
2023-11-21 13:47:50 +01:00
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
ImGui::NewLine();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
auto &currOccurrences = *m_sortedOccurrences;
|
2022-07-29 18:49:43 +02:00
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
ImGui::PushItemWidth(-30_scaled);
|
2023-12-19 13:10:25 +01:00
|
|
|
auto prevFilterLength = m_currFilter->length();
|
|
|
|
if (ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, *m_currFilter)) {
|
|
|
|
if (prevFilterLength > m_currFilter->length())
|
|
|
|
*m_sortedOccurrences = *m_foundOccurrences;
|
2023-03-17 11:32:08 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (m_filterTask.isRunning())
|
|
|
|
m_filterTask.interrupt();
|
2023-03-17 11:32:08 +01:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
if (!m_currFilter->empty()) {
|
2024-07-27 16:29:06 +02:00
|
|
|
m_filterTask = TaskManager::createTask("hex.builtin.task.filtering_data"_lang, currOccurrences.size(), [this, provider, &currOccurrences](Task &task) {
|
2023-11-21 13:47:50 +01:00
|
|
|
u64 progress = 0;
|
|
|
|
std::erase_if(currOccurrences, [this, provider, &task, &progress](const auto ®ion) {
|
|
|
|
task.update(progress);
|
|
|
|
progress += 1;
|
2023-04-08 20:59:33 +02:00
|
|
|
|
2023-12-19 13:10:25 +01:00
|
|
|
return !hex::containsIgnoreCase(this->decodeValue(provider, region), m_currFilter.get(provider));
|
2023-03-17 11:32:08 +01:00
|
|
|
});
|
2023-11-21 13:47:50 +01:00
|
|
|
});
|
2022-07-29 18:49:43 +02:00
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
}
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
|
2024-02-17 21:42:41 +01:00
|
|
|
ImGui::SameLine();
|
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
const auto startPos = ImGui::GetCursorPos();
|
2024-02-17 21:42:41 +01:00
|
|
|
ImGui::BeginDisabled(m_sortedOccurrences->empty());
|
|
|
|
if (ImGuiExt::DimmedIconButton(ICON_VS_EXPORT, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
2024-06-18 22:57:55 +02:00
|
|
|
ImGui::OpenPopup("ExportResults");
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
|
|
|
|
ImGui::SetNextWindowPos(ImGui::GetWindowPos() + ImVec2(startPos.x, ImGui::GetCursorPosY()));
|
|
|
|
if (ImGui::BeginPopup("ExportResults")) {
|
|
|
|
for (const auto &formatter : ContentRegistry::DataFormatter::impl::getFindExporterEntries()) {
|
|
|
|
const auto formatterName = formatter.unlocalizedName;
|
|
|
|
const auto name = toUpper(formatterName);
|
2024-02-17 21:42:41 +01:00
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
const auto &extension = formatter.fileExtension;
|
2024-02-17 21:42:41 +01:00
|
|
|
|
2024-06-18 22:57:55 +02:00
|
|
|
if (ImGui::MenuItem(name.c_str())) {
|
|
|
|
fs::openFileBrowser(fs::DialogMode::Save, { { name.c_str(), extension.c_str() } }, [&](const std::fs::path &path) {
|
|
|
|
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
|
|
|
if (!file.isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto result = formatter.callback(
|
|
|
|
m_sortedOccurrences.get(provider),
|
|
|
|
[&](Occurrence o){ return this->decodeValue(provider, o); });
|
|
|
|
|
|
|
|
file.writeVector(result);
|
|
|
|
file.close();
|
|
|
|
});
|
2024-02-17 21:42:41 +01:00
|
|
|
}
|
2024-06-18 22:57:55 +02:00
|
|
|
}
|
|
|
|
ImGui::EndPopup();
|
2024-02-17 21:42:41 +01:00
|
|
|
}
|
|
|
|
|
2023-11-28 10:21:54 +01:00
|
|
|
if (ImGui::BeginTable("##entries", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImMax(ImGui::GetContentRegionAvail(), ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5)))) {
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::TableSetupScrollFreeze(0, 1);
|
2023-12-23 21:09:41 +01:00
|
|
|
ImGui::TableSetupColumn("hex.ui.common.offset"_lang, 0, -1, ImGui::GetID("offset"));
|
|
|
|
ImGui::TableSetupColumn("hex.ui.common.size"_lang, 0, -1, ImGui::GetID("size"));
|
|
|
|
ImGui::TableSetupColumn("hex.ui.common.value"_lang, 0, -1, ImGui::GetID("value"));
|
2023-11-21 13:47:50 +01:00
|
|
|
|
|
|
|
auto sortSpecs = ImGui::TableGetSortSpecs();
|
|
|
|
|
|
|
|
if (sortSpecs->SpecsDirty) {
|
|
|
|
std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool {
|
|
|
|
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
|
|
|
return left.region.getStartAddress() > right.region.getStartAddress();
|
|
|
|
else
|
|
|
|
return left.region.getStartAddress() < right.region.getStartAddress();
|
|
|
|
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
|
|
|
return left.region.getSize() > right.region.getSize();
|
|
|
|
else
|
|
|
|
return left.region.getSize() < right.region.getSize();
|
|
|
|
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
|
|
|
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
|
|
|
return this->decodeValue(provider, left) > this->decodeValue(provider, right);
|
|
|
|
else
|
|
|
|
return this->decodeValue(provider, left) < this->decodeValue(provider, right);
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
return false;
|
|
|
|
});
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
sortSpecs->SpecsDirty = false;
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::TableHeadersRow();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGuiListClipper clipper;
|
|
|
|
clipper.Begin(currOccurrences.size(), ImGui::GetTextLineHeightWithSpacing());
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
while (clipper.Step()) {
|
|
|
|
for (size_t i = clipper.DisplayStart; i < std::min<size_t>(clipper.DisplayEnd, currOccurrences.size()); i++) {
|
|
|
|
auto &foundItem = currOccurrences[i];
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGuiExt::TextFormatted("0x{:08X}", foundItem.region.getStartAddress());
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGuiExt::TextFormatted("{}", hex::toByteString(foundItem.region.getSize()));
|
|
|
|
ImGui::TableNextColumn();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::PushID(i);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
auto value = this->decodeValue(provider, foundItem, 256);
|
|
|
|
ImGuiExt::TextFormatted("{}", value);
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) {
|
2024-03-21 23:50:13 +01:00
|
|
|
if (ImGui::GetIO().KeyShift && m_lastSelectedOccurrence != nullptr) {
|
|
|
|
for (auto start = std::min(&foundItem, m_lastSelectedOccurrence.get(provider)); start <= std::max(&foundItem, m_lastSelectedOccurrence.get(provider)); start += 1)
|
|
|
|
start->selected = true;
|
|
|
|
|
|
|
|
} else if (ImGui::GetIO().KeyCtrl) {
|
2023-11-21 13:47:50 +01:00
|
|
|
foundItem.selected = !foundItem.selected;
|
|
|
|
} else {
|
2023-12-19 13:10:25 +01:00
|
|
|
for (auto &occurrence : *m_sortedOccurrences)
|
2023-11-21 13:47:50 +01:00
|
|
|
occurrence.selected = false;
|
|
|
|
foundItem.selected = true;
|
|
|
|
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
|
2023-05-28 11:50:10 +02:00
|
|
|
}
|
2024-03-21 23:50:13 +01:00
|
|
|
|
|
|
|
m_lastSelectedOccurrence = &foundItem;
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
drawContextMenu(foundItem, value);
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::PopID();
|
|
|
|
}
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
2023-11-21 13:47:50 +01:00
|
|
|
clipper.End();
|
2022-07-29 13:59:57 +02:00
|
|
|
|
2023-11-21 13:47:50 +01:00
|
|
|
ImGui::EndTable();
|
2022-07-29 13:59:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|