Compare commits

...

4 Commits

Author SHA1 Message Date
WerWolv
16b91eb715 fix: ImHex not loading python libraries correctly 2024-03-24 16:55:56 +01:00
WerWolv
8b9d09aa97 impr: Restructure and cleanup script loader and templates 2024-03-24 15:51:05 +01:00
WerWolv
ccb78246ab feat: Implemented full script loader API in python 2024-03-24 13:43:14 +01:00
WerWolv
3144d79605 feat: Added support for writing scripts in Python 2024-03-24 11:06:01 +01:00
25 changed files with 662 additions and 83 deletions

View File

@@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.16)
include(ImHexPlugin)
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
if (DEFAULT_MAGIC_FILE_PATH)
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
endif ()
add_imhex_plugin(
NAME
builtin
@@ -126,6 +121,11 @@ add_imhex_plugin(
LLVMDemangle
)
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
if (DEFAULT_MAGIC_FILE_PATH)
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
endif ()
if (WIN32)
target_link_libraries(builtin PRIVATE setupapi)
endif ()

View File

@@ -15,10 +15,10 @@ using namespace hex;
using namespace hex::plugin::decompress;
IMHEX_PLUGIN_FEATURES() {
{ "bzip2 Support", IMHEX_FEATURE_ENABLED(BZIP2) },
{ "zlib Support", IMHEX_FEATURE_ENABLED(ZLIB) },
{ "LZMA Support", IMHEX_FEATURE_ENABLED(LIBLZMA) },
{ "zstd Support", IMHEX_FEATURE_ENABLED(ZSTD) },
{ "bzip2", IMHEX_FEATURE_ENABLED(BZIP2) },
{ "zlib", IMHEX_FEATURE_ENABLED(ZLIB) },
{ "LZMA", IMHEX_FEATURE_ENABLED(LIBLZMA) },
{ "zstd", IMHEX_FEATURE_ENABLED(ZSTD) },
};
IMHEX_PLUGIN_SETUP("Decompressing", "WerWolv", "Support for decompressing data") {

View File

@@ -1,24 +1,11 @@
cmake_minimum_required(VERSION 3.16)
include(ImHexPlugin)
find_package(CoreClrEmbed)
add_imhex_plugin(
NAME
script_loader
SOURCES
source/plugin_script_loader.cpp
INCLUDES
include
LIBRARIES
fonts
ui
)
if (CoreClrEmbed_FOUND)
set(IMHEX_DOTNET_SCRIPT_SUPPORT ON)
add_library(nethost SHARED IMPORTED)
target_include_directories(nethost INTERFACE "${CoreClrEmbed_INCLUDE_DIRS}")
get_filename_component(CoreClrEmbed_FOLDER ${CoreClrEmbed_SHARED_LIBRARIES} DIRECTORY)
@@ -29,25 +16,70 @@ if (CoreClrEmbed_FOUND)
BUILD_RPATH ${CoreClrEmbed_FOLDER}
INSTALL_RPATH ${CoreClrEmbed_FOLDER})
target_link_directories(script_loader PRIVATE ${CoreClrEmbed_FOLDER})
target_include_directories(script_loader PRIVATE ${CoreClrEmbed_INCLUDE_DIRS})
target_compile_definitions(script_loader PRIVATE DOTNET_PLUGINS=1)
target_sources(script_loader PRIVATE
source/loaders/dotnet/dotnet_loader.cpp
source/script_api/v1/mem.cpp
source/script_api/v1/bookmarks.cpp
source/script_api/v1/ui.cpp
source/script_api/v1/logger.cpp
)
set(EXTRA_BUNDLE_LIBRARY_PATHS "${CoreClrEmbed_FOLDER}" PARENT_SCOPE)
if (IMHEX_BUNDLE_DOTNET)
install(FILES ${CoreClrEmbed_SHARED_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif ()
endif()
endif()
add_subdirectory(dotnet)
find_package(Python3 COMPONENTS Interpreter Development.Embed)
if (Python3_FOUND)
set(IMHEX_PYTHON_SCRIPT_SUPPORT ON)
get_target_property(PYTHON_LIBRARY Python3::Python IMPORTED_LOCATION)
get_target_property(PYTHON_INCLUDE_DIR Python3::Python INTERFACE_INCLUDE_DIRECTORIES)
endif()
add_subdirectory(support/c)
add_imhex_plugin(
NAME
script_loader
SOURCES
source/plugin_script_loader.cpp
INCLUDES
include
LIBRARIES
c_api
fonts
FEATURES
DOTNET
PYTHON
)
if (IMHEX_DOTNET_SCRIPT_SUPPORT)
message(STATUS "Enabling .NET Scripting support!")
target_link_directories(script_loader PRIVATE ${CoreClrEmbed_FOLDER})
target_include_directories(script_loader PRIVATE ${CoreClrEmbed_INCLUDE_DIRS})
target_compile_definitions(script_loader PRIVATE IMHEX_DOTNET_SCRIPT_SUPPORT=1)
target_sources(script_loader PRIVATE
source/loaders/dotnet/dotnet_loader.cpp
)
add_subdirectory(support/dotnet)
add_dependencies(script_loader AssemblyLoader)
enable_plugin_feature(DOTNET)
endif()
endif ()
if (IMHEX_PYTHON_SCRIPT_SUPPORT)
message(STATUS "Enabling Python Scripting support!")
target_compile_definitions(script_loader PRIVATE IMHEX_PYTHON_SCRIPT_SUPPORT=1)
target_sources(script_loader PRIVATE
source/loaders/python/python_loader.cpp
source/loaders/python/library_wrapper.cpp
)
target_include_directories(script_loader PRIVATE ${PYTHON_INCLUDE_DIR})
target_compile_definitions(script_loader PRIVATE PYTHON_LIBRARY_PATH="${PYTHON_LIBRARY}")
enable_plugin_feature(PYTHON)
endif()

View File

@@ -10,7 +10,7 @@ namespace hex::script::loader {
class DotNetLoader : public ScriptLoader {
public:
DotNetLoader() = default;
DotNetLoader() : ScriptLoader(".NET") {}
~DotNetLoader() override = default;
bool initialize() override;

View File

@@ -3,30 +3,44 @@
#include <functional>
#include <string>
#include <vector>
#include <hex/helpers/utils.hpp>
#if defined(OS_WINDOWS)
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace hex::script::loader {
class ScriptLoader;
struct Script {
std::string name;
std::function<void()> entryPoint;
const ScriptLoader *loader;
};
class ScriptLoader {
public:
ScriptLoader() = default;
ScriptLoader(std::string typeName) : m_typeName(std::move(typeName)) {}
virtual ~ScriptLoader() = default;
virtual bool initialize() = 0;
virtual bool loadAll() = 0;
void addScript(std::string name, std::function<void()> entryPoint) {
m_scripts.emplace_back(std::move(name), std::move(entryPoint));
m_scripts.emplace_back(std::move(name), std::move(entryPoint), this);
}
const auto& getScripts() const {
return m_scripts;
}
const std::string& getTypeName() const {
return m_typeName;
}
protected:
void clearScripts() {
m_scripts.clear();
@@ -34,6 +48,49 @@ namespace hex::script::loader {
private:
std::vector<Script> m_scripts;
std::string m_typeName;
};
#if defined(OS_WINDOWS)
inline void *loadLibrary(const wchar_t *path) {
try {
HMODULE h = ::LoadLibraryW(path);
return h;
} catch (...) {
return nullptr;
}
}
inline void *loadLibrary(const char *path) {
try {
auto utf16Path = hex::utf8ToUtf16(path);
HMODULE h = ::LoadLibraryW(utf16Path.c_str());
return h;
} catch (...) {
return nullptr;
}
}
template<typename T>
T getExport(void *h, const char *name) {
try {
FARPROC f = ::GetProcAddress(static_cast<HMODULE>(h), name);
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(f));
} catch (...) {
return nullptr;
}
}
#else
inline void *loadLibrary(const char *path) {
void *h = dlopen(path, RTLD_LAZY);
return h;
}
template<typename T>
T getExport(void *h, const char *name) {
void *f = dlsym(h, name);
return reinterpret_cast<T>(f);
}
#endif
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <loaders/loader.hpp>
#include <wolv/io/fs.hpp>
#include <functional>
namespace hex::script::loader {
class PythonLoader : public ScriptLoader {
public:
PythonLoader() : ScriptLoader("Python") {}
~PythonLoader() override = default;
bool initialize() override;
bool loadAll() override;
private:
std::vector<void*> m_loadedModules;
};
}

View File

@@ -3,10 +3,8 @@
#include <stdexcept>
#if defined(OS_WINDOWS)
#include <Windows.h>
#define STRING(str) L##str
#else
#include <dlfcn.h>
#define STRING(str) str
#endif
@@ -34,38 +32,6 @@ namespace hex::script::loader {
using get_hostfxr_path_fn = int(*)(char_t * buffer, size_t * buffer_size, const get_hostfxr_parameters *parameters);
#if defined(OS_WINDOWS)
void *loadLibrary(const char_t *path) {
try {
HMODULE h = ::LoadLibraryW(path);
return h;
} catch (...) {
return nullptr;
}
}
template<typename T>
T getExport(void *h, const char *name) {
try {
FARPROC f = ::GetProcAddress(static_cast<HMODULE>(h), name);
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(f));
} catch (...) {
return nullptr;
}
}
#else
void *loadLibrary(const char_t *path) {
void *h = dlopen(path, RTLD_LAZY);
return h;
}
template<typename T>
T getExport(void *h, const char *name) {
void *f = dlsym(h, name);
return reinterpret_cast<T>(f);
}
#endif
hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
hostfxr_close_fn hostfxr_close = nullptr;

View File

@@ -0,0 +1,112 @@
#if defined(__declspec)
#undef __declspec
#define __declspec(x)
#endif
#include <Python.h>
#include <loaders/loader.hpp>
#define FUNCTION_DEFINITION(ret, name, args1, args2) \
decltype(name) *name##Func = nullptr; \
extern "C" ret name args1 { \
return name##Func args2; \
}
#define INIT_FUNCTION(name) name##Func = hex::script::loader::getExport<decltype(name##Func)>(pythonLibrary, #name)
FUNCTION_DEFINITION(void, PyPreConfig_InitPythonConfig, (PyPreConfig *config), (config))
FUNCTION_DEFINITION(PyStatus, Py_PreInitialize, (const PyPreConfig *src_config), (src_config))
FUNCTION_DEFINITION(int, PyStatus_Exception, (PyStatus err), (err))
FUNCTION_DEFINITION(void, Py_Initialize, (), ())
FUNCTION_DEFINITION(void, Py_Finalize, (), ())
FUNCTION_DEFINITION(PyInterpreterState *, PyInterpreterState_Get, (), ())
FUNCTION_DEFINITION(PyThreadState *, PyEval_SaveThread, (), ())
FUNCTION_DEFINITION(void, PyEval_RestoreThread, (PyThreadState * state), (state))
FUNCTION_DEFINITION(void, PyErr_Fetch, (PyObject **ptype, PyObject **pvalue, PyObject **ptraceback), (ptype, pvalue, ptraceback))
FUNCTION_DEFINITION(void, PyErr_NormalizeException, (PyObject **ptype, PyObject **pvalue, PyObject **ptraceback), (ptype, pvalue, ptraceback))
FUNCTION_DEFINITION(PyObject *, PyUnicode_FromString, (const char *u), (u))
FUNCTION_DEFINITION(PyObject *, PyImport_Import, (PyObject *name), (name))
FUNCTION_DEFINITION(void, _Py_Dealloc, (PyObject *pobj), (pobj))
FUNCTION_DEFINITION(PyObject *, PyModule_GetDict, (PyObject *pobj), (pobj))
FUNCTION_DEFINITION(PyObject *, PyDict_GetItemString, (PyObject *dp, const char *key), (dp, key))
FUNCTION_DEFINITION(int, PyCallable_Check, (PyObject *dp), (dp))
FUNCTION_DEFINITION(PyObject *, PyTuple_New, (Py_ssize_t len), (len))
FUNCTION_DEFINITION(int, PyTuple_SetItem, (PyObject *p, Py_ssize_t pos, PyObject *o), (p, pos, o))
FUNCTION_DEFINITION(PyObject *, PyObject_CallObject, (PyObject *callable, PyObject *args), (callable, args))
FUNCTION_DEFINITION(PyObject *, PyUnicode_Join, (PyObject *separator, PyObject *iterable), (separator, iterable))
FUNCTION_DEFINITION(const char *, PyUnicode_AsUTF8, (PyObject *unicode), (unicode))
FUNCTION_DEFINITION(void, PyErr_Clear, (), ())
FUNCTION_DEFINITION(int, PyModule_AddStringConstant, (PyObject *module, const char *name, const char *value), (module, name, value))
FUNCTION_DEFINITION(PyObject *, PyEval_GetBuiltins, (), ())
FUNCTION_DEFINITION(int, PyDict_SetItemString, (PyObject *dp, const char *key, PyObject *item), (dp, key, item))
FUNCTION_DEFINITION(PyObject *, PyRun_StringFlags, (const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags), (str, start, globals, locals, flags))
FUNCTION_DEFINITION(void, PyThreadState_Clear, (PyThreadState *tstate), (tstate))
FUNCTION_DEFINITION(void, PyThreadState_DeleteCurrent, (), ())
FUNCTION_DEFINITION(PyThreadState *, PyThreadState_New, (PyInterpreterState *interp), (interp))
FUNCTION_DEFINITION(PyObject *, PyImport_AddModule, (const char *name), (name))
FUNCTION_DEFINITION(PyObject *, PyModule_New, (const char *name), (name))
FUNCTION_DEFINITION(PyObject *, PyObject_GetAttrString, (PyObject *pobj, const char *name), (pobj, name))
FUNCTION_DEFINITION(int, PyObject_HasAttrString, (PyObject *pobj, const char *name), (pobj, name))
FUNCTION_DEFINITION(PyObject*, PySys_GetObject, (const char *name), (name))
FUNCTION_DEFINITION(int, PyList_Append, (PyObject *plist, PyObject *pvalue), (plist, pvalue))
bool initPythonLoader() {
void *pythonLibrary = nullptr;
for (const std::fs::path &path : { std::fs::path(PYTHON_LIBRARY_PATH), std::fs::path(PYTHON_LIBRARY_PATH).filename() }) {
pythonLibrary = hex::script::loader::loadLibrary(path.c_str());
if (pythonLibrary != nullptr) {
break;
}
}
if (pythonLibrary == nullptr)
return false;
INIT_FUNCTION(PyPreConfig_InitPythonConfig);
INIT_FUNCTION(Py_PreInitialize);
INIT_FUNCTION(Py_Initialize);
INIT_FUNCTION(Py_Finalize);
INIT_FUNCTION(PySys_GetObject);
INIT_FUNCTION(PyEval_SaveThread);
INIT_FUNCTION(PyEval_RestoreThread);
INIT_FUNCTION(PyEval_GetBuiltins);
INIT_FUNCTION(PyRun_StringFlags);
INIT_FUNCTION(PyErr_Fetch);
INIT_FUNCTION(PyErr_NormalizeException);
INIT_FUNCTION(PyErr_Clear);
INIT_FUNCTION(PyStatus_Exception);
INIT_FUNCTION(PyThreadState_Clear);
INIT_FUNCTION(PyThreadState_DeleteCurrent);
INIT_FUNCTION(PyThreadState_New);
INIT_FUNCTION(PyInterpreterState_Get);
INIT_FUNCTION(PyUnicode_FromString);
INIT_FUNCTION(PyUnicode_Join);
INIT_FUNCTION(PyUnicode_AsUTF8);
INIT_FUNCTION(PyTuple_New);
INIT_FUNCTION(PyTuple_SetItem);
INIT_FUNCTION(PyImport_Import);
INIT_FUNCTION(PyImport_AddModule);
INIT_FUNCTION(PyModule_New);
INIT_FUNCTION(PyModule_AddStringConstant);
INIT_FUNCTION(PyModule_GetDict);
INIT_FUNCTION(PyDict_GetItemString);
INIT_FUNCTION(PyDict_SetItemString);
INIT_FUNCTION(PyList_Append);
INIT_FUNCTION(PyCallable_Check);
INIT_FUNCTION(PyObject_CallObject);
INIT_FUNCTION(PyObject_GetAttrString);
INIT_FUNCTION(PyObject_HasAttrString);
INIT_FUNCTION(_Py_Dealloc);
return true;
}

View File

@@ -0,0 +1,168 @@
#include <loaders/python/python_loader.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <hex/helpers/utils.hpp>
#include <romfs/romfs.hpp>
#if defined(__declspec)
#undef __declspec
#define __declspec(x)
#endif
#include <Python.h>
bool initPythonLoader();
namespace hex::script::loader {
static PyInterpreterState *mainThreadState;
bool PythonLoader::initialize() {
if (!initPythonLoader())
return false;
PyPreConfig preconfig;
PyPreConfig_InitPythonConfig(&preconfig);
preconfig.utf8_mode = 1;
auto status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
return false;
}
Py_Initialize();
mainThreadState = PyInterpreterState_Get();
PyEval_SaveThread();
return true;
}
namespace {
std::string getCurrentTraceback() {
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
PyObject *pModuleName = PyUnicode_FromString("traceback");
PyObject *pModule = PyImport_Import(pModuleName);
Py_DECREF(pModuleName);
if (pModule != nullptr) {
PyObject *pDict = PyModule_GetDict(pModule);
PyObject *pFunc = PyDict_GetItemString(pDict, "format_exception");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, ptype);
PyTuple_SetItem(pArgs, 1, pvalue);
PyTuple_SetItem(pArgs, 2, ptraceback);
PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pResult != NULL) {
const char *errorMessage = PyUnicode_AsUTF8(PyUnicode_Join(PyUnicode_FromString(""), pResult));
Py_DECREF(pResult);
Py_DECREF(pModule);
return errorMessage;
}
}
Py_DECREF(pModule);
}
PyErr_Clear();
return "";
}
void populateModule(PyObject *pyModule, const std::string &sourceCode) {
PyModule_AddStringConstant(pyModule, "__file__", "");
PyObject *localDict = PyModule_GetDict(pyModule);
PyObject *builtins = PyEval_GetBuiltins();
PyDict_SetItemString(localDict, "__builtins__", builtins);
PyErr_Clear();
PyObject *pyValue = PyRun_String(sourceCode.c_str(), Py_file_input, localDict, localDict);
if (pyValue != nullptr) {
Py_DECREF(pyValue);
} else {
log::error("{}", getCurrentTraceback());
}
}
}
bool PythonLoader::loadAll() {
this->clearScripts();
for (const auto &imhexPath : hex::fs::getDefaultPaths(hex::fs::ImHexPath::Scripts)) {
auto directoryPath = imhexPath / "custom" / "python";
if (!wolv::io::fs::exists(directoryPath))
wolv::io::fs::createDirectories(directoryPath);
if (!wolv::io::fs::exists(directoryPath))
continue;
for (const auto &entry : std::fs::directory_iterator(directoryPath)) {
if (!entry.is_directory())
continue;
const auto &scriptFolder = entry.path();
const auto scriptPath = scriptFolder / "main.py";
if (!std::fs::exists(scriptPath))
continue;
auto scriptPathString = wolv::util::toUTF8String(scriptPath);
wolv::io::File scriptFile(scriptPathString, wolv::io::File::Mode::Read);
if (!scriptFile.isValid())
continue;
PyThreadState* ts = PyThreadState_New(mainThreadState);
PyEval_RestoreThread(ts);
ON_SCOPE_EXIT {
PyThreadState_Clear(ts);
PyThreadState_DeleteCurrent();
};
PyObject* sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString(wolv::util::toUTF8String(scriptFolder).c_str()));
PyObject *imhexInternalModule = PyImport_AddModule("__imhex_internal__");
PyModule_AddStringConstant(imhexInternalModule, "script_loader_handle", hex::format("{}", reinterpret_cast<intptr_t>(hex::getContainingModule((void*)&getCurrentTraceback))).c_str());
PyObject *mainModule = PyModule_New(scriptPathString.c_str());
populateModule(mainModule, scriptFile.readString());
if (PyObject_HasAttrString(mainModule, "main")) {
this->addScript(entry.path().stem().string(), [mainModule] {
PyThreadState* ts = PyThreadState_New(mainThreadState);
PyEval_RestoreThread(ts);
ON_SCOPE_EXIT {
PyThreadState_Clear(ts);
PyThreadState_DeleteCurrent();
};
auto mainFunction = PyObject_GetAttrString(mainModule, "main");
PyObject_CallObject(mainFunction, nullptr);
Py_DECREF(mainFunction);
});
}
m_loadedModules.push_back(mainModule);
}
}
return true;
}
}

View File

@@ -7,6 +7,7 @@
#include <hex/ui/imgui_imhex_extensions.h>
#include <loaders/dotnet/dotnet_loader.hpp>
#include <loaders/python/python_loader.hpp>
#include <romfs/romfs.hpp>
#include <nlohmann/json.hpp>
@@ -15,8 +16,11 @@ using namespace hex;
using namespace hex::script::loader;
using ScriptLoaders = std::tuple<
#if defined(DOTNET_PLUGINS)
DotNetLoader
#if defined(IMHEX_DOTNET_SCRIPT_SUPPORT)
DotNetLoader,
#endif
#if defined(IMHEX_PYTHON_SCRIPT_SUPPORT)
PythonLoader
#endif
>;
@@ -91,9 +95,9 @@ namespace {
}
for (const auto &script : scripts) {
const auto &[name, entryPoint] = *script;
const auto &[name, entryPoint, loader] = *script;
if (ImGui::MenuItem(name.c_str())) {
if (ImGui::MenuItem(name.c_str(), loader->getTypeName().c_str())) {
runnerTask = TaskManager::createTask("Running script...", TaskManager::NoProgress, [entryPoint](auto&) {
entryPoint();
});
@@ -115,6 +119,11 @@ namespace {
}
IMHEX_PLUGIN_FEATURES() {
{ ".NET", IMHEX_FEATURE_ENABLED(DOTNET) },
{ "Python", IMHEX_FEATURE_ENABLED(PYTHON) },
};
IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") {
hex::log::debug("Using romfs: '{}'", romfs::name());
for (auto &path : romfs::list("lang"))
@@ -123,4 +132,4 @@ IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") {
if (initializeAllLoaders()) {
addScriptsMenu();
}
}
}

View File

@@ -0,0 +1,14 @@
project(c_api)
add_library(c_api OBJECT
source/script_api/v1/bookmarks.cpp
source/script_api/v1/logger.cpp
source/script_api/v1/mem.cpp
source/script_api/v1/ui.cpp
)
target_include_directories(c_api PUBLIC
include
)
target_link_libraries(c_api PRIVATE libimhex ui)
target_compile_definitions(c_api PRIVATE IMHEX_PROJECT_NAME="Script")

View File

@@ -26,6 +26,14 @@ SCRIPT_API(void writeMemory, u64 address, size_t size, const void *buffer) {
provider->write(address, buffer, size);
}
SCRIPT_API(u64 getBaseAddress) {
return hex::ImHexApi::Provider::get()->getBaseAddress();
}
SCRIPT_API(u64 getDataSize) {
return hex::ImHexApi::Provider::get()->getSize();
}
SCRIPT_API(bool getSelection, u64 *start, u64 *end) {
if (start == nullptr || end == nullptr)
return false;

View File

@@ -1,5 +1,4 @@
using ImHex;
using ImGuiNET;
class Script {

View File

@@ -0,0 +1,180 @@
import __imhex_internal__
import ctypes
from enum import Enum
from abc import ABC, abstractmethod
_script_loader = ctypes.CDLL("Script Loader", ctypes.DEFAULT_MODE, int(__imhex_internal__.script_loader_handle))
_callback_refs = []
class Color:
def __init__(self, r: int, g: int, b: int, a: int):
self.r = r
self.g = g
self.b = b
self.a = a
def to_int(self):
return (self.a << 24) | (self.b << 16) | (self.g << 8) | self.r
class UI:
@staticmethod
def show_message_box(message: str):
_script_loader.showMessageBoxV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def show_input_text_box(title: str, message: str, buffer_size: int = 256):
buffer = ctypes.create_string_buffer(buffer_size)
_script_loader.showInputTextBoxV1(ctypes.create_string_buffer(title.encode("utf-8")),
ctypes.create_string_buffer(message.encode("utf-8")),
buffer, buffer_size)
return buffer.value.decode("utf-8")
@staticmethod
def show_yes_no_question_box(title: str, message: str):
result = ctypes.c_bool()
_script_loader.showYesNoQuestionBoxV1(ctypes.create_string_buffer(title.encode("utf-8")),
ctypes.create_string_buffer(message.encode("utf-8")),
ctypes.byref(result))
class ToastType(Enum):
INFO = 0
WARNING = 1
ERROR = 2
@staticmethod
def show_toast(message: str, toast_type: ToastType):
_script_loader.showToastV1(ctypes.create_string_buffer(message.encode("utf-8")), toast_type.value)
@staticmethod
def get_imgui_context():
return _script_loader.getImGuiContextV1()
@staticmethod
def register_view(icon: str, name: str, draw_callback):
draw_function = ctypes.CFUNCTYPE(None)
global _callback_refs
_callback_refs.append(draw_function(draw_callback))
_script_loader.registerViewV1(ctypes.create_string_buffer(icon.encode("utf-8")),
ctypes.create_string_buffer(name.encode("utf-8")),
_callback_refs[-1])
@staticmethod
def add_menu_item(icon: str, menu_name: str, item_name: str, callback):
callback_function = ctypes.CFUNCTYPE(None)
global _callback_refs
_callback_refs.append(callback_function(callback))
_script_loader.addMenuItemV1(ctypes.create_string_buffer(icon.encode("utf-8")),
ctypes.create_string_buffer(menu_name.encode("utf-8")),
ctypes.create_string_buffer(item_name.encode("utf-8")),
_callback_refs[-1])
class Bookmarks:
@staticmethod
def create_bookmark(address: int, size: int, color: Color, name: str, description: str = ""):
_script_loader.addBookmarkV1(address, size,
color.to_int(),
ctypes.create_string_buffer(name.encode("utf-8")),
ctypes.create_string_buffer(description.encode("utf-8")))
class Logger:
@staticmethod
def print(message: str):
_script_loader.logPrintV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def println(message: str):
_script_loader.logPrintlnV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def debug(message: str):
_script_loader.logDebugV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def info(message: str):
_script_loader.logInfoV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def warn(message: str):
_script_loader.logWarnV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def error(message: str):
_script_loader.logErrorV1(ctypes.create_string_buffer(message.encode("utf-8")))
@staticmethod
def fatal(message: str):
_script_loader.logFatalV1(ctypes.create_string_buffer(message.encode("utf-8")))
class Memory:
@staticmethod
def read(address: int, size: int):
buffer = ctypes.create_string_buffer(size)
_script_loader.readMemoryV1(address, buffer, size)
return buffer.raw
@staticmethod
def write(address: int, data: bytes):
_script_loader.writeMemoryV1(address, data, len(data))
@staticmethod
def get_base_address():
return _script_loader.getBaseAddressV1()
@staticmethod
def get_data_size():
return _script_loader.getDataSizeV1()
@staticmethod
def get_selection():
start = ctypes.c_uint64()
end = ctypes.c_uint64()
if not _script_loader.getSelectionV1(ctypes.byref(start), ctypes.byref(end)):
return None, None
else:
return start.value, end.value
class Provider(ABC):
def __init__(self, type_name, name):
self.type_name = type_name
self.name = name
@abstractmethod
def read(self, address: int, size: int):
pass
@abstractmethod
def write(self, address: int, data: bytes):
pass
@abstractmethod
def get_size(self):
pass
@staticmethod
def register_provider(provider):
provider_read_function = ctypes.CFUNCTYPE(None, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_uint64)
provider_write_function = ctypes.CFUNCTYPE(None, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_uint64)
provider_get_size_function = ctypes.CFUNCTYPE(ctypes.c_uint64)
global _callback_refs
_callback_refs.append(provider_read_function(provider.read))
_callback_refs.append(provider_write_function(provider.write))
_callback_refs.append(provider_get_size_function(provider.get_size))
_script_loader.registerMemoryProviderV1(ctypes.create_string_buffer(provider.type_name.encode("utf-8")),
ctypes.create_string_buffer(provider.name.encode("utf-8")),
_callback_refs[-3],
_callback_refs[-2],
_callback_refs[-1])

View File

@@ -0,0 +1,11 @@
import imhex
# ImHex executes the entire script when it's being loaded and only adds it to the
# Run Script menu if there is a main function.
def main():
"""
This is the main function that will be called when the script is executed
"""
pass