pluginloader.cpp
1 //------------------------------------------------------------------------------
2 // pluginloader.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2011 "Blzut3" <admin@maniacsvault.net>
22 //------------------------------------------------------------------------------
23 #include "pluginloader.h"
24 
25 #include "log.h"
26 #include "configuration/doomseekerconfig.h"
27 #include "ini/inisection.h"
28 #include "ini/inivariable.h"
29 #include "plugins/engineplugin.h"
30 #include "serverapi/masterclient.h"
31 #include "strings.h"
32 #include <cassert>
33 #include <QDir>
34 
35 #ifdef Q_OS_WIN32
36  #include <windows.h>
37  #define dlopen(a,b) LoadLibrary(a)
38  #define dlsym(a,b) GetProcAddress(a, b)
39  #define dlclose(a) FreeLibrary(a)
40  #define dlerror() GetLastError()
41  #ifdef _MSC_VER
42  #pragma warning(disable: 4251)
43  #endif
44 #else
45  #include <dlfcn.h>
46  #include <dirent.h>
47 #endif
48 
50 DClass<PluginLoader::Plugin>
51 {
52  public:
53  EnginePlugin *info;
54  QString file;
55  #ifdef Q_OS_WIN32
56  HMODULE library;
57  #else
58  void *library;
59  #endif
60 };
61 
62 DPointered(PluginLoader::Plugin)
63 
64 PluginLoader::Plugin::Plugin(unsigned int type, QString file)
65 {
66  d->file = file;
67  // Load the library
68  d->library = dlopen(d->file.toAscii().constData(), RTLD_NOW);
69 
70  if(d->library != NULL)
71  {
72  unsigned int (*doomSeekerABI)() = (unsigned int(*)()) (dlsym(d->library, "doomSeekerABI"));
73  if(!doomSeekerABI || doomSeekerABI() != DOOMSEEKER_ABI_VERSION)
74  {
75  // Unsupported version
76  unload();
77  return;
78  }
79 
80  EnginePlugin *(*doomSeekerInit)() = (EnginePlugin *(*)()) (dlsym(d->library, "doomSeekerInit"));
81  if(doomSeekerInit == NULL)
82  { // This is not a valid plugin.
83  unload();
84  return;
85  }
86 
87  d->info = doomSeekerInit();
88  if(!info()->data()->valid)
89  {
90  unload();
91  return;
92  }
93 
94  gLog << QObject::tr("Loaded plugin: \"%1\"!").arg(info()->data()->name);
95  }
96  else
97  {
98  gLog << QObject::tr("Failed to open plugin: %1").arg(file);
99  gLog << QString("Last error was: %1").arg(dlerror());
100  }
101 }
102 
103 PluginLoader::Plugin::~Plugin()
104 {
105  unload();
106 }
107 
108 void *PluginLoader::Plugin::function(const char* func) const
109 {
110  return (void *) dlsym(d->library, func);
111 }
112 
114 {
115  return d->info;
116 }
117 
118 void PluginLoader::Plugin::initConfig()
119 {
120  if (isValid())
121  {
122  IniSection cfgSection = gConfig.iniSectionForPlugin(info()->data()->name);
123  info()->setConfig(cfgSection);
124  }
125 }
126 
127 bool PluginLoader::Plugin::isValid() const
128 {
129  return d->library != NULL;
130 }
131 
132 void PluginLoader::Plugin::unload()
133 {
134  if (d->library != NULL)
135  {
136  dlclose(d->library);
137  d->library = NULL;
138  }
139 }
140 
142 DClass<PluginLoader>
143 {
144  public:
145  unsigned int type;
146  QString pluginsDirectory;
147  QList<PluginLoader::Plugin*> plugins;
148 };
149 
150 DPointered(PluginLoader)
151 
152 PluginLoader *PluginLoader::staticInstance = NULL;
153 
154 PluginLoader::PluginLoader(unsigned int type, const QStringList &directories)
155 {
156  d->type = type;
157  foreach (const QString &dir, directories)
158  {
159  d->pluginsDirectory = dir;
160  if (filesInDir())
161  {
162  break;
163  }
164  }
165  if (numPlugins() == 0) // No plugins?!
166  {
167  gLog << QObject::tr("Failed to locate plugins.");
168  }
169 }
170 
171 PluginLoader::~PluginLoader()
172 {
173  qDeleteAll(d->plugins);
174 }
175 
177 {
178  qDeleteAll(d->plugins);
179  d->plugins.clear();
180 }
181 
183 {
184  if (staticInstance != NULL)
185  {
186  delete staticInstance;
187  staticInstance = NULL;
188  }
189 }
190 
191 bool PluginLoader::filesInDir()
192 {
193  gLog << QString("Attempting to load plugins from directory: %1").arg(d->pluginsDirectory);
194  QDir dir(d->pluginsDirectory);
195  if (!dir.exists())
196  {
197  return false;
198  }
199 #ifdef Q_OS_WIN32
200  QStringList windowsNamesFilter;
201  windowsNamesFilter << "*.dll";
202  dir.setNameFilters(windowsNamesFilter);
203 #endif
204  foreach (const QString& entry, dir.entryList(QDir::Files))
205  {
206  QString pluginFilePath = Strings::combinePaths(d->pluginsDirectory, entry);
207  Plugin *plugin = new Plugin(d->type, pluginFilePath);
208  if (plugin->isValid())
209  d->plugins << plugin;
210  else
211  delete plugin;
212  }
213  return numPlugins() != 0;
214 }
215 
216 EnginePlugin *PluginLoader::info(int pluginIndex) const
217 {
218  const Plugin* p = plugin(pluginIndex);
219  if (p != NULL)
220  {
221  return p->info();
222  }
223  return NULL;
224 }
225 
226 void PluginLoader::init(const QStringList &directories)
227 {
228  if (staticInstance != NULL)
229  {
230  qDebug() << "Attempting to re-init PluginLoader";
231  assert(false);
232  return;
233  }
234  staticInstance = new PluginLoader(MAKEID('E', 'N', 'G', 'N'), directories);
235 }
236 
238 {
239  foreach (Plugin *plugin, d->plugins)
240  {
241  plugin->initConfig();
242  }
243 }
244 
246 {
247  assert(staticInstance != NULL);
248  return staticInstance;
249 }
250 
251 const unsigned int PluginLoader::numPlugins() const
252 {
253  return d->plugins.size();
254 }
255 
256 const QList<PluginLoader::Plugin*> &PluginLoader::plugins() const
257 {
258  return d->plugins;
259 }
260 
261 const PluginLoader::Plugin* PluginLoader::plugin(unsigned int index) const
262 {
263  return d->plugins[index];
264 }
265 
266 int PluginLoader::pluginIndexFromName(const QString& name) const
267 {
268  // Why the mangling?
269  // Ever since version 0.8.1b there was a bug that removed all spacebars
270  // from plugin names. This bug is fixed in a commit made on 2013-11-01,
271  // but the fix breaks at least some parts of configuration for plugins
272  // that have spacebars in their names. For example, all server
273  // configurations for Chocolate Doom won't load anymore. To prevent that,
274  // we need to treat spacebars as non-existent here. Simply put:
275  // "Chocolate Doom" == "ChocolateDoom"
276  QString mangledName = QString(name).replace(" ", "");
277  for (int i = 0; i < d->plugins.size(); ++i)
278  {
279  QString mangledCandidate = QString(d->plugins[i]->info()->data()->name).replace(" ", "");
280  if (mangledName.compare(mangledCandidate) == 0)
281  {
282  return i;
283  }
284  }
285 
286  return -1;
287 }
288 
289 void PluginLoader::resetPluginsDirectory(const QString& pluginsDirectory)
290 {
291  d->pluginsDirectory = pluginsDirectory;
292  clearPlugins();
293  filesInDir();
294 }
295 
296 const PluginLoader::Plugin* PluginLoader::operator[] (unsigned int index) const
297 {
298  return d->plugins[index];
299 }
static QString combinePaths(QString pathFront, QString pathEnd)
Definition: strings.cpp:145
void resetPluginsDirectory(const QString &pluginsDirectory)
Resets the plugins directory, clearing the loaded plugins and getting new loaded plugins in the proce...
const Plugin * plugin(unsigned int index) const
Returns the requested plugin or NULL.
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.
const unsigned int numPlugins() const
Gets the number of loaded plugins.
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 NULL.
INI section representation.
Definition: inisection.h:40
void * function(const char *func) const
Returns a pointer to the requested function or NULL.