1 /*
2 	Copyright (C) 2005-2007 Feeling Software Inc.
3 	Portions of the code are:
4 	Copyright (C) 2005-2007 Sony Computer Entertainment America
5 
6 	MIT License: http://www.opensource.org/licenses/mit-license.php
7 */
8 
9 #include "StdAfx.h"
10 #include "FUPlugin.h"
11 #include "FUPluginManager.h"
12 #include "FUFileManager.h"
13 
14 #ifdef WIN32
15 	#include <io.h>
16 	#if defined(UNICODE)
17 		#define ffinddata _wfinddata_t
18 		#define ffindfirst _wfindfirst
19 		#define ffindclose _findclose
20 		#define ffindnext _wfindnext
21 	#else // UNICODE
22 		#define ffinddata _finddata_t
23 		#define ffindfirst _findfirst
24 		#define ffindclose _findclose
25 		#define ffindnext _findnext
26 	#endif
27 #elif defined(__APPLE__) || defined(LINUX)
28 	#include <dlfcn.h>
29 	#include <dirent.h>
30 #endif //WIN32
31 
32 //
33 // FUPlugin
34 //
35 
36 ImplementObjectType(FUPlugin);
37 
38 //
39 // FUPluginManager
40 //
41 
FUPluginManager(const fchar * UNUSED (_filter))42 FUPluginManager::FUPluginManager(const fchar* UNUSED(_filter))
43 {
44 #if 0 // disabled because it causes loads of valgrind warnings on Linux
45 	fstring applicationFolderName = FUFileManager::GetApplicationFolderName();
46 	if (!applicationFolderName.empty())
47 	{
48 		LoadPluginsInFolderName(applicationFolderName, _filter);
49 	}
50 
51 	fstring moduleFolderName = FUFileManager::GetModuleFolderName();
52 	if (!moduleFolderName.empty() && !IsEquivalent(moduleFolderName, applicationFolderName))
53 	{
54 		LoadPluginsInFolderName(moduleFolderName, _filter);
55 	}
56 #endif
57 }
58 
LoadPluginsInFolderName(const fstring & folderName,const fchar * _filter)59 void FUPluginManager::LoadPluginsInFolderName(const fstring& folderName, const fchar* _filter)
60 {
61 	// Append the wanted extension for the plugins.
62 	FUStringBuilder pluginFolder(folderName);
63 	fchar lastChar = folderName[pluginFolder.length() - 1];
64 	if (lastChar != '\\' && lastChar != '/') pluginFolder.append((fchar) '/');
65 	pluginFolder.append(FC("Plugins/"));
66 	pluginFolderName = pluginFolder.ToString();
67 
68 	if (_filter == NULL || _filter[0] == 0) _filter = FC("*.*");
69 	do
70 	{
71 		const fchar* nextFilter = fstrchr(_filter, '|');
72 		fstring filter(_filter);
73 		if (nextFilter != NULL)
74 		{
75 			filter.erase(nextFilter - _filter);
76 			++nextFilter; // skip the pipe.
77 		}
78 		_filter = nextFilter;
79 
80 		// Windows-only for now.
81 #if defined(WIN32)
82 		size_t filterLength = filter.length();
83 		// Iterate over all the filtered files within the given folder.
84 		ffinddata folderIterator;
85 		fstring searchString = pluginFolderName + filter;
86 		intptr_t folderHandle = ffindfirst(searchString.c_str(), &folderIterator);
87 		if (folderHandle != -1L)
88 		{
89 			int32 isDone = FALSE;
90 			while (isDone == FALSE)
91 			{
92 				bool keep = false;
93 				PluginLibrary* library = new PluginLibrary();
94 				library->filename = pluginFolderName + folderIterator.name;
95 
96 				// work around for wildcards and 3 letter extensions that pick up 3+ letter extensions
97 				// e.g. "dir *.fvp" in command prompt on a directory with "a.fvp", "a.fvpa", and "a.dll" returns
98 				// "a.fvp" and "a.fvpa"
99 				bool checkModule = true;
100 				if (filterLength > 3)
101 				{
102 					if ((filter.at(filterLength-4) == FC('.')) && (filter.at(filterLength-3) != FC('*')) &&
103 							(filter.at(filterLength-2) != FC('*')) && (filter.at(filterLength-1) != FC('*')))
104 					{
105 						size_t filepathLength = fstrlen(folderIterator.name);
106 						checkModule = (folderIterator.name[filepathLength-4] == filter.at(filterLength-4)) &&
107 								(folderIterator.name[filepathLength-3] == filter.at(filterLength-3)) &&
108 								(folderIterator.name[filepathLength-2] == filter.at(filterLength-2)) &&
109 								(folderIterator.name[filepathLength-1] == filter.at(filterLength-1));
110 					}
111 				}
112 
113 				library->module = LoadLibrary(library->filename.c_str());
114 				if (checkModule && (library->module != NULL))
115 				{
116 					// Retrieve the necessary callbacks
117 					library->getPluginCount = (GetPluginCount) GetProcAddress(library->module, "GetPluginCount");
118 					library->getPluginType = (GetPluginType) GetProcAddress(library->module, "GetPluginType");
119 					library->createPlugin = (CreatePlugin) GetProcAddress(library->module, "CreatePlugin");
120 					keep = library->createPlugin != NULL && library->getPluginType != NULL && library->getPluginCount != NULL;
121 				}
122 
123 				// This is a valid library.
124 				if (keep) loadedLibraries.push_back(library);
125 				else { SAFE_DELETE(library); }
126 				isDone = ffindnext(folderHandle, &folderIterator);
127 			}
128 			ffindclose(folderHandle);
129 		}
130 
131 #elif defined(__APPLE__) || defined(LINUX)
132 		fm::string s_filter = TO_STRING(filter);
133 		if (s_filter.length() > 0 && s_filter.front() == '*') s_filter.erase(0, 1);
134 		if (s_filter.length() > 0 && s_filter.back() == '*') s_filter.pop_back();
135 
136 		DIR* directory = opendir(TO_STRING(pluginFolderName).c_str());
137 		if (directory == NULL) continue;
138 
139 		dirent* directoryEntry;
140 		while ((directoryEntry = readdir(directory)) != NULL)
141 		{
142 			if (directoryEntry->d_type == DT_DIR) continue; // skip sub-folders.
143 			if (strstr((const char*) directoryEntry->d_name, s_filter.c_str()) != NULL)
144 			{
145 				// We have a match.
146 				bool keep = false;
147 				PluginLibrary* library = new PluginLibrary();
148 				library->filename = pluginFolderName + TO_FSTRING((const char*) directoryEntry->d_name);
149 				fm::string libraryModuleFilename = TO_STRING(library->filename);
150 				DEBUG_OUT("Found dynamic library: %s\n", libraryModuleFilename.c_str());
151 				library->module = dlopen(libraryModuleFilename.c_str(), RTLD_NOW);
152 				if (library->module != NULL)
153 				{
154 					// Retrieve the necessary callbacks
155 					library->getPluginCount = (GetPluginCount) dlsym(library->module, "GetPluginCount");
156 					library->getPluginType = (GetPluginType) dlsym(library->module, "GetPluginType");
157 					library->createPlugin = (CreatePlugin) dlsym(library->module, "CreatePlugin");
158 					keep = library->createPlugin != NULL && library->getPluginType != NULL && library->getPluginCount != NULL;
159 				}
160 
161 				// This is a valid library.
162 				if (keep) loadedLibraries.push_back(library);
163 				else { SAFE_DELETE(library); }
164 			}
165 		}
166 		closedir(directory);
167 
168 #endif // WIN32
169 	} while (_filter != NULL);
170 }
171 
~FUPluginManager()172 FUPluginManager::~FUPluginManager()
173 {
174 	UnloadPlugins();
175 	FUAssert(loadedPlugins.empty(), return);
176 
177 	// Detach all the plugin libraries.
178 	for (PluginLibraryList::iterator it = loadedLibraries.begin(); it != loadedLibraries.end(); ++it)
179 	{
180 #if defined(WIN32)
181 		if ((*it)->module != NULL) FreeLibrary((*it)->module);
182 #elif defined(LINUX) || defined(__APPLE__)
183 		if ((*it)->module != NULL) dlclose((*it)->module);
184 #endif // WIN32
185 	}
186 	CLEAR_POINTER_VECTOR(loadedLibraries);
187 }
188 
LoadPlugins(const FUObjectType & pluginType)189 void FUPluginManager::LoadPlugins(const FUObjectType& pluginType)
190 {
191 	for (PluginLibraryList::iterator it = loadedLibraries.begin(); it != loadedLibraries.end(); ++it)
192 	{
193 #ifndef _DEBUG
194 		try
195 #endif // _DEBUG
196 		{
197 			DEBUG_OUT("Loading plug-in: %s\n", TO_STRING((*it)->filename).c_str());
198 			FUAssert((*it)->createPlugin != NULL && (*it)->getPluginType != NULL && (*it)->getPluginCount != NULL, continue);
199 			uint32 pluginCount = (*((*it)->getPluginCount))();
200 			for (uint32 i = 0; i < pluginCount; ++i)
201 			{
202 				// Retrieve the types of all the plug-ins within this library.
203 				// Compare them against the wanted types and create the wanted plug-ins.
204 				const FUObjectType* type = (*((*it)->getPluginType))(i);
205 				if (type->Includes(pluginType))
206 				{
207 					FUPlugin* plugin = (*((*it)->createPlugin))(i);
208 					if (plugin == NULL) continue;
209 					loadedPlugins.push_back(plugin);
210 				}
211 			}
212 		}
213 #ifndef _DEBUG
214 		catch (...)
215 		{
216 			fm::string _filename = TO_STRING((*it)->filename);
217 			ERROR_OUT("Unhandled exception when loading plugin: %s.", _filename.c_str());
218 		}
219 #endif // _DEBUG
220 	}
221 }
222 
AddPluginLibrary(FUPluginManager::GetPluginCount fnGetPluginCount,FUPluginManager::GetPluginType fnGetPluginType,FUPluginManager::CreatePlugin fnCreatePlugin)223 void FUPluginManager::AddPluginLibrary(FUPluginManager::GetPluginCount fnGetPluginCount, FUPluginManager::GetPluginType fnGetPluginType, FUPluginManager::CreatePlugin fnCreatePlugin)
224 {
225 	PluginLibrary* library = new PluginLibrary();
226 	library->getPluginCount = fnGetPluginCount;
227 	library->getPluginType = fnGetPluginType;
228 	library->createPlugin = fnCreatePlugin;
229 	library->filename.clear();
230 	library->module = NULL;
231 	loadedLibraries.push_back(library);
232 }
233 
UnloadPlugins()234 void FUPluginManager::UnloadPlugins()
235 {
236 	loadedPlugins.clear();
237 }
238