fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / plugin_manager.cc
index 3d2b34f65778d0246b9b0c687485457b8a8a28fb..23515a2e2e67867556cc4e167501358408fab5db 100644 (file)
@@ -26,7 +26,9 @@
 #include <sys/types.h>
 #include <cstdio>
 #include <cstdlib>
-#include <fstream>
+
+#include <glib.h>
+#include "pbd/gstdio_compat.h"
 
 #ifdef HAVE_LRDF
 #include <lrdf.h>
 #include "fst.h"
 #include "pbd/basename.h"
 #include <cstring>
+
+// dll-info
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+
 #endif // WINDOWS_VST_SUPPORT
 
 #ifdef LXVST_SUPPORT
@@ -46,7 +55,6 @@
 #include <cstring>
 #endif //LXVST_SUPPORT
 
-#include <glib/gstdio.h>
 #include <glibmm/miscutils.h>
 #include <glibmm/pattern.h>
 #include <glibmm/fileutils.h>
@@ -59,6 +67,8 @@
 #include "ardour/filesystem_paths.h"
 #include "ardour/ladspa.h"
 #include "ardour/ladspa_plugin.h"
+#include "ardour/luascripting.h"
+#include "ardour/luaproc.h"
 #include "ardour/plugin.h"
 #include "ardour/plugin_manager.h"
 #include "ardour/rc_configuration.h"
@@ -85,7 +95,7 @@
 #include "pbd/error.h"
 #include "pbd/stl_delete.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 #include "ardour/debug.h"
 
@@ -111,6 +121,7 @@ PluginManager::PluginManager ()
        , _ladspa_plugin_info(0)
        , _lv2_plugin_info(0)
        , _au_plugin_info(0)
+       , _lua_plugin_info(0)
        , _cancel_scan(false)
        , _cancel_timeout(false)
 {
@@ -123,7 +134,7 @@ PluginManager::PluginManager ()
 
 #ifdef PLATFORM_WINDOWS
        // on windows the .exe needs to be in the same folder with libardour.dll
-       vstsp += Glib::build_filename(g_win32_get_package_installation_directory_of_module (0), "bin");
+       vstsp += Glib::build_filename(windows_package_directory_path(), "bin");
 #else
        // on Unices additional internal-use binaries are deployed to $libdir
        vstsp += ARDOUR::ardour_dll_directory();
@@ -207,6 +218,8 @@ PluginManager::PluginManager ()
        }
 
        BootMessage (_("Discovering Plugins"));
+
+       LuaScripting::instance().scripts_changed.connect_same_thread (lua_refresh_connection, boost::bind (&PluginManager::lua_refresh_cb, this));
 }
 
 
@@ -219,6 +232,7 @@ PluginManager::~PluginManager()
                delete _ladspa_plugin_info;
                delete _lv2_plugin_info;
                delete _au_plugin_info;
+               delete _lua_plugin_info;
        }
 }
 
@@ -236,6 +250,8 @@ PluginManager::refresh (bool cache_only)
 
        BootMessage (_("Scanning LADSPA Plugins"));
        ladspa_refresh ();
+       BootMessage (_("Scanning Lua DSP Processors"));
+       lua_refresh ();
 #ifdef LV2_SUPPORT
        BootMessage (_("Scanning LV2 Plugins"));
        lv2_refresh ();
@@ -266,10 +282,15 @@ PluginManager::refresh (bool cache_only)
                if (!cache_only) {
                        string fn = Glib::build_filename (ARDOUR::user_cache_directory(), VST_BLACKLIST);
                        if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
-                               std::string bl;
-                               std::ifstream ifs (fn.c_str ());
-                               bl.assign ((std::istreambuf_iterator<char> (ifs)), (std::istreambuf_iterator<char> ()));
-                               PBD::info << _("VST Blacklist:") << "\n" << bl << "-----" << endmsg;
+                               gchar *bl = NULL;
+                               if (g_file_get_contents(fn.c_str (), &bl, NULL, NULL)) {
+                                       if (Config->get_verbose_plugin_scan()) {
+                                               PBD::info << _("VST Blacklist: ") << fn << "\n" << bl << "-----" << endmsg;
+                                       } else {
+                                               PBD::info << _("VST Blacklist:") << "\n" << bl << "-----" << endmsg;
+                                       }
+                                       g_free (bl);
+                               }
                        }
                }
 #endif
@@ -424,11 +445,7 @@ void
 PluginManager::clear_au_cache ()
 {
 #ifdef AUDIOUNIT_SUPPORT
-       // AUPluginInfo::au_cache_path ()
-       string fn = Glib::build_filename (ARDOUR::user_config_directory(), "au_cache");
-       if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
-               ::g_unlink(fn.c_str());
-       }
+       AUPluginInfo::clear_cache ();
 #endif
 }
 
@@ -443,6 +460,32 @@ PluginManager::clear_au_blacklist ()
 #endif
 }
 
+void
+PluginManager::lua_refresh ()
+{
+       if (_lua_plugin_info) {
+               _lua_plugin_info->clear ();
+       } else {
+               _lua_plugin_info = new ARDOUR::PluginInfoList ();
+       }
+       ARDOUR::LuaScriptList & _scripts (LuaScripting::instance ().scripts (LuaScriptInfo::DSP));
+       for (LuaScriptList::const_iterator s = _scripts.begin(); s != _scripts.end(); ++s) {
+               LuaPluginInfoPtr lpi (new LuaPluginInfo(*s));
+               _lua_plugin_info->push_back (lpi);
+       }
+}
+
+void
+PluginManager::lua_refresh_cb ()
+{
+       Glib::Threads::Mutex::Lock lm (_lock, Glib::Threads::TRY_LOCK);
+       if (!lm.locked()) {
+               return;
+       }
+       lua_refresh ();
+       PluginListChanged (); /* EMIT SIGNAL */
+}
+
 void
 PluginManager::ladspa_refresh ()
 {
@@ -573,6 +616,11 @@ PluginManager::ladspa_discover (string path)
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("LADSPA plugin found at %1\n", path));
 
        for (uint32_t i = 0; ; ++i) {
+               /* if a ladspa plugin allocates memory here
+                * it is never free()ed (or plugin-dependent only when unloading).
+                * For some plugins memory allocated is incremental, we should
+                * avoid re-scanning plugins and file bug reports.
+                */
                if ((descriptor = dfunc (i)) == 0) {
                        break;
                }
@@ -779,13 +827,77 @@ PluginManager::windows_vst_discover_from_path (string path, bool cache_only)
        return ret;
 }
 
+static std::string dll_info (std::string path) {
+       std::string rv;
+       uint8_t buf[68];
+       uint16_t type = 0;
+       off_t pe_hdr_off = 0;
+
+       int fd = g_open(path.c_str(), O_RDONLY, 0444);
+
+       if (fd < 0) {
+               return _("cannot open dll"); // TODO strerror()
+       }
+
+       if (68 != read (fd, buf, 68)) {
+               rv = _("invalid dll, file too small");
+               goto errorout;
+       }
+       if (buf[0] != 'M' && buf[1] != 'Z') {
+               rv = _("not a dll");
+               goto errorout;
+       }
+
+       pe_hdr_off = *((int32_t*) &buf[60]);
+       if (pe_hdr_off !=lseek (fd, pe_hdr_off, SEEK_SET)) {
+               rv = _("cannot determine dll type");
+               goto errorout;
+       }
+       if (6 != read (fd, buf, 6)) {
+               rv = _("cannot read dll PE header");
+               goto errorout;
+       }
+
+       if (buf[0] != 'P' && buf[1] != 'E') {
+               rv = _("invalid dll PE header");
+               goto errorout;
+       }
+
+       type = *((uint16_t*) &buf[4]);
+       switch (type) {
+               case 0x014c:
+                       rv = _("i386 (32-bit)");
+                       break;
+               case  0x0200:
+                       rv = _("Itanium");
+                       break;
+               case 0x8664:
+                       rv = _("x64 (64-bit)");
+                       break;
+               case 0:
+                       rv = _("Native Architecture");
+                       break;
+               default:
+                       rv = _("Unknown Architecture");
+                       break;
+       }
+errorout:
+       assert (rv.length() > 0);
+       close (fd);
+       return rv;
+}
+
 int
 PluginManager::windows_vst_discover (string path, bool cache_only)
 {
        DEBUG_TRACE (DEBUG::PluginManager, string_compose ("windows_vst_discover '%1'\n", path));
 
        if (Config->get_verbose_plugin_scan()) {
-               info << string_compose (_(" *  %1 %2"), path, (cache_only ? _(" (cache only)") : "")) << endmsg;
+               if (cache_only) {
+                       info << string_compose (_(" *  %1 (cache only)"), path) << endmsg;
+               } else {
+                       info << string_compose (_(" *  %1 - %2"), path, dll_info (path)) << endmsg;
+               }
        }
 
        _cancel_timeout = false;
@@ -856,7 +968,7 @@ PluginManager::windows_vst_discover (string path, bool cache_only)
                        _windows_vst_plugin_info->push_back (info);
                        discovered++;
                        if (Config->get_verbose_plugin_scan()) {
-                               PBD::info << string_compose (_(" -> OK. (VST Plugin \"%1\" added)."), info->name) << endmsg;
+                               PBD::info << string_compose (_(" -> OK (VST Plugin \"%1\" was added)."), info->name) << endmsg;
                        }
                }
        }
@@ -1005,14 +1117,8 @@ PluginManager::get_status (const PluginInfoPtr& pi)
 void
 PluginManager::save_statuses ()
 {
-       ofstream ofs;
        std::string path = Glib::build_filename (user_config_directory(), "plugin_statuses");
-
-       ofs.open (path.c_str(), ios_base::openmode (ios::out|ios::trunc));
-
-       if (!ofs) {
-               return;
-       }
+       stringstream ofs;
 
        for (PluginStatusList::iterator i = statuses.begin(); i != statuses.end(); ++i) {
                switch ((*i).type) {
@@ -1031,6 +1137,9 @@ PluginManager::save_statuses ()
                case LXVST:
                        ofs << "LXVST";
                        break;
+               case Lua:
+                       ofs << "Lua";
+                       break;
                }
 
                ofs << ' ';
@@ -1051,19 +1160,20 @@ PluginManager::save_statuses ()
                ofs << (*i).unique_id;;
                ofs << endl;
        }
-
-       ofs.close ();
+       g_file_set_contents (path.c_str(), ofs.str().c_str(), -1, NULL);
+       PluginStatusesChanged (); /* EMIT SIGNAL */
 }
 
 void
 PluginManager::load_statuses ()
 {
        std::string path = Glib::build_filename (user_config_directory(), "plugin_statuses");
-       ifstream ifs (path.c_str());
-
-       if (!ifs) {
+       gchar *fbuf = NULL;
+       if (!g_file_get_contents (path.c_str(), &fbuf, NULL, NULL))  {
                return;
        }
+       stringstream ifs (fbuf);
+       g_free (fbuf);
 
        std::string stype;
        std::string sstatus;
@@ -1116,6 +1226,8 @@ PluginManager::load_statuses ()
                        type = Windows_VST;
                } else if (stype == "LXVST") {
                        type = LXVST;
+               } else if (stype == "Lua") {
+                       type = Lua;
                } else {
                        error << string_compose (_("unknown plugin type \"%1\" - ignored"), stype)
                              << endmsg;
@@ -1126,8 +1238,6 @@ PluginManager::load_statuses ()
                strip_whitespace_edges (id);
                set_status (type, id, status);
        }
-
-       ifs.close ();
 }
 
 void
@@ -1195,3 +1305,10 @@ PluginManager::au_plugin_info ()
 #endif
        return _empty_plugin_info;
 }
+
+ARDOUR::PluginInfoList&
+PluginManager::lua_plugin_info ()
+{
+       assert(_lua_plugin_info);
+       return *_lua_plugin_info;
+}