text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/module.h
// Created by  : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "uid.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <utility>
#include <vector>

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
class FactoryInfo
{
public:
//------------------------------------------------------------------------
	using PFactoryInfo = Steinberg::PFactoryInfo;

	FactoryInfo () noexcept {}
	~FactoryInfo () noexcept {}
	FactoryInfo (const FactoryInfo&) noexcept = default;
	FactoryInfo (PFactoryInfo&&) noexcept;
	FactoryInfo (FactoryInfo&&) noexcept = default;
	FactoryInfo& operator= (const FactoryInfo&) noexcept = default;
	FactoryInfo& operator= (FactoryInfo&&) noexcept;
	FactoryInfo& operator= (PFactoryInfo&&) noexcept;

	std::string vendor () const noexcept;
	std::string url () const noexcept;
	std::string email () const noexcept;
	Steinberg::int32 flags () const noexcept;
	bool classesDiscardable () const noexcept;
	bool licenseCheck () const noexcept;
	bool componentNonDiscardable () const noexcept;

	PFactoryInfo& get () noexcept { return info; }
//------------------------------------------------------------------------
private:
	PFactoryInfo info {};
};

//------------------------------------------------------------------------
class ClassInfo
{
public:
//------------------------------------------------------------------------
	using SubCategories = std::vector<std::string>;
	using PClassInfo = Steinberg::PClassInfo;
	using PClassInfo2 = Steinberg::PClassInfo2;
	using PClassInfoW = Steinberg::PClassInfoW;

//------------------------------------------------------------------------
	ClassInfo () noexcept {}
	explicit ClassInfo (const PClassInfo& info) noexcept;
	explicit ClassInfo (const PClassInfo2& info) noexcept;
	explicit ClassInfo (const PClassInfoW& info) noexcept;
	ClassInfo (const ClassInfo&) = default;
	ClassInfo& operator= (const ClassInfo&) = default;
	ClassInfo (ClassInfo&&) = default;
	ClassInfo& operator= (ClassInfo&&) = default;

	const UID& ID () const noexcept;
	int32_t cardinality () const noexcept;
	const std::string& category () const noexcept;
	const std::string& name () const noexcept;
	const std::string& vendor () const noexcept;
	const std::string& version () const noexcept;
	const std::string& sdkVersion () const noexcept;
	const SubCategories& subCategories () const noexcept;
	std::string subCategoriesString () const noexcept;
	Steinberg::uint32 classFlags () const noexcept;

	struct Data
	{
		UID classID;
		int32_t cardinality;
		std::string category;
		std::string name;
		std::string vendor;
		std::string version;
		std::string sdkVersion;
		SubCategories subCategories;
		Steinberg::uint32 classFlags = 0;
	};

	Data& get () noexcept { return data; }
//------------------------------------------------------------------------
private:
	void parseSubCategories (const std::string& str) noexcept;
	Data data {};
};

//------------------------------------------------------------------------
class PluginFactory
{
public:
//------------------------------------------------------------------------
	using ClassInfos = std::vector<ClassInfo>;
	using PluginFactoryPtr = Steinberg::IPtr<Steinberg::IPluginFactory>;

//------------------------------------------------------------------------
	explicit PluginFactory (const PluginFactoryPtr& factory) noexcept;

	void setHostContext (Steinberg::FUnknown* context) const noexcept;

	FactoryInfo info () const noexcept;
	uint32_t classCount () const noexcept;
	ClassInfos classInfos () const noexcept;

	template <typename T>
	Steinberg::IPtr<T> createInstance (const UID& classID) const noexcept;

	const PluginFactoryPtr& get () const noexcept { return factory; }
//------------------------------------------------------------------------
private:
	PluginFactoryPtr factory;
};

//------------------------------------------------------------------------
class Module
{
public:
//------------------------------------------------------------------------
	using Ptr = std::shared_ptr<Module>;
	using PathList = std::vector<std::string>;

//------------------------------------------------------------------------
	static Ptr create (const std::string& path, std::string& errorDescription);
	static PathList getModulePaths ();

	const std::string& getName () const noexcept { return name; }
	const std::string& getPath () const noexcept { return path; }
	const PluginFactory& getFactory () const noexcept { return factory; }
//------------------------------------------------------------------------
protected:
	virtual ~Module () noexcept = default;
	virtual bool load (const std::string& path, std::string& errorDescription) = 0;

	PluginFactory factory {nullptr};
	std::string name;
	std::string path;
};

//------------------------------------------------------------------------
template <typename T>
inline Steinberg::IPtr<T> PluginFactory::createInstance (const UID& classID) const noexcept
{
	T* obj = nullptr;
	if (factory->createInstance (classID.data (), T::iid, reinterpret_cast<void**> (&obj)) ==
	    Steinberg::kResultTrue)
		return Steinberg::owned (obj);
	return nullptr;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    :
// Filename    : public.sdk/source/vst/hosting/module_linux.cpp
// Created by  : Steinberg, 08/2016
// Description : hosting module classes (linux implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "stringconvert.h"
#include "module.h"
#include "optional.h"
#include <dlfcn.h>
#include <algorithm>
#include <experimental/filesystem>
#include <sys/utsname.h>
#include <sys/types.h>
#include <unistd.h>

//------------------------------------------------------------------------
extern "C" {
typedef bool (PLUGIN_API* ModuleEntryFunc) (void*);
typedef bool (PLUGIN_API* ModuleExitFunc) ();
}

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
Optional<std::string> getCurrentMachineName ()
{
	struct utsname unameData;

	int res = uname (&unameData);
	if (res != 0)
		return {};

	return {unameData.machine};
}

using Path = std::experimental::filesystem::path;
//------------------------------------------------------------------------
Optional<Path> getApplicationPath ()
{
	std::string appPath = "";

	pid_t pid = getpid ();
	char buf[10];
	sprintf (buf, "%d", pid);
	std::string _link = "/proc/";
	_link.append (buf);
	_link.append ("/exe");
	char proc[1024];
	int ch = readlink (_link.c_str (), proc, 1024);
	if (ch == -1)
		return {};

	proc[ch] = 0;
	appPath = proc;
	std::string::size_type t = appPath.find_last_of ("/");
	appPath = appPath.substr (0, t);

	return Path {appPath};
}

//------------------------------------------------------------------------
class LinuxModule : public Module
{
public:
	template <typename T>
	T getFunctionPointer (const char* name)
	{
		return reinterpret_cast<T> (dlsym (module, name));
	}

	~LinuxModule ()
	{
		factory = PluginFactory (nullptr);

		if (module)
		{
			if (auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit"))
				moduleExit ();

			dlclose (module);
		}
	}

	static Optional<Path> getSOPath (const std::string& inPath)
	{
		using namespace std::experimental;

		Path modulePath {inPath};
		if (!filesystem::is_directory (modulePath))
			return {};

		auto stem = modulePath.stem ();

		modulePath /= "Contents";
		if (!filesystem::is_directory (modulePath))
			return {};

		auto machine = getCurrentMachineName ();
		if (!machine)
			return {};

		modulePath /= *machine + "-linux";
		if (!filesystem::is_directory (modulePath))
			return {};

		stem.replace_extension (".so");
		modulePath /= stem;
		return Optional<Path> (std::move (modulePath));
	}

	bool load (const std::string& inPath, std::string& errorDescription) override
	{
		auto modulePath = getSOPath (inPath);
		if (!modulePath)
		{
			errorDescription = inPath + " is not a module directory.";
			return false;
		}

		module = dlopen (modulePath->generic_u8string ().data (), RTLD_LAZY);
		if (!module)
		{
			errorDescription = "dlopen failed.\n";
			errorDescription += dlerror ();
			return false;
		}

		auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
		if (!factoryProc)
		{
			errorDescription =
			    "The shared library does not export the required 'GetPluginFactory' function";
			return false;
		}
		auto moduleEntry = getFunctionPointer<ModuleEntryFunc> ("ModuleEntry");
		if (!moduleEntry)
		{
			errorDescription =
			    "The shared library does not export the required 'ModuleEntry' function";
			return false;
		}
		if (!moduleEntry (module))
		{
			errorDescription = "Calling 'ModuleEntry' failed";
			return false;
		}
		auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (factoryProc ());
		if (!f)
		{
			errorDescription = "Calling 'GetPluginFactory' returned nullptr";
			return false;
		}
		factory = PluginFactory (f);
		return true;
	}

	void* module {nullptr};
};

//------------------------------------------------------------------------
void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList)
{
	try
	{
		using namespace std::experimental;
		for (auto& p : filesystem::recursive_directory_iterator (path))
		{
			if (p.path ().extension () == ext && filesystem::is_directory (p))
			{
				pathList.push_back (p.path ().generic_u8string ());
			}
		}
	}
	catch (...)
	{
	}
}

//------------------------------------------------------------------------
void findModules (const std::string& path, Module::PathList& pathList)
{
	findFilesWithExt (path, ".vst3", pathList);
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
	auto module = std::make_shared<LinuxModule> ();
	if (module->load (path, errorDescription))
	{
		auto it = std::find_if (path.rbegin (), path.rend (),
		                        [] (const std::string::value_type& c) { return c == '/'; });
		if (it != path.rend ())
			module->name = {it.base (), path.end ()};
		return module;
	}
	return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
	/* VST3 component locations on linux :
	    * User privately installed	: $HOME/.vst3/
	    * Distribution installed	: /usr/lib/vst3/
	    * Locally installed			: /usr/local/lib/vst3/
	    * Application				: /$APPFOLDER/vst3/
	*/

	const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"};

	// 32bit Plug-ins on 64bit OS
	// const auto systemPaths = {"/usr/lib32/vst3/", "/usr/local/lib32/vst3/"};

	PathList list;
	if (auto homeDir = getenv ("HOME"))
	{
		std::experimental::filesystem::path homePath (homeDir);
		homePath /= ".vst3";
		findModules (homePath.generic_u8string (), list);
	}
	for (auto path : systemPaths)
		findModules (path, list);

	// application level
	auto appPath = getApplicationPath ();
	if (appPath)
	{
		*appPath /= "vst3";
		findModules (appPath->generic_u8string (), list);
	}

	return list;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/module_mac.mm
// Created by  : Steinberg, 08/2016
// Description : hosting module classes (macOS implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#import "module.h"

#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>

//------------------------------------------------------------------------
extern "C" {
typedef bool (*BundleEntryFunc) (CFBundleRef);
typedef bool (*BundleExitFunc) ();
}

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
template <typename T>
class CFPtr
{
public:
	inline CFPtr (const T& obj = nullptr) : obj (obj) {}
	inline CFPtr (CFPtr&& other) { *this = other; }
	inline ~CFPtr ()
	{
		if (obj)
			CFRelease (obj);
	}

	inline CFPtr& operator= (CFPtr&& other)
	{
		obj = other.obj;
		other.obj = nullptr;
		return *this;
	}
	inline CFPtr& operator= (const T& o)
	{
		if (obj)
			CFRelease (obj);
		obj = o;
		return *this;
	}
	inline operator T () const { return obj; } // act as T
private:
	CFPtr (const CFPtr& other) = delete;
	CFPtr& operator= (const CFPtr& other) = delete;

	T obj = nullptr;
};

//------------------------------------------------------------------------
class MacModule : public Module
{
public:
	template <typename T>
	T getFunctionPointer (const char* name)
	{
		assert (bundle);
		CFPtr<CFStringRef> functionName {
		    CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII)};
		return reinterpret_cast<T> (CFBundleGetFunctionPointerForName (bundle, functionName));
	}

	bool loadInternal (const std::string& path, std::string& errorDescription)
	{
		CFPtr<CFURLRef> url (CFURLCreateFromFileSystemRepresentation (
		    kCFAllocatorDefault, reinterpret_cast<const UInt8*> (path.data ()), path.length (),
		    true));
		if (!url)
			return false;
		bundle = CFBundleCreate (kCFAllocatorDefault, url);
		CFErrorRef error = nullptr;
		if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error))
		{
			if (error)
			{
				CFPtr<CFStringRef> errorString (CFErrorCopyDescription (error));
				if (errorString)
				{
					auto stringLength = CFStringGetLength (errorString);
					auto maxSize =
					    CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8);
					auto buffer = std::make_unique<char[]> (maxSize);
					if (CFStringGetCString (errorString, buffer.get (), maxSize,
					                        kCFStringEncodingUTF8))
						errorDescription = buffer.get ();
					CFRelease (error);
				}
			}
			else
			{
				errorDescription = "Could not create Bundle for path: " + path;
			}
			return false;
		}
		auto bundleEntry = getFunctionPointer<BundleEntryFunc> ("bundleEntry");
		if (!bundleEntry)
		{
			errorDescription = "Bundle does not export the required 'bundleEntry' function";
			return false;
		}
		auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit");
		if (!bundleExit)
		{
			errorDescription = "Bundle does not export the required 'bundleExit' function";
			return false;
		}
		auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
		if (!factoryProc)
		{
			errorDescription = "Bundle does not export the required 'GetPluginFactory' function";
			return false;
		}
		if (!bundleEntry (bundle))
		{
			errorDescription = "Calling 'bundleEntry' failed";
			return false;
		}
		auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (factoryProc ());
		if (!f)
		{
			errorDescription = "Calling 'GetPluginFactory' returned nullptr";
			return false;
		}
		factory = PluginFactory (f);
		return true;
	}

	bool load (const std::string& path, std::string& errorDescription) override
	{
		if (!path.empty () && path[0] != '/')
		{
			auto buffer = std::make_unique<char[]> (PATH_MAX);
			auto workDir = getcwd (buffer.get (), PATH_MAX);
			if (workDir)
			{
				std::string wd (workDir);
				wd += "/";
				return loadInternal (wd + path, errorDescription);
			}
		}
		return loadInternal (path, errorDescription);
	}

	~MacModule ()
	{
        factory = PluginFactory (nullptr);

		if (bundle)
		{
			if (auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit"))
				bundleExit ();

            if (CFBundleIsExecutableLoaded ((CFBundleRef)bundle))
            {
                CFBundleUnloadExecutable ((CFBundleRef)bundle);
            }
		}

	}

	CFPtr<CFBundleRef> bundle;
};

//------------------------------------------------------------------------
void getModules (NSSearchPathDomainMask domain, Module::PathList& result)
{
	NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory
	                                                           inDomain:domain
	                                                  appropriateForURL:nil
	                                                             create:NO
	                                                              error:nil];
	if (libraryUrl == nil)
		return;
	NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"];
	if (audioUrl == nil)
		return;
	NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"];
	if (plugInsUrl == nil)
		return;
	NSURL* vst3Url =
	    [[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath];
	if (vst3Url == nil)
		return;
	NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager]
	               enumeratorAtURL:[vst3Url URLByResolvingSymlinksInPath]
	    includingPropertiesForKeys:nil
	                       options:NSDirectoryEnumerationSkipsPackageDescendants
	                  errorHandler:nil];
	for (NSURL* url in enumerator)
	{
		if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"])
		{

			result.emplace_back ([url.path UTF8String]);
		}
	}
}

//------------------------------------------------------------------------
void getApplicationModules (Module::PathList& result)
{
	auto bundle = CFBundleGetMainBundle ();
	if (!bundle)
		return;
	auto bundleUrl = static_cast<NSURL*> (CFBundleCopyBundleURL (bundle));
	if (!bundleUrl)
		return;
	auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"];
	if (!resUrl)
		return;
	auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"];
	if (!vst3Url)
		return;
	NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager]
	               enumeratorAtURL:[vst3Url URLByResolvingSymlinksInPath]
	    includingPropertiesForKeys:nil
	                       options:NSDirectoryEnumerationSkipsPackageDescendants
	                  errorHandler:nil];
	for (NSURL* url in enumerator)
	{
		if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"])
		{
			result.emplace_back ([url.path UTF8String]);
		}
	}
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
	auto module = std::make_shared<MacModule> ();
	if (module->load (path, errorDescription))
	{
		module->path = path;
		auto it = std::find_if (path.rbegin (), path.rend (),
		                        [] (const std::string::value_type& c) { return c == '/'; });
		if (it != path.rend ())
			module->name = {it.base (), path.end ()};
		return module;
	}
	return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
	PathList list;
	getModules (NSUserDomainMask, list);
	getModules (NSLocalDomainMask, list);
	// TODO getModules (NSNetworkDomainMask, list);
	getApplicationModules (list);
	return list;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/eventlist.h
// Created by  : Steinberg, 03/05/2008.
// Description : VST 3 event list implementation
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/vst/ivstevents.h"

namespace Steinberg {
namespace Vst {

//------------------------------------------------------------------------
/** Implementation's example of IEventList.
\ingroup sdkBase
*/
class EventList : public IEventList
{
public:
	EventList (int32 maxSize = 50);
	virtual ~EventList ();

	void clear () { fillCount = 0; }

	int32 PLUGIN_API getEventCount () SMTG_OVERRIDE { return fillCount; }
	tresult PLUGIN_API getEvent (int32 index, Event& e) SMTG_OVERRIDE;
	tresult PLUGIN_API addEvent (Event& e) SMTG_OVERRIDE;

	Event* getEventByIndex (int32 index);
	void setMaxSize (int32 maxSize);

//------------------------------------------------------------------------
	DECLARE_FUNKNOWN_METHODS
protected:
	Event* events;
	int32 maxSize;
	int32 fillCount;
};

//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/parameterchanges.cpp
// Created by  : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "parameterchanges.h"

namespace Steinberg {
namespace Vst {

//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (ParameterChanges, IParameterChanges, IParameterChanges::iid)
IMPLEMENT_FUNKNOWN_METHODS (ParameterValueQueue, IParamValueQueue, IParamValueQueue::iid)

const int32 kQueueReservedPoints = 5;

//-----------------------------------------------------------------------------
ParameterValueQueue::ParameterValueQueue (ParamID paramID)
: paramID (paramID)
{
	values.reserve (kQueueReservedPoints);
	FUNKNOWN_CTOR
}

//-----------------------------------------------------------------------------
ParameterValueQueue::~ParameterValueQueue ()
{
	FUNKNOWN_DTOR
}

//-----------------------------------------------------------------------------
void ParameterValueQueue::clear ()
{
	values.clear ();
}

//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterValueQueue::getPointCount ()
{
	return (int32)values.size ();
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::getPoint (int32 index, int32& sampleOffset, ParamValue& value)
{
	if (index < (int32)values.size ())
	{
		const ParameterQueueValue& queueValue = values[index];
		sampleOffset = queueValue.sampleOffset;
		value = queueValue.value;
		return kResultTrue;
	}
	return kResultFalse;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::addPoint (int32 sampleOffset, ParamValue value, int32& index)
{
	int32 destIndex = (int32)values.size ();
	for (uint32 i = 0; i < values.size (); i++)
	{
		if (values[i].sampleOffset == sampleOffset)
		{
			values[i].value = value;
			index = i;
			return kResultTrue;
		}
		else if (values[i].sampleOffset > sampleOffset)
		{
			destIndex = i;
			break;
		}
	}

	// need new point
	ParameterQueueValue queueValue (value, sampleOffset);
	if (destIndex == (int32)values.size ())
		values.push_back (queueValue);
	else
		values.insert (values.begin () + destIndex, queueValue);

	index = destIndex;

	return kResultTrue;
}

//-----------------------------------------------------------------------------
// ParameterChanges
//-----------------------------------------------------------------------------
ParameterChanges::ParameterChanges (int32 maxParameters)
: usedQueueCount (0)
{
	FUNKNOWN_CTOR
	setMaxParameters (maxParameters);
}

//-----------------------------------------------------------------------------
ParameterChanges::~ParameterChanges ()
{
	setMaxParameters (0);

	FUNKNOWN_DTOR
}

//-----------------------------------------------------------------------------
void ParameterChanges::setMaxParameters (int32 maxParameters)
{
	if (maxParameters < 0)
		return;

	while ((int32)queues.size () < maxParameters)
	{
		ParameterValueQueue* valueQueue = new ParameterValueQueue (0xffffffff);
		queues.push_back (valueQueue);
	}

	while ((int32)queues.size () > maxParameters)
	{
		queues.back ()->release ();
		queues.pop_back ();
	}
	if (usedQueueCount > maxParameters)
		usedQueueCount = maxParameters;
}

//-----------------------------------------------------------------------------
void ParameterChanges::clearQueue ()
{
	usedQueueCount = 0;
}

//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterChanges::getParameterCount ()
{
	return usedQueueCount;
}

//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::getParameterData (int32 index)
{
	if (index < usedQueueCount)
		return queues[index];
	return nullptr;
}

//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::addParameterData (const ParamID& pid, int32& index)
{
	for (int32 i = 0; i < usedQueueCount; i++)
	{
		if (queues[i]->getParameterId () == pid)
		{
			index = i;
			return queues[i];
		}
	}

	ParameterValueQueue* valueQueue = nullptr;
	if (usedQueueCount < (int32)queues.size ())
	{
		valueQueue = queues[usedQueueCount];
		valueQueue->setParamID (pid);
		valueQueue->clear ();
	}
	else
	{
		valueQueue = new ParameterValueQueue (pid);
		queues.push_back (valueQueue);
	}

	index = usedQueueCount;
	usedQueueCount++;
	return valueQueue;
}

//-----------------------------------------------------------------------------
// ParameterChangeTransfer
//-----------------------------------------------------------------------------
ParameterChangeTransfer::ParameterChangeTransfer (int32 maxParameters)
: size (0)
, changes (nullptr)
, readIndex (0)
, writeIndex (0)
{
	setMaxParameters (maxParameters);
}

//-----------------------------------------------------------------------------
ParameterChangeTransfer::~ParameterChangeTransfer ()
{
	setMaxParameters (0);
}

//-----------------------------------------------------------------------------
void ParameterChangeTransfer::setMaxParameters (int32 maxParameters)
{
	// reserve memory for twice the amount of all parameters
	int32 newSize = maxParameters * 2;
	if (size != newSize)
	{
		if (changes)
			delete [] changes;
		changes = nullptr;
		size = newSize;
		if (size > 0)
			changes = new ParameterChange [size];
	}
}

//-----------------------------------------------------------------------------
void ParameterChangeTransfer::addChange (ParamID pid, ParamValue value, int32 sampleOffset)
{
	if (changes)
	{
		changes[writeIndex].id = pid;
		changes[writeIndex].value = value;
		changes[writeIndex].sampleOffset = sampleOffset;

		int32 newWriteIndex = writeIndex + 1;
		if (newWriteIndex >= size)
			newWriteIndex = 0;
		if (readIndex != newWriteIndex)
			writeIndex = newWriteIndex;
	}
}

//-----------------------------------------------------------------------------
bool ParameterChangeTransfer::getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset)
{
	if (!changes)
		return false;

	int32 currentWriteIndex = writeIndex;
	if (readIndex != currentWriteIndex)
	{
		pid = changes [readIndex].id;
		value = changes [readIndex].value;
		sampleOffset = changes [readIndex].sampleOffset;

		int32 newReadIndex = readIndex + 1;
		if (newReadIndex >= size)
			newReadIndex = 0;
		readIndex = newReadIndex;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesTo (ParameterChanges& dest)
{
	ParamID pid;
	ParamValue value;
	int32 sampleOffset;
	int32 index;

	while (getNextChange (pid, value, sampleOffset))
	{
		IParamValueQueue* queue = dest.addParameterData (pid, index);
		if (queue)
		{
			queue->addPoint (sampleOffset, value, index);
		}
	}
}

//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesFrom (ParameterChanges& source)
{
	ParamValue value;
	int32 sampleOffset;
	for (int32 i = 0; i < source.getParameterCount (); i++)
	{
		IParamValueQueue* queue = source.getParameterData (i);
		if (queue)
		{
			for (int32 j = 0; j < queue->getPointCount (); j++)
			{
				if (queue->getPoint (j, sampleOffset, value) == kResultTrue)
				{
					addChange (queue->getParameterId (), value, sampleOffset);
				}
			}
		}
	}
}

//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/parameterchanges.h
// Created by  : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <vector>

namespace Steinberg {
namespace Vst {

//------------------------------------------------------------------------
/** Implementation's example of IParamValueQueue - not threadsave!.
\ingroup hostingBase
*/
class ParameterValueQueue : public IParamValueQueue
{
public:
	//------------------------------------------------------------------------
	ParameterValueQueue (ParamID paramID);
	virtual ~ParameterValueQueue ();

	ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return paramID; }
	int32 PLUGIN_API getPointCount () SMTG_OVERRIDE;
	tresult PLUGIN_API getPoint (int32 index, int32& sampleOffset, ParamValue& value) SMTG_OVERRIDE;
	tresult PLUGIN_API addPoint (int32 sampleOffset, ParamValue value, int32& index) SMTG_OVERRIDE;

	void setParamID (ParamID pID) {paramID = pID;}
	void clear ();
	//------------------------------------------------------------------------
	DECLARE_FUNKNOWN_METHODS
protected:
	ParamID paramID;

	struct ParameterQueueValue
	{
		ParameterQueueValue (ParamValue value, int32 sampleOffset) : value (value), sampleOffset (sampleOffset) {}
		ParamValue value;
		int32 sampleOffset;
	};
	std::vector<ParameterQueueValue> values;
};

//------------------------------------------------------------------------
/** Implementation's example of IParameterChanges - not threadsave!.
\ingroup hostingBase
*/
class ParameterChanges : public IParameterChanges
{
public:
	//------------------------------------------------------------------------
	ParameterChanges (int32 maxParameters = 0);
	virtual ~ParameterChanges ();

	void clearQueue ();
	void setMaxParameters (int32 maxParameters);

	//---IParameterChanges-----------------------------
	int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE;
	IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE;
	IParamValueQueue* PLUGIN_API addParameterData (const ParamID& pid, int32& index) SMTG_OVERRIDE;

	//------------------------------------------------------------------------
	DECLARE_FUNKNOWN_METHODS
protected:
	std::vector<ParameterValueQueue*> queues;
	int32 usedQueueCount;
};

//------------------------------------------------------------------------
/** Ring buffer for transferring parameter changes from a writer to a read thread .
\ingroup hostingBase
*/
class ParameterChangeTransfer
{
public:
	//------------------------------------------------------------------------
	ParameterChangeTransfer (int32 maxParameters = 0);
	virtual ~ParameterChangeTransfer ();

	void setMaxParameters (int32 maxParameters);

	void addChange (ParamID pid, ParamValue value, int32 sampleOffset);
	bool getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset);

	void transferChangesTo (ParameterChanges& dest);
	void transferChangesFrom (ParameterChanges& source);

	void removeChanges () { writeIndex = readIndex; }

	//------------------------------------------------------------------------
protected:
	struct ParameterChange
	{
		ParamID id;
		ParamValue value;
		int32 sampleOffset;
	};
	int32 size;
	ParameterChange* changes;

	volatile int32 readIndex;
	volatile int32 writeIndex;
};

//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : plugprovider.h
// Created by  : Steinberg, 04/2005
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "base/source/fobject.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/testsuite/vsttestsuite.h" // for IPlugProvider

namespace Steinberg {
namespace Vst {

class IComponent;
class IEditController;
class ConnectionProxy;

//------------------------------------------------------------------------
/** Helper for creating and initializing component.
\ingroup Validator */
//------------------------------------------------------------------------
class PlugProvider : public FObject, public IPlugProvider
{
public:
	using ClassInfo = VST3::Hosting::ClassInfo;
	using PluginFactory = VST3::Hosting::PluginFactory;

	//--- ---------------------------------------------------------------------
	PlugProvider (const PluginFactory& factory, ClassInfo info, bool plugIsGlobal = true);
	virtual ~PlugProvider ();

	//--- from IPlugProvider ------------------
	IComponent* getComponent () SMTG_OVERRIDE;
	IEditController* getController () SMTG_OVERRIDE;
	tresult releasePlugIn (IComponent* component, IEditController* controller) SMTG_OVERRIDE;
	const char8* getSubCategories () const SMTG_OVERRIDE
	{
		return classInfo.subCategoriesString ().data ();
	}
	tresult getPluginUID (FUID& uid) const SMTG_OVERRIDE;

	//--- ---------------------------------------------------------------------
	OBJ_METHODS (PlugProvider, FObject)
	REFCOUNT_METHODS (FObject)
	DEF_INTERFACES_1 (IPlugProvider, FObject)
//------------------------------------------------------------------------
protected:
	bool setupPlugin (FUnknown* hostContext);
	bool connectComponents ();
	bool disconnectComponents ();
	void terminatePlugin ();

	PluginFactory factory;
	IPtr<IComponent> component;
	IPtr<IEditController> controller;
	ClassInfo classInfo;

	OPtr<ConnectionProxy> componentCP;
	OPtr<ConnectionProxy> controllerCP;

	bool plugIsGlobal;
};
}
} // namespaces


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    :
// Filename    : public.sdk/source/vst/hosting/uid.h
// Created by  : Steinberg, 08/2016
// Description : UID
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "optional.h"
#include "pluginterfaces/base/funknown.h"
#include <string>

//------------------------------------------------------------------------
namespace VST3 {

//------------------------------------------------------------------------
struct UID
{
#if defined(WINDOWS) && WINDOWS == 1
	static constexpr bool defaultComFormat = true;
#else
	static constexpr bool defaultComFormat = false;
#endif

	using TUID = Steinberg::TUID;

	constexpr UID () noexcept = default;
	UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat = defaultComFormat)
	noexcept;
	UID (const TUID& uid) noexcept;
	UID (const UID& uid) noexcept;
	UID& operator= (const UID& uid) noexcept;
	UID& operator= (const TUID& uid) noexcept;

	constexpr const TUID& data () const noexcept;
	constexpr size_t size () const noexcept;

	std::string toString (bool comFormat = defaultComFormat) const noexcept;
	static Optional<UID> fromString (const std::string& str,
	                                 bool comFormat = defaultComFormat) noexcept;

	static UID fromTUID (const TUID _uid) noexcept;
//------------------------------------------------------------------------
private:
	Steinberg::TUID _data {};

	struct GUID
	{
		uint32_t Data1;
		uint16_t Data2;
		uint16_t Data3;
		uint8_t Data4[8];
	};
};

//------------------------------------------------------------------------
inline bool operator== (const UID& uid1, const UID& uid2)
{
	const uint64_t* p1 = reinterpret_cast<const uint64_t*> (uid1.data ());
	const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid2.data ());
	return p1[0] == p2[0] && p1[1] == p2[1];
}

//------------------------------------------------------------------------
inline bool operator!= (const UID& uid1, const UID& uid2)
{
	return !(uid1 == uid2);
}

//------------------------------------------------------------------------
inline bool operator< (const UID& uid1, const UID& uid2)
{
	const uint64_t* p1 = reinterpret_cast<const uint64_t*> (uid1.data ());
	const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid2.data ());
	return (p1[0] < p2[0]) && (p1[1] < p2[1]);
}

//------------------------------------------------------------------------
inline UID::UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat) noexcept
{
	if (comFormat)
	{
		_data[0] = static_cast<int8_t> ((l1 & 0x000000FF));
		_data[1] = static_cast<int8_t> ((l1 & 0x0000FF00) >> 8);
		_data[2] = static_cast<int8_t> ((l1 & 0x00FF0000) >> 16);
		_data[3] = static_cast<int8_t> ((l1 & 0xFF000000) >> 24);
		_data[4] = static_cast<int8_t> ((l2 & 0x00FF0000) >> 16);
		_data[5] = static_cast<int8_t> ((l2 & 0xFF000000) >> 24);
		_data[6] = static_cast<int8_t> ((l2 & 0x000000FF));
		_data[7] = static_cast<int8_t> ((l2 & 0x0000FF00) >> 8);
		_data[8] = static_cast<int8_t> ((l3 & 0xFF000000) >> 24);
		_data[9] = static_cast<int8_t> ((l3 & 0x00FF0000) >> 16);
		_data[10] = static_cast<int8_t> ((l3 & 0x0000FF00) >> 8);
		_data[11] = static_cast<int8_t> ((l3 & 0x000000FF));
		_data[12] = static_cast<int8_t> ((l4 & 0xFF000000) >> 24);
		_data[13] = static_cast<int8_t> ((l4 & 0x00FF0000) >> 16);
		_data[14] = static_cast<int8_t> ((l4 & 0x0000FF00) >> 8);
		_data[15] = static_cast<int8_t> ((l4 & 0x000000FF));
	}
	else
	{
		_data[0] = static_cast<int8_t> ((l1 & 0xFF000000) >> 24);
		_data[1] = static_cast<int8_t> ((l1 & 0x00FF0000) >> 16);
		_data[2] = static_cast<int8_t> ((l1 & 0x0000FF00) >> 8);
		_data[3] = static_cast<int8_t> ((l1 & 0x000000FF));
		_data[4] = static_cast<int8_t> ((l2 & 0xFF000000) >> 24);
		_data[5] = static_cast<int8_t> ((l2 & 0x00FF0000) >> 16);
		_data[6] = static_cast<int8_t> ((l2 & 0x0000FF00) >> 8);
		_data[7] = static_cast<int8_t> ((l2 & 0x000000FF));
		_data[8] = static_cast<int8_t> ((l3 & 0xFF000000) >> 24);
		_data[9] = static_cast<int8_t> ((l3 & 0x00FF0000) >> 16);
		_data[10] = static_cast<int8_t> ((l3 & 0x0000FF00) >> 8);
		_data[11] = static_cast<int8_t> ((l3 & 0x000000FF));
		_data[12] = static_cast<int8_t> ((l4 & 0xFF000000) >> 24);
		_data[13] = static_cast<int8_t> ((l4 & 0x00FF0000) >> 16);
		_data[14] = static_cast<int8_t> ((l4 & 0x0000FF00) >> 8);
		_data[15] = static_cast<int8_t> ((l4 & 0x000000FF));
	}
}

//------------------------------------------------------------------------
inline UID::UID (const TUID& uid) noexcept
{
	*this = uid;
}

//------------------------------------------------------------------------
inline UID::UID (const UID& uid) noexcept
{
	*this = uid;
}

//------------------------------------------------------------------------
inline UID& UID::operator= (const UID& uid) noexcept
{
	*this = uid.data ();
	return *this;
}

//------------------------------------------------------------------------
inline UID& UID::operator= (const TUID& uid) noexcept
{
	uint64_t* p1 = reinterpret_cast<uint64_t*> (_data);
	const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid);
	p1[0] = p2[0];
	p1[1] = p2[1];
	return *this;
}

//------------------------------------------------------------------------
inline constexpr auto UID::data () const noexcept -> const TUID&
{
	return _data;
}

//------------------------------------------------------------------------
inline constexpr size_t UID::size () const noexcept
{
	return sizeof (TUID);
}

//------------------------------------------------------------------------
inline std::string UID::toString (bool comFormat) const noexcept
{
	std::string result;
	result.reserve (32);
	if (comFormat)
	{
		const auto& g = reinterpret_cast<const GUID*> (_data);

		char tmp[21] {};
		sprintf (tmp, "%08X%04X%04X", g->Data1, g->Data2, g->Data3);
		result = tmp;

		for (uint32_t i = 0; i < 8; ++i)
		{
			char s[3] {};
			sprintf (s, "%02X", g->Data4[i]);
			result += s;
		}
	}
	else
	{
		for (uint32_t i = 0; i < 16; ++i)
		{
			char s[3] {};
			sprintf (s, "%02X", static_cast<uint8_t> (_data[i]));
			result += s;
		}
	}
	return result;
}

//------------------------------------------------------------------------
inline Optional<UID> UID::fromString (const std::string& str, bool comFormat) noexcept
{
	if (str.length () != 32)
		return {};
	// TODO: this is a copy from FUID. there are no input validation checks !!!
	if (comFormat)
	{
		TUID uid {};
		GUID g;
		char s[33];

		strcpy (s, str.data ());
		s[8] = 0;
		sscanf (s, "%x", &g.Data1);
		strcpy (s, str.data () + 8);
		s[4] = 0;
		sscanf (s, "%hx", &g.Data2);
		strcpy (s, str.data () + 12);
		s[4] = 0;
		sscanf (s, "%hx", &g.Data3);

		memcpy (uid, &g, 8);

		for (uint32_t i = 8; i < 16; ++i)
		{
			char s2[3] {};
			s2[0] = str[i * 2];
			s2[1] = str[i * 2 + 1];

			int32_t d = 0;
			sscanf (s2, "%2x", &d);
			uid[i] = static_cast<char> (d);
		}
		return {uid};
	}
	else
	{
		TUID uid {};
		for (uint32_t i = 0; i < 16; ++i)
		{
			char s[3] {};
			s[0] = str[i * 2];
			s[1] = str[i * 2 + 1];

			int32_t d = 0;
			sscanf (s, "%2x", &d);
			uid[i] = static_cast<char> (d);
		}
		return {uid};
	}
	return {};
}

//------------------------------------------------------------------------
inline UID UID::fromTUID (const TUID _uid) noexcept
{
	UID result;

	uint64_t* p1 = reinterpret_cast<uint64_t*> (result._data);
	const uint64_t* p2 = reinterpret_cast<const uint64_t*> (_uid);
	p1[0] = p2[0];
	p1[1] = p2[1];

	return result;
}

//------------------------------------------------------------------------
} // VST3


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : plugprovider.cpp
// Created by  : Steinberg, 08/2016
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "plugprovider.h"

#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"

namespace Steinberg {
extern FUnknown* gStandardPluginContext;

FUnknown* getPluginContext ()
{
	return gStandardPluginContext;
}
}

#include <cstdio>
#include <iostream>

static std::ostream* errorStream = &std::cout;

namespace Steinberg {
namespace Vst {

//------------------------------------------------------------------------
class ConnectionProxy : public FObject, public IConnectionPoint
{
public:
	ConnectionProxy (IConnectionPoint* srcConnection);
	virtual ~ConnectionProxy ();

	//--- from IConnectionPoint
	tresult PLUGIN_API connect (IConnectionPoint* other) override;
	tresult PLUGIN_API disconnect (IConnectionPoint* other) override;
	tresult PLUGIN_API notify (IMessage* message) override;

	bool disconnect ();

	OBJ_METHODS (ConnectionProxy, FObject)
	REFCOUNT_METHODS (FObject)
	DEF_INTERFACES_1 (IConnectionPoint, FObject)

protected:
	IPtr<IConnectionPoint> srcConnection;
	IPtr<IConnectionPoint> dstConnection;
};

//------------------------------------------------------------------------
ConnectionProxy::ConnectionProxy (IConnectionPoint* srcConnection)
: srcConnection (srcConnection) // share it
{
}

//------------------------------------------------------------------------
ConnectionProxy::~ConnectionProxy ()
{
}

//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::connect (IConnectionPoint* other)
{
	if (other == nullptr)
		return kInvalidArgument;
	if (dstConnection)
		return kResultFalse;

	dstConnection = other; // share it
	tresult res = srcConnection->connect (this);
	if (res != kResultTrue)
		dstConnection = nullptr;
	return res;
}

//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::disconnect (IConnectionPoint* other)
{
	if (!other)
		return kInvalidArgument;

	if (other == dstConnection)
	{
		if (srcConnection)
			srcConnection->disconnect (this);
		dstConnection = nullptr;
		return kResultTrue;
	}

	return kInvalidArgument;
}

//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::notify (IMessage* message)
{
	if (dstConnection)
	{
		// TODO we should test if we are in UI main thread else postpone the message
		return dstConnection->notify (message);
	}
	return kResultFalse;
}

//------------------------------------------------------------------------
bool ConnectionProxy::disconnect ()
{
	return disconnect (dstConnection) == kResultTrue;
}

//------------------------------------------------------------------------
// PlugProvider
//------------------------------------------------------------------------
PlugProvider::PlugProvider (const PluginFactory& factory, ClassInfo classInfo, bool plugIsGlobal)
: factory (factory)
, component (nullptr)
, controller (nullptr)
, classInfo (classInfo)
, plugIsGlobal (plugIsGlobal)
{
	if (plugIsGlobal)
	{
		setupPlugin (getPluginContext ());
	}
}

//------------------------------------------------------------------------
PlugProvider::~PlugProvider ()
{
	terminatePlugin ();
}

//------------------------------------------------------------------------
IComponent* PlugProvider::getComponent ()
{
	if (!component)
		setupPlugin (getPluginContext ());

	if (component)
		component->addRef ();

	return component;
}

//------------------------------------------------------------------------
IEditController* PlugProvider::getController ()
{
	if (controller)
		controller->addRef ();

	// 'iController == 0' is allowed! In this case the plug has no controller
	return controller;
}

//------------------------------------------------------------------------
tresult PlugProvider::getPluginUID (FUID& uid) const
{
	uid = classInfo.ID ().data ();
	return kResultOk;
}

//------------------------------------------------------------------------
tresult PlugProvider::releasePlugIn (IComponent* iComponent, IEditController* iController)
{
	if (iComponent)
		iComponent->release ();

	if (iController)
		iController->release ();

	if (!plugIsGlobal)
	{
		terminatePlugin ();
	}

	return kResultOk;
}

//------------------------------------------------------------------------
bool PlugProvider::setupPlugin (FUnknown* hostContext)
{
	bool res = false;

	//---create Plug-in here!--------------
	// create its component part
	component = factory.createInstance<IComponent> (classInfo.ID ());
	if (component)
	{
		// initialize the component with our context
		res = (component->initialize (hostContext) == kResultOk);

		// try to create the controller part from the component
		// (for Plug-ins which did not succeed to separate component from controller)
		if (component->queryInterface (IEditController::iid, (void**)&controller) != kResultTrue)
		{
			TUID controllerCID;

			// ask for the associated controller class ID
			if (component->getControllerClassId (controllerCID) == kResultTrue)
			{
				// create its controller part created from the factory
				controller = factory.createInstance<IEditController> (VST3::UID (controllerCID));
				if (controller)
				{
					// initialize the component with our context
					res = (controller->initialize (hostContext) == kResultOk);
				}
			}
		}
	}
	else if (errorStream)
	{
		*errorStream << "Failed to create instance of " << classInfo.name () << "!\n";
	}

	if (res)
		connectComponents ();

	return res;
}

//------------------------------------------------------------------------
bool PlugProvider::connectComponents ()
{
	if (!component || !controller)
		return false;

	FUnknownPtr<IConnectionPoint> compICP (component);
	FUnknownPtr<IConnectionPoint> contrICP (controller);
	if (!compICP || !contrICP)
		return false;

	bool res = false;

	componentCP = NEW ConnectionProxy (compICP);
	controllerCP = NEW ConnectionProxy (contrICP);

	if (componentCP->connect (contrICP) != kResultTrue)
	{
		// TODO: Alert or what for non conformant plugin ?
	}
	else
	{
		if (controllerCP->connect (compICP) != kResultTrue)
		{
			// TODO: Alert or what for non conformant plugin ?
		}
		else
			res = true;
	}
	return res;
}

//------------------------------------------------------------------------
bool PlugProvider::disconnectComponents ()
{
	if (!componentCP || !controllerCP)
		return false;

	bool res = componentCP->disconnect ();
	res &= controllerCP->disconnect ();

	componentCP = nullptr;
	controllerCP = nullptr;

	return res;
}

//------------------------------------------------------------------------
void PlugProvider::terminatePlugin ()
{
	disconnectComponents ();

	bool controllerIsComponent = false;
	if (component)
	{
		controllerIsComponent = FUnknownPtr<IEditController> (component).getInterface () != 0;
		component->terminate ();
	}

	if (controller && controllerIsComponent == false)
		controller->terminate ();

	component = nullptr;
	controller = nullptr;
}
}
} // namespaces


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    :
// Filename    : public.sdk/source/vst/hosting/module_win32.cpp
// Created by  : Steinberg, 08/2016
// Description : hosting module classes (win32 implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "stringconvert.h"
#include "module.h"
#include <Windows.h>
#include <Shlobj.h>
#include <algorithm>
#include <experimental/filesystem>

#pragma comment(lib, "Shell32")

//------------------------------------------------------------------------
extern "C" {
typedef bool (PLUGIN_API* InitModuleFunc) ();
typedef bool (PLUGIN_API* ExitModuleFunc) ();
}

using namespace std::experimental;

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
class Win32Module : public Module
{
public:
	template <typename T>
	T getFunctionPointer (const char* name)
	{
		return reinterpret_cast<T> (GetProcAddress (module, name));
	}

	~Win32Module ()
	{
		factory = PluginFactory (nullptr);

		if (module)
		{
			if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
				dllExit ();

			FreeLibrary ((HMODULE)module);
		}
	}

	bool load (const std::string& inPath, std::string& errorDescription) override
	{
		auto wideStr = StringConvert::convert (inPath);
		module = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
		if (!module)
		{
			// TODO: is there an API to get more information about the failure ?
			errorDescription = "LoadLibray failed.";
			return false;
		}

		auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
		auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
		if (!factoryProc)
		{
			errorDescription = "dll does not export the required 'GetPluginFactory' function";
			return false;
		}
		if (dllEntry && !dllEntry ())
		{
			errorDescription = "Calling 'InitDll' failed";
			return false;
		}
		auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (factoryProc ());
		if (!f)
		{
			errorDescription = "Calling 'GetPluginFactory' returned nullptr";
			return false;
		}
		factory = PluginFactory (f);
		return true;
	}

	HINSTANCE module {nullptr};
};

//------------------------------------------------------------------------
Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
{
	PWSTR wideStr {};
	if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
		return {};
	return StringConvert::convert (wideStr);
}

//------------------------------------------------------------------------
void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList)
{
	for (auto& p : filesystem::recursive_directory_iterator (path))
	{
		if (p.path ().extension () == ext)
		{
			pathList.push_back (p.path ().generic_u8string ());
		}
	}
}

//------------------------------------------------------------------------
void findModules (const std::string& path, Module::PathList& pathList)
{
	findFilesWithExt (path, ".vst3", pathList);
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
	auto module = std::make_shared<Win32Module> ();
	if (module->load (path, errorDescription))
	{
		module->path = path;
		auto it = std::find_if (path.rbegin (), path.rend (),
		                        [] (const std::string::value_type& c) { return c == '/'; });
		if (it != path.rend ())
			module->name = {it.base (), path.end ()};
		return module;
	}
	return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
	// find plug-ins located in common/VST3
	PathList list;
	if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
	{
		filesystem::path p (*knownFolder);
		p.append ("VST3");
		findModules (p.generic_u8string (), list);
	}

	// find plug-ins located in VST3 (application folder)
	WCHAR modulePath[MAX_PATH + 1];
	GetModuleFileNameW (nullptr, modulePath, MAX_PATH);
	auto appPath = StringConvert::convert (modulePath);
	filesystem::path path (appPath);
	path = path.parent_path ();
	path = path.append ("VST3");
	findModules (path.generic_u8string (), list);

	return list;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/hostclasses.h
// Created by  : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "pluginterfaces/vst/ivsthostapplication.h"

#include "base/source/fstring.h"

#include <map>

namespace Steinberg {
namespace Vst {

//------------------------------------------------------------------------
/** Implementation's example of IHostApplication.
\ingroup hostingBase
*/
class HostApplication : public IHostApplication
{
public:
	HostApplication () { FUNKNOWN_CTOR }
	virtual ~HostApplication () { FUNKNOWN_DTOR }

	tresult PLUGIN_API getName (String128 name) SMTG_OVERRIDE;
	tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) SMTG_OVERRIDE;

	DECLARE_FUNKNOWN_METHODS
};

class HostAttribute;
//------------------------------------------------------------------------
/** Implementation's example of IAttributeList.
\ingroup hostingBase
*/
class HostAttributeList : public IAttributeList
{
public:
	HostAttributeList ();
	virtual ~HostAttributeList ();

	tresult PLUGIN_API setInt (AttrID aid, int64 value) SMTG_OVERRIDE;
	tresult PLUGIN_API getInt (AttrID aid, int64& value) SMTG_OVERRIDE;
	tresult PLUGIN_API setFloat (AttrID aid, double value) SMTG_OVERRIDE;
	tresult PLUGIN_API getFloat (AttrID aid, double& value) SMTG_OVERRIDE;
	tresult PLUGIN_API setString (AttrID aid, const TChar* string) SMTG_OVERRIDE;
	tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 size) SMTG_OVERRIDE;
	tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 size) SMTG_OVERRIDE;
	tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& size) SMTG_OVERRIDE;

	DECLARE_FUNKNOWN_METHODS
protected:
	void removeAttrID (AttrID aid);
	std::map<String, HostAttribute*> list;
};

//------------------------------------------------------------------------
/** Implementation's example of IMessage.
\ingroup hostingBase
*/
class HostMessage : public IMessage
{
public:
	HostMessage ();
	virtual ~HostMessage ();

	const char* PLUGIN_API getMessageID () SMTG_OVERRIDE;
	void PLUGIN_API setMessageID (const char* messageID) SMTG_OVERRIDE;
	IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE;

	DECLARE_FUNKNOWN_METHODS
protected:
	char* messageId;
	HostAttributeList* attributeList;
};

//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/hostclasses.cpp
// Created by  : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example implementations for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "hostclasses.h"

#include <algorithm>

namespace Steinberg {
namespace Vst {

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::getName (String128 name)
{
	String str ("My VST3 HostApplication");
	str.copyTo16 (name, 0, 127);
	return kResultTrue;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj)
{
	FUID classID (cid);
	FUID interfaceID (_iid);
	if (classID == IMessage::iid && interfaceID == IMessage::iid)
	{
		*obj = new HostMessage;
		return kResultTrue;
	}
	else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid)
	{
		*obj = new HostAttributeList;
		return kResultTrue;
	}
	*obj = 0;
	return kResultFalse;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj)
{
	QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication)
	QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication)
	*obj = 0;
	return kResultFalse;
}

//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::addRef ()
{
	return 1;
}

//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::release ()
{
	return 1;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid)
//-----------------------------------------------------------------------------
HostMessage::HostMessage () : messageId (0), attributeList (0)
{
	FUNKNOWN_CTOR
}

//-----------------------------------------------------------------------------
HostMessage::~HostMessage ()
{
	setMessageID (0);
	if (attributeList)
		attributeList->release ();
	FUNKNOWN_DTOR
}

//-----------------------------------------------------------------------------
const char* PLUGIN_API HostMessage::getMessageID ()
{
	return messageId;
}

//-----------------------------------------------------------------------------
void PLUGIN_API HostMessage::setMessageID (const char* mid)
{
	if (messageId)
		delete[] messageId;
	messageId = 0;
	if (mid)
	{
		size_t len = strlen (mid) + 1;
		messageId = new char[len];
		strcpy (messageId, mid);
	}
}

//-----------------------------------------------------------------------------
IAttributeList* PLUGIN_API HostMessage::getAttributes ()
{
	if (!attributeList)
		attributeList = new HostAttributeList;
	return attributeList;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
class HostAttribute
{
public:
	enum Type
	{
		kInteger,
		kFloat,
		kString,
		kBinary
	};

	HostAttribute (int64 value) : size (0), type (kInteger) { v.intValue = value; }
	HostAttribute (double value) : size (0), type (kFloat) { v.floatValue = value; }
	HostAttribute (const TChar* value, uint32 size) : size (size), type (kString)
	{
		v.stringValue = new TChar[size];
		memcpy (v.stringValue, value, size * sizeof (TChar));
	}
	HostAttribute (const void* value, uint32 size) : size (size), type (kBinary)
	{
		v.binaryValue = new char[size];
		memcpy (v.binaryValue, value, size);
	}
	~HostAttribute ()
	{
		if (size)
			delete[] v.binaryValue;
	}

	int64 intValue () const { return v.intValue; }
	double floatValue () const { return v.floatValue; }
	const TChar* stringValue (uint32& stringSize)
	{
		stringSize = size;
		return v.stringValue;
	}
	const void* binaryValue (uint32& binarySize)
	{
		binarySize = size;
		return v.binaryValue;
	}

	Type getType () const { return type; }

protected:
	union v
	{
		int64 intValue;
		double floatValue;
		TChar* stringValue;
		char* binaryValue;
	} v;
	uint32 size;
	Type type;
};

typedef std::map<String, HostAttribute*>::iterator mapIterator;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid)
//-----------------------------------------------------------------------------
HostAttributeList::HostAttributeList ()
{
	FUNKNOWN_CTOR
}

//-----------------------------------------------------------------------------
HostAttributeList::~HostAttributeList ()
{
	std::map<String, HostAttribute*>::reverse_iterator it = list.rbegin ();
	while (it != list.rend ())
	{
		delete it->second;
		it++;
	}
	FUNKNOWN_DTOR
}

//-----------------------------------------------------------------------------
void HostAttributeList::removeAttrID (AttrID aid)
{
	mapIterator it = list.find (aid);
	if (it != list.end ())
	{
		delete it->second;
		list.erase (it);
	}
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value)
{
	removeAttrID (aid);
	list[aid] = new HostAttribute (value);
	return kResultTrue;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value)
{
	mapIterator it = list.find (aid);
	if (it != list.end () && it->second)
	{
		value = it->second->intValue ();
		return kResultTrue;
	}
	return kResultFalse;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value)
{
	removeAttrID (aid);
	list[aid] = new HostAttribute (value);
	return kResultTrue;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value)
{
	mapIterator it = list.find (aid);
	if (it != list.end () && it->second)
	{
		value = it->second->floatValue ();
		return kResultTrue;
	}
	return kResultFalse;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string)
{
	removeAttrID (aid);
	list[aid] = new HostAttribute (string, String (const_cast<TChar*> (string)).length ());
	return kResultTrue;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 size)
{
	mapIterator it = list.find (aid);
	if (it != list.end () && it->second)
	{
		uint32 stringSize = 0;
		const TChar* _string = it->second->stringValue (stringSize);
		memcpy (string, _string, std::min<uint32> (stringSize, size) * sizeof (TChar));
		return kResultTrue;
	}
	return kResultFalse;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 size)
{
	removeAttrID (aid);
	list[aid] = new HostAttribute (data, size);
	return kResultTrue;
}

//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& size)
{
	mapIterator it = list.find (aid);
	if (it != list.end () && it->second)
	{
		data = it->second->binaryValue (size);
		return kResultTrue;
	}
	size = 0;
	return kResultFalse;
}
}
} // namespace


text:
//-----------------------------------------------------------------------------
// Project     : VST SDK
//
// Category    : Helpers
// Filename    : public.sdk/source/vst/hosting/module.cpp
// Created by  : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "module.h"
#include "stringconvert.h"
#include <sstream>
#include <utility>

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept
{
	*this = std::move (other);
}

//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept
{
	info = std::move (other.info);
	other.info = {};
	return *this;
}

//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept
{
	info = std::move (other);
	other = {};
	return *this;
}

//------------------------------------------------------------------------
std::string FactoryInfo::vendor () const noexcept
{
	return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize);
}

//------------------------------------------------------------------------
std::string FactoryInfo::url () const noexcept
{
	return StringConvert::convert (info.url, PFactoryInfo::kURLSize);
}

//------------------------------------------------------------------------
std::string FactoryInfo::email () const noexcept
{
	return StringConvert::convert (info.email, PFactoryInfo::kEmailSize);
}

//------------------------------------------------------------------------
Steinberg::int32 FactoryInfo::flags () const noexcept
{
	return info.flags;
}

//------------------------------------------------------------------------
bool FactoryInfo::classesDiscardable () const noexcept
{
	return (info.flags & PFactoryInfo::kClassesDiscardable) != 0;
}

//------------------------------------------------------------------------
bool FactoryInfo::licenseCheck () const noexcept
{
	return (info.flags & PFactoryInfo::kLicenseCheck) != 0;
}

//------------------------------------------------------------------------
bool FactoryInfo::componentNonDiscardable () const noexcept
{
	return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory)
{
}

//------------------------------------------------------------------------
void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept
{
	if (auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory))
		f->setHostContext (context);
}

//------------------------------------------------------------------------
FactoryInfo PluginFactory::info () const noexcept
{
	Steinberg::PFactoryInfo i;
	factory->getFactoryInfo (&i);
	return FactoryInfo (std::move (i));
}

//------------------------------------------------------------------------
uint32_t PluginFactory::classCount () const noexcept
{
	auto count = factory->countClasses ();
	assert (count >= 0);
	return static_cast<uint32_t> (count);
}

//------------------------------------------------------------------------
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
	auto count = classCount ();
	ClassInfos classes;
	classes.reserve (count);
	auto f3 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory);
	auto f2 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory2> (factory);
	Steinberg::PClassInfo ci;
	Steinberg::PClassInfo2 ci2;
	Steinberg::PClassInfoW ci3;
	for (uint32_t i = 0; i < count; ++i)
	{
		if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
			classes.emplace_back (ci3);
		else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
			classes.emplace_back (ci2);
		else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
			classes.emplace_back (ci);
		auto& classInfo = classes.back ();
		if (classInfo.vendor ().empty ())
			classInfo.get ().vendor = info ().vendor ();
	}
	return classes;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
const UID& ClassInfo::ID () const noexcept
{
	return data.classID;
}

//------------------------------------------------------------------------
int32_t ClassInfo::cardinality () const noexcept
{
	return data.cardinality;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::category () const noexcept
{
	return data.category;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::name () const noexcept
{
	return data.name;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::vendor () const noexcept
{
	return data.vendor;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::version () const noexcept
{
	return data.version;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::sdkVersion () const noexcept
{
	return data.sdkVersion;
}

//------------------------------------------------------------------------
const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept
{
	return data.subCategories;
}

//------------------------------------------------------------------------
Steinberg::uint32 ClassInfo::classFlags () const noexcept
{
	return data.classFlags;
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo& info) noexcept
{
	data.classID = info.cid;
	data.cardinality = info.cardinality;
	data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
	data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo2& info) noexcept
{
	data.classID = info.cid;
	data.cardinality = info.cardinality;
	data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
	data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
	data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
	data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
	data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
	parseSubCategories (
	    StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
	data.classFlags = info.classFlags;
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfoW& info) noexcept
{
	data.classID = info.cid;
	data.cardinality = info.cardinality;
	data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
	data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
	data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
	data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
	data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
	parseSubCategories (
	    StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
	data.classFlags = info.classFlags;
}

//------------------------------------------------------------------------
void ClassInfo::parseSubCategories (const std::string& str) noexcept
{
	std::stringstream stream (str);
	std::string item;
	while (std::getline (stream, item, '|'))
		data.subCategories.emplace_back (move (item));
}

//------------------------------------------------------------------------
std::string ClassInfo::subCategoriesString () const noexcept
{
	std::string result;
	if (data.subCategories.empty ())
		return result;
	result = data.subCategories[0];
	for (auto index = 1u; index < data.subCategories.size (); ++index)
		result += "|" + data.subCategories[index];
	return result;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3