mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
VST3: Add moduleinfotool sources
This commit is contained in:
parent
dccde43300
commit
417f0e9ca3
17 changed files with 8026 additions and 0 deletions
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
```
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
}
|
||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue