You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

651 lines
16KB

  1. #include "plugin.hpp"
  2. #include "system.hpp"
  3. #include "network.hpp"
  4. #include "asset.hpp"
  5. #include "string.hpp"
  6. #include "app.hpp"
  7. #include "app/common.hpp"
  8. #include "plugin/callbacks.hpp"
  9. #include "settings.hpp"
  10. #include <unistd.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <sys/param.h> // for MAXPATHLEN
  14. #include <fcntl.h>
  15. #include <thread>
  16. #include <map>
  17. #include <stdexcept>
  18. #define ZIP_STATIC
  19. #include <zip.h>
  20. #include <jansson.h>
  21. #if defined ARCH_WIN
  22. #include <windows.h>
  23. #include <direct.h>
  24. #define mkdir(_dir, _perms) _mkdir(_dir)
  25. #else
  26. #include <dlfcn.h>
  27. #endif
  28. #include <dirent.h>
  29. #include <osdialog.h>
  30. namespace rack {
  31. namespace plugin {
  32. ////////////////////
  33. // private API
  34. ////////////////////
  35. static bool loadPlugin(std::string path) {
  36. // Load plugin.json
  37. std::string metadataFilename = path + "/plugin.json";
  38. FILE *file = fopen(metadataFilename.c_str(), "r");
  39. if (!file) {
  40. WARN("Plugin metadata file %s does not exist", metadataFilename.c_str());
  41. return false;
  42. }
  43. DEFER({
  44. fclose(file);
  45. });
  46. json_error_t error;
  47. json_t *rootJ = json_loadf(file, 0, &error);
  48. if (!rootJ) {
  49. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  50. return false;
  51. }
  52. DEFER({
  53. json_decref(rootJ);
  54. });
  55. // Load plugin library
  56. std::string libraryFilename;
  57. #if defined ARCH_LIN
  58. libraryFilename = path + "/" + "plugin.so";
  59. #elif defined ARCH_WIN
  60. libraryFilename = path + "/" + "plugin.dll";
  61. #elif ARCH_MAC
  62. libraryFilename = path + "/" + "plugin.dylib";
  63. #endif
  64. // Check file existence
  65. if (!system::isFile(libraryFilename)) {
  66. WARN("Plugin file %s does not exist", libraryFilename.c_str());
  67. return false;
  68. }
  69. // Load dynamic/shared library
  70. #if defined ARCH_WIN
  71. SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
  72. HINSTANCE handle = LoadLibrary(libraryFilename.c_str());
  73. SetErrorMode(0);
  74. if (!handle) {
  75. int error = GetLastError();
  76. WARN("Failed to load library %s: code %d", libraryFilename.c_str(), error);
  77. return false;
  78. }
  79. #else
  80. void *handle = dlopen(libraryFilename.c_str(), RTLD_NOW);
  81. if (!handle) {
  82. WARN("Failed to load library %s: %s", libraryFilename.c_str(), dlerror());
  83. return false;
  84. }
  85. #endif
  86. // Call plugin's init() function
  87. typedef void (*InitCallback)(Plugin *);
  88. InitCallback initCallback;
  89. #if defined ARCH_WIN
  90. initCallback = (InitCallback) GetProcAddress(handle, "init");
  91. #else
  92. initCallback = (InitCallback) dlsym(handle, "init");
  93. #endif
  94. if (!initCallback) {
  95. WARN("Failed to read init() symbol in %s", libraryFilename.c_str());
  96. return false;
  97. }
  98. // Construct and initialize Plugin instance
  99. Plugin *plugin = new Plugin;
  100. plugin->path = path;
  101. plugin->handle = handle;
  102. initCallback(plugin);
  103. plugin->fromJson(rootJ);
  104. // Check slug
  105. if (!isSlugValid(plugin->slug)) {
  106. WARN("Plugin slug \"%s\" is invalid", plugin->slug.c_str());
  107. // TODO Fix memory leak with `plugin`
  108. return false;
  109. }
  110. // Reject plugin if slug already exists
  111. Plugin *oldPlugin = getPlugin(plugin->slug);
  112. if (oldPlugin) {
  113. WARN("Plugin \"%s\" is already loaded, not attempting to load it again", plugin->slug.c_str());
  114. // TODO Fix memory leak with `plugin`
  115. return false;
  116. }
  117. // Add plugin to list
  118. plugins.push_back(plugin);
  119. INFO("Loaded plugin %s v%s from %s", plugin->slug.c_str(), plugin->version.c_str(), libraryFilename.c_str());
  120. // Normalize tags
  121. for (Model *model : plugin->models) {
  122. std::vector<std::string> normalizedTags;
  123. for (const std::string &tag : model->tags) {
  124. std::string normalizedTag = normalizeTag(tag);
  125. if (!normalizedTag.empty())
  126. normalizedTags.push_back(normalizedTag);
  127. }
  128. model->tags = normalizedTags;
  129. }
  130. // Search for presets
  131. for (Model *model : plugin->models) {
  132. std::string presetDir = asset::plugin(plugin, "presets/" + model->slug);
  133. for (const std::string &presetPath : system::listEntries(presetDir)) {
  134. DEBUG("%s", presetPath.c_str());
  135. model->presetPaths.push_back(presetPath);
  136. }
  137. }
  138. return true;
  139. }
  140. static bool syncPlugin(std::string slug, json_t *manifestJ, bool dryRun) {
  141. // Check that "status" is "available"
  142. json_t *statusJ = json_object_get(manifestJ, "status");
  143. if (!statusJ) {
  144. return false;
  145. }
  146. std::string status = json_string_value(statusJ);
  147. if (status != "available") {
  148. return false;
  149. }
  150. // Get latest version
  151. json_t *latestVersionJ = json_object_get(manifestJ, "latestVersion");
  152. if (!latestVersionJ) {
  153. WARN("Could not get latest version of plugin %s", slug.c_str());
  154. return false;
  155. }
  156. std::string latestVersion = json_string_value(latestVersionJ);
  157. // Check whether we already have a plugin with the same slug and version
  158. Plugin *plugin = getPlugin(slug);
  159. if (plugin && plugin->version == latestVersion) {
  160. return false;
  161. }
  162. json_t *nameJ = json_object_get(manifestJ, "name");
  163. std::string name;
  164. if (nameJ) {
  165. name = json_string_value(nameJ);
  166. }
  167. else {
  168. name = slug;
  169. }
  170. #if defined ARCH_WIN
  171. std::string arch = "win";
  172. #elif ARCH_MAC
  173. std::string arch = "mac";
  174. #elif defined ARCH_LIN
  175. std::string arch = "lin";
  176. #endif
  177. std::string downloadUrl;
  178. downloadUrl = app::API_URL;
  179. downloadUrl += "/download";
  180. if (dryRun) {
  181. downloadUrl += "/available";
  182. }
  183. downloadUrl += "?token=" + network::encodeUrl(settings::token);
  184. downloadUrl += "&slug=" + network::encodeUrl(slug);
  185. downloadUrl += "&version=" + network::encodeUrl(latestVersion);
  186. downloadUrl += "&arch=" + network::encodeUrl(arch);
  187. if (dryRun) {
  188. // Check if available
  189. json_t *availableResJ = network::requestJson(network::METHOD_GET, downloadUrl, NULL);
  190. if (!availableResJ) {
  191. WARN("Could not check whether download is available");
  192. return false;
  193. }
  194. DEFER({
  195. json_decref(availableResJ);
  196. });
  197. json_t *successJ = json_object_get(availableResJ, "success");
  198. return json_boolean_value(successJ);
  199. }
  200. else {
  201. downloadName = name;
  202. downloadProgress = 0.0;
  203. INFO("Downloading plugin %s %s %s", slug.c_str(), latestVersion.c_str(), arch.c_str());
  204. // Download zip
  205. std::string pluginDest = asset::user("plugins/" + slug + ".zip");
  206. if (!network::requestDownload(downloadUrl, pluginDest, &downloadProgress)) {
  207. WARN("Plugin %s download was unsuccessful", slug.c_str());
  208. return false;
  209. }
  210. downloadName = "";
  211. return true;
  212. }
  213. }
  214. static void loadPlugins(std::string path) {
  215. std::string message;
  216. for (std::string pluginPath : system::listEntries(path)) {
  217. if (!system::isDirectory(pluginPath))
  218. continue;
  219. if (!loadPlugin(pluginPath)) {
  220. // Ignore bad plugins. They are reported in log.txt.
  221. }
  222. }
  223. }
  224. /** Returns 0 if successful */
  225. static int extractZipHandle(zip_t *za, const char *dir) {
  226. int err;
  227. for (int i = 0; i < zip_get_num_entries(za, 0); i++) {
  228. zip_stat_t zs;
  229. err = zip_stat_index(za, i, 0, &zs);
  230. if (err) {
  231. WARN("zip_stat_index() failed: error %d", err);
  232. return err;
  233. }
  234. int nameLen = strlen(zs.name);
  235. char path[MAXPATHLEN];
  236. snprintf(path, sizeof(path), "%s/%s", dir, zs.name);
  237. if (zs.name[nameLen - 1] == '/') {
  238. if (mkdir(path, 0755)) {
  239. if (errno != EEXIST) {
  240. WARN("mkdir(%s) failed: error %d", path, errno);
  241. return errno;
  242. }
  243. }
  244. }
  245. else {
  246. zip_file_t *zf = zip_fopen_index(za, i, 0);
  247. if (!zf) {
  248. WARN("zip_fopen_index() failed");
  249. return -1;
  250. }
  251. FILE *outFile = fopen(path, "wb");
  252. if (!outFile)
  253. continue;
  254. while (1) {
  255. char buffer[1<<15];
  256. int len = zip_fread(zf, buffer, sizeof(buffer));
  257. if (len <= 0)
  258. break;
  259. fwrite(buffer, 1, len, outFile);
  260. }
  261. err = zip_fclose(zf);
  262. if (err) {
  263. WARN("zip_fclose() failed: error %d", err);
  264. return err;
  265. }
  266. fclose(outFile);
  267. }
  268. }
  269. return 0;
  270. }
  271. /** Returns 0 if successful */
  272. static int extractZip(const char *filename, const char *path) {
  273. int err;
  274. zip_t *za = zip_open(filename, 0, &err);
  275. if (!za) {
  276. WARN("Could not open zip %s: error %d", filename, err);
  277. return err;
  278. }
  279. DEFER({
  280. zip_close(za);
  281. });
  282. err = extractZipHandle(za, path);
  283. return err;
  284. }
  285. static void extractPackages(const std::string &path) {
  286. std::string message;
  287. for (std::string packagePath : system::listEntries(path)) {
  288. if (string::extension(packagePath) != "zip")
  289. continue;
  290. INFO("Extracting package %s", packagePath.c_str());
  291. // Extract package
  292. if (extractZip(packagePath.c_str(), path.c_str())) {
  293. WARN("Package %s failed to extract", packagePath.c_str());
  294. message += string::f("Could not extract package %s\n", packagePath.c_str());
  295. continue;
  296. }
  297. // Remove package
  298. if (remove(packagePath.c_str())) {
  299. WARN("Could not delete file %s: error %d", packagePath.c_str(), errno);
  300. }
  301. }
  302. if (!message.empty()) {
  303. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  304. }
  305. }
  306. ////////////////////
  307. // public API
  308. ////////////////////
  309. void init() {
  310. // Load Core
  311. Plugin *corePlugin = new Plugin;
  312. // This function is defined in Core/plugin.cpp
  313. ::init(corePlugin);
  314. plugins.push_back(corePlugin);
  315. // Get user plugins directory
  316. std::string pluginsDir = asset::user("plugins");
  317. mkdir(pluginsDir.c_str(), 0755);
  318. // Extract packages and load plugins
  319. extractPackages(pluginsDir);
  320. loadPlugins(pluginsDir);
  321. // Copy Fundamental package to plugins directory if Fundamental is not loaded
  322. std::string fundamentalSrc = asset::system("Fundamental.zip");
  323. std::string fundamentalDir = asset::user("plugins/Fundamental");
  324. if (!settings::devMode && !getPlugin("Fundamental") && system::isFile(fundamentalSrc)) {
  325. extractZip(fundamentalSrc.c_str(), pluginsDir.c_str());
  326. loadPlugin(fundamentalDir);
  327. }
  328. }
  329. void destroy() {
  330. for (Plugin *plugin : plugins) {
  331. // Free library handle
  332. #if defined ARCH_WIN
  333. if (plugin->handle)
  334. FreeLibrary((HINSTANCE) plugin->handle);
  335. #else
  336. if (plugin->handle)
  337. dlclose(plugin->handle);
  338. #endif
  339. // For some reason this segfaults.
  340. // It might be best to let them leak anyway, because "crash on exit" issues would occur with badly-written plugins.
  341. // delete plugin;
  342. }
  343. plugins.clear();
  344. }
  345. void logIn(const std::string &email, const std::string &password) {
  346. json_t *reqJ = json_object();
  347. json_object_set(reqJ, "email", json_string(email.c_str()));
  348. json_object_set(reqJ, "password", json_string(password.c_str()));
  349. std::string tokenUrl = app::API_URL;
  350. tokenUrl += "/token";
  351. json_t *resJ = network::requestJson(network::METHOD_POST, tokenUrl, reqJ);
  352. json_decref(reqJ);
  353. if (resJ) {
  354. json_t *errorJ = json_object_get(resJ, "error");
  355. if (errorJ) {
  356. const char *errorStr = json_string_value(errorJ);
  357. loginStatus = errorStr;
  358. }
  359. else {
  360. json_t *tokenJ = json_object_get(resJ, "token");
  361. if (tokenJ) {
  362. const char *tokenStr = json_string_value(tokenJ);
  363. settings::token = tokenStr;
  364. loginStatus = "";
  365. }
  366. }
  367. json_decref(resJ);
  368. }
  369. }
  370. void logOut() {
  371. settings::token = "";
  372. }
  373. bool sync(bool dryRun) {
  374. if (settings::token.empty())
  375. return false;
  376. bool available = false;
  377. if (!dryRun) {
  378. isDownloading = true;
  379. downloadProgress = 0.0;
  380. downloadName = "Updating plugins...";
  381. }
  382. DEFER({
  383. isDownloading = false;
  384. });
  385. // Get user's plugins list
  386. json_t *pluginsReqJ = json_object();
  387. json_object_set(pluginsReqJ, "token", json_string(settings::token.c_str()));
  388. std::string pluginsUrl = app::API_URL;
  389. pluginsUrl += "/plugins";
  390. json_t *pluginsResJ = network::requestJson(network::METHOD_GET, pluginsUrl, pluginsReqJ);
  391. json_decref(pluginsReqJ);
  392. if (!pluginsResJ) {
  393. WARN("Request for user's plugins failed");
  394. return false;
  395. }
  396. DEFER({
  397. json_decref(pluginsResJ);
  398. });
  399. json_t *errorJ = json_object_get(pluginsResJ, "error");
  400. if (errorJ) {
  401. WARN("Request for user's plugins returned an error: %s", json_string_value(errorJ));
  402. return false;
  403. }
  404. // Get community manifests
  405. std::string manifestsUrl = app::API_URL;
  406. manifestsUrl += "/community/manifests";
  407. json_t *manifestsResJ = network::requestJson(network::METHOD_GET, manifestsUrl, NULL);
  408. if (!manifestsResJ) {
  409. WARN("Request for community manifests failed");
  410. return false;
  411. }
  412. DEFER({
  413. json_decref(manifestsResJ);
  414. });
  415. // Check each plugin in list of plugin slugs
  416. json_t *pluginsJ = json_object_get(pluginsResJ, "plugins");
  417. if (!pluginsJ) {
  418. WARN("No plugins array");
  419. return false;
  420. }
  421. json_t *manifestsJ = json_object_get(manifestsResJ, "manifests");
  422. if (!manifestsJ) {
  423. WARN("No manifests object");
  424. return false;
  425. }
  426. size_t slugIndex;
  427. json_t *slugJ;
  428. json_array_foreach(pluginsJ, slugIndex, slugJ) {
  429. std::string slug = json_string_value(slugJ);
  430. // Search for slug in manifests
  431. const char *manifestSlug;
  432. json_t *manifestJ = NULL;
  433. json_object_foreach(manifestsJ, manifestSlug, manifestJ) {
  434. if (slug == std::string(manifestSlug))
  435. break;
  436. }
  437. if (!manifestJ)
  438. continue;
  439. if (syncPlugin(slug, manifestJ, dryRun)) {
  440. available = true;
  441. }
  442. }
  443. return available;
  444. }
  445. void cancelDownload() {
  446. // TODO
  447. }
  448. bool isLoggedIn() {
  449. return settings::token != "";
  450. }
  451. Plugin *getPlugin(const std::string &pluginSlug) {
  452. for (Plugin *plugin : plugins) {
  453. if (plugin->slug == pluginSlug) {
  454. return plugin;
  455. }
  456. }
  457. return NULL;
  458. }
  459. Model *getModel(const std::string &pluginSlug, const std::string &modelSlug) {
  460. Plugin *plugin = getPlugin(pluginSlug);
  461. if (!plugin)
  462. return NULL;
  463. Model *model = plugin->getModel(modelSlug);
  464. if (!model)
  465. return NULL;
  466. return model;
  467. }
  468. /** List of allowed tags in human display form, alphabetized.
  469. All tags here should be in sentence caps for display consistency.
  470. However, tags are case-insensitive in plugin metadata.
  471. */
  472. const std::set<std::string> allowedTags = {
  473. "Arpeggiator",
  474. "Attenuator", // With a level knob and not much else.
  475. "Blank", // No parameters or ports. Serves no purpose except visual.
  476. "Chorus",
  477. "Clock generator",
  478. "Clock modulator", // Clock dividers, multipliers, etc.
  479. "Compressor", // With threshold, ratio, knee, etc parameters.
  480. "Controller", // Use only if the artist "performs" with this module. Simply having knobs is not enough. Examples: on-screen keyboard, XY pad.
  481. "Delay",
  482. "Digital",
  483. "Distortion",
  484. "Drum",
  485. "Dual", // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module.
  486. "Dynamics",
  487. "Effect",
  488. "Envelope follower",
  489. "Envelope generator",
  490. "Equalizer",
  491. "Expander", // Expands the functionality of a "mother" module when placed next to it. Expanders should inherit the tags of its mother module.
  492. "External",
  493. "Flanger",
  494. "Function generator",
  495. "Granular",
  496. "LFO",
  497. "Limiter",
  498. "Logic",
  499. "Low pass gate",
  500. "MIDI",
  501. "Mixer",
  502. "Multiple",
  503. "Noise",
  504. "Panning",
  505. "Phaser",
  506. "Physical modeling",
  507. "Polyphonic",
  508. "Quad", // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module.
  509. "Quantizer",
  510. "Random",
  511. "Recording",
  512. "Reverb",
  513. "Ring modulator",
  514. "Sample and hold",
  515. "Sampler",
  516. "Sequencer",
  517. "Slew limiter",
  518. "Switch",
  519. "Synth voice", // A synth voice must have, at the minimum, a built-in oscillator and envelope.
  520. "Tuner",
  521. "Utility", // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc.
  522. "VCA",
  523. "VCF",
  524. "VCO",
  525. "Visual",
  526. "Vocoder",
  527. "Waveshaper",
  528. };
  529. /** List of common synonyms for allowed tags.
  530. Aliases and tags must be lowercase.
  531. */
  532. const std::map<std::string, std::string> tagAliases = {
  533. {"amplifier", "vca"},
  534. {"clock", "clock generator"},
  535. {"drums", "drum"},
  536. {"eq", "equalizer"},
  537. {"filter", "vcf"},
  538. {"low frequency oscillator", "lfo"},
  539. {"lowpass gate", "low pass gate"},
  540. {"oscillator", "vco"},
  541. {"percussion", "drum"},
  542. {"poly", "polyphonic"},
  543. {"s&h", "sample and hold"},
  544. {"voltage controlled amplifier", "vca"},
  545. {"voltage controlled filter", "vcf"},
  546. {"voltage controlled oscillator", "vco"},
  547. };
  548. std::string normalizeTag(const std::string &tag) {
  549. std::string lowercaseTag = string::lowercase(tag);
  550. // Transform aliases
  551. auto it = tagAliases.find(lowercaseTag);
  552. if (it != tagAliases.end())
  553. lowercaseTag = it->second;
  554. // Find allowed tag
  555. for (const std::string &allowedTag : allowedTags) {
  556. if (lowercaseTag == string::lowercase(allowedTag))
  557. return allowedTag;
  558. }
  559. return "";
  560. }
  561. bool isSlugValid(const std::string &slug) {
  562. for (char c : slug) {
  563. if (!(std::isalnum(c) || c == '-' || c == '_'))
  564. return false;
  565. }
  566. return true;
  567. }
  568. std::vector<Plugin*> plugins;
  569. bool isDownloading = false;
  570. float downloadProgress = 0.f;
  571. std::string downloadName;
  572. std::string loginStatus;
  573. } // namespace plugin
  574. } // namespace rack