mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-06 19:40:10 +02:00
[yabridgectl] Add a rewriter for VST3 moduleinfo
This commit is contained in:
Generated
+24
@@ -232,6 +232,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -395,6 +401,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -427,6 +439,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_jsonrc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b591e90bcce7185aa4f8c775c504456586ae0f7df49a4087a1ee4179d402b8a8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@@ -628,6 +651,7 @@ dependencies = [
|
||||
"reflink",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_jsonrc",
|
||||
"textwrap 0.11.0",
|
||||
"toml",
|
||||
"walkdir",
|
||||
|
||||
@@ -22,6 +22,7 @@ reflink = { git = "https://github.com/nicokoch/reflink", rev = "e8d93b465f5d9ad3
|
||||
rayon = "1.5.1"
|
||||
serde = "1.0.133"
|
||||
serde_derive = "1.0.133"
|
||||
serde_jsonrc = "0.1"
|
||||
# NOTE: textwrap 0.12.0 up to at least 0.13.4 apply the subsequent indent after
|
||||
# wrapping
|
||||
textwrap = { version = "0.11.0", features = ["term_size"] }
|
||||
|
||||
@@ -27,6 +27,7 @@ mod actions;
|
||||
mod config;
|
||||
mod files;
|
||||
mod utils;
|
||||
mod vst3_moduleinfo;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// We'll modify our `PATH` environment variable so it matches up with
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// yabridge: a Wine plugin bridge
|
||||
// Copyright (C) 2020-2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::fmt::Write;
|
||||
|
||||
/// Part of the VST3 `moduleinfo.json` file:
|
||||
/// <https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/VST+Module+Architecture/ModuleInfo-JSON.html>
|
||||
///
|
||||
/// Since we only need this to rewrite the UIDs, all the other fields are stored in this `other`
|
||||
/// map. And while this is technically supposed to be JSON5, `serde_jsonrc` can also parse trailing
|
||||
/// commands and comments and works a lot better.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModuleInfo {
|
||||
#[serde(rename = "Classes")]
|
||||
classes: Vec<Class>,
|
||||
#[serde(rename = "Compatibility")]
|
||||
compatibility_mappings: Vec<CompatibilityMapping>,
|
||||
#[serde(flatten)]
|
||||
other: serde_jsonrc::Map<String, serde_jsonrc::Value>,
|
||||
}
|
||||
|
||||
/// A single class object, we only care about the CID since we need to rewrite those.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Class {
|
||||
#[serde(rename = "CID")]
|
||||
cid: String,
|
||||
#[serde(flatten)]
|
||||
other: serde_jsonrc::Map<String, serde_jsonrc::Value>,
|
||||
}
|
||||
|
||||
/// A mapping from old class IDs to new class IDs.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct CompatibilityMapping {
|
||||
#[serde(rename = "New")]
|
||||
new: String,
|
||||
#[serde(rename = "Old")]
|
||||
old: Vec<String>,
|
||||
// This will probably stay empty, but let's add it just in case the format changes.
|
||||
#[serde(flatten)]
|
||||
other: serde_jsonrc::Map<String, serde_jsonrc::Value>,
|
||||
}
|
||||
|
||||
impl ModuleInfo {
|
||||
/// Rewrite the module info in place to switch between COM-style class ID byte orders and the
|
||||
/// other style used on Linux and macOS. This is needed for cross platform plugin compatibility,
|
||||
/// because someone at Steinberg was a genius.
|
||||
pub fn rewrite_uid_byte_orders(&mut self) -> Result<()> {
|
||||
for class in &mut self.classes {
|
||||
class.cid = encode_hex_uid(&rewrite_uid_byte_order(&decode_hex_uid(&class.cid)?));
|
||||
}
|
||||
|
||||
for mapping in &mut self.compatibility_mappings {
|
||||
mapping.new = encode_hex_uid(&rewrite_uid_byte_order(&decode_hex_uid(&mapping.new)?));
|
||||
for cid in &mut mapping.old {
|
||||
*cid = encode_hex_uid(&rewrite_uid_byte_order(&decode_hex_uid(cid)?))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a hexadecimal UID from a string. Returns an error if the parsing failed.
|
||||
fn decode_hex_uid(hex_uid: &str) -> Result<[u8; 16]> {
|
||||
if hex_uid.len() != 32 {
|
||||
anyhow::bail!("Incorrect UID hex string length: {hex_uid:?}");
|
||||
}
|
||||
|
||||
// `u8::from_str_radix` only works with str slices, and there's no way to iterate over strings
|
||||
// in str slices, so iterating over indices and manually slicing is the only solution ehre
|
||||
let mut uid = [0; 16];
|
||||
for (idx, uid_byte) in uid.iter_mut().enumerate() {
|
||||
let start_idx = idx * 2;
|
||||
let end_idx = start_idx + 2;
|
||||
*uid_byte = u8::from_str_radix(&hex_uid[start_idx..end_idx], 16)
|
||||
.with_context(|| format!("Invalid hexadecimal string: {hex_uid:?}"))?;
|
||||
}
|
||||
|
||||
Ok(uid)
|
||||
}
|
||||
|
||||
/// Format a UID stored in a byte array as a 16 character hexadecimal string.
|
||||
fn encode_hex_uid(uid: &[u8; 16]) -> String {
|
||||
let mut hex_uid = String::with_capacity(uid.len() * 2);
|
||||
for b in uid {
|
||||
write!(&mut hex_uid, "{:02X}", b).unwrap();
|
||||
}
|
||||
|
||||
hex_uid
|
||||
}
|
||||
|
||||
/// Switch between the COM and non-COM byte orders for a UID.
|
||||
fn rewrite_uid_byte_order(old_uid: &[u8; 16]) -> [u8; 16] {
|
||||
let mut new_uid = *old_uid;
|
||||
|
||||
new_uid[0] = old_uid[3];
|
||||
new_uid[1] = old_uid[2];
|
||||
new_uid[2] = old_uid[1];
|
||||
new_uid[3] = old_uid[0];
|
||||
|
||||
new_uid[4] = old_uid[5];
|
||||
new_uid[5] = old_uid[4];
|
||||
new_uid[6] = old_uid[7];
|
||||
new_uid[7] = old_uid[6];
|
||||
|
||||
new_uid
|
||||
}
|
||||
Reference in New Issue
Block a user