2022-02-01 18:09:40 +01:00
# include <hex/api/plugin_manager.hpp>
2023-06-26 14:01:45 +02:00
# include <hex/api/imhex_api.hpp>
2020-12-22 18:10:01 +01:00
2021-08-29 22:15:18 +02:00
# include <hex/helpers/logger.hpp>
2023-11-10 20:47:08 +01:00
# include <hex/helpers/fmt.hpp>
2024-01-30 15:50:00 +01:00
# include <hex/helpers/auto_reset.hpp>
2021-08-29 22:15:18 +02:00
2023-03-12 18:43:05 +01:00
# include <wolv/utils/string.hpp>
2020-12-22 18:10:01 +01:00
# include <filesystem>
2023-11-30 10:22:15 +01:00
# if defined(OS_WINDOWS)
# include <windows.h>
# else
# include <dlfcn.h>
# endif
2020-12-22 18:10:01 +01:00
namespace hex {
2023-12-24 12:20:51 +01:00
Plugin : : Plugin ( const std : : fs : : path & path ) : m_path ( path ) {
2023-12-28 14:59:23 +01:00
log : : info ( " Loading plugin '{}' " , wolv : : util : : toUTF8String ( path . filename ( ) ) ) ;
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
2023-12-19 13:10:25 +01:00
m_handle = uintptr_t ( LoadLibraryW ( path . c_str ( ) ) ) ;
2022-06-29 21:34:17 +02:00
2023-12-19 13:10:25 +01:00
if ( m_handle = = uintptr_t ( INVALID_HANDLE_VALUE ) | | m_handle = = 0 ) {
2023-12-23 21:09:41 +01:00
log : : error ( " Loading plugin '{}' failed: {} {}! " , wolv : : util : : toUTF8String ( path . filename ( ) ) , : : GetLastError ( ) , std : : system_category ( ) . message ( : : GetLastError ( ) ) ) ;
2022-06-29 21:34:17 +02:00
return ;
}
# else
2023-12-19 13:10:25 +01:00
m_handle = uintptr_t ( dlopen ( wolv : : util : : toUTF8String ( path ) . c_str ( ) , RTLD_LAZY ) ) ;
2022-06-29 21:34:17 +02:00
2023-12-19 13:10:25 +01:00
if ( m_handle = = 0 ) {
2023-12-23 21:09:41 +01:00
log : : error ( " Loading plugin '{}' failed: {}! " , wolv : : util : : toUTF8String ( path . filename ( ) ) , dlerror ( ) ) ;
2022-06-29 21:34:17 +02:00
return ;
}
# endif
2020-12-22 18:10:01 +01:00
2024-01-29 15:44:18 +01:00
const auto fileName = path . stem ( ) . string ( ) ;
m_functions . initializePluginFunction = getPluginFunction < PluginFunctions : : InitializePluginFunc > ( " initializePlugin " ) ;
m_functions . initializeLibraryFunction = getPluginFunction < PluginFunctions : : InitializePluginFunc > ( hex : : format ( " initializeLibrary_{} " , fileName ) ) ;
m_functions . getPluginNameFunction = getPluginFunction < PluginFunctions : : GetPluginNameFunc > ( " getPluginName " ) ;
m_functions . getLibraryNameFunction = getPluginFunction < PluginFunctions : : GetLibraryNameFunc > ( hex : : format ( " getLibraryName_{} " , fileName ) ) ;
m_functions . getPluginAuthorFunction = getPluginFunction < PluginFunctions : : GetPluginAuthorFunc > ( " getPluginAuthor " ) ;
m_functions . getPluginDescriptionFunction = getPluginFunction < PluginFunctions : : GetPluginDescriptionFunc > ( " getPluginDescription " ) ;
m_functions . getCompatibleVersionFunction = getPluginFunction < PluginFunctions : : GetCompatibleVersionFunc > ( " getCompatibleVersion " ) ;
m_functions . setImGuiContextFunction = getPluginFunction < PluginFunctions : : SetImGuiContextFunc > ( " setImGuiContext " ) ;
m_functions . setImGuiContextLibraryFunction = getPluginFunction < PluginFunctions : : SetImGuiContextFunc > ( hex : : format ( " setImGuiContext_{} " , fileName ) ) ;
m_functions . getSubCommandsFunction = getPluginFunction < PluginFunctions : : GetSubCommandsFunc > ( " getSubCommands " ) ;
m_functions . getFeaturesFunction = getPluginFunction < PluginFunctions : : GetSubCommandsFunc > ( " getFeatures " ) ;
2023-10-04 12:00:32 +02:00
}
2024-01-12 23:03:13 +01:00
Plugin : : Plugin ( const std : : string & name , const hex : : PluginFunctions & functions ) {
2023-12-23 21:09:41 +01:00
m_handle = 0 ;
m_functions = functions ;
2024-01-12 23:03:13 +01:00
m_path = name ;
2024-02-01 11:40:27 +01:00
m_addedManually = true ;
2020-12-22 18:10:01 +01:00
}
2023-10-04 12:00:32 +02:00
2021-02-19 13:22:12 +01:00
Plugin : : Plugin ( Plugin & & other ) noexcept {
2023-12-19 13:10:25 +01:00
m_handle = other . m_handle ;
2023-11-30 10:22:15 +01:00
other . m_handle = 0 ;
2023-10-04 12:00:32 +02:00
2023-12-23 23:12:15 +01:00
m_path = std : : move ( other . m_path ) ;
2024-02-01 11:40:27 +01:00
m_addedManually = other . m_addedManually ;
2023-12-23 21:09:41 +01:00
2023-12-23 23:12:15 +01:00
m_functions = other . m_functions ;
other . m_functions = { } ;
}
Plugin & Plugin : : operator = ( Plugin & & other ) noexcept {
m_handle = other . m_handle ;
other . m_handle = 0 ;
m_path = std : : move ( other . m_path ) ;
2024-02-01 11:40:27 +01:00
m_addedManually = other . m_addedManually ;
2022-02-01 22:09:44 +01:00
2023-12-19 13:10:25 +01:00
m_functions = other . m_functions ;
2023-10-04 12:00:32 +02:00
other . m_functions = { } ;
2023-12-23 23:12:15 +01:00
return * this ;
2021-02-07 14:57:13 +01:00
}
2020-12-22 18:10:01 +01:00
Plugin : : ~ Plugin ( ) {
2024-01-22 12:53:07 +01:00
if ( isLoaded ( ) ) {
log : : debug ( " Trying to unload plugin '{}' " , getPluginName ( ) ) ;
}
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
2023-12-19 13:10:25 +01:00
if ( m_handle ! = 0 )
2024-01-07 16:07:53 +01:00
if ( FreeLibrary ( HMODULE ( m_handle ) ) = = FALSE ) {
log : : error ( " Error when unloading plugin '{}': {}! " , wolv : : util : : toUTF8String ( m_path . filename ( ) ) , std : : system_category ( ) . message ( : : GetLastError ( ) ) ) ;
}
2022-06-29 21:34:17 +02:00
# else
2023-12-19 13:10:25 +01:00
if ( m_handle ! = 0 )
dlclose ( reinterpret_cast < void * > ( m_handle ) ) ;
2022-06-29 21:34:17 +02:00
# endif
2020-12-22 18:10:01 +01:00
}
2022-01-17 20:06:00 +01:00
bool Plugin : : initializePlugin ( ) const {
2023-12-19 13:10:25 +01:00
const auto pluginName = wolv : : util : : toUTF8String ( m_path . filename ( ) ) ;
2023-07-15 14:29:14 +02:00
2023-12-24 12:20:51 +01:00
if ( this - > isLibraryPlugin ( ) ) {
2024-01-09 11:31:56 +01:00
m_functions . initializeLibraryFunction ( ) ;
2024-01-09 11:38:56 +01:00
log : : info ( " Library '{}' initialized successfully " , pluginName ) ;
m_initialized = true ;
return true ;
2023-12-23 21:09:41 +01:00
}
2024-01-09 11:31:56 +01:00
2022-01-23 23:28:56 +01:00
const auto requestedVersion = getCompatibleVersion ( ) ;
2024-01-26 21:11:56 +01:00
const auto imhexVersion = ImHexApi : : System : : getImHexVersion ( ) ;
if ( ! imhexVersion . starts_with ( requestedVersion ) ) {
2023-06-26 14:01:45 +02:00
if ( requestedVersion . empty ( ) ) {
2023-12-19 13:10:25 +01:00
log : : warn ( " Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex. " , wolv : : util : : toUTF8String ( m_path . filename ( ) ) ) ;
2023-06-26 14:01:45 +02:00
} else {
2023-12-19 13:10:25 +01:00
log : : error ( " Refused to load plugin '{}' which was built for a different version of ImHex: '{}' " , wolv : : util : : toUTF8String ( m_path . filename ( ) ) , requestedVersion ) ;
2023-06-26 14:01:45 +02:00
return false ;
}
2022-01-23 23:28:56 +01:00
}
2023-12-19 13:10:25 +01:00
if ( m_functions . initializePluginFunction ! = nullptr ) {
2023-07-15 14:29:14 +02:00
try {
2023-12-19 13:10:25 +01:00
m_functions . initializePluginFunction ( ) ;
2023-07-15 14:29:14 +02:00
} catch ( const std : : exception & e ) {
log : : error ( " Plugin '{}' threw an exception on init: {} " , pluginName , e . what ( ) ) ;
2023-08-06 21:33:15 +02:00
return false ;
2023-07-15 14:29:14 +02:00
} catch ( . . . ) {
log : : error ( " Plugin '{}' threw an exception on init " , pluginName ) ;
2023-08-06 21:33:15 +02:00
return false ;
2023-07-15 14:29:14 +02:00
}
2022-01-17 20:06:00 +01:00
} else {
2023-10-04 12:00:32 +02:00
log : : error ( " Plugin '{}' does not have a proper entrypoint " , pluginName ) ;
2022-01-17 20:06:00 +01:00
return false ;
}
2022-01-23 23:28:56 +01:00
2023-12-01 14:07:10 +01:00
log : : info ( " Plugin '{}' initialized successfully " , pluginName ) ;
2023-12-19 13:10:25 +01:00
m_initialized = true ;
2022-01-23 23:28:56 +01:00
return true ;
2021-02-19 13:22:12 +01:00
}
std : : string Plugin : : getPluginName ( ) const {
2023-12-27 16:33:49 +01:00
if ( m_functions . getPluginNameFunction ! = nullptr ) {
2023-12-19 13:10:25 +01:00
return m_functions . getPluginNameFunction ( ) ;
2023-12-27 16:33:49 +01:00
} else {
2023-12-23 21:09:41 +01:00
if ( this - > isLibraryPlugin ( ) )
2024-01-12 23:03:13 +01:00
return m_functions . getLibraryNameFunction ( ) ;
2023-12-23 21:09:41 +01:00
else
return hex : : format ( " Unknown Plugin @ 0x{0:016X} " , m_handle ) ;
}
2021-02-19 13:22:12 +01:00
}
std : : string Plugin : : getPluginAuthor ( ) const {
2023-12-19 13:10:25 +01:00
if ( m_functions . getPluginAuthorFunction ! = nullptr )
return m_functions . getPluginAuthorFunction ( ) ;
2021-02-19 13:22:12 +01:00
else
return " Unknown " ;
}
std : : string Plugin : : getPluginDescription ( ) const {
2023-12-19 13:10:25 +01:00
if ( m_functions . getPluginDescriptionFunction ! = nullptr )
return m_functions . getPluginDescriptionFunction ( ) ;
2021-02-19 13:22:12 +01:00
else
return " " ;
2020-12-22 18:10:01 +01:00
}
2022-01-23 23:28:56 +01:00
std : : string Plugin : : getCompatibleVersion ( ) const {
2023-12-19 13:10:25 +01:00
if ( m_functions . getCompatibleVersionFunction ! = nullptr )
return m_functions . getCompatibleVersionFunction ( ) ;
2022-01-23 23:28:56 +01:00
else
return " " ;
}
2023-12-23 21:09:41 +01:00
2021-08-21 00:52:11 +02:00
void Plugin : : setImGuiContext ( ImGuiContext * ctx ) const {
2023-12-19 13:10:25 +01:00
if ( m_functions . setImGuiContextFunction ! = nullptr )
m_functions . setImGuiContextFunction ( ctx ) ;
2021-08-21 00:52:11 +02:00
}
2022-03-04 11:36:37 +01:00
const std : : fs : : path & Plugin : : getPath ( ) const {
2023-12-19 13:10:25 +01:00
return m_path ;
2022-01-17 20:06:00 +01:00
}
2023-12-23 23:12:15 +01:00
bool Plugin : : isValid ( ) const {
2023-12-24 13:14:51 +01:00
return m_handle ! = 0 | | m_functions . initializeLibraryFunction ! = nullptr | | m_functions . initializePluginFunction ! = nullptr ;
2023-12-23 23:12:15 +01:00
}
2022-01-23 23:28:56 +01:00
bool Plugin : : isLoaded ( ) const {
2023-12-19 13:10:25 +01:00
return m_initialized ;
2022-01-18 00:10:10 +01:00
}
2023-07-13 14:08:23 +02:00
std : : span < SubCommand > Plugin : : getSubCommands ( ) const {
2023-12-19 13:10:25 +01:00
if ( m_functions . getSubCommandsFunction ! = nullptr ) {
2024-01-12 23:03:13 +01:00
const auto result = m_functions . getSubCommandsFunction ( ) ;
2024-01-18 11:11:06 +01:00
if ( result = = nullptr )
return { } ;
2024-01-12 23:03:13 +01:00
2023-11-10 20:47:08 +01:00
return * static_cast < std : : vector < SubCommand > * > ( result ) ;
2023-12-27 16:33:49 +01:00
} else {
2023-07-13 14:08:23 +02:00
return { } ;
2023-12-27 16:33:49 +01:00
}
2023-07-13 14:08:23 +02:00
}
2023-12-31 11:39:24 +01:00
std : : span < Feature > Plugin : : getFeatures ( ) const {
if ( m_functions . getFeaturesFunction ! = nullptr ) {
2024-01-12 23:03:13 +01:00
const auto result = m_functions . getFeaturesFunction ( ) ;
2024-01-13 00:34:13 +01:00
if ( result = = nullptr )
return { } ;
2024-01-12 23:03:13 +01:00
2023-12-31 11:39:24 +01:00
return * static_cast < std : : vector < Feature > * > ( result ) ;
} else {
return { } ;
}
}
2023-12-24 12:20:51 +01:00
bool Plugin : : isLibraryPlugin ( ) const {
return m_functions . initializeLibraryFunction ! = nullptr & &
m_functions . initializePluginFunction = = nullptr ;
}
2024-02-01 11:40:27 +01:00
bool Plugin : : wasAddedManually ( ) const {
return m_addedManually ;
}
2023-12-24 12:20:51 +01:00
2022-01-17 20:06:00 +01:00
2023-11-10 20:47:08 +01:00
void * Plugin : : getPluginFunction ( const std : : string & symbol ) const {
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
2023-12-19 13:10:25 +01:00
return reinterpret_cast < void * > ( GetProcAddress ( HMODULE ( m_handle ) , symbol . c_str ( ) ) ) ;
2022-06-29 21:34:17 +02:00
# else
2023-12-19 13:10:25 +01:00
return dlsym ( reinterpret_cast < void * > ( m_handle ) , symbol . c_str ( ) ) ;
2022-06-29 21:34:17 +02:00
# endif
2022-01-23 23:28:56 +01:00
}
2024-01-22 23:35:00 +01:00
void PluginManager : : addLoadPath ( const std : : fs : : path & path ) {
getPluginLoadPaths ( ) . emplace_back ( path ) ;
}
bool PluginManager : : load ( ) {
bool success = true ;
for ( const auto & loadPath : getPluginLoadPaths ( ) )
success = PluginManager : : load ( loadPath ) & & success ;
return success ;
}
2024-01-07 18:45:17 +01:00
2022-03-04 11:36:37 +01:00
bool PluginManager : : load ( const std : : fs : : path & pluginFolder ) {
2023-03-12 18:27:29 +01:00
if ( ! wolv : : io : : fs : : exists ( pluginFolder ) )
2021-04-20 21:46:48 +02:00
return false ;
2020-12-22 18:10:01 +01:00
2023-10-04 12:00:32 +02:00
getPluginPaths ( ) . push_back ( pluginFolder ) ;
2020-12-22 18:10:01 +01:00
2023-12-23 21:09:41 +01:00
// Load library plugins first
for ( auto & pluginPath : std : : fs : : directory_iterator ( pluginFolder ) ) {
2024-01-07 18:45:17 +01:00
if ( pluginPath . is_regular_file ( ) & & pluginPath . path ( ) . extension ( ) = = " .hexpluglib " ) {
if ( ! isPluginLoaded ( pluginPath . path ( ) ) ) {
getPlugins ( ) . emplace_back ( pluginPath . path ( ) ) ;
}
}
2023-12-23 21:09:41 +01:00
}
// Load regular plugins afterwards
2022-03-04 11:36:37 +01:00
for ( auto & pluginPath : std : : fs : : directory_iterator ( pluginFolder ) ) {
2024-01-07 18:45:17 +01:00
if ( pluginPath . is_regular_file ( ) & & pluginPath . path ( ) . extension ( ) = = " .hexplug " ) {
if ( ! isPluginLoaded ( pluginPath . path ( ) ) ) {
getPlugins ( ) . emplace_back ( pluginPath . path ( ) ) ;
}
}
2021-01-12 16:50:15 +01:00
}
2021-04-20 21:46:48 +02:00
2023-12-23 23:12:15 +01:00
std : : erase_if ( getPlugins ( ) , [ ] ( const Plugin & plugin ) {
return ! plugin . isValid ( ) ;
} ) ;
2021-04-20 21:46:48 +02:00
return true ;
2020-12-22 18:10:01 +01:00
}
2024-01-22 12:53:07 +01:00
void PluginManager : : initializeNewPlugins ( ) {
for ( const auto & plugin : getPlugins ( ) ) {
if ( ! plugin . isLoaded ( ) )
hex : : unused ( plugin . initializePlugin ( ) ) ;
}
}
2021-04-20 21:46:48 +02:00
void PluginManager : : unload ( ) {
2023-10-04 12:00:32 +02:00
getPluginPaths ( ) . clear ( ) ;
2024-01-06 16:09:05 +01:00
// Unload plugins in reverse order
auto & plugins = getPlugins ( ) ;
2024-02-01 11:40:27 +01:00
std : : list < Plugin > savedPlugins ;
2024-01-22 12:53:07 +01:00
while ( ! plugins . empty ( ) ) {
2024-02-01 11:40:27 +01:00
if ( plugins . back ( ) . wasAddedManually ( ) )
savedPlugins . emplace_front ( std : : move ( plugins . back ( ) ) ) ;
2024-01-06 16:09:05 +01:00
plugins . pop_back ( ) ;
}
2024-02-01 11:40:27 +01:00
getPlugins ( ) = std : : move ( savedPlugins ) ;
2020-12-22 18:10:01 +01:00
}
2024-01-12 23:03:13 +01:00
void PluginManager : : addPlugin ( const std : : string & name , hex : : PluginFunctions functions ) {
getPlugins ( ) . emplace_back ( name , functions ) ;
2023-07-26 13:50:51 +02:00
}
2024-01-22 12:53:07 +01:00
std : : list < Plugin > & PluginManager : : getPlugins ( ) {
static std : : list < Plugin > plugins ;
2023-10-04 12:00:32 +02:00
return plugins ;
}
std : : vector < std : : fs : : path > & PluginManager : : getPluginPaths ( ) {
2024-01-30 11:21:34 +01:00
static AutoReset < std : : vector < std : : fs : : path > > pluginPaths ;
2023-10-04 12:00:32 +02:00
return pluginPaths ;
2020-12-22 18:10:01 +01:00
}
2024-01-22 23:35:00 +01:00
std : : vector < std : : fs : : path > & PluginManager : : getPluginLoadPaths ( ) {
2024-01-30 11:21:34 +01:00
static AutoReset < std : : vector < std : : fs : : path > > pluginPaths ;
2024-01-22 23:35:00 +01:00
return pluginPaths ;
}
2024-01-07 18:45:17 +01:00
bool PluginManager : : isPluginLoaded ( const std : : fs : : path & path ) {
return std : : ranges : : any_of ( getPlugins ( ) , [ & path ] ( const Plugin & plugin ) {
return plugin . getPath ( ) . filename ( ) = = path . filename ( ) ;
} ) ;
}
2020-12-22 18:10:01 +01:00
}