feat: Added Hex Editor "Decode as Encoding" option

This commit is contained in:
WerWolv
2025-08-28 23:22:26 +02:00
parent ff70518882
commit bf08ed563a
6 changed files with 90 additions and 11 deletions

View File

@@ -27,10 +27,11 @@ namespace hex {
EncodingFile& operator=(const EncodingFile &other);
EncodingFile& operator=(EncodingFile &&other) noexcept;
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<u8> buffer) const;
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<const u8> buffer) const;
[[nodiscard]] u64 getEncodingLengthFor(std::span<u8> buffer) const;
[[nodiscard]] u64 getShortestSequence() const { return m_shortestSequence; }
[[nodiscard]] u64 getLongestSequence() const { return m_longestSequence; }
[[nodiscard]] std::string decodeAll(std::span<const u8> buffer) const;
[[nodiscard]] bool valid() const { return m_valid; }

View File

@@ -88,7 +88,7 @@ namespace hex {
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<u8> buffer) const {
std::pair<std::string_view, size_t> EncodingFile::getEncodingFor(std::span<const u8> buffer) const {
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
const auto &[size, mapping] = *riter;
@@ -116,6 +116,19 @@ namespace hex {
return 1;
}
std::string EncodingFile::decodeAll(std::span<const u8> buffer) const {
std::string result;
while (!buffer.empty()) {
const auto [character, size] = getEncodingFor(buffer);
result += character;
buffer = buffer.subspan(size);
}
return result;
}
void EncodingFile::parse(const std::string &content) {
m_tableContent = content;
for (const auto &line : wolv::util::splitString(m_tableContent, "\n")) {

View File

@@ -26,6 +26,9 @@ namespace hex::plugin::builtin {
[[nodiscard]] virtual bool canBePinned() const { return false; }
[[nodiscard]] bool isPinned() const { return m_isPinned; }
void setPinned(const bool pinned) { m_isPinned = pinned; }
[[nodiscard]] virtual ImGuiWindowFlags getFlags() const { return ImGuiWindowFlags_AlwaysAutoResize; }
private:
bool m_isPinned = false;
};

View File

@@ -877,6 +877,8 @@
"hex.builtin.view.hex_editor.menu.edit.select_all": "Select all",
"hex.builtin.view.hex_editor.menu.edit.set_base": "Set Base Address...",
"hex.builtin.view.hex_editor.menu.edit.set_page_size": "Set Page Size...",
"hex.builtin.view.hex_editor.menu.edit.decode_as_text": "Decode as Encoding",
"hex.builtin.view.hex_editor.menu.edit.decoded_string.popup.title": "Decoded Text",
"hex.builtin.view.hex_editor.menu.file.goto": "Go to address...",
"hex.builtin.view.hex_editor.menu.file.skip_until": "Skip Until",
"hex.builtin.view.hex_editor.menu.file.skip_until.previous_differing_byte": "Previous Differing Byte",

View File

@@ -689,14 +689,7 @@ namespace hex::plugin::builtin {
std::vector<u8> stringBuffer(std::min<size_t>(currSelection->size, 0x1000), 0x00);
ImHexApi::Provider::get()->read(currSelection->address, stringBuffer.data(), stringBuffer.size());
u64 offset = 0;
while (offset < stringBuffer.size()) {
const auto [character, size] = encodingFile.getEncodingFor(std::span(stringBuffer).subspan(offset));
value += character;
offset += size;
}
copyValue = value;
copyValue = value = encodingFile.decodeAll(stringBuffer);
if (value.size() > MaxStringLength) {
value.resize(MaxStringLength);

View File

@@ -32,6 +32,7 @@
#include <content/popups/hex_editor/popup_hex_editor_find.hpp>
#include <pl/patterns/pattern.hpp>
#include <hex/helpers/menu_items.hpp>
#include <ui/text_editor.hpp>
#include <wolv/literals.hpp>
using namespace std::literals::string_literals;
@@ -545,6 +546,46 @@ namespace hex::plugin::builtin {
std::function<void(const Region &selection, bool selectionCheck)> m_pasteCallback;
};
class PopupDecodedString final : public ViewHexEditor::Popup {
public:
explicit PopupDecodedString(std::string decodedString) : m_decodedString(std::move(decodedString)) {
}
void draw(ViewHexEditor *) override {
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
auto text = wolv::util::trim(wolv::util::wrapMonospacedString(
m_decodedString,
ImGui::CalcTextSize("M").x,
std::max(100_scaled, ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize - ImGui::GetStyle().FrameBorderSize)
));
ImGui::InputTextMultiline(
"##",
text.data(),
text.size() + 1,
ImGui::GetContentRegionAvail(),
ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll
);
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
}
[[nodiscard]] UnlocalizedString getTitle() const override {
return "hex.builtin.view.hex_editor.menu.edit.decoded_string.popup.title";
}
[[nodiscard]] bool canBePinned() const override { return true; }
[[nodiscard]] ImGuiWindowFlags getFlags() const override { return ImGuiWindowFlags_None; }
private:
std::string m_decodedString;
ui::TextEditor m_editor;
};
/* Hex Editor */
ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name", ICON_VS_FILE_BINARY) {
@@ -711,7 +752,7 @@ namespace hex::plugin::builtin {
}
if (m_currPopup != nullptr) {
if (ImGui::Begin(fmt::format("##{}", m_currPopup->getTitle().get()).c_str(), &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
if (ImGui::Begin(fmt::format("##{}", m_currPopup->getTitle().get()).c_str(), &open, m_currPopup->getFlags() | ImGuiWindowFlags_NoDocking)) {
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
this->closePopup();
} else {
@@ -1666,6 +1707,32 @@ namespace hex::plugin::builtin {
},
[] { return ImHexApi::HexEditor::isSelectionValid() && ImHexApi::Provider::isValid(); },
this);
/* Decode as Text */
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.decode_as_text" }, ICON_VS_CHAT_SPARKLE, 1960, Shortcut::None,
[this] {
const auto selection = ImHexApi::HexEditor::getSelection();
TaskManager::createTask("", TaskManager::NoProgress, [this, selection] {
const auto &customEncoding = this->m_hexEditor.getCustomEncoding();
if (!customEncoding.has_value())
return;
std::vector<u8> buffer(selection->getSize());
selection->getProvider()->read(selection->getStartAddress(), buffer.data(), buffer.size());
auto decodedString = customEncoding->decodeAll(buffer);
TaskManager::doLater([this, decodedString = std::move(decodedString)]() mutable {
this->openPopup<PopupDecodedString>(std::move(decodedString));
});
});
},
[this] {
return ImHexApi::HexEditor::isSelectionValid() &&
ImHexApi::Provider::isValid() &&
this->m_hexEditor.getCustomEncoding().has_value();
},
this);
}
}