Add Unity Mono tools
This commit is contained in:
143
libs/Unity/MonoLoader.cpp
Normal file
143
libs/Unity/MonoLoader.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "MonoLoader.hpp"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
HMODULE MonoLoader::WaitForMono(const char* monoName) {
|
||||
HMODULE mono = nullptr;
|
||||
while (!mono) {
|
||||
mono = GetModuleHandleA(monoName);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
}
|
||||
return mono;
|
||||
}
|
||||
|
||||
bool MonoLoader::Initialize(std::shared_ptr<spdlog::logger> log, const char* monoName) {
|
||||
if (log) logger = log;
|
||||
|
||||
HMODULE mono = WaitForMono(monoName);
|
||||
if (!mono) {
|
||||
logger->error("Mono module not found.");
|
||||
return false;
|
||||
}
|
||||
logger->info("Mono DLL found at {:p}", (void*)mono);
|
||||
|
||||
g_mono_get_root_domain = (MonoDomain * (*)())GetProcAddress(mono, "mono_get_root_domain");
|
||||
g_mono_thread_attach = (void(*)(MonoDomain*))GetProcAddress(mono, "mono_thread_attach");
|
||||
g_mono_domain_assembly_open = (MonoAssembly * (*)(MonoDomain*, const char*))GetProcAddress(mono, "mono_domain_assembly_open");
|
||||
g_mono_assembly_get_image = (MonoImage * (*)(MonoAssembly*))GetProcAddress(mono, "mono_assembly_get_image");
|
||||
g_mono_class_from_name = (MonoClass * (*)(MonoImage*, const char*, const char*))GetProcAddress(mono, "mono_class_from_name");
|
||||
g_mono_class_get_method_from_name = (MonoMethod * (*)(MonoClass*, const char*, int))GetProcAddress(mono, "mono_class_get_method_from_name");
|
||||
g_mono_compile_method = (void* (*)(MonoMethod*))GetProcAddress(mono, "mono_compile_method");
|
||||
mono_assembly_foreach = (void(*)(void(*)(MonoAssembly*, void*), void*))GetProcAddress(mono, "mono_assembly_foreach");
|
||||
mono_assembly_get_name = (mono_assembly_get_name_t)GetProcAddress(mono, "mono_assembly_get_name");
|
||||
mono_assembly_name_get_name = (mono_assembly_name_get_name_t)GetProcAddress(mono, "mono_assembly_name_get_name");
|
||||
mono_class_get_methods = (mono_class_get_methods_t)GetProcAddress(mono, "mono_class_get_methods");
|
||||
mono_method_get_name = (mono_method_get_name_t)GetProcAddress(mono, "mono_method_get_name");
|
||||
mono_method_signature = (mono_method_signature_t)GetProcAddress(mono, "mono_method_signature");
|
||||
mono_signature_get_param_count = (mono_signature_get_param_count_t)GetProcAddress(mono, "mono_signature_get_param_count");
|
||||
|
||||
if (!g_mono_get_root_domain || !g_mono_thread_attach || !g_mono_domain_assembly_open ||
|
||||
!g_mono_assembly_get_image || !g_mono_class_from_name || !g_mono_class_get_method_from_name ||
|
||||
!g_mono_compile_method || !mono_assembly_foreach || !mono_assembly_get_name || !mono_assembly_name_get_name ||
|
||||
!mono_class_get_methods || !mono_method_get_name || !mono_method_signature || !mono_signature_get_param_count) {
|
||||
logger->error("Failed to resolve one or more Mono exports.");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_monoDomain = g_mono_get_root_domain();
|
||||
if (!g_monoDomain) {
|
||||
logger->error("Mono root domain is null.");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_mono_thread_attach(g_monoDomain);
|
||||
logger->info("Attached to Mono domain {:p}", (void*)g_monoDomain);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FindAssemblyData {
|
||||
MonoLoader* loader;
|
||||
const std::string* targetName;
|
||||
MonoAssembly* result = nullptr;
|
||||
};
|
||||
|
||||
void MonoLoader::FindAssemblyCallback(MonoAssembly* assembly, void* userData) {
|
||||
auto data = reinterpret_cast<FindAssemblyData*>(userData);
|
||||
if (!assembly || !data || !data->targetName) return;
|
||||
|
||||
const void* nameStruct = data->loader->mono_assembly_get_name(assembly);
|
||||
const char* nameCStr = data->loader->mono_assembly_name_get_name(nameStruct);
|
||||
|
||||
if (nameCStr && *data->targetName == nameCStr) {
|
||||
data->result = assembly;
|
||||
}
|
||||
}
|
||||
|
||||
MonoAssembly* MonoLoader::FindAssembly(const std::string& name) {
|
||||
if (!mono_assembly_foreach || !mono_assembly_get_name || !mono_assembly_name_get_name) {
|
||||
logger->error("Mono exports not resolved, cannot enumerate assemblies.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FindAssemblyData data;
|
||||
data.loader = this;
|
||||
data.targetName = &name;
|
||||
data.result = nullptr;
|
||||
|
||||
mono_assembly_foreach(MonoLoader::FindAssemblyCallback, &data);
|
||||
return data.result;
|
||||
}
|
||||
|
||||
MonoClass* MonoLoader::FindClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className) {
|
||||
MonoAssembly* assembly = FindAssembly(assemblyName);
|
||||
if (!assembly) return nullptr;
|
||||
|
||||
MonoImage* image = g_mono_assembly_get_image(assembly);
|
||||
if (!image) return nullptr;
|
||||
|
||||
return g_mono_class_from_name(image, namespaceName.c_str(), className.c_str());
|
||||
}
|
||||
|
||||
uint8_t* MonoLoader::GetCompiledMethod(MonoClass* monoClass, const std::string& methodName, int paramCount) {
|
||||
if (!monoClass) return nullptr;
|
||||
MonoMethod* method = g_mono_class_get_method_from_name(monoClass, methodName.c_str(), paramCount);
|
||||
if (!method) return nullptr;
|
||||
return (uint8_t*)g_mono_compile_method(method);
|
||||
}
|
||||
|
||||
void MonoLoader::DumpMethods(MonoClass* monoClass, std::shared_ptr<spdlog::logger> logger) {
|
||||
if (!monoClass) {
|
||||
logger->error("DumpMethods: monoClass is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_monoDomain) {
|
||||
logger->error("DumpMethods: Mono domain is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
g_mono_thread_attach(g_monoDomain);
|
||||
|
||||
if (!mono_class_get_methods || !mono_method_get_name || !mono_method_signature || !mono_signature_get_param_count) {
|
||||
logger->error("DumpMethods: Required Mono exports not resolved.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger->info("---- Dumping methods ----");
|
||||
|
||||
void* iter = nullptr;
|
||||
MonoMethod* method = nullptr;
|
||||
|
||||
while ((method = mono_class_get_methods(monoClass, &iter)) != nullptr) {
|
||||
const char* name = mono_method_get_name(method);
|
||||
|
||||
MonoMethodSignature* sig = mono_method_signature(method);
|
||||
uint32_t paramCount = 0;
|
||||
|
||||
if (sig) paramCount = mono_signature_get_param_count(sig);
|
||||
|
||||
logger->info("Method: {} | Params: {}", name ? name : "null", paramCount);
|
||||
}
|
||||
|
||||
logger->info("-------------------------");
|
||||
}
|
||||
116
libs/Unity/MonoLoader.hpp
Normal file
116
libs/Unity/MonoLoader.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
// Forward declarations Mono
|
||||
struct MonoDomain;
|
||||
struct MonoAssembly;
|
||||
struct MonoImage;
|
||||
struct MonoClass;
|
||||
struct MonoMethod;
|
||||
struct MonoMethodSignature;
|
||||
|
||||
// Typedefs pour les exports Mono
|
||||
using mono_assembly_foreach_t = void(*)(void(*)(MonoAssembly*, void*), void*);
|
||||
using mono_assembly_get_name_t = const void* (*)(MonoAssembly*);
|
||||
using mono_assembly_name_get_name_t = const char* (*)(const void*);
|
||||
using mono_class_get_methods_t = MonoMethod * (*)(MonoClass*, void**);
|
||||
using mono_method_get_name_t = const char* (*)(MonoMethod*);
|
||||
using mono_method_signature_t = MonoMethodSignature * (*)(MonoMethod*);
|
||||
using mono_signature_get_param_count_t = uint32_t(*)(MonoMethodSignature*);
|
||||
|
||||
class MonoLoader {
|
||||
public:
|
||||
MonoLoader() = default;
|
||||
~MonoLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief Callback used during Mono assembly enumeration.
|
||||
*
|
||||
* This function is passed to `mono_assembly_foreach` and checks each loaded assembly
|
||||
* to find the one matching a specific name (provided via userData).
|
||||
* Can be used to initialize pointers to target assemblies.
|
||||
*
|
||||
* @param assembly Pointer to the current assembly.
|
||||
* @param userData User data passed to `mono_assembly_foreach`, typically a structure
|
||||
* containing the target name and a location to store the result.
|
||||
*/
|
||||
bool Initialize(std::shared_ptr<spdlog::logger> log = nullptr, const char* monoName = "mono-2.0-bdwgc.dll");
|
||||
MonoDomain* GetDomain() const { return g_monoDomain; }
|
||||
|
||||
/**
|
||||
* @brief Finds a Mono assembly by its name.
|
||||
*
|
||||
* This function iterates over all loaded assemblies in the Mono domain and
|
||||
* returns a pointer to the assembly matching the given name.
|
||||
*
|
||||
* @param name Name of the assembly to search for.
|
||||
* @return MonoAssembly* Pointer to the found assembly, or nullptr if not found.
|
||||
*/
|
||||
MonoAssembly* FindAssembly(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief Finds a class in a loaded assembly by namespace and name.
|
||||
* @param assemblyName Name of the assembly (ex: "UnityEngine.CoreModule").
|
||||
* @param namespaceName Namespace of the class (ex: "UnityEngine").
|
||||
* @param className Name of the class (ex: "Screen").
|
||||
* @return Pointer to the MonoClass, or nullptr if not found.
|
||||
*/
|
||||
MonoClass* FindClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className);
|
||||
|
||||
/**
|
||||
* @brief Finds a method in a MonoClass and compiles it.
|
||||
* @param monoClass The MonoClass to search in.
|
||||
* @param methodName The method name (ex: "get_width").
|
||||
* @param paramCount Number of parameters.
|
||||
* @return Compiled method address (uint8_t*), or nullptr if not found.
|
||||
*/
|
||||
uint8_t* GetCompiledMethod(MonoClass* monoClass, const std::string& methodName, int paramCount);
|
||||
|
||||
void DumpMethods(MonoClass* monoClass, std::shared_ptr<spdlog::logger> log = nullptr);
|
||||
|
||||
// Exports
|
||||
mono_assembly_foreach_t mono_assembly_foreach = nullptr;
|
||||
mono_assembly_get_name_t mono_assembly_get_name = nullptr;
|
||||
mono_assembly_name_get_name_t mono_assembly_name_get_name = nullptr;
|
||||
MonoDomain* (*g_mono_get_root_domain)() = nullptr;
|
||||
void (*g_mono_thread_attach)(MonoDomain*) = nullptr;
|
||||
MonoAssembly* (*g_mono_domain_assembly_open)(MonoDomain*, const char*) = nullptr;
|
||||
MonoImage* (*g_mono_assembly_get_image)(MonoAssembly*) = nullptr;
|
||||
MonoClass* (*g_mono_class_from_name)(MonoImage*, const char*, const char*) = nullptr;
|
||||
MonoMethod* (*g_mono_class_get_method_from_name)(MonoClass*, const char*, int) = nullptr;
|
||||
void* (*g_mono_compile_method)(MonoMethod*) = nullptr;
|
||||
mono_class_get_methods_t mono_class_get_methods = nullptr;
|
||||
mono_method_get_name_t mono_method_get_name = nullptr;
|
||||
mono_method_signature_t mono_method_signature = nullptr;
|
||||
mono_signature_get_param_count_t mono_signature_get_param_count = nullptr;
|
||||
|
||||
|
||||
private:
|
||||
MonoDomain* g_monoDomain = nullptr;
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
|
||||
/**
|
||||
* @brief Waits for the Mono module to be loaded in the process and returns its handle.
|
||||
*
|
||||
* This function loops every 200 ms until the "mono-2.0-bdwgc.dll" module
|
||||
* is present in the current process. Useful to ensure that Mono is initialized
|
||||
* before resolving its exports.
|
||||
*
|
||||
* @return HMODULE Handle of the Mono module, or nullptr if not found (practically never).
|
||||
*/
|
||||
HMODULE WaitForMono(const char* monoName);
|
||||
|
||||
/**
|
||||
* @brief Callback used during Mono assembly enumeration.
|
||||
*
|
||||
* This function is passed to `mono_assembly_foreach` and checks each loaded assembly
|
||||
* to find the one matching a specific name (provided via userData).
|
||||
* Can be used to initialize pointers to target assemblies.
|
||||
*
|
||||
* @param assembly Pointer to the current assembly.
|
||||
* @param userData User data passed to `mono_assembly_foreach`, typically a structure
|
||||
* containing the target name and a location to store the result.
|
||||
*/
|
||||
static void FindAssemblyCallback(MonoAssembly* assembly, void* userData);
|
||||
};
|
||||
Reference in New Issue
Block a user