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>
2022-10-04 09:10:58 +02:00
# include <hex/helpers/utils.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>
2022-06-29 21:34:17 +02:00
# include <system_error>
2020-12-22 18:10:01 +01:00
namespace hex {
2022-03-04 11:36:37 +01:00
Plugin : : Plugin ( const std : : fs : : path & path ) : m_path ( path ) {
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
this - > m_handle = LoadLibraryW ( path . c_str ( ) ) ;
2023-07-15 14:29:14 +02:00
if ( this - > m_handle = = INVALID_HANDLE_VALUE | | this - > m_handle = = nullptr ) {
2022-06-29 21:34:17 +02:00
log : : error ( " LoadLibraryW failed: {}! " , std : : system_category ( ) . message ( : : GetLastError ( ) ) ) ;
return ;
}
# else
2023-03-12 18:43:05 +01:00
this - > m_handle = dlopen ( wolv : : util : : toUTF8String ( path ) . c_str ( ) , RTLD_LAZY ) ;
2022-06-29 21:34:17 +02:00
if ( this - > m_handle = = nullptr ) {
log : : error ( " dlopen failed: {}! " , dlerror ( ) ) ;
return ;
}
# endif
2020-12-22 18:10:01 +01:00
2022-02-01 22:09:44 +01:00
this - > m_initializePluginFunction = getPluginFunction < InitializePluginFunc > ( " initializePlugin " ) ;
this - > m_getPluginNameFunction = getPluginFunction < GetPluginNameFunc > ( " getPluginName " ) ;
this - > m_getPluginAuthorFunction = getPluginFunction < GetPluginAuthorFunc > ( " getPluginAuthor " ) ;
this - > m_getPluginDescriptionFunction = getPluginFunction < GetPluginDescriptionFunc > ( " getPluginDescription " ) ;
this - > m_getCompatibleVersionFunction = getPluginFunction < GetCompatibleVersionFunc > ( " getCompatibleVersion " ) ;
this - > m_setImGuiContextFunction = getPluginFunction < SetImGuiContextFunc > ( " setImGuiContext " ) ;
this - > m_isBuiltinPluginFunction = getPluginFunction < IsBuiltinPluginFunc > ( " isBuiltinPlugin " ) ;
2023-07-13 14:08:23 +02:00
this - > m_getSubCommandsFunction = getPluginFunction < GetSubCommandsFunc > ( " getSubCommands " ) ;
2020-12-22 18:10:01 +01:00
}
2021-02-19 13:22:12 +01:00
Plugin : : Plugin ( Plugin & & other ) noexcept {
2021-02-07 14:57:13 +01:00
this - > m_handle = other . m_handle ;
2022-02-01 22:09:44 +01:00
this - > m_path = std : : move ( other . m_path ) ;
this - > m_initializePluginFunction = other . m_initializePluginFunction ;
this - > m_getPluginNameFunction = other . m_getPluginNameFunction ;
this - > m_getPluginAuthorFunction = other . m_getPluginAuthorFunction ;
this - > m_getPluginDescriptionFunction = other . m_getPluginDescriptionFunction ;
this - > m_getCompatibleVersionFunction = other . m_getCompatibleVersionFunction ;
this - > m_setImGuiContextFunction = other . m_setImGuiContextFunction ;
this - > m_isBuiltinPluginFunction = other . m_isBuiltinPluginFunction ;
2023-07-13 14:08:23 +02:00
this - > m_getSubCommandsFunction = other . m_getSubCommandsFunction ;
2022-02-01 22:09:44 +01:00
other . m_handle = nullptr ;
other . m_initializePluginFunction = nullptr ;
other . m_getPluginNameFunction = nullptr ;
other . m_getPluginAuthorFunction = nullptr ;
other . m_getPluginDescriptionFunction = nullptr ;
other . m_getCompatibleVersionFunction = nullptr ;
other . m_setImGuiContextFunction = nullptr ;
other . m_isBuiltinPluginFunction = nullptr ;
2023-07-13 14:08:23 +02:00
other . m_getSubCommandsFunction = nullptr ;
2021-02-07 14:57:13 +01:00
}
2020-12-22 18:10:01 +01:00
Plugin : : ~ Plugin ( ) {
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
if ( this - > m_handle ! = nullptr )
FreeLibrary ( this - > m_handle ) ;
# else
if ( this - > m_handle ! = nullptr )
dlclose ( this - > m_handle ) ;
# endif
2020-12-22 18:10:01 +01:00
}
2022-01-17 20:06:00 +01:00
bool Plugin : : initializePlugin ( ) const {
2023-06-18 10:18:41 +02:00
if ( this - > m_handle = = nullptr )
return false ;
2023-07-15 14:29:14 +02:00
const auto pluginName = wolv : : util : : toUTF8String ( this - > m_path . filename ( ) ) ;
2022-01-23 23:28:56 +01:00
const auto requestedVersion = getCompatibleVersion ( ) ;
2023-06-26 14:01:45 +02:00
if ( requestedVersion ! = ImHexApi : : System : : getImHexVersion ( ) ) {
if ( requestedVersion . empty ( ) ) {
log : : warn ( " Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex. " , wolv : : util : : toUTF8String ( this - > m_path . filename ( ) ) ) ;
} else {
log : : error ( " Refused to load plugin '{}' which was built for a different version of ImHex: '{}' " , wolv : : util : : toUTF8String ( this - > m_path . filename ( ) ) , requestedVersion ) ;
return false ;
}
2022-01-23 23:28:56 +01:00
}
2022-01-17 20:06:00 +01:00
if ( this - > m_initializePluginFunction ! = nullptr ) {
2023-07-15 14:29:14 +02:00
try {
this - > m_initializePluginFunction ( ) ;
} catch ( const std : : exception & e ) {
log : : error ( " Plugin '{}' threw an exception on init: {} " , pluginName , e . what ( ) ) ;
} catch ( . . . ) {
log : : error ( " Plugin '{}' threw an exception on init " , pluginName ) ;
}
2022-01-17 20:06:00 +01:00
} else {
return false ;
}
2022-01-23 23:28:56 +01:00
this - > m_initialized = true ;
return true ;
2021-02-19 13:22:12 +01:00
}
std : : string Plugin : : getPluginName ( ) const {
if ( this - > m_getPluginNameFunction ! = nullptr )
return this - > m_getPluginNameFunction ( ) ;
else
2021-05-29 21:51:00 +02:00
return hex : : format ( " Unknown Plugin @ 0x{0:016X} " , reinterpret_cast < intptr_t > ( this - > m_handle ) ) ;
2021-02-19 13:22:12 +01:00
}
std : : string Plugin : : getPluginAuthor ( ) const {
if ( this - > m_getPluginAuthorFunction ! = nullptr )
return this - > m_getPluginAuthorFunction ( ) ;
else
return " Unknown " ;
}
std : : string Plugin : : getPluginDescription ( ) const {
if ( this - > m_getPluginDescriptionFunction ! = nullptr )
return this - > m_getPluginDescriptionFunction ( ) ;
else
return " " ;
2020-12-22 18:10:01 +01:00
}
2022-01-23 23:28:56 +01:00
std : : string Plugin : : getCompatibleVersion ( ) const {
if ( this - > m_getCompatibleVersionFunction ! = nullptr )
return this - > m_getCompatibleVersionFunction ( ) ;
else
return " " ;
}
2021-08-21 00:52:11 +02:00
void Plugin : : setImGuiContext ( ImGuiContext * ctx ) const {
if ( this - > m_setImGuiContextFunction ! = nullptr )
this - > m_setImGuiContextFunction ( ctx ) ;
}
2022-02-01 18:09:40 +01:00
[[nodiscard]] bool Plugin : : isBuiltinPlugin ( ) const {
if ( this - > m_isBuiltinPluginFunction ! = nullptr )
return this - > m_isBuiltinPluginFunction ( ) ;
else
return false ;
}
2022-03-04 11:36:37 +01:00
const std : : fs : : path & Plugin : : getPath ( ) const {
2022-01-17 20:06:00 +01:00
return this - > m_path ;
}
2022-01-23 23:28:56 +01:00
bool Plugin : : isLoaded ( ) const {
return this - > m_initialized ;
2022-01-18 00:10:10 +01:00
}
2023-07-13 14:08:23 +02:00
std : : span < SubCommand > Plugin : : getSubCommands ( ) const {
if ( this - > m_getSubCommandsFunction ! = nullptr ) {
auto result = this - > m_getSubCommandsFunction ( ) ;
2023-07-21 14:12:08 +02:00
return * reinterpret_cast < std : : vector < SubCommand > * > ( result ) ;
2023-07-13 14:08:23 +02:00
} else
return { } ;
}
2022-01-17 20:06:00 +01:00
2022-01-24 20:53:17 +01:00
void * Plugin : : getPluginFunction ( const std : : string & symbol ) {
2022-06-29 21:34:17 +02:00
# if defined(OS_WINDOWS)
return reinterpret_cast < void * > ( GetProcAddress ( this - > m_handle , symbol . c_str ( ) ) ) ;
# else
return dlsym ( this - > m_handle , symbol . c_str ( ) ) ;
# endif
2022-01-23 23:28:56 +01:00
}
2022-01-17 20:06:00 +01:00
2022-03-04 11:36:37 +01:00
std : : fs : : path PluginManager : : s_pluginFolder ;
2022-02-01 18:09:40 +01:00
std : : vector < Plugin > PluginManager : : s_plugins ;
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
2021-04-20 21:46:48 +02:00
PluginManager : : s_pluginFolder = pluginFolder ;
2020-12-22 18:10:01 +01:00
2022-03-04 11:36:37 +01:00
for ( auto & pluginPath : std : : fs : : directory_iterator ( pluginFolder ) ) {
2021-02-07 13:40:47 +01:00
if ( pluginPath . is_regular_file ( ) & & pluginPath . path ( ) . extension ( ) = = " .hexplug " )
2022-06-29 21:34:17 +02:00
PluginManager : : s_plugins . emplace_back ( pluginPath . path ( ) ) ;
2021-01-12 16:50:15 +01:00
}
2021-04-20 21:46:48 +02:00
if ( PluginManager : : s_plugins . empty ( ) )
return false ;
return true ;
2020-12-22 18:10:01 +01:00
}
2021-04-20 21:46:48 +02:00
void PluginManager : : unload ( ) {
PluginManager : : s_plugins . clear ( ) ;
PluginManager : : s_pluginFolder . clear ( ) ;
2020-12-22 18:10:01 +01:00
}
2021-04-20 21:46:48 +02:00
void PluginManager : : reload ( ) {
PluginManager : : unload ( ) ;
PluginManager : : load ( PluginManager : : s_pluginFolder ) ;
2020-12-22 18:10:01 +01:00
}
}