Initial release
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
|
||||
if(ANDROID)
|
||||
return()
|
||||
endif(ANDROID)
|
||||
|
||||
# Add VST3 Hosting Samples
|
||||
if(NOT DEFINED SMTG_ADD_VST3_UTILITIES)
|
||||
option(SMTG_ADD_VST3_UTILITIES "Build VST3 Utilities" ON)
|
||||
endif(NOT DEFINED SMTG_ADD_VST3_UTILITIES)
|
||||
|
||||
if(SMTG_ADD_VST3_UTILITIES)
|
||||
include(SMTG_AddSubDirectories)
|
||||
smtg_add_subdirectories()
|
||||
endif(SMTG_ADD_VST3_UTILITIES)
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
|
||||
set(moduleinfotool_sources
|
||||
source/main.cpp
|
||||
../../../source/vst/moduleinfo/json.h
|
||||
../../../source/vst/moduleinfo/jsoncxx.h
|
||||
../../../source/vst/moduleinfo/moduleinfo.h
|
||||
../../../source/vst/moduleinfo/moduleinfocreator.cpp
|
||||
../../../source/vst/moduleinfo/moduleinfocreator.h
|
||||
../../../source/vst/moduleinfo/moduleinfoparser.cpp
|
||||
../../../source/vst/moduleinfo/moduleinfoparser.h
|
||||
)
|
||||
|
||||
if(SMTG_MAC)
|
||||
set(moduleinfotool_sources
|
||||
${moduleinfotool_sources}
|
||||
${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm
|
||||
)
|
||||
set_source_files_properties(
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm" PROPERTIES
|
||||
COMPILE_FLAGS "-fobjc-arc"
|
||||
)
|
||||
elseif(SMTG_LINUX)
|
||||
set(moduleinfotool_sources
|
||||
${moduleinfotool_sources}
|
||||
${SDK_ROOT}/public.sdk/source/vst/hosting/module_linux.cpp
|
||||
)
|
||||
elseif(SMTG_WIN)
|
||||
set(moduleinfotool_sources
|
||||
${moduleinfotool_sources}
|
||||
${SDK_ROOT}/public.sdk/source/vst/hosting/module_win32.cpp
|
||||
)
|
||||
endif(SMTG_MAC)
|
||||
|
||||
set(target moduleinfotool)
|
||||
add_executable(${target} ${moduleinfotool_sources})
|
||||
|
||||
if(SDK_IDE_UTILITIES_FOLDER)
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
${SDK_IDE_UTILITIES_FOLDER}
|
||||
)
|
||||
endif(SDK_IDE_UTILITIES_FOLDER)
|
||||
|
||||
target_compile_features(${target}
|
||||
PUBLIC
|
||||
cxx_std_17
|
||||
)
|
||||
target_link_libraries(${target}
|
||||
PRIVATE
|
||||
sdk_hosting
|
||||
)
|
||||
smtg_target_codesign(${target})
|
||||
smtg_target_setup_universal_binary(${target})
|
||||
if(APPLE AND NOT XCODE)
|
||||
find_library(COCOA_FRAMEWORK Cocoa)
|
||||
target_link_libraries(${target}
|
||||
PRIVATE
|
||||
${COCOA_FRAMEWORK}
|
||||
)
|
||||
endif(APPLE AND NOT XCODE)
|
||||
|
||||
if(MINGW)
|
||||
set_target_properties(${target} PROPERTIES LINK_FLAGS "-municode")
|
||||
endif(MINGW)
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "public.sdk/source/common/readfile.h"
|
||||
#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 "public.sdk/source/vst/utility/stringconvert.h"
|
||||
#include "base/source/fcommandline.h"
|
||||
#include "pluginterfaces/base/fplatform.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::optional<ModuleInfo::CompatibilityList> openAndParseCompatJSON (const std::string& path)
|
||||
{
|
||||
auto data = readFile (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 = readFile (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;
|
||||
#if SMTG_OS_WINDOWS
|
||||
auto tmp = Vst::StringConvert::convert (valueMap[optOutputPath]);
|
||||
auto outputFile = reinterpret_cast<const wchar_t*> (tmp.data ());
|
||||
#else
|
||||
auto outputFile = valueMap[optOutputPath];
|
||||
#endif
|
||||
auto ostream = new std::ofstream (outputFile);
|
||||
|
||||
if (ostream->is_open ())
|
||||
outputStream = ostream;
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot create output file: " << valueMap[optOutputPath] << '\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
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
#if SMTG_OS_WINDOWS
|
||||
//------------------------------------------------------------------------
|
||||
using Utf8String = std::string;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using Utf8Args = std::vector<Utf8String>;
|
||||
Utf8Args toUtf8Args (int argc, wchar_t* wargv[])
|
||||
{
|
||||
Utf8Args utf8Args;
|
||||
for (int i = 0; i < argc; i++)
|
||||
{
|
||||
auto str = reinterpret_cast<const Steinberg::Vst::TChar*> (wargv[i]);
|
||||
utf8Args.push_back (Steinberg::Vst::StringConvert::convert (str));
|
||||
}
|
||||
|
||||
return utf8Args;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
using Utf8ArgPtrs = std::vector<char*>;
|
||||
Utf8ArgPtrs toUtf8ArgPtrs (Utf8Args& utf8Args)
|
||||
{
|
||||
Utf8ArgPtrs utf8ArgPtrs;
|
||||
for (auto& el : utf8Args)
|
||||
{
|
||||
utf8ArgPtrs.push_back (el.data ());
|
||||
}
|
||||
|
||||
return utf8ArgPtrs;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int wmain (int argc, wchar_t* wargv[])
|
||||
{
|
||||
Utf8Args utf8Args = toUtf8Args (argc, wargv);
|
||||
Utf8ArgPtrs utf8ArgPtrs = toUtf8ArgPtrs (utf8Args);
|
||||
|
||||
char** argv = &(utf8ArgPtrs.at (0));
|
||||
return Steinberg::ModuleInfoTool::run (argc, argv);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
return Steinberg::ModuleInfoTool::run (argc, argv);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
#endif // SMTG_OS_WINDOWS
|
||||
Reference in New Issue
Block a user