| 
							- // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
 - // SPDX-License-Identifier: GPL-2.0-or-later
 - 
 - #include "CarlaUtils.h"
 - 
 - #include "CarlaBackendUtils.hpp"
 - #include "CarlaBinaryUtils.hpp"
 - #include "CarlaJuceUtils.hpp"
 - #include "CarlaPipeUtils.hpp"
 - #include "CarlaSha1Utils.hpp"
 - #include "CarlaTimeUtils.hpp"
 - 
 - #include "water/files/File.h"
 - #include "water/files/FileInputStream.h"
 - #include "water/threads/ChildProcess.h"
 - #include "water/text/StringArray.h"
 - 
 - namespace CB = CARLA_BACKEND_NAMESPACE;
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 - 
 - #ifndef CARLA_OS_WIN
 - static water::String findWinePrefix(const water::String filename, const int recursionLimit = 10)
 - {
 -     if (recursionLimit == 0 || filename.length() < 5 || ! filename.contains("/"))
 -         return "";
 - 
 -     const water::String path(filename.upToLastOccurrenceOf("/", false, false));
 - 
 -     if (water::File(path + "/dosdevices").isDirectory())
 -         return path;
 - 
 -     return findWinePrefix(path, recursionLimit-1);
 - }
 - #endif
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 - 
 - static const char* const gPluginsDiscoveryNullCharPtr = "";
 - 
 - _CarlaPluginDiscoveryMetadata::_CarlaPluginDiscoveryMetadata() noexcept
 -     : name(gPluginsDiscoveryNullCharPtr),
 -       maker(gPluginsDiscoveryNullCharPtr),
 -       category(CB::PLUGIN_CATEGORY_NONE),
 -       hints(0x0) {}
 - 
 - _CarlaPluginDiscoveryIO::_CarlaPluginDiscoveryIO() noexcept
 -     : audioIns(0),
 -       audioOuts(0),
 -       cvIns(0),
 -       cvOuts(0),
 -       midiIns(0),
 -       midiOuts(0),
 -       parameterIns(0),
 -       parameterOuts(0) {}
 - 
 - _CarlaPluginDiscoveryInfo::_CarlaPluginDiscoveryInfo() noexcept
 -     : btype(CB::BINARY_NONE),
 -       ptype(CB::PLUGIN_NONE),
 -       filename(gPluginsDiscoveryNullCharPtr),
 -       label(gPluginsDiscoveryNullCharPtr),
 -       uniqueId(0),
 -       metadata()  {}
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 - 
 - struct CarlaPluginDiscoveryOptions {
 -    #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
 -     struct {
 -         bool autoPrefix;
 -         CarlaString executable;
 -         CarlaString fallbackPrefix;
 -     } wine;
 -    #endif
 - 
 -     static CarlaPluginDiscoveryOptions& getInstance() noexcept
 -     {
 -         static CarlaPluginDiscoveryOptions instance;
 -         return instance;
 -     }
 - };
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 - 
 - class CarlaPluginDiscovery : private CarlaPipeServer
 - {
 - public:
 -     CarlaPluginDiscovery(const char* const discoveryTool,
 -                          const BinaryType btype,
 -                          const PluginType ptype,
 -                          const std::vector<water::File>&& binaries,
 -                          const CarlaPluginDiscoveryCallback discoveryCb,
 -                          const CarlaPluginCheckCacheCallback checkCacheCb,
 -                          void* const callbackPtr)
 -         : fBinaryType(btype),
 -           fPluginType(ptype),
 -           fDiscoveryCallback(discoveryCb),
 -           fCheckCacheCallback(checkCacheCb),
 -           fCallbackPtr(callbackPtr),
 -           fPluginPath(nullptr),
 -           fPluginsFoundInBinary(false),
 -           fBinaryIndex(0),
 -           fBinaryCount(static_cast<uint>(binaries.size())),
 -           fBinaries(binaries),
 -           fDiscoveryTool(discoveryTool),
 -           fLastMessageTime(0),
 -           fNextLabel(nullptr),
 -           fNextMaker(nullptr),
 -           fNextName(nullptr)
 -     {
 -         start();
 -     }
 - 
 -     CarlaPluginDiscovery(const char* const discoveryTool,
 -                          const BinaryType btype,
 -                          const PluginType ptype,
 -                          const CarlaPluginDiscoveryCallback discoveryCb,
 -                          const CarlaPluginCheckCacheCallback checkCacheCb,
 -                          void* const callbackPtr,
 -                          const char* const pluginPath = nullptr)
 -         : fBinaryType(btype),
 -           fPluginType(ptype),
 -           fDiscoveryCallback(discoveryCb),
 -           fCheckCacheCallback(checkCacheCb),
 -           fCallbackPtr(callbackPtr),
 -           fPluginPath(pluginPath != nullptr ? carla_strdup_safe(pluginPath) : nullptr),
 -           fPluginsFoundInBinary(false),
 -           fBinaryIndex(0),
 -           fBinaryCount(1),
 -           fDiscoveryTool(discoveryTool),
 -           fLastMessageTime(0),
 -           fNextLabel(nullptr),
 -           fNextMaker(nullptr),
 -           fNextName(nullptr)
 -     {
 -         start();
 -     }
 - 
 -     ~CarlaPluginDiscovery()
 -     {
 -         stopPipeServer(5000);
 -         std::free(fNextLabel);
 -         std::free(fNextMaker);
 -         std::free(fNextName);
 -         delete[] fPluginPath;
 -     }
 - 
 -     bool idle()
 -     {
 -         if (isPipeRunning())
 -         {
 -             idlePipe();
 - 
 -             // automatically skip a plugin if 30s passes without a reply
 -             const uint32_t timeNow = carla_gettime_ms();
 - 
 -             if (timeNow - fLastMessageTime < 30000)
 -                 return true;
 - 
 -             carla_stdout("Plugin took too long to respond, skipping...");
 -             stopPipeServer(1000);
 -         }
 - 
 -         // report binary as having no plugins
 -         if (fCheckCacheCallback != nullptr && !fPluginsFoundInBinary && !fBinaries.empty())
 -         {
 -             const water::File file(fBinaries[fBinaryIndex]);
 -             const water::String filename(file.getFullPathName());
 - 
 -             makeHash(file, filename);
 - 
 -             if (! fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
 -                 fDiscoveryCallback(fCallbackPtr, nullptr, fNextSha1Sum);
 -         }
 - 
 -         if (++fBinaryIndex == fBinaryCount)
 -             return false;
 - 
 -         start();
 -         return true;
 -     }
 - 
 -     void skip()
 -     {
 -         if (isPipeRunning())
 -             stopPipeServer(1000);
 -     }
 - 
 - protected:
 -     bool msgReceived(const char* const msg) noexcept
 -     {
 -         fLastMessageTime = carla_gettime_ms();
 - 
 -         if (std::strcmp(msg, "warning") == 0 || std::strcmp(msg, "error") == 0)
 -         {
 -             const char* text = nullptr;
 -             readNextLineAsString(text, false);
 -             carla_stdout("discovery: %s", text);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "init") == 0)
 -         {
 -             const char* _;
 -             readNextLineAsString(_, false);
 -             new (&fNextInfo) _CarlaPluginDiscoveryInfo();
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "end") == 0)
 -         {
 -             const char* _;
 -             readNextLineAsString(_, false);
 - 
 -             if (fNextInfo.label == nullptr)
 -                 fNextInfo.label = gPluginsDiscoveryNullCharPtr;
 - 
 -             if (fNextInfo.metadata.maker == nullptr)
 -                 fNextInfo.metadata.maker = gPluginsDiscoveryNullCharPtr;
 - 
 -             if (fNextInfo.metadata.name == nullptr)
 -                 fNextInfo.metadata.name = gPluginsDiscoveryNullCharPtr;
 - 
 -             if (fBinaries.empty())
 -             {
 -                 char* filename = nullptr;
 - 
 -                 if (fPluginType == CB::PLUGIN_LV2)
 -                 {
 -                     do {
 -                         const char* const slash = std::strchr(fNextLabel, CARLA_OS_SEP);
 -                         CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
 -                         filename = strdup(fNextLabel);
 -                         filename[slash - fNextLabel] = '\0';
 -                         fNextInfo.filename = filename;
 -                         fNextInfo.label = slash + 1;
 -                     } while (false);
 -                 }
 - 
 -                 fNextInfo.ptype = fPluginType;
 -                 fDiscoveryCallback(fCallbackPtr, &fNextInfo, nullptr);
 - 
 -                 std::free(filename);
 -             }
 -             else
 -             {
 -                 CARLA_SAFE_ASSERT(fNextSha1Sum.isNotEmpty());
 -                 const water::String filename(fBinaries[fBinaryIndex].getFullPathName());
 -                 fNextInfo.filename = filename.toRawUTF8();
 -                 fNextInfo.ptype = fPluginType;
 -                 fPluginsFoundInBinary = true;
 -                 carla_stdout("Found %s from %s", fNextInfo.metadata.name, fNextInfo.filename);
 -                 fDiscoveryCallback(fCallbackPtr, &fNextInfo, fNextSha1Sum);
 -             }
 - 
 -             std::free(fNextLabel);
 -             fNextLabel = nullptr;
 - 
 -             std::free(fNextMaker);
 -             fNextMaker = nullptr;
 - 
 -             std::free(fNextName);
 -             fNextName = nullptr;
 - 
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "build") == 0)
 -         {
 -             uint8_t btype = 0;
 -             readNextLineAsByte(btype);
 -             fNextInfo.btype = static_cast<BinaryType>(btype);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "hints") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.metadata.hints);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "category") == 0)
 -         {
 -             const char* category = nullptr;
 -             readNextLineAsString(category, false);
 -             fNextInfo.metadata.category = CB::getPluginCategoryFromString(category);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "name") == 0)
 -         {
 -             fNextInfo.metadata.name = fNextName = readNextLineAsString();
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "label") == 0)
 -         {
 -             fNextInfo.label = fNextLabel = readNextLineAsString();
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "maker") == 0)
 -         {
 -             fNextInfo.metadata.maker = fNextMaker = readNextLineAsString();
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "uniqueId") == 0)
 -         {
 -             readNextLineAsULong(fNextInfo.uniqueId);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "audio.ins") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.audioIns);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "audio.outs") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.audioOuts);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "cv.ins") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.cvIns);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "cv.outs") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.cvOuts);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "midi.ins") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.midiIns);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "midi.outs") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.midiOuts);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "parameters.ins") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.parameterIns);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "parameters.outs") == 0)
 -         {
 -             readNextLineAsUInt(fNextInfo.io.parameterOuts);
 -             return true;
 -         }
 - 
 -         if (std::strcmp(msg, "exiting") == 0)
 -         {
 -             stopPipeServer(1000);
 -             return true;
 -         }
 - 
 -         carla_stdout("discovery: unknown message '%s' received", msg);
 -         return true;
 -     }
 - 
 - private:
 -     const BinaryType fBinaryType;
 -     const PluginType fPluginType;
 -     const CarlaPluginDiscoveryCallback fDiscoveryCallback;
 -     const CarlaPluginCheckCacheCallback fCheckCacheCallback;
 -     void* const fCallbackPtr;
 -     const char* fPluginPath;
 - 
 -     bool fPluginsFoundInBinary;
 -     uint fBinaryIndex;
 -     const uint fBinaryCount;
 -     const std::vector<water::File> fBinaries;
 -     const CarlaString fDiscoveryTool;
 - 
 -     uint32_t fLastMessageTime;
 - 
 -     CarlaPluginDiscoveryInfo fNextInfo;
 -     CarlaString fNextSha1Sum;
 -     char* fNextLabel;
 -     char* fNextMaker;
 -     char* fNextName;
 - 
 -     void start()
 -     {
 -         using water::File;
 -         using water::String;
 - 
 -         fLastMessageTime = carla_gettime_ms();
 -         fPluginsFoundInBinary = false;
 -         fNextSha1Sum.clear();
 - 
 -        #ifndef CARLA_OS_WIN
 -         const CarlaPluginDiscoveryOptions& options(CarlaPluginDiscoveryOptions::getInstance());
 - 
 -         String helperTool;
 - 
 -         switch (fBinaryType)
 -         {
 -         case CB::BINARY_WIN32:
 -             if (options.wine.executable.isNotEmpty())
 -                 helperTool = options.wine.executable.buffer();
 -             else
 -                 helperTool = "wine";
 -             break;
 - 
 -         case CB::BINARY_WIN64:
 -             if (options.wine.executable.isNotEmpty())
 -             {
 -                 helperTool = options.wine.executable.buffer();
 - 
 -                 if (helperTool.isNotEmpty() && helperTool[0] == CARLA_OS_SEP && File(helperTool + "64").existsAsFile())
 -                     helperTool += "64";
 -             }
 -             else
 -             {
 -                 helperTool = "wine";
 -             }
 -             break;
 - 
 -         default:
 -             break;
 -         }
 - 
 -         String winePrefix;
 - 
 -         if (options.wine.autoPrefix && !fBinaries.empty())
 -         {
 -             const File file(fBinaries[fBinaryIndex]);
 -             const String filename(file.getFullPathName());
 - 
 -             winePrefix = findWinePrefix(filename);
 -         }
 - 
 -         if (winePrefix.isEmpty())
 -         {
 -             const char* const envWinePrefix = std::getenv("WINEPREFIX");
 - 
 -             if (envWinePrefix != nullptr && envWinePrefix[0] != '\0')
 -                 winePrefix = envWinePrefix;
 -             else if (options.wine.fallbackPrefix.isNotEmpty())
 -                 winePrefix = options.wine.fallbackPrefix.buffer();
 -             else
 -                 winePrefix = File::getSpecialLocation(File::userHomeDirectory).getFullPathName() + "/.wine";
 -         }
 - 
 -         const CarlaScopedEnvVar sev1("WINEDEBUG", "-all");
 -         const CarlaScopedEnvVar sev2("WINEPREFIX", winePrefix.toRawUTF8());
 -        #endif
 - 
 -         const CarlaScopedEnvVar sev3("CARLA_DISCOVERY_NO_PROCESSING_CHECKS", "1");
 - 
 -         if (fBinaries.empty())
 -         {
 -             if (fBinaryType == CB::BINARY_NATIVE)
 -             {
 -                 switch (fPluginType)
 -                 {
 -                 default:
 -                     break;
 -                 case CB::PLUGIN_INTERNAL:
 -                 case CB::PLUGIN_LV2:
 -                 case CB::PLUGIN_JSFX:
 -                 case CB::PLUGIN_SFZ:
 -                     if (const uint count = carla_get_cached_plugin_count(fPluginType, fPluginPath))
 -                     {
 -                         for (uint i=0; i<count; ++i)
 -                         {
 -                             const CarlaCachedPluginInfo* const pinfo = carla_get_cached_plugin_info(fPluginType, i);
 - 
 -                             if (pinfo == nullptr || !pinfo->valid)
 -                                 continue;
 - 
 -                             char* filename = nullptr;
 -                             CarlaPluginDiscoveryInfo info = {};
 -                             info.btype = CB::BINARY_NATIVE;
 -                             info.ptype = fPluginType;
 -                             info.metadata.name = pinfo->name;
 -                             info.metadata.maker = pinfo->maker;
 -                             info.metadata.category = pinfo->category;
 -                             info.metadata.hints = pinfo->hints;
 -                             info.io.audioIns = pinfo->audioIns;
 -                             info.io.audioOuts = pinfo->audioOuts;
 -                             info.io.cvIns = pinfo->cvIns;
 -                             info.io.cvOuts = pinfo->cvOuts;
 -                             info.io.midiIns = pinfo->midiIns;
 -                             info.io.midiOuts = pinfo->midiOuts;
 -                             info.io.parameterIns = pinfo->parameterIns;
 -                             info.io.parameterOuts = pinfo->parameterOuts;
 - 
 -                             if (fPluginType == CB::PLUGIN_LV2)
 -                             {
 -                                 const char* const slash = std::strchr(pinfo->label, CARLA_OS_SEP);
 -                                 CARLA_SAFE_ASSERT_BREAK(slash != nullptr);
 -                                 filename = strdup(pinfo->label);
 -                                 filename[slash - pinfo->label] = '\0';
 -                                 info.filename = filename;
 -                                 info.label = slash + 1;
 -                             }
 -                             else
 -                             {
 -                                 info.filename = gPluginsDiscoveryNullCharPtr;
 -                                 info.label = pinfo->label;
 -                             }
 - 
 -                             fDiscoveryCallback(fCallbackPtr, &info, nullptr);
 - 
 -                             std::free(filename);
 -                         }
 -                     }
 -                     return;
 -                 }
 -             }
 - 
 -            #ifndef CARLA_OS_WIN
 -             if (helperTool.isNotEmpty())
 -                 startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all", -1, 2000);
 -             else
 -            #endif
 -                 startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), ":all", -1, 2000);
 -         }
 -         else
 -         {
 -             const File file(fBinaries[fBinaryIndex]);
 -             const String filename(file.getFullPathName());
 - 
 -             if (fCheckCacheCallback != nullptr)
 -             {
 -                 makeHash(file, filename);
 - 
 -                 if (fCheckCacheCallback(fCallbackPtr, filename.toRawUTF8(), fNextSha1Sum))
 -                 {
 -                     fPluginsFoundInBinary = true;
 -                     carla_debug("Skipping \"%s\", using cache", filename.toRawUTF8());
 -                     return;
 -                 }
 -             }
 - 
 -             carla_stdout("Scanning \"%s\"...", filename.toRawUTF8());
 - 
 -            #ifndef CARLA_OS_WIN
 -             if (helperTool.isNotEmpty())
 -                 startPipeServer(helperTool.toRawUTF8(), fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8(), -1, 2000);
 -             else
 -            #endif
 -                 startPipeServer(fDiscoveryTool, getPluginTypeAsString(fPluginType), filename.toRawUTF8(), -1, 2000);
 -         }
 -     }
 - 
 -     void makeHash(const water::File& file, const water::String& filename)
 -     {
 -         CarlaSha1 sha1;
 - 
 -         /* do we want this? it is not exactly needed and makes discovery slow..
 -         if (file.existsAsFile() && file.getSize() < 20*1024*1024) // dont bother hashing > 20Mb files
 -         {
 -             water::FileInputStream stream(file);
 - 
 -             if (stream.openedOk())
 -             {
 -                 uint8_t block[8192];
 -                 for (int r; r = stream.read(block, sizeof(block)), r > 0;)
 -                     sha1.write(block, r);
 -             }
 -         }
 -         */
 - 
 -         sha1.write(filename.toRawUTF8(), filename.length());
 - 
 -         const int64_t mtime = file.getLastModificationTime();
 -         sha1.write(&mtime, sizeof(mtime));
 - 
 -         fNextSha1Sum = sha1.resultAsString();
 -     }
 - 
 -     CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaPluginDiscovery)
 - };
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 - 
 - static bool findDirectories(std::vector<water::File>& files, const char* const pluginPath, const char* const wildcard)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
 - 
 -     if (pluginPath[0] == '\0')
 -         return true;
 - 
 -     using water::File;
 -     using water::String;
 -     using water::StringArray;
 - 
 -     const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
 - 
 -     if (splitPaths.size() == 0)
 -         return true;
 - 
 -     for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
 -     {
 -         const File dir(*it);
 -         std::vector<File> results;
 - 
 -         if (dir.findChildFiles(results, File::findDirectories|File::ignoreHiddenFiles, true, wildcard) > 0)
 -         {
 -             files.reserve(files.size() + results.size());
 -             files.insert(files.end(), results.begin(), results.end());
 -         }
 -     }
 - 
 -     return files.empty();
 - }
 - 
 - static bool findFiles(std::vector<water::File>& files,
 -                       const BinaryType btype, const char* const pluginPath, const char* const wildcard)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
 - 
 -     if (pluginPath[0] == '\0')
 -         return true;
 - 
 -     using water::File;
 -     using water::String;
 -     using water::StringArray;
 - 
 -     const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
 - 
 -     if (splitPaths.size() == 0)
 -         return true;
 - 
 -     for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
 -     {
 -         const File dir(*it);
 -         std::vector<File> results;
 - 
 -         if (dir.findChildFiles(results, File::findFiles|File::ignoreHiddenFiles, true, wildcard) > 0)
 -         {
 -             files.reserve(files.size() + results.size());
 - 
 -             for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
 -             {
 -                 const File file(*cit);
 - 
 -                 if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
 -                     files.push_back(file);
 -             }
 -         }
 -     }
 - 
 -     return files.empty();
 - }
 - 
 - static bool findVST3s(std::vector<water::File>& files,
 -                       const BinaryType btype, const char* const pluginPath)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(pluginPath != nullptr, true);
 - 
 -     if (pluginPath[0] == '\0')
 -         return true;
 - 
 -     using water::File;
 -     using water::String;
 -     using water::StringArray;
 - 
 -     const StringArray splitPaths(StringArray::fromTokens(pluginPath, CARLA_OS_SPLIT_STR, ""));
 - 
 -     if (splitPaths.size() == 0)
 -         return true;
 - 
 -     const uint flags = btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64
 -                      ? File::findDirectories|File::findFiles
 -                      : File::findDirectories;
 - 
 -     for (String *it = splitPaths.begin(), *end = splitPaths.end(); it != end; ++it)
 -     {
 -         const File dir(*it);
 -         std::vector<File> results;
 - 
 -         if (dir.findChildFiles(results, flags|File::ignoreHiddenFiles, true, "*.vst3") > 0)
 -         {
 -             files.reserve(files.size() + results.size());
 - 
 -             for (std::vector<File>::const_iterator cit = results.begin(); cit != results.end(); ++cit)
 -             {
 -                 const File file(*cit);
 - 
 -                 if (CB::getBinaryTypeFromFile(file.getFullPathName().toRawUTF8()) == btype)
 -                     files.push_back(file);
 -             }
 -         }
 -     }
 - 
 -     return files.empty();
 - }
 - 
 - CarlaPluginDiscoveryHandle carla_plugin_discovery_start(const char* const discoveryTool,
 -                                                         const BinaryType btype,
 -                                                         const PluginType ptype,
 -                                                         const char* const pluginPath,
 -                                                         const CarlaPluginDiscoveryCallback discoveryCb,
 -                                                         const CarlaPluginCheckCacheCallback checkCacheCb,
 -                                                         void* const callbackPtr)
 - {
 -     CARLA_SAFE_ASSERT_RETURN(btype != CB::BINARY_NONE, nullptr);
 -     CARLA_SAFE_ASSERT_RETURN(ptype != CB::PLUGIN_NONE, nullptr);
 -     CARLA_SAFE_ASSERT_RETURN(discoveryTool != nullptr && discoveryTool[0] != '\0', nullptr);
 -     CARLA_SAFE_ASSERT_RETURN(discoveryCb != nullptr, nullptr);
 -     carla_debug("carla_plugin_discovery_start(%s, %d:%s, %d:%s, %s, %p, %p, %p)",
 -                 discoveryTool, btype, BinaryType2Str(btype), ptype, PluginType2Str(ptype), pluginPath,
 -                 discoveryCb, checkCacheCb, callbackPtr);
 - 
 -     bool directories = false;
 -     const char* wildcard = nullptr;
 - 
 -     switch (ptype)
 -     {
 -     case CB::PLUGIN_INTERNAL:
 -     case CB::PLUGIN_LV2:
 -     case CB::PLUGIN_SFZ:
 -     case CB::PLUGIN_JSFX:
 -     case CB::PLUGIN_DLS:
 -     case CB::PLUGIN_GIG:
 -     case CB::PLUGIN_SF2:
 -         CARLA_SAFE_ASSERT_UINT_RETURN(btype == CB::BINARY_NATIVE, btype, nullptr);
 -         break;
 -     default:
 -         break;
 -     }
 - 
 -     switch (ptype)
 -     {
 -     case CB::PLUGIN_NONE:
 -     case CB::PLUGIN_JACK:
 -     case CB::PLUGIN_TYPE_COUNT:
 -         return nullptr;
 - 
 -     case CB::PLUGIN_LV2:
 -     case CB::PLUGIN_SFZ:
 -     case CB::PLUGIN_JSFX:
 -     {
 -         const CarlaScopedEnvVar csev("CARLA_DISCOVERY_PATH", pluginPath);
 -         return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr, pluginPath);
 -     }
 - 
 -     case CB::PLUGIN_INTERNAL:
 -         return new CarlaPluginDiscovery(discoveryTool, btype, ptype, discoveryCb, checkCacheCb, callbackPtr);
 - 
 -     case CB::PLUGIN_LADSPA:
 -     case CB::PLUGIN_DSSI:
 -        #ifdef CARLA_OS_WIN
 -         wildcard = "*.dll";
 -        #else
 -         if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
 -         {
 -             wildcard = "*.dll";
 -         }
 -         else
 -         {
 -            #ifdef CARLA_OS_MAC
 -             wildcard = "*.dylib";
 -            #else
 -             wildcard = "*.so";
 -            #endif
 -         }
 -        #endif
 -         break;
 - 
 -     case CB::PLUGIN_VST2:
 -        #ifdef CARLA_OS_WIN
 -         wildcard = "*.dll";
 -        #else
 -         if (btype == CB::BINARY_WIN32 || btype == CB::BINARY_WIN64)
 -         {
 -             wildcard = "*.dll";
 -         }
 -         else
 -         {
 -            #ifdef CARLA_OS_MAC
 -             directories = true;
 -             wildcard = "*.vst";
 -            #else
 -             wildcard = "*.so";
 -            #endif
 -         }
 -        #endif
 -         break;
 - 
 -     case CB::PLUGIN_VST3:
 -         directories = true;
 -         wildcard = "*.vst3";
 -         break;
 - 
 -     case CB::PLUGIN_AU:
 -         directories = true;
 -         wildcard = "*.component";
 -         break;
 - 
 -     case CB::PLUGIN_CLAP:
 -         wildcard = "*.clap";
 -        #ifdef CARLA_OS_MAC
 -         directories = true;
 -        #endif
 -         break;
 - 
 -     case CB::PLUGIN_DLS:
 -         wildcard = "*.dls";
 -         break;
 -     case CB::PLUGIN_GIG:
 -         wildcard = "*.gig";
 -         break;
 -     case CB::PLUGIN_SF2:
 -         wildcard = "*.sf2";
 -         break;
 -     }
 - 
 -     CARLA_SAFE_ASSERT_RETURN(wildcard != nullptr, nullptr);
 - 
 -     std::vector<water::File> files;
 - 
 -     if (ptype == CB::PLUGIN_VST3)
 -     {
 -         if (findVST3s(files, btype, pluginPath))
 -             return nullptr;
 -     }
 -     else if (directories)
 -     {
 -         if (findDirectories(files, pluginPath, wildcard))
 -             return nullptr;
 -     }
 -     else
 -     {
 -         if (findFiles(files, btype, pluginPath, wildcard))
 -             return nullptr;
 -     }
 - 
 -     return new CarlaPluginDiscovery(discoveryTool, btype, ptype, std::move(files),
 -                                     discoveryCb, checkCacheCb, callbackPtr);
 - }
 - 
 - bool carla_plugin_discovery_idle(const CarlaPluginDiscoveryHandle handle)
 - {
 -     return static_cast<CarlaPluginDiscovery*>(handle)->idle();
 - }
 - 
 - void carla_plugin_discovery_skip(const CarlaPluginDiscoveryHandle handle)
 - {
 -     static_cast<CarlaPluginDiscovery*>(handle)->skip();
 - }
 - 
 - void carla_plugin_discovery_stop(const CarlaPluginDiscoveryHandle handle)
 - {
 -     delete static_cast<CarlaPluginDiscovery*>(handle);
 - }
 - 
 - void carla_plugin_discovery_set_option(const EngineOption option, const int value, const char* const valueStr)
 - {
 -     switch (option)
 -     {
 -    #if !defined(BUILD_BRIDGE_ALTERNATIVE_ARCH) && !defined(CARLA_OS_WIN)
 -     case CB::ENGINE_OPTION_WINE_EXECUTABLE:
 -         if (valueStr != nullptr && valueStr[0] != '\0')
 -             CarlaPluginDiscoveryOptions::getInstance().wine.executable = valueStr;
 -         else
 -             CarlaPluginDiscoveryOptions::getInstance().wine.executable.clear();
 -         break;
 -     case CB::ENGINE_OPTION_WINE_AUTO_PREFIX:
 -         CARLA_SAFE_ASSERT_RETURN(value == 0 || value == 1,);
 -         CarlaPluginDiscoveryOptions::getInstance().wine.autoPrefix = value != 0;
 -         break;
 -     case CB::ENGINE_OPTION_WINE_FALLBACK_PREFIX:
 -         if (valueStr != nullptr && valueStr[0] != '\0')
 -             CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix = valueStr;
 -         else
 -             CarlaPluginDiscoveryOptions::getInstance().wine.fallbackPrefix.clear();
 -         break;
 -    #endif
 -     default:
 -         break;
 -     }
 - }
 - 
 - // --------------------------------------------------------------------------------------------------------------------
 
 
  |