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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 791 additions and 422 deletions

View file

@ -85,6 +85,7 @@ jobs:
mingw-w64-${{ matrix.arch }}-polly mingw-w64-${{ matrix.arch }}-polly
mingw-w64-${{ matrix.arch }}-python mingw-w64-${{ matrix.arch }}-python
mingw-w64-${{ matrix.arch }}-freetype mingw-w64-${{ matrix.arch }}-freetype
mingw-w64-${{ matrix.arch }}-dlfcn
- name: ✋ Build - name: ✋ Build
run: | run: |

View file

@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.16)
project(imhex VERSION 1.5.0) project(imhex VERSION 1.5.0)
SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC) set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
# Enforce that we use non system Python 3 on macOS. # Enforce that we use non system Python 3 on macOS.
@ -17,10 +17,11 @@ pkg_search_module(CRYPTO REQUIRED libcrypto)
pkg_search_module(CAPSTONE REQUIRED capstone) pkg_search_module(CAPSTONE REQUIRED capstone)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
find_package(nlohmann_json REQUIRED) find_package(nlohmann_json REQUIRED)
find_package(Freetype REQUIRED)
find_package(Python COMPONENTS Interpreter Development) find_package(Python COMPONENTS Interpreter Development)
add_subdirectory(external/llvm/Demangle) add_subdirectory(external/llvm/Demangle)
add_subdirectory(external/ImGui)
add_subdirectory(plugins/libimhex)
if(Python_VERSION LESS 3) if(Python_VERSION LESS 3)
message(STATUS ${PYTHON_VERSION_MAJOR_MINOR}) message(STATUS ${PYTHON_VERSION_MAJOR_MINOR})
@ -33,32 +34,32 @@ if(NOT MAGIC_FOUND)
endif() endif()
# Add include directories # Add include directories
include_directories(include ${GLFW_INCLUDE_DIRS} ${GLM_INCLUDE_DIRS} ${CRYPTO_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS}) include_directories(include ${GLFW_INCLUDE_DIRS} ${GLM_INCLUDE_DIRS} ${CRYPTO_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${MAGIC_INCLUDE_DIRS} ${Python_INCLUDE_DIRS})
# Get Python major and minor # Get Python major and minor
string(REPLACE "." ";" PYTHON_VERSION_MAJOR_MINOR ${Python_VERSION}) string(REPLACE "." ";" PYTHON_VERSION_MAJOR_MINOR ${Python_VERSION})
list(REMOVE_AT PYTHON_VERSION_MAJOR_MINOR 2) list(REMOVE_AT PYTHON_VERSION_MAJOR_MINOR 2)
list(JOIN PYTHON_VERSION_MAJOR_MINOR "." PYTHON_VERSION_MAJOR_MINOR) list(JOIN PYTHON_VERSION_MAJOR_MINOR "." PYTHON_VERSION_MAJOR_MINOR)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD -DPYTHON_VERSION_MAJOR_MINOR=\"\\\"${PYTHON_VERSION_MAJOR_MINOR}\"\\\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -DPYTHON_VERSION_MAJOR_MINOR=\"\\\"${PYTHON_VERSION_MAJOR_MINOR}\"\\\"")
# Detect current OS / System # Detect current OS / System
if (MSYS OR MINGW) if (MSYS OR MINGW)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_WINDOWS -static-libstdc++ -static-libgcc -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_WINDOWS -static-libstdc++ -static-libgcc -static")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,-subsystem,windows") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wl,-subsystem,windows")
elseif(APPLE) elseif(APPLE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_MACOS") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_MACOS")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_LINUX") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_LINUX")
else() else()
message(FATAL_ERROR "Unknown / unsupported system!") message(FATAL_ERROR "Unknown / unsupported system!")
endif() endif()
# Detect 32 vs. 64 bit system # Detect 32 vs. 64 bit system
if(CMAKE_SIZEOF_VOID_P EQUAL 8) if(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DARCH_64_BIT") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DARCH_64_BIT")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DARCH_32_BIT") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DARCH_32_BIT")
endif() endif()
# Get the current working branch # Get the current working branch
@ -77,11 +78,11 @@ execute_process(
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
) )
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGIT_COMMIT_HASH=\"\\\"${GIT_COMMIT_HASH}\"\\\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGIT_COMMIT_HASH=\"\\\"${GIT_COMMIT_HASH}\"\\\"")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGIT_BRANCH=\"\\\"${GIT_BRANCH}\"\\\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGIT_BRANCH=\"\\\"${GIT_BRANCH}\"\\\"")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE -DIMHEX_VERSION=\"\\\"${PROJECT_VERSION}\"\\\"") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE -DIMHEX_VERSION=\"\\\"${PROJECT_VERSION}\"\\\"")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DIMHEX_VERSION=\"\\\"${PROJECT_VERSION}-Debug\"\\\"") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DIMHEX_VERSION=\"\\\"${PROJECT_VERSION}-Debug\"\\\"")
add_executable(imhex add_executable(imhex
source/main.cpp source/main.cpp
@ -93,6 +94,7 @@ add_executable(imhex
source/helpers/math_evaluator.cpp source/helpers/math_evaluator.cpp
source/helpers/project_file_handler.cpp source/helpers/project_file_handler.cpp
source/helpers/loader_script_handler.cpp source/helpers/loader_script_handler.cpp
source/helpers/plugin_handler.cpp
source/lang/preprocessor.cpp source/lang/preprocessor.cpp
source/lang/lexer.cpp source/lang/lexer.cpp
@ -116,18 +118,6 @@ add_executable(imhex
source/views/view_patches.cpp source/views/view_patches.cpp
source/views/view_command_palette.cpp source/views/view_command_palette.cpp
libs/glad/source/glad.c
libs/ImGui/source/imgui.cpp
libs/ImGui/source/imgui_draw.cpp
libs/ImGui/source/imgui_widgets.cpp
libs/ImGui/source/imgui_demo.cpp
libs/ImGui/source/imgui_freetype.cpp
libs/ImGui/source/imgui_impl_glfw.cpp
libs/ImGui/source/imgui_impl_opengl3.cpp
libs/ImGui/source/ImGuiFileBrowser.cpp
libs/ImGui/source/TextEditor.cpp
resource.rc resource.rc
) )
@ -136,9 +126,9 @@ set_target_properties(imhex PROPERTIES CXX_VISIBILITY_PRESET hidden)
target_link_directories(imhex PRIVATE ${GLFW_LIBRARY_DIRS} ${CRYPTO_LIBRARY_DIRS} ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS}) target_link_directories(imhex PRIVATE ${GLFW_LIBRARY_DIRS} ${CRYPTO_LIBRARY_DIRS} ${CAPSTONE_LIBRARY_DIRS} ${MAGIC_LIBRARY_DIRS})
if (WIN32) if (WIN32)
target_link_libraries(imhex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a LLVMDemangle ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) target_link_libraries(imhex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a LLVMDemangle imgui libimhex ${Python_LIBRARIES} nlohmann_json::nlohmann_json dl)
elseif (UNIX) elseif (UNIX)
target_link_libraries(imhex glfw magic crypto ${CMAKE_DL_LIBS} capstone LLVMDemangle ${Python_LIBRARIES} nlohmann_json::nlohmann_json Freetype::Freetype) target_link_libraries(imhex glfw magic crypto ${CMAKE_DL_LIBS} capstone LLVMDemangle imgui libimhex ${Python_LIBRARIES} nlohmann_json::nlohmann_json dl)
endif() endif()
install(TARGETS imhex DESTINATION bin) install(TARGETS imhex DESTINATION bin)

View file

@ -12,4 +12,5 @@ pacman -S --needed \
mingw-w64-x86_64-openssl \ mingw-w64-x86_64-openssl \
mingw-w64-x86_64-polly \ mingw-w64-x86_64-polly \
mingw-w64-x86_64-python \ mingw-w64-x86_64-python \
mingw-w64-x86_64-freetype mingw-w64-x86_64-freetype \
mingw-w64-x86_64-dlfcn

35
external/ImGui/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.16)
project(imgui)
set(CMAKE_CXX_STANDARD 17)
find_package(PkgConfig REQUIRED)
find_package(Freetype REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)
add_library(imgui
source/imgui.cpp
source/imgui_demo.cpp
source/imgui_draw.cpp
source/imgui_freetype.cpp
source/imgui_impl_glfw.cpp
source/imgui_impl_opengl3.cpp
source/imgui_widgets.cpp
source/ImGuiFileBrowser.cpp
source/TextEditor.cpp
)
add_compile_definitions(IMGUI_IMPL_OPENGL_LOADER_GLAD)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../glad ${CMAKE_CURRENT_BINARY_DIR}/external/glad)
target_include_directories(imgui PUBLIC include)
target_include_directories(imgui PRIVATE ${FREETYPE_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS})
target_link_directories(imgui PRIVATE ${GLFW_LIBRARY_DIRS})
if (WIN32)
target_link_libraries(imgui Freetype::Freetype glad libglfw3.a)
elseif (UNIX)
target_link_libraries(imgui Freetype::Freetype glad glfw)
endif()

11
external/glad/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.16)
project(glad)
set(CMAKE_CXX_STANDARD 17)
add_library(glad
source/glad.c
)
target_include_directories(glad PUBLIC include)
target_link_libraries(glad PRIVATE dl)

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

@ -17,7 +17,7 @@ namespace hex::lang {
public: public:
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess); 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; } const std::pair<u32, std::string>& getError() { return this->m_error; }

View file

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

View file

@ -16,7 +16,7 @@ namespace hex::lang {
public: public:
Preprocessor(); 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 addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
void addDefaultPragmaHandlers(); 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); explicit ViewBookmarks(prv::Provider* &dataProvider);
~ViewBookmarks() override; ~ViewBookmarks() override;
void createView() override; void drawContent() override;
void createMenu() override; void drawMenu() override;
private: private:
prv::Provider* &m_dataProvider; prv::Provider* &m_dataProvider;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

@ -15,12 +15,16 @@ using s32 = std::int32_t;
using s64 = std::int64_t; using s64 = std::int64_t;
using s128 = __int128_t; using s128 = __int128_t;
#include "lang/result.hpp"
#include "lang/results.hpp"
extern int mainArgc; extern int mainArgc;
extern char **mainArgv; extern char **mainArgv;
#define IMHEX_PLUGIN namespace hex::plugin::internal { \
void setImGuiContext(ImGuiContext *ctx) { \
ImGui::SetCurrentContext(ctx); \
} \
} \
namespace hex::plugin
#ifdef OS_WINDOWS #ifdef OS_WINDOWS
#define MAGIC_PATH_SEPARATOR ";" #define MAGIC_PATH_SEPARATOR ";"
#else #else

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();
}
}

View file

@ -0,0 +1,70 @@
#include "helpers/plugin_handler.hpp"
#include <dlfcn.h>
#include <filesystem>
namespace hex {
// hex::plugin::createView(void)
constexpr auto CreateViewSymbol = "_ZN3hex6plugin10createViewEv";
// hex::plugin::drawToolsEntry(void)
constexpr auto DrawToolsEntrySymbol = "_ZN3hex6plugin14drawToolsEntryEv";
// hex::plugin::internal::setImGuiContext(ImGuiContext*)
constexpr auto SetImGuiContextSymbol = "_ZN3hex6plugin8internal15setImGuiContextEP12ImGuiContext";
Plugin::Plugin(std::string_view path) {
this->m_handle = dlopen(path.data(), RTLD_LAZY);
if (this->m_handle == nullptr)
return;
this->m_createViewFunction = reinterpret_cast<CreateViewFunc>(dlsym(this->m_handle, CreateViewSymbol));
this->m_drawToolsEntryFunction = reinterpret_cast<DrawToolsEntryFunc>(dlsym(this->m_handle, DrawToolsEntrySymbol));
this->m_setImGuiContextFunction = reinterpret_cast<SetImGuiContextFunc>(dlsym(this->m_handle, SetImGuiContextSymbol));
}
Plugin::~Plugin() {
if (this->m_handle != nullptr)
dlclose(this->m_handle);
}
void Plugin::setImGuiContext(ImGuiContext *ctx) const {
if (this->m_setImGuiContextFunction != nullptr)
this->m_setImGuiContextFunction(ctx);
}
View* Plugin::createView() const {
if (this->m_createViewFunction != nullptr)
return this->m_createViewFunction();
return nullptr;
}
void Plugin::drawToolsEntry() const {
if (this->m_drawToolsEntryFunction != nullptr)
this->m_drawToolsEntryFunction();
}
void PluginHandler::load(std::string_view pluginFolder) {
PluginHandler::unload();
if (!std::filesystem::exists(pluginFolder))
throw std::runtime_error("Failed to find plugin folder");
PluginHandler::s_pluginFolder = pluginFolder;
for (auto& pluginPath : std::filesystem::directory_iterator(pluginFolder))
PluginHandler::s_plugins.emplace_back(pluginPath.path().string());
}
void PluginHandler::unload() {
PluginHandler::s_plugins.clear();
PluginHandler::s_pluginFolder.clear();
}
void PluginHandler::reload() {
PluginHandler::load(PluginHandler::s_pluginFolder);
}
}

View file

@ -347,7 +347,7 @@ namespace hex::lang {
return { nullptr, 0 }; return { nullptr, 0 };
} }
std::pair<Result, std::vector<PatternData*>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) { std::optional<std::vector<PatternData*>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
// Evaluate types // Evaluate types
for (const auto &node : ast) { for (const auto &node : ast) {
@ -420,9 +420,9 @@ namespace hex::lang {
for (const auto &var : variables) for (const auto &var : variables)
if (var == nullptr) if (var == nullptr)
return { ResultEvaluatorError, { } }; return { };
return { ResultSuccess, variables }; return variables;
} }
} }

View file

@ -66,7 +66,7 @@ namespace hex::lang {
return integer; return integer;
} }
std::pair<Result, std::vector<Token>> Lexer::lex(const std::string& code) { std::optional<std::vector<Token>> Lexer::lex(const std::string& code) {
std::vector<Token> tokens; std::vector<Token> tokens;
u32 offset = 0; u32 offset = 0;
@ -142,7 +142,7 @@ namespace hex::lang {
if (offset >= code.length()) { if (offset >= code.length()) {
this->m_error = { lineNumber, "Invalid character literal" }; this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } }; return { };
} }
char character = code[offset]; char character = code[offset];
@ -152,19 +152,19 @@ namespace hex::lang {
if (offset >= code.length()) { if (offset >= code.length()) {
this->m_error = { lineNumber, "Invalid character literal" }; this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } }; return { };
} }
if (code[offset] != '\\' && code[offset] != '\'') { if (code[offset] != '\\' && code[offset] != '\'') {
this->m_error = { lineNumber, "Invalid escape sequence" }; this->m_error = { lineNumber, "Invalid escape sequence" };
return { ResultLexicalError, { } }; return { };
} }
character = code[offset]; character = code[offset];
} else { } else {
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') { if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') {
this->m_error = { lineNumber, "Invalid character literal" }; this->m_error = { lineNumber, "Invalid character literal" };
return { ResultLexicalError, { } }; return { };
} }
} }
@ -172,7 +172,7 @@ namespace hex::lang {
if (offset >= code.length() || code[offset] != '\'') { if (offset >= code.length() || code[offset] != '\'') {
this->m_error = { lineNumber, "Missing terminating ' after character literal" }; this->m_error = { lineNumber, "Missing terminating ' after character literal" };
return { ResultLexicalError, { } }; return { };
} }
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber }); tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber });
@ -240,19 +240,19 @@ namespace hex::lang {
if (!integer.has_value()) { if (!integer.has_value()) {
this->m_error = { lineNumber, "Invalid integer literal" }; this->m_error = { lineNumber, "Invalid integer literal" };
return { ResultLexicalError, {}}; return { };
} }
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber }); tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber });
offset += (end - &code[offset]); offset += (end - &code[offset]);
} else { } else {
this->m_error = { lineNumber, "Unknown token" }; this->m_error = { lineNumber, "Unknown token" };
return { ResultLexicalError, {} }; return { };
} }
} }
tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber }); tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber });
return { ResultSuccess, tokens }; return tokens;
} }
} }

View file

@ -649,15 +649,15 @@ namespace hex::lang {
return program; return program;
} }
std::pair<Result, std::vector<ASTNode*>> Parser::parse(const std::vector<Token> &tokens) { std::optional<std::vector<ASTNode*>> Parser::parse(const std::vector<Token> &tokens) {
auto currentToken = tokens.begin(); auto currentToken = tokens.begin();
auto program = parseTillToken(currentToken, Token::Type::EndOfProgram); auto program = parseTillToken(currentToken, Token::Type::EndOfProgram);
if (program.empty() || currentToken != tokens.end()) if (program.empty() || currentToken != tokens.end())
return { ResultParseError, { } }; return { };
return { ResultSuccess, program }; return program;
} }
} }

View file

@ -6,7 +6,7 @@ namespace hex::lang {
} }
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) { std::optional<std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
u32 offset = 0; u32 offset = 0;
u32 lineNumber = 1; u32 lineNumber = 1;
@ -29,7 +29,7 @@ namespace hex::lang {
offset += 1; offset += 1;
if (code[offset] != '<' && code[offset] != '"') if (code[offset] != '<' && code[offset] != '"')
return { ResultPreprocessingError, "" }; return { };
char endChar = code[offset]; char endChar = code[offset];
if (endChar == '<') endChar = '>'; if (endChar == '<') endChar = '>';
@ -42,7 +42,7 @@ namespace hex::lang {
offset += 1; offset += 1;
if (offset >= code.length()) if (offset >= code.length())
return { ResultPreprocessingError, "" }; return { };
} }
offset += 1; offset += 1;
@ -51,7 +51,7 @@ namespace hex::lang {
FILE *file = fopen(includeFile.c_str(), "r"); FILE *file = fopen(includeFile.c_str(), "r");
if (file == nullptr) if (file == nullptr)
return { ResultPreprocessingError, "" }; return { };
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
size_t size = ftell(file); size_t size = ftell(file);
@ -61,11 +61,11 @@ namespace hex::lang {
fread(buffer, size, 1, file); fread(buffer, size, 1, file);
buffer[size] = 0x00; buffer[size] = 0x00;
auto [result, preprocessedInclude] = this->preprocess(buffer, false); auto preprocessedInclude = this->preprocess(buffer, false);
if (result.failed()) if (!preprocessedInclude.has_value())
return { ResultPreprocessingError, "" }; return { };
output += preprocessedInclude; output += preprocessedInclude.value();
delete[] buffer; delete[] buffer;
@ -81,7 +81,7 @@ namespace hex::lang {
defineName += code[offset]; defineName += code[offset];
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r') if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
return { ResultPreprocessingError, "" }; return { };
offset += 1; offset += 1;
} }
@ -91,14 +91,14 @@ namespace hex::lang {
std::string replaceValue; std::string replaceValue;
while (code[offset] != '\n' && code[offset] != '\r') { while (code[offset] != '\n' && code[offset] != '\r') {
if (offset >= code.length()) if (offset >= code.length())
return { ResultPreprocessingError, "" }; return { };
replaceValue += code[offset]; replaceValue += code[offset];
offset += 1; offset += 1;
} }
if (replaceValue.empty()) if (replaceValue.empty())
return { ResultPreprocessingError, "" }; return { };
this->m_defines.emplace(defineName, replaceValue); this->m_defines.emplace(defineName, replaceValue);
} else if (code.substr(offset, 6) == "pragma") { } else if (code.substr(offset, 6) == "pragma") {
@ -112,7 +112,7 @@ namespace hex::lang {
pragmaKey += code[offset]; pragmaKey += code[offset];
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r') if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
return { ResultPreprocessingError, "" }; return { };
offset += 1; offset += 1;
} }
@ -123,18 +123,18 @@ namespace hex::lang {
std::string pragmaValue; std::string pragmaValue;
while (code[offset] != '\n' && code[offset] != '\r') { while (code[offset] != '\n' && code[offset] != '\r') {
if (offset >= code.length()) if (offset >= code.length())
return { ResultPreprocessingError, "" }; return { };
pragmaValue += code[offset]; pragmaValue += code[offset];
offset += 1; offset += 1;
} }
if (pragmaValue.empty()) if (pragmaValue.empty())
return { ResultPreprocessingError, "" }; return { };
this->m_pragmas.emplace(pragmaKey, pragmaValue); this->m_pragmas.emplace(pragmaKey, pragmaValue);
} else } else
return { ResultPreprocessingError, "" }; return { };
} }
if (code[offset] == '\n') if (code[offset] == '\n')
@ -158,13 +158,13 @@ namespace hex::lang {
for (const auto &[type, value] : this->m_pragmas) { for (const auto &[type, value] : this->m_pragmas) {
if (this->m_pragmaHandlers.contains(type)) { if (this->m_pragmaHandlers.contains(type)) {
if (!this->m_pragmaHandlers[type](value)) if (!this->m_pragmaHandlers[type](value))
return { ResultPreprocessingError, { } }; return { };
} else } else
return { ResultPreprocessingError, { } }; return { };
} }
} }
return { ResultSuccess, output }; return output;
} }
void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) { void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) {

View file

@ -39,7 +39,7 @@ namespace hex {
View::unsubscribeEvent(Events::ProjectFileStore); View::unsubscribeEvent(Events::ProjectFileStore);
} }
void ViewBookmarks::createView() { void ViewBookmarks::drawContent() {
if (ImGui::Begin("Bookmarks", &this->getWindowOpenState())) { if (ImGui::Begin("Bookmarks", &this->getWindowOpenState())) {
if (ImGui::BeginChild("##scrolling")) { if (ImGui::BeginChild("##scrolling")) {
@ -107,7 +107,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewBookmarks::createMenu() { void ViewBookmarks::drawMenu() {
} }

View file

@ -17,7 +17,7 @@ namespace hex {
} }
void ViewCommandPalette::createView() { void ViewCommandPalette::drawContent() {
auto windowPos = View::getWindowPosition(); auto windowPos = View::getWindowPosition();
auto windowSize = View::getWindowSize(); auto windowSize = View::getWindowSize();
@ -54,7 +54,7 @@ namespace hex {
} }
} }
void ViewCommandPalette::createMenu() { void ViewCommandPalette::drawMenu() {
} }

View file

@ -35,7 +35,7 @@ namespace hex {
View::unsubscribeEvent(Events::RegionSelected); View::unsubscribeEvent(Events::RegionSelected);
} }
void ViewDataInspector::createView() { void ViewDataInspector::drawContent() {
if (this->m_shouldInvalidate) { if (this->m_shouldInvalidate) {
this->m_shouldInvalidate = false; this->m_shouldInvalidate = false;
@ -165,7 +165,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewDataInspector::createMenu() { void ViewDataInspector::drawMenu() {
} }

View file

@ -29,7 +29,7 @@ namespace hex {
View::unsubscribeEvent(Events::RegionSelected); View::unsubscribeEvent(Events::RegionSelected);
} }
void ViewDisassembler::createView() { void ViewDisassembler::drawContent() {
if (this->m_shouldInvalidate) { if (this->m_shouldInvalidate) {
this->m_disassembly.clear(); this->m_disassembly.clear();
@ -282,7 +282,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewDisassembler::createMenu() { void ViewDisassembler::drawMenu() {
} }

View file

@ -37,7 +37,7 @@ namespace hex {
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big)); snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big));
} }
void ViewHashes::createView() { void ViewHashes::drawContent() {
if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
@ -240,7 +240,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewHashes::createMenu() { void ViewHashes::drawMenu() {
} }

View file

@ -275,13 +275,13 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewHelp::createView() { void ViewHelp::drawContent() {
this->drawAboutPopup(); this->drawAboutPopup();
this->drawPatternHelpPopup(); this->drawPatternHelpPopup();
this->drawMathEvaluatorHelp(); this->drawMathEvaluatorHelp();
} }
void ViewHelp::createMenu() { void ViewHelp::drawMenu() {
if (ImGui::BeginMenu("Help")) { if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About", "")) { if (ImGui::MenuItem("About", "")) {
View::doLater([] { ImGui::OpenPopup("About"); }); View::doLater([] { ImGui::OpenPopup("About"); });

View file

@ -101,7 +101,7 @@ namespace hex {
this->m_dataProvider = nullptr; this->m_dataProvider = nullptr;
} }
void ViewHexEditor::createView() { void ViewHexEditor::drawContent() {
size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize(); size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize();
this->m_memoryEditor.DrawWindow("Hex Editor", &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress()); this->m_memoryEditor.DrawWindow("Hex Editor", &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
@ -266,7 +266,7 @@ namespace hex {
} }
} }
void ViewHexEditor::createMenu() { void ViewHexEditor::drawMenu() {
if (ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open File...", "CTRL + O")) { if (ImGui::MenuItem("Open File...", "CTRL + O")) {

View file

@ -46,7 +46,7 @@ namespace hex {
return entropy / 8; return entropy / 8;
} }
void ViewInformation::createView() { void ViewInformation::drawContent() {
if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
@ -188,7 +188,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewInformation::createMenu() { void ViewInformation::drawMenu() {
} }

View file

@ -28,7 +28,7 @@ namespace hex {
View::unsubscribeEvent(Events::ProjectFileLoad); View::unsubscribeEvent(Events::ProjectFileLoad);
} }
void ViewPatches::createView() { void ViewPatches::drawContent() {
if (ImGui::Begin("Patches", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Patches", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) { if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
@ -84,7 +84,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewPatches::createMenu() { void ViewPatches::drawMenu() {
} }

View file

@ -167,7 +167,7 @@ namespace hex {
View::unsubscribeEvent(Events::ProjectFileLoad); View::unsubscribeEvent(Events::ProjectFileLoad);
} }
void ViewPattern::createMenu() { void ViewPattern::drawMenu() {
if (ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Load pattern...")) { if (ImGui::MenuItem("Load pattern...")) {
View::doLater([]{ ImGui::OpenPopup("Open Hex Pattern"); }); View::doLater([]{ ImGui::OpenPopup("Open Hex Pattern"); });
@ -176,7 +176,7 @@ namespace hex {
} }
} }
void ViewPattern::createView() { void ViewPattern::drawContent() {
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) { if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
this->m_textEditor.Render("Pattern"); this->m_textEditor.Render("Pattern");
@ -279,43 +279,43 @@ namespace hex {
}); });
preprocessor.addDefaultPragmaHandlers(); preprocessor.addDefaultPragmaHandlers();
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer); auto preprocesedCode = preprocessor.preprocess(buffer);
if (preprocessingResult.failed()) { if (!preprocesedCode.has_value()) {
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() }); this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
return; return;
} }
hex::lang::Lexer lexer; hex::lang::Lexer lexer;
auto [lexResult, tokens] = lexer.lex(preprocesedCode); auto tokens = lexer.lex(preprocesedCode.value());
if (lexResult.failed()) { if (!tokens.has_value()) {
this->m_textEditor.SetErrorMarkers({ lexer.getError() }); this->m_textEditor.SetErrorMarkers({ lexer.getError() });
return; return;
} }
hex::lang::Parser parser; hex::lang::Parser parser;
auto [parseResult, ast] = parser.parse(tokens); auto ast = parser.parse(tokens.value());
if (parseResult.failed()) { if (!ast.has_value()) {
this->m_textEditor.SetErrorMarkers({ parser.getError() }); this->m_textEditor.SetErrorMarkers({ parser.getError() });
printf("%d %s\n", parser.getError().first, parser.getError().second.c_str()); printf("%d %s\n", parser.getError().first, parser.getError().second.c_str());
return; return;
} }
hex::ScopeExit deleteAst([&ast=ast]{ for(auto &node : ast) delete node; }); hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast.value()) delete node; });
hex::lang::Validator validator; hex::lang::Validator validator;
auto validatorResult = validator.validate(ast); auto validatorResult = validator.validate(ast.value());
if (!validatorResult) { if (!validatorResult) {
this->m_textEditor.SetErrorMarkers({ validator.getError() }); this->m_textEditor.SetErrorMarkers({ validator.getError() });
return; return;
} }
hex::lang::Evaluator evaluator(this->m_dataProvider, defaultDataEndianess); hex::lang::Evaluator evaluator(this->m_dataProvider, defaultDataEndianess);
auto [evaluateResult, patternData] = evaluator.evaluate(ast); auto patternData = evaluator.evaluate(ast.value());
if (evaluateResult.failed()) { if (!patternData.has_value()) {
this->m_textEditor.SetErrorMarkers({ evaluator.getError() }); this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
return; return;
} }
this->m_patternData = patternData; this->m_patternData = patternData.value();
this->postEvent(Events::PatternChanged); this->postEvent(Events::PatternChanged);
} }

View file

@ -48,7 +48,7 @@ namespace hex {
return false; return false;
} }
void ViewPatternData::createView() { void ViewPatternData::drawContent() {
if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) { if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
@ -69,7 +69,7 @@ namespace hex {
ImGui::End(); ImGui::End();
} }
void ViewPatternData::createMenu() { void ViewPatternData::drawMenu() {
} }

View file

@ -46,7 +46,7 @@ namespace hex {
} }
void ViewStrings::createView() { void ViewStrings::drawContent() {
if (this->m_shouldInvalidate) { if (this->m_shouldInvalidate) {
this->m_shouldInvalidate = false; this->m_shouldInvalidate = false;
@ -181,7 +181,7 @@ namespace hex {
} }
} }
void ViewStrings::createMenu() { void ViewStrings::drawMenu() {
} }

View file

@ -5,6 +5,7 @@
#include <optional> #include <optional>
#include "providers/provider.hpp" #include "providers/provider.hpp"
#include "helpers/plugin_handler.hpp"
#include "helpers/utils.hpp" #include "helpers/utils.hpp"
#include <llvm/Demangle/Demangle.h> #include <llvm/Demangle/Demangle.h>
@ -291,7 +292,7 @@ namespace hex {
} }
} }
void ViewTools::createView() { void ViewTools::drawContent() {
if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
this->drawDemangler(); this->drawDemangler();
@ -300,11 +301,14 @@ namespace hex {
this->drawMathEvaluator(); this->drawMathEvaluator();
this->drawColorPicker(); this->drawColorPicker();
for (const auto& plugin : PluginHandler::getPlugins())
plugin.drawToolsEntry();
} }
ImGui::End(); ImGui::End();
} }
void ViewTools::createMenu() { void ViewTools::drawMenu() {
} }

View file

@ -1,6 +1,7 @@
#include "hex.hpp"
#include "window.hpp" #include "window.hpp"
#include <hex.hpp>
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
@ -10,6 +11,8 @@
#include "imgui_impl_opengl3.h" #include "imgui_impl_opengl3.h"
#include "imgui_freetype.h" #include "imgui_freetype.h"
#include "helpers/plugin_handler.hpp"
#include <glad/glad.h> #include <glad/glad.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@ -47,11 +50,13 @@ namespace hex {
Window::Window() { Window::Window() {
this->initGLFW(); this->initGLFW();
this->initImGui(); this->initImGui();
this->initPlugins();
} }
Window::~Window() { Window::~Window() {
this->deinitImGui(); this->deinitImGui();
this->deinitGLFW(); this->deinitGLFW();
this->deinitPlugins();
for (auto &view : this->m_views) for (auto &view : this->m_views)
delete view; delete view;
@ -61,6 +66,9 @@ namespace hex {
while (!glfwWindowShouldClose(this->m_window)) { while (!glfwWindowShouldClose(this->m_window)) {
this->frameBegin(); this->frameBegin();
for (const auto &plugin : PluginHandler::getPlugins())
plugin.setImGuiContext(ImGui::GetCurrentContext());
for (const auto &call : View::getDeferedCalls()) for (const auto &call : View::getDeferedCalls())
call(); call();
View::getDeferedCalls().clear(); View::getDeferedCalls().clear();
@ -70,7 +78,15 @@ namespace hex {
continue; continue;
ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize()); ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize());
view->createView(); view->drawContent();
}
for (auto &view : this->m_pluginViews) {
if (!view->getWindowOpenState())
continue;
ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize());
view->drawContent();
} }
View::drawCommonInterfaces(); View::drawCommonInterfaces();
@ -145,11 +161,25 @@ namespace hex {
if (view->hasViewMenuItemEntry()) if (view->hasViewMenuItemEntry())
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState()); ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
} }
if (ImGui::BeginMenu("Plugin Views")) {
for (auto &view : this->m_pluginViews) {
if (view->hasViewMenuItemEntry())
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
}
ImGui::EndMenu();
}
ImGui::EndMenu(); ImGui::EndMenu();
} }
for (auto &view : this->m_views) { for (auto &view : this->m_views) {
view->createMenu(); view->drawMenu();
}
for (auto &view : this->m_pluginViews) {
view->drawMenu();
} }
if (ImGui::BeginMenu("View")) { if (ImGui::BeginMenu("View")) {
@ -173,12 +203,24 @@ namespace hex {
} }
if (auto &[key, mods] = Window::s_currShortcut; key != -1) { if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
bool shortcutHandled = false;
for (auto &view : this->m_pluginViews) {
if (view->getWindowOpenState()) {
if (view->handleShortcut(key, mods)) {
shortcutHandled = true;
break;
}
}
}
if (!shortcutHandled) {
for (auto &view : this->m_views) { for (auto &view : this->m_views) {
if (view->getWindowOpenState()) { if (view->getWindowOpenState()) {
if (view->handleShortcut(key, mods)) if (view->handleShortcut(key, mods))
break; break;
} }
} }
}
Window::s_currShortcut = { -1, -1 }; Window::s_currShortcut = { -1, -1 };
} }
@ -334,6 +376,15 @@ namespace hex {
ImGui_ImplOpenGL3_Init("#version 150"); ImGui_ImplOpenGL3_Init("#version 150");
} }
void Window::initPlugins() {
PluginHandler::load((std::filesystem::path(mainArgv[0]).parent_path() / "plugins").string());
for (const auto &plugin : PluginHandler::getPlugins()) {
if (auto view = plugin.createView(); view != nullptr)
this->m_pluginViews.push_back(view);
}
}
void Window::deinitGLFW() { void Window::deinitGLFW() {
glfwDestroyWindow(this->m_window); glfwDestroyWindow(this->m_window);
glfwTerminate(); glfwTerminate();
@ -345,4 +396,11 @@ namespace hex {
ImGui::DestroyContext(); ImGui::DestroyContext();
} }
void Window::deinitPlugins() {
PluginHandler::unload();
for (auto &view : this->m_pluginViews)
delete view;
}
} }