2023-07-15 14:29:14 +02:00
|
|
|
#include <loaders/dotnet/dotnet_loader.hpp>
|
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
#include <Windows.h>
|
|
|
|
#define STRING(str) L##str
|
|
|
|
#else
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#define STRING(str) str
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
|
|
#include <nethost.h>
|
|
|
|
#include <coreclr_delegates.h>
|
|
|
|
#include <hostfxr.h>
|
2024-03-12 23:17:49 +01:00
|
|
|
#include <imgui.h>
|
|
|
|
#include <hex/api/plugin_manager.hpp>
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
#include <hex/helpers/fs.hpp>
|
|
|
|
#include <wolv/io/fs.hpp>
|
|
|
|
#include <wolv/utils/guards.hpp>
|
|
|
|
#include <wolv/utils/string.hpp>
|
|
|
|
#include <hex/helpers/fmt.hpp>
|
|
|
|
#include <hex/helpers/logger.hpp>
|
2024-03-12 23:17:49 +01:00
|
|
|
#include <hex/helpers/utils.hpp>
|
|
|
|
|
|
|
|
extern "C" void igSetCurrentContext(ImGuiContext* ctx);
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
namespace hex::script::loader {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2023-11-10 20:47:08 +01:00
|
|
|
using get_hostfxr_path_fn = int(*)(char_t * buffer, size_t * buffer_size, const get_hostfxr_parameters *parameters);
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
void *loadLibrary(const char_t *path) {
|
|
|
|
try {
|
|
|
|
HMODULE h = ::LoadLibraryW(path);
|
2023-11-10 20:47:08 +01:00
|
|
|
return h;
|
2023-07-15 14:29:14 +02:00
|
|
|
} catch (...) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
T getExport(void *h, const char *name) {
|
|
|
|
try {
|
2023-11-10 20:47:08 +01:00
|
|
|
FARPROC f = ::GetProcAddress(static_cast<HMODULE>(h), name);
|
2023-11-10 22:21:19 +01:00
|
|
|
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(f));
|
2023-07-15 14:29:14 +02:00
|
|
|
} catch (...) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
void *loadLibrary(const char_t *path) {
|
2023-07-20 20:59:06 +02:00
|
|
|
void *h = dlopen(path, RTLD_LAZY);
|
2023-07-15 14:29:14 +02:00
|
|
|
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;
|
2024-03-12 23:17:49 +01:00
|
|
|
hostfxr_set_runtime_property_value_fn hostfxr_set_runtime_property_value = nullptr;
|
|
|
|
hostfxr_set_error_writer_fn hostfxr_set_error_writer = nullptr;
|
|
|
|
|
|
|
|
void* pInvokeOverride(const char *libraryName, const char *symbolName) {
|
|
|
|
auto library = std::string_view(libraryName);
|
|
|
|
if (library == "cimgui") {
|
|
|
|
return getExport<void*>(ImHexApi::System::getLibImHexModuleHandle(), symbolName);
|
|
|
|
} else if (library == "ImHex") {
|
2024-03-13 08:38:40 +01:00
|
|
|
return getExport<void*>(hex::getContainingModule((void*)&pInvokeOverride), symbolName);
|
2024-03-12 23:17:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
bool loadHostfxr() {
|
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
auto netHostLibrary = loadLibrary(L"nethost.dll");
|
|
|
|
#elif defined(OS_LINUX)
|
|
|
|
auto netHostLibrary = loadLibrary("libnethost.so");
|
|
|
|
#elif defined(OS_MACOS)
|
2023-10-01 18:07:16 +02:00
|
|
|
void *netHostLibrary = nullptr;
|
|
|
|
for (const auto &pluginPath : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
|
|
|
|
auto frameworksPath = pluginPath.parent_path().parent_path() / "Frameworks";
|
|
|
|
|
|
|
|
netHostLibrary = loadLibrary((frameworksPath / "libnethost.dylib").c_str());
|
|
|
|
if (netHostLibrary != nullptr)
|
|
|
|
break;
|
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
#endif
|
|
|
|
|
2023-10-01 18:07:16 +02:00
|
|
|
if (netHostLibrary == nullptr) {
|
|
|
|
log::error("Could not load libnethost!");
|
2023-07-15 14:29:14 +02:00
|
|
|
return false;
|
2023-10-01 18:07:16 +02:00
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
auto get_hostfxr_path_ptr = getExport<get_hostfxr_path_fn>(netHostLibrary, "get_hostfxr_path");
|
|
|
|
|
|
|
|
std::array<char_t, 300> buffer = { 0 };
|
|
|
|
size_t bufferSize = buffer.size();
|
|
|
|
if (get_hostfxr_path_ptr(buffer.data(), &bufferSize, nullptr) != 0) {
|
2023-10-01 18:07:16 +02:00
|
|
|
log::error("Could not get hostfxr path!");
|
2023-07-15 14:29:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *hostfxrLibrary = loadLibrary(buffer.data());
|
2023-10-01 18:07:16 +02:00
|
|
|
if (hostfxrLibrary == nullptr) {
|
|
|
|
log::error("Could not load hostfxr library!");
|
2023-07-15 14:29:14 +02:00
|
|
|
return false;
|
2023-10-01 18:07:16 +02:00
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
hostfxr_initialize_for_runtime_config
|
|
|
|
= getExport<hostfxr_initialize_for_runtime_config_fn>(hostfxrLibrary, "hostfxr_initialize_for_runtime_config");
|
|
|
|
hostfxr_get_runtime_delegate
|
|
|
|
= getExport<hostfxr_get_runtime_delegate_fn>(hostfxrLibrary, "hostfxr_get_runtime_delegate");
|
|
|
|
hostfxr_close
|
|
|
|
= getExport<hostfxr_close_fn>(hostfxrLibrary, "hostfxr_close");
|
2024-03-12 23:17:49 +01:00
|
|
|
hostfxr_set_runtime_property_value
|
|
|
|
= getExport<hostfxr_set_runtime_property_value_fn>(hostfxrLibrary, "hostfxr_set_runtime_property_value");
|
|
|
|
hostfxr_set_error_writer
|
|
|
|
= getExport<hostfxr_set_error_writer_fn>(hostfxrLibrary, "hostfxr_set_error_writer");
|
2023-07-15 14:29:14 +02:00
|
|
|
}
|
|
|
|
|
2024-03-12 23:17:49 +01:00
|
|
|
hostfxr_set_error_writer([] HOSTFXR_CALLTYPE (const char_t *message) {
|
|
|
|
if constexpr (std::same_as<char_t, wchar_t>) {
|
|
|
|
log::error("{}", utf16ToUtf8(message));
|
|
|
|
} else {
|
|
|
|
log::error("{}", message);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-15 14:29:14 +02:00
|
|
|
return
|
|
|
|
hostfxr_initialize_for_runtime_config != nullptr &&
|
|
|
|
hostfxr_get_runtime_delegate != nullptr &&
|
2024-03-12 23:17:49 +01:00
|
|
|
hostfxr_close != nullptr &&
|
|
|
|
hostfxr_set_runtime_property_value != nullptr &&
|
|
|
|
hostfxr_set_error_writer != nullptr;
|
2023-07-15 14:29:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
load_assembly_and_get_function_pointer_fn getLoadAssemblyFunction(const std::fs::path &path) {
|
|
|
|
load_assembly_and_get_function_pointer_fn loadAssemblyFunction = nullptr;
|
|
|
|
|
|
|
|
hostfxr_handle ctx = nullptr;
|
|
|
|
|
|
|
|
u32 result = hostfxr_initialize_for_runtime_config(path.c_str(), nullptr, &ctx);
|
|
|
|
ON_SCOPE_EXIT {
|
|
|
|
hostfxr_close(ctx);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (result > 2 || ctx == nullptr) {
|
|
|
|
throw std::runtime_error(hex::format("Failed to initialize command line {:X}", result));
|
|
|
|
}
|
|
|
|
|
2024-03-12 23:17:49 +01:00
|
|
|
hostfxr_set_runtime_property_value(ctx, STRING("PINVOKE_OVERRIDE"), utf8ToUtf16(hex::format("{}", (void*)pInvokeOverride)).c_str());
|
|
|
|
|
2023-07-15 14:29:14 +02:00
|
|
|
result = hostfxr_get_runtime_delegate(
|
|
|
|
ctx,
|
2023-07-21 20:25:56 +02:00
|
|
|
hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer,
|
2023-11-10 20:47:08 +01:00
|
|
|
reinterpret_cast<void**>(&loadAssemblyFunction)
|
2023-07-15 14:29:14 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if (result != 0 || loadAssemblyFunction == nullptr) {
|
|
|
|
throw std::runtime_error("Failed to get load_assembly_and_get_function_pointer delegate");
|
|
|
|
}
|
|
|
|
|
|
|
|
return loadAssemblyFunction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DotNetLoader::initialize() {
|
2023-07-20 20:59:06 +02:00
|
|
|
if (!loadHostfxr()) {
|
|
|
|
log::error("Failed to initialize dotnet loader, could not load hostfxr");
|
2023-07-15 14:29:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& path : hex::fs::getDefaultPaths(hex::fs::ImHexPath::Plugins)) {
|
|
|
|
auto assemblyLoader = path / "AssemblyLoader.dll";
|
|
|
|
if (!wolv::io::fs::exists(assemblyLoader))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto loadAssembly = getLoadAssemblyFunction(std::fs::absolute(path) / "AssemblyLoader.runtimeconfig.json");
|
|
|
|
|
|
|
|
auto dotnetType = STRING("ImHex.EntryPoint, AssemblyLoader");
|
|
|
|
|
|
|
|
const char_t *dotnetTypeMethod = STRING("ExecuteScript");
|
2023-09-08 22:00:15 +02:00
|
|
|
this-> m_assemblyLoaderPathString = assemblyLoader.native();
|
2023-07-15 14:29:14 +02:00
|
|
|
|
|
|
|
component_entry_point_fn entryPoint = nullptr;
|
|
|
|
u32 result = loadAssembly(
|
2023-12-19 13:10:25 +01:00
|
|
|
m_assemblyLoaderPathString.c_str(),
|
2023-07-15 14:29:14 +02:00
|
|
|
dotnetType,
|
|
|
|
dotnetTypeMethod,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
2023-11-10 20:47:08 +01:00
|
|
|
reinterpret_cast<void**>(&entryPoint)
|
2023-07-15 14:29:14 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if (result != 0 || entryPoint == nullptr) {
|
|
|
|
log::error("Failed to load assembly loader '{}'", assemblyLoader.string());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-03-10 22:05:26 +01:00
|
|
|
m_runMethod = [entryPoint](const std::string &methodName, bool keepLoaded, const std::fs::path &path) -> int {
|
|
|
|
auto pathString = wolv::util::toUTF8String(path);
|
|
|
|
|
|
|
|
auto string = hex::format("{}||{}||{}", keepLoaded ? "LOAD" : "EXEC", methodName, pathString);
|
|
|
|
auto result = entryPoint(string.data(), string.size());
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
m_methodExists = [entryPoint](const std::string &methodName, const std::fs::path &path) -> bool {
|
|
|
|
auto pathString = wolv::util::toUTF8String(path);
|
|
|
|
|
|
|
|
auto string = hex::format("CHECK||{}||{}", methodName, pathString);
|
2023-07-15 14:29:14 +02:00
|
|
|
auto result = entryPoint(string.data(), string.size());
|
|
|
|
|
|
|
|
return result == 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DotNetLoader::loadAll() {
|
|
|
|
this->clearScripts();
|
|
|
|
|
|
|
|
for (const auto &imhexPath : hex::fs::getDefaultPaths(hex::fs::ImHexPath::Scripts)) {
|
|
|
|
auto directoryPath = imhexPath / "custom" / "dotnet";
|
|
|
|
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)) {
|
2023-07-15 14:50:39 +02:00
|
|
|
if (!entry.is_directory())
|
2023-07-15 14:29:14 +02:00
|
|
|
continue;
|
|
|
|
|
2023-07-15 14:50:39 +02:00
|
|
|
const auto &scriptPath = entry.path() / "Main.dll";
|
2023-07-15 14:29:14 +02:00
|
|
|
if (!std::fs::exists(scriptPath))
|
|
|
|
continue;
|
|
|
|
|
2024-03-10 22:05:26 +01:00
|
|
|
if (m_methodExists("Main", scriptPath)) {
|
|
|
|
this->addScript(entry.path().stem().string(), [this, scriptPath] {
|
|
|
|
hex::unused(m_runMethod("Main", false, scriptPath));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_methodExists("OnLoad", scriptPath)) {
|
|
|
|
hex::unused(m_runMethod("OnLoad", true, scriptPath));
|
|
|
|
}
|
2023-07-15 14:29:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-12 23:17:49 +01:00
|
|
|
}
|