diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index a0624732c..9dfe89e3e 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -22,13 +22,13 @@ using ImGuiInputTextFlags = int; struct ImColor; namespace hex { - class View; class Shortcut; namespace dp { class Node; } + namespace prv { class Provider; } @@ -43,19 +43,16 @@ namespace hex { plugins when needed. */ namespace ContentRegistry { - /* Settings Registry. Allows adding of new entries into the ImHex preferences window. */ namespace Settings { - namespace Widgets { - class Widget { public: virtual ~Widget() = default; - virtual bool draw(const std::string &name) = 0; + virtual bool draw(const std::string& name) = 0; - virtual void load(const nlohmann::json &data) = 0; + virtual void load(const nlohmann::json& data) = 0; virtual nlohmann::json store() = 0; class Interface { @@ -80,7 +77,7 @@ namespace hex { return *this; } - Interface& setTooltip(const std::string &tooltip) { + Interface& setTooltip(const std::string& tooltip) { this->m_tooltip = tooltip; return *this; @@ -130,13 +127,13 @@ namespace hex { Interface m_interface = Interface(this); }; - class Checkbox : public Widget { + class Checkbox : public Widget { public: - explicit Checkbox(bool defaultValue) : m_value(defaultValue) { } + explicit Checkbox(bool defaultValue) : m_value(defaultValue) {} - bool draw(const std::string &name) override; + bool draw(const std::string& name) override; - void load(const nlohmann::json &data) override; + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] bool isChecked() const { return this->m_value; } @@ -144,12 +141,13 @@ namespace hex { private: bool m_value; }; + class SliderInteger : public Widget { public: - SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) { } - bool draw(const std::string &name) override; + SliderInteger(i32 defaultValue, i32 min, i32 max) : m_value(defaultValue), m_min(min), m_max(max) {} + bool draw(const std::string& name) override; - void load(const nlohmann::json &data) override; + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] i32 getValue() const { return this->m_value; } @@ -158,12 +156,15 @@ namespace hex { int m_value; i32 m_min, m_max; }; - class SliderFloat : public Widget { - public: - SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), m_max(max) { } - bool draw(const std::string &name) override; - void load(const nlohmann::json &data) override; + class SliderFloat : public Widget { + public: + SliderFloat(float defaultValue, float min, float max) : m_value(defaultValue), m_min(min), + m_max(max) {} + + bool draw(const std::string& name) override; + + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] float getValue() const { return this->m_value; } @@ -172,13 +173,14 @@ namespace hex { float m_value; float m_min, m_max; }; - class ColorPicker : public Widget { + + class ColorPicker : public Widget { public: explicit ColorPicker(ImColor defaultColor); - bool draw(const std::string &name) override; + bool draw(const std::string& name) override; - void load(const nlohmann::json &data) override; + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] ImColor getColor() const; @@ -186,13 +188,18 @@ namespace hex { private: std::array m_value{}; }; - class DropDown : public Widget { + + class DropDown : public Widget { public: - explicit DropDown(const std::vector &items, const std::vector &settingsValues, const nlohmann::json &defaultItem) : m_items(items), m_settingsValues(settingsValues), m_defaultItem(defaultItem) { } + explicit DropDown(const std::vector& items, + const std::vector& settingsValues, + const nlohmann::json& defaultItem) : m_items(items), + m_settingsValues(settingsValues), + m_defaultItem(defaultItem) {} - bool draw(const std::string &name) override; + bool draw(const std::string& name) override; - void load(const nlohmann::json &data) override; + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] @@ -205,13 +212,14 @@ namespace hex { int m_value = -1; }; - class TextBox : public Widget { + + class TextBox : public Widget { public: - explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) { } + explicit TextBox(std::string defaultValue) : m_value(std::move(defaultValue)) {} - bool draw(const std::string &name) override; + bool draw(const std::string& name) override; - void load(const nlohmann::json &data) override; + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] @@ -220,11 +228,12 @@ namespace hex { private: std::string m_value; }; - class FilePicker : public Widget { - public: - bool draw(const std::string &name) override; - void load(const nlohmann::json &data) override; + class FilePicker : public Widget { + public: + bool draw(const std::string& name) override; + + void load(const nlohmann::json& data) override; nlohmann::json store() override; [[nodiscard]] @@ -233,12 +242,9 @@ namespace hex { private: std::string m_value; }; - - } namespace impl { - struct Entry { std::string unlocalizedName; std::unique_ptr widget; @@ -259,39 +265,44 @@ namespace hex { void store(); void clear(); - std::vector &getSettings(); - nlohmann::json& getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue); - nlohmann::json &getSettingsData(); + std::vector& getSettings(); + nlohmann::json& getSetting(const std::string& unlocalizedCategory, const std::string& unlocalizedName, + const nlohmann::json& defaultValue); + nlohmann::json& getSettingsData(); - Widgets::Widget* add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, std::unique_ptr &&widget); + Widgets::Widget* add(const std::string& unlocalizedCategory, const std::string& unlocalizedSubCategory, + const std::string& unlocalizedName, std::unique_ptr&& widget); } - template T> - Widgets::Widget::Interface& add(const std::string &unlocalizedCategory, const std::string &unlocalizedSubCategory, const std::string &unlocalizedName, auto && ... args) { + template T> + Widgets::Widget::Interface& add(const std::string& unlocalizedCategory, + const std::string& unlocalizedSubCategory, + const std::string& unlocalizedName, auto&&... args) { return impl::add( - unlocalizedCategory, - unlocalizedSubCategory, - unlocalizedName, - std::make_unique(std::forward(args)...) - )->getInterface(); + unlocalizedCategory, + unlocalizedSubCategory, + unlocalizedName, + std::make_unique(std::forward(args)...) + )->getInterface(); } - void setCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedDescription); + void setCategoryDescription(const std::string& unlocalizedCategory, + const std::string& unlocalizedDescription); - nlohmann::json read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &defaultValue); - void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const nlohmann::json &value); + nlohmann::json read(const std::string& unlocalizedCategory, const std::string& unlocalizedName, + const nlohmann::json& defaultValue); + void write(const std::string& unlocalizedCategory, const std::string& unlocalizedName, + const nlohmann::json& value); } /* Command Palette Command Registry. Allows adding of new commands to the command palette */ namespace CommandPaletteCommands { - enum class Type : u32 { SymbolCommand, KeywordCommand }; namespace impl { - struct QueryResult { std::string name; std::function callback; @@ -299,7 +310,7 @@ namespace hex { using DisplayCallback = std::function; using ExecuteCallback = std::function; - using QueryCallback = std::function(std::string)>; + using QueryCallback = std::function(std::string)>; struct Entry { Type type; @@ -316,9 +327,8 @@ namespace hex { DisplayCallback displayCallback; }; - std::vector &getEntries(); - std::vector &getHandlers(); - + std::vector& getEntries(); + std::vector& getHandlers(); } /** @@ -331,10 +341,10 @@ namespace hex { */ void add( Type type, - const std::string &command, - const std::string &unlocalizedDescription, - const impl::DisplayCallback &displayCallback, - const impl::ExecuteCallback &executeCallback = [](auto) {}); + const std::string& command, + const std::string& unlocalizedDescription, + const impl::DisplayCallback& displayCallback, + const impl::ExecuteCallback& executeCallback = [](auto) {}); /** * @brief Adds a new command handler to the command palette @@ -345,17 +355,16 @@ namespace hex { */ void addHandler( Type type, - const std::string &command, - const impl::QueryCallback &queryCallback, - const impl::DisplayCallback &displayCallback); + const std::string& command, + const impl::QueryCallback& queryCallback, + const impl::DisplayCallback& displayCallback); } /* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */ namespace PatternLanguage { - namespace impl { - - using VisualizerFunctionCallback = std::function)>; + using VisualizerFunctionCallback = std::function)>; struct FunctionDefinition { pl::api::Namespace ns; @@ -372,11 +381,10 @@ namespace hex { VisualizerFunctionCallback callback; }; - std::map &getVisualizers(); - std::map &getInlineVisualizers(); - std::map &getPragmas(); - std::vector &getFunctions(); - + std::map& getVisualizers(); + std::map& getInlineVisualizers(); + std::map& getPragmas(); + std::vector& getFunctions(); } /** @@ -396,14 +404,14 @@ namespace hex { * @param runtime The pattern language runtime to configure * @param provider The provider to use for data access */ - void configureRuntime(pl::PatternLanguage &runtime, prv::Provider *provider); + void configureRuntime(pl::PatternLanguage& runtime, prv::Provider *provider); /** * @brief Adds a new pragma to the pattern language * @param name The name of the pragma * @param handler The handler that will be called when the pragma is encountered */ - void addPragma(const std::string &name, const pl::api::PragmaHandler &handler); + void addPragma(const std::string& name, const pl::api::PragmaHandler& handler); /** * @brief Adds a new function to the pattern language @@ -412,7 +420,8 @@ namespace hex { * @param parameterCount The amount of parameters the function takes * @param func The function callback */ - void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); + void addFunction(const pl::api::Namespace& ns, const std::string& name, + pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback& func); /** * @brief Adds a new dangerous function to the pattern language @@ -422,7 +431,9 @@ namespace hex { * @param parameterCount The amount of parameters the function takes * @param func The function callback */ - void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); + void addDangerousFunction(const pl::api::Namespace& ns, const std::string& name, + pl::api::FunctionParameterCount parameterCount, + const pl::api::FunctionCallback& func); /** * @brief Adds a new visualizer to the pattern language @@ -431,7 +442,8 @@ namespace hex { * @param function The function callback * @param parameterCount The amount of parameters the function takes */ - void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); + void addVisualizer(const std::string& name, const impl::VisualizerFunctionCallback& function, + pl::api::FunctionParameterCount parameterCount); /** * @brief Adds a new inline visualizer to the pattern language @@ -440,18 +452,15 @@ namespace hex { * @param function The function callback * @param parameterCount The amount of parameters the function takes */ - void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); - + void addInlineVisualizer(const std::string& name, const impl::VisualizerFunctionCallback& function, + pl::api::FunctionParameterCount parameterCount); } /* View Registry. Allows adding of new windows */ namespace Views { - namespace impl { - - void add(std::unique_ptr &&view); - std::map> &getEntries(); - + void add(std::unique_ptr&& view); + std::map>& getEntries(); } /** @@ -460,8 +469,8 @@ namespace hex { * @tparam Args Arguments types * @param args Arguments passed to the constructor of the view */ - template T, typename... Args> - void add(Args &&...args) { + template T, typename... Args> + void add(Args&&... args) { return impl::add(std::make_unique(std::forward(args)...)); } @@ -470,14 +479,12 @@ namespace hex { * @param unlocalizedName The unlocalized name of the view * @return The view if it exists, nullptr otherwise */ - View* getViewByName(const std::string &unlocalizedName); + View* getViewByName(const std::string& unlocalizedName); } /* Tools Registry. Allows adding new entries to the tools window */ namespace Tools { - namespace impl { - using Callback = std::function; struct Entry { @@ -486,8 +493,7 @@ namespace hex { bool detached; }; - std::vector &getEntries(); - + std::vector& getEntries(); } /** @@ -495,24 +501,22 @@ namespace hex { * @param unlocalizedName The unlocalized name of the tool * @param function The function that will be called to draw the tool */ - void add(const std::string &unlocalizedName, const impl::Callback &function); + void add(const std::string& unlocalizedName, const impl::Callback& function); } /* Data Inspector Registry. Allows adding of new types to the data inspector */ namespace DataInspector { - - enum class NumberDisplayStyle - { + enum class NumberDisplayStyle { Decimal, Hexadecimal, Octal }; namespace impl { - - using DisplayFunction = std::function; - using EditingFunction = std::function(std::string, std::endian)>; - using GeneratorFunction = std::function &, std::endian, NumberDisplayStyle)>; + using DisplayFunction = std::function; + using EditingFunction = std::function(std::string, std::endian)>; + using GeneratorFunction = std::function&, std::endian, + NumberDisplayStyle)>; struct Entry { std::string unlocalizedName; @@ -522,8 +526,7 @@ namespace hex { std::optional editingFunction; }; - std::vector &getEntries(); - + std::vector& getEntries(); } /** @@ -533,7 +536,9 @@ namespace hex { * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ - void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt); + void add(const std::string& unlocalizedName, size_t requiredSize, + impl::GeneratorFunction displayGeneratorFunction, + std::optional editingFunction = std::nullopt); /** * @brief Adds a new entry to the data inspector @@ -543,14 +548,14 @@ namespace hex { * @param displayGeneratorFunction The function that will be called to generate the display function * @param editingFunction The function that will be called to edit the data */ - void add(const std::string &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional editingFunction = std::nullopt); + void add(const std::string& unlocalizedName, size_t requiredSize, size_t maxSize, + impl::GeneratorFunction displayGeneratorFunction, + std::optional editingFunction = std::nullopt); } /* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */ namespace DataProcessorNode { - namespace impl { - using CreatorFunction = std::function()>; struct Entry { @@ -559,9 +564,9 @@ namespace hex { CreatorFunction creatorFunction; }; - void add(const Entry &entry); + void add(const Entry& entry); - std::vector &getEntries(); + std::vector& getEntries(); } @@ -573,12 +578,13 @@ namespace hex { * @param unlocalizedName The unlocalized name of the node * @param args Arguments passed to the constructor of the node */ - template T, typename... Args> - void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args &&...args) { - add(impl::Entry { + template T, typename... Args> + void add(const std::string& unlocalizedCategory, const std::string& unlocalizedName, Args&&... args) { + add(impl::Entry{ unlocalizedCategory.c_str(), unlocalizedName.c_str(), - [=, ...args = std::forward(args)] mutable { + [=, ...args = std::forward(args)] mutable + { auto node = std::make_unique(std::forward(args)...); node->setUnlocalizedName(unlocalizedName); return node; @@ -590,36 +596,29 @@ namespace hex { * @brief Adds a separator to the data processor right click menu */ void addSeparator(); - } /* Language Registry. Allows together with the Lang class and the _lang user defined literal to add new languages */ namespace Language { - /** * @brief Loads localization information from json data * @param data The language data */ - void addLocalization(const nlohmann::json &data); + void addLocalization(const nlohmann::json& data); namespace impl { - - std::map &getLanguages(); - std::map> &getLanguageDefinitions(); - + std::map& getLanguages(); + std::map>& getLanguageDefinitions(); } - } /* Interface Registry. Allows adding new items to various interfaces */ namespace Interface { - namespace impl { - - using DrawCallback = std::function; - using MenuCallback = std::function; - using EnabledCallback = std::function; - using ClickCallback = std::function; + using DrawCallback = std::function; + using MenuCallback = std::function; + using EnabledCallback = std::function; + using ClickCallback = std::function; struct MainMenuItem { std::string unlocalizedName; @@ -648,15 +647,14 @@ namespace hex { constexpr static auto SeparatorValue = "$SEPARATOR$"; constexpr static auto SubMenuValue = "$SUBMENU$"; - std::multimap &getMainMenuItems(); - std::multimap &getMenuItems(); - - std::vector &getWelcomeScreenEntries(); - std::vector &getFooterItems(); - std::vector &getToolbarItems(); - std::vector &getSidebarItems(); - std::vector &getTitleBarButtons(); + std::multimap& getMainMenuItems(); + std::multimap& getMenuItems(); + std::vector& getWelcomeScreenEntries(); + std::vector& getFooterItems(); + std::vector& getToolbarItems(); + std::vector& getSidebarItems(); + std::vector& getTitleBarButtons(); } /** @@ -664,7 +662,7 @@ namespace hex { * @param unlocalizedName The unlocalized name of the entry * @param priority The priority of the entry. Lower values are displayed first */ - void registerMainMenuItem(const std::string &unlocalizedName, u32 priority); + void registerMainMenuItem(const std::string& unlocalizedName, u32 priority); /** * @brief Adds a new main menu entry @@ -675,7 +673,9 @@ namespace hex { * @param enabledCallback The function to call to determine if the entry is enabled * @param view The view to use for the entry. If nullptr, the shortcut will work globally */ - void addMenuItem(const std::vector &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr); + void addMenuItem(const std::vector& unlocalizedMainMenuNames, u32 priority, + const Shortcut& shortcut, const impl::MenuCallback& function, + const impl::EnabledCallback& enabledCallback = [] { return true; }, View *view = nullptr); /** * @brief Adds a new main menu sub-menu entry @@ -684,7 +684,9 @@ namespace hex { * @param function The function to call when the entry is clicked * @param enabledCallback The function to call to determine if the entry is enabled */ - void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }); + void addMenuItemSubMenu(std::vector unlocalizedMainMenuNames, u32 priority, + const impl::MenuCallback& function, + const impl::EnabledCallback& enabledCallback = [] { return true; }); /** * @brief Adds a new main menu separator @@ -698,19 +700,19 @@ namespace hex { * @brief Adds a new welcome screen entry * @param function The function to call to draw the entry */ - void addWelcomeScreenEntry(const impl::DrawCallback &function); + void addWelcomeScreenEntry(const impl::DrawCallback& function); /** * @brief Adds a new footer item * @param function The function to call to draw the item */ - void addFooterItem(const impl::DrawCallback &function); + void addFooterItem(const impl::DrawCallback& function); /** * @brief Adds a new toolbar item * @param function The function to call to draw the item */ - void addToolbarItem(const impl::DrawCallback &function); + void addToolbarItem(const impl::DrawCallback& function); /** * @brief Adds a new sidebar item @@ -718,7 +720,8 @@ namespace hex { * @param function The function to call to draw the item * @param enabledCallback The function */ - void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback = []{ return true; }); + void addSidebarItem(const std::string& icon, const impl::DrawCallback& function, + const impl::EnabledCallback& enabledCallback = [] { return true; }); /** * @brief Adds a new title bar button @@ -726,22 +729,19 @@ namespace hex { * @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param function The function to call when the button is clicked */ - void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function); - + void addTitleBarButton(const std::string& icon, const std::string& unlocalizedTooltip, + const impl::ClickCallback& function); } /* Provider Registry. Allows adding new data providers to be created from the UI */ namespace Provider { - namespace impl { - - void addProviderName(const std::string &unlocalizedName); + void addProviderName(const std::string& unlocalizedName); using ProviderCreationFunction = prv::Provider*(*)(); - void add(const std::string &typeName, ProviderCreationFunction creationFunction); - - std::vector &getEntries(); + void add(const std::string& typeName, ProviderCreationFunction creationFunction); + std::vector& getEntries(); } /** @@ -749,7 +749,7 @@ namespace hex { * @tparam T The provider type that extends hex::prv::Provider * @param addToList Whether to display the provider in the Other Providers list in the welcome screen and File menu */ - template T> + template T> void add(bool addToList = true) { auto typeName = T().getTypeName(); @@ -760,22 +760,19 @@ namespace hex { if (addToList) impl::addProviderName(typeName); } - } /* Data Formatter Registry. Allows adding formatters that are used in the Copy-As menu for example */ namespace DataFormatter { - namespace impl { - using Callback = std::function; + struct Entry { std::string unlocalizedName; Callback callback; }; - std::vector &getEntries(); - + std::vector& getEntries(); } @@ -784,23 +781,20 @@ namespace hex { * @param unlocalizedName The unlocalized name of the formatter * @param callback The function to call to format the data */ - void add(const std::string &unlocalizedName, const impl::Callback &callback); - + void add(const std::string& unlocalizedName, const impl::Callback& callback); } /* File Handler Registry. Allows adding handlers for opening files specific file types */ namespace FileHandler { - namespace impl { - using Callback = std::function; + struct Entry { std::vector extensions; Callback callback; }; - std::vector &getEntries(); - + std::vector& getEntries(); } /** @@ -808,19 +802,17 @@ namespace hex { * @param extensions The file extensions to handle * @param callback The function to call to handle the file */ - void add(const std::vector &extensions, const impl::Callback &callback); - + void add(const std::vector& extensions, const impl::Callback& callback); } /* Hex Editor Registry. Allows adding new functionality to the hex editor */ namespace HexEditor { - class DataVisualizer { public: DataVisualizer(std::string unlocalizedName, u16 bytesPerCell, u16 maxCharsPerCell) : m_unlocalizedName(std::move(unlocalizedName)), m_bytesPerCell(bytesPerCell), - m_maxCharsPerCell(maxCharsPerCell) { } + m_maxCharsPerCell(maxCharsPerCell) {} virtual ~DataVisualizer() = default; @@ -835,8 +827,9 @@ namespace hex { protected: const static int TextInputFlags; - bool drawDefaultScalarEditingTextBox(u64 address, const char *format, ImGuiDataType dataType, u8 *data, ImGuiInputTextFlags flags) const; - bool drawDefaultTextEditingTextBox(u64 address, std::string &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: std::string m_unlocalizedName; @@ -845,11 +838,9 @@ namespace hex { }; namespace impl { + void addDataVisualizer(std::shared_ptr&& visualizer); - void addDataVisualizer(std::shared_ptr &&visualizer); - - std::vector> &getVisualizers(); - + std::vector>& getVisualizers(); } /** @@ -857,8 +848,8 @@ namespace hex { * @tparam T The data visualizer type that extends hex::DataVisualizer * @param args The arguments to pass to the constructor of the data visualizer */ - template T, typename... Args> - void addDataVisualizer(Args &&...args) { + template T, typename... Args> + void addDataVisualizer(Args&&... args) { return impl::addDataVisualizer(std::make_shared(std::forward(args)...)); } @@ -867,13 +858,11 @@ namespace hex { * @param unlocalizedName Unlocalized name of the data visualizer * @return The data visualizer, or nullptr if it doesn't exist */ - std::shared_ptr getVisualizerByName(const std::string &unlocalizedName); - + std::shared_ptr getVisualizerByName(const std::string& unlocalizedName); } /* Hash Registry. Allows adding new hashes to the Hash view */ namespace Hashes { - class Hash { public: explicit Hash(std::string unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {} @@ -884,13 +873,11 @@ namespace hex { using Callback = std::function(const Region&, prv::Provider *)>; Function(Hash *type, std::string name, Callback callback) - : m_type(type), m_name(std::move(name)), m_callback(std::move(callback)) { + : m_type(type), m_name(std::move(name)), m_callback(std::move(callback)) {} - } - - [[nodiscard]] Hash *getType() { return this->m_type; } - [[nodiscard]] const Hash *getType() const { return this->m_type; } - [[nodiscard]] const std::string &getName() const { return this->m_name; } + [[nodiscard]] Hash* getType() { return this->m_type; } + [[nodiscard]] const Hash* getType() const { return this->m_type; } + [[nodiscard]] const std::string& getName() const { return this->m_name; } const std::vector& get(const Region& region, prv::Provider *provider) { if (this->m_cache.empty()) { @@ -912,19 +899,19 @@ namespace hex { std::vector m_cache; }; - virtual void draw() { } + virtual void draw() {} [[nodiscard]] virtual Function create(std::string name) = 0; [[nodiscard]] virtual nlohmann::json store() const = 0; - virtual void load(const nlohmann::json &json) = 0; + virtual void load(const nlohmann::json& json) = 0; - [[nodiscard]] const std::string &getUnlocalizedName() const { + [[nodiscard]] const std::string& getUnlocalizedName() const { return this->m_unlocalizedName; } protected: - [[nodiscard]] Function create(const std::string &name, const Function::Callback &callback) { - return { this, name, callback }; + [[nodiscard]] Function create(const std::string& name, const Function::Callback& callback) { + return {this, name, callback}; } private: @@ -932,10 +919,9 @@ namespace hex { }; namespace impl { + std::vector>& getHashes(); - std::vector> &getHashes(); - - void add(std::unique_ptr &&hash); + void add(std::unique_ptr&& hash); } @@ -944,16 +930,14 @@ namespace hex { * @tparam T The hash type that extends hex::Hash * @param args The arguments to pass to the constructor of the hash */ - template - void add(Args && ... args) { + template + void add(Args&&... args) { impl::add(std::make_unique(std::forward(args)...)); } - } /* Background Service Registry. Allows adding new background services */ namespace BackgroundServices { - namespace impl { using Callback = std::function; @@ -962,62 +946,95 @@ namespace hex { std::jthread thread; }; - std::vector &getServices(); + std::vector& getServices(); void stopServices(); } - void registerService(const std::string &unlocalizedName, const impl::Callback &callback); + void registerService(const std::string& unlocalizedName, const impl::Callback& callback); } /* Network Communication Interface Registry. Allows adding new communication interface endpoints */ namespace CommunicationInterface { - namespace impl { - using NetworkCallback = std::function; + using NetworkCallback = std::function; - std::map &getNetworkEndpoints(); + std::map& getNetworkEndpoints(); } - void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback); - + void registerNetworkEndpoint(const std::string& endpoint, const impl::NetworkCallback& callback); } /* Experiments Registry. Allows adding new experiments */ namespace Experiments { - namespace impl { - struct Experiment { std::string unlocalizedName, unlocalizedDescription; bool enabled; }; - std::map &getExperiments(); + std::map& getExperiments(); } - void addExperiment(const std::string &experimentName, const std::string &unlocalizedName, const std::string &unlocalizedDescription = ""); - void enableExperiement(const std::string &experimentName, bool enabled); + void addExperiment(const std::string& experimentName, const std::string& unlocalizedName, + const std::string& unlocalizedDescription = ""); + void enableExperiement(const std::string& experimentName, bool enabled); - [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName); + [[nodiscard]] bool isExperimentEnabled(const std::string& experimentName); } + /* Reports Registry. Allows adding new report formatters */ namespace Reports { - namespace impl { - - using Callback = std::function; + using Callback = std::function; struct ReportGenerator { Callback callback; }; std::vector& getGenerators(); - } void addReportProvider(impl::Callback callback); + } + /* Disassembler Registry. Allows adding new disassembler architectures */ + namespace Disassembler { + struct Instruction { + Region region; + std::string mnemonic; + std::string operands; + + std::optional jumpDestination; + }; + + class Architecture { + public: + explicit Architecture(std::string unlocalizedName) : m_unlocalizedName(std::move(unlocalizedName)) {} + virtual ~Architecture() = default; + + [[nodiscard]] const std::string& getUnlocalizedName() const { + return this->m_unlocalizedName; + } + + virtual void drawConfigInterface() = 0; + virtual std::vector disassemble(prv::Provider *provider, const Region& region) = 0; + + private: + std::string m_unlocalizedName; + }; + + namespace impl { + + std::vector>& getArchitectures(); + + void add(std::unique_ptr &&architecture); + + } + + template T> + void add(auto && ... args) { + impl::add(std::make_unique(std::forward(args)...)); + } } } - } diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 750b4cd8f..ee4baa9a1 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -1116,4 +1116,23 @@ namespace hex { } + namespace ContentRegistry::Disassembler { + + namespace impl { + + + std::vector>& getArchitectures() { + static std::vector> architectures; + + return architectures; + } + + void add(std::unique_ptr &&architecture) { + getArchitectures().emplace_back(std::move(architecture)); + } + + } + + } + } diff --git a/main/gui/source/init/tasks.cpp b/main/gui/source/init/tasks.cpp index 64e20291e..2cb5f0bc4 100644 --- a/main/gui/source/init/tasks.cpp +++ b/main/gui/source/init/tasks.cpp @@ -129,6 +129,7 @@ namespace hex::init { ContentRegistry::Experiments::impl::getExperiments().clear(); ContentRegistry::Reports::impl::getGenerators().clear(); + ContentRegistry::Disassembler::impl::getArchitectures().clear(); LayoutManager::reset(); diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index 2a728b1f1..4724ca6a2 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -45,6 +45,7 @@ add_imhex_plugin( source/content/report_generators.cpp source/content/init_tasks.cpp source/content/fonts.cpp + source/content/disassemblers.cpp source/content/data_processor_nodes/basic_nodes.cpp source/content/data_processor_nodes/control_nodes.cpp diff --git a/plugins/builtin/include/content/views/view_disassembler.hpp b/plugins/builtin/include/content/views/view_disassembler.hpp index 4a4d93476..63dca9079 100644 --- a/plugins/builtin/include/content/views/view_disassembler.hpp +++ b/plugins/builtin/include/content/views/view_disassembler.hpp @@ -1,46 +1,36 @@ #pragma once -#include - +#include #include -#include - -#include - -#include -#include namespace hex::plugin::builtin { - struct Disassembly { - u64 address; - u64 offset; - size_t size; - std::string bytes; - std::string mnemonic; - std::string operators; - }; - class ViewDisassembler : public View::Window { public: explicit ViewDisassembler(); - ~ViewDisassembler() override; void drawContent() override; + ImGuiWindowFlags getWindowFlags() const override { + return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + } + private: - TaskHolder m_disassemblerTask; + struct DisassemblyLine { + ImHexApi::HexEditor::ProviderRegion region; + std::string bytes; + std::string mnemonic; + std::string operands; - u64 m_baseAddress = 0; - ui::RegionType m_range = ui::RegionType::EntireData; - Region m_codeRegion = { 0, 0 }; + std::optional jumpDestination; + ImVec2 linePos; + }; - Architecture m_architecture = Architecture::ARM; - cs_mode m_mode = cs_mode(0); + void addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction); - std::vector m_disassembly; - - void disassemble(); + private: + PerProvider> m_lines; + ContentRegistry::Disassembler::Architecture *m_currArchitecture = nullptr; }; } \ No newline at end of file diff --git a/plugins/builtin/source/content/disassemblers.cpp b/plugins/builtin/source/content/disassemblers.cpp new file mode 100644 index 000000000..a0125524c --- /dev/null +++ b/plugins/builtin/source/content/disassemblers.cpp @@ -0,0 +1,115 @@ +#include +#include + +#include +#include +#include +#include + +namespace hex::plugin::builtin { + + namespace { + + using namespace ContentRegistry::Disassembler; + using namespace wolv::literals; + + class ArchitectureCapstoneBase : public Architecture { + public: + ArchitectureCapstoneBase(const std::string &unlocalizedName, cs_arch arch) : Architecture(unlocalizedName), m_architecture(arch) { } + + std::vector disassemble(prv::Provider *provider, const Region ®ion) override { + std::vector disassembly; + + csh csHandle = {}; + if (cs_open(this->m_architecture, CS_MODE_64, &csHandle) != CS_ERR_OK) + return {}; + ON_SCOPE_EXIT { cs_close(&csHandle); }; + + cs_option(csHandle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + cs_option(csHandle, CS_OPT_DETAIL, CS_OPT_ON); + cs_option(csHandle, CS_OPT_SKIPDATA, CS_OPT_ON); + + cs_insn *instruction = cs_malloc(csHandle); + ON_SCOPE_EXIT { cs_free(instruction, 1); }; + + std::vector bytes; + u64 prevAddress = std::numeric_limits::max(); + for (u64 address = region.getStartAddress(); address < region.getEndAddress();) { + if (prevAddress == address) + break; + bytes.resize(std::min(2_MiB, (region.getEndAddress() - address) + 1)); + provider->read(address, bytes.data(), bytes.size()); + + const u8 *code = bytes.data(); + size_t size = bytes.size(); + prevAddress = address; + while (cs_disasm_iter(csHandle, &code, &size, &address, instruction)) { + auto line = Instruction { + .region = { instruction->address, instruction->size }, + .mnemonic = instruction->mnemonic, + .operands = instruction->op_str, + .jumpDestination = getJumpDestination(*instruction) + }; + + disassembly.emplace_back(std::move(line)); + } + } + + return disassembly; + } + + void drawConfigInterface() override { + ImGui::TextUnformatted("Config Interface"); + } + + virtual std::optional getJumpDestination(const cs_insn &instruction) = 0; + + private: + cs_arch m_architecture; + }; + + + class ArchitectureX86 : public ArchitectureCapstoneBase { + public: + ArchitectureX86() : ArchitectureCapstoneBase("x86", CS_ARCH_X86) { } + + std::optional getJumpDestination(const cs_insn &instruction) override { + // Get jump destination of jumps on x86 + if (instruction.id == X86_INS_JMP) { + if (instruction.detail->x86.op_count != 1) + return std::nullopt; + + const auto &op = instruction.detail->x86.operands[0]; + + if (op.type == X86_OP_IMM) + return op.imm; + + if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP) + return instruction.address + instruction.size + op.mem.disp; + } + + // Get jump destination of conditional jumps on x86 + if (instruction.id >= X86_INS_JAE && instruction.id <= X86_INS_JLE) { + if (instruction.detail->x86.op_count != 1) + return std::nullopt; + + const auto &op = instruction.detail->x86.operands[0]; + + if (op.type == X86_OP_IMM) + return op.imm; + + if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP) + return instruction.address + instruction.size + op.mem.disp; + } + + return std::nullopt; + } + }; + + } + + void registerDisassemblers() { + ContentRegistry::Disassembler::add(); + } + +} diff --git a/plugins/builtin/source/content/views/view_disassembler.cpp b/plugins/builtin/source/content/views/view_disassembler.cpp index 1c771e488..76b6b5871 100644 --- a/plugins/builtin/source/content/views/view_disassembler.cpp +++ b/plugins/builtin/source/content/views/view_disassembler.cpp @@ -1,450 +1,178 @@ #include "content/views/view_disassembler.hpp" -#include #include -#include +#include using namespace std::literals::string_literals; namespace hex::plugin::builtin { ViewDisassembler::ViewDisassembler() : View::Window("hex.builtin.view.disassembler.name") { - EventManager::subscribe(this, [this](const auto*) { - this->m_disassembly.clear(); - }); + } - ViewDisassembler::~ViewDisassembler() { - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); - EventManager::unsubscribe(this); + void ViewDisassembler::addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction) { + prv::ProviderReader reader(provider); + reader.seek(instruction.region.getStartAddress()); + reader.setEndAddress(instruction.region.getEndAddress()); + + std::string bytes; + for (const auto& byte : reader) { + bytes += fmt::format("{:02X} ", byte); + } + bytes.pop_back(); + + this->m_lines.get(provider).emplace_back(ImHexApi::HexEditor::ProviderRegion { instruction.region, provider }, std::move(bytes), instruction.mnemonic, instruction.operands, instruction.jumpDestination, ImVec2()); } - void ViewDisassembler::disassemble() { - this->m_disassembly.clear(); + static void drawJumpLine(ImVec2 start, ImVec2 end, float columnWidth, u32 slot, ImVec2, bool hovered) { + const u32 slotCount = std::floor(std::max(1.0F, columnWidth / 10_scaled)); - this->m_disassemblerTask = TaskManager::createTask("hex.builtin.view.disassembler.disassembling", this->m_codeRegion.getSize(), [this](auto &task) { - csh capstoneHandle; - cs_insn *instructions = nullptr; + if (slot >= slotCount) + return; - cs_mode mode = this->m_mode; + auto drawList = ImGui::GetWindowDrawList(); - // Create a capstone disassembler instance - if (cs_open(Disassembler::toCapstoneArchitecture(this->m_architecture), mode, &capstoneHandle) == CS_ERR_OK) { + if (start.y > end.y) + slot = slotCount - slot - 1; - // Tell capstone to skip data bytes - cs_option(capstoneHandle, CS_OPT_SKIPDATA, CS_OPT_ON); + const auto width = (columnWidth / float(slotCount)) * float(slot + 1); + const auto lineColor = ImColor::HSV(hovered ? 0.25F : 0.3F + (slot / float(slotCount)) * 0.7F, hovered ? 1.0F : 0.8F, hovered ? 1.0F : 0.8F); + const auto thickness = 2.0_scaled; - auto provider = ImHexApi::Provider::get(); - std::vector buffer(2048, 0x00); - size_t size = this->m_codeRegion.getSize(); + // Draw line + drawList->AddLine(start - ImVec2(width, 0), start, lineColor, thickness); + drawList->AddLine(end - ImVec2(width, 0), end, lineColor, thickness); + drawList->AddLine(start - ImVec2(width, 0), end - ImVec2(width, 0), lineColor, thickness); - // Read the data in chunks and disassemble it - for (u64 address = 0; address < size; address += 2048) { - task.update(address); - - // Read a chunk of data - size_t bufferSize = std::min(u64(2048), (size - address)); - provider->read(this->m_codeRegion.getStartAddress() + address, buffer.data(), bufferSize); - - // Ask capstone to disassemble the data - size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions); - if (instructionCount == 0) - break; - - // Reserve enough space for the disassembly - this->m_disassembly.reserve(this->m_disassembly.size() + instructionCount); - - // Convert the capstone instructions to our disassembly format - u64 usedBytes = 0; - for (u32 i = 0; i < instructionCount; i++) { - const auto &instr = instructions[i]; - Disassembly disassembly = { }; - disassembly.address = instr.address; - disassembly.offset = this->m_codeRegion.getStartAddress() + address + usedBytes; - disassembly.size = instr.size; - disassembly.mnemonic = instr.mnemonic; - disassembly.operators = instr.op_str; - - for (u16 j = 0; j < instr.size; j++) - disassembly.bytes += hex::format("{0:02X} ", instr.bytes[j]); - disassembly.bytes.pop_back(); - - this->m_disassembly.push_back(disassembly); - - usedBytes += instr.size; - } - - // If capstone couldn't disassemble all bytes in the buffer, we might have cut off an instruction - // Adjust the address,so it's being disassembled when we read the next chunk - if (instructionCount < bufferSize) - address -= (bufferSize - usedBytes); - - // Clean up the capstone instructions - cs_free(instructions, instructionCount); - } - - cs_close(&capstoneHandle); - } - }); + //if (end.y >= startCursorPos.y + ImGui::GetScrollY()) + // Draw arrow head + drawList->AddLine(end + scaled({ -5, -5 }), end, lineColor, thickness); + drawList->AddLine(end + scaled({ -5, 5 }), end, lineColor, thickness); } + void ViewDisassembler::drawContent() { - auto provider = ImHexApi::Provider::get(); - if (ImHexApi::Provider::isValid() && provider->isReadable()) { - ImGuiExt::Header("hex.builtin.view.disassembler.position"_lang, true); + const auto &architectures = ContentRegistry::Disassembler::impl::getArchitectures(); + if (this->m_currArchitecture == nullptr) { + this->m_currArchitecture = architectures.front().get(); + } - // Draw base address input - ImGuiExt::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal); - - // Draw region selection picker - ui::regionSelectionPicker(&this->m_codeRegion, provider, &this->m_range); - - // Draw settings - { - ImGuiExt::Header("hex.builtin.common.settings"_lang); - - // Draw architecture selector - if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast(&this->m_architecture), Disassembler::ArchitectureNames.data(), Disassembler::getArchitectureSupportedCount())) - this->m_mode = cs_mode(0); - - // Draw sub-settings for each architecture - if (ImGuiExt::BeginBox()) { - - // Draw endian radio buttons. This setting is available for all architectures - static int littleEndian = true; - ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false); - - ImGui::NewLine(); - - // Draw architecture specific settings - switch (this->m_architecture) { - case Architecture::ARM: - { - static int mode = CS_MODE_ARM; - ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB); - - static int extraMode = 0; - ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8); - - this->m_mode = cs_mode(mode | extraMode); - } - break; - case Architecture::MIPS: - { - static int mode = CS_MODE_MIPS32; - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6); - - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3); - - static bool microMode; - ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode); - - this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0))); - } - break; - case Architecture::X86: - { - static int mode = CS_MODE_32; - ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); - - this->m_mode = cs_mode(mode); - } - break; - case Architecture::PPC: - { - static int mode = CS_MODE_32; - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); - - static bool qpx = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx); - - #if CS_API_MAJOR >= 5 - static bool spe = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe); - static bool booke = false; - ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke); - - this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0))); - #else - this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0))); - #endif - } - break; - case Architecture::SPARC: - { - static bool v9Mode = false; - ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode); - - this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0)); - } - break; - #if CS_API_MAJOR >= 5 - case Architecture::RISCV: - { - static int mode = CS_MODE_RISCV32; - ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64); - - static bool compressed = false; - ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed); - - this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0))); - } - break; - #endif - case Architecture::M68K: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000}, - { "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010}, - { "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020}, - { "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030}, - { "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040}, - { "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - case Architecture::M680X: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 }, - { "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 }, - { "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 }, - { "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 }, - { "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 }, - { "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 }, - { "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 }, - { "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 }, - { "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12}, - { "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - #if CS_API_MAJOR >= 5 - case Architecture::MOS65XX: - { - static int selectedMode = 0; - - std::pair modes[] = { - {"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 }, - { "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 }, - { "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 }, - { "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X }, - { "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX}, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectedMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectedMode].second); - } - break; - #endif - #if CS_API_MAJOR >= 5 - case Architecture::BPF: - { - static int mode = CS_MODE_BPF_CLASSIC; - ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC); - ImGui::SameLine(); - ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED); - - this->m_mode = cs_mode(mode); - } - break; - case Architecture::SH: - { - static u32 selectionMode = 0; - static bool fpu = false; - static bool dsp = false; - - std::pair modes[] = { - { "hex.builtin.view.disassembler.sh.sh2"_lang, CS_MODE_SH2 }, - { "hex.builtin.view.disassembler.sh.sh2a"_lang, CS_MODE_SH2A }, - { "hex.builtin.view.disassembler.sh.sh3"_lang, CS_MODE_SH3 }, - { "hex.builtin.view.disassembler.sh.sh4"_lang, CS_MODE_SH4 }, - { "hex.builtin.view.disassembler.sh.sh4a"_lang, CS_MODE_SH4A }, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectionMode = i; - } - ImGui::EndCombo(); - } - - ImGui::Checkbox("hex.builtin.view.disassembler.sh.fpu"_lang, &fpu); - ImGui::SameLine(); - ImGui::Checkbox("hex.builtin.view.disassembler.sh.dsp"_lang, &dsp); - - this->m_mode = cs_mode(modes[selectionMode].second | (fpu ? CS_MODE_SHFPU : cs_mode(0)) | (dsp ? CS_MODE_SHDSP : cs_mode(0))); - } - break; - case Architecture::TRICORE: - { - static u32 selectionMode = 0; - - std::pair modes[] = { - { "hex.builtin.view.disassembler.tricore.110"_lang, CS_MODE_TRICORE_110 }, - { "hex.builtin.view.disassembler.tricore.120"_lang, CS_MODE_TRICORE_120 }, - { "hex.builtin.view.disassembler.tricore.130"_lang, CS_MODE_TRICORE_130 }, - { "hex.builtin.view.disassembler.tricore.131"_lang, CS_MODE_TRICORE_131 }, - { "hex.builtin.view.disassembler.tricore.160"_lang, CS_MODE_TRICORE_160 }, - { "hex.builtin.view.disassembler.tricore.161"_lang, CS_MODE_TRICORE_161 }, - { "hex.builtin.view.disassembler.tricore.162"_lang, CS_MODE_TRICORE_162 }, - }; - - if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) { - for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { - if (ImGui::Selectable(modes[i].first)) - selectionMode = i; - } - ImGui::EndCombo(); - } - - this->m_mode = cs_mode(modes[selectionMode].second); - } - break; - case Architecture::WASM: - #endif - case Architecture::EVM: - case Architecture::TMS320C64X: - case Architecture::ARM64: - case Architecture::SYSZ: - case Architecture::XCORE: - this->m_mode = cs_mode(0); - break; - } - - ImGuiExt::EndBox(); + ImGui::BeginDisabled(!this->m_lines->empty()); + if (ImGui::BeginCombo("##architectures", Lang(this->m_currArchitecture->getUnlocalizedName()))) { + for (const auto &architecture : architectures) { + if (ImGui::Selectable(Lang(architecture->getUnlocalizedName()))) { + this->m_currArchitecture = architecture.get(); } } - // Draw disassemble button - ImGui::BeginDisabled(this->m_disassemblerTask.isRunning()); + ImGui::EndCombo(); + } + ImGui::EndDisabled(); + + ImGui::SameLine(); + + if (this->m_lines->empty()) { + auto provider = ImHexApi::Provider::get(); + if (ImGuiExt::DimmedButton("Disassemble", ImVec2(ImGui::GetContentRegionAvail().x, 0))) { + auto disassembly = this->m_currArchitecture->disassemble(provider, ImHexApi::HexEditor::getSelection().value()); + + for (const auto &instruction : disassembly) + this->addLine(provider, instruction); + } + + ImGuiExt::BeginSubWindow("Config"); { - if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang)) - this->disassemble(); + this->m_currArchitecture->drawConfigInterface(); } - ImGui::EndDisabled(); + ImGuiExt::EndSubWindow(); + } else { + if (ImGuiExt::DimmedButton("Reset", ImVec2(ImGui::GetContentRegionAvail().x, 0))) { + this->m_lines->clear(); + } + } - // Draw a spinner if the disassembler is running - if (this->m_disassemblerTask.isRunning()) { - ImGui::SameLine(); - ImGuiExt::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang); + if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, ImGui::GetContentRegionAvail())) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("##jumps"); + ImGui::TableSetupColumn("##address", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 80_scaled); + ImGui::TableSetupColumn("##bytes", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 80_scaled); + ImGui::TableSetupColumn("##instruction", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoResize); + + ImGui::TableHeadersRow(); + + ImGuiListClipper clipper; + clipper.Begin(this->m_lines->size(), ImGui::GetTextLineHeightWithSpacing()); + + int processingStart = 0, processingEnd = 0; + + auto startCursorPos = ImGui::GetCursorScreenPos(); + float jumpColumnWidth = 0.0F; + std::optional hoveredAddress; + while (clipper.Step()) { + processingStart = clipper.DisplayStart; + processingEnd = clipper.DisplayEnd; + for (auto i = processingStart; i < processingEnd; i += 1) { + auto &line = this->m_lines->at(i); + ImGui::TableNextRow(); + + auto height = ImGui::CalcTextSize(line.bytes.c_str(), nullptr, false, 80_scaled).y; + + ImGui::TableNextColumn(); + { + // Reserve some space to draw the jump lines later + + // Remember the position of the line so we can draw the jump lines later + jumpColumnWidth = ImGui::GetContentRegionAvail().x; + line.linePos = ImGui::GetCursorScreenPos() + ImVec2(jumpColumnWidth, height / 2); + } + + ImGui::TableNextColumn(); + if (ImGui::Selectable(hex::format("0x{:08X}", line.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, height))) { + ImHexApi::HexEditor::setSelection(line.region); + } + + if (ImGui::IsItemHovered()) + hoveredAddress = line.region.getStartAddress(); + + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight)); + ImGuiExt::TextFormattedWrapped("{}", line.bytes); + ImGui::PopStyleColor(); + + ImGui::TableNextColumn(); + ImGuiExt::TextFormattedColored(ImGui::GetColorU32(ImGuiCol_HeaderActive), "{} ", line.mnemonic); + ImGui::SameLine(0, 0); + ImGuiExt::TextFormatted("{}", line.operands); + } } - ImGui::NewLine(); + // Draw jump arrows + { + std::list destinations; + for (auto sourceLineIndex = processingStart; sourceLineIndex < processingEnd; sourceLineIndex += 1) { + const auto &sourceLine = this->m_lines->at(sourceLineIndex); - ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang); - ImGui::Separator(); + if (sourceLine.jumpDestination.has_value()) { + for (auto destinationLineIndex = processingStart; destinationLineIndex < processingEnd; destinationLineIndex += 1) { + const auto &destinationLine = this->m_lines->at(destinationLineIndex); - // Draw disassembly table - if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { - ImGui::TableSetupScrollFreeze(0, 1); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang); - ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang); - - if (!this->m_disassemblerTask.isRunning()) { - ImGuiListClipper clipper; - clipper.Begin(this->m_disassembly.size()); - - ImGui::TableHeadersRow(); - while (clipper.Step()) { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const auto &instruction = this->m_disassembly[i]; - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - // Draw a selectable label for the address - ImGui::PushID(i); - if (ImGui::Selectable("##DisassemblyLine", false, ImGuiSelectableFlags_SpanAllColumns)) { - ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size); + if (destinationLine.region.getStartAddress() == sourceLine.jumpDestination.value()) { + drawJumpLine(sourceLine.linePos, destinationLine.linePos, jumpColumnWidth, destinations.size(), startCursorPos, hoveredAddress == sourceLine.region.getStartAddress() || hoveredAddress == destinationLine.region.getStartAddress()); + destinations.push_back(destinationLine.region.getStartAddress()); + break; } - ImGui::PopID(); - - // Draw instruction address - ImGui::SameLine(); - ImGuiExt::TextFormatted("0x{0:X}", instruction.address); - - // Draw instruction offset - ImGui::TableNextColumn(); - ImGuiExt::TextFormatted("0x{0:X}", instruction.offset); - - // Draw instruction bytes - ImGui::TableNextColumn(); - ImGui::TextUnformatted(instruction.bytes.c_str()); - - // Draw instruction mnemonic and operands - ImGui::TableNextColumn(); - ImGuiExt::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic); - ImGui::SameLine(); - ImGui::TextUnformatted(instruction.operators.c_str()); } } - clipper.End(); + std::erase_if(destinations, [&](u64 address) { + return sourceLine.region.getStartAddress() == address; + }); } - - ImGui::EndTable(); } + + ImGui::EndTable(); } } diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index 90b08ac63..6a2cf9456 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -701,7 +701,7 @@ namespace hex::plugin::builtin { auto provider = ImHexApi::Provider::get(); - auto reader = prv::ProviderReader (provider); + auto reader = prv::ProviderReader(provider); reader.seek(selection.getStartAddress()); reader.setEndAddress(selection.getEndAddress()); diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index 163a38c3c..cea98c517 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -39,6 +39,7 @@ namespace hex::plugin::builtin { void registerProjectHandlers(); void registerAchievements(); void registerReportGenerators(); + void registerDisassemblers(); void addFooterItems(); void addTitleBarButtons(); @@ -106,6 +107,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerCommandForwarders(); registerAchievements(); registerReportGenerators(); + registerDisassemblers(); addFooterItems(); addTitleBarButtons();