diff --git a/plugins/builtin/include/content/views/view_find.hpp b/plugins/builtin/include/content/views/view_find.hpp index 26299639fb858..4580810e51078 100644 --- a/plugins/builtin/include/content/views/view_find.hpp +++ b/plugins/builtin/include/content/views/view_find.hpp @@ -42,7 +42,8 @@ namespace hex::plugin::builtin { Sequence, Regex, BinaryPattern, - Value + Value, + Approximate } mode = Mode::Strings; enum class StringType : int { ASCII = 0, UTF16LE = 1, UTF16BE = 2, ASCII_UTF16LE = 3, ASCII_UTF16BE = 4 }; @@ -93,6 +94,16 @@ namespace hex::plugin::builtin { } type = Type::U8; } value; + struct Approximate { + std::endian endian = std::endian::native; + bool aligned = true; + bool ignore_zeroes = true; + + enum class Type { + F32 = 0, F64 = 1 + } type = Type::F32; + } approximate; + } m_searchSettings, m_decodeSettings; using OccurrenceTree = wolv::container::IntervalTree; @@ -111,6 +122,7 @@ namespace hex::plugin::builtin { static std::vector searchRegex(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Regex &settings); static std::vector searchBinaryPattern(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::BinaryPattern &settings); static std::vector searchValue(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Value &settings); + static std::vector searchApproximate(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Approximate &settings); void drawContextMenu(Occurrence &target, const std::string &value); diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index fb864051f7d25..d426574a7bbf8 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -853,6 +853,9 @@ "hex.builtin.view.find.value.max": "Maximum Value", "hex.builtin.view.find.value.min": "Minimum Value", "hex.builtin.view.find.value.range": "Ranged Search", + "hex.builtin.view.find.approximate": "Almost integers", + "hex.builtin.view.find.approximate.aligned": "Aligned", + "hex.builtin.view.find.approximate.ignore_zeroes": "Skip zeroes", "hex.builtin.view.hashes.function": "Hash function", "hex.builtin.view.hashes.hash": "Hash", "hex.builtin.view.hashes.hover_info": "Hover over the Hex Editor selection and hold down SHIFT to view the hashes of that region.", diff --git a/plugins/builtin/source/content/views/view_find.cpp b/plugins/builtin/source/content/views/view_find.cpp index 37d43ba134545..820d375bdd7aa 100644 --- a/plugins/builtin/source/content/views/view_find.cpp +++ b/plugins/builtin/source/content/views/view_find.cpp @@ -437,6 +437,62 @@ namespace hex::plugin::builtin { return results; } + std::vector ViewFind::searchApproximate(Task &task, prv::Provider *provider, Region searchRegion, const SearchSettings::Approximate &settings) { + std::vector results; + + auto reader = prv::ProviderReader(provider); + reader.seek(searchRegion.getStartAddress()); + reader.setEndAddress(searchRegion.getEndAddress()); + + using FloatingType = ViewFind::SearchSettings::Approximate::Type; + + const size_t size = settings.type == FloatingType::F32 ? sizeof(float) : sizeof(double); + const auto advance = settings.aligned ? size : 1; + + std::variant floating_var; + if (settings.type == FloatingType::F32) { + floating_var = 0.F; + } else { + floating_var = 0.0; + } + + for (u64 address = searchRegion.getStartAddress(); address <= searchRegion.getEndAddress() - size + 1; address += advance) { + task.update(address); + + auto result = std::visit([&](T) { + using DecayedType = std::remove_cvref_t>; + + DecayedType value = 0; + reader.read(address, reinterpret_cast(&value), size); + value = hex::changeEndianess(value, size, settings.endian); + + if (settings.ignore_zeroes && value == T(0)) { + return false; + } + + return std::floor(value) == value; + }, floating_var); + + if (result) { + Occurrence::DecodeType decodeType = [&]{ + switch (settings.type) { + using enum Occurrence::DecodeType; + + case FloatingType::F32: + return Float; + case FloatingType::F64: + return Double; + default: + return Binary; + } + }(); + + results.push_back(Occurrence { Region { address, size }, decodeType, settings.endian, false }); + } + } + return results; + } + void ViewFind::runSearch() { Region searchRegion = this->m_searchSettings.region; @@ -472,6 +528,9 @@ namespace hex::plugin::builtin { case Value: this->m_foundOccurrences.get(provider) = searchValue(task, provider, searchRegion, settings.value); break; + case Approximate: + this->m_foundOccurrences.get(provider) = searchApproximate(task, provider, searchRegion, settings.approximate); + break; } this->m_sortedOccurrences.get(provider) = this->m_foundOccurrences.get(provider); @@ -494,6 +553,7 @@ namespace hex::plugin::builtin { using enum SearchSettings::Mode; case Value: + case Approximate: case Strings: { switch (occurrence.decodeType) { @@ -800,6 +860,53 @@ namespace hex::plugin::builtin { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("hex.builtin.view.find.approximate"_lang)) { + auto &settings = this->m_searchSettings.approximate; + + mode = SearchSettings::Mode::Approximate; + + this->m_settingsValid = true; + + const std::array InputTypes = { + "hex.builtin.common.type.f32"_lang, + "hex.builtin.common.type.f64"_lang + }; + + if (ImGui::BeginCombo("hex.builtin.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) { + for (size_t i = 0; i < InputTypes.size(); i++) { + auto type = static_cast(i); + + if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) { + settings.type = type; + } + } + ImGui::EndCombo(); + } + + { + int selection = [&] { + switch (settings.endian) { + default: + case std::endian::little: return 0; + case std::endian::big: return 1; + } + }(); + + std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang }; + if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) { + switch (selection) { + default: + case 0: settings.endian = std::endian::little; break; + case 1: settings.endian = std::endian::big; break; + } + } + } + + ImGui::Checkbox("hex.builtin.view.find.approximate.aligned"_lang, &settings.aligned); + ImGui::Checkbox("hex.builtin.view.find.approximate.ignore_zeroes"_lang, &settings.ignore_zeroes); + + ImGui::EndTabItem(); + } ImGui::EndTabBar(); }