From 7a8e0ed5cd492cdb0d5fc2b283ddd669b5715b04 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 13 Jul 2020 19:08:38 +0200 Subject: [PATCH] Add VST2 file indexing to yabridgectl --- tools/yabridgectl/Cargo.lock | 1 + tools/yabridgectl/Cargo.toml | 1 + tools/yabridgectl/src/files.rs | 106 +++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 tools/yabridgectl/src/files.rs diff --git a/tools/yabridgectl/Cargo.lock b/tools/yabridgectl/Cargo.lock index 755c06f8..c05c3344 100644 --- a/tools/yabridgectl/Cargo.lock +++ b/tools/yabridgectl/Cargo.lock @@ -440,6 +440,7 @@ version = "1.2.1" dependencies = [ "aho-corasick", "clap", + "lazy_static", "rayon", "serde", "serde_derive", diff --git a/tools/yabridgectl/Cargo.toml b/tools/yabridgectl/Cargo.toml index 5594950d..f6f2c5f0 100644 --- a/tools/yabridgectl/Cargo.toml +++ b/tools/yabridgectl/Cargo.toml @@ -12,6 +12,7 @@ license = "GPL-3.0-or-later" [dependencies] aho-corasick = "0.7.13" clap = "3.0.0-beta.1" +lazy_static = "1.4.0" rayon = "1.3.1" serde = "1.0.114" serde_derive = "1.0.114" diff --git a/tools/yabridgectl/src/files.rs b/tools/yabridgectl/src/files.rs new file mode 100644 index 00000000..efc965e6 --- /dev/null +++ b/tools/yabridgectl/src/files.rs @@ -0,0 +1,106 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// 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. +// +// 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. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Functions to index plugins and to set up yabridge for those plugins. + +use aho_corasick::AhoCorasick; +use lazy_static::lazy_static; +use rayon::prelude::*; +use std::path::{Path, PathBuf}; +use std::process::Command; +use walkdir::WalkDir; + +/// Stores the results from searching for Windows VST plugin `.dll` files and native Linux `.so` +/// files inside of a directory. These `.so` files are kept track of so we can report the current +/// installation status and to be able to purge orphan files. +#[derive(Debug)] +pub struct SearchResults { + /// Absolute paths to the found VST2 `.dll` files. + pub vst2_files: Vec, + /// Absolute paths to any `.so` files inside of the directory, and whether they're a symlink or + /// a regular file. + pub so_files: Vec, +} + +#[derive(Debug)] +pub enum FoundFile { + Symlink(PathBuf), + Regular(PathBuf), +} + +/// Search for Windows VST2 plugins and .so files under a directory. This will return an error if +/// the directory does not exist, or if `winedump` could not be found. +pub fn index(directory: &Path) -> Result { + // First we'll find all .dll and .so files in the directory + let mut dll_files: Vec = Vec::new(); + let mut so_files: Vec = Vec::new(); + // XXX: We're silently skipping directories and files we don't have permission to read. This + // sounds like the expected behavior, but I"m not entirely sure. + for entry in WalkDir::new(directory) + .follow_links(true) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|x| !x.file_type().is_dir()) + { + match entry.path().extension().and_then(|os| os.to_str()) { + Some("dll") => dll_files.push(entry.into_path()), + Some("so") => { + if entry.path_is_symlink() { + so_files.push(FoundFile::Symlink(entry.into_path())); + } else { + so_files.push(FoundFile::Regular(entry.into_path())); + } + } + _ => (), + } + } + + // Then we'll filter out any .dll files that are not VST2 plugins by checking whether the + // exptected entry points for VST2 plugins are present + lazy_static! { + static ref VST2_AUTOMATON: AhoCorasick = + AhoCorasick::new_auto_configured(&["VSTPluginMain", "main", "main_plugin"]); + } + + let vst2_files: Vec = dll_files + .into_par_iter() + .map(|path| { + let exported_functions = Command::new("winedump") + .arg("-j") + .arg("export") + .arg(&path) + .output()? + .stdout; + + Ok((path, exported_functions)) + }) + .filter_map(|result| match result { + Ok((path, exported_functions)) => { + if VST2_AUTOMATON.is_match(exported_functions) { + Some(Ok(path)) + } else { + None + } + } + Err(err) => Some(Err(err)), + }) + .collect::>()?; + + Ok(SearchResults { + vst2_files, + so_files, + }) +}