Files
hot-step-cpp-ROCm/engine/vendor/vst3sdk/public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
T
2026-08-16 18:24:52 +07:00

518 lines
16 KiB
C++

//-----------------------------------------------------------------------------
// 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
//
//-----------------------------------------------------------------------------
// 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 "moduleinfoparser.h"
#include "jsoncxx.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <limits>
#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