mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 07:47:03 -05:00
Compare commits
4 Commits
releases/v
...
python_scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16b91eb715 | ||
|
|
8b9d09aa97 | ||
|
|
ccb78246ab | ||
|
|
3144d79605 |
@@ -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 ()
|
||||
@@ -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") {
|
||||
|
||||
@@ -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()
|
||||
@@ -10,7 +10,7 @@ namespace hex::script::loader {
|
||||
|
||||
class DotNetLoader : public ScriptLoader {
|
||||
public:
|
||||
DotNetLoader() = default;
|
||||
DotNetLoader() : ScriptLoader(".NET") {}
|
||||
~DotNetLoader() override = default;
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
112
plugins/script_loader/source/loaders/python/library_wrapper.cpp
Normal file
112
plugins/script_loader/source/loaders/python/library_wrapper.cpp
Normal 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;
|
||||
}
|
||||
168
plugins/script_loader/source/loaders/python/python_loader.cpp
Normal file
168
plugins/script_loader/source/loaders/python/python_loader.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
plugins/script_loader/support/c/CMakeLists.txt
Normal file
14
plugins/script_loader/support/c/CMakeLists.txt
Normal 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")
|
||||
@@ -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;
|
||||
@@ -1,5 +1,4 @@
|
||||
using ImHex;
|
||||
using ImGuiNET;
|
||||
|
||||
class Script {
|
||||
|
||||
|
||||
180
plugins/script_loader/templates/Python/imhex.py
Normal file
180
plugins/script_loader/templates/Python/imhex.py
Normal 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])
|
||||
11
plugins/script_loader/templates/Python/main.py
Normal file
11
plugins/script_loader/templates/Python/main.py
Normal 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
|
||||
Reference in New Issue
Block a user