23 #include "pluginloader.h" 25 #include "configuration/doomseekerconfig.h" 26 #include "ini/inisection.h" 27 #include "ini/inivariable.h" 29 #include "plugins/engineplugin.h" 30 #include "serverapi/masterclient.h" 31 #include "strings.hpp" 37 #define dlopen(a, b) LoadLibrary(a) 38 #define dlsym(a, b) GetProcAddress(a, b) 39 #define dlclose(a) FreeLibrary(a) 40 #define dlerror() PluginLoader::Plugin::getDllWindowsErrorMessage() 42 #pragma warning(disable: 4251) 50 DClass<PluginLoader::Plugin>
61 static bool isForbiddenPlugin(QString file)
63 return QFileInfo(file).fileName().toLower().contains(
"vavoom");
69 PluginLoader::Plugin::Plugin(
unsigned int type, QString file)
77 gLog << QObject::tr(
"Skipping loading of forbidden plugin: %1").arg(file);
83 UINT oldErrorMode = SetErrorMode(0);
84 SetErrorMode(oldErrorMode | SEM_FAILCRITICALERRORS);
86 d->library = dlopen(d->file.toUtf8().constData(), RTLD_NOW);
88 SetErrorMode(oldErrorMode);
91 if (d->library !=
nullptr)
93 auto doomSeekerABI = (
unsigned int (*)())(dlsym(d->library,
"doomSeekerABI"));
94 if (!doomSeekerABI || doomSeekerABI() != DOOMSEEKER_ABI_VERSION)
98 if (doomSeekerABI !=
nullptr)
100 reason = QObject::tr(
101 "plugin ABI version mismatch; plugin: %1, Doomseeker: %2").arg(
102 doomSeekerABI()).arg(DOOMSEEKER_ABI_VERSION);
106 reason = QObject::tr(
"plugin doesn't report its ABI version");
108 gLog << QObject::tr(
"Cannot load plugin %1, reason: %2.").arg(file, reason);
113 auto doomSeekerInit = (
EnginePlugin * (*)())(dlsym(d->library,
"doomSeekerInit"));
114 if (doomSeekerInit ==
nullptr)
120 d->info = doomSeekerInit();
121 if (!info()->data()->valid)
127 gLog << QObject::tr(
"Loaded plugin: \"%1\"!").arg(info()->data()->name);
132 gLog << QObject::tr(
"Failed to open plugin: %1").arg(file);
133 gLog << QString(
"Last error was: %1").arg(dlerror());
137 PluginLoader::Plugin::~Plugin()
144 return (
void *) dlsym(d->library, func);
152 void PluginLoader::Plugin::initConfig()
156 IniSection cfgSection = gConfig.iniSectionForPlugin(
info()->data()->name);
157 info()->setConfig(cfgSection);
161 bool PluginLoader::Plugin::isValid()
const 163 return d->library !=
nullptr;
166 void PluginLoader::Plugin::unload()
168 if (d->library !=
nullptr)
171 d->library =
nullptr;
176 QString PluginLoader::Plugin::getDllWindowsErrorMessage()
178 QString baseErrorString(
"%1 (%2)");
180 DWORD errorId = GetLastError();
184 return baseErrorString.arg(
"Procedure not found. Perhaps this plugin is for a different Doomseeker version?",
185 QString::number(errorId));
190 DWORD bufLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
191 FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
192 (LPWSTR) &lpMsgBuf, 0, NULL );
194 LPCWSTR lpMsgStr = (LPCWSTR)lpMsgBuf;
195 QString result = QString::fromWCharArray(lpMsgStr, bufLen);
196 if (result.contains(
"%1"))
200 result = result.arg(
"Plugin");
202 result = result.trimmed();
206 return baseErrorString.arg(result, QString::number(errorId));
217 QString pluginsDirectory;
218 QList<PluginLoader::Plugin *> plugins;
225 PluginLoader::PluginLoader(
unsigned int type,
const QStringList &directories)
228 for (
const QString &dir : directories)
230 d->pluginsDirectory = dir;
238 gLog << QObject::tr(
"Failed to locate plugins.");
242 PluginLoader::~PluginLoader()
244 qDeleteAll(d->plugins);
249 qDeleteAll(d->plugins);
255 if (staticInstance !=
nullptr)
257 delete staticInstance;
258 staticInstance =
nullptr;
262 bool PluginLoader::filesInDir()
264 gLog << QString(
"Attempting to load plugins from directory: %1").arg(d->pluginsDirectory);
265 QDir dir(d->pluginsDirectory);
271 QStringList windowsNamesFilter;
272 windowsNamesFilter <<
"*.dll";
273 dir.setNameFilters(windowsNamesFilter);
275 for (
const QString &entry : dir.entryList(QDir::Files))
279 if (plugin->isValid())
280 d->plugins << plugin;
299 if (staticInstance !=
nullptr)
301 qDebug() <<
"Attempting to re-init PluginLoader";
305 staticInstance =
new PluginLoader(MAKEID(
'E',
'N',
'G',
'N'), directories);
312 plugin->initConfig();
318 assert(staticInstance !=
nullptr);
319 return staticInstance;
324 return d->plugins.size();
327 const QList<PluginLoader::Plugin *> &PluginLoader::plugins()
const 334 return d->plugins[index];
347 QString mangledName = QString(name).replace(
" ",
"");
348 for (
int i = 0; i < d->plugins.size(); ++i)
350 QString mangledCandidate = QString(d->plugins[i]->info()->data()->name).replace(
" ",
"");
351 if (mangledName.compare(mangledCandidate) == 0)
362 d->pluginsDirectory = pluginsDirectory;
369 return d->plugins[index];
static QString combinePaths(QString pathFront, QString pathEnd)
void resetPluginsDirectory(const QString &pluginsDirectory)
Resets the plugins directory, clearing the loaded plugins and getting new loaded plugins in the proce...
unsigned int numPlugins() const
Gets the number of loaded plugins.
const Plugin * plugin(unsigned int index) const
Returns the requested plugin or nullptr.
void clearPlugins()
Clears the plugins list.
int pluginIndexFromName(const QString &name) const
Looks for a plugin which name equals to parameter.
EnginePlugin * info(int pluginIndex) const
Convenience method - calls Plugin::info() for specified plugin.
static void deinit()
Destroys the init() instance.
static void init(const QStringList &directories)
Attempts to load plugins from given set of directories.
static PluginLoader * instance()
Accesses instance of the class after init().
void initConfig()
Inits configuration for plugins.
EnginePlugin * info() const
Main plugin interface.
const Plugin * operator[](unsigned int index) const
Returns the requested plugin or nullptr.
INI section representation.
Plugin(unsigned int type, QString file)
Inits a plugin.
void * function(const char *func) const
Returns a pointer to the requested function or nullptr.