From 0017cd2e40f19c35c07d510d5f4841c7a713adfb Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sat, 18 Feb 2023 22:20:02 +0100 Subject: [PATCH] feat: Added binary hex cell visualizer Closes #939 --- .../include/hex/api/content_registry.hpp | 3 +- lib/libimhex/include/hex/helpers/utils.hpp | 1 + lib/libimhex/source/api/content_registry.cpp | 33 ++++++++++++++- lib/libimhex/source/helpers/utils.cpp | 19 +++++++++ plugins/builtin/romfs/lang/en_US.json | 1 + .../builtin/source/content/data_inspector.cpp | 16 ++------ .../source/content/data_visualizers.cpp | 40 +++++++++++++++++-- plugins/builtin/source/ui/hex_editor.cpp | 4 +- 8 files changed, 98 insertions(+), 19 deletions(-) diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index 84dad16e8..4f8449f6c 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -417,7 +417,8 @@ namespace hex { protected: const static int TextInputFlags; - bool drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const; + bool drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const; + bool drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const; private: u16 m_bytesPerCell; u16 m_maxCharsPerCell; diff --git a/lib/libimhex/include/hex/helpers/utils.hpp b/lib/libimhex/include/hex/helpers/utils.hpp index 3419043ff..db00c7d63 100644 --- a/lib/libimhex/include/hex/helpers/utils.hpp +++ b/lib/libimhex/include/hex/helpers/utils.hpp @@ -44,6 +44,7 @@ namespace hex { std::string to_string(u128 value); std::string to_string(i128 value); + std::optional parseBinaryString(const std::string &string); std::string toByteString(u64 bytes); std::string makePrintable(u8 c); diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 0f2352655..6a4cdd1b6 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -607,7 +607,7 @@ namespace hex { const int DataVisualizer::TextInputFlags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll; - bool DataVisualizer::drawDefaultEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const { + bool DataVisualizer::drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const { struct UserData { u8 *data; i32 maxChars; @@ -636,6 +636,37 @@ namespace hex { return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape); } + bool DataVisualizer::drawDefaultTextEditingTextBox(u64 address, std::string &data, ImGuiInputTextFlags flags) const { + struct UserData { + std::string *data; + i32 maxChars; + + bool editingDone; + }; + + UserData userData = { + .data = &data, + .maxChars = this->getMaxCharsPerCell(), + + .editingDone = false + }; + + ImGui::PushID(reinterpret_cast(address)); + ImGui::InputText("##editing_input", data.data(), data.size() + 1, flags | TextInputFlags | ImGuiInputTextFlags_CallbackEdit, [](ImGuiInputTextCallbackData *data) -> int { + auto &userData = *reinterpret_cast(data->UserData); + + userData.data->resize(data->BufSize); + + if (data->BufTextLen >= userData.maxChars) + userData.editingDone = true; + + return 0; + }, &userData); + ImGui::PopID(); + + return userData.editingDone || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Escape); + } + void impl::addDataVisualizer(const std::string &unlocalizedName, DataVisualizer *visualizer) { getVisualizers().insert({ unlocalizedName, visualizer }); diff --git a/lib/libimhex/source/helpers/utils.cpp b/lib/libimhex/source/helpers/utils.cpp index 14e7d43a2..2e7ef58e5 100644 --- a/lib/libimhex/source/helpers/utils.cpp +++ b/lib/libimhex/source/helpers/utils.cpp @@ -66,6 +66,25 @@ namespace hex { return { data + index + 1 }; } + std::optional parseBinaryString(const std::string &string) { + if (string.empty()) + return std::nullopt; + + u8 byte = 0x00; + for (char c : string) { + byte <<= 1; + + if (c == '1') + byte |= 0b01; + else if (c == '0') + byte |= 0b00; + else + return std::nullopt; + } + + return byte; + } + std::string toByteString(u64 bytes) { double value = bytes; u8 unitIndex = 0; diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 4ab8fed4e..0830b6d0c 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -821,6 +821,7 @@ "hex.builtin.view.yara.reset": "Reset", "hex.builtin.view.yara.rule_added": "Yara rule added!", "hex.builtin.view.yara.whole_data": "Whole file matches!", + "hex.builtin.visualizer.binary": "Binary", "hex.builtin.visualizer.decimal.signed.16bit": "Decimal Signed (16 bits)", "hex.builtin.visualizer.decimal.signed.32bit": "Decimal Signed (32 bits)", "hex.builtin.visualizer.decimal.signed.64bit": "Decimal Signed (64 bits)", diff --git a/plugins/builtin/source/content/data_inspector.cpp b/plugins/builtin/source/content/data_inspector.cpp index f1548c49c..68f3bdbd0 100644 --- a/plugins/builtin/source/content/data_inspector.cpp +++ b/plugins/builtin/source/content/data_inspector.cpp @@ -146,19 +146,11 @@ namespace hex::plugin::builtin { value = value.substr(2); if (value.size() > 8) return { }; - u8 byte = 0x00; - for (char c : value) { - byte <<= 1; - if (c == '1') - byte |= 0b01; - else if (c == '0') - byte |= 0b00; - else - return { }; - } - - return { byte }; + if (auto result = hex::parseBinaryString(value); result.has_value()) + return { result.value() }; + else + return { }; } ); diff --git a/plugins/builtin/source/content/data_visualizers.cpp b/plugins/builtin/source/content/data_visualizers.cpp index 1bc305b68..298927b3d 100644 --- a/plugins/builtin/source/content/data_visualizers.cpp +++ b/plugins/builtin/source/content/data_visualizers.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -54,7 +53,7 @@ namespace hex::plugin::builtin { hex::unused(address, startedEditing); if (size == ByteCount) { - return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); + return drawDefaultScalarEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); } else return false; @@ -107,7 +106,7 @@ namespace hex::plugin::builtin { hex::unused(address, startedEditing); if (size == ByteCount) { - return drawDefaultEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); + return drawDefaultScalarEditingTextBox(address, getFormatString(upperCase), getImGuiDataType(), data, ImGuiInputTextFlags_CharsHexadecimal); } else return false; @@ -298,6 +297,39 @@ namespace hex::plugin::builtin { }; + class DataVisualizerBinary : public hex::ContentRegistry::HexEditor::DataVisualizer { + public: + DataVisualizerBinary() : DataVisualizer(1, 8) { } + + void draw(u64 address, const u8 *data, size_t size, bool) override { + hex::unused(address); + + if (size == 1) + ImGui::TextFormatted("{:08b}", *data); + } + + bool drawEditing(u64 address, u8 *data, size_t, bool, bool startedEditing) override { + hex::unused(address, startedEditing); + + if (startedEditing) { + this->m_inputBuffer = hex::format("{:08b}", *data); + } + + if (drawDefaultTextEditingTextBox(address, this->m_inputBuffer, ImGuiInputTextFlags_None)) { + hex::trim(this->m_inputBuffer); + if (auto result = hex::parseBinaryString(this->m_inputBuffer); result.has_value()) { + *data = result.value(); + return true; + } + } + + return false; + } + + private: + std::string m_inputBuffer; + }; + void registerDataVisualizers() { ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.8bit"); ContentRegistry::HexEditor::addDataVisualizer>("hex.builtin.visualizer.hexadecimal.16bit"); @@ -320,6 +352,8 @@ namespace hex::plugin::builtin { ContentRegistry::HexEditor::addDataVisualizer("hex.builtin.visualizer.rgba8"); ContentRegistry::HexEditor::addDataVisualizer("hex.builtin.visualizer.hexii"); + + ContentRegistry::HexEditor::addDataVisualizer("hex.builtin.visualizer.binary"); } } \ No newline at end of file diff --git a/plugins/builtin/source/ui/hex_editor.cpp b/plugins/builtin/source/ui/hex_editor.cpp index 3b8d84a9f..25dba1dcf 100644 --- a/plugins/builtin/source/ui/hex_editor.cpp +++ b/plugins/builtin/source/ui/hex_editor.cpp @@ -300,6 +300,8 @@ namespace hex::plugin::builtin::ui { if (!this->m_editingAddress.has_value()) this->m_editingCellType = CellType::None; + + this->m_enteredEditingMode = false; } } @@ -678,8 +680,6 @@ namespace hex::plugin::builtin::ui { ImGui::EndTable(); } ImGui::PopStyleVar(); - - this->m_enteredEditingMode = false; } void HexEditor::drawFooter(const ImVec2 &size) {