feat: Replace useless constants view with a constant search option in the Find view

This commit is contained in:
WerWolv
2025-12-02 23:02:44 +01:00
parent da0c1674a6
commit d4df465633
27 changed files with 218 additions and 291 deletions

View File

@@ -1,152 +0,0 @@
#include "content/views/view_constants.hpp"
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/default_paths.hpp>
#include <wolv/utils/string.hpp>
#include <wolv/io/file.hpp>
#include <filesystem>
#include <nlohmann/json.hpp>
#include <fonts/vscode_icons.hpp>
namespace hex::plugin::builtin {
ViewConstants::ViewConstants() : View::Window("hex.builtin.view.constants.name", ICON_VS_SYMBOL_CONSTANT) {
this->reloadConstants();
}
void ViewConstants::reloadConstants() {
m_constants.clear();
m_filterIndices.clear();
for (const auto &path : paths::Constants.read()) {
if (!wolv::io::fs::exists(path)) continue;
std::error_code error;
for (auto &file : std::fs::directory_iterator(path, error)) {
if (!file.is_regular_file()) continue;
if (file.path().extension() != ".json") continue;
if (file.path().filename().u8string().starts_with('_')) continue;
try {
auto fileData = wolv::io::File(file.path(), wolv::io::File::Mode::Read).readString();
auto content = nlohmann::json::parse(fileData);
for (auto value : content.at("values")) {
Constant constant;
constant.category = content.at("name").get<std::string>();
constant.name = value.at("name").get<std::string>();
if (value.contains("desc"))
constant.description = value.at("desc").get<std::string>();
constant.value = value.at("value").get<std::string>();
auto type = value.at("type");
if (type == "int10")
constant.type = ConstantType::Int10;
else if (type == "int16be")
constant.type = ConstantType::Int16BigEndian;
else if (type == "int16le")
constant.type = ConstantType::Int16LittleEndian;
else
throw std::runtime_error("Invalid type");
m_filterIndices.push_back(m_constants.size());
m_constants.push_back(constant);
}
} catch (...) {
log::error("Failed to parse constants file {}", wolv::util::toUTF8String(file.path()));
}
}
}
}
void ViewConstants::drawContent() {
ImGui::PushItemWidth(-1);
if (ImGuiExt::InputTextIcon("##search", ICON_VS_FILTER, m_filter)) {
m_filterIndices.clear();
// Filter the constants according to the entered value
for (u64 i = 0; i < m_constants.size(); i++) {
auto &constant = m_constants[i];
if (hex::containsIgnoreCase(constant.name, m_filter) ||
hex::containsIgnoreCase(constant.category, m_filter) ||
hex::containsIgnoreCase(constant.description, m_filter) ||
hex::containsIgnoreCase(constant.value, m_filter))
m_filterIndices.push_back(i);
}
}
ImGui::PopItemWidth();
if (ImGui::BeginTable("##strings", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.view.constants.row.category"_lang, 0, -1, ImGui::GetID("category"));
ImGui::TableSetupColumn("hex.builtin.view.constants.row.name"_lang, 0, -1, ImGui::GetID("name"));
ImGui::TableSetupColumn("hex.builtin.view.constants.row.desc"_lang, 0, -1, ImGui::GetID("desc"));
ImGui::TableSetupColumn("hex.builtin.view.constants.row.value"_lang, 0, -1, ImGui::GetID("value"));
auto sortSpecs = ImGui::TableGetSortSpecs();
// Handle table sorting
if (sortSpecs->SpecsDirty) {
std::sort(m_constants.begin(), m_constants.end(), [&sortSpecs](const Constant &left, const Constant &right) -> bool {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("category")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.category > right.category;
else
return left.category < right.category;
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.name > right.name;
else
return left.name < right.name;
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("desc")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.description > right.description;
else
return left.description < right.description;
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.value > right.value;
else
return left.value < right.value;
}
return false;
});
sortSpecs->SpecsDirty = false;
}
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
clipper.Begin(m_filterIndices.size());
// Draw the constants table
while (clipper.Step()) {
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
auto &constant = m_constants[m_filterIndices[i]];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(constant.category.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(constant.name.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(constant.description.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(constant.value.c_str());
}
}
clipper.End();
ImGui::EndTable();
}
}
}

View File

@@ -17,6 +17,10 @@
#include <boost/regex.hpp>
#include <content/helpers/constants.hpp>
#include <hex/helpers/default_paths.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
ViewFind::ViewFind() : View::Window("hex.builtin.view.find.name", ICON_VS_SEARCH) {
@@ -55,8 +59,8 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
{
auto region = occurrence.value.region;
const auto value = this->decodeValue(ImHexApi::Provider::get(), occurrence.value, 256);
auto region = occurrence.value->region;
const auto value = this->decodeValue(ImHexApi::Provider::get(), *occurrence.value, 256);
ImGui::ColorButton("##color", ImColor(HighlightColor()), ImGuiColorEditFlags_AlphaOpaque);
ImGui::SameLine(0, 10);
@@ -291,7 +295,7 @@ namespace hex::plugin::builtin {
if (!validChar || startAddress + countedCharacters == endAddress) {
if (countedCharacters >= settings.minLength) {
if (!settings.nullTermination || byte == 0x00) {
results.push_back(Occurrence { Region { startAddress, size_t(countedCharacters) }, decodeType, endian, false });
results.push_back(Occurrence { Region { startAddress, size_t(countedCharacters) }, endian, decodeType, false, {} });
}
}
@@ -379,7 +383,7 @@ namespace hex::plugin::builtin {
auto address = occurrence.getAddress();
reader.seek(address + 1);
results.push_back(Occurrence{ Region { address, bytes.size() }, decodeType, endian, false });
results.push_back(Occurrence{ Region { address, bytes.size() }, endian, decodeType, false, {} });
progress = address - searchRegion.getStartAddress();
}
@@ -440,7 +444,7 @@ namespace hex::plugin::builtin {
if (matchedBytes == settings.pattern.getSize()) {
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false });
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, std::endian::native, Occurrence::DecodeType::Binary, false, {} });
it.setAddress(occurrenceAddress);
matchedBytes = 0;
}
@@ -466,7 +470,7 @@ namespace hex::plugin::builtin {
}
if (match)
results.push_back(Occurrence { Region { address, patternSize }, Occurrence::DecodeType::Binary, std::endian::native, false });
results.push_back(Occurrence { Region { address, patternSize }, std::endian::native, Occurrence::DecodeType::Binary, false, {} });
}
}
@@ -550,7 +554,96 @@ namespace hex::plugin::builtin {
}
}();
results.push_back(Occurrence { Region { address, size }, decodeType, settings.endian, false });
results.push_back(Occurrence { Region { address, size }, settings.endian, decodeType, false, {} });
}
}
return results;
}
std::vector<ViewFind::Occurrence> ViewFind::searchConstants(Task &task, prv::Provider* provider, Region searchRegion, const SearchSettings::Constants &settings) {
std::vector<Occurrence> results;
std::vector<ConstantGroup> constantGroups;
for (const auto &path : paths::Constants.read()) {
for (const auto &entry : std::fs::directory_iterator(path)) {
try {
constantGroups.emplace_back(entry.path());
} catch (const std::exception &e) {
ui::ToastError::open(fmt::format("Failed to load constant group from {}: {}", wolv::util::toUTF8String(entry.path()), e.what()));
}
}
}
auto reader = prv::ProviderReader(provider);
reader.seek(searchRegion.getStartAddress());
reader.setEndAddress(searchRegion.getEndAddress());
u64 constantCount = 0;
for (const auto &group : constantGroups) {
constantCount += group.getConstants().size();
}
task.setMaxValue(constantCount * searchRegion.getSize());
u64 progress = 0;
for (const auto &group : constantGroups) {
for (const auto &constant : group.getConstants()) {
const auto &pattern = constant.value;
const size_t patternSize = pattern.getSize();
if (settings.alignment == 1) {
u32 matchedBytes = 0;
for (auto it = reader.begin(); it < reader.end(); it += 1) {
auto byte = *it;
task.update(progress + it.getAddress());
if (pattern.matchesByte(byte, matchedBytes)) {
matchedBytes++;
if (matchedBytes == pattern.getSize()) {
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
results.push_back(Occurrence {
Region { occurrenceAddress, patternSize },
std::endian::native,
Occurrence::DecodeType::ASCII,
false,
fmt::format("[{}] {}", group.getName(), constant.name)
});
it.setAddress(occurrenceAddress);
matchedBytes = 0;
}
} else {
if (matchedBytes > 0)
it -= matchedBytes;
matchedBytes = 0;
}
}
} else {
std::vector<u8> data(patternSize);
for (u64 address = searchRegion.getStartAddress(); address < searchRegion.getEndAddress(); address += settings.alignment) {
reader.read(address, data.data(), data.size());
task.update(address);
bool match = true;
for (u32 i = 0; i < patternSize; i++) {
if (!pattern.matchesByte(data[i], i)) {
match = false;
break;
}
}
if (match)
results.push_back(Occurrence {
Region { address, patternSize },
std::endian::native,
Occurrence::DecodeType::ASCII,
false,
fmt::format("[] {}", group.getName(), constant.name)
});
}
}
progress += searchRegion.getSize();
}
}
@@ -592,9 +685,12 @@ namespace hex::plugin::builtin {
case Value:
m_foundOccurrences.get(provider) = searchValue(task, provider, searchRegion, settings.value);
break;
case Constants:
m_foundOccurrences.get(provider) = searchConstants(task, provider, searchRegion, settings.constants);
break;
}
m_sortedOccurrences.get(provider) = m_foundOccurrences.get(provider);
m_sortedOccurrences.get(provider).clear();
m_lastSelectedOccurrence = nullptr;
for (const auto &occurrence : m_foundOccurrences.get(provider))
@@ -636,16 +732,16 @@ namespace hex::plugin::builtin {
result += hex::encodeByteString({ bytes[i] });
break;
case Unsigned:
result += formatBytes<u64>(bytes, occurrence.endian);
result = formatBytes<u64>(bytes, occurrence.endian);
break;
case Signed:
result += formatBytes<i64>(bytes, occurrence.endian);
result = formatBytes<i64>(bytes, occurrence.endian);
break;
case Float:
result += formatBytes<float>(bytes, occurrence.endian);
result = formatBytes<float>(bytes, occurrence.endian);
break;
case Double:
result += formatBytes<double>(bytes, occurrence.endian);
result = formatBytes<double>(bytes, occurrence.endian);
break;
}
}
@@ -653,6 +749,9 @@ namespace hex::plugin::builtin {
case BinaryPattern:
result = hex::encodeByteString(bytes);
break;
case Constants:
result = occurrence.string;
break;
}
if (occurrence.region.getSize() > maxBytes)
@@ -958,6 +1057,16 @@ namespace hex::plugin::builtin {
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.find.constants"_lang)) {
auto &settings = m_searchSettings.constants;
mode = SearchSettings::Mode::Constants;
constexpr static u32 min = 1, max = 0x1000;
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
@@ -1075,6 +1184,11 @@ namespace hex::plugin::builtin {
auto sortSpecs = ImGui::TableGetSortSpecs();
if (m_sortedOccurrences->empty() && !m_foundOccurrences->empty()) {
currOccurrences = *m_foundOccurrences;
sortSpecs->SpecsDirty = true;
}
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")) {