1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 PluginManager.cpp
6
7 Leland Lucius
8
9 *******************************************************************//*!
10
11 \file PluginManager.cpp
12 \brief
13
14 ************************************************************************//**
15 \class PluginManager
16 \brief PluginManager maintains a list of all plug ins. That covers modules,
17 effects, generators, analysis-effects, commands. It also has functions
18 for shared and private configs - which need to move out.
19 *****************************************************************************/
20
21
22 #include "PluginManager.h"
23
24
25
26 #include <algorithm>
27
28 #include <wx/log.h>
29 #include <wx/tokenzr.h>
30
31 #include "ModuleInterface.h"
32
33 #include "AudacityFileConfig.h"
34 #include "Internat.h" // for macro XO
35 #include "FileNames.h"
36 #include "MemoryX.h"
37 #include "ModuleManager.h"
38 #include "PlatformCompatibility.h"
39 #include "widgets/AudacityMessageBox.h"
40
41 ///////////////////////////////////////////////////////////////////////////////
42 //
43 // Plugindescriptor
44 //
45 ///////////////////////////////////////////////////////////////////////////////
46
PluginDescriptor()47 PluginDescriptor::PluginDescriptor()
48 {
49 mPluginType = PluginTypeNone;
50 mEnabled = false;
51 mValid = false;
52 mInstance = nullptr;
53
54 mEffectType = EffectTypeNone;
55 mEffectInteractive = false;
56 mEffectDefault = false;
57 mEffectLegacy = false;
58 mEffectRealtime = false;
59 mEffectAutomatable = false;
60 }
61
~PluginDescriptor()62 PluginDescriptor::~PluginDescriptor()
63 {
64 }
65
66 PluginDescriptor &PluginDescriptor::operator =(PluginDescriptor &&) = default;
67
IsInstantiated() const68 bool PluginDescriptor::IsInstantiated() const
69 {
70 return mInstance != nullptr;
71 }
72
GetInstance()73 ComponentInterface *PluginDescriptor::GetInstance()
74 {
75 if (!mInstance)
76 {
77 if (GetPluginType() == PluginTypeModule)
78 mInstance = ModuleManager::Get().CreateProviderInstance(GetID(), GetPath());
79 else
80 {
81 muInstance = ModuleManager::Get().CreateInstance(GetProviderID(), GetPath());
82 mInstance = muInstance.get();
83 }
84 }
85
86 return mInstance;
87 }
88
SetInstance(std::unique_ptr<ComponentInterface> instance)89 void PluginDescriptor::SetInstance(std::unique_ptr<ComponentInterface> instance)
90 {
91 muInstance = std::move(instance);
92 mInstance = muInstance.get();
93 }
94
GetPluginType() const95 PluginType PluginDescriptor::GetPluginType() const
96 {
97 return mPluginType;
98 }
99
GetID() const100 const PluginID & PluginDescriptor::GetID() const
101 {
102 return mID;
103 }
104
GetProviderID() const105 const PluginID & PluginDescriptor::GetProviderID() const
106 {
107 return mProviderID;
108 }
109
GetPath() const110 const PluginPath & PluginDescriptor::GetPath() const
111 {
112 return mPath;
113 }
114
GetSymbol() const115 const ComponentInterfaceSymbol & PluginDescriptor::GetSymbol() const
116 {
117 return mSymbol;
118 }
119
GetUntranslatedVersion() const120 wxString PluginDescriptor::GetUntranslatedVersion() const
121 {
122 return mVersion;
123 }
124
GetVendor() const125 wxString PluginDescriptor::GetVendor() const
126 {
127 return mVendor;
128 }
129
IsEnabled() const130 bool PluginDescriptor::IsEnabled() const
131 {
132 return mEnabled;
133 }
134
IsValid() const135 bool PluginDescriptor::IsValid() const
136 {
137 return mValid;
138 }
139
SetPluginType(PluginType type)140 void PluginDescriptor::SetPluginType(PluginType type)
141 {
142 mPluginType = type;
143 }
144
SetID(const PluginID & ID)145 void PluginDescriptor::SetID(const PluginID & ID)
146 {
147 mID = ID;
148 }
149
SetProviderID(const PluginID & providerID)150 void PluginDescriptor::SetProviderID(const PluginID & providerID)
151 {
152 mProviderID = providerID;
153 }
154
SetPath(const PluginPath & path)155 void PluginDescriptor::SetPath(const PluginPath & path)
156 {
157 mPath = path;
158 }
159
SetSymbol(const ComponentInterfaceSymbol & symbol)160 void PluginDescriptor::SetSymbol(const ComponentInterfaceSymbol & symbol)
161 {
162 mSymbol = symbol;
163 }
164
SetVersion(const wxString & version)165 void PluginDescriptor::SetVersion(const wxString & version)
166 {
167 mVersion = version;
168 }
169
SetVendor(const wxString & vendor)170 void PluginDescriptor::SetVendor(const wxString & vendor)
171 {
172 mVendor = vendor;
173 }
174
SetEnabled(bool enable)175 void PluginDescriptor::SetEnabled(bool enable)
176 {
177 mEnabled = enable;
178 }
179
SetValid(bool valid)180 void PluginDescriptor::SetValid(bool valid)
181 {
182 mValid = valid;
183 }
184
185 // Effects
186
GetEffectFamily() const187 wxString PluginDescriptor::GetEffectFamily() const
188 {
189 return mEffectFamily;
190 }
191
GetEffectType() const192 EffectType PluginDescriptor::GetEffectType() const
193 {
194 return mEffectType;
195 }
196
IsEffectInteractive() const197 bool PluginDescriptor::IsEffectInteractive() const
198 {
199 return mEffectInteractive;
200 }
201
IsEffectDefault() const202 bool PluginDescriptor::IsEffectDefault() const
203 {
204 return mEffectDefault;
205 }
206
IsEffectLegacy() const207 bool PluginDescriptor::IsEffectLegacy() const
208 {
209 return mEffectLegacy;
210 }
211
IsEffectRealtime() const212 bool PluginDescriptor::IsEffectRealtime() const
213 {
214 return mEffectRealtime;
215 }
216
IsEffectAutomatable() const217 bool PluginDescriptor::IsEffectAutomatable() const
218 {
219 return mEffectAutomatable;
220 }
221
SetEffectFamily(const wxString & family)222 void PluginDescriptor::SetEffectFamily(const wxString & family)
223 {
224 mEffectFamily = family;
225 }
226
SetEffectType(EffectType type)227 void PluginDescriptor::SetEffectType(EffectType type)
228 {
229 mEffectType = type;
230 }
231
SetEffectInteractive(bool interactive)232 void PluginDescriptor::SetEffectInteractive(bool interactive)
233 {
234 mEffectInteractive = interactive;
235 }
236
SetEffectDefault(bool dflt)237 void PluginDescriptor::SetEffectDefault(bool dflt)
238 {
239 mEffectDefault = dflt;
240 }
241
SetEffectLegacy(bool legacy)242 void PluginDescriptor::SetEffectLegacy(bool legacy)
243 {
244 mEffectLegacy = legacy;
245 }
246
SetEffectRealtime(bool realtime)247 void PluginDescriptor::SetEffectRealtime(bool realtime)
248 {
249 mEffectRealtime = realtime;
250 }
251
SetEffectAutomatable(bool automatable)252 void PluginDescriptor::SetEffectAutomatable(bool automatable)
253 {
254 mEffectAutomatable = automatable;
255 }
256
257 // Importer
258
GetImporterIdentifier() const259 const wxString & PluginDescriptor::GetImporterIdentifier() const
260 {
261 return mImporterIdentifier;
262 }
263
SetImporterIdentifier(const wxString & identifier)264 void PluginDescriptor::SetImporterIdentifier(const wxString & identifier)
265 {
266 mImporterIdentifier = identifier;
267 }
268
GetImporterExtensions() const269 const FileExtensions & PluginDescriptor::GetImporterExtensions()
270 const
271 {
272 return mImporterExtensions;
273 }
274
SetImporterExtensions(FileExtensions extensions)275 void PluginDescriptor::SetImporterExtensions( FileExtensions extensions )
276 {
277 mImporterExtensions = std::move( extensions );
278 }
279
280 ///////////////////////////////////////////////////////////////////////////////
281 //
282 // PluginManager
283 //
284 ///////////////////////////////////////////////////////////////////////////////
285
286 // Registry has the list of plug ins
287 #define REGVERKEY wxString(wxT("/pluginregistryversion"))
288 #define REGVERCUR wxString(wxT("1.1"))
289 #define REGROOT wxString(wxT("/pluginregistry/"))
290
291 // Settings has the values of the plug in settings.
292 #define SETVERKEY wxString(wxT("/pluginsettingsversion"))
293 #define SETVERCUR wxString(wxT("1.0"))
294 #define SETROOT wxString(wxT("/pluginsettings/"))
295
296 #define KEY_ID wxT("ID")
297 #define KEY_PATH wxT("Path")
298 #define KEY_SYMBOL wxT("Symbol")
299 #define KEY_NAME wxT("Name")
300 #define KEY_VENDOR wxT("Vendor")
301 #define KEY_VERSION wxT("Version")
302 #define KEY_DESCRIPTION wxT("Description")
303 #define KEY_LASTUPDATED wxT("LastUpdated")
304 #define KEY_ENABLED wxT("Enabled")
305 #define KEY_VALID wxT("Valid")
306 #define KEY_PROVIDERID wxT("ProviderID")
307 #define KEY_EFFECTTYPE wxT("EffectType")
308 #define KEY_EFFECTFAMILY wxT("EffectFamily")
309 #define KEY_EFFECTDEFAULT wxT("EffectDefault")
310 #define KEY_EFFECTINTERACTIVE wxT("EffectInteractive")
311 #define KEY_EFFECTREALTIME wxT("EffectRealtime")
312 #define KEY_EFFECTAUTOMATABLE wxT("EffectAutomatable")
313 #define KEY_EFFECTTYPE_NONE wxT("None")
314 #define KEY_EFFECTTYPE_ANALYZE wxT("Analyze")
315 #define KEY_EFFECTTYPE_GENERATE wxT("Generate")
316 #define KEY_EFFECTTYPE_PROCESS wxT("Process")
317 #define KEY_EFFECTTYPE_TOOL wxT("Tool")
318 #define KEY_EFFECTTYPE_HIDDEN wxT("Hidden")
319 #define KEY_IMPORTERIDENT wxT("ImporterIdent")
320 //#define KEY_IMPORTERFILTER wxT("ImporterFilter")
321 #define KEY_IMPORTEREXTENSIONS wxT("ImporterExtensions")
322
323 // ============================================================================
324 //
325 // PluginManagerInterface implementation
326 //
327 // ============================================================================
328
DefaultRegistrationCallback(ModuleInterface * provider,ComponentInterface * pInterface)329 const PluginID &PluginManagerInterface::DefaultRegistrationCallback(
330 ModuleInterface *provider, ComponentInterface *pInterface )
331 {
332 EffectDefinitionInterface * pEInterface = dynamic_cast<EffectDefinitionInterface*>(pInterface);
333 if( pEInterface )
334 return PluginManager::Get().RegisterPlugin(provider, pEInterface, PluginTypeEffect);
335 ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
336 if( pCInterface )
337 return PluginManager::Get().RegisterPlugin(provider, pCInterface);
338 static wxString empty;
339 return empty;
340 }
341
AudacityCommandRegistrationCallback(ModuleInterface * provider,ComponentInterface * pInterface)342 const PluginID &PluginManagerInterface::AudacityCommandRegistrationCallback(
343 ModuleInterface *provider, ComponentInterface *pInterface )
344 {
345 ComponentInterface * pCInterface = dynamic_cast<ComponentInterface*>(pInterface);
346 if( pCInterface )
347 return PluginManager::Get().RegisterPlugin(provider, pCInterface);
348 static wxString empty;
349 return empty;
350 }
351
GetPluginEnabledSetting(const PluginID & ID) const352 RegistryPath PluginManager::GetPluginEnabledSetting( const PluginID &ID ) const
353 {
354 auto pPlugin = GetPlugin( ID );
355 if ( pPlugin )
356 return GetPluginEnabledSetting( *pPlugin );
357 return {};
358 }
359
GetPluginEnabledSetting(const PluginDescriptor & desc) const360 RegistryPath PluginManager::GetPluginEnabledSetting(
361 const PluginDescriptor &desc ) const
362 {
363 switch ( desc.GetPluginType() ) {
364 case PluginTypeModule: {
365 // Retrieve optional family symbol that was recorded in
366 // RegisterPlugin() for the module
367 auto family = desc.GetEffectFamily();
368 if ( family.empty() ) // as for built-in effect and command modules
369 return {};
370 else
371 return wxT('/') + family + wxT("/Enable");
372 }
373 case PluginTypeEffect:
374 // do NOT use GetEffectFamily() for this descriptor, but instead,
375 // delegate to the plugin descriptor of the provider, which may
376 // be different (may be empty)
377 return GetPluginEnabledSetting( desc.GetProviderID() );
378 default:
379 return {};
380 }
381 }
382
IsPluginRegistered(const PluginPath & path,const TranslatableString * pName)383 bool PluginManager::IsPluginRegistered(
384 const PluginPath &path, const TranslatableString *pName)
385 {
386 for (auto &pair : mPlugins) {
387 if (auto &descriptor = pair.second; descriptor.GetPath() == path) {
388 if (pName)
389 descriptor.SetSymbol(
390 { descriptor.GetSymbol().Internal(), *pName });
391 return true;
392 }
393 }
394 return false;
395 }
396
RegisterPlugin(ModuleInterface * module)397 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *module)
398 {
399 PluginDescriptor & plug = CreatePlugin(GetID(module), module, PluginTypeModule);
400 plug.SetEffectFamily(module->GetOptionalFamilySymbol().Internal());
401
402 plug.SetEnabled(true);
403 plug.SetValid(true);
404
405 return plug.GetID();
406 }
407
RegisterPlugin(ModuleInterface * provider,ComponentInterface * command)408 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, ComponentInterface *command)
409 {
410 PluginDescriptor & plug = CreatePlugin(GetID(command), command, (PluginType)PluginTypeAudacityCommand);
411
412 plug.SetProviderID(PluginManager::GetID(provider));
413
414 plug.SetEnabled(true);
415 plug.SetValid(true);
416
417 return plug.GetID();
418 }
419
RegisterPlugin(ModuleInterface * provider,EffectDefinitionInterface * effect,int type)420 const PluginID & PluginManager::RegisterPlugin(ModuleInterface *provider, EffectDefinitionInterface *effect, int type)
421 {
422 PluginDescriptor & plug = CreatePlugin(GetID(effect), effect, (PluginType)type);
423
424 plug.SetProviderID(PluginManager::GetID(provider));
425
426 plug.SetEffectType(effect->GetClassification());
427 plug.SetEffectFamily(effect->GetFamily().Internal());
428 plug.SetEffectInteractive(effect->IsInteractive());
429 plug.SetEffectDefault(effect->IsDefault());
430 plug.SetEffectRealtime(effect->SupportsRealtime());
431 plug.SetEffectAutomatable(effect->SupportsAutomation());
432
433 plug.SetEnabled(true);
434 plug.SetValid(true);
435
436 return plug.GetID();
437 }
438
FindFilesInPathList(const wxString & pattern,const FilePaths & pathList,FilePaths & files,bool directories)439 void PluginManager::FindFilesInPathList(const wxString & pattern,
440 const FilePaths & pathList,
441 FilePaths & files,
442 bool directories)
443 {
444
445 wxLogNull nolog;
446
447 // Why bother...
448 if (pattern.empty())
449 {
450 return;
451 }
452
453 // TODO: We REALLY need to figure out the "Audacity" plug-in path(s)
454
455 FilePaths paths;
456
457 // Add the "per-user" plug-ins directory
458 {
459 const wxFileName &ff = FileNames::PlugInDir();
460 paths.push_back(ff.GetFullPath());
461 }
462
463 // Add the "Audacity" plug-ins directory
464 wxFileName ff = PlatformCompatibility::GetExecutablePath();
465 #if defined(__WXMAC__)
466 // Path ends for example in "Audacity.app/Contents/MacOSX"
467 //ff.RemoveLastDir();
468 //ff.RemoveLastDir();
469 // just remove the MacOSX part.
470 ff.RemoveLastDir();
471 #endif
472 ff.AppendDir(wxT("plug-ins"));
473 paths.push_back(ff.GetPath());
474
475 // Weed out duplicates
476 for (const auto &filePath : pathList)
477 {
478 ff = filePath;
479 const wxString path{ ff.GetFullPath() };
480 if (paths.Index(path, wxFileName::IsCaseSensitive()) == wxNOT_FOUND)
481 {
482 paths.push_back(path);
483 }
484 }
485
486 // Find all matching files in each path
487 for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
488 {
489 ff = paths[i] + wxFILE_SEP_PATH + pattern;
490 wxDir::GetAllFiles(ff.GetPath(), &files, ff.GetFullName(), directories ? wxDIR_DEFAULT : wxDIR_FILES);
491 }
492
493 return;
494 }
495
HasSharedConfigGroup(const PluginID & ID,const RegistryPath & group)496 bool PluginManager::HasSharedConfigGroup(const PluginID & ID, const RegistryPath & group)
497 {
498 return HasGroup(SharedGroup(ID, group));
499 }
500
GetSharedConfigSubgroups(const PluginID & ID,const RegistryPath & group,RegistryPaths & subgroups)501 bool PluginManager::GetSharedConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups)
502 {
503 return GetSubgroups(SharedGroup(ID, group), subgroups);
504 }
505
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,wxString & value,const wxString & defval)506 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval)
507 {
508 return GetConfig(SharedKey(ID, group, key), value, defval);
509 }
510
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,int & value,int defval)511 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval)
512 {
513 return GetConfig(SharedKey(ID, group, key), value, defval);
514 }
515
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,bool & value,bool defval)516 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval)
517 {
518 return GetConfig(SharedKey(ID, group, key), value, defval);
519 }
520
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,float & value,float defval)521 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval)
522 {
523 return GetConfig(SharedKey(ID, group, key), value, defval);
524 }
525
GetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,double & value,double defval)526 bool PluginManager::GetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval)
527 {
528 return GetConfig(SharedKey(ID, group, key), value, defval);
529 }
530
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const wxString & value)531 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value)
532 {
533 return SetConfig(SharedKey(ID, group, key), value);
534 }
535
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const int & value)536 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value)
537 {
538 return SetConfig(SharedKey(ID, group, key), value);
539 }
540
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const bool & value)541 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value)
542 {
543 return SetConfig(SharedKey(ID, group, key), value);
544 }
545
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const float & value)546 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value)
547 {
548 return SetConfig(SharedKey(ID, group, key), value);
549 }
550
SetSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const double & value)551 bool PluginManager::SetSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value)
552 {
553 return SetConfig(SharedKey(ID, group, key), value);
554 }
555
RemoveSharedConfigSubgroup(const PluginID & ID,const RegistryPath & group)556 bool PluginManager::RemoveSharedConfigSubgroup(const PluginID & ID, const RegistryPath & group)
557 {
558 bool result = GetSettings()->DeleteGroup(SharedGroup(ID, group));
559 if (result)
560 {
561 GetSettings()->Flush();
562 }
563
564 return result;
565 }
566
RemoveSharedConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)567 bool PluginManager::RemoveSharedConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
568 {
569 bool result = GetSettings()->DeleteEntry(SharedKey(ID, group, key));
570 if (result)
571 {
572 GetSettings()->Flush();
573 }
574
575 return result;
576 }
577
HasPrivateConfigGroup(const PluginID & ID,const RegistryPath & group)578 bool PluginManager::HasPrivateConfigGroup(const PluginID & ID, const RegistryPath & group)
579 {
580 return HasGroup(PrivateGroup(ID, group));
581 }
582
GetPrivateConfigSubgroups(const PluginID & ID,const RegistryPath & group,RegistryPaths & subgroups)583 bool PluginManager::GetPrivateConfigSubgroups(const PluginID & ID, const RegistryPath & group, RegistryPaths & subgroups)
584 {
585 return GetSubgroups(PrivateGroup(ID, group), subgroups);
586 }
587
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,wxString & value,const wxString & defval)588 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, wxString & value, const wxString & defval)
589 {
590 return GetConfig(PrivateKey(ID, group, key), value, defval);
591 }
592
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,int & value,int defval)593 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, int & value, int defval)
594 {
595 return GetConfig(PrivateKey(ID, group, key), value, defval);
596 }
597
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,bool & value,bool defval)598 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, bool & value, bool defval)
599 {
600 return GetConfig(PrivateKey(ID, group, key), value, defval);
601 }
602
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,float & value,float defval)603 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, float & value, float defval)
604 {
605 return GetConfig(PrivateKey(ID, group, key), value, defval);
606 }
607
GetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,double & value,double defval)608 bool PluginManager::GetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, double & value, double defval)
609 {
610 return GetConfig(PrivateKey(ID, group, key), value, defval);
611 }
612
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const wxString & value)613 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const wxString & value)
614 {
615 return SetConfig(PrivateKey(ID, group, key), value);
616 }
617
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const int & value)618 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const int & value)
619 {
620 return SetConfig(PrivateKey(ID, group, key), value);
621 }
622
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const bool & value)623 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const bool & value)
624 {
625 return SetConfig(PrivateKey(ID, group, key), value);
626 }
627
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const float & value)628 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const float & value)
629 {
630 return SetConfig(PrivateKey(ID, group, key), value);
631 }
632
SetPrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key,const double & value)633 bool PluginManager::SetPrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key, const double & value)
634 {
635 return SetConfig(PrivateKey(ID, group, key), value);
636 }
637
RemovePrivateConfigSubgroup(const PluginID & ID,const RegistryPath & group)638 bool PluginManager::RemovePrivateConfigSubgroup(const PluginID & ID, const RegistryPath & group)
639 {
640 bool result = GetSettings()->DeleteGroup(PrivateGroup(ID, group));
641 if (result)
642 {
643 GetSettings()->Flush();
644 }
645
646 return result;
647 }
648
RemovePrivateConfig(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)649 bool PluginManager::RemovePrivateConfig(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
650 {
651 bool result = GetSettings()->DeleteEntry(PrivateKey(ID, group, key));
652 if (result)
653 {
654 GetSettings()->Flush();
655 }
656
657 return result;
658 }
659
660 // ============================================================================
661 //
662 // PluginManager
663 //
664 // ============================================================================
665
666 // The one and only PluginManager
667 std::unique_ptr<PluginManager> PluginManager::mInstance{};
668
669 // ----------------------------------------------------------------------------
670 // Creation/Destruction
671 // ----------------------------------------------------------------------------
672
PluginManager()673 PluginManager::PluginManager()
674 {
675 mSettings = NULL;
676 }
677
~PluginManager()678 PluginManager::~PluginManager()
679 {
680 // Ensure termination (harmless if already done)
681 Terminate();
682 }
683
684 // ----------------------------------------------------------------------------
685 // PluginManager implementation
686 // ----------------------------------------------------------------------------
687
688 // ============================================================================
689 //
690 // Return reference to singleton
691 //
692 // (Thread-safe...no active threading during construction or after destruction)
693 // ============================================================================
694
Get()695 PluginManager & PluginManager::Get()
696 {
697 if (!mInstance)
698 {
699 mInstance.reset(safenew PluginManager);
700 }
701
702 return *mInstance;
703 }
704
Initialize()705 void PluginManager::Initialize()
706 {
707 // Always load the registry first
708 Load();
709
710 // And force load of setting to verify it's accessible
711 GetSettings();
712
713 auto &mm = ModuleManager::Get();
714 mm.DiscoverProviders();
715 for (const auto &[id, module] : mm.Providers()) {
716 RegisterPlugin(module.get());
717 // Allow the module to auto-register children
718 module->AutoRegisterPlugins(*this);
719 }
720
721 // And finally check for updates
722 #ifndef EXPERIMENTAL_EFFECT_MANAGEMENT
723 CheckForUpdates();
724 #else
725 const bool kFast = true;
726 CheckForUpdates( kFast );
727 #endif
728 }
729
Terminate()730 void PluginManager::Terminate()
731 {
732 // Get rid of all non-module plugins first
733 PluginMap::iterator iter = mPlugins.begin();
734 while (iter != mPlugins.end())
735 {
736 PluginDescriptor & plug = iter->second;
737 if (plug.GetPluginType() == PluginTypeEffect)
738 {
739 mPlugins.erase(iter++);
740 continue;
741 }
742
743 ++iter;
744 }
745
746 // Now get rid of the modules
747 iter = mPlugins.begin();
748 while (iter != mPlugins.end())
749 {
750 mPlugins.erase(iter++);
751 }
752 }
753
DropFile(const wxString & fileName)754 bool PluginManager::DropFile(const wxString &fileName)
755 {
756 auto &mm = ModuleManager::Get();
757 const wxFileName src{ fileName };
758
759 for (auto &plug : PluginsOfType(PluginTypeModule)) {
760 auto module = static_cast<ModuleInterface *>
761 (mm.CreateProviderInstance(plug.GetID(), plug.GetPath()));
762 if (! module)
763 continue;
764
765 const auto &ff = module->InstallPath();
766 const auto &extensions = module->GetFileExtensions();
767 if ( !ff.empty() &&
768 extensions.Index(src.GetExt(), false) != wxNOT_FOUND ) {
769 TranslatableString errMsg;
770 // Do dry-run test of the file format
771 unsigned nPlugIns =
772 module->DiscoverPluginsAtPath(fileName, errMsg, {});
773 if (nPlugIns) {
774 // File contents are good for this module, so check no others.
775 // All branches of this block return true, even in case of
776 // failure for other reasons, to signal that other drag-and-drop
777 // actions should not be tried.
778
779 // Find path to copy it
780 wxFileName dst;
781 dst.AssignDir( ff );
782 dst.SetFullName( src.GetFullName() );
783 if ( dst.Exists() ) {
784 // Query whether to overwrite
785 bool overwrite = (wxYES == ::AudacityMessageBox(
786 XO("Overwrite the plug-in file %s?")
787 .Format( dst.GetFullPath() ),
788 XO("Plug-in already exists"),
789 wxYES_NO ) );
790 if ( !overwrite )
791 return true;
792 }
793
794 // Move the file or subtree
795 bool copied = false;
796 auto dstPath = dst.GetFullPath();
797 if ( src.FileExists() )
798 // A simple one-file plug-in
799 copied = FileNames::DoCopyFile(
800 src.GetFullPath(), dstPath, true );
801 else {
802 // A sub-folder
803 // such as for some VST packages
804 // Recursive copy needed -- to do
805 return true;
806 }
807
808 if (!copied) {
809 ::AudacityMessageBox(
810 XO("Plug-in file is in use. Failed to overwrite") );
811 return true;
812 }
813
814 // Register for real
815 std::vector<PluginID> ids;
816 std::vector<wxString> names;
817 nPlugIns = module->DiscoverPluginsAtPath(dstPath, errMsg,
818 [&](ModuleInterface *provider, ComponentInterface *ident)
819 -> const PluginID& {
820 // Register as by default, but also collecting the PluginIDs
821 // and names
822 auto &id = PluginManagerInterface::DefaultRegistrationCallback(
823 provider, ident);
824 ids.push_back(id);
825 names.push_back( ident->GetSymbol().Translation() );
826 return id;
827 });
828 if ( ! nPlugIns ) {
829 // Unlikely after the dry run succeeded
830 ::AudacityMessageBox(
831 XO("Failed to register:\n%s").Format( errMsg ) );
832 return true;
833 }
834
835 // Ask whether to enable the plug-ins
836 if (auto nIds = ids.size()) {
837 auto message = XPC(
838 /* i18n-hint A plug-in is an optional added program for a sound
839 effect, or generator, or analyzer */
840 "Enable this plug-in?\n",
841 "Enable these plug-ins?\n",
842 0,
843 "plug-ins"
844 )( nIds );
845 for (const auto &name : names)
846 message.Join( Verbatim( name ), wxT("\n") );
847 bool enable = (wxYES == ::AudacityMessageBox(
848 message,
849 XO("Enable new plug-ins"),
850 wxYES_NO ) );
851 for (const auto &id : ids)
852 mPlugins[id].SetEnabled(enable);
853 // Make changes to enabled status persist:
854 this->Save();
855 }
856
857 return true;
858 }
859 }
860 }
861
862 return false;
863 }
864
Load()865 void PluginManager::Load()
866 {
867 // Create/Open the registry
868 auto pRegistry = AudacityFileConfig::Create(
869 {}, {}, FileNames::PluginRegistry());
870 auto ®istry = *pRegistry;
871
872 // If this group doesn't exist then we have something that's not a registry.
873 // We should probably warn the user, but it's pretty unlikely that this will happen.
874 if (!registry.HasGroup(REGROOT))
875 {
876 // Must start over
877 // This DeleteAll affects pluginregistry.cfg only, not audacity.cfg
878 // That is, the memory of on/off states of effect (and generator,
879 // analyzer, and tool) plug-ins
880 registry.DeleteAll();
881 registry.Flush();
882 return;
883 }
884
885 // Check for a registry version that we can understand
886 // TODO: Should also check for a registry file that is newer than
887 // what we can understand.
888 wxString regver = registry.Read(REGVERKEY);
889 if (regver < REGVERCUR )
890 {
891 // Conversion code here, for when registry version changes.
892
893 // We iterate through the effects, possibly updating their info.
894 wxString groupName;
895 long groupIndex;
896 wxString group = GetPluginTypeString(PluginTypeEffect);
897 wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
898 wxArrayString groupsToDelete;
899
900 registry.SetPath(cfgPath);
901 for (bool cont = registry.GetFirstGroup(groupName, groupIndex);
902 cont;
903 registry.SetPath(cfgPath),
904 cont = registry.GetNextGroup(groupName, groupIndex))
905 {
906 registry.SetPath(groupName);
907 wxString effectSymbol = registry.Read(KEY_SYMBOL, "");
908 wxString effectVersion = registry.Read(KEY_VERSION, "");
909
910
911 // For 2.3.0 the plugins we distribute have moved around.
912 // So we upped the registry version number to 1.1.
913 // These particular config edits were originally written to fix Bug 1914.
914 if (regver <= "1.0") {
915 // Nyquist prompt is a built-in that has moved to the tools menu.
916 if (effectSymbol == NYQUIST_PROMPT_ID) {
917 registry.Write(KEY_EFFECTTYPE, "Tool");
918 // Old version of SDE was in Analyze menu. Now it is in Tools.
919 // We don't want both the old and the new.
920 } else if ((effectSymbol == "Sample Data Export") && (effectVersion == "n/a")) {
921 groupsToDelete.push_back(cfgPath + groupName);
922 // Old version of SDI was in Generate menu. Now it is in Tools.
923 } else if ((effectSymbol == "Sample Data Import") && (effectVersion == "n/a")) {
924 groupsToDelete.push_back(cfgPath + groupName);
925 }
926 }
927
928 }
929 // Doing the deletion within the search loop risked skipping some items,
930 // hence the delayed delete.
931 for (unsigned int i = 0; i < groupsToDelete.size(); i++) {
932 registry.DeleteGroup(groupsToDelete[i]);
933 }
934 registry.SetPath("");
935 registry.Write(REGVERKEY, REGVERCUR);
936 // Updates done. Make sure we read the updated data later.
937 registry.Flush();
938 }
939
940 // Load all provider plugins first
941 LoadGroup(®istry, PluginTypeModule);
942
943 // Now the rest
944 LoadGroup(®istry, PluginTypeEffect);
945 LoadGroup(®istry, PluginTypeAudacityCommand );
946 LoadGroup(®istry, PluginTypeExporter);
947 LoadGroup(®istry, PluginTypeImporter);
948
949 LoadGroup(®istry, PluginTypeStub);
950 return;
951 }
952
LoadGroup(FileConfig * pRegistry,PluginType type)953 void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type)
954 {
955 #ifdef __WXMAC__
956 // Bug 1590: On Mac, we should purge the registry of Nyquist plug-ins
957 // bundled with other versions of Audacity, assuming both versions
958 // were properly installed in /Applications (or whatever it is called in
959 // your locale)
960
961 const auto fullExePath = PlatformCompatibility::GetExecutablePath();
962
963 // Strip rightmost path components up to *.app
964 wxFileName exeFn{ fullExePath };
965 exeFn.SetEmptyExt();
966 exeFn.SetName(wxString{});
967 while(exeFn.GetDirCount() && !exeFn.GetDirs().back().EndsWith(".app"))
968 exeFn.RemoveLastDir();
969
970 const auto goodPath = exeFn.GetPath();
971
972 if(exeFn.GetDirCount())
973 exeFn.RemoveLastDir();
974 const auto possiblyBadPath = exeFn.GetPath();
975
976 auto AcceptPath = [&](const wxString &path) {
977 if (!path.StartsWith(possiblyBadPath))
978 // Assume it's not under /Applications
979 return true;
980 if (path.StartsWith(goodPath))
981 // It's bundled with this executable
982 return true;
983 return false;
984 };
985 #else
986 auto AcceptPath = [](const wxString&){ return true; };
987 #endif
988
989 wxString strVal;
990 bool boolVal;
991 wxString groupName;
992 long groupIndex;
993 wxString group = GetPluginTypeString(type);
994 wxString cfgPath = REGROOT + group + wxCONFIG_PATH_SEPARATOR;
995
996 pRegistry->SetPath(cfgPath);
997 for (bool cont = pRegistry->GetFirstGroup(groupName, groupIndex);
998 cont;
999 pRegistry->SetPath(cfgPath),
1000 cont = pRegistry->GetNextGroup(groupName, groupIndex))
1001 {
1002 PluginDescriptor plug;
1003
1004 pRegistry->SetPath(groupName);
1005
1006 groupName = ConvertID(groupName);
1007
1008 // Bypass group if the ID is already in use
1009 if (mPlugins.count(groupName))
1010 continue;
1011
1012 // Set the ID and type
1013 plug.SetID(groupName);
1014 plug.SetPluginType(type);
1015
1016 // Get the provider ID and bypass group if not found
1017 if (!pRegistry->Read(KEY_PROVIDERID, &strVal, wxEmptyString))
1018 {
1019 // Bypass group if the provider isn't valid
1020 if (!strVal.empty() && !mPlugins.count(strVal))
1021 continue;
1022 }
1023 plug.SetProviderID(PluginID(strVal));
1024
1025 // Get the path (optional)
1026 pRegistry->Read(KEY_PATH, &strVal, wxEmptyString);
1027 if (!AcceptPath(strVal))
1028 // Ignore the obsolete path in the config file, during session,
1029 // but don't remove it from the file. Maybe you really want to
1030 // switch back to the other version of Audacity and lose nothing.
1031 continue;
1032 plug.SetPath(strVal);
1033
1034 /*
1035 // PRL: Ignore names written in configs before 2.3.0!
1036 // use Internal string only! Let the present version of Audacity map
1037 // that to a user-visible string.
1038 // Get the name and bypass group if not found
1039 if (!pRegistry->Read(KEY_NAME, &strVal))
1040 {
1041 continue;
1042 }
1043 plug.SetName(strVal);
1044 */
1045
1046 // Get the symbol...Audacity 2.3.0 or later requires it
1047 // bypass group if not found
1048 // Note, KEY_SYMBOL started getting written to config files in 2.1.0.
1049 // KEY_NAME (now ignored) was written before that, but only for VST
1050 // effects.
1051 if (!pRegistry->Read(KEY_SYMBOL, &strVal))
1052 continue;
1053
1054 // Related to Bug2778: config file only remembered an internal name,
1055 // so this symbol may not contain the correct TranslatableString.
1056 // See calls to IsPluginRegistered which can correct that.
1057 plug.SetSymbol(strVal);
1058
1059 // Get the version and bypass group if not found
1060 if (!pRegistry->Read(KEY_VERSION, &strVal))
1061 {
1062 continue;
1063 }
1064 plug.SetVersion(strVal);
1065
1066 // Get the vendor and bypass group if not found
1067 if (!pRegistry->Read(KEY_VENDOR, &strVal))
1068 {
1069 continue;
1070 }
1071 plug.SetVendor( strVal );
1072
1073 #if 0
1074 // This was done before version 2.2.2, but the value was not really used
1075 // But absence of a value will cause early versions to skip the group
1076 // Therefore we still write a blank to keep pluginregistry.cfg
1077 // backwards-compatible
1078
1079 // Get the description and bypass group if not found
1080 if (!pRegistry->Read(KEY_DESCRIPTION, &strVal))
1081 {
1082 continue;
1083 }
1084 #endif
1085
1086 // Is it enabled...default to no if not found
1087 pRegistry->Read(KEY_ENABLED, &boolVal, false);
1088 plug.SetEnabled(boolVal);
1089
1090 // Is it valid...default to no if not found
1091 pRegistry->Read(KEY_VALID, &boolVal, false);
1092 plug.SetValid(boolVal);
1093
1094 switch (type)
1095 {
1096 case PluginTypeModule:
1097 {
1098 // Nothing to do here yet
1099 }
1100 break;
1101
1102 case PluginTypeEffect:
1103 {
1104 // Get the effect type and bypass group if not found
1105 if (!pRegistry->Read(KEY_EFFECTTYPE, &strVal))
1106 continue;
1107
1108 if (strVal == KEY_EFFECTTYPE_NONE)
1109 plug.SetEffectType(EffectTypeNone);
1110 else if (strVal == KEY_EFFECTTYPE_ANALYZE)
1111 plug.SetEffectType(EffectTypeAnalyze);
1112 else if (strVal == KEY_EFFECTTYPE_GENERATE)
1113 plug.SetEffectType(EffectTypeGenerate);
1114 else if (strVal == KEY_EFFECTTYPE_PROCESS)
1115 plug.SetEffectType(EffectTypeProcess);
1116 else if (strVal == KEY_EFFECTTYPE_TOOL)
1117 plug.SetEffectType(EffectTypeTool);
1118 else if (strVal == KEY_EFFECTTYPE_HIDDEN)
1119 plug.SetEffectType(EffectTypeHidden);
1120 else
1121 continue;
1122
1123 // Get the effect family and bypass group if not found
1124 if (!pRegistry->Read(KEY_EFFECTFAMILY, &strVal))
1125 {
1126 continue;
1127 }
1128 plug.SetEffectFamily(strVal);
1129
1130 // Is it a default (above the line) effect and bypass group if not found
1131 if (!pRegistry->Read(KEY_EFFECTDEFAULT, &boolVal))
1132 {
1133 continue;
1134 }
1135 plug.SetEffectDefault(boolVal);
1136
1137 // Is it an interactive effect and bypass group if not found
1138 if (!pRegistry->Read(KEY_EFFECTINTERACTIVE, &boolVal))
1139 {
1140 continue;
1141 }
1142 plug.SetEffectInteractive(boolVal);
1143
1144 // Is it a realtime capable effect and bypass group if not found
1145 if (!pRegistry->Read(KEY_EFFECTREALTIME, &boolVal))
1146 {
1147 continue;
1148 }
1149 plug.SetEffectRealtime(boolVal);
1150
1151 // Does the effect support automation...bypass group if not found
1152 if (!pRegistry->Read(KEY_EFFECTAUTOMATABLE, &boolVal))
1153 {
1154 continue;
1155 }
1156 plug.SetEffectAutomatable(boolVal);
1157 }
1158 break;
1159
1160 case PluginTypeImporter:
1161 {
1162 // Get the importer identifier and bypass group if not found
1163 if (!pRegistry->Read(KEY_IMPORTERIDENT, &strVal))
1164 {
1165 continue;
1166 }
1167 plug.SetImporterIdentifier(strVal);
1168
1169 // Get the importer extensions and bypass group if not found
1170 if (!pRegistry->Read(KEY_IMPORTEREXTENSIONS, &strVal))
1171 {
1172 continue;
1173 }
1174 FileExtensions extensions;
1175 wxStringTokenizer tkr(strVal, wxT(":"));
1176 while (tkr.HasMoreTokens())
1177 {
1178 extensions.push_back(tkr.GetNextToken());
1179 }
1180 plug.SetImporterExtensions(extensions);
1181 }
1182 break;
1183
1184 case PluginTypeStub:
1185 {
1186 // Nothing additional for stubs
1187 }
1188 break;
1189
1190 // Not used by 2.1.1 or greater and should be removed after a few releases past 2.1.0.
1191 case PluginTypeNone:
1192 {
1193 // Used for stub groups
1194 }
1195 break;
1196
1197 default:
1198 {
1199 continue;
1200 }
1201 }
1202
1203 // Everything checked out...accept the plugin
1204 mPlugins[groupName] = std::move(plug);
1205 }
1206
1207 return;
1208 }
1209
Save()1210 void PluginManager::Save()
1211 {
1212 // Create/Open the registry
1213 auto pRegistry = AudacityFileConfig::Create(
1214 {}, {}, FileNames::PluginRegistry());
1215 auto ®istry = *pRegistry;
1216
1217 // Clear pluginregistry.cfg (not audacity.cfg)
1218 registry.DeleteAll();
1219
1220 // Write the version string
1221 registry.Write(REGVERKEY, REGVERCUR);
1222
1223 // Save the individual groups
1224 SaveGroup(®istry, PluginTypeEffect);
1225 SaveGroup(®istry, PluginTypeExporter);
1226 SaveGroup(®istry, PluginTypeAudacityCommand);
1227 SaveGroup(®istry, PluginTypeImporter);
1228 SaveGroup(®istry, PluginTypeStub);
1229
1230 // Not used by 2.1.1 or greater, but must save to allow users to switch between 2.1.0
1231 // and 2.1.1+. This should be removed after a few releases past 2.1.0.
1232 //SaveGroup(®istry, PluginTypeNone);
1233
1234 // And now the providers
1235 SaveGroup(®istry, PluginTypeModule);
1236
1237 // Just to be safe
1238 registry.Flush();
1239 }
1240
SaveGroup(FileConfig * pRegistry,PluginType type)1241 void PluginManager::SaveGroup(FileConfig *pRegistry, PluginType type)
1242 {
1243 wxString group = GetPluginTypeString(type);
1244 for (auto &pair : mPlugins) {
1245 auto & plug = pair.second;
1246
1247 if (plug.GetPluginType() != type)
1248 {
1249 continue;
1250 }
1251
1252 pRegistry->SetPath(REGROOT + group + wxCONFIG_PATH_SEPARATOR + ConvertID(plug.GetID()));
1253
1254 pRegistry->Write(KEY_PATH, plug.GetPath());
1255
1256 // See comments with the corresponding load-time call to SetSymbol().
1257 pRegistry->Write(KEY_SYMBOL, plug.GetSymbol().Internal());
1258
1259 // PRL: Writing KEY_NAME which is no longer read, but older Audacity
1260 // versions expect to find it.
1261 pRegistry->Write(KEY_NAME, plug.GetSymbol().Msgid().MSGID());
1262
1263 pRegistry->Write(KEY_VERSION, plug.GetUntranslatedVersion());
1264 pRegistry->Write(KEY_VENDOR, plug.GetVendor());
1265 // Write a blank -- see comments in LoadGroup:
1266 pRegistry->Write(KEY_DESCRIPTION, wxString{});
1267 pRegistry->Write(KEY_PROVIDERID, plug.GetProviderID());
1268 pRegistry->Write(KEY_ENABLED, plug.IsEnabled());
1269 pRegistry->Write(KEY_VALID, plug.IsValid());
1270
1271 switch (type)
1272 {
1273 case PluginTypeModule:
1274 break;
1275
1276 case PluginTypeEffect:
1277 {
1278 EffectType etype = plug.GetEffectType();
1279 wxString stype;
1280 if (etype == EffectTypeNone)
1281 stype = KEY_EFFECTTYPE_NONE;
1282 else if (etype == EffectTypeAnalyze)
1283 stype = KEY_EFFECTTYPE_ANALYZE;
1284 else if (etype == EffectTypeGenerate)
1285 stype = KEY_EFFECTTYPE_GENERATE;
1286 else if (etype == EffectTypeProcess)
1287 stype = KEY_EFFECTTYPE_PROCESS;
1288 else if (etype == EffectTypeTool)
1289 stype = KEY_EFFECTTYPE_TOOL;
1290 else if (etype == EffectTypeHidden)
1291 stype = KEY_EFFECTTYPE_HIDDEN;
1292
1293 pRegistry->Write(KEY_EFFECTTYPE, stype);
1294 pRegistry->Write(KEY_EFFECTFAMILY, plug.GetEffectFamily());
1295 pRegistry->Write(KEY_EFFECTDEFAULT, plug.IsEffectDefault());
1296 pRegistry->Write(KEY_EFFECTINTERACTIVE, plug.IsEffectInteractive());
1297 pRegistry->Write(KEY_EFFECTREALTIME, plug.IsEffectRealtime());
1298 pRegistry->Write(KEY_EFFECTAUTOMATABLE, plug.IsEffectAutomatable());
1299 }
1300 break;
1301
1302 case PluginTypeImporter:
1303 {
1304 pRegistry->Write(KEY_IMPORTERIDENT, plug.GetImporterIdentifier());
1305 const auto & extensions = plug.GetImporterExtensions();
1306 wxString strExt;
1307 for (size_t i = 0, cnt = extensions.size(); i < cnt; i++)
1308 {
1309 strExt += extensions[i] + wxT(":");
1310 }
1311 strExt.RemoveLast(1);
1312 pRegistry->Write(KEY_IMPORTEREXTENSIONS, strExt);
1313 }
1314 break;
1315
1316 default:
1317 break;
1318 }
1319 }
1320
1321 return;
1322 }
1323
1324 // If bFast is true, do not do a full check. Just check the ones
1325 // that are quick to check. Currently (Feb 2017) just Nyquist
1326 // and built-ins.
CheckForUpdates(bool bFast)1327 void PluginManager::CheckForUpdates(bool bFast)
1328 {
1329 ModuleManager & mm = ModuleManager::Get();
1330 wxArrayString pathIndex;
1331 for (auto &pair : mPlugins) {
1332 auto &plug = pair.second;
1333
1334 // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
1335 if (plug.GetPluginType() != PluginTypeNone)
1336 pathIndex.push_back(plug.GetPath().BeforeFirst(wxT(';')));
1337 }
1338
1339 // Check all known plugins to ensure they are still valid and scan for NEW ones.
1340 //
1341 // All NEW plugins get a stub entry created that will remain in place until the
1342 // user enables or disables the plugin.
1343 //
1344 // Because we use the plugins "path" as returned by the providers, we can actually
1345 // have multiple providers report the same path since, at this point, they only
1346 // know that the path might possibly be one supported by the provider.
1347 //
1348 // When the user enables the plugin, each provider that reported it will be asked
1349 // to register the plugin.
1350 for (auto &pair : mPlugins) {
1351 auto &plug = pair.second;
1352 const PluginID & plugID = plug.GetID();
1353 const wxString & plugPath = plug.GetPath();
1354 PluginType plugType = plug.GetPluginType();
1355
1356 // Bypass 2.1.0 placeholders...remove this after a few releases past 2.1.0
1357 if (plugType == PluginTypeNone)
1358 {
1359 continue;
1360 }
1361
1362 if ( plugType == PluginTypeModule )
1363 {
1364 if( bFast )
1365 {
1366 // Skip modules, when doing a fast refresh/check.
1367 }
1368 else if (!mm.IsProviderValid(plugID, plugPath))
1369 {
1370 plug.SetEnabled(false);
1371 plug.SetValid(false);
1372 }
1373 else
1374 {
1375 // Collect plugin paths
1376 PluginPaths paths;
1377 if (auto provider = mm.CreateProviderInstance( plugID, plugPath ) )
1378 paths = provider->FindPluginPaths( *this );
1379 for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
1380 {
1381 wxString path = paths[i].BeforeFirst(wxT(';'));;
1382 if ( ! make_iterator_range( pathIndex ).contains( path ) )
1383 {
1384 PluginID ID = plugID + wxT("_") + path;
1385 PluginDescriptor & plug2 = mPlugins[ID]; // This will create a NEW descriptor
1386 plug2.SetPluginType(PluginTypeStub);
1387 plug2.SetID(ID);
1388 plug2.SetProviderID(plugID);
1389 plug2.SetPath(path);
1390 plug2.SetEnabled(false);
1391 plug2.SetValid(false);
1392 }
1393 }
1394 }
1395 }
1396 else if (plugType != PluginTypeNone && plugType != PluginTypeStub)
1397 {
1398 plug.SetValid(mm.IsPluginValid(plug.GetProviderID(), plugPath, bFast));
1399 if (!plug.IsValid())
1400 {
1401 plug.SetEnabled(false);
1402 }
1403 }
1404 }
1405
1406 Save();
1407
1408 return;
1409 }
1410
1411 // Here solely for the purpose of Nyquist Workbench until
1412 // a better solution is devised.
RegisterPlugin(std::unique_ptr<EffectDefinitionInterface> effect,PluginType type)1413 const PluginID & PluginManager::RegisterPlugin(
1414 std::unique_ptr<EffectDefinitionInterface> effect, PluginType type)
1415 {
1416 PluginDescriptor & plug =
1417 CreatePlugin(GetID(effect.get()), effect.get(), type);
1418
1419 plug.SetEffectType(effect->GetType());
1420 plug.SetEffectFamily(effect->GetFamily().Internal());
1421 plug.SetEffectInteractive(effect->IsInteractive());
1422 plug.SetEffectDefault(effect->IsDefault());
1423 plug.SetEffectRealtime(effect->SupportsRealtime());
1424 plug.SetEffectAutomatable(effect->SupportsAutomation());
1425
1426 plug.SetInstance(std::move(effect));
1427 plug.SetEffectLegacy(true);
1428 plug.SetEnabled(true);
1429 plug.SetValid(true);
1430
1431 return plug.GetID();
1432 }
1433
UnregisterPlugin(const PluginID & ID)1434 void PluginManager::UnregisterPlugin(const PluginID & ID)
1435 {
1436 mPlugins.erase(ID);
1437 }
1438
GetPluginCount(PluginType type)1439 int PluginManager::GetPluginCount(PluginType type)
1440 {
1441 return count_if(mPlugins.begin(), mPlugins.end(), [type](auto &pair){
1442 return pair.second.GetPluginType() == type; });
1443 }
1444
GetPlugin(const PluginID & ID) const1445 const PluginDescriptor *PluginManager::GetPlugin(const PluginID & ID) const
1446 {
1447 if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1448 return nullptr;
1449 else
1450 return &iter->second;
1451 }
1452
Advance(bool incrementing)1453 void PluginManager::Iterator::Advance(bool incrementing)
1454 {
1455 const auto end = mPm.mPlugins.end();
1456 if (incrementing && mIterator != end)
1457 ++mIterator;
1458 bool all = mPluginType == PluginTypeNone && mEffectType == EffectTypeNone;
1459 for (; mIterator != end; ++mIterator) {
1460 auto &plug = mIterator->second;
1461 if (!all && !(plug.IsValid() && plug.IsEnabled()))
1462 continue;
1463 auto plugType = plug.GetPluginType();
1464 if ((mPluginType == PluginTypeNone || (plugType & mPluginType)) &&
1465 (mEffectType == EffectTypeNone || plug.GetEffectType() == mEffectType)) {
1466 if (!all && (plugType & PluginTypeEffect)) {
1467 // This preference may be written by EffectsPrefs
1468 auto setting = mPm.GetPluginEnabledSetting( plug );
1469 if (!(setting.empty() || gPrefs->Read( setting, true )))
1470 continue;
1471 }
1472 // Pause iteration at this match
1473 break;
1474 }
1475 }
1476 }
1477
Iterator(PluginManager & manager)1478 PluginManager::Iterator::Iterator(PluginManager &manager)
1479 : mPm{ manager }
1480 , mIterator{ manager.mPlugins.begin() }
1481 {
1482 }
1483
Iterator(PluginManager & manager,int type)1484 PluginManager::Iterator::Iterator(PluginManager &manager, int type)
1485 : mPm{ manager }
1486 , mIterator{ manager.mPlugins.begin() }
1487 , mPluginType{ type }
1488 {
1489 Advance(false);
1490 }
1491
Iterator(PluginManager & manager,EffectType type)1492 PluginManager::Iterator::Iterator(PluginManager &manager, EffectType type)
1493 : mPm{ manager }
1494 , mIterator{ manager.mPlugins.begin() }
1495 , mEffectType{ type }
1496 {
1497 Advance(false);
1498 }
1499
operator ++()1500 auto PluginManager::Iterator::operator ++() -> Iterator &
1501 {
1502 Advance(true);
1503 return *this;
1504 }
1505
IsPluginEnabled(const PluginID & ID)1506 bool PluginManager::IsPluginEnabled(const PluginID & ID)
1507 {
1508 if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1509 return false;
1510 else
1511 return iter->second.IsEnabled();
1512 }
1513
EnablePlugin(const PluginID & ID,bool enable)1514 void PluginManager::EnablePlugin(const PluginID & ID, bool enable)
1515 {
1516 if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1517 return;
1518 else
1519 iter->second.SetEnabled(enable);
1520 }
1521
GetSymbol(const PluginID & ID)1522 const ComponentInterfaceSymbol & PluginManager::GetSymbol(const PluginID & ID)
1523 {
1524 if (auto iter = mPlugins.find(ID); iter == mPlugins.end()) {
1525 static ComponentInterfaceSymbol empty;
1526 return empty;
1527 }
1528 else
1529 return iter->second.GetSymbol();
1530 }
1531
GetInstance(const PluginID & ID)1532 ComponentInterface *PluginManager::GetInstance(const PluginID & ID)
1533 {
1534 if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1535 return nullptr;
1536 else {
1537 auto &plug = iter->second;
1538
1539 // If not dealing with legacy effects, make sure the provider is loaded
1540 if (!plug.IsEffectLegacy())
1541 {
1542 const PluginID & prov = plug.GetProviderID();
1543 if (auto iter2 = mPlugins.find(prov); iter2 == mPlugins.end())
1544 return nullptr;
1545 else
1546 iter2->second.GetInstance();
1547 }
1548
1549 return plug.GetInstance();
1550 }
1551 }
1552
GetID(ModuleInterface * module)1553 PluginID PluginManager::GetID(ModuleInterface *module)
1554 {
1555 return ModuleManager::GetID(module);
1556 }
1557
GetID(ComponentInterface * command)1558 PluginID PluginManager::GetID(ComponentInterface *command)
1559 {
1560 return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1561 GetPluginTypeString(PluginTypeAudacityCommand),
1562 wxEmptyString,
1563 command->GetVendor().Internal(),
1564 command->GetSymbol().Internal(),
1565 command->GetPath());
1566 }
1567
GetID(EffectDefinitionInterface * effect)1568 PluginID PluginManager::GetID(EffectDefinitionInterface *effect)
1569 {
1570 return wxString::Format(wxT("%s_%s_%s_%s_%s"),
1571 GetPluginTypeString(PluginTypeEffect),
1572 effect->GetFamily().Internal(),
1573 effect->GetVendor().Internal(),
1574 effect->GetSymbol().Internal(),
1575 effect->GetPath());
1576 }
1577
1578 // This string persists in configuration files
1579 // So config compatibility will break if it is changed across Audacity versions
GetPluginTypeString(PluginType type)1580 wxString PluginManager::GetPluginTypeString(PluginType type)
1581 {
1582 wxString str;
1583
1584 switch (type)
1585 {
1586 default:
1587 case PluginTypeNone:
1588 str = wxT("Placeholder");
1589 break;
1590 case PluginTypeStub:
1591 str = wxT("Stub");
1592 break;
1593 case PluginTypeEffect:
1594 str = wxT("Effect");
1595 break;
1596 case PluginTypeAudacityCommand:
1597 str = wxT("Generic");
1598 break;
1599 case PluginTypeExporter:
1600 str = wxT("Exporter");
1601 break;
1602 case PluginTypeImporter:
1603 str = wxT("Importer");
1604 break;
1605 case PluginTypeModule:
1606 str = ModuleManager::GetPluginTypeString();
1607 break;
1608 }
1609
1610 return str;
1611 }
1612
CreatePlugin(const PluginID & id,ComponentInterface * ident,PluginType type)1613 PluginDescriptor & PluginManager::CreatePlugin(const PluginID & id,
1614 ComponentInterface *ident,
1615 PluginType type)
1616 {
1617 // This will either create a NEW entry or replace an existing entry
1618 PluginDescriptor & plug = mPlugins[id];
1619
1620 plug.SetPluginType(type);
1621
1622 plug.SetID(id);
1623 plug.SetPath(ident->GetPath());
1624 plug.SetSymbol(ident->GetSymbol());
1625 plug.SetVendor(ident->GetVendor().Internal());
1626 plug.SetVersion(ident->GetVersion());
1627
1628 return plug;
1629 }
1630
GetSettings()1631 FileConfig *PluginManager::GetSettings()
1632 {
1633 if (!mSettings)
1634 {
1635 mSettings =
1636 AudacityFileConfig::Create({}, {}, FileNames::PluginSettings());
1637
1638 // Check for a settings version that we can understand
1639 if (mSettings->HasEntry(SETVERKEY))
1640 {
1641 wxString setver = mSettings->Read(SETVERKEY, SETVERKEY);
1642 if (setver < SETVERCUR )
1643 {
1644 // This is where we'd put in conversion code when the
1645 // settings version changes.
1646 //
1647 // Should also check for a settings file that is newer than
1648 // what we can understand.
1649 }
1650 }
1651 else
1652 {
1653 // Make sure is has a version string
1654 mSettings->Write(SETVERKEY, SETVERCUR);
1655 mSettings->Flush();
1656 }
1657 }
1658
1659 return mSettings.get();
1660 }
1661
HasGroup(const RegistryPath & group)1662 bool PluginManager::HasGroup(const RegistryPath & group)
1663 {
1664 auto settings = GetSettings();
1665
1666 bool res = settings->HasGroup(group);
1667 if (res)
1668 {
1669 // The group exists, but empty groups aren't considered valid
1670 wxString oldPath = settings->GetPath();
1671 settings->SetPath(group);
1672 res = settings->GetNumberOfEntries() || settings->GetNumberOfGroups();
1673 settings->SetPath(oldPath);
1674 }
1675
1676 return res;
1677 }
1678
GetSubgroups(const RegistryPath & group,RegistryPaths & subgroups)1679 bool PluginManager::GetSubgroups(const RegistryPath & group, RegistryPaths & subgroups)
1680 {
1681 if (group.empty() || !HasGroup(group))
1682 {
1683 return false;
1684 }
1685
1686 wxString path = GetSettings()->GetPath();
1687 GetSettings()->SetPath(group);
1688
1689 wxString name;
1690 long index = 0;
1691 if (GetSettings()->GetFirstGroup(name, index))
1692 {
1693 do
1694 {
1695 subgroups.push_back(name);
1696 } while (GetSettings()->GetNextGroup(name, index));
1697 }
1698
1699 GetSettings()->SetPath(path);
1700
1701 return true;
1702 }
1703
GetConfig(const RegistryPath & key,int & value,int defval)1704 bool PluginManager::GetConfig(const RegistryPath & key, int & value, int defval)
1705 {
1706 bool result = false;
1707
1708 if (!key.empty())
1709 {
1710 result = GetSettings()->Read(key, &value, defval);
1711 }
1712
1713 return result;
1714 }
1715
GetConfig(const RegistryPath & key,wxString & value,const wxString & defval)1716 bool PluginManager::GetConfig(const RegistryPath & key, wxString & value, const wxString & defval)
1717 {
1718 bool result = false;
1719
1720 if (!key.empty())
1721 {
1722 wxString wxval;
1723
1724 result = GetSettings()->Read(key, &wxval, defval);
1725
1726 value = wxval;
1727 }
1728
1729 return result;
1730 }
1731
GetConfig(const RegistryPath & key,bool & value,bool defval)1732 bool PluginManager::GetConfig(const RegistryPath & key, bool & value, bool defval)
1733 {
1734 bool result = false;
1735
1736 if (!key.empty())
1737 {
1738 result = GetSettings()->Read(key, &value, defval);
1739 }
1740
1741 return result;
1742 }
1743
GetConfig(const RegistryPath & key,float & value,float defval)1744 bool PluginManager::GetConfig(const RegistryPath & key, float & value, float defval)
1745 {
1746 bool result = false;
1747
1748 if (!key.empty())
1749 {
1750 double dval = 0.0;
1751
1752 result = GetSettings()->Read(key, &dval, (double) defval);
1753
1754 value = (float) dval;
1755 }
1756
1757 return result;
1758 }
1759
GetConfig(const RegistryPath & key,double & value,double defval)1760 bool PluginManager::GetConfig(const RegistryPath & key, double & value, double defval)
1761 {
1762 bool result = false;
1763
1764 if (!key.empty())
1765 {
1766 result = GetSettings()->Read(key, &value, defval);
1767 }
1768
1769 return result;
1770 }
1771
SetConfig(const RegistryPath & key,const wxString & value)1772 bool PluginManager::SetConfig(const RegistryPath & key, const wxString & value)
1773 {
1774 bool result = false;
1775
1776 if (!key.empty())
1777 {
1778 wxString wxval = value;
1779 result = GetSettings()->Write(key, wxval);
1780 if (result)
1781 {
1782 result = GetSettings()->Flush();
1783 }
1784 }
1785
1786 return result;
1787 }
1788
SetConfig(const RegistryPath & key,const int & value)1789 bool PluginManager::SetConfig(const RegistryPath & key, const int & value)
1790 {
1791 bool result = false;
1792
1793 if (!key.empty())
1794 {
1795 result = GetSettings()->Write(key, value);
1796 if (result)
1797 {
1798 result = GetSettings()->Flush();
1799 }
1800 }
1801
1802 return result;
1803 }
1804
SetConfig(const RegistryPath & key,const bool & value)1805 bool PluginManager::SetConfig(const RegistryPath & key, const bool & value)
1806 {
1807 bool result = false;
1808
1809 if (!key.empty())
1810 {
1811 result = GetSettings()->Write(key, value);
1812 if (result)
1813 {
1814 result = GetSettings()->Flush();
1815 }
1816 }
1817
1818 return result;
1819 }
1820
SetConfig(const RegistryPath & key,const float & value)1821 bool PluginManager::SetConfig(const RegistryPath & key, const float & value)
1822 {
1823 bool result = false;
1824
1825 if (!key.empty())
1826 {
1827 result = GetSettings()->Write(key, value);
1828 if (result)
1829 {
1830 result = GetSettings()->Flush();
1831 }
1832 }
1833
1834 return result;
1835 }
1836
SetConfig(const RegistryPath & key,const double & value)1837 bool PluginManager::SetConfig(const RegistryPath & key, const double & value)
1838 {
1839 bool result = false;
1840
1841 if (!key.empty())
1842 {
1843 result = GetSettings()->Write(key, value);
1844 if (result)
1845 {
1846 result = GetSettings()->Flush();
1847 }
1848 }
1849
1850 return result;
1851 }
1852
1853 /* Return value is a key for lookup in a config file */
SettingsPath(const PluginID & ID,bool shared)1854 RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared)
1855 {
1856 // All the strings reported by PluginDescriptor and used in this function
1857 // persist in the plugin settings configuration file, so they should not
1858 // be changed across Audacity versions, or else compatibility of the
1859 // configuration files will break.
1860
1861 if (auto iter = mPlugins.find(ID); iter == mPlugins.end())
1862 return {};
1863 else {
1864 const PluginDescriptor & plug = iter->second;
1865
1866 wxString id = GetPluginTypeString(plug.GetPluginType()) +
1867 wxT("_") +
1868 plug.GetEffectFamily() + // is empty for non-Effects
1869 wxT("_") +
1870 plug.GetVendor() +
1871 wxT("_") +
1872 (shared ? wxString{} : plug.GetSymbol().Internal());
1873
1874 return SETROOT +
1875 ConvertID(id) +
1876 wxCONFIG_PATH_SEPARATOR +
1877 (shared ? wxT("shared") : wxT("private")) +
1878 wxCONFIG_PATH_SEPARATOR;
1879 }
1880 }
1881
1882 /* Return value is a key for lookup in a config file */
SharedGroup(const PluginID & ID,const RegistryPath & group)1883 RegistryPath PluginManager::SharedGroup(const PluginID & ID, const RegistryPath & group)
1884 {
1885 wxString path = SettingsPath(ID, true);
1886
1887 wxFileName ff(group);
1888 if (!ff.GetName().empty())
1889 {
1890 path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
1891 }
1892
1893 return path;
1894 }
1895
1896 /* Return value is a key for lookup in a config file */
SharedKey(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)1897 RegistryPath PluginManager::SharedKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
1898 {
1899 auto path = SharedGroup(ID, group);
1900 if (path.empty())
1901 {
1902 return path;
1903 }
1904
1905 return path + key;
1906 }
1907
1908 /* Return value is a key for lookup in a config file */
PrivateGroup(const PluginID & ID,const RegistryPath & group)1909 RegistryPath PluginManager::PrivateGroup(const PluginID & ID, const RegistryPath & group)
1910 {
1911 auto path = SettingsPath(ID, false);
1912
1913 wxFileName ff(group);
1914 if (!ff.GetName().empty())
1915 {
1916 path += ff.GetFullPath(wxPATH_UNIX) + wxCONFIG_PATH_SEPARATOR;
1917 }
1918
1919 return path;
1920 }
1921
1922 /* Return value is a key for lookup in a config file */
PrivateKey(const PluginID & ID,const RegistryPath & group,const RegistryPath & key)1923 RegistryPath PluginManager::PrivateKey(const PluginID & ID, const RegistryPath & group, const RegistryPath & key)
1924 {
1925 auto path = PrivateGroup(ID, group);
1926 if (path.empty())
1927 {
1928 return path;
1929 }
1930
1931 return path + key;
1932 }
1933
1934 // Sanitize the ID...not the best solution, but will suffice until this
1935 // is converted to XML. We use base64 encoding to preserve case.
ConvertID(const PluginID & ID)1936 wxString PluginManager::ConvertID(const PluginID & ID)
1937 {
1938 if (ID.StartsWith(wxT("base64:")))
1939 {
1940 wxString id = ID.Mid(7);
1941 ArrayOf<char> buf{ id.length() / 4 * 3 };
1942 id = wxString::FromUTF8(buf.get(), b64decode(id, buf.get()));
1943 return id;
1944 }
1945
1946 const wxCharBuffer & buf = ID.ToUTF8();
1947 return wxT("base64:") + b64encode(buf, strlen(buf));
1948 }
1949
1950 ////////////////////////////////////////////////////////////////////////////////
1951 // Base64 en/decoding
1952 //
1953 // Original routines marked as public domain and found at:
1954 //
1955 // http://en.wikibooks.org/wiki/Algorithm_implementation/Miscellaneous/Base64
1956 //
1957 ////////////////////////////////////////////////////////////////////////////////
1958
1959 // Lookup table for encoding
1960 const static wxChar cset[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
1961 const static char padc = wxT('=');
1962
b64encode(const void * in,int len)1963 wxString PluginManager::b64encode(const void *in, int len)
1964 {
1965 unsigned char *p = (unsigned char *) in;
1966 wxString out;
1967
1968 unsigned long temp;
1969 for (int i = 0; i < len / 3; i++)
1970 {
1971 temp = (*p++) << 16; //Convert to big endian
1972 temp += (*p++) << 8;
1973 temp += (*p++);
1974 out += cset[(temp & 0x00FC0000) >> 18];
1975 out += cset[(temp & 0x0003F000) >> 12];
1976 out += cset[(temp & 0x00000FC0) >> 6];
1977 out += cset[(temp & 0x0000003F)];
1978 }
1979
1980 switch (len % 3)
1981 {
1982 case 1:
1983 temp = (*p++) << 16; //Convert to big endian
1984 out += cset[(temp & 0x00FC0000) >> 18];
1985 out += cset[(temp & 0x0003F000) >> 12];
1986 out += padc;
1987 out += padc;
1988 break;
1989
1990 case 2:
1991 temp = (*p++) << 16; //Convert to big endian
1992 temp += (*p++) << 8;
1993 out += cset[(temp & 0x00FC0000) >> 18];
1994 out += cset[(temp & 0x0003F000) >> 12];
1995 out += cset[(temp & 0x00000FC0) >> 6];
1996 out += padc;
1997 break;
1998 }
1999
2000 return out;
2001 }
2002
b64decode(const wxString & in,void * out)2003 int PluginManager::b64decode(const wxString &in, void *out)
2004 {
2005 int len = in.length();
2006 unsigned char *p = (unsigned char *) out;
2007
2008 if (len % 4) //Sanity check
2009 {
2010 return 0;
2011 }
2012
2013 int padding = 0;
2014 if (len)
2015 {
2016 if (in[len - 1] == padc)
2017 {
2018 padding++;
2019 }
2020
2021 if (in[len - 2] == padc)
2022 {
2023 padding++;
2024 }
2025 }
2026
2027 //const char *a = in.mb_str();
2028 //Setup a vector to hold the result
2029 unsigned long temp = 0; //Holds decoded quanta
2030 int i = 0;
2031 while (i < len)
2032 {
2033 for (int quantumPosition = 0; quantumPosition < 4; quantumPosition++)
2034 {
2035 unsigned char c = in[i];
2036 temp <<= 6;
2037
2038 if (c >= 0x41 && c <= 0x5A)
2039 {
2040 temp |= c - 0x41;
2041 }
2042 else if (c >= 0x61 && c <= 0x7A)
2043 {
2044 temp |= c - 0x47;
2045 }
2046 else if (c >= 0x30 && c <= 0x39)
2047 {
2048 temp |= c + 0x04;
2049 }
2050 else if (c == 0x2B)
2051 {
2052 temp |= 0x3E;
2053 }
2054 else if (c == 0x2F)
2055 {
2056 temp |= 0x3F;
2057 }
2058 else if (c == padc)
2059 {
2060 switch (len - i)
2061 {
2062 case 1: //One pad character
2063 *p++ = (temp >> 16) & 0x000000FF;
2064 *p++ = (temp >> 8) & 0x000000FF;
2065 return p - (unsigned char *) out;
2066 case 2: //Two pad characters
2067 *p++ = (temp >> 10) & 0x000000FF;
2068 return p - (unsigned char *) out;
2069 }
2070 }
2071 i++;
2072 }
2073 *p++ = (temp >> 16) & 0x000000FF;
2074 *p++ = (temp >> 8) & 0x000000FF;
2075 *p++ = temp & 0x000000FF;
2076 }
2077
2078 return p - (unsigned char *) out;
2079 }
2080
2081 // This is defined out-of-line here, to keep ComponentInterface free of other
2082 // #include directives.
GetName()2083 TranslatableString ComponentInterface::GetName()
2084 {
2085 return GetSymbol().Msgid();
2086 }
2087