--- ../Rack/src/plugin.cpp 2022-11-23 23:11:38.000000000 +0000 +++ plugin.cpp 2022-11-25 23:27:58.000000000 +0000 @@ -1,342 +1,41 @@ -#include -#include -#include -#include - -#include -#include -#include -#include // for MAXPATHLEN -#include -#if defined ARCH_WIN - #include - #include -#else - #include // for dlopen -#endif -#include +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021-2022 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +/** + * This file is an edited version of VCVRack's plugin.cpp + * Copyright (C) 2016-2021 VCV. + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + */ -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include namespace rack { - -namespace core { -void init(rack::plugin::Plugin* plugin); -} // namespace core - namespace plugin { -//////////////////// -// private API -//////////////////// - - -static void* getSymbol(void* handle, const char* name) { - if (!handle) - return NULL; - -#if defined ARCH_WIN - return (void*) GetProcAddress((HMODULE) handle, name); -#else - return dlsym(handle, name); -#endif -} - -/** Returns library handle */ -static void* loadLibrary(std::string libraryPath) { -#if defined ARCH_WIN - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); - std::wstring libraryFilenameW = string::UTF8toUTF16(libraryPath); - HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str()); - SetErrorMode(0); - if (!handle) { - int error = GetLastError(); - throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error); - } -#else - // Since Rack 2, plugins on Linux/Mac link to the absolute path /tmp/Rack2/libRack. - // Create a symlink at /tmp/Rack2 to the system dir containting libRack. - std::string systemDir = system::getAbsolute(asset::systemDir); - std::string linkPath = "/tmp/Rack2"; - if (!settings::devMode) { - // Clean up old symbolic link in case a different edition was run earlier - system::remove(linkPath); - system::createSymbolicLink(systemDir, linkPath); - } - // Load library with dlopen - void* handle = NULL; - #if defined ARCH_LIN - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); - #elif defined ARCH_MAC - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); - #endif - if (!settings::devMode) { - system::remove(linkPath); - } - if (!handle) - throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror()); -#endif - return handle; -} - -typedef void (*InitCallback)(Plugin*); - -static InitCallback loadPluginCallback(Plugin* plugin) { - // Load plugin library - std::string libraryExt; -#if defined ARCH_LIN - libraryExt = "so"; -#elif defined ARCH_WIN - libraryExt = "dll"; -#elif ARCH_MAC - libraryExt = "dylib"; -#endif - std::string libraryPath = system::join(plugin->path, "plugin." + libraryExt); - - // Check file existence - if (!system::isFile(libraryPath)) - throw Exception("Plugin binary not found at %s", libraryPath.c_str()); - - // Load dynamic/shared library - plugin->handle = loadLibrary(libraryPath); - - // Get plugin's init() function - InitCallback initCallback = (InitCallback) getSymbol(plugin->handle, "init"); - if (!initCallback) - throw Exception("Failed to read init() symbol in %s", libraryPath.c_str()); - - return initCallback; -} - - -/** If path is blank, loads Core */ -static Plugin* loadPlugin(std::string path) { - if (path == "") - INFO("Loading Core plugin"); - else - INFO("Loading plugin from %s", path.c_str()); - - Plugin* plugin = new Plugin; - try { - // Set plugin path - plugin->path = (path == "") ? asset::systemDir : path; - - // Get modified timestamp - if (path != "") { - struct stat statbuf; - if (!stat(path.c_str(), &statbuf)) { -#if defined ARCH_MAC - plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9; -#elif defined ARCH_WIN - plugin->modifiedTimestamp = (double) statbuf.st_mtime; -#elif defined ARCH_LIN - plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9; -#endif - } - } - - // Load plugin.json - std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); - FILE* file = std::fopen(manifestFilename.c_str(), "r"); - if (!file) - throw Exception("Manifest file %s does not exist", manifestFilename.c_str()); - DEFER({std::fclose(file);}); - - json_error_t error; - json_t* rootJ = json_loadf(file, 0, &error); - if (!rootJ) - throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text); - DEFER({json_decref(rootJ);}); - - // Load manifest - plugin->fromJson(rootJ); - - // Reject plugin if slug already exists - Plugin* existingPlugin = getPlugin(plugin->slug); - if (existingPlugin) - throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()); - - // Call init callback - InitCallback initCallback; - if (path == "") { - initCallback = core::init; - } - else { - initCallback = loadPluginCallback(plugin); - } - initCallback(plugin); - - // Load modules manifest - json_t* modulesJ = json_object_get(rootJ, "modules"); - plugin->modulesFromJson(modulesJ); - - // Call settingsFromJson() if exists - // Returns NULL for Core. - auto settingsFromJson = (decltype(&::settingsFromJson)) getSymbol(plugin->handle, "settingsFromJson"); - if (settingsFromJson) { - json_t* settingsJ = json_object_get(settings::pluginSettingsJ, plugin->slug.c_str()); - if (settingsJ) - settingsFromJson(settingsJ); - } - } - catch (Exception& e) { - WARN("Could not load plugin %s: %s", path.c_str(), e.what()); - delete plugin; - return NULL; - } - - INFO("Loaded %s v%s", plugin->slug.c_str(), plugin->version.c_str()); - plugins.push_back(plugin); - return plugin; -} - - -static void loadPlugins(std::string path) { - for (std::string pluginPath : system::getEntries(path)) { - if (!system::isDirectory(pluginPath)) - continue; - if (!loadPlugin(pluginPath)) { - // Ignore bad plugins. They are reported in the log. - } - } -} - - -static void extractPackages(std::string path) { - std::string message; - - for (std::string packagePath : system::getEntries(path)) { - if (!system::isFile(packagePath)) - continue; - if (system::getExtension(packagePath) != ".vcvplugin") - continue; - - // Extract package - INFO("Extracting package %s", packagePath.c_str()); - try { - system::unarchiveToDirectory(packagePath, path); - } - catch (Exception& e) { - WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what()); - message += string::f("Could not extract plugin package %s\n", packagePath.c_str()); - continue; - } - // Remove package - system::remove(packagePath.c_str()); - } - if (!message.empty()) { - osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); - } -} - -//////////////////// -// public API -//////////////////// - -void init() { - // Don't re-initialize - assert(plugins.empty()); - - // Load Core - loadPlugin(""); - - pluginsPath = asset::user("plugins"); - - // Get user plugins directory - system::createDirectory(pluginsPath); - - // Don't load plugins if safe mode is enabled - if (settings::safeMode) - return; - - // Extract packages and load plugins - extractPackages(pluginsPath); - loadPlugins(pluginsPath); - - // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it - if (!settings::devMode && !getPlugin("Fundamental")) { - std::string fundamentalSrc = asset::system("Fundamental.vcvplugin"); - std::string fundamentalDir = system::join(pluginsPath, "Fundamental"); - if (system::isFile(fundamentalSrc)) { - INFO("Extracting bundled Fundamental package"); - try { - system::unarchiveToDirectory(fundamentalSrc.c_str(), pluginsPath.c_str()); - loadPlugin(fundamentalDir); - } - catch (Exception& e) { - WARN("Could not extract Fundamental package: %s", e.what()); - } - } - } -} - - -static void destroyPlugin(Plugin* plugin) { - void* handle = plugin->handle; - - // Call destroy() if defined in the plugin library - typedef void (*DestroyCallback)(); - DestroyCallback destroyCallback = NULL; - if (handle) { - destroyCallback = (DestroyCallback) getSymbol(handle, "destroy"); - } - if (destroyCallback) { - try { - destroyCallback(); - } - catch (Exception& e) { - WARN("Could not destroy plugin %s", plugin->slug.c_str()); - } - } - - // We must delete the Plugin instance *before* freeing the library, because the vtables of Model subclasses are defined in the library, which are needed in the Plugin destructor. - delete plugin; - - // Free library handle - if (handle) { -#if defined ARCH_WIN - FreeLibrary((HINSTANCE) handle); -#else - dlclose(handle); -#endif - } -} - - -void destroy() { - while (!plugins.empty()) { - Plugin* plugin = plugins.back(); - INFO("Destroying plugin %s", plugin->name.c_str()); - destroyPlugin(plugin); - plugins.pop_back(); - } - assert(plugins.empty()); -} - - -void settingsMergeJson(json_t* rootJ) { - for (Plugin* plugin : plugins) { - auto settingsToJson = (decltype(&::settingsToJson)) getSymbol(plugin->handle, "settingsToJson"); - if (settingsToJson) { - json_t* settingsJ = settingsToJson(); - json_object_set_new(rootJ, plugin->slug.c_str(), settingsJ); - } - else { - json_object_del(rootJ, plugin->slug.c_str()); - } - } -} +void settingsMergeJson(json_t*) {} /** Given slug => fallback slug. @@ -348,6 +47,7 @@ {"VultModules", "VultModulesFree"}, {"AudibleInstrumentsPreview", "AudibleInstruments"}, {"SequelSequencers", "DanielDavies"}, + {"DelexanderVol1", "DelexandraVol1"}, // {"", ""}, }; @@ -389,8 +89,19 @@ */ using PluginModuleSlug = std::tuple; static const std::map moduleSlugFallbacks = { + {{"Core", "AudioInterface2"}, {"Cardinal", "HostAudio2"}}, + {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}}, + {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}}, + {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}}, + {{"Core", "MIDICCToCVInterface"}, {"Cardinal", "HostMIDICC"}}, + {{"Core", "MIDITriggerToCVInterface"}, {"Cardinal", "HostMIDIGate"}}, + {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}}, + {{"Core", "CV-CC"}, {"Cardinal", "HostMIDICC"}}, + {{"Core", "CV-Gate"}, {"Cardinal", "HostMIDIGate"}}, + {{"Core", "MIDI-Map"}, {"Cardinal", "HostMIDIMap"}}, + {{"Core", "Notes"}, {"Cardinal", "TextEditor"}}, + {{"Core", "Blank"}, {"Cardinal", "Blank"}}, {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}}, - {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}}, // {{"", ""}, {"", ""}}, }; @@ -478,7 +189,6 @@ } -std::string pluginsPath; std::vector plugins;