total99resources.cpp
1 /********************************************************************************
2  * FARSA - Total99 *
3  * Copyright (C) 2005-2011 Gianluca Massera <emmegian@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (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 St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #include "total99resources.h"
21 #include <cstdlib>
22 #include <QApplication>
23 #include <QFile>
24 #include <QDir>
25 #include <QFileInfo>
26 #include <QPluginLoader>
27 #include <QStringList>
28 #include <QRegExp>
29 #include <QFileInfo>
30 #include <QSet>
31 #include <QStringList>
32 #include "configurationparameters.h"
33 #include "factory.h"
34 #include "farsaplugin.h"
35 #include "evorobotviewer.h"
36 #include "logger.h"
37 #include "dependencysorter.h"
38 
39 // We need this to set the DLL search path on Windows
40 #ifdef FARSA_WIN
41  #include "Windows.h"
42 #endif
43 
44 // All the suff below is to avoid warnings on Windows about the use of the
45 // unsafe function getenv
46 #if defined(_MSC_VER)
47  #pragma warning(push)
48  #pragma warning(disable:4996)
49 #endif
50 
51 namespace farsa {
52 
53 QString Total99Resources::findResource( QString resourceName ) {
54  //--- search in the user directory
55  if ( QFile::exists( confUserPath + "/" + resourceName ) ) {
56  return ( confUserPath + "/" + resourceName );
57  }
58  //--- search in the template user directory
59  if ( QFile::exists( confUserPath + "/templates/" + uiTemplate + "/" + resourceName ) ) {
60  return ( confUserPath + "/templates/" + uiTemplate + "/" + resourceName );
61  }
62  //--- search in the global directory
63  if ( QFile::exists( confBasePath + "/" + resourceName ) ) {
64  return ( confBasePath + "/" + resourceName );
65  }
66  //--- search in the global template directory
67  if ( QFile::exists( confBasePath + "/templates/" + uiTemplate + "/" + resourceName ) ) {
68  return ( confBasePath + "/templates/" + uiTemplate + "/" + resourceName );
69  }
70  //--- not find anything
71  return QString();
72 }
73 
78 // QString Total99Resources::pluginUserPath;
81 
82 bool Total99Resources::loadPlugin(QString filename)
83 {
84 #ifdef FARSA_WIN
85  // On Windows, we first have to change the DLL search path to include the directory with plugins
86  SetDllDirectory(pluginBasePath.toLatin1().data());
87 #endif
88 
89  // Loading the plugin
90  QString errorString;
91  FarsaPlugin* plugin = loadSinglePlugin(filename, &errorString);
92 
93  if (plugin == NULL) {
94  // Error loading the plugin
95  Logger::error("ERROR LOADING PLUGIN: " + errorString);
96  return false;
97  }
98  Logger::info("Loaded plugin \"" + filename + "\"");
99 
100  // The object to sort plugin by dependency
101  DependencySorter<QString> pluginDependencies;
102 
103  // Getting plugin dependencies. We store loaded plugin in a map whose key is the plugin name (we also use this
104  // to avoid loading the same plugin twice) and we keep a list of dependencies yet to load. Here it is not
105  // important the loading order (while the registration order is, see below).
106  QStringList dependenciesToLoad = plugin->getDependencies();
107  QMap<QString, FarsaPlugin*> loadedDependencies;
108  while (!dependenciesToLoad.empty()) {
109  // Taking the first dependency
110  QString curDep = dependenciesToLoad.takeFirst();
111 
112  // If the plugin has already been loaded, skipping it
113  if (loadedDependencies.contains(curDep)) {
114  continue;
115  }
116 
117  // Loading the plugin
118  loadedDependencies[curDep] = loadSinglePlugin(curDep, &errorString);
119  if (loadedDependencies[curDep] == NULL) {
120  // Error loading the plugin
121  Logger::error("ERROR LOADING PLUGIN: " + errorString);
122  return false;
123  }
124  Logger::info("Loaded plugin \"" + curDep + "\"");
125 
126  // Adding the dependencies of the plugin we have just loaded
127  QStringList newDependencies = loadedDependencies[curDep]->getDependencies();
128  pluginDependencies.add(curDep, newDependencies);
129  foreach (QString d, newDependencies) {
130  dependenciesToLoad.append(d);
131  }
132  }
133 
134  // Now that we have loaded all plugins, we need to sort the dependencies, so to call registration in the
135  // correct order
136  try {
137  QStringList sortedDependencies = pluginDependencies.sort();
138  foreach (QString d, sortedDependencies) {
139  loadedDependencies[d]->registerTypesOnFactory();
140  }
141  } catch (CircularDependencyException &) {
142  Logger::error("ERROR LOADING PLUGIN: circular dependency found");
143  return false;
144  }
145 
146  // We can finally register the plugin we loaded first
147  plugin->registerTypesOnFactory();
148 
149 #ifdef FARSA_WIN
150  // Reverting to the default behaviour
151  SetDllDirectory(NULL);
152 #endif
153 
154  return true;
155 }
156 
158 {
159  foreach (QString pluginfile, dir.entryList()) {
160  if (!QLibrary::isLibrary(pluginfile)) {
161  continue;
162  }
163 
164  // Building a regular expression to actually load only files containing farsaPlugin
165  QRegExp r(".*farsaPlugin.*");
166  if (r.indexIn(pluginfile) == -1) {
167  continue;
168  }
169  if (loadPlugin(dir.absoluteFilePath(pluginfile))) {
170  Logger::info( "Loaded Plugin \"" + pluginfile + "\"");
171  }
172  }
173 }
174 
176 {
177  // First loading plugins in the pluginFile:X parameters
178  QStringList plugins = params.getParametersWithPrefixList("TOTAL99", "pluginFile" );
179  foreach (QString param, plugins) {
180  QString value = params.getValue("TOTAL99/" + param);
181  if (!QFileInfo(value).isFile()) {
182  // Trying to see if the plugin is in the global plugin directory
183  value = pluginBasePath + "/" + value + ".farsaPlugin" + pluginSuffix;
184  if (!QFileInfo(value).isFile()) {
185  Logger::warning( "Ignoring un-existing plugin \"" + params.getValue("TOTAL99/" + param) + "\"" );
186  continue;
187  }
188  }
189  loadPlugin(value);
190  }
191 
192  // Then loading all pugins from the pluginPath:X parameters
193  QStringList pluginPaths = params.getParametersWithPrefixList( "TOTAL99", "pluginPath" );
194  foreach( QString param, pluginPaths ) {
195  QString value = params.getValue( "TOTAL99/"+param );
196  if ( ! QFileInfo( value ).isDir() ) {
197  Logger::warning( "Ignoring un-existing plugin path \"" + value + "\"" );
198  continue;
199  }
200  loadPlugins( QDir( value ) );
201  }
202 }
203 
205 #ifdef FARSA_WIN
206  confBasePath = qApp->applicationDirPath() + "/../conf";
207  pluginConfigBasePath = qApp->applicationDirPath() + "/../plugins";
208 #else
209  confBasePath = qApp->applicationDirPath() + "/../share/FARSA/conf";
210  pluginConfigBasePath = qApp->applicationDirPath() + "/../share/FARSA/plugins";
211 #endif
212  pluginBasePath = qApp->applicationDirPath() + "/../lib/FARSA/plugins";
213 #ifdef FARSA_LINUX
214  confUserPath = QString(getenv("HOME")) + "/.FARSA/total99";
215 // pluginUserPath = QString(getenv("HOME")) + "/.FARSA/total99/plugins";
216  pluginSuffix = ".so";
217 #endif
218 #ifdef FARSA_MAC
219  confUserPath = QString(getenv("HOME")) + "/Library/Application Support/FARSA/Total99";
220 // pluginUserPath = QString(getenv("HOME")) + "/Library/Application Support/FARSA/Total99/plugins";
221  pluginSuffix = ".dylib";
222 #endif
223 #ifdef FARSA_WIN
224  confUserPath = QString(getenv("APPDATA")) + "/FARSA/Total99";
225 // pluginUserPath = QString(getenv("APPDATA")) + "/FARSA/Total99/plugins";
226 #ifdef FARSA_DEBUG
227  pluginSuffix = "d.dll";
228 #else
229  pluginSuffix = ".dll";
230 #endif
231 #endif
232 
233  QDir dir;
234  dir.mkpath( confUserPath );
235 // dir.mkpath( pluginUserPath );
236 
237  // NOTE: This has been removed because it is not documented and furthermore with the upcoming
238  // moving of big parts of code to plugins this would mean loading a lot of stuffs
239 // // Loading plugins from default directories
240 // loadPlugins( QDir( pluginBasePath ) );
241 // loadPlugins( QDir( pluginUserPath ) );
242 
243  uiTemplate = "kids";
244  Logger::info( "Total99 Resources Initialized" );
245 }
246 
247 FarsaPlugin* Total99Resources::loadSinglePlugin(QString filename, QString* errorString)
248 {
249  if (!QFileInfo(filename).isFile()) {
250  filename = pluginBasePath + "/" + filename + ".farsaPlugin" + pluginSuffix;
251  }
252 
253  // Trying to load the plugin
254  QPluginLoader loader(filename);
255  FarsaPlugin* plugin = qobject_cast<FarsaPlugin*>(loader.instance());
256  if ((plugin == NULL) && (errorString != NULL)) {
257  *errorString = "Error trying to load \"" + filename + "\", reason: " + loader.errorString();
258  }
259 
260  return plugin;
261 }
262 
263 }
264 
265 // All the suff below is to restore the warning state on Windows
266 #if defined(_MSC_VER)
267  #pragma warning(pop)
268 #endif
269