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

@@ -1,58 +0,0 @@
#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) {
for (auto &handler : this->m_eventHandlers)
if (eventType == handler.eventType)
handler.callback(userData);
}
void 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 unsubscribe(Events eventType, void *sender) {
std::erase_if(this->m_eventHandlers, [&eventType, &sender](EventHandler handler) {
return eventType == handler.eventType && sender == handler.owner;
});
}
private:
std::vector<EventHandler> m_eventHandlers;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include "views/view.hpp"
#include <string_view>
namespace hex {
class Plugin {
public:
Plugin(std::string_view path);
~Plugin();
void setImGuiContext(ImGuiContext *ctx) const;
View* createView() const;
void drawToolsEntry() const;
private:
using SetImGuiContextFunc = void(*)(ImGuiContext*);
using CreateViewFunc = View*(*)();
using DrawToolsEntryFunc = void(*)();
void *m_handle = nullptr;
SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
CreateViewFunc m_createViewFunction = nullptr;
DrawToolsEntryFunc m_drawToolsEntryFunction = nullptr;
};
class PluginHandler {
public:
PluginHandler() = delete;
static void load(std::string_view pluginFolder);
static void unload();
static void reload();
static const auto& getPlugins() {
return PluginHandler::s_plugins;
}
private:
static inline std::string s_pluginFolder;
static inline std::vector<Plugin> s_plugins;
};
}

View File

@@ -1,28 +0,0 @@
#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;
#include "lang/result.hpp"
#include "lang/results.hpp"
extern int mainArgc;
extern char **mainArgv;
#ifdef OS_WINDOWS
#define MAGIC_PATH_SEPARATOR ";"
#else
#define MAGIC_PATH_SEPARATOR ":"
#endif

View File

@@ -17,7 +17,7 @@ namespace hex::lang {
public:
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess);
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
const std::pair<u32, std::string>& getError() { return this->m_error; }

View File

@@ -4,6 +4,7 @@
#include "token.hpp"
#include <optional>
#include <string>
#include <vector>
@@ -13,7 +14,7 @@ namespace hex::lang {
public:
Lexer();
std::pair<Result, std::vector<Token>> lex(const std::string& code);
std::optional<std::vector<Token>> lex(const std::string& code);
const std::pair<u32, std::string>& getError() { return this->m_error; }

View File

@@ -14,7 +14,7 @@ namespace hex::lang {
using TokenIter = std::vector<Token>::const_iterator;
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
std::optional<std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
const std::pair<u32, std::string>& getError() { return this->m_error; }

View File

@@ -16,7 +16,7 @@ namespace hex::lang {
public:
Preprocessor();
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = true);
std::optional<std::string> preprocess(const std::string& code, bool initialRun = true);
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
void addDefaultPragmaHandlers();

View File

@@ -1,21 +0,0 @@
#pragma once
#include <cstdint>
namespace hex::lang {
class Result {
public:
constexpr Result(const std::uint8_t module, const std::uint32_t desc) noexcept : m_result((module << 24) | (desc & 0x00FFFFFF)) { }
constexpr std::uint32_t getResult() const noexcept { return this->m_result; }
constexpr std::uint8_t getModule() const noexcept { return this->m_result >> 24; }
constexpr std::uint32_t getDescription() const noexcept { return this->m_result & 0x00FFFFFF; }
constexpr bool succeeded() const noexcept { return this->m_result == 0; }
constexpr bool failed() const noexcept { return !succeeded(); }
private:
const std::uint32_t m_result;
};
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include "result.hpp"
namespace hex::lang {
constexpr Result ResultSuccess(0, 0);
constexpr Result ResultPreprocessingError(1, 1);
constexpr Result ResultLexicalError(2, 1);
constexpr Result ResultParseError(3, 1);
constexpr Result ResultEvaluatorError(4, 1);
}

View File

@@ -1,69 +0,0 @@
#pragma once
#include <hex.hpp>
#include <cmath>
#include <map>
#include <optional>
#include <string>
#include <vector>
namespace hex::prv {
class Provider {
public:
constexpr static size_t PageSize = 0x1000'0000;
Provider() {
this->m_patches.emplace_back();
}
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) { this->readRaw(offset, buffer, size); }
virtual void write(u64 offset, const void *buffer, size_t size) { this->writeRaw(offset, buffer, 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() { return this->m_patches.back(); }
void applyPatches() {
for (auto &[patchAddress, patch] : this->m_patches.back())
this->writeRaw(patchAddress, &patch, 1);
}
u32 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
u32 getCurrentPage() const { return this->m_currPage; }
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
virtual size_t getBaseAddress() {
return PageSize * this->m_currPage;
}
virtual size_t getSize() {
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
}
virtual std::optional<u32> getPageOfAddress(u64 address) {
u32 page = std::floor(address / double(PageSize));
if (page >= this->getPageCount())
return { };
return page;
}
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

@@ -1,122 +0,0 @@
#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) : m_viewName(viewName) { }
virtual ~View() { }
virtual void createView() = 0;
virtual void createMenu() { }
virtual bool handleShortcut(int key, int mods) { return false; }
static std::vector<std::function<void()>>& getDeferedCalls() {
return View::s_deferedCalls;
}
static void postEvent(Events eventType, const void *userData = nullptr) {
View::s_eventManager.post(eventType, userData);
}
static void 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();
}
}
static void showErrorPopup(std::string_view errorMessage) {
View::s_errorMessage = errorMessage;
ImGui::OpenPopup("Error");
}
static void setWindowPosition(s32 x, s32 y) {
View::s_windowPos = ImVec2(x, y);
}
static const ImVec2& getWindowPosition() {
return View::s_windowPos;
}
static void setWindowSize(s32 width, s32 height) {
View::s_windowSize = ImVec2(width, height);
}
static const ImVec2& getWindowSize() {
return View::s_windowSize;
}
virtual bool hasViewMenuItemEntry() { return true; }
virtual ImVec2 getMinSize() { return ImVec2(480, 720); }
virtual ImVec2 getMaxSize() { return ImVec2(FLT_MAX, FLT_MAX); }
bool& getWindowOpenState() {
return this->m_windowOpen;
}
const std::string getName() const {
return this->m_viewName;
}
protected:
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
View::s_eventManager.subscribe(eventType, this, callback);
}
void unsubscribeEvent(Events eventType) {
View::s_eventManager.unsubscribe(eventType, this);
}
void doLater(std::function<void()> &&function) {
View::s_deferedCalls.push_back(function);
}
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;
};
void confirmButtons(const char *textLeft, const char *textRight, auto leftButtonFn, auto 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();
}
}

View File

@@ -16,8 +16,8 @@ namespace hex {
explicit ViewBookmarks(prv::Provider* &dataProvider);
~ViewBookmarks() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -19,8 +19,8 @@ namespace hex {
ViewCommandPalette();
~ViewCommandPalette() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
bool handleShortcut(int key, int mods) override;

View File

@@ -54,8 +54,8 @@ namespace hex {
explicit ViewDataInspector(prv::Provider* &dataProvider);
~ViewDataInspector() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -27,8 +27,8 @@ namespace hex {
explicit ViewDisassembler(prv::Provider* &dataProvider);
~ViewDisassembler() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -13,8 +13,8 @@ namespace hex {
explicit ViewHashes(prv::Provider* &dataProvider);
~ViewHashes() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -19,8 +19,8 @@ namespace hex {
ViewHelp();
~ViewHelp() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
bool hasViewMenuItemEntry() override { return false; }

View File

@@ -23,8 +23,8 @@ namespace hex {
ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
~ViewHexEditor() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
bool handleShortcut(int key, int mods) override;
private:

View File

@@ -16,8 +16,8 @@ namespace hex {
explicit ViewInformation(prv::Provider* &dataProvider);
~ViewInformation() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -16,8 +16,8 @@ namespace hex {
explicit ViewPatches(prv::Provider* &dataProvider);
~ViewPatches() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -20,8 +20,8 @@ namespace hex {
explicit ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
~ViewPattern() override;
void createMenu() override;
void createView() override;
void drawMenu() override;
void drawContent() override;
private:
std::vector<lang::PatternData*> &m_patternData;

View File

@@ -19,8 +19,8 @@ namespace hex {
ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
~ViewPatternData() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:

View File

@@ -20,8 +20,8 @@ namespace hex {
explicit ViewStrings(prv::Provider* &dataProvider);
~ViewStrings() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
prv::Provider* &m_dataProvider;

View File

@@ -18,8 +18,8 @@ namespace hex {
ViewTools(hex::prv::Provider* &provider);
~ViewTools() override;
void createView() override;
void createMenu() override;
void drawContent() override;
void drawMenu() override;
private:
hex::prv::Provider* &m_dataProvider;

View File

@@ -39,11 +39,14 @@ namespace hex {
void initGLFW();
void initImGui();
void initPlugins();
void deinitGLFW();
void deinitImGui();
void deinitPlugins();
GLFWwindow* m_window;
std::vector<View*> m_views;
std::vector<View*> m_pluginViews;
float m_globalScale = 1.0f, m_fontScale = 1.0f;
bool m_fpsVisible = false;