diff --git a/tools/yabridgectl/src/actions.rs b/tools/yabridgectl/src/actions.rs index 87127956..5b5a4a47 100644 --- a/tools/yabridgectl/src/actions.rs +++ b/tools/yabridgectl/src/actions.rs @@ -43,7 +43,7 @@ pub fn remove_directory(config: &mut Config, path: &Path) -> Result<()> { // Ask the user to remove any leftover files to prevent possible future problems and out of date // copies - let orphan_files = files::index_so_files(path); + let orphan_files = files::index(path).so_files; if !orphan_files.is_empty() { println!( "Warning: Found {} leftover .so files still in this directory:", @@ -84,7 +84,7 @@ pub fn list_directories(config: &Config) -> Result<()> { /// Print the current configuration and the installation status for all found plugins. pub fn show_status(config: &Config) -> Result<()> { let results = config - .index_directories() + .search_directories() .context("Failure while searching for plugins")?; println!( @@ -171,7 +171,7 @@ pub fn do_sync(config: &mut Config, options: &SyncOptions) -> Result<()> { println!("Using '{}'\n", files.libyabridge_vst2.display()); let results = config - .index_directories() + .search_directories() .context("Failure while searching for plugins")?; // Keep track of some global statistics diff --git a/tools/yabridgectl/src/config.rs b/tools/yabridgectl/src/config.rs index f891e7c7..b24510fd 100644 --- a/tools/yabridgectl/src/config.rs +++ b/tools/yabridgectl/src/config.rs @@ -255,10 +255,14 @@ impl Config { /// Search for VST2 and VST3 plugins in all of the registered plugins directories. This will /// return an error if `winedump` could not be called. - pub fn index_directories(&self) -> Result> { + pub fn search_directories(&self) -> Result> { self.plugin_dirs .par_iter() - .map(|path| files::index(path).map(|search_results| (path.as_path(), search_results))) + .map(|path| { + files::index(path) + .search() + .map(|search_results| (path.as_path(), search_results)) + }) .collect() } } diff --git a/tools/yabridgectl/src/files.rs b/tools/yabridgectl/src/files.rs index 7209eade..15523948 100644 --- a/tools/yabridgectl/src/files.rs +++ b/tools/yabridgectl/src/files.rs @@ -36,9 +36,24 @@ pub struct SearchResults { pub vst2_files: Vec, /// Absolute paths to found VST3 modules. Either legacy `.vst3` DLL files or VST 3.6.10 bundles. pub vst3_modules: Vec, - /// `.dll` files skipped over during the serach. Used for printing statistics and shown when + /// `.dll` files skipped over during the search. Used for printing statistics and shown when /// running `yabridgectl sync --verbose`. pub skipped_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, +} + +/// The results of the first step of the search process. We'll first index all possibly relevant +/// files in a directory before filtering them down to a `SearchResults` object. +#[derive(Debug)] +pub struct SearchIndex { + /// Any `.dll` file. + pub dll_files: Vec, + /// Any `.vst3` file or directory. This can be either a legacy `.vst3` DLL module or a VST + /// 3.6.10 module (or some kind of random other file, of course). + pub vst3_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, @@ -68,6 +83,8 @@ impl SearchResults { /// /// These two functions could be combined into a single function, but speed isn't really an /// issue here and it's a bit more organized this way. + /// + /// TODO: Do this for VST3 plugins pub fn installation_status(&self) -> BTreeMap<&Path, Option<&NativeSoFile>> { let so_files: HashMap<&Path, &NativeSoFile> = self .so_files @@ -87,6 +104,8 @@ impl SearchResults { } /// Find all `.so` files in the search results that do not belong to a VST2 plugin `.dll` file. + /// + /// TODO: Also do something similar for VST3 plugins pub fn orphans(&self) -> Vec<&NativeSoFile> { // We need to store these in a map so we can easily entries with corresponding `.dll` files let mut orphans: HashMap<&Path, &NativeSoFile> = self @@ -98,7 +117,7 @@ impl SearchResults { orphans.remove(vst2_path.with_extension("so").as_path()); } - orphans.into_iter().map(|(_, file)| file).collect() + orphans.values().cloned().collect() } } @@ -112,69 +131,11 @@ impl NativeSoFile { } } -/// 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 (dll_files, so_files) = find_files(directory); - - lazy_static! { - static ref VST2_AUTOMATON: AhoCorasick = - AhoCorasick::new_auto_configured(&["VSTPluginMain", "main", "main_plugin"]); - } - - // THne we'll figure out which `.dll` files are VST2 plugins and which should be skipped by - // checking whether the file contains one of the VST2 entry point functions. The boolean flag in - // this vector indicates whether it is a VST2 plugin. - let dll_files: Vec<(PathBuf, bool)> = dll_files - .into_par_iter() - .map(|path| { - let exported_functions = Command::new("winedump") - .arg("-j") - .arg("export") - .arg(&path) - .output() - .context( - "Could not find 'winedump'. In some distributions this is part of a seperate \ - Wine tools package.", - )? - .stdout; - - Ok((path, VST2_AUTOMATON.is_match(exported_functions))) - }) - .collect::>()?; - - let mut vst2_files = Vec::new(); - let mut skipped_files = Vec::new(); - for (path, is_vst2_plugin) in dll_files { - if is_vst2_plugin { - vst2_files.push(path); - } else { - skipped_files.push(path); - } - } - - Ok(SearchResults { - vst2_files, - skipped_files, - so_files, - // TODO: Search for VST3 modules - vst3_modules: Vec::new(), - }) -} - -/// THe same as [index()](index), but only report found `.so` files. This avoids unnecesarily -/// filtering the found `.dll` files. -pub fn index_so_files(directory: &Path) -> Vec { - let (_, so_files) = find_files(directory); - - so_files -} - -/// Find all `.dll` and `.so` files under a directory. The results are a pair of `(dll_files, -/// so_files)`. -fn find_files(directory: &Path) -> (Vec, Vec) { +/// Find all `.dll`, `.vst3` and `.so` files under a directory. These results can be filtered down +/// to actual VST2 plugins and VST3 modules using `search()`. +pub fn index(directory: &Path) -> SearchIndex { let mut dll_files: Vec = Vec::new(); + let mut vst3_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. @@ -198,6 +159,7 @@ fn find_files(directory: &Path) -> (Vec, Vec) { match entry.path().extension().and_then(|os| os.to_str()) { Some("dll") => dll_files.push(entry.into_path()), + Some("vst3") => vst3_files.push(entry.into_path()), Some("so") => { if entry.path_is_symlink() { so_files.push(NativeSoFile::Symlink(entry.into_path())); @@ -209,5 +171,61 @@ fn find_files(directory: &Path) -> (Vec, Vec) { } } - (dll_files, so_files) + SearchIndex { + dll_files, + vst3_files, + so_files, + } +} + +impl SearchIndex { + /// Filter these indexing results down to actual VST2 plugins and VST3 modules. This will skip + /// all invalid files, such as regular `.dll` libraries. Will return an error if `winedump` + /// could not be found. + pub fn search(self) -> Result { + lazy_static! { + static ref VST2_AUTOMATON: AhoCorasick = + AhoCorasick::new_auto_configured(&["VSTPluginMain", "main", "main_plugin"]); + } + + // THne we'll figure out which `.dll` files are VST2 plugins and which should be skipped by + // checking whether the file contains one of the VST2 entry point functions. The boolean flag in + // this vector indicates whether it is a VST2 plugin. + let dll_files: Vec<(PathBuf, bool)> = self + .dll_files + .into_par_iter() + .map(|path| { + let exported_functions = Command::new("winedump") + .arg("-j") + .arg("export") + .arg(&path) + .output() + .context( + "Could not find 'winedump'. In some distributions this is part of a seperate \ + Wine tools package.", + )? + .stdout; + + Ok((path, VST2_AUTOMATON.is_match(exported_functions))) + }) + .collect::>()?; + + let mut vst2_files: Vec = Vec::new(); + let mut skipped_files: Vec = Vec::new(); + for (path, is_vst2_plugin) in dll_files { + if is_vst2_plugin { + vst2_files.push(path); + } else { + skipped_files.push(path); + } + } + + Ok(SearchResults { + vst2_files, + // TODO: Search for VST3 modules + vst3_modules: Vec::new(), + skipped_files, + so_files: self.so_files, + }) + } }