Browse Source

VST3: Add moduleinfotool sources

v7.0.9
reuk 2 years ago
parent
commit
417f0e9ca3
No known key found for this signature in database GPG Key ID: FCB43929F012EE5C
17 changed files with 8026 additions and 0 deletions
  1. +366
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h
  2. +366
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp
  3. +340
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp
  4. +212
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h
  5. +361
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp
  6. +390
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm
  7. +610
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp
  8. +43
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md
  9. +3403
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h
  10. +427
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h
  11. +99
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h
  12. +309
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp
  13. +67
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h
  14. +536
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
  15. +68
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h
  16. +135
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h
  17. +294
    -0
      modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h

+ 366
- 0
modules/juce_audio_processors/format_types/VST3_SDK/base/source/fcommandline.h View File

@@ -0,0 +1,366 @@
//------------------------------------------------------------------------
// Project : SDK Base
// Version : 1.0
//
// Category : Helpers
// Filename : base/source/fcommandline.h
// Created by : Steinberg, 2007
// Description : Very simple command-line parser.
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------
/** @file base/source/fcommandline.h
Very simple command-line parser.
@see Steinberg::CommandLine */
//------------------------------------------------------------------------
#pragma once

#include <algorithm>
#include <deque>
#include <initializer_list>
#include <iterator>
#include <map>
#include <sstream>
#include <vector>

namespace Steinberg {
//------------------------------------------------------------------------
/** Very simple command-line parser.

Parses the command-line into a CommandLine::VariablesMap.\n
The command-line parser uses CommandLine::Descriptions to define the available options.

@b Example:
\code
#include "base/source/fcommandline.h"
#include <iostream>

int main (int argc, char* argv[])
{
using namespace std;

CommandLine::Descriptions desc;
CommandLine::VariablesMap valueMap;

desc.addOptions ("myTool")
("help", "produce help message")
("opt1", string(), "option 1")
("opt2", string(), "option 2")
;
CommandLine::parse (argc, argv, desc, valueMap);

if (valueMap.hasError () || valueMap.count ("help"))
{
cout << desc << "\n";
return 1;
}
if (valueMap.count ("opt1"))
{
cout << "Value of option 1 " << valueMap["opt1"] << "\n";
}
if (valueMap.count ("opt2"))
{
cout << "Value of option 2 " << valueMap["opt2"] << "\n";
}
return 0;
}
\endcode
@note
This is a "header only" implementation.\n
If you need the declarations in more than one cpp file, you have to define
@c SMTG_NO_IMPLEMENTATION in all but one file.

*/
//------------------------------------------------------------------------
namespace CommandLine {
//------------------------------------------------------------------------
/** Command-line parsing result.
This is the result of the parser.\n
- Use hasError() to check for errors.\n
- To test if a option was specified on the command-line use: count()\n
- To retrieve the value of an options, use operator [](const VariablesMapContainer::key_type k)\n
*/
//------------------------------------------------------------------------
class VariablesMap
{
bool mParaError;
using VariablesMapContainer = std::map<std::string, std::string>;
VariablesMapContainer mVariablesMapContainer;
public:
VariablesMap () : mParaError (false) {} ///< Constructor. Creates a empty VariablesMap.
bool hasError () const { return mParaError; } ///< Returns @c true when an error has occurred.
void setError () { mParaError = true; } ///< Sets the error state to @c true.
std::string& operator [](const VariablesMapContainer::key_type k); ///< Retrieve the value of option @c k.
const std::string& operator [](const VariablesMapContainer::key_type k) const; ///< Retrieve the value of option @c k.
VariablesMapContainer::size_type count (const VariablesMapContainer::key_type k) const; ///< Returns @c != @c 0 if command-line contains option @c k.
};

//! type of the list of elements on the command line that are not handled by options parsing
using FilesVector = std::vector<std::string>;

//------------------------------------------------------------------------
/** The description of one single command-line option.
Normally you rarely use a Description directly.\n
In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions.
*/
//------------------------------------------------------------------------
class Description : public std::string
{
public:
Description (const std::string& name, const std::string& help, const std::string& valueType ); ///< Construct a Description
std::string mHelp; ///< The help string for this option.
std::string mType; ///< The type of this option (kBool, kString).
static const std::string kBool;
static const std::string kString;
};
//------------------------------------------------------------------------
/** List of command-line option descriptions.
Use addOptions(const std::string&) to add Descriptions.
*/
//------------------------------------------------------------------------
class Descriptions
{
using DescriptionsList = std::deque<Description>;
DescriptionsList mDescriptions;
std::string mCaption;
public:
/** Sets the command-line tool caption and starts adding Descriptions. */
Descriptions& addOptions (const std::string& caption = "",
std::initializer_list<Description>&& options = {});
/** Parse the command-line. */
bool parse (int ac, char* av[], VariablesMap& result, FilesVector* files = nullptr) const;
/** Print a brief description for the command-line tool into the stream @c os. */
void print (std::ostream& os) const;
/** Add a new switch. Only */
Descriptions& operator() (const std::string& name, const std::string& help);
/** Add a new option of type @c inType. Currently only std::string is supported. */
template <typename Type>
Descriptions& operator () (const std::string& name, const Type& inType, std::string help);
};
//------------------------------------------------------------------------
// If you need the declarations in more than one cpp file you have to define
// SMTG_NO_IMPLEMENTATION in all but one file.
//------------------------------------------------------------------------
#ifndef SMTG_NO_IMPLEMENTATION

//------------------------------------------------------------------------
/*! If command-line contains option @c k more than once, only the last value will survive. */
//------------------------------------------------------------------------
std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k)
{
return mVariablesMapContainer[k];
}

//------------------------------------------------------------------------
/*! If command-line contains option @c k more than once, only the last value will survive. */
//------------------------------------------------------------------------
const std::string& VariablesMap::operator [](const VariablesMapContainer::key_type k) const
{
return (*const_cast<VariablesMap*>(this))[k];
}
//------------------------------------------------------------------------
VariablesMap::VariablesMapContainer::size_type VariablesMap::count (const VariablesMapContainer::key_type k) const
{
return mVariablesMapContainer.count (k);
}

//------------------------------------------------------------------------
/** Add a new option with a string as parameter. */
//------------------------------------------------------------------------
template <> Descriptions& Descriptions::operator() (const std::string& name, const std::string& inType, std::string help)
{
mDescriptions.emplace_back (name, help, inType);
return *this;
}
bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files = nullptr); ///< Parse the command-line.
std::ostream& operator<< (std::ostream& os, const Descriptions& desc); ///< Make Descriptions stream able.

const std::string Description::kBool = "bool";
const std::string Description::kString = "string";

//------------------------------------------------------------------------
/*! In most cases you will use the Descriptions::addOptions (const std::string&) method to create and add descriptions.
@param[in] name of the option.
@param[in] help a help description for this option.
@param[out] valueType Description::kBool or Description::kString.
*/
Description::Description (const std::string& name, const std::string& help, const std::string& valueType)
: std::string (name)
, mHelp (help)
, mType (valueType)
{
}

//------------------------------------------------------------------------
/*! Returning a reference to *this, enables chaining of calls to operator()(const std::string&,
const std::string&).
@param[in] name of the added option.
@param[in] help a help description for this option.
@return a reference to *this.
*/
Descriptions& Descriptions::operator () (const std::string& name, const std::string& help)
{
mDescriptions.emplace_back (name, help, Description::kBool);
return *this;
}

//------------------------------------------------------------------------
/*! <b>Usage example:</b>
@code
CommandLine::Descriptions desc;
desc.addOptions ("myTool") // Set caption to "myTool"
("help", "produce help message") // add switch -help
("opt1", string(), "option 1") // add string option -opt1
("opt2", string(), "option 2") // add string option -opt2
;
@endcode
@note
The operator() is used for every additional option.

Or with initializer list :
@code
CommandLine::Descriptions desc;
desc.addOptions ("myTool", // Set caption to "myTool"
{{"help", "produce help message", Description::kBool}, // add switch -help
{"opt1", "option 1", Description::kString}, // add string option -opt1
{"opt2", "option 2", Description::kString}} // add string option -opt2
);
@endcode
@param[in] caption the caption of the command-line tool.
@param[in] options initializer list with options
@return a reverense to *this.
*/
Descriptions& Descriptions::addOptions (const std::string& caption,
std::initializer_list<Description>&& options)
{
mCaption = caption;
std::move (options.begin (), options.end (), std::back_inserter (mDescriptions));
return *this;
}

//------------------------------------------------------------------------
/*! @param[in] ac count of command-line parameters
@param[in] av command-line as array of strings
@param[out] result the parsing result
@param[out] files optional list of elements on the command line that are not handled by options parsing
*/
bool Descriptions::parse (int ac, char* av[], VariablesMap& result, FilesVector* files) const
{
using namespace std;

for (int i = 1; i < ac; i++)
{
string current = av[i];
if (current[0] == '-')
{
int pos = current[1] == '-' ? 2 : 1;
current = current.substr (pos, string::npos);

DescriptionsList::const_iterator found =
find (mDescriptions.begin (), mDescriptions.end (), current);
if (found != mDescriptions.end ())
{
result[*found] = "true";
if (found->mType != Description::kBool)
{
if (((i + 1) < ac) && *av[i + 1] != '-')
{
result[*found] = av[++i];
}
else
{
result[*found] = "error!";
result.setError ();
return false;
}
}
}
else
{
result.setError ();
return false;
}
}
else if (files)
files->push_back (av[i]);
}
return true;
}

//------------------------------------------------------------------------
/*! The description includes the help strings for all options. */
//------------------------------------------------------------------------
void Descriptions::print (std::ostream& os) const
{
if (!mCaption.empty ())
os << mCaption << ":\n";

size_t maxLength = 0u;
std::for_each (mDescriptions.begin (), mDescriptions.end (),
[&] (const Description& d) { maxLength = std::max (maxLength, d.size ()); });

for (const Description& opt : mDescriptions)
{
os << "-" << opt;
for (auto s = opt.size (); s < maxLength; ++s)
os << " ";
os << " | " << opt.mHelp << "\n";
}
}

//------------------------------------------------------------------------
std::ostream& operator<< (std::ostream& os, const Descriptions& desc)
{
desc.print (os);
return os;
}

//------------------------------------------------------------------------
/*! @param[in] ac count of command-line parameters
@param[in] av command-line as array of strings
@param[in] desc Descriptions including all allowed options
@param[out] result the parsing result
@param[out] files optional list of elements on the command line that are not handled by options parsing
*/
bool parse (int ac, char* av[], const Descriptions& desc, VariablesMap& result, FilesVector* files)
{
return desc.parse (ac, av, result, files);
}
#endif

} //namespace CommandLine
} //namespace Steinberg

+ 366
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/samples/vst-utilities/moduleinfotool/source/main.cpp View File

@@ -0,0 +1,366 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfotool
// Filename : public.sdk/samples/vst-utilities/moduleinfotool/main.cpp
// Created by : Steinberg, 12/2021
// Description : main entry point
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfocreator.h"
#include "public.sdk/source/vst/moduleinfo/moduleinfoparser.h"
#include "base/source/fcommandline.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <cstdio>
#include <fstream>
#include <iostream>

//------------------------------------------------------------------------
namespace Steinberg {
namespace ModuleInfoTool {
namespace {

//------------------------------------------------------------------------
constexpr auto BUILD_INFO = "moduleinfotool 1.0.0 [Built " __DATE__ "]";

//------------------------------------------------------------------------
//-- Options
constexpr auto optHelp = "help";
constexpr auto optCreate = "create";
constexpr auto optValidate = "validate";
constexpr auto optModuleVersion = "version";
constexpr auto optModulePath = "path";
constexpr auto optInfoPath = "infopath";
constexpr auto optModuleCompatPath = "compat";
constexpr auto optOutputPath = "output";

//------------------------------------------------------------------------
void printUsage (std::ostream& s)
{
s << "Usage:\n";
s << " moduleinfotool -create -version VERSION -path MODULE_PATH [-compat PATH -output PATH]\n";
s << " moduleinfotool -validate -path MODULE_PATH [-infopath PATH]\n";
}

//------------------------------------------------------------------------
std::string loadFile (const std::string& path)
{
std::ifstream file (path, std::ios_base::in | std::ios_base::binary);
if (!file.is_open ())
return {};
auto size = file.seekg (0, std::ios_base::end).tellg ();
file.seekg (0, std::ios_base::beg);
std::string data;
data.resize (size);
file.read (data.data (), data.size ());
if (file.bad ())
return {};
return data;
}

//------------------------------------------------------------------------
std::optional<ModuleInfo::CompatibilityList> openAndParseCompatJSON (const std::string& path)
{
auto data = loadFile (path);
if (data.empty ())
{
std::cout << "Can not read '" << path << "'\n";
printUsage (std::cout);
return {};
}
std::stringstream error;
auto result = ModuleInfoLib::parseCompatibilityJson (data, &error);
if (!result)
{
std::cout << "Can not parse '" << path << "'\n";
std::cout << error.str ();
printUsage (std::cout);
return {};
}
return result;
}

//------------------------------------------------------------------------
int createJSON (const std::optional<ModuleInfo::CompatibilityList>& compat,
const std::string& modulePath, const std::string& moduleVersion,
std::ostream& outStream)
{
std::string errorStr;
auto module = VST3::Hosting::Module::create (modulePath, errorStr);
if (!module)
{
std::cout << errorStr;
return 1;
}
auto moduleInfo = ModuleInfoLib::createModuleInfo (*module, false);
if (compat)
moduleInfo.compatibility = *compat;
moduleInfo.version = moduleVersion;

std::stringstream output;
ModuleInfoLib::outputJson (moduleInfo, output);
auto str = output.str ();
outStream << str;
return 0;
}

//------------------------------------------------------------------------
struct validate_error : std::exception
{
validate_error (const std::string& str) : str (str) {}
const char* what () const noexcept override { return str.data (); }

private:
std::string str;
};

//------------------------------------------------------------------------
void validate (const ModuleInfo& moduleInfo, const VST3::Hosting::Module& module)
{
auto factory = module.getFactory ();
auto factoryInfo = factory.info ();
auto classInfoList = factory.classInfos ();
auto snapshotList = module.getSnapshots (module.getPath ());

if (factoryInfo.vendor () != moduleInfo.factoryInfo.vendor)
throw validate_error ("factoryInfo.vendor different: " + moduleInfo.factoryInfo.vendor);
if (factoryInfo.url () != moduleInfo.factoryInfo.url)
throw validate_error ("factoryInfo.url different: " + moduleInfo.factoryInfo.url);
if (factoryInfo.email () != moduleInfo.factoryInfo.email)
throw validate_error ("factoryInfo.email different: " + moduleInfo.factoryInfo.email);
if (factoryInfo.flags () != moduleInfo.factoryInfo.flags)
throw validate_error ("factoryInfo.flags different: " +
std::to_string (moduleInfo.factoryInfo.flags));

for (const auto& ci : moduleInfo.classes)
{
auto cid = VST3::UID::fromString (ci.cid);
if (!cid)
throw validate_error ("could not parse class UID: " + ci.cid);
auto it = std::find_if (classInfoList.begin (), classInfoList.end (),
[&] (const auto& el) { return el.ID () == *cid; });
if (it == classInfoList.end ())
throw validate_error ("cannot find CID in class list: " + ci.cid);
if (it->name () != ci.name)
throw validate_error ("class name different: " + ci.name);
if (it->category () != ci.category)
throw validate_error ("class category different: " + ci.category);
if (it->vendor () != ci.vendor)
throw validate_error ("class vendor different: " + ci.vendor);
if (it->version () != ci.version)
throw validate_error ("class version different: " + ci.version);
if (it->sdkVersion () != ci.sdkVersion)
throw validate_error ("class sdkVersion different: " + ci.sdkVersion);
if (it->subCategories () != ci.subCategories)
throw validate_error ("class subCategories different: " /* + ci.subCategories*/);
if (it->cardinality () != ci.cardinality)
throw validate_error ("class cardinality different: " +
std::to_string (ci.cardinality));
if (it->classFlags () != ci.flags)
throw validate_error ("class flags different: " + std::to_string (ci.flags));
classInfoList.erase (it);

auto snapshotListIt = std::find_if (snapshotList.begin (), snapshotList.end (),
[&] (const auto& el) { return el.uid == *cid; });
if (snapshotListIt == snapshotList.end () && !ci.snapshots.empty ())
throw validate_error ("cannot find snapshots for: " + ci.cid);
for (const auto& snapshot : ci.snapshots)
{
auto snapshotIt = std::find_if (
snapshotListIt->images.begin (), snapshotListIt->images.end (),
[&] (const auto& el) { return el.scaleFactor == snapshot.scaleFactor; });
if (snapshotIt == snapshotListIt->images.end ())
throw validate_error ("cannot find snapshots for scale factor: " +
std::to_string (snapshot.scaleFactor));
std::string_view path (snapshotIt->path);
if (path.find (module.getPath ()) == 0)
path.remove_prefix (module.getPath ().size () + 1);
if (path != snapshot.path)
throw validate_error ("cannot find snapshots with path: " + snapshot.path);
snapshotListIt->images.erase (snapshotIt);
}
if (snapshotListIt != snapshotList.end () && !snapshotListIt->images.empty ())
{
std::string errorStr = "Missing Snapshots in moduleinfo:\n";
for (const auto& s : snapshotListIt->images)
{
errorStr += s.path + '\n';
}
throw validate_error (errorStr);
}
if (snapshotListIt != snapshotList.end ())
snapshotList.erase (snapshotListIt);
}
if (!classInfoList.empty ())
throw validate_error ("Missing classes in moduleinfo");
if (!snapshotList.empty ())
throw validate_error ("Missing snapshots in moduleinfo");
}

//------------------------------------------------------------------------
int validate (const std::string& modulePath, std::string infoJsonPath)
{
if (infoJsonPath.empty ())
{
auto path = VST3::Hosting::Module::getModuleInfoPath (modulePath);
if (!path)
{
std::cerr << "Module does not contain a moduleinfo.json: '" << modulePath << "'"
<< '\n';
return 1;
}
infoJsonPath = *path;
}

auto data = loadFile (infoJsonPath);
if (data.empty ())
{
std::cerr << "Empty or non existing file: '" << infoJsonPath << "'" << '\n';
printUsage (std::cout);
return 1;
}
auto moduleInfo = ModuleInfoLib::parseJson (data, &std::cerr);
if (moduleInfo)
{
std::string errorStr;
auto module = VST3::Hosting::Module::create (modulePath, errorStr);
if (!module)
{
std::cerr << errorStr;
printUsage (std::cout);
return 1;
}
try
{
validate (*moduleInfo, *module);
}
catch (const std::exception& exc)
{
std::cerr << "Error:\n" << exc.what () << '\n';
printUsage (std::cout);
return 1;
}
return 0;
}
printUsage (std::cout);
return 1;
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
int run (int argc, char* argv[])
{
// parse command line
CommandLine::Descriptions desc;
CommandLine::VariablesMap valueMap;
CommandLine::FilesVector files;

using Description = CommandLine::Description;
desc.addOptions (
BUILD_INFO,
{
{optCreate, "Create moduleinfo", Description::kBool},
{optValidate, "Validate moduleinfo", Description::kBool},
{optModuleVersion, "Module version", Description::kString},
{optModulePath, "Path to module", Description::kString},
{optInfoPath, "Path to moduleinfo.json", Description::kString},
{optModuleCompatPath, "Path to compatibility.json", Description::kString},
{optOutputPath, "Write json to file instead of stdout", Description::kString},
{optHelp, "Print help", Description::kBool},
});
CommandLine::parse (argc, argv, desc, valueMap, &files);

bool isCreate = valueMap.count (optCreate) != 0 && valueMap.count (optModuleVersion) != 0 &&
valueMap.count (optModulePath) != 0;
bool isValidate = valueMap.count (optValidate) && valueMap.count (optModulePath) != 0;

if (valueMap.hasError () || valueMap.count (optHelp) || !(isCreate || isValidate))
{
std::cout << '\n' << desc << '\n';
printUsage (std::cout);
return 1;
}

int result = 1;

const auto& modulePath = valueMap[optModulePath];
if (isCreate)
{
auto* outputStream = &std::cout;
std::optional<ModuleInfo::CompatibilityList> compat;
if (valueMap.count (optModuleCompatPath) != 0)
{
const auto& compatPath = valueMap[optModuleCompatPath];
compat = openAndParseCompatJSON (compatPath);
if (!compat)
return 1;
}
bool writeToFile = false;
if (valueMap.count (optOutputPath) != 0)
{
writeToFile = true;
auto outputFile = valueMap[optOutputPath];
auto ostream = new std::ofstream (outputFile);
if (ostream->is_open ())
outputStream = ostream;
else
{
std::cout << "Cannot create output file: " << outputFile << '\n';
return result;
}
}
const auto& moduleVersion = valueMap[optModuleVersion];
result = createJSON (compat, modulePath, moduleVersion, *outputStream);
if (writeToFile)
delete outputStream;
}
else if (isValidate)
{
std::string moduleInfoJsonPath;
if (valueMap.count (optInfoPath) != 0)
moduleInfoJsonPath = valueMap[optInfoPath];
result = validate (modulePath, moduleInfoJsonPath);
}
return result;
}

//------------------------------------------------------------------------
} // ModuleInfoTool
} // Steinberg

//------------------------------------------------------------------------
int main (int argc, char* argv[])
{
return Steinberg::ModuleInfoTool::run (argc, argv);
}

+ 340
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.cpp View File

@@ -0,0 +1,340 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "module.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/utility/optional.h"
#include <sstream>
#include <utility>

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept
{
*this = std::move (other);
}

//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept
{
info = std::move (other.info);
other.info = {};
return *this;
}

//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept
{
info = std::move (other);
other = {};
return *this;
}

//------------------------------------------------------------------------
std::string FactoryInfo::vendor () const noexcept
{
return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize);
}

//------------------------------------------------------------------------
std::string FactoryInfo::url () const noexcept
{
return StringConvert::convert (info.url, PFactoryInfo::kURLSize);
}

//------------------------------------------------------------------------
std::string FactoryInfo::email () const noexcept
{
return StringConvert::convert (info.email, PFactoryInfo::kEmailSize);
}

//------------------------------------------------------------------------
Steinberg::int32 FactoryInfo::flags () const noexcept
{
return info.flags;
}

//------------------------------------------------------------------------
bool FactoryInfo::classesDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kClassesDiscardable) != 0;
}

//------------------------------------------------------------------------
bool FactoryInfo::licenseCheck () const noexcept
{
return (info.flags & PFactoryInfo::kLicenseCheck) != 0;
}

//------------------------------------------------------------------------
bool FactoryInfo::componentNonDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory)
{
}

//------------------------------------------------------------------------
void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept
{
if (auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory))
f->setHostContext (context);
}

//------------------------------------------------------------------------
FactoryInfo PluginFactory::info () const noexcept
{
Steinberg::PFactoryInfo i;
factory->getFactoryInfo (&i);
return FactoryInfo (std::move (i));
}

//------------------------------------------------------------------------
uint32_t PluginFactory::classCount () const noexcept
{
auto count = factory->countClasses ();
assert (count >= 0);
return static_cast<uint32_t> (count);
}

//------------------------------------------------------------------------
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
auto count = classCount ();
Optional<FactoryInfo> factoryInfo;
ClassInfos classes;
classes.reserve (count);
auto f3 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory);
auto f2 = Steinberg::FUnknownPtr<Steinberg::IPluginFactory2> (factory);
Steinberg::PClassInfo ci;
Steinberg::PClassInfo2 ci2;
Steinberg::PClassInfoW ci3;
for (uint32_t i = 0; i < count; ++i)
{
if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
classes.emplace_back (ci3);
else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
classes.emplace_back (ci2);
else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
classes.emplace_back (ci);
auto& classInfo = classes.back ();
if (classInfo.vendor ().empty ())
{
if (!factoryInfo)
factoryInfo = Optional<FactoryInfo> (info ());
classInfo.get ().vendor = factoryInfo->vendor ();
}
}
return classes;
}

//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
const UID& ClassInfo::ID () const noexcept
{
return data.classID;
}

//------------------------------------------------------------------------
int32_t ClassInfo::cardinality () const noexcept
{
return data.cardinality;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::category () const noexcept
{
return data.category;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::name () const noexcept
{
return data.name;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::vendor () const noexcept
{
return data.vendor;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::version () const noexcept
{
return data.version;
}

//------------------------------------------------------------------------
const std::string& ClassInfo::sdkVersion () const noexcept
{
return data.sdkVersion;
}

//------------------------------------------------------------------------
const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept
{
return data.subCategories;
}

//------------------------------------------------------------------------
Steinberg::uint32 ClassInfo::classFlags () const noexcept
{
return data.classFlags;
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo& info) noexcept
{
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo2& info) noexcept
{
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}

//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfoW& info) noexcept
{
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}

//------------------------------------------------------------------------
void ClassInfo::parseSubCategories (const std::string& str) noexcept
{
std::stringstream stream (str);
std::string item;
while (std::getline (stream, item, '|'))
data.subCategories.emplace_back (move (item));
}

//------------------------------------------------------------------------
std::string ClassInfo::subCategoriesString () const noexcept
{
std::string result;
if (data.subCategories.empty ())
return result;
result = data.subCategories[0];
for (auto index = 1u; index < data.subCategories.size (); ++index)
result += "|" + data.subCategories[index];
return result;
}

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
std::pair<size_t, size_t> rangeOfScaleFactor (const std::string& name)
{
auto result = std::make_pair (std::string::npos, std::string::npos);
size_t xIndex = name.find_last_of ('x');
if (xIndex == std::string::npos)
return result;

size_t indicatorIndex = name.find_last_of ('_');
if (indicatorIndex == std::string::npos)
return result;
if (xIndex < indicatorIndex)
return result;
result.first = indicatorIndex + 1;
result.second = xIndex;
return result;
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Optional<double> Module::Snapshot::decodeScaleFactor (const std::string& name)
{
auto range = rangeOfScaleFactor (name);
if (range.first == std::string::npos || range.second == std::string::npos)
return {};
std::string tmp (name.data () + range.first, range.second - range.first);
std::istringstream sstream (tmp);
sstream.imbue (std::locale::classic ());
sstream.precision (static_cast<std::streamsize> (3));
double result;
sstream >> result;
return Optional<double> (result);
}

//------------------------------------------------------------------------
Optional<UID> Module::Snapshot::decodeUID (const std::string& filename)
{
if (filename.size () < 45)
return {};
if (filename.find ("_snapshot") != 32)
return {};
auto uidStr = filename.substr (0, 32);
return UID::fromString (uidStr);
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

+ 212
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module.h View File

@@ -0,0 +1,212 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.h
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "../utility/uid.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <utility>
#include <vector>

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
class FactoryInfo
{
public:
//------------------------------------------------------------------------
using PFactoryInfo = Steinberg::PFactoryInfo;

FactoryInfo () noexcept {}
~FactoryInfo () noexcept {}
FactoryInfo (const FactoryInfo&) noexcept = default;
FactoryInfo (PFactoryInfo&&) noexcept;
FactoryInfo (FactoryInfo&&) noexcept = default;
FactoryInfo& operator= (const FactoryInfo&) noexcept = default;
FactoryInfo& operator= (FactoryInfo&&) noexcept;
FactoryInfo& operator= (PFactoryInfo&&) noexcept;

std::string vendor () const noexcept;
std::string url () const noexcept;
std::string email () const noexcept;
Steinberg::int32 flags () const noexcept;
bool classesDiscardable () const noexcept;
bool licenseCheck () const noexcept;
bool componentNonDiscardable () const noexcept;

PFactoryInfo& get () noexcept { return info; }
//------------------------------------------------------------------------
private:
PFactoryInfo info {};
};

//------------------------------------------------------------------------
class ClassInfo
{
public:
//------------------------------------------------------------------------
using SubCategories = std::vector<std::string>;
using PClassInfo = Steinberg::PClassInfo;
using PClassInfo2 = Steinberg::PClassInfo2;
using PClassInfoW = Steinberg::PClassInfoW;

//------------------------------------------------------------------------
ClassInfo () noexcept {}
explicit ClassInfo (const PClassInfo& info) noexcept;
explicit ClassInfo (const PClassInfo2& info) noexcept;
explicit ClassInfo (const PClassInfoW& info) noexcept;
ClassInfo (const ClassInfo&) = default;
ClassInfo& operator= (const ClassInfo&) = default;
ClassInfo (ClassInfo&&) = default;
ClassInfo& operator= (ClassInfo&&) = default;

const UID& ID () const noexcept;
int32_t cardinality () const noexcept;
const std::string& category () const noexcept;
const std::string& name () const noexcept;
const std::string& vendor () const noexcept;
const std::string& version () const noexcept;
const std::string& sdkVersion () const noexcept;
const SubCategories& subCategories () const noexcept;
std::string subCategoriesString () const noexcept;
Steinberg::uint32 classFlags () const noexcept;

struct Data
{
UID classID;
int32_t cardinality;
std::string category;
std::string name;
std::string vendor;
std::string version;
std::string sdkVersion;
SubCategories subCategories;
Steinberg::uint32 classFlags = 0;
};

Data& get () noexcept { return data; }
//------------------------------------------------------------------------
private:
void parseSubCategories (const std::string& str) noexcept;
Data data {};
};

//------------------------------------------------------------------------
class PluginFactory
{
public:
//------------------------------------------------------------------------
using ClassInfos = std::vector<ClassInfo>;
using PluginFactoryPtr = Steinberg::IPtr<Steinberg::IPluginFactory>;

//------------------------------------------------------------------------
explicit PluginFactory (const PluginFactoryPtr& factory) noexcept;

void setHostContext (Steinberg::FUnknown* context) const noexcept;

FactoryInfo info () const noexcept;
uint32_t classCount () const noexcept;
ClassInfos classInfos () const noexcept;

template <typename T>
Steinberg::IPtr<T> createInstance (const UID& classID) const noexcept;

const PluginFactoryPtr& get () const noexcept { return factory; }
//------------------------------------------------------------------------
private:
PluginFactoryPtr factory;
};

//------------------------------------------------------------------------
//------------------------------------------------------------------------
class Module
{
public:
//------------------------------------------------------------------------
struct Snapshot
{
struct ImageDesc
{
double scaleFactor {1.};
std::string path;
};
UID uid;
std::vector<ImageDesc> images;

static Optional<double> decodeScaleFactor (const std::string& path);
static Optional<UID> decodeUID (const std::string& filename);
};

using Ptr = std::shared_ptr<Module>;
using PathList = std::vector<std::string>;
using SnapshotList = std::vector<Snapshot>;

//------------------------------------------------------------------------
static Ptr create (const std::string& path, std::string& errorDescription);
static PathList getModulePaths ();
static SnapshotList getSnapshots (const std::string& modulePath);
/** get the path to the module info json file if it exists */
static Optional<std::string> getModuleInfoPath (const std::string& modulePath);

const std::string& getName () const noexcept { return name; }
const std::string& getPath () const noexcept { return path; }
const PluginFactory& getFactory () const noexcept { return factory; }
//------------------------------------------------------------------------
protected:
virtual ~Module () noexcept = default;
virtual bool load (const std::string& path, std::string& errorDescription) = 0;

PluginFactory factory {nullptr};
std::string name;
std::string path;
};

//------------------------------------------------------------------------
template <typename T>
inline Steinberg::IPtr<T> PluginFactory::createInstance (const UID& classID) const noexcept
{
T* obj = nullptr;
if (factory->createInstance (classID.data (), T::iid, reinterpret_cast<void**> (&obj)) ==
Steinberg::kResultTrue)
return Steinberg::owned (obj);
return nullptr;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

+ 361
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_linux.cpp View File

@@ -0,0 +1,361 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_linux.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (linux implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "module.h"
#include "../utility/optional.h"
#include "../utility/stringconvert.h"
#include <algorithm>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#if (__cplusplus >= 201707L)
#if __has_include(<filesystem>)
#define USE_EXPERIMENTAL_FS 0
#elif __has_include(<experimental/filesystem>)
#define USE_EXPERIMENTAL_FS 1
#endif
#else
#define USE_EXPERIMENTAL_FS 1
#endif

#if USE_EXPERIMENTAL_FS == 1
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#else
#include <filesystem>
namespace filesystem = std::filesystem;
#endif

//------------------------------------------------------------------------
extern "C" {
using ModuleEntryFunc = bool (PLUGIN_API*) (void*);
using ModuleExitFunc = bool (PLUGIN_API*) ();
}

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

using Path = filesystem::path;

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
Optional<std::string> getCurrentMachineName ()
{
struct utsname unameData;

int res = uname (&unameData);
if (res != 0)
return {};

return {unameData.machine};
}

//------------------------------------------------------------------------
Optional<Path> getApplicationPath ()
{
std::string appPath = "";

pid_t pid = getpid ();
char buf[10];
sprintf (buf, "%d", pid);
std::string _link = "/proc/";
_link.append (buf);
_link.append ("/exe");
char proc[1024];
int ch = readlink (_link.c_str (), proc, 1024);
if (ch == -1)
return {};

proc[ch] = 0;
appPath = proc;
std::string::size_type t = appPath.find_last_of ("/");
appPath = appPath.substr (0, t);

return Path {appPath};
}

//------------------------------------------------------------------------
class LinuxModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (dlsym (mModule, name));
}

~LinuxModule () override
{
factory = PluginFactory (nullptr);

if (mModule)
{
if (auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit"))
moduleExit ();

dlclose (mModule);
}
}

static Optional<Path> getSOPath (const std::string& inPath)
{
Path modulePath {inPath};
if (!filesystem::is_directory (modulePath))
return {};

auto stem = modulePath.stem ();

modulePath /= "Contents";
if (!filesystem::is_directory (modulePath))
return {};

// use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux"
auto machine = getCurrentMachineName ();
if (!machine)
return {};

modulePath /= *machine + "-linux";
if (!filesystem::is_directory (modulePath))
return {};

stem.replace_extension (".so");
modulePath /= stem;
return Optional<Path> (std::move (modulePath));
}

bool load (const std::string& inPath, std::string& errorDescription) override
{
auto modulePath = getSOPath (inPath);
if (!modulePath)
{
errorDescription = inPath + " is not a module directory.";
return false;
}

mModule = dlopen (reinterpret_cast<const char*> (modulePath->generic_string ().data ()),
RTLD_LAZY);
if (!mModule)
{
errorDescription = "dlopen failed.\n";
errorDescription += dlerror ();
return false;
}
// ModuleEntry is mandatory
auto moduleEntry = getFunctionPointer<ModuleEntryFunc> ("ModuleEntry");
if (!moduleEntry)
{
errorDescription =
"The shared library does not export the required 'ModuleEntry' function";
return false;
}
// ModuleExit is mandatory
auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit");
if (!moduleExit)
{
errorDescription =
"The shared library does not export the required 'ModuleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription =
"The shared library does not export the required 'GetPluginFactory' function";
return false;
}

if (!moduleEntry (mModule))
{
errorDescription = "Calling 'ModuleEntry' failed";
return false;
}
auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}

void* mModule {nullptr};
};

//------------------------------------------------------------------------
void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList,
bool recursive = true)
{
try
{
for (auto& p : filesystem::directory_iterator (path))
{
if (p.path ().extension () == ext)
{
pathList.push_back (p.path ().generic_string ());
}
else if (recursive && p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (p.path (), ext, pathList);
}
}
}
catch (...)
{
}
}

//------------------------------------------------------------------------
void findModules (const std::string& path, Module::PathList& pathList)
{
findFilesWithExt (path, ".vst3", pathList);
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<LinuxModule> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
/* VST3 component locations on linux :
* User privately installed : $HOME/.vst3/
* Distribution installed : /usr/lib/vst3/
* Locally installed : /usr/local/lib/vst3/
* Application : /$APPFOLDER/vst3/
*/

const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"};

PathList list;
if (auto homeDir = getenv ("HOME"))
{
filesystem::path homePath (homeDir);
homePath /= ".vst3";
findModules (homePath.generic_string (), list);
}
for (auto path : systemPaths)
findModules (path, list);

// application level
auto appPath = getApplicationPath ();
if (appPath)
{
*appPath /= "vst3";
findModules (appPath->generic_string (), list);
}

return list;
}

//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
filesystem::path path (modulePath);
path /= "Contents";
path /= "Resources";
path /= "Snapshots";
PathList pngList;
findFilesWithExt (path, ".png", pngList, false);
for (auto& png : pngList)
{
filesystem::path p (png);
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;

Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}

//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
SnapshotList result;
filesystem::path path (modulePath);
path /= "Contents";
path /= "moduleinfo.json";
if (filesystem::exists (path))
return {path.generic_string ()};
return {};
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

+ 390
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_mac.mm View File

@@ -0,0 +1,390 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_mac.mm
// Created by : Steinberg, 08/2016
// Description : hosting module classes (macOS implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#import "module.h"

#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>

#if !__has_feature(objc_arc)
#error this file needs to be compiled with automatic reference counting enabled
#endif

//------------------------------------------------------------------------
extern "C" {
typedef bool (*BundleEntryFunc) (CFBundleRef);
typedef bool (*BundleExitFunc) ();
}

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

//------------------------------------------------------------------------
namespace {

//------------------------------------------------------------------------
template <typename T>
class CFPtr
{
public:
inline CFPtr (const T& obj = nullptr) : obj (obj) {}
inline CFPtr (CFPtr&& other) { *this = other; }
inline ~CFPtr ()
{
if (obj)
CFRelease (obj);
}

inline CFPtr& operator= (CFPtr&& other)
{
obj = other.obj;
other.obj = nullptr;
return *this;
}
inline CFPtr& operator= (const T& o)
{
if (obj)
CFRelease (obj);
obj = o;
return *this;
}
inline operator T () const { return obj; } // act as T
private:
CFPtr (const CFPtr& other) = delete;
CFPtr& operator= (const CFPtr& other) = delete;

T obj = nullptr;
};

//------------------------------------------------------------------------
class MacModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
assert (bundle);
CFPtr<CFStringRef> functionName (
CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII));
return reinterpret_cast<T> (CFBundleGetFunctionPointerForName (bundle, functionName));
}

bool loadInternal (const std::string& path, std::string& errorDescription)
{
CFPtr<CFURLRef> url (CFURLCreateFromFileSystemRepresentation (
kCFAllocatorDefault, reinterpret_cast<const UInt8*> (path.data ()), path.length (),
true));
if (!url)
return false;
bundle = CFBundleCreate (kCFAllocatorDefault, url);
CFErrorRef error = nullptr;
if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error))
{
if (error)
{
CFPtr<CFStringRef> errorString (CFErrorCopyDescription (error));
if (errorString)
{
auto stringLength = CFStringGetLength (errorString);
auto maxSize =
CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8);
auto buffer = std::make_unique<char[]> (maxSize);
if (CFStringGetCString (errorString, buffer.get (), maxSize,
kCFStringEncodingUTF8))
errorDescription = buffer.get ();
CFRelease (error);
}
}
else
{
errorDescription = "Could not create Bundle for path: " + path;
}
return false;
}
// bundleEntry is mandatory
auto bundleEntry = getFunctionPointer<BundleEntryFunc> ("bundleEntry");
if (!bundleEntry)
{
errorDescription = "Bundle does not export the required 'bundleEntry' function";
return false;
}
// bundleExit is mandatory
auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit");
if (!bundleExit)
{
errorDescription = "Bundle does not export the required 'bundleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "Bundle does not export the required 'GetPluginFactory' function";
return false;
}
if (!bundleEntry (bundle))
{
errorDescription = "Calling 'bundleEntry' failed";
return false;
}
auto f = owned (factoryProc ());
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}

bool load (const std::string& path, std::string& errorDescription) override
{
if (!path.empty () && path[0] != '/')
{
auto buffer = std::make_unique<char[]> (PATH_MAX);
auto workDir = getcwd (buffer.get (), PATH_MAX);
if (workDir)
{
std::string wd (workDir);
wd += "/";
return loadInternal (wd + path, errorDescription);
}
}
return loadInternal (path, errorDescription);
}

~MacModule () override
{
factory = PluginFactory (nullptr);

if (bundle)
{
if (auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit"))
bundleExit ();
}
}

CFPtr<CFBundleRef> bundle;
};

//------------------------------------------------------------------------
void findModulesInDirectory (NSURL* dirUrl, Module::PathList& result)
{
dirUrl = [dirUrl URLByResolvingSymlinksInPath];
if (!dirUrl)
return;
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager]
enumeratorAtURL: dirUrl
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsPackageDescendants
errorHandler:nil];
for (NSURL* url in enumerator)
{
if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"])
{
CFPtr<CFArrayRef> archs (
CFBundleCopyExecutableArchitecturesForURL (static_cast<CFURLRef> (url)));
if (archs)
result.emplace_back ([url.path UTF8String]);
}
else
{
id resValue;
if (![url getResourceValue:&resValue forKey:NSURLIsSymbolicLinkKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
auto resolvedUrl = [url URLByResolvingSymlinksInPath];
if (![resolvedUrl getResourceValue:&resValue forKey:NSURLIsDirectoryKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
findModulesInDirectory (resolvedUrl, result);
}
}
}

//------------------------------------------------------------------------
void getModules (NSSearchPathDomainMask domain, Module::PathList& result)
{
NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory
inDomain:domain
appropriateForURL:nil
create:NO
error:nil];
if (libraryUrl == nil)
return;
NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"];
if (audioUrl == nil)
return;
NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"];
if (plugInsUrl == nil)
return;
NSURL* vst3Url =
[[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath];
if (vst3Url == nil)
return;
findModulesInDirectory (vst3Url, result);
}

//------------------------------------------------------------------------
void getApplicationModules (Module::PathList& result)
{
auto bundle = CFBundleGetMainBundle ();
if (!bundle)
return;
auto bundleUrl = static_cast<NSURL*> (CFBridgingRelease (CFBundleCopyBundleURL (bundle)));
if (!bundleUrl)
return;
auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"];
if (!resUrl)
return;
auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"];
if (!vst3Url)
return;
findModulesInDirectory (vst3Url, result);
}

//------------------------------------------------------------------------
void getModuleSnapshots (const std::string& path, Module::SnapshotList& result)
{
auto nsString = [NSString stringWithUTF8String:path.data ()];
if (!nsString)
return;
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return;
auto urls = [NSBundle URLsForResourcesWithExtension:@"png"
subdirectory:@"Snapshots"
inBundleWithURL:bundleUrl];
if (!urls || [urls count] == 0)
return;

for (NSURL* url in urls)
{
std::string fullpath ([[url path] UTF8String]);
std::string filename ([[[url path] lastPathComponent] UTF8String]);
auto uid = Module::Snapshot::decodeUID (filename);
if (!uid)
continue;

auto scaleFactor = 1.;
if (auto decodedScaleFactor = Module::Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;

Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (fullpath);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto module = std::make_shared<MacModule> ();
if (module->load (path, errorDescription))
{
module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
module->name = {it.base (), path.end ()};
return std::move (module);
}
return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
PathList list;
getModules (NSUserDomainMask, list);
getModules (NSLocalDomainMask, list);
// TODO getModules (NSNetworkDomainMask, list);
getApplicationModules (list);
return list;
}

//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList list;
getModuleSnapshots (modulePath, list);
return list;
}

//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto nsString = [NSString stringWithUTF8String:modulePath.data ()];
if (!nsString)
return {};
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return {};
auto contentsUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"];
if (!contentsUrl)
return {};
auto moduleInfoUrl = [contentsUrl URLByAppendingPathComponent:@"moduleinfo.json"];
if (!moduleInfoUrl)
return {};
NSError* error = nil;
if ([moduleInfoUrl checkResourceIsReachableAndReturnError:&error])
return {std::string (moduleInfoUrl.fileSystemRepresentation)};
return {};
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

+ 610
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/hosting/module_win32.cpp View File

@@ -0,0 +1,610 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_win32.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (win32 implementation)
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "../utility/optional.h"
#include "../utility/stringconvert.h"
#include "module.h"

#include <shlobj.h>
#include <windows.h>

#include <algorithm>
#include <iostream>

#if SMTG_CPP17

#if __has_include(<filesystem>)
#define USE_FILESYSTEM 1
#elif __has_include(<experimental/filesystem>)
#define USE_FILESYSTEM 0
#endif

#else // !SMTG_CPP17

#define USE_FILESYSTEM 0

#endif // SMTG_CPP17

#if USE_FILESYSTEM == 1

#include <filesystem>
namespace filesystem = std::filesystem;

#else // USE_FILESYSTEM == 0

// The <experimental/filesystem> header is deprecated. It is superseded by the C++17 <filesystem>
// header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the
// warning, otherwise the build will fail in VS2019 16.3.0
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;

#endif // USE_FILESYSTEM

#pragma comment(lib, "Shell32")

//------------------------------------------------------------------------
extern "C" {
using InitModuleFunc = bool (PLUGIN_API*) ();
using ExitModuleFunc = bool (PLUGIN_API*) ();
}

//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {

constexpr unsigned long kIPPathNameMax = 1024;

//------------------------------------------------------------------------
namespace {

#define USE_OLE !USE_FILESYSTEM

#if SMTG_PLATFORM_64

#if SMTG_OS_WINDOWS_ARM

#if SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64ec-win";
constexpr auto architectureX64String = "x86_64-win";
#else // !SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64-win";
#endif // SMTG_CPU_ARM_64EC

constexpr auto architectureArm64XString = "arm64x-win";

#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86_64-win";
#endif // SMTG_OS_WINDOWS_ARM

#else // !SMTG_PLATFORM_64

#if SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "arm-win";
#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86-win";
#endif // SMTG_OS_WINDOWS_ARM

#endif // SMTG_PLATFORM_64

#if USE_OLE
//------------------------------------------------------------------------
struct Ole
{
static Ole& instance ()
{
static Ole gInstance;
return gInstance;
}

private:
Ole () { OleInitialize (nullptr); }
~Ole () { OleUninitialize (); }
};
#endif // USE_OLE

//------------------------------------------------------------------------
class Win32Module : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (GetProcAddress (mModule, name));
}

~Win32Module () override
{
factory = PluginFactory (nullptr);

if (mModule)
{
// ExitDll is optional
if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
dllExit ();

FreeLibrary ((HMODULE)mModule);
}
}

//--- -----------------------------------------------------------------------
HINSTANCE loadAsPackage (const std::string& inPath, const char* archString = architectureString)
{
filesystem::path p (inPath);
auto filename = p.filename ();
p /= "Contents";
p /= archString;
p /= filename;
auto wideStr = StringConvert::convert (p.string ());
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
#if SMTG_CPU_ARM_64EC
if (instance == nullptr)
instance = loadAsPackage (inPath, architectureArm64XString);
if (instance == nullptr)
instance = loadAsPackage (inPath, architectureX64String);
#endif
return instance;
}

//--- -----------------------------------------------------------------------
HINSTANCE loadAsDll (const std::string& inPath, std::string& errorDescription)
{
auto wideStr = StringConvert::convert (inPath);
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
if (instance == nullptr)
{
auto lastError = GetLastError ();
LPVOID lpMessageBuffer {nullptr};
if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr, lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMessageBuffer, 0, nullptr) > 0)
{
errorDescription = "LoadLibray failed: " + std::string ((char*)lpMessageBuffer);
LocalFree (lpMessageBuffer);
}
else
{
errorDescription =
"LoadLibrary failed with error number : " + std::to_string (lastError);
}
}
return instance;
}

//--- -----------------------------------------------------------------------
bool load (const std::string& inPath, std::string& errorDescription) override
{
if (filesystem::is_directory (inPath))
{
// try as package (bundle)
mModule = loadAsPackage (inPath);
}
else
{
// try old definition without package
mModule = loadAsDll (inPath, errorDescription);
}
if (mModule == nullptr)
return false;

auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "The dll does not export the required 'GetPluginFactory' function";
return false;
}
// InitDll is optional
auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
if (dllEntry && !dllEntry ())
{
errorDescription = "Calling 'InitDll' failed";
return false;
}
auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}

HINSTANCE mModule {nullptr};
};

//------------------------------------------------------------------------
bool openVST3Package (const filesystem::path& p, const char* archString,
filesystem::path* result = nullptr)
{
auto path = p;
path /= "Contents";
path /= archString;
path /= p.filename ();
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (path.c_str ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle (hFile);
if (result)
*result = path;
return true;
}
return false;
}

//------------------------------------------------------------------------
bool checkVST3Package (const filesystem::path& p, filesystem::path* result = nullptr,
const char* archString = architectureString)
{
if (openVST3Package (p, archString, result))
return true;

#if SMTG_CPU_ARM_64EC
if (openVST3Package (p, architectureArm64XString, result))
return true;
if (openVST3Package (p, architectureX64String, result))
return true;
#endif
return false;
}

//------------------------------------------------------------------------
bool isFolderSymbolicLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
if (/*filesystem::exists (p) &&*/ filesystem::is_symlink (p))
return true;
#else
std::wstring wString = p.generic_wstring ();
auto attrib = GetFileAttributesW (reinterpret_cast<LPCWSTR> (wString.c_str ()));
if (attrib & FILE_ATTRIBUTE_REPARSE_POINT)
{
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.c_str ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return true;
CloseHandle (hFile);
}
#endif
return false;
}

//------------------------------------------------------------------------
Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
{
PWSTR wideStr {};
if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
return {};
return StringConvert::convert (Steinberg::wscast (wideStr));
}

//------------------------------------------------------------------------
VST3::Optional<filesystem::path> resolveShellLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
return {filesystem::read_symlink (p).lexically_normal ()};
#elif USE_OLE
Ole::instance ();

IShellLink* shellLink = nullptr;
if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLink, reinterpret_cast<LPVOID*> (&shellLink))))
return {};

IPersistFile* persistFile = nullptr;
if (!SUCCEEDED (
shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast<void**> (&persistFile))))
return {};

if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ)))
return {};

if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500))))
return {};

WCHAR resolvedPath[kIPPathNameMax];
if (!SUCCEEDED (shellLink->GetPath (resolvedPath, kIPPathNameMax, nullptr, SLGP_SHORTPATH)))
return {};

std::wstring longPath;
longPath.resize (kIPPathNameMax);
auto numChars =
GetLongPathNameW (resolvedPath, const_cast<wchar_t*> (longPath.data ()), kIPPathNameMax);
if (!numChars)
return {};
longPath.resize (numChars);

persistFile->Release ();
shellLink->Release ();

return {filesystem::path (longPath)};
#else
return {};
#endif
}

//------------------------------------------------------------------------
void findFilesWithExt (const filesystem::path& path, const std::string& ext,
Module::PathList& pathList, bool recursive = true)
{
for (auto& p : filesystem::directory_iterator (path))
{
#if USE_FILESYSTEM
filesystem::path finalPath (p);
if (isFolderSymbolicLink (p))
{
if (auto res = resolveShellLink (p))
{
finalPath = *res;
if (!filesystem::exists (finalPath))
continue;
}
else
continue;
}
const auto& cpExt = finalPath.extension ();
if (cpExt == ext)
{
filesystem::path result;
if (checkVST3Package (finalPath, &result))
{
pathList.push_back (result.generic_string ());
continue;
}
}

if (filesystem::is_directory (finalPath))
{
if (recursive)
findFilesWithExt (finalPath, ext, pathList, recursive);
}
else if (cpExt == ext)
pathList.push_back (finalPath.generic_string ());
#else
const auto& cp = p.path ();
const auto& cpExt = cp.extension ();
if (cpExt == ext)
{
if ((p.status ().type () == filesystem::file_type::directory) ||
isFolderSymbolicLink (p))
{
filesystem::path result;
if (checkVST3Package (p, &result))
{
pathList.push_back (result.generic_u8string ());
continue;
}
findFilesWithExt (cp, ext, pathList, recursive);
}
else
pathList.push_back (cp.generic_u8string ());
}
else if (recursive)
{
if (p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (cp, ext, pathList, recursive);
}
else if (cpExt == ".lnk")
{
if (auto resolvedLink = resolveShellLink (cp))
{
if (resolvedLink->extension () == ext)
{
if (filesystem::is_directory (*resolvedLink) ||
isFolderSymbolicLink (*resolvedLink))
{
filesystem::path result;
if (checkVST3Package (*resolvedLink, &result))
{
pathList.push_back (result.generic_u8string ());
continue;
}
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
else
pathList.push_back (resolvedLink->generic_u8string ());
}
else if (filesystem::is_directory (*resolvedLink))
{
const auto& str = resolvedLink->generic_u8string ();
if (cp.generic_u8string ().compare (0, str.size (), str.data (),
str.size ()) != 0)
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
}
}
}
#endif
}
}

//------------------------------------------------------------------------
void findModules (const filesystem::path& path, Module::PathList& pathList)
{
if (filesystem::exists (path))
findFilesWithExt (path, ".vst3", pathList);
}

//------------------------------------------------------------------------
Optional<filesystem::path> getContentsDirectoryFromModuleExecutablePath (
const std::string& modulePath)
{
filesystem::path path (modulePath);

path = path.parent_path ();
if (path.filename () != architectureString)
return {};
path = path.parent_path ();
if (path.filename () != "Contents")
return {};

return Optional<filesystem::path> {std::move (path)};
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<Win32Module> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}

//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
// find plug-ins located in common/VST3
PathList list;
if (auto knownFolder = getKnownFolder (FOLDERID_UserProgramFilesCommon))
{
filesystem::path p (*knownFolder);
p.append ("VST3");
findModules (p, list);
}

if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
{
filesystem::path p (*knownFolder);
p.append ("VST3");
findModules (p, list);
}

// find plug-ins located in VST3 (application folder)
WCHAR modulePath[kIPPathNameMax];
GetModuleFileNameW (nullptr, modulePath, kIPPathNameMax);
auto appPath = StringConvert::convert (Steinberg::wscast (modulePath));
filesystem::path path (appPath);
path = path.parent_path ();
path = path.append ("VST3");
findModules (path, list);

return list;
}

//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return {};
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> {p};
}

*path /= "moduleinfo.json";

if (filesystem::exists (*path))
{
return {path->generic_string ()};
}
return {};
}

//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return result;
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> (p);
}

*path /= "Resources";
*path /= "Snapshots";

if (filesystem::exists (*path) == false)
return result;

PathList pngList;
findFilesWithExt (*path, ".png", pngList, false);
for (auto& png : pngList)
{
filesystem::path p (png);
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;

Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}

//------------------------------------------------------------------------
} // Hosting
} // VST3

+ 43
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/ReadMe.md View File

@@ -0,0 +1,43 @@

# ModuleInfoLib

This is a c++17 library to parse and create the Steinberg moduleinfo.json files.

## Parsing

To parse a moduleinfo.json file you need to include the following files to your project:

* moduleinfoparser.cpp
* moduleinfoparser.h
* moduleinfo.h
* json.h
* jsoncxx.h

And add a header search path to the root folder of the VST SDK.

Now to parse a moduleinfo.json file in code you need to read the moduleinfo.json into a memory buffer and call

``` c++
auto moduleInfo = ModuleInfoLib::parseCompatibilityJson (std::string_view (buffer, bufferSize), &std::cerr);
```

Afterwards if parsing succeeded the moduleInfo optional has a value containing the ModuleInfo.

## Creating

The VST3 SDK contains the moduleinfotool utility that can create moduleinfo.json files from VST3 modules.

To add this capability to your own project you need to link to the sdk_hosting library from the SDK and include the following files to your project:

* moduleinfocreator.cpp
* moduleinfocreator.h
* moduleinfo.h

Additionally you need to add the module platform implementation from the hosting directory (module_win32.cpp, module_mac.mm or module_linux.cpp).

Now you can use the two methods in moduleinfocreator.h to create a moduleinfo.json file:

``` c++
auto moduleInfo = ModuleInfoLib::createModuleInfo (module, false);
ModuleInfoLib::outputJson (moduleInfo, std::cout);
```

+ 3403
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/json.h
File diff suppressed because it is too large
View File


+ 427
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/jsoncxx.h View File

@@ -0,0 +1,427 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category :
// Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h
// Created by : Steinberg, 12/2021
// Description :
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "json.h"
#include <cassert>
#include <cstdlib>
#include <optional>
#include <string>
#include <string_view>
#include <variant>

#if defined(_MSC_VER) || __has_include(<charconv>)
#include <charconv>
#define SMTG_HAS_CHARCONV
#endif

//------------------------------------------------------------------------
namespace JSON {
namespace Detail {

//------------------------------------------------------------------------
template <typename JsonT>
struct Base
{
explicit Base (JsonT* o) : object_ (o) {}
explicit Base (const Base& o) : object_ (o.object_) {}

Base& operator= (const Base& o) = default;

operator JsonT* () const { return object_; }
JsonT* jsonValue () const { return object_; }

protected:
Base () : object_ (nullptr) {}

JsonT* object_;
};

//------------------------------------------------------------------------
template <typename JsonElement>
struct Iterator
{
explicit Iterator (JsonElement el) : el (el) {}

bool operator== (const Iterator& other) const { return other.el == el; }
bool operator!= (const Iterator& other) const { return other.el != el; }

JsonElement operator* () const { return el; }
JsonElement operator-> () const { return el; }

Iterator& operator++ ()
{
if (el)
el = el.next ();
return *this;
}

Iterator operator++ (int)
{
auto it = Iterator (el);
operator++ ();
return it;
}

private:
JsonElement el;
};

//------------------------------------------------------------------------
} // Detail

struct Object;
struct Array;
struct String;
struct Number;
struct Boolean;

//------------------------------------------------------------------------
enum class Type
{
Object,
Array,
String,
Number,
True,
False,
Null,
};

//------------------------------------------------------------------------
struct SourceLocation
{
size_t offset;
size_t line;
size_t row;
};

//------------------------------------------------------------------------
struct Value : Detail::Base<json_value_s>
{
using Detail::Base<json_value_s>::Base;
using VariantT = std::variant<Object, Array, String, Number, Boolean, std::nullptr_t>;

std::optional<Object> asObject () const;
std::optional<Array> asArray () const;
std::optional<String> asString () const;
std::optional<Number> asNumber () const;
std::optional<Boolean> asBoolean () const;
std::optional<std::nullptr_t> asNull () const;

VariantT asVariant () const;
Type type () const;

SourceLocation getSourceLocation () const;
};

//------------------------------------------------------------------------
struct Boolean
{
Boolean (size_t type) : value (type == json_type_true) {}

operator bool () const { return value; }

private:
bool value;
};

//------------------------------------------------------------------------
struct String : Detail::Base<json_string_s>
{
using Detail::Base<json_string_s>::Base;

std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; }

SourceLocation getSourceLocation () const;
};

//------------------------------------------------------------------------
struct Number : Detail::Base<json_number_s>
{
using Detail::Base<json_number_s>::Base;

std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; }

std::optional<int64_t> getInteger () const;
std::optional<double> getDouble () const;
};

//------------------------------------------------------------------------
struct ObjectElement : Detail::Base<json_object_element_s>
{
using Detail::Base<json_object_element_s>::Base;

String name () const { return String (jsonValue ()->name); }
Value value () const { return Value (jsonValue ()->value); }

ObjectElement next () const { return ObjectElement (jsonValue ()->next); }
};

//------------------------------------------------------------------------
struct Object : Detail::Base<json_object_s>
{
using Detail::Base<json_object_s>::Base;
using Iterator = Detail::Iterator<ObjectElement>;

size_t size () const { return jsonValue ()->length; }

Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); }
Iterator end () const { return Iterator (ObjectElement (nullptr)); }
};

//------------------------------------------------------------------------
struct ArrayElement : Detail::Base<json_array_element_s>
{
using Detail::Base<json_array_element_s>::Base;

Value value () const { return Value (jsonValue ()->value); }

ArrayElement next () const { return ArrayElement (jsonValue ()->next); }
};

//------------------------------------------------------------------------
struct Array : Detail::Base<json_array_s>
{
using Detail::Base<json_array_s>::Base;
using Iterator = Detail::Iterator<ArrayElement>;

size_t size () const { return jsonValue ()->length; }

Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); }
Iterator end () const { return Iterator (ArrayElement (nullptr)); }
};

//------------------------------------------------------------------------
struct Document : Value
{
static std::variant<Document, json_parse_result_s> parse (std::string_view data)
{
auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); };
json_parse_result_s parse_result {};
auto value = json_parse_ex (data.data (), data.size (),
json_parse_flags_allow_json5 |
json_parse_flags_allow_location_information,
allocate, nullptr, &parse_result);
if (value)
return Document (value);
return parse_result;
}
~Document () noexcept
{
if (object_)
std::free (object_);
}

Document (Document&& doc) noexcept { *this = std::move (doc); }
Document& operator= (Document&& doc) noexcept
{
std::swap (object_, doc.object_);
return *this;
}

private:
using Value::Value;
};

//------------------------------------------------------------------------
inline std::optional<Object> Value::asObject () const
{
if (type () != Type::Object)
return {};
return Object (json_value_as_object (jsonValue ()));
}

//------------------------------------------------------------------------
inline std::optional<Array> Value::asArray () const
{
if (type () != Type::Array)
return {};
return Array (json_value_as_array (jsonValue ()));
}

//------------------------------------------------------------------------
inline std::optional<String> Value::asString () const
{
if (type () != Type::String)
return {};
return String (json_value_as_string (jsonValue ()));
}

//------------------------------------------------------------------------
inline std::optional<Number> Value::asNumber () const
{
if (type () != Type::Number)
return {};
return Number (json_value_as_number (jsonValue ()));
}

//------------------------------------------------------------------------
inline std::optional<Boolean> Value::asBoolean () const
{
if (type () == Type::True || type () == Type::False)
return Boolean (jsonValue ()->type);
return {};
}

//------------------------------------------------------------------------
inline std::optional<std::nullptr_t> Value::asNull () const
{
if (type () != Type::Null)
return {};
return nullptr;
}

//------------------------------------------------------------------------
inline Type Value::type () const
{
switch (jsonValue ()->type)
{
case json_type_string: return Type::String;
case json_type_number: return Type::Number;
case json_type_object: return Type::Object;
case json_type_array: return Type::Array;
case json_type_true: return Type::True;
case json_type_false: return Type::False;
case json_type_null: return Type::Null;
}
assert (false);
return Type::Null;
}

//------------------------------------------------------------------------
inline Value::VariantT Value::asVariant () const
{
switch (type ())
{
case Type::String: return *asString ();
case Type::Number: return *asNumber ();
case Type::Object: return *asObject ();
case Type::Array: return *asArray ();
case Type::True: return *asBoolean ();
case Type::False: return *asBoolean ();
case Type::Null: return *asNull ();
}
assert (false);
return nullptr;
}

//------------------------------------------------------------------------
inline SourceLocation Value::getSourceLocation () const
{
auto exValue = reinterpret_cast<json_value_ex_s*> (jsonValue ());
return {exValue->offset, exValue->line_no, exValue->row_no};
}

//------------------------------------------------------------------------
inline SourceLocation String::getSourceLocation () const
{
auto exValue = reinterpret_cast<json_string_ex_s*> (jsonValue ());
return {exValue->offset, exValue->line_no, exValue->row_no};
}

//------------------------------------------------------------------------
inline std::optional<int64_t> Number::getInteger () const
{
#if defined(SMTG_HAS_CHARCONV)
int64_t result {0};
auto res = std::from_chars (jsonValue ()->number,
jsonValue ()->number + jsonValue ()->number_size, result);
if (res.ec == std::errc ())
return result;
return {};
#else
int64_t result {0};
std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size);
if (std::sscanf (str.data (), "%lld", &result) != 1)
return {};
return result;
#endif
}

//------------------------------------------------------------------------
inline std::optional<double> Number::getDouble () const
{
#if 1 // clang still has no floting point from_chars version
size_t ctrl {0};
auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl);
if (ctrl > 0)
return result;
#else
double result {0.};
auto res = std::from_chars (jsonValue ()->number,
jsonValue ()->number + jsonValue ()->number_size, result);
if (res.ec == std::errc ())
return result;
#endif
return {};
}

//------------------------------------------------------------------------
inline std::string_view errorToString (json_parse_error_e error)
{
switch (error)
{
case json_parse_error_e::json_parse_error_none: return {};
case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket:
return "json_parse_error_expected_comma_or_closing_bracket";
case json_parse_error_e::json_parse_error_expected_colon:
return "json_parse_error_expected_colon";
case json_parse_error_e::json_parse_error_expected_opening_quote:
return "json_parse_error_expected_opening_quote";
case json_parse_error_e::json_parse_error_invalid_string_escape_sequence:
return "json_parse_error_invalid_string_escape_sequence";
case json_parse_error_e::json_parse_error_invalid_number_format:
return "json_parse_error_invalid_number_format";
case json_parse_error_e::json_parse_error_invalid_value:
return "json_parse_error_invalid_value";
case json_parse_error_e::json_parse_error_premature_end_of_buffer:
return "json_parse_error_premature_end_of_buffer";
case json_parse_error_e::json_parse_error_invalid_string:
return "json_parse_error_invalid_string";
case json_parse_error_e::json_parse_error_allocator_failed:
return "json_parse_error_allocator_failed";
case json_parse_error_e::json_parse_error_unexpected_trailing_characters:
return "json_parse_error_unexpected_trailing_characters";
case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown";
}
return {};
}

//------------------------------------------------------------------------
} // JSON

+ 99
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfo.h View File

@@ -0,0 +1,99 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfo.h
// Created by : Steinberg, 12/2021
// Description :
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include <string>
#include <vector>

//------------------------------------------------------------------------
namespace Steinberg {

//------------------------------------------------------------------------
struct ModuleInfo
{
//------------------------------------------------------------------------
struct FactoryInfo
{
std::string vendor;
std::string url;
std::string email;
int32_t flags {0};
};

//------------------------------------------------------------------------
struct Snapshot
{
double scaleFactor {1.};
std::string path;
};
using SnapshotList = std::vector<Snapshot>;

//------------------------------------------------------------------------
struct ClassInfo
{
std::string cid;
std::string category;
std::string name;
std::string vendor;
std::string version;
std::string sdkVersion;
std::vector<std::string> subCategories;
SnapshotList snapshots;
int32_t cardinality {0x7FFFFFFF};
uint32_t flags {0};
};

//------------------------------------------------------------------------
struct Compatibility
{
std::string newCID;
std::vector<std::string> oldCID;
};

using ClassList = std::vector<ClassInfo>;
using CompatibilityList = std::vector<Compatibility>;

std::string name;
std::string version;
FactoryInfo factoryInfo;
ClassList classes;
CompatibilityList compatibility;
};

//------------------------------------------------------------------------
} // Steinberg

+ 309
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp View File

@@ -0,0 +1,309 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp
// Created by : Steinberg, 12/2021
// Description : utility functions to create moduleinfo json files
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "moduleinfocreator.h"
#include "jsoncxx.h"
#include <algorithm>
#include <stdexcept>
#include <string>

//------------------------------------------------------------------------
namespace Steinberg::ModuleInfoLib {
using namespace VST3;
namespace {

//------------------------------------------------------------------------
struct JSON5Writer
{
private:
std::ostream& stream;
bool beautify;
bool lastIsComma {false};
int32_t intend {0};

void doBeautify ()
{
if (beautify)
{
stream << '\n';
for (int i = 0; i < intend; ++i)
stream << " ";
}
}

void writeComma ()
{
if (lastIsComma)
return;
stream << ",";
lastIsComma = true;
}
void startObject ()
{
stream << "{";
++intend;
lastIsComma = false;
}
void endObject ()
{
--intend;
doBeautify ();
stream << "}";
lastIsComma = false;
}
void startArray ()
{
stream << "[";
++intend;
lastIsComma = false;
}
void endArray ()
{
--intend;
doBeautify ();
stream << "]";
lastIsComma = false;
}

public:
JSON5Writer (std::ostream& stream, bool beautify = true) : stream (stream), beautify (beautify)
{
}

void string (std::string_view str)
{
stream << "\"" << str << "\"";
lastIsComma = false;
}

void boolean (bool val)
{
stream << (val ? "true" : "false");
lastIsComma = false;
}

template <typename ValueT>
void value (ValueT val)
{
stream << val;
lastIsComma = false;
}

template <typename Proc>
void object (Proc proc)
{
startObject ();
proc ();
endObject ();
}

template <typename Iterator, typename Proc>
void array (Iterator begin, Iterator end, Proc proc)
{
startArray ();
while (begin != end)
{
doBeautify ();
proc (begin);
++begin;
writeComma ();
}
endArray ();
}

template <typename Proc>
void keyValue (std::string_view key, Proc proc)
{
doBeautify ();
string (key);
stream << ": ";
proc ();
writeComma ();
}
};

//------------------------------------------------------------------------
void writeSnapshots (const ModuleInfo::SnapshotList& snapshots, JSON5Writer& w)
{
w.keyValue ("Snapshots", [&] () {
w.array (snapshots.begin (), snapshots.end (), [&] (const auto& el) {
w.object ([&] () {
w.keyValue ("Scale Factor", [&] () { w.value (el->scaleFactor); });
w.keyValue ("Path", [&] () { w.string (el->path); });
});
});
});
}

//------------------------------------------------------------------------
void writeClassInfo (const ModuleInfo::ClassInfo& cls, JSON5Writer& w)
{
w.keyValue ("CID", [&] () { w.string (cls.cid); });
w.keyValue ("Category", [&] () { w.string (cls.category); });
w.keyValue ("Name", [&] () { w.string (cls.name); });
w.keyValue ("Vendor", [&] () { w.string (cls.vendor); });
w.keyValue ("Version", [&] () { w.string (cls.version); });
w.keyValue ("SDKVersion", [&] () { w.string (cls.sdkVersion); });
const auto& sc = cls.subCategories;
if (!sc.empty ())
{
w.keyValue ("Sub Categories", [&] () {
w.array (sc.begin (), sc.end (), [&] (const auto& cat) { w.string (*cat); });
});
}
w.keyValue ("Class Flags", [&] () { w.value (cls.flags); });
w.keyValue ("Cardinality", [&] () { w.value (cls.cardinality); });
writeSnapshots (cls.snapshots, w);
}

//------------------------------------------------------------------------
void writePluginCompatibility (const ModuleInfo::CompatibilityList& compat, JSON5Writer& w)
{
if (compat.empty ())
return;
w.keyValue ("Compatibility", [&] () {
w.array (compat.begin (), compat.end (), [&] (auto& el) {
w.object ([&] () {
w.keyValue ("New", [&] () { w.string (el->newCID); });
w.keyValue ("Old", [&] () {
w.array (el->oldCID.begin (), el->oldCID.end (),
[&] (auto& oldEl) { w.string (*oldEl); });
});
});
});
});
}

//------------------------------------------------------------------------
void writeFactoryInfo (const ModuleInfo::FactoryInfo& fi, JSON5Writer& w)
{
w.keyValue ("Factory Info", [&] () {
w.object ([&] () {
w.keyValue ("Vendor", [&] () { w.string (fi.vendor); });
w.keyValue ("URL", [&] () { w.string (fi.url); });
w.keyValue ("E-Mail", [&] () { w.string (fi.email); });
w.keyValue ("Flags", [&] () {
w.object ([&] () {
w.keyValue ("Unicode",
[&] () { w.boolean (fi.flags & PFactoryInfo::kUnicode); });
w.keyValue ("Classes Discardable", [&] () {
w.boolean (fi.flags & PFactoryInfo::kClassesDiscardable);
});
w.keyValue ("Component Non Discardable", [&] () {
w.boolean (fi.flags & PFactoryInfo::kComponentNonDiscardable);
});
});
});
});
});
}

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses)
{
ModuleInfo info;

const auto& factory = module.getFactory ();
auto factoryInfo = factory.info ();

info.name = module.getName ();
auto pos = info.name.find_last_of ('.');
if (pos != std::string::npos)
info.name.erase (pos);

info.factoryInfo.vendor = factoryInfo.vendor ();
info.factoryInfo.url = factoryInfo.url ();
info.factoryInfo.email = factoryInfo.email ();
info.factoryInfo.flags = factoryInfo.flags ();

if (factoryInfo.classesDiscardable () == false ||
(factoryInfo.classesDiscardable () && includeDiscardableClasses))
{
auto snapshots = VST3::Hosting::Module::getSnapshots (module.getPath ());
for (const auto& ci : factory.classInfos ())
{
ModuleInfo::ClassInfo classInfo;
classInfo.cid = ci.ID ().toString ();
classInfo.category = ci.category ();
classInfo.name = ci.name ();
classInfo.vendor = ci.vendor ();
classInfo.version = ci.version ();
classInfo.sdkVersion = ci.sdkVersion ();
classInfo.subCategories = ci.subCategories ();
classInfo.cardinality = ci.cardinality ();
classInfo.flags = ci.classFlags ();
auto snapshotIt = std::find_if (snapshots.begin (), snapshots.end (),
[&] (const auto& el) { return el.uid == ci.ID (); });
if (snapshotIt != snapshots.end ())
{
for (auto& s : snapshotIt->images)
{
std::string_view path (s.path);
if (path.find (module.getPath ()) == 0)
path.remove_prefix (module.getPath ().size () + 1);
classInfo.snapshots.emplace_back (
ModuleInfo::Snapshot {s.scaleFactor, {path.data (), path.size ()}});
}
snapshots.erase (snapshotIt);
}
info.classes.emplace_back (std::move (classInfo));
}
}
return info;
}

//------------------------------------------------------------------------
void outputJson (const ModuleInfo& info, std::ostream& output)
{
JSON5Writer w (output);
w.object ([&] () {
w.keyValue ("Name", [&] () { w.string (info.name); });
w.keyValue ("Version", [&] () { w.string (info.version); });
writeFactoryInfo (info.factoryInfo, w);
writePluginCompatibility (info.compatibility, w);
w.keyValue ("Classes", [&] () {
w.array (info.classes.begin (), info.classes.end (),
[&] (const auto& cls) { w.object ([&] () { writeClassInfo (*cls, w); }); });
});
});
}

//------------------------------------------------------------------------
} // Steinberg::ModuelInfoLib

+ 67
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfocreator.h View File

@@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.h
// Created by : Steinberg, 12/2021
// Description : utility functions to create moduleinfo json files
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "moduleinfo.h"
#include "public.sdk/source/vst/hosting/module.h"
#include <iostream>
#include <optional>
#include <string_view>

//------------------------------------------------------------------------
namespace Steinberg::ModuleInfoLib {

//------------------------------------------------------------------------
/** create a ModuleInfo from a module
*
* @param module module to create the module info from
* @param includeDiscardableClasses if true adds the current available classes to the module info
* @return a ModuleInfo struct with the classes and factory info of the module
*/
ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses);

//------------------------------------------------------------------------
/** output the ModuleInfo as json to the stream
*
* @param info module info
* @param output output stream
*/
void outputJson (const ModuleInfo& info, std::ostream& output);

//------------------------------------------------------------------------
} // Steinberg::ModuelInfoLib

+ 536
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp View File

@@ -0,0 +1,536 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
// Created by : Steinberg, 01/2022
// Description : utility functions to parse moduleinfo json files
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "moduleinfoparser.h"
#include "jsoncxx.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <stdexcept>

//------------------------------------------------------------------------
namespace Steinberg::ModuleInfoLib {
namespace {

//------------------------------------------------------------------------
void printJsonParseError (json_parse_result_s& parseResult, std::ostream& errorOut)
{
errorOut << "error : "
<< JSON::errorToString (static_cast<json_parse_error_e> (parseResult.error)) << '\n';
errorOut << "offset : " << parseResult.error_offset << '\n';
errorOut << "line no: " << parseResult.error_line_no << '\n';
errorOut << "row no : " << parseResult.error_row_no << '\n';
}

//------------------------------------------------------------------------
struct parse_error : std::exception
{
parse_error (const std::string& str, const JSON::Value& value)
: str (str), location (value.getSourceLocation ())
{
addLocation (location);
}
parse_error (const std::string& str, const JSON::String& value)
: str (str), location (value.getSourceLocation ())
{
addLocation (location);
}
const char* what () const noexcept override { return str.data (); }

private:
void addLocation (const JSON::SourceLocation& loc)
{
str += '\n';
str += "offset:";
str += std::to_string (loc.offset);
str += '\n';
str += "line:";
str += std::to_string (loc.line);
str += '\n';
str += "row:";
str += std::to_string (loc.row);
str += '\n';
}

std::string str;
JSON::SourceLocation location;
};

//------------------------------------------------------------------------
struct ModuleInfoJsonParser
{
ModuleInfoJsonParser () = default;

std::string_view getText (const JSON::Value& value) const
{
if (auto str = value.asString ())
return str->text ();
throw parse_error ("Expect a String here", value);
}

template <typename T>
T getInteger (const JSON::Value& value) const
{
if (auto number = value.asNumber ())
{
if (auto result = number->getInteger ())
{
if (result > static_cast<int64_t> (std::numeric_limits<T>::max ()) ||
result < static_cast<int64_t> (std::numeric_limits<T>::min ()))
throw parse_error ("Value is out of range here", value);
return static_cast<T> (*result);
}
throw parse_error ("Expect an Integer here", value);
}
throw parse_error ("Expect a Number here", value);
}

double getDouble (const JSON::Value& value) const
{
if (auto number = value.asNumber ())
{
if (auto result = number->getDouble ())
return *result;
throw parse_error ("Expect a Double here", value);
}
throw parse_error ("Expect a Number here", value);
}

void parseFactoryInfo (const JSON::Value& value)
{
enum ParsedBits
{
Vendor = 1 << 0,
URL = 1 << 1,
EMail = 1 << 2,
Flags = 1 << 3,
};
uint32_t parsed {0};
if (auto obj = value.asObject ())
{
for (const auto& el : *obj)
{
auto elementName = el.name ().text ();
if (elementName == "Vendor")
{
if (parsed & ParsedBits::Vendor)
throw parse_error ("Only one 'Vendor' key allowed", el.name ());
parsed |= ParsedBits::Vendor;
info.factoryInfo.vendor = getText (el.value ());
}
else if (elementName == "URL")
{
if (parsed & ParsedBits::URL)
throw parse_error ("Only one 'URL' key allowed", el.name ());
parsed |= ParsedBits::URL;
info.factoryInfo.url = getText (el.value ());
}
else if (elementName == "E-Mail")
{
if (parsed & ParsedBits::EMail)
throw parse_error ("Only one 'E-Mail' key allowed", el.name ());
parsed |= ParsedBits::EMail;
info.factoryInfo.email = getText (el.value ());
}
else if (elementName == "Flags")
{
if (parsed & ParsedBits::Flags)
throw parse_error ("Only one 'Flags' key allowed", el.name ());
auto flags = el.value ().asObject ();
if (!flags)
throw parse_error ("Expect 'Flags' to be a JSON Object", el.name ());
for (const auto& flag : *flags)
{
auto flagName = flag.name ().text ();
auto flagValue = flag.value ().asBoolean ();
if (!flagValue)
throw parse_error ("Flag must be a boolean", flag.value ());
if (flagName == "Classes Discardable")
{
if (*flagValue)
info.factoryInfo.flags |= PFactoryInfo::kClassesDiscardable;
}
else if (flagName == "Component Non Discardable")
{
if (*flagValue)
info.factoryInfo.flags |= PFactoryInfo::kComponentNonDiscardable;
}
else if (flagName == "Unicode")
{
if (*flagValue)
info.factoryInfo.flags |= PFactoryInfo::kUnicode;
}
else
throw parse_error ("Unknown flag", flag.name ());
}
parsed |= ParsedBits::Flags;
}
}
}
if (!(parsed & ParsedBits::Vendor))
throw std::logic_error ("Missing 'Vendor' in Factory Info");
if (!(parsed & ParsedBits::URL))
throw std::logic_error ("Missing 'URL' in Factory Info");
if (!(parsed & ParsedBits::EMail))
throw std::logic_error ("Missing 'EMail' in Factory Info");
if (!(parsed & ParsedBits::Flags))
throw std::logic_error ("Missing 'Flags' in Factory Info");
}

void parseClasses (const JSON::Value& value)
{
enum ParsedBits
{
CID = 1 << 0,
Category = 1 << 1,
Name = 1 << 2,
Vendor = 1 << 3,
Version = 1 << 4,
SDKVersion = 1 << 5,
SubCategories = 1 << 6,
ClassFlags = 1 << 7,
Snapshots = 1 << 8,
Cardinality = 1 << 9,
};

auto array = value.asArray ();
if (!array)
throw parse_error ("Expect Classes Array", value);
for (const auto& classInfoEl : *array)
{
auto classInfo = classInfoEl.value ().asObject ();
if (!classInfo)
throw parse_error ("Expect Class Object", classInfoEl.value ());

ModuleInfo::ClassInfo ci {};

uint32_t parsed {0};

for (const auto& el : *classInfo)
{
auto elementName = el.name ().text ();
if (elementName == "CID")
{
if (parsed & ParsedBits::CID)
throw parse_error ("Only one 'CID' key allowed", el.name ());
ci.cid = getText (el.value ());
parsed |= ParsedBits::CID;
}
else if (elementName == "Category")
{
if (parsed & ParsedBits::Category)
throw parse_error ("Only one 'Category' key allowed", el.name ());
ci.category = getText (el.value ());
parsed |= ParsedBits::Category;
}
else if (elementName == "Name")
{
if (parsed & ParsedBits::Name)
throw parse_error ("Only one 'Name' key allowed", el.name ());
ci.name = getText (el.value ());
parsed |= ParsedBits::Name;
}
else if (elementName == "Vendor")
{
if (parsed & ParsedBits::Vendor)
throw parse_error ("Only one 'Vendor' key allowed", el.name ());
ci.vendor = getText (el.value ());
parsed |= ParsedBits::Vendor;
}
else if (elementName == "Version")
{
if (parsed & ParsedBits::Version)
throw parse_error ("Only one 'Version' key allowed", el.name ());
ci.version = getText (el.value ());
parsed |= ParsedBits::Version;
}
else if (elementName == "SDKVersion")
{
if (parsed & ParsedBits::SDKVersion)
throw parse_error ("Only one 'SDKVersion' key allowed", el.name ());
ci.sdkVersion = getText (el.value ());
parsed |= ParsedBits::SDKVersion;
}
else if (elementName == "Sub Categories")
{
if (parsed & ParsedBits::SubCategories)
throw parse_error ("Only one 'Sub Categories' key allowed", el.name ());
auto subCatArr = el.value ().asArray ();
if (!subCatArr)
throw parse_error ("Expect Array here", el.value ());
for (const auto& catEl : *subCatArr)
{
auto cat = getText (catEl.value ());
ci.subCategories.emplace_back (cat);
}
parsed |= ParsedBits::SubCategories;
}
else if (elementName == "Class Flags")
{
if (parsed & ParsedBits::ClassFlags)
throw parse_error ("Only one 'Class Flags' key allowed", el.name ());
ci.flags = getInteger<uint32_t> (el.value ());
parsed |= ParsedBits::ClassFlags;
}
else if (elementName == "Cardinality")
{
if (parsed & ParsedBits::Cardinality)
throw parse_error ("Only one 'Cardinality' key allowed", el.name ());
ci.cardinality = getInteger<int32_t> (el.value ());
parsed |= ParsedBits::Cardinality;
}
else if (elementName == "Snapshots")
{
if (parsed & ParsedBits::Snapshots)
throw parse_error ("Only one 'Snapshots' key allowed", el.name ());
auto snapArr = el.value ().asArray ();
if (!snapArr)
throw parse_error ("Expect Array here", el.value ());
for (const auto& snapEl : *snapArr)
{
auto snap = snapEl.value ().asObject ();
if (!snap)
throw parse_error ("Expect Object here", snapEl.value ());
ModuleInfo::Snapshot snapshot;
for (const auto& spEl : *snap)
{
auto spElName = spEl.name ().text ();
if (spElName == "Path")
snapshot.path = getText (spEl.value ());
else if (spElName == "Scale Factor")
snapshot.scaleFactor = getDouble (spEl.value ());
else
throw parse_error ("Unexpected key", spEl.name ());
}
if (snapshot.scaleFactor == 0. || snapshot.path.empty ())
throw parse_error ("Missing Snapshot keys", snapEl.value ());
ci.snapshots.emplace_back (std::move (snapshot));
}
parsed |= ParsedBits::Snapshots;
}
else
throw parse_error ("Unexpected key", el.name ());
}
if (!(parsed & ParsedBits::CID))
throw parse_error ("'CID' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::Category))
throw parse_error ("'Category' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::Name))
throw parse_error ("'Name' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::Vendor))
throw parse_error ("'Vendor' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::Version))
throw parse_error ("'Version' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::SDKVersion))
throw parse_error ("'SDK Version' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::ClassFlags))
throw parse_error ("'Class Flags' key missing", classInfoEl.value ());
if (!(parsed & ParsedBits::Cardinality))
throw parse_error ("'Cardinality' key missing", classInfoEl.value ());
info.classes.emplace_back (std::move (ci));
}
}

void parseCompatibility (const JSON::Value& value)
{
auto arr = value.asArray ();
if (!arr)
throw parse_error ("Expect Array here", value);
for (const auto& el : *arr)
{
auto obj = el.value ().asObject ();
if (!obj)
throw parse_error ("Expect Object here", el.value ());

ModuleInfo::Compatibility compat;
for (const auto& objEl : *obj)
{
auto elementName = objEl.name ().text ();
if (elementName == "New")
compat.newCID = getText (objEl.value ());
else if (elementName == "Old")
{
auto oldElArr = objEl.value ().asArray ();
if (!oldElArr)
throw parse_error ("Expect Array here", objEl.value ());
for (const auto& old : *oldElArr)
{
compat.oldCID.emplace_back (getText (old.value ()));
}
}
}
if (compat.newCID.empty ())
throw parse_error ("Expect New CID here", el.value ());
if (compat.oldCID.empty ())
throw parse_error ("Expect Old CID here", el.value ());
info.compatibility.emplace_back (std::move (compat));
}
}

void parse (const JSON::Document& doc)
{
auto docObj = doc.asObject ();
if (!docObj)
throw parse_error ("Unexpected", doc);

enum ParsedBits
{
Name = 1 << 0,
Version = 1 << 1,
FactoryInfo = 1 << 2,
Compatibility = 1 << 3,
Classes = 1 << 4,
};

uint32_t parsed {0};
for (const auto& el : *docObj)
{
auto elementName = el.name ().text ();
if (elementName == "Name")
{
if (parsed & ParsedBits::Name)
throw parse_error ("Only one 'Name' key allowed", el.name ());
parsed |= ParsedBits::Name;
info.name = getText (el.value ());
}
else if (elementName == "Version")
{
if (parsed & ParsedBits::Version)
throw parse_error ("Only one 'Version' key allowed", el.name ());
parsed |= ParsedBits::Version;
info.version = getText (el.value ());
}
else if (elementName == "Factory Info")
{
if (parsed & ParsedBits::FactoryInfo)
throw parse_error ("Only one 'Factory Info' key allowed", el.name ());
parseFactoryInfo (el.value ());
parsed |= ParsedBits::FactoryInfo;
}
else if (elementName == "Compatibility")
{
if (parsed & ParsedBits::Compatibility)
throw parse_error ("Only one 'Compatibility' key allowed", el.name ());
parseCompatibility (el.value ());
parsed |= ParsedBits::Compatibility;
}
else if (elementName == "Classes")
{
if (parsed & ParsedBits::Classes)
throw parse_error ("Only one 'Classes' key allowed", el.name ());
parseClasses (el.value ());
parsed |= ParsedBits::Classes;
}
else
{
throw parse_error ("Unexpected JSON Token", el.name ());
}
}
if (!(parsed & ParsedBits::Name))
throw std::logic_error ("'Name' key missing");
if (!(parsed & ParsedBits::Version))
throw std::logic_error ("'Version' key missing");
if (!(parsed & ParsedBits::FactoryInfo))
throw std::logic_error ("'Factory Info' key missing");
if (!(parsed & ParsedBits::Classes))
throw std::logic_error ("'Classes' key missing");
}

ModuleInfo&& takeInfo () { return std::move (info); }

private:
ModuleInfo info;
};

//------------------------------------------------------------------------
} // anonymous

//------------------------------------------------------------------------
std::optional<ModuleInfo> parseJson (std::string_view jsonData, std::ostream* optErrorOutput)
{
auto docVar = JSON::Document::parse (jsonData);
if (auto res = std::get_if<json_parse_result_s> (&docVar))
{
if (optErrorOutput)
printJsonParseError (*res, *optErrorOutput);
return {};
}
auto doc = std::get_if<JSON::Document> (&docVar);
assert (doc);
try
{
ModuleInfoJsonParser parser;
parser.parse (*doc);
return parser.takeInfo ();
}
catch (std::exception& error)
{
if (optErrorOutput)
*optErrorOutput << error.what () << '\n';
return {};
}
// unreachable
}

//------------------------------------------------------------------------
std::optional<ModuleInfo::CompatibilityList> parseCompatibilityJson (std::string_view jsonData,
std::ostream* optErrorOutput)
{
auto docVar = JSON::Document::parse (jsonData);
if (auto res = std::get_if<json_parse_result_s> (&docVar))
{
if (optErrorOutput)
printJsonParseError (*res, *optErrorOutput);
return {};
}
auto doc = std::get_if<JSON::Document> (&docVar);
assert (doc);
try
{
ModuleInfoJsonParser parser;
parser.parseCompatibility (*doc);
return parser.takeInfo ().compatibility;
}
catch (std::exception& error)
{
if (optErrorOutput)
*optErrorOutput << error.what () << '\n';
return {};
}
// unreachable
}

//------------------------------------------------------------------------
} // Steinberg::ModuelInfoLib

+ 68
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/moduleinfo/moduleinfoparser.h View File

@@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.h
// Created by : Steinberg, 01/2022
// Description : utility functions to parse moduleinfo json files
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "moduleinfo.h"
#include <iostream>
#include <optional>
#include <string_view>

//------------------------------------------------------------------------
namespace Steinberg::ModuleInfoLib {

//------------------------------------------------------------------------
/** parse a json formatted string to a ModuleInfo struct
*
* @param jsonData a string view to a json formatted string
* @param optErrorOutput optional error output stream where to print parse error
* @return ModuleInfo if parsing succeeded
*/
std::optional<ModuleInfo> parseJson (std::string_view jsonData, std::ostream* optErrorOutput);

//------------------------------------------------------------------------
/** parse a json formatted string to a ModuleInfo::CompatibilityList
*
* @param jsonData a string view to a json formatted string
* @param optErrorOutput optional error output stream where to print parse error
* @return ModuleInfo::CompatibilityList if parsing succeeded
*/
std::optional<ModuleInfo::CompatibilityList> parseCompatibilityJson (std::string_view jsonData,
std::ostream* optErrorOutput);

//------------------------------------------------------------------------
} // Steinberg::ModuelInfoLib

+ 135
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/optional.h View File

@@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/utility/optional.h
// Created by : Steinberg, 08/2016
// Description : optional helper
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include <cassert>
#include <memory>
#include <utility>

//------------------------------------------------------------------------
namespace VST3 {

//------------------------------------------------------------------------
template <typename T>
struct Optional
{
Optional () noexcept : valid (false) {}
explicit Optional (const T& v) noexcept : _value (v), valid (true) {}
Optional (T&& v) noexcept : _value (std::move (v)), valid (true) {}

Optional (Optional&& other) noexcept { *this = std::move (other); }
Optional& operator= (Optional&& other) noexcept
{
valid = other.valid;
_value = std::move (other._value);
return *this;
}

explicit operator bool () const noexcept
{
setValidationChecked ();
return valid;
}

const T& operator* () const noexcept
{
checkValid ();
return _value;
}

const T* operator-> () const noexcept
{
checkValid ();
return &_value;
}

T& operator* () noexcept
{
checkValid ();
return _value;
}

T* operator-> () noexcept
{
checkValid ();
return &_value;
}

T&& value () noexcept
{
checkValid ();
return move (_value);
}

const T& value () const noexcept
{
checkValid ();
return _value;
}

void swap (T& other) noexcept
{
checkValid ();
auto tmp = std::move (other);
other = std::move (_value);
_value = std::move (tmp);
}

private:
T _value {};
bool valid;

#if !defined(NDEBUG)
mutable bool validationChecked {false};
#endif

void setValidationChecked () const
{
#if !defined(NDEBUG)
validationChecked = true;
#endif
}
void checkValid () const
{
#if !defined(NDEBUG)
assert (validationChecked);
#endif
}
};

//------------------------------------------------------------------------
}

+ 294
- 0
modules/juce_audio_processors/format_types/VST3_SDK/public.sdk/source/vst/utility/uid.h View File

@@ -0,0 +1,294 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/utility/uid.h
// Created by : Steinberg, 08/2016
// Description : UID
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

#include "optional.h"
#include "pluginterfaces/base/funknown.h"
#include <string>

//------------------------------------------------------------------------
namespace VST3 {

//------------------------------------------------------------------------
struct UID
{
#if defined(SMTG_OS_WINDOWS) && SMTG_OS_WINDOWS == 1
static constexpr bool defaultComFormat = true;
#else
static constexpr bool defaultComFormat = false;
#endif

using TUID = Steinberg::TUID;

constexpr UID () noexcept = default;
UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat = defaultComFormat)
noexcept;
UID (const TUID& uid) noexcept;
UID (const UID& uid) noexcept;
UID& operator= (const UID& uid) noexcept;
UID& operator= (const TUID& uid) noexcept;

constexpr const TUID& data () const noexcept;
constexpr size_t size () const noexcept;

std::string toString (bool comFormat = defaultComFormat) const noexcept;

template<typename StringT>
static Optional<UID> fromString (const StringT& str,
bool comFormat = defaultComFormat) noexcept;

static UID fromTUID (const TUID _uid) noexcept;
//------------------------------------------------------------------------
private:
Steinberg::TUID _data {};

struct GUID
{
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
};
};

//------------------------------------------------------------------------
inline bool operator== (const UID& uid1, const UID& uid2)
{
const uint64_t* p1 = reinterpret_cast<const uint64_t*> (uid1.data ());
const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid2.data ());
return p1[0] == p2[0] && p1[1] == p2[1];
}

//------------------------------------------------------------------------
inline bool operator!= (const UID& uid1, const UID& uid2)
{
return !(uid1 == uid2);
}

//------------------------------------------------------------------------
inline bool operator< (const UID& uid1, const UID& uid2)
{
const uint64_t* p1 = reinterpret_cast<const uint64_t*> (uid1.data ());
const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid2.data ());
return (p1[0] < p2[0]) && (p1[1] < p2[1]);
}

//------------------------------------------------------------------------
inline UID::UID (uint32_t l1, uint32_t l2, uint32_t l3, uint32_t l4, bool comFormat) noexcept
{
if (comFormat)
{
_data[0] = static_cast<int8_t> ((l1 & 0x000000FF));
_data[1] = static_cast<int8_t> ((l1 & 0x0000FF00) >> 8);
_data[2] = static_cast<int8_t> ((l1 & 0x00FF0000) >> 16);
_data[3] = static_cast<int8_t> ((l1 & 0xFF000000) >> 24);
_data[4] = static_cast<int8_t> ((l2 & 0x00FF0000) >> 16);
_data[5] = static_cast<int8_t> ((l2 & 0xFF000000) >> 24);
_data[6] = static_cast<int8_t> ((l2 & 0x000000FF));
_data[7] = static_cast<int8_t> ((l2 & 0x0000FF00) >> 8);
_data[8] = static_cast<int8_t> ((l3 & 0xFF000000) >> 24);
_data[9] = static_cast<int8_t> ((l3 & 0x00FF0000) >> 16);
_data[10] = static_cast<int8_t> ((l3 & 0x0000FF00) >> 8);
_data[11] = static_cast<int8_t> ((l3 & 0x000000FF));
_data[12] = static_cast<int8_t> ((l4 & 0xFF000000) >> 24);
_data[13] = static_cast<int8_t> ((l4 & 0x00FF0000) >> 16);
_data[14] = static_cast<int8_t> ((l4 & 0x0000FF00) >> 8);
_data[15] = static_cast<int8_t> ((l4 & 0x000000FF));
}
else
{
_data[0] = static_cast<int8_t> ((l1 & 0xFF000000) >> 24);
_data[1] = static_cast<int8_t> ((l1 & 0x00FF0000) >> 16);
_data[2] = static_cast<int8_t> ((l1 & 0x0000FF00) >> 8);
_data[3] = static_cast<int8_t> ((l1 & 0x000000FF));
_data[4] = static_cast<int8_t> ((l2 & 0xFF000000) >> 24);
_data[5] = static_cast<int8_t> ((l2 & 0x00FF0000) >> 16);
_data[6] = static_cast<int8_t> ((l2 & 0x0000FF00) >> 8);
_data[7] = static_cast<int8_t> ((l2 & 0x000000FF));
_data[8] = static_cast<int8_t> ((l3 & 0xFF000000) >> 24);
_data[9] = static_cast<int8_t> ((l3 & 0x00FF0000) >> 16);
_data[10] = static_cast<int8_t> ((l3 & 0x0000FF00) >> 8);
_data[11] = static_cast<int8_t> ((l3 & 0x000000FF));
_data[12] = static_cast<int8_t> ((l4 & 0xFF000000) >> 24);
_data[13] = static_cast<int8_t> ((l4 & 0x00FF0000) >> 16);
_data[14] = static_cast<int8_t> ((l4 & 0x0000FF00) >> 8);
_data[15] = static_cast<int8_t> ((l4 & 0x000000FF));
}
}

//------------------------------------------------------------------------
inline UID::UID (const TUID& uid) noexcept
{
*this = uid;
}

//------------------------------------------------------------------------
inline UID::UID (const UID& uid) noexcept
{
*this = uid;
}

//------------------------------------------------------------------------
inline UID& UID::operator= (const UID& uid) noexcept
{
*this = uid.data ();
return *this;
}

//------------------------------------------------------------------------
inline UID& UID::operator= (const TUID& uid) noexcept
{
uint64_t* p1 = reinterpret_cast<uint64_t*> (_data);
const uint64_t* p2 = reinterpret_cast<const uint64_t*> (uid);
p1[0] = p2[0];
p1[1] = p2[1];
return *this;
}

//------------------------------------------------------------------------
inline constexpr auto UID::data () const noexcept -> const TUID&
{
return _data;
}

//------------------------------------------------------------------------
inline constexpr size_t UID::size () const noexcept
{
return sizeof (TUID);
}

//------------------------------------------------------------------------
inline std::string UID::toString (bool comFormat) const noexcept
{
std::string result;
result.reserve (32);
if (comFormat)
{
const auto& g = reinterpret_cast<const GUID*> (_data);

char tmp[21] {};
snprintf (tmp, 21, "%08X%04X%04X", g->Data1, g->Data2, g->Data3);
result = tmp;

for (uint32_t i = 0; i < 8; ++i)
{
char s[3] {};
snprintf (s, 3, "%02X", g->Data4[i]);
result += s;
}
}
else
{
for (uint32_t i = 0; i < 16; ++i)
{
char s[3] {};
snprintf (s, 3, "%02X", static_cast<uint8_t> (_data[i]));
result += s;
}
}
return result;
}

//------------------------------------------------------------------------
template<typename StringT>
inline Optional<UID> UID::fromString (const StringT& str, bool comFormat) noexcept
{
if (str.length () != 32)
return {};
// TODO: this is a copy from FUID. there are no input validation checks !!!
if (comFormat)
{
TUID uid {};
GUID g;
char s[33];

strcpy (s, str.data ());
s[8] = 0;
sscanf (s, "%x", &g.Data1);
strcpy (s, str.data () + 8);
s[4] = 0;
sscanf (s, "%hx", &g.Data2);
strcpy (s, str.data () + 12);
s[4] = 0;
sscanf (s, "%hx", &g.Data3);

memcpy (uid, &g, 8);

for (uint32_t i = 8; i < 16; ++i)
{
char s2[3] {};
s2[0] = str[i * 2];
s2[1] = str[i * 2 + 1];

int32_t d = 0;
sscanf (s2, "%2x", &d);
uid[i] = static_cast<char> (d);
}
return {uid};
}
else
{
TUID uid {};
for (uint32_t i = 0; i < 16; ++i)
{
char s[3] {};
s[0] = str[i * 2];
s[1] = str[i * 2 + 1];

int32_t d = 0;
sscanf (s, "%2x", &d);
uid[i] = static_cast<char> (d);
}
return {uid};
}
}

//------------------------------------------------------------------------
inline UID UID::fromTUID (const TUID _uid) noexcept
{
UID result;

uint64_t* p1 = reinterpret_cast<uint64_t*> (result._data);
const uint64_t* p2 = reinterpret_cast<const uint64_t*> (_uid);
p1[0] = p2[0];
p1[1] = p2[1];

return result;
}

//------------------------------------------------------------------------
} // VST3

Loading…
Cancel
Save