4 Plugins Development Guide
WerWolv edited this page 2021-02-19 16:02:15 +01:00

Plugins

Plugins allow third parties to add, customize or improve existing features in ImHex. They are shared libraries (dll, so, etc.) that are loaded when ImHex is started.

Following things can be done through plugins currently:

To load a plugin, place it in the plugins folder next to the ImHex executable.

Getting started

To start making a custom plugin, first of all take a look at the project found under plugins/example.

Main file

Every plugin must #include <plugin.hpp> to get access to all relevant functions and then define a IMHEX_PLUGIN_SETUP block. This block gets executed once when the plugin is loaded and should register all new features.

#include <plugin.hpp>   // Needed for plugin functions

// Register your features here
IMHEX_PLUGIN_SETUP("Plugin Name", "Plugin Author", "Plugin Description") {
   // ...
}

Content Registry

New features can easily be added through the content registry.

Custom Views

A view is a window (like the hex editor or the pattern language code window) that can be freely dragged around and contain any content needed.

#include <plugin.hpp>

class ViewCustom : public View {
public:
    ViewCustom() : hex::View("Custom") {}
    ~ViewCustom() override {}

    void drawContent() override {
        // Call into ImGui here to draw your view
    }

    void drawMenu() override {
        // Create new menu entries here
    }
};

IMHEX_PLUGIN_SETUP("Custom View", "WerWolv", "Plugin that adds a new view") {
    // Add ViewCustom to ImHex. This also creates a new entry in the View menu
    ContentRegistry::Views::add<ViewCustom>();
}

Tools Window Entries

Tools are usually some small features that are not big enough to need their own window but still might be useful for people. The demangler or calculator are such features.

#include <plugin.hpp>

IMHEX_PLUGIN_SETUP("Custom tool", "WerWolv", "Plugin that adds a new tool") {
    ContentRegistry::Tools::add("My Fancy Tool", [] {
        // Call into ImHex here to create your new tools interface
    });
}

Settings

Settings are used to expose options to the user through the Help -> Preferences interface. They are saved to a json file when ImHex closes and loaded from there again when ImHex starts.

#include <plugin.hpp>

IMHEX_PLUGIN_SETUP("Custom setting", "WerWolv", "Plugin that adds a new setting") {
    ContentRegistry::Settings::add("My Category", "My Setting", "Default Value" /* or an integer */, [](auto &json) {
        // This function gets called when the settings interface is drawn. Draw your interface with ImGui here
        // When a setting is changed, write the new value to the json parameter. Or read from it to get the current value
        // If no entry in the settings json is available yet, the default value will be written to it. This may be a string or an int
        // Return true if the setting was changed, otherwise return false

        bool checkboxValue = json;
        if (ImGui::CheckBox("##checkbox", &checkboxValue)) {
           json = checkboxValue;
           return true;
        }
        
        return false;
    });
}

To get notified when a setting gets changed, subscribe to the Events::SettingChanged event

Events

Custom events may be used to communicate between different parts of the plugin or between entirely different plugins. ContentRegistry::Events::get() maps a provided unique string to an Event ID. This way many different plugins may create events and use them simultaneously.

    // Subscribe to your new event
    View::subscribeEvent(ContentRegistry::Events::get("MyPlugin::MyEvent"), [](auto userData) {
        // Handle your event here
    });

    // Post your new event
    View::postEvent(ContentRegistry::Events::get("MyPlugin::MyEvent"), nullptr);

Command Palette Commands

The command palette is a text input driven quick menu that can be brought up by pressing SHIFT + CTRL + P. Its intention is to provide access to often used functions directly through easy to remember shortcut keywords.

#include <plugin.hpp>

IMHEX_PLUGIN_SETUP("Custom command", "WerWolv", "Plugin that adds a new command to the command palette") {
    ContentRegistry::CommandPaletteCommands::add(ContentRegistry::CommandPaletteCommands::Type::SymbolCommand, "%", "Single character command",
        [](std::string input) {
            // Handle any parameters here. Input is whatever the user typed in stripped of the % here
        });

    ContentRegistry::CommandPaletteCommands::add(ContentRegistry::CommandPaletteCommands::Type::SymbolCommand, "/command", "Keyword command",
        [](std::string input) {
            // Handle any parameters here. Input is whatever the user typed in stripped of the /command prefix and the space following it
        });
}

Pattern Language Functions

Pattern language functions are functions like assert or findSequence that can be used to call C++ code from the pattern language. They can extend the language freely through this interface.

#include <plugin.hpp>

IMHEX_PLUGIN_SETUP("Custom pattern function", "WerWolv", "Plugin that adds a new function to the pattern language") {
    // myFunc is the name under which the function will be used in the language
    // The second parameter shows how many parameters the function needs. this can be one of 'UnlimitedParameters',
    // 'MoreParametersThan | (number)', 'LessParametersThan | (number)', 'NoParameters'. These should be self explainatory
    ContentRegistry::PatternLanguageFunctions::add("myFunc", ContentRegistry::PatternLanguageFunctions::MoreParametersThan | 1,
        [this](auto params) {
            // Handle the function call here. 'params' is a list of all parameter ASTNodes passed in.

            // Return a new ASTNodeIntegerLiteral to return an int, a new ASTNodeStringLiteral to return a string or nullptr to return nothing
            return nullptr;
        });
}

Data Inspector entries

The data inspector is a view that shows decoded information about the currently selected data. This can be extended to show more decoded values

#include <plugin.hpp>

IMHEX_PLUGIN_SETUP("Custom data type", "WerWolv", "Plugin that adds a new type to the data inspector") {
    ContentRegistry::DataInspector::add("My Type", sizeof(MyType), [](auto buffer, auto endian, auto style) {
        // Calculate and create the data to display here. This function will only be executed when the inspector is invalidated (e.g by moving the cursor)
        // buffer is a vector containing enough bytes to fit your type
        // endian is the currently selected endianess
        // style is the currently selected display style (decimal, hexadecimal, octal)
        auto &myType = *reinterpret_cast<MyType*>(buffer.data());
        auto value = myTypeToString(myType);

        // Return a function here that will be called to draw the entry. Keep all calculations out of here. They should be done before
        // and just passed into here.
        return [value] { ImGui::TextUnformatted(value.c_str()); };
    });
}

Data Processor Nodes

The data processor is a node-based data pre-processor that allows modifying data visually before it gets displayed in the hex editor view. New nodes can be added through this API.

    class NodeBufferMultiply : public dp::Node {
    public:
        NodeNullptr() : Node("Buffer Multiply", { // Name is used for the name displayed in the node header
            // List your node inputs and outputs here
            dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Buffer, "Input"),
            dp::Attribute(dp::Attribute::IOType::In, dp::Attribute::Type::Integer, "Factor"),
            dp::Attribute(dp::Attribute::IOType::Out, dp::Attribute::Type::Buffer, "Output")
        }) {}

         void drawNode() override {
             // Make ImGui calls here to add extra options, information, etc. to the Node body
         }

        // This function gets called every time the user changes any nodes or links in the data processor.
        // Here input data can be read, modified and outputted
        void process() override {
            auto input = this->getBufferOnInput(0);
            auto factor = this->getIntegerOnInput(1);

            for (u8 &byte : input)
                byte *= factor;

            this->setBufferOnOutput(2);
        }
    };

IMHEX_PLUGIN_SETUP("Custom node", "WerWolv", "Plugin that adds a custom data processor node") {
    ContentRegistry::DataProcessorNode::add<NodeBufferMultiply>("Category Name", "Node Name"); // Category and name are used for the context menu
}

Available attribute data types are Buffer for passing around an unspecified number of bytes, Integer for passing around a 64 bit unsigned integer and Float for passing around a 32 bit floating point number.

To get the value on an input use:

std::vector<u8> buffer = this->getBufferOnInput(Index);
u64 integer = this->getIntegerOnInput(Index);
float floatingPoint = this->getFloatOnInput(Index);

where Index is the index of the attribute to read from in the order they were specified in the constructor.

Setting the value on an output works exactly the same using the functions

this->setBufferOnOutput(Index, buffer);
this->setIntegerOnOutput(Index, integer);
this->setFloatOnOutput(Index, floatingPoint);

Localization

ImHex supports full string localization as well as adding additional languages.

Most plugins just need to register their own strings so they can be translated. For this, the following code can be used

    ContentRegistry::Language::addLocalizations("en-US", {
        { "myplugin.language", "Language" },
        { "myplugin.whatever", "Whatever" }
    });

    ContentRegistry::Language::addLocalizations("de-DE", {
        { "myplugin.language", "Sprache" },
        { "myplugin.whatever", "Was auch immer" }
    });

To add a new language that's not yet supported by ImHex directly, use the registerLanguage function. It takes the english name of the language followed by the ISO 639-1 language code as arguments.

    ContentRegistry::Language::registerLanguage("German", "de-DE");

To reference your registered strings within your plugin, simply use

std::string language = "myplugin.language"_lang;
printf("%s\", language); // Prints "Language" when language is set to English (US) and "Sprache" when set to German

Various Interface entries

The interface registry is a common place to add new content to various interfaces such as the Welcome screen.

IMHEX_PLUGIN_SETUP("Custom interface", "WerWolv", "Plugin that adds new information to various interfaces") {

    ContentRegistry::Interface::addWelcomeScreenEntry([] {
        ImGui::TextUnformatted("Hello Welcome Screen");
    });

    ContentRegistry::Interface::addFooterItem([] {
        if (ImGui::Button("Footer Button")) {
            // ...
        }
    });

}