diff --git a/include/helpers/event.hpp b/include/helpers/event.hpp index d1cdb95f1..ca17c68d8 100644 --- a/include/helpers/event.hpp +++ b/include/helpers/event.hpp @@ -16,6 +16,7 @@ namespace hex { SelectionChangeRequest, AddBookmark, + AppendPatternLanguageCode, ProjectFileStore, ProjectFileLoad diff --git a/include/helpers/loader_script_handler.hpp b/include/helpers/loader_script_handler.hpp index b39687087..3549bf9e4 100644 --- a/include/helpers/loader_script_handler.hpp +++ b/include/helpers/loader_script_handler.hpp @@ -25,6 +25,9 @@ namespace hex { static PyObject* Py_getFilePath(PyObject *self, PyObject *args); static PyObject* Py_addPatch(PyObject *self, PyObject *args); static PyObject* Py_addBookmark(PyObject *self, PyObject *args); + + static PyObject* Py_addStruct(PyObject *self, PyObject *args); + static PyObject* Py_addUnion(PyObject *self, PyObject *args); }; } \ No newline at end of file diff --git a/source/helpers/loader_script_handler.cpp b/source/helpers/loader_script_handler.cpp index 26ec379b5..e11429a2c 100644 --- a/source/helpers/loader_script_handler.cpp +++ b/source/helpers/loader_script_handler.cpp @@ -6,9 +6,13 @@ #define PY_SSIZE_T_CLEAN #include +#include + #include #include +using namespace std::literals::string_literals; + namespace hex { PyObject* LoaderScript::Py_getFilePath(PyObject *self, PyObject *args) { @@ -64,28 +68,122 @@ namespace hex { Py_RETURN_NONE; } + static PyObject* createStructureType(std::string keyword, PyObject *args) { + auto type = PyTuple_GetItem(args, 0); + if (type == nullptr) { + PyErr_BadArgument(); + return nullptr; + } + + auto instance = PyObject_CallObject(type, nullptr); + if (instance == nullptr) { + PyErr_BadArgument(); + return nullptr; + } + + hex::ScopeExit instanceCleanup([&]{ Py_DECREF(instance); }); + + if (instance->ob_type->tp_base == nullptr || instance->ob_type->tp_base->tp_name != "ImHexType"s) { + PyErr_SetString(PyExc_TypeError, "class type must extend from ImHexType"); + return nullptr; + } + + auto dict = instance->ob_type->tp_dict; + if (dict == nullptr) { + PyErr_BadArgument(); + return nullptr; + } + + auto annotations = PyDict_GetItemString(dict, "__annotations__"); + if (annotations == nullptr) { + PyErr_BadArgument(); + return nullptr; + } + + auto list = PyDict_Items(annotations); + if (list == nullptr) { + PyErr_BadArgument(); + return nullptr; + } + + hex::ScopeExit listCleanup([&]{ Py_DECREF(list); }); + + std::string code = keyword + " " + instance->ob_type->tp_name + " {\n"; + + for (u16 i = 0; i < PyList_Size(list); i++) { + auto item = PyList_GetItem(list, i); + + auto memberName = PyUnicode_AsUTF8(PyTuple_GetItem(item, 0)); + auto memberType = PyTuple_GetItem(item, 1); + if (memberType == nullptr) { + PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType"); + return nullptr; + } + auto memberTypeInstance = PyObject_CallObject(memberType, nullptr); + if (memberTypeInstance == nullptr || memberTypeInstance->ob_type->tp_base == nullptr || memberTypeInstance->ob_type->tp_base->tp_name != "ImHexType"s) { + PyErr_SetString(PyExc_TypeError, "member needs to have a annotation extending from ImHexType"); + Py_DECREF(memberTypeInstance); + + return nullptr; + } + + code += " "s + memberTypeInstance->ob_type->tp_name + " "s + memberName + ";\n"; + + Py_DECREF(memberTypeInstance); + } + + code += "};\n"; + + View::postEvent(Events::AppendPatternLanguageCode, code.c_str()); + + Py_RETURN_NONE; + } + + PyObject* LoaderScript::Py_addStruct(PyObject *self, PyObject *args) { + return createStructureType("struct", args); + } + + PyObject* LoaderScript::Py_addUnion(PyObject *self, PyObject *args) { + return createStructureType("union", args); + } + bool LoaderScript::processFile(std::string_view scriptPath) { Py_SetProgramName(Py_DecodeLocale(__argv[0], nullptr)); - Py_SetPythonHome(Py_DecodeLocale(std::filesystem::path(__argv[0]).parent_path().string().c_str(), nullptr)); + if (std::filesystem::exists(std::filesystem::path(__argv[0]).parent_path().string() + "/lib/python3.8")) + Py_SetPythonHome(Py_DecodeLocale(std::filesystem::path(__argv[0]).parent_path().string().c_str(), nullptr)); + + PyImport_AppendInittab("_imhex", []() -> PyObject* { - PyImport_AppendInittab("imhex", []() -> PyObject* { static PyMethodDef ImHexMethods[] = { - { "get_file_path", &LoaderScript::Py_getFilePath, METH_NOARGS, "Returns the path of the file being loaded." }, - { "patch", &LoaderScript::Py_addPatch, METH_VARARGS, "Patches a region of memory" }, - { "add_bookmark", &LoaderScript::Py_addBookmark, METH_VARARGS, "Adds a bookmark" }, - { nullptr, nullptr, 0, nullptr } + { "get_file_path", &LoaderScript::Py_getFilePath, METH_NOARGS, "Returns the path of the file being loaded." }, + { "patch", &LoaderScript::Py_addPatch, METH_VARARGS, "Patches a region of memory" }, + { "add_bookmark", &LoaderScript::Py_addBookmark, METH_VARARGS, "Adds a bookmark" }, + { "add_struct", &LoaderScript::Py_addStruct, METH_VARARGS, "Adds a struct" }, + { "add_union", &LoaderScript::Py_addUnion, METH_VARARGS, "Adds a union" }, + { nullptr, nullptr, 0, nullptr } }; static PyModuleDef ImHexModule = { PyModuleDef_HEAD_INIT, "imhex", nullptr, -1, ImHexMethods, nullptr, nullptr, nullptr, nullptr }; - return PyModule_Create(&ImHexModule); + auto module = PyModule_Create(&ImHexModule); + if (module == nullptr) + return nullptr; + + return module; }); Py_Initialize(); + { + auto sysPath = PySys_GetObject("path"); + auto path = PyUnicode_FromString("lib"); + + PyList_Insert(sysPath, 0, path); + } + FILE *scriptFile = fopen(scriptPath.data(), "r"); PyRun_SimpleFile(scriptFile, scriptPath.data()); diff --git a/source/views/view_pattern.cpp b/source/views/view_pattern.cpp index d9b9bbe50..1f95fb5ac 100644 --- a/source/views/view_pattern.cpp +++ b/source/views/view_pattern.cpp @@ -87,6 +87,13 @@ namespace hex { this->parsePattern(this->m_textEditor.GetText().data()); }); + View::subscribeEvent(Events::AppendPatternLanguageCode, [this](const void *userData) { + const char *code = static_cast(userData); + + this->m_textEditor.InsertText("\n"); + this->m_textEditor.InsertText(code); + }); + View::subscribeEvent(Events::FileLoaded, [this](const void* userData) { if (!this->m_textEditor.GetText().empty()) return;