diff --git a/hi_backend/backend/CompileExporter.cpp b/hi_backend/backend/CompileExporter.cpp index b6c154968a..74f83d54b5 100644 --- a/hi_backend/backend/CompileExporter.cpp +++ b/hi_backend/backend/CompileExporter.cpp @@ -651,19 +651,17 @@ bool CompileExporter::checkSanity(BuildOption option) const String pluginCode = SettingWindows::getSettingValue((int)SettingWindows::ProjectSettingWindow::Attributes::PluginCode, handler); - const String codeWildcard = "[A-Z][a-z][a-z][a-z]"; - - if(!RegexFunctions::matchesWildcard(codeWildcard, pluginCode)) + if(!AudioUnitCodeValidator::isValidSubtype(pluginCode)) { - printErrorMessage("Illegal Project code", "The Plugin Code must have this structure: 'Abcd'"); + printErrorMessage("Illegal Project code", "The Plugin Code must match [a-zA-Z0-9-_]{4}"); return false; } const String companyCode = SettingWindows::getSettingValue((int)SettingWindows::UserSettingWindow::Attributes::CompanyCode, handler); - - if(!RegexFunctions::matchesWildcard(codeWildcard, companyCode)) + + if(!AudioUnitCodeValidator::isValidManufacturer(companyCode)) { - printErrorMessage("Illegal Company code", "The Company Code must have this structure: 'Abcd'"); + printErrorMessage("Illegal Company code", "The Company Code must match [a-zA-Z0-9-_]{4} with at least one upper case letter"); return false; } diff --git a/hi_core/hi_core/SettingsWindows.cpp b/hi_core/hi_core/SettingsWindows.cpp index 97a704966c..2af346168e 100644 --- a/hi_core/hi_core/SettingsWindows.cpp +++ b/hi_core/hi_core/SettingsWindows.cpp @@ -296,12 +296,11 @@ String SettingWindows::ProjectSettingWindow::sanityCheck(const XmlElement& xmlSe "This is required for the user presets to detect whether it should ask for updating the presets after a version bump."; }; - const String pluginCode = GET_VALUE_FROM_XML(ProjectSettingWindow::Attributes::PluginCode); - const String codeWildcard = "[A-Z][a-z][a-z][a-z]"; + const String pluginCode = GET_VALUE_FROM_XML(ProjectSettingWindow::Attributes::PluginCode); - if (!RegexFunctions::matchesWildcard(codeWildcard, pluginCode)) + if (!AudioUnitCodeValidator::isValidSubtype(pluginCode)) { - return "The plugin code doesn't match the required formula. Use something like 'Abcd'\n" \ + return "The plugin code must contain only upper- or lower-case letters from a-z, numbers from 0-9, and - or _.\n" \ "This is required for exported AU plugins to pass the AU validation."; }; diff --git a/hi_core/hi_core/UtilityClasses.cpp b/hi_core/hi_core/UtilityClasses.cpp index 969d8b2f51..7ba04e3e39 100644 --- a/hi_core/hi_core/UtilityClasses.cpp +++ b/hi_core/hi_core/UtilityClasses.cpp @@ -566,7 +566,34 @@ bool RegexFunctions::matchesWildcard(const String &wildcard, const String &strin return false; } #endif -} +} + +/** + Apple's documentation + (https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/AudioUnitDevelopmentFundamentals/AudioUnitDevelopmentFundamentals.html#//apple_ref/doc/uid/TP40003278-CH7-SW1) + states that we "are free to use any subtype code, including subtypes named with only lowercase letters" and + "Manufacturer codes must contain at least one uppercase character." + + This is evidenced by many large vendors' plugins: + - Native Instruments uses "-NI-" as their manufacturer code, + using numbers in subtype codes (i.e.: Maschine 2 is "NiM2") + - TAL Software's TAL Filter 2 uses "025e" as its subtype code + - Waves uses "ksWV" as their manufacturer code + - iZotope uses "iZtp" as their manufacturer code + + To satisfy these requirements as simply as possible, these methods check that: + - all characters are within [a-zA-Z0-9_-] + - for manufacturer codes, at least one character is an upper case letter. +*/ +bool AudioUnitCodeValidator::isValidSubtype(const String &fourCC) +{ + return fourCC.length() == 4 && fourCC.containsOnly("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-"); +} + +bool AudioUnitCodeValidator::isValidManufacturer(const String &fourCC) +{ + return AudioUnitCodeValidator::isValidSubtype(fourCC) && fourCC.containsAnyOf("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +} ScopedNoDenormals::ScopedNoDenormals() { diff --git a/hi_core/hi_core/UtilityClasses.h b/hi_core/hi_core/UtilityClasses.h index a9133d5294..345d4a8334 100644 --- a/hi_core/hi_core/UtilityClasses.h +++ b/hi_core/hi_core/UtilityClasses.h @@ -56,7 +56,17 @@ class RegexFunctions static bool matchesWildcard(const String &wildcard, const String &stringToTest, const Processor* /*processorForErrorOutput*/=nullptr); }; - + +/** A Helper class for validating Audio Unit codes */ +class AudioUnitCodeValidator +{ +public: + /** Checks if the given string is a valid Apple AU subtype code. */ + static bool isValidSubtype(const String &fourCC); + + /** Checks if the given string is a valid Apple AU manufacturer code. */ + static bool isValidManufacturer(const String &fourCC); +}; /** A small helper class that uses RAII for enabling flush to zero mode. */ class ScopedNoDenormals