Added Plugin support (#102)

* Build refactoring and initial plugin support

* Possibly fixed linux / mac build

* Added libdl to libglad build script

* Add glfw to imgui dependencies

* Refactored common functionality into "libimhex" for plugins

* Added plugin loading and example plugin

* Added proper API for creating a custom view and a custom tools entry with plugins
This commit is contained in:
WerWolv
2020-12-22 18:10:01 +01:00
committed by GitHub
parent b9324f44e6
commit dbbc525174
83 changed files with 791 additions and 422 deletions

View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.16)
project(example)
set(CMAKE_CXX_STANDARD 20)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../external/ImGui ${CMAKE_CURRENT_BINARY_DIR}/external/ImGui)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libimhex ${CMAKE_CURRENT_BINARY_DIR}/plugins/libimhex)
set(CMAKE_SHARED_LIBRARY_PREFIX "plugin")
add_library(example SHARED
source/plugin_example.cpp
)
target_include_directories(example PUBLIC include)
target_link_libraries(example imgui libimhex)

View File

@@ -0,0 +1,33 @@
#include <hex.hpp>
#include <views/view.hpp>
#include <imgui.h>
class ViewExample : public hex::View {
public:
ViewExample() : hex::View("Example") {}
~ViewExample() override {}
void drawContent() override {
if (ImGui::Begin("Example")) {
ImGui::Text("Custom plugin window");
}
ImGui::End();
}
};
IMHEX_PLUGIN {
View* createView() {
return new ViewExample();
}
void drawToolsEntry() {
if (ImGui::CollapsingHeader("Example Tool")) {
ImGui::Text("Custom Plugin tool");
}
}
}

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.16)
project(libimhex)
set(CMAKE_CXX_STANDARD 20)
if (TARGET libimhex)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../external/ImGui ${CMAKE_CURRENT_BINARY_DIR}/external/ImGui)
endif()
set(CMAKE_STATIC_LIBRARY_PREFIX "")
add_library(libimhex STATIC
source/helpers/event.cpp
source/providers/provider.cpp
source/views/view.cpp
)
target_include_directories(libimhex PUBLIC include)
target_link_libraries(libimhex PRIVATE imgui)

View File

@@ -0,0 +1,41 @@
#pragma once
#include <vector>
#include <functional>
namespace hex {
enum class Events {
FileLoaded,
DataChanged,
PatternChanged,
FileDropped,
WindowClosing,
RegionSelected,
SelectionChangeRequest,
AddBookmark,
AppendPatternLanguageCode,
ProjectFileStore,
ProjectFileLoad
};
struct EventHandler {
void *owner;
Events eventType;
std::function<void(const void*)> callback;
};
class EventManager {
public:
void post(Events eventType, const void *userData);
void subscribe(Events eventType, void *owner, std::function<void(const void*)> callback);
void unsubscribe(Events eventType, void *sender);
private:
std::vector<EventHandler> m_eventHandlers;
};
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <cstddef>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using u128 = __uint128_t;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using s128 = __int128_t;
extern int mainArgc;
extern char **mainArgv;
#define IMHEX_PLUGIN namespace hex::plugin::internal { \
void setImGuiContext(ImGuiContext *ctx) { \
ImGui::SetCurrentContext(ctx); \
} \
} \
namespace hex::plugin
#ifdef OS_WINDOWS
#define MAGIC_PATH_SEPARATOR ";"
#else
#define MAGIC_PATH_SEPARATOR ":"
#endif

View File

@@ -0,0 +1,49 @@
#pragma once
#include <hex.hpp>
#include <map>
#include <optional>
#include <string>
#include <vector>
namespace hex::prv {
class Provider {
public:
constexpr static size_t PageSize = 0x1000'0000;
Provider();
virtual ~Provider() = default;
virtual bool isAvailable() = 0;
virtual bool isReadable() = 0;
virtual bool isWritable() = 0;
virtual void read(u64 offset, void *buffer, size_t size);
virtual void write(u64 offset, const void *buffer, size_t size);
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
virtual size_t getActualSize() = 0;
std::map<u64, u8>& getPatches();
void applyPatches();
u32 getPageCount();
u32 getCurrentPage() const;
void setCurrentPage(u32 page);
virtual size_t getBaseAddress();
virtual size_t getSize();
virtual std::optional<u32> getPageOfAddress(u64 address);
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
protected:
u32 m_currPage = 0;
std::vector<std::map<u64, u8>> m_patches;
};
}

View File

@@ -0,0 +1,71 @@
#pragma once
#include <hex.hpp>
#include "imgui.h"
#include "helpers/event.hpp"
#include <functional>
#include <string>
#include <vector>
namespace hex {
class View {
public:
View(std::string viewName);
virtual ~View() = default;
virtual void drawContent() = 0;
virtual void drawMenu();
virtual bool handleShortcut(int key, int mods);
static std::vector<std::function<void()>>& getDeferedCalls();
static void postEvent(Events eventType, const void *userData = nullptr);
static void drawCommonInterfaces();
static void showErrorPopup(std::string_view errorMessage);
static void setWindowPosition(s32 x, s32 y);
static const ImVec2& getWindowPosition();
static void setWindowSize(s32 width, s32 height);
static const ImVec2& getWindowSize();
virtual bool hasViewMenuItemEntry();
virtual ImVec2 getMinSize();
virtual ImVec2 getMaxSize();
bool& getWindowOpenState();
const std::string getName() const;
protected:
void subscribeEvent(Events eventType, std::function<void(const void*)> callback);
void unsubscribeEvent(Events eventType);
void doLater(std::function<void()> &&function);
protected:
void confirmButtons(const char *textLeft, const char *textRight, std::function<void()> leftButtonFn, std::function<void()> rightButtonFn);
private:
std::string m_viewName;
bool m_windowOpen = false;
static inline EventManager s_eventManager;
static inline std::vector<std::function<void()>> s_deferedCalls;
static inline std::string s_errorMessage;
static inline ImVec2 s_windowPos;
static inline ImVec2 s_windowSize;
};
}

View File

@@ -0,0 +1,25 @@
#include "helpers/event.hpp"
namespace hex {
void EventManager::post(Events eventType, const void *userData) {
for (auto &handler : this->m_eventHandlers)
if (eventType == handler.eventType)
handler.callback(userData);
}
void EventManager::subscribe(Events eventType, void *owner, std::function<void(const void*)> callback) {
for (auto &handler : this->m_eventHandlers)
if (eventType == handler.eventType && owner == handler.owner)
return;
this->m_eventHandlers.push_back(EventHandler { owner, eventType, callback });
}
void EventManager::unsubscribe(Events eventType, void *sender) {
std::erase_if(this->m_eventHandlers, [&eventType, &sender](EventHandler handler) {
return eventType == handler.eventType && sender == handler.owner;
});
}
}

View File

@@ -0,0 +1,66 @@
#include "providers/provider.hpp"
#include <hex.hpp>
#include <cmath>
#include <map>
#include <optional>
#include <string>
#include <vector>
namespace hex::prv {
Provider::Provider() {
this->m_patches.emplace_back();
}
void Provider::read(u64 offset, void *buffer, size_t size) {
this->readRaw(offset, buffer, size);
}
void Provider::write(u64 offset, const void *buffer, size_t size) {
this->writeRaw(offset, buffer, size);
}
std::map<u64, u8>& Provider::getPatches() {
return this->m_patches.back();
}
void Provider::applyPatches() {
for (auto &[patchAddress, patch] : this->m_patches.back())
this->writeRaw(patchAddress, &patch, 1);
}
u32 Provider::getPageCount() {
return std::ceil(this->getActualSize() / double(PageSize));
}
u32 Provider::getCurrentPage() const {
return this->m_currPage;
}
void Provider::setCurrentPage(u32 page) {
if (page < getPageCount())
this->m_currPage = page;
}
size_t Provider::getBaseAddress() {
return PageSize * this->m_currPage;
}
size_t Provider::getSize() {
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
}
std::optional<u32> Provider::getPageOfAddress(u64 address) {
u32 page = std::floor(address / double(PageSize));
if (page >= this->getPageCount())
return { };
return page;
}
}

View File

@@ -0,0 +1,107 @@
#include "views/view.hpp"
#include "imgui.h"
#include <functional>
#include <string>
#include <vector>
namespace hex {
View::View(std::string viewName) : m_viewName(viewName) { }
void View::drawMenu() { }
bool View::handleShortcut(int key, int mods) { return false; }
std::vector<std::function<void()>>& View::getDeferedCalls() {
return View::s_deferedCalls;
}
void View::postEvent(Events eventType, const void *userData) {
View::s_eventManager.post(eventType, userData);
}
void View::drawCommonInterfaces() {
if (ImGui::BeginPopupModal("Error", nullptr, ImGuiWindowFlags_NoResize)) {
ImGui::NewLine();
if (ImGui::BeginChild("##scrolling", ImVec2(300, 100))) {
ImGui::SetCursorPosX((300 - ImGui::CalcTextSize(View::s_errorMessage.c_str(), nullptr, false).x) / 2.0F);
ImGui::TextWrapped("%s", View::s_errorMessage.c_str());
ImGui::EndChild();
}
ImGui::NewLine();
ImGui::SetCursorPosX(75);
if (ImGui::Button("Okay", ImVec2(150, 20)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
}
void View::showErrorPopup(std::string_view errorMessage) {
View::s_errorMessage = errorMessage;
ImGui::OpenPopup("Error");
}
void View::setWindowPosition(s32 x, s32 y) {
View::s_windowPos = ImVec2(x, y);
}
const ImVec2& View::getWindowPosition() {
return View::s_windowPos;
}
void View::setWindowSize(s32 width, s32 height) {
View::s_windowSize = ImVec2(width, height);
}
const ImVec2& View::getWindowSize() {
return View::s_windowSize;
}
bool View::hasViewMenuItemEntry() {
return true;
}
ImVec2 View::getMinSize() {
return ImVec2(480, 720);
}
ImVec2 View::getMaxSize() {
return ImVec2(FLT_MAX, FLT_MAX);
}
bool& View::getWindowOpenState() {
return this->m_windowOpen;
}
const std::string View::getName() const {
return this->m_viewName;
}
void View::subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
View::s_eventManager.subscribe(eventType, this, callback);
}
void View::unsubscribeEvent(Events eventType) {
View::s_eventManager.unsubscribe(eventType, this);
}
void View::doLater(std::function<void()> &&function) {
View::s_deferedCalls.push_back(function);
}
void View::confirmButtons(const char *textLeft, const char *textRight, std::function<void()> leftButtonFn, std::function<void()> rightButtonFn) {
auto width = ImGui::GetWindowWidth();
ImGui::SetCursorPosX(width / 9);
if (ImGui::Button(textLeft, ImVec2(width / 3, 0)))
leftButtonFn();
ImGui::SameLine();
ImGui::SetCursorPosX(width / 9 * 5);
if (ImGui::Button(textRight, ImVec2(width / 3, 0)))
rightButtonFn();
}
}