2023-07-15 14:29:14 +02:00
# include <loaders/dotnet/dotnet_loader.hpp>
# include <stdexcept>
# if defined(OS_WINDOWS)
# define STRING(str) L##str
# else
# define STRING(str) str
# endif
# include <array>
# include <nethost.h>
# include <coreclr_delegates.h>
# include <hostfxr.h>
2024-07-02 23:15:54 +02:00
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>
2024-06-22 10:44:55 +02:00
# include <hex/helpers/default_paths.hpp>
2024-05-18 12:57:29 +02:00
# include <toasts/toast_notification.hpp>
2024-03-12 23:17:49 +01:00
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
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 " ) ;
2024-03-14 17:49:04 +01:00
# elif defined(OS_LINUX)
2023-07-15 14:29:14 +02:00
auto netHostLibrary = loadLibrary ( " libnethost.so " ) ;
# elif defined(OS_MACOS)
2023-10-01 18:07:16 +02:00
void * netHostLibrary = nullptr ;
2024-06-22 10:44:55 +02:00
for ( const auto & pluginPath : paths : : Plugins . read ( ) ) {
2023-10-01 18:07:16 +02:00
auto frameworksPath = pluginPath . parent_path ( ) . parent_path ( ) / " Frameworks " ;
netHostLibrary = loadLibrary ( ( frameworksPath / " libnethost.dylib " ) . c_str ( ) ) ;
if ( netHostLibrary ! = nullptr )
break ;
}
2024-05-18 11:10:55 +02:00
if ( netHostLibrary = = nullptr ) {
2024-06-22 10:44:55 +02:00
for ( const auto & librariesPath : paths : : Libraries . read ( ) ) {
2024-05-18 11:10:55 +02:00
netHostLibrary = loadLibrary ( ( librariesPath / " 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 ) {
2024-06-27 17:11:07 +02:00
log : : debug ( " libnethost is not available! Disabling .NET support " ) ;
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 " ) ;
2024-03-21 21:39:13 +01:00
std : : array < char_t , 300 > buffer = { } ;
2023-07-15 14:29:14 +02:00
size_t bufferSize = buffer . size ( ) ;
2024-03-21 21:39:13 +01:00
2024-05-18 11:10:55 +02:00
u32 result = get_hostfxr_path_ptr ( buffer . data ( ) , & bufferSize , nullptr ) ;
2024-03-21 21:39:13 +01:00
if ( result ! = 0 ) {
log : : error ( hex : : format ( " Could not get hostfxr path! 0x{:X} " , result ) ) ;
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-07-02 23:15:54 +02:00
hostfxr_set_error_writer ( [ ] HOSTFXR_CALLTYPE ( const char_t * ) { } ) ;
2024-03-12 23:17:49 +01:00
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 ) {
2024-07-02 23:15:54 +02:00
if ( result = = /* FrameworkMissingFailure */ 0x80008096 ) {
log : : warn ( " ImHex has built-in support for .NET scripts and extensions. However, these can only be used when the .NET runtime is installed. " ) ;
log : : warn ( " Please install version {} or later of the .NET runtime if you plan to use them. Otherwise this error can be safely ignored. " , IMHEX_DOTNET_RUNTIME_VERSION ) ;
}
throw std : : runtime_error ( hex : : format ( " Command line init failed 0x{:X} " , result ) ) ;
2023-07-15 14:29:14 +02:00
}
2024-03-14 17:49:04 +01:00
# if defined (OS_WINDOWS)
hostfxr_set_runtime_property_value ( ctx , STRING ( " PINVOKE_OVERRIDE " ) , utf8ToUtf16 ( hex : : format ( " {} " , ( void * ) pInvokeOverride ) ) . c_str ( ) ) ;
# else
hostfxr_set_runtime_property_value ( ctx , STRING ( " PINVOKE_OVERRIDE " ) , hex : : format ( " {} " , ( void * ) pInvokeOverride ) . c_str ( ) ) ;
# endif
2024-03-12 23:17:49 +01:00
2024-07-02 23:15:54 +02:00
hostfxr_set_error_writer ( [ ] HOSTFXR_CALLTYPE ( const char_t * message ) {
# if defined(OS_WINDOWS)
log : : error ( " {} " , utf16ToUtf8 ( message ) ) ;
# else
log : : error ( " {} " , message ) ;
# endif
} ) ;
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 ) {
2024-03-21 21:39:13 +01:00
throw std : : runtime_error ( hex : : format ( " Failed to get load_assembly_and_get_function_pointer delegate 0x{:X} " , result ) ) ;
2023-07-15 14:29:14 +02:00
}
return loadAssemblyFunction ;
}
}
bool DotNetLoader : : initialize ( ) {
2023-07-20 20:59:06 +02:00
if ( ! loadHostfxr ( ) ) {
2023-07-15 14:29:14 +02:00
return false ;
}
2024-06-22 10:44:55 +02:00
for ( const auto & path : paths : : Plugins . read ( ) ) {
2023-07-15 14:29:14 +02:00
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 ) {
2024-03-21 21:39:13 +01:00
log : : error ( " Failed to load assembly loader '{}'! 0x{:X} " , assemblyLoader . string ( ) , result ) ;
2023-07-15 14:29:14 +02:00
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 ( ) ;
2024-06-22 10:44:55 +02:00
for ( const auto & imhexPath : paths : : Scripts . read ( ) ) {
2023-07-15 14:29:14 +02:00
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-06-07 21:27:01 +02:00
bool skip = false ;
for ( const auto & existingScript : getScripts ( ) ) {
if ( existingScript . path = = scriptPath ) {
skip = true ;
}
}
if ( skip )
continue ;
2024-05-17 09:27:00 +02:00
const bool hasMain = m_methodExists ( " Main " , scriptPath ) ;
const bool hasOnLoad = m_methodExists ( " OnLoad " , scriptPath ) ;
2024-05-18 12:57:29 +02:00
const auto scriptName = entry . path ( ) . stem ( ) . string ( ) ;
2024-05-17 09:27:00 +02:00
2024-06-07 21:27:01 +02:00
if ( hasMain & & hasOnLoad ) {
log : : error ( " Script '{}' has both a Main() and a OnLoad() function. Only one is allowed per script. " , scriptName ) ;
continue ;
} else if ( ! hasMain & & ! hasOnLoad ) {
log : : error ( " Script '{}' has neither a Main() nor a OnLoad() function. " , scriptName ) ;
continue ;
}
2024-05-17 09:27:00 +02:00
if ( hasMain ) {
2024-06-07 21:27:01 +02:00
this - > addScript ( scriptName , scriptPath , false , [ this , scriptPath ] {
2024-05-18 12:57:29 +02:00
auto result = m_runMethod ( " Main " , false , scriptPath ) ;
if ( result ! = 0 ) {
ui : : ToastError : : open ( hex : : format ( " Script '{}' running failed with code {} " , result ) ) ;
}
2024-03-10 22:05:26 +01:00
} ) ;
2024-05-17 09:27:00 +02:00
} else if ( hasOnLoad ) {
2024-06-07 21:27:01 +02:00
this - > addScript ( scriptName , scriptPath , true , [ ] { } ) ;
2024-05-18 12:57:29 +02:00
auto result = m_runMethod ( " OnLoad " , true , scriptPath ) ;
if ( result ! = 0 ) {
TaskManager : : doLater ( [ = ] {
ui : : ToastError : : open ( hex : : format ( " Script '{}' loading failed with code {} " , scriptName , result ) ) ;
} ) ;
}
2024-03-10 22:05:26 +01:00
}
2023-07-15 14:29:14 +02:00
}
}
return true ;
}
2024-06-07 21:27:01 +02:00
void DotNetLoader : : clearScripts ( ) {
2024-06-07 21:57:39 +02:00
std : : erase_if ( getScripts ( ) , [ ] ( const Script & script ) {
return ! script . background ;
2024-06-07 21:27:01 +02:00
} ) ;
}
2024-03-12 23:17:49 +01:00
}