beginning of JSON schema + now vst3 plugin factory is good

This commit is contained in:
Stefano D'Angelo 2023-12-18 15:19:04 +01:00
parent 6cab6e8516
commit 33c743a3a2
7 changed files with 183 additions and 35 deletions

54
schema.json Normal file
View File

@ -0,0 +1,54 @@
{
"title": "Tibia data",
"type": "object",
"properties": {
"company": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Company name",
"pattern": "^[\u0000-\ud7ff\ue000-\uffff]+$"
},
"url": {
"type": "string",
"description": "Company website URL",
"pattern": "^[\u0000-\ud7ff\ue000-\uffff]+$"
},
"email": {
"type": "string",
"description": "Company email",
"pattern": "^[\u0000-\ud7ff\ue000-\uffff]+$"
}
},
"required" : [ "name", "url", "email" ]
},
"product": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Product name",
"pattern": "^[\u0000-\ud7ff\ue000-\uffff]+$"
},
"version": {
"type": "string",
"description": "Product version in MAJOR.MINOR.PATCH format (all numeric decimal)",
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
},
"buildVersion": {
"type": "string",
"description": "Product build version (numeric decimal)",
"pattern": "^(0|[1-9][0-9]*)$"
},
"bundleName": {
"type": "string",
"description": "Product bundle name base",
"pattern": "^[_0-9a-zA-Z]+$"
}
},
"required": [ "name", "version", "buildVersion", "bundleName" ]
}
},
"required": [ "company", "product" ]
}

View File

@ -3,10 +3,10 @@
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>{{=it.plugin.bundleName}}</string>
<string>{{=it.product.bundleName}}</string>
<key>CFBundleExecutable</key>
<string>{{=it.plugin.bundleName}}</string>
<string>{{=it.product.bundleName}}</string>
</dict>
</plist>

View File

@ -1,2 +1,2 @@
BUNDLE_NAME := {{=it.plugin.bundleName}}
BUNDLE_NAME := {{=it.product.bundleName}}
VST3_C_API_DIR := ../../vst3_c_api

View File

@ -1,9 +1,17 @@
#define DATA_COMPANY_NAME "{{=it.company.name}}"
#define DATA_COMPANY_URL "{{=it.company.url}}"
#define DATA_COMPANY_EMAIL "{{=it.company.email}}"
#define DATA_COMPANY_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.company.name, 63)}}"
#define DATA_COMPANY_URL "{{=it.tibia.CGetUTF8StringLiteral(it.company.url, 255)}}"
#define DATA_COMPANY_EMAIL "{{=it.tibia.CGetUTF8StringLiteral(it.company.email, 127)}}"
#define DATA_PLUGIN_NAME "{{=it.plugin.name}}"
#define DATA_PLUGIN_VERSION "{{=it.plugin.version}}"
#define DATA_PRODUCT_NAME "{{=it.tibia.CGetUTF8StringLiteral(it.product.name, 63)}}"
#define DATA_PRODUCT_VERSION "{{=(it.product.version + '.' + it.product.buildVersion).substring(0, 63)}}"
static Steinberg_char16 dataProductNameW[64] = { {{~Array.from(it.product.name).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
static Steinberg_char16 dataProductVersionW[64] = { {{~Array.from(it.product.version + "." + it.product.buildVersion).slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_SDK_VERSION "VST 3.7.4 | Tibia"
static Steinberg_char16 dataVST3SDKVersionW[64] = { {{~Array.from("VST 3.7.4 | Tibia") :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
static Steinberg_char16 dataVST3ControllerNameW[64] = { {{~Array.from(it.product.name + " Controller").slice(0, 63) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 };
#define DATA_VST3_PLUGIN_CID_1 0x{{=it.vst3.plugin.cid.substring(0, 8)}}
#define DATA_VST3_PLUGIN_CID_2 0x{{=it.vst3.plugin.cid.substring(8, 16)}}
@ -17,14 +25,14 @@
#define DATA_VST3_SUBCATEGORY "{{=it.vst3.subCategory}}"
#define DATA_PLUGIN_BUSES_AUDIO_INPUT_N {{=it.plugin.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N {{=it.plugin.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define DATA_PLUGIN_BUSES_EVENT_INPUT_N {{=it.plugin.buses.filter(x => x.type == "event" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_EVENT_OUTPUT_N {{=it.plugin.buses.filter(x => x.type == "event" && x.direction == "output").length}}
#define DATA_PLUGIN_BUSES_AUDIO_INPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N {{=it.product.buses.filter(x => x.type == "audio" && x.direction == "output").length}}
#define DATA_PLUGIN_BUSES_EVENT_INPUT_N {{=it.product.buses.filter(x => x.type == "event" && x.direction == "input").length}}
#define DATA_PLUGIN_BUSES_EVENT_OUTPUT_N {{=it.product.buses.filter(x => x.type == "event" && x.direction == "output").length}}
#if DATA_PLUGIN_BUSES_AUDIO_INPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_AUDIO_INPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "audio" && x.direction == "input") :b}}
{{~it.product.buses.filter(x => x.type == "audio" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
@ -39,7 +47,7 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_AUDIO_IN
#if DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PLUGIN_BUSES_AUDIO_OUTPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "audio" && x.direction == "output") :b}}
{{~it.product.buses.filter(x => x.type == "audio" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kAudio,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
@ -54,7 +62,7 @@ static struct Steinberg_Vst_BusInfo busInfoAudioOutput[DATA_PLUGIN_BUSES_AUDIO_O
#if DATA_PLUGIN_BUSES_EVENT_INPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoEventInput[DATA_PLUGIN_BUSES_EVENT_INPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "event" && x.direction == "input") :b}}
{{~it.product.buses.filter(x => x.type == "event" && x.direction == "input") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kInput,
@ -69,7 +77,7 @@ static struct Steinberg_Vst_BusInfo busInfoEventInput[DATA_PLUGIN_BUSES_EVENT_IN
#if DATA_PLUGIN_BUSES_EVENT_OUTPUT_N > 0
static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_EVENT_OUTPUT_N] = {
{{~it.plugin.buses.filter(x => x.type == "event" && x.direction == "output") :b}}
{{~it.product.buses.filter(x => x.type == "event" && x.direction == "output") :b}}
{
/* .mediaType = */ Steinberg_Vst_MediaTypes_kEvent,
/* .direction = */ Steinberg_Vst_BusDirections_kOutput,
@ -82,11 +90,11 @@ static struct Steinberg_Vst_BusInfo busInfoAudioInput[DATA_PLUGIN_BUSES_EVENT_OU
};
#endif
#define DATA_PLUGIN_PARAMETERS_N {{=it.plugin.parameters.length}}
#define DATA_PLUGIN_PARAMETERS_N {{=it.product.parameters.length}}
#if DATA_PLUGIN_PARAMETERS_N > 0
static struct Steinberg_Vst_ParameterInfo parameterInfo[DATA_PLUGIN_PARAMETERS_N] = {
{{~it.plugin.parameters :p:i}}
{{~it.product.parameters :p:i}}
{
/* .id = */ {{=i}},
/* .title = */ { {{~Array.from(p.name) :c}}0x{{=c.charCodeAt(0).toString(16)}}, {{~}}0 },

View File

@ -9,6 +9,7 @@
// https://github.com/rubberduck-vba/Rubberduck/wiki/COM-in-plain-C
// https://devblogs.microsoft.com/oldnewthing/20040205-00/?p=40733
#ifdef NDEBUG
# define TRACE(...) /* do nothing */
#else
@ -683,8 +684,8 @@ static Steinberg_tresult factoryQueryInterface(void *thisInterface, const Steinb
TRACE("factory queryInterface\n");
if (memcmp(iid, Steinberg_FUnknown_iid, sizeof(Steinberg_TUID))
&& memcmp(iid, Steinberg_IPluginFactory_iid, sizeof(Steinberg_TUID))
&& memcmp(iid, Steinberg_IPluginFactory2_iid, sizeof(Steinberg_TUID))) {
//&& memcmp(iid, Steinberg_IPluginFactory3_iid, sizeof(Steinberg_TUID))) {
&& memcmp(iid, Steinberg_IPluginFactory2_iid, sizeof(Steinberg_TUID))
&& memcmp(iid, Steinberg_IPluginFactory3_iid, sizeof(Steinberg_TUID))) {
TRACE(" not supported\n");
*obj = NULL;
return Steinberg_kNoInterface;
@ -725,14 +726,14 @@ static Steinberg_tresult factoryGetClassInfo(void *thisInterface, Steinberg_int3
memcpy(info->cid, pluginCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Audio Module Class");
strcpy(info->name, DATA_PLUGIN_NAME);
strcpy(info->name, DATA_PRODUCT_NAME);
break;
case 1:
TRACE(" class 1\n");
memcpy(info->cid, controllerCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Component Controller Class");
strcpy(info->name, DATA_PLUGIN_NAME " Controller");
strcpy(info->name, DATA_PRODUCT_NAME " Controller");
break;
default:
return Steinberg_kInvalidArgument;
@ -804,24 +805,24 @@ static Steinberg_tresult factoryGetClassInfo2(void* thisInterface, Steinberg_int
memcpy(info->cid, pluginCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Audio Module Class");
strcpy(info->name, DATA_PLUGIN_NAME);
strcpy(info->name, DATA_PRODUCT_NAME);
info->classFlags = Steinberg_Vst_ComponentFlags_kDistributable;
strcpy(info->subCategories, DATA_VST3_SUBCATEGORY);
*info->vendor = '\0';
strcpy(info->version, DATA_PLUGIN_VERSION);
strcpy(info->sdkVersion, "VST 3.7.4 | Tibia");
strcpy(info->version, DATA_PRODUCT_VERSION);
strcpy(info->sdkVersion, "VST " DATA_VST3_SDK_VERSION " | Tibia");
break;
case 1:
TRACE(" class 1\n");
memcpy(info->cid, controllerCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Component Controller Class");
strcpy(info->name, DATA_PLUGIN_NAME " Controller");
strcpy(info->name, DATA_PRODUCT_NAME " Controller");
info->classFlags = 0;
*info->subCategories = '\0';
*info->vendor = '\0';
strcpy(info->version, DATA_PLUGIN_VERSION);
strcpy(info->sdkVersion, "VST 3.7.4 | Tibia");
strcpy(info->version, DATA_PRODUCT_VERSION);
strcpy(info->sdkVersion, "VST " DATA_VST3_SDK_VERSION " | Tibia");
break;
default:
return Steinberg_kInvalidArgument;
@ -832,6 +833,35 @@ static Steinberg_tresult factoryGetClassInfo2(void* thisInterface, Steinberg_int
static Steinberg_tresult factoryGetClassInfoUnicode(void* thisInterface, Steinberg_int32 index, struct Steinberg_PClassInfoW* info) {
TRACE("getClassInfo unicode\n");
switch (index) {
case 0:
TRACE(" class 0\n");
memcpy(info->cid, pluginCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Audio Module Class");
memcpy(info->name, dataProductNameW, 64 * sizeof(Steinberg_char16));
info->classFlags = Steinberg_Vst_ComponentFlags_kDistributable;
strcpy(info->subCategories, DATA_VST3_SUBCATEGORY);
*info->vendor = '\0';
memcpy(info->version, dataProductVersionW, 64 * sizeof(Steinberg_char16));
memcpy(info->sdkVersion, dataVST3SDKVersionW, 64 * sizeof(Steinberg_char16));
break;
case 1:
TRACE(" class 1\n");
memcpy(info->cid, controllerCID, sizeof(Steinberg_TUID));
info->cardinality = Steinberg_PClassInfo_ClassCardinality_kManyInstances;
strcpy(info->category, "Component Controller Class");
memcpy(info->name, dataVST3ControllerNameW, 64 * sizeof(Steinberg_char16));
info->classFlags = 0;
*info->subCategories = '\0';
*info->vendor = '\0';
memcpy(info->version, dataProductVersionW, 64 * sizeof(Steinberg_char16));
memcpy(info->sdkVersion, dataVST3SDKVersionW, 64 * sizeof(Steinberg_char16));
break;
default:
return Steinberg_kInvalidArgument;
break;
}
return Steinberg_kResultOk;
}
@ -840,7 +870,7 @@ static Steinberg_tresult factorySetHostContext(void* thisInterface, struct Stein
return Steinberg_kResultOk;
}
static Steinberg_IPluginFactory2Vtbl factoryVtbl = {
static Steinberg_IPluginFactory3Vtbl factoryVtbl = {
/* FUnknown */
/* .queryInterface = */ factoryQueryInterface,
/* .addRef = */ factoryAddRef,
@ -856,10 +886,10 @@ static Steinberg_IPluginFactory2Vtbl factoryVtbl = {
/* .getClassInfo2 = */ factoryGetClassInfo2,
/* IPluginFactory3 */
/* .getClassInfoUnicode = */ //factoryGetClassInfoUnicode,
/* .setHostContext = */ //factorySetHostContext
/* .getClassInfoUnicode = */ factoryGetClassInfoUnicode,
/* .setHostContext = */ factorySetHostContext
};
static Steinberg_IPluginFactory2 factory = { &factoryVtbl };
static Steinberg_IPluginFactory3 factory = { &factoryVtbl };
Steinberg_IPluginFactory * GetPluginFactory() {
return (Steinberg_IPluginFactory *)&factory;

View File

@ -1,7 +1,8 @@
{
"plugin": {
"name": "Tibia test plugin",
"version": "1.0.0.1",
"product": {
"name": "Tibia test product",
"version": "1.0.0",
"buildVersion": "1",
"bundleName": "tibia_test",
"buses": [
{

55
tibia
View File

@ -12,6 +12,11 @@ var jsonFiles = process.argv[2].split(",");
var template = process.argv[3];
var outputDir = process.argv[4];
var schema = JSON.parse(fs.readFileSync(__dirname + path.sep + "schema.json", { encoding: "utf-8" }));
var Ajv = require("ajv");
var ajv = new Ajv();
var ajvValidate = ajv.compile(schema);
var data = {};
for (var i = 0; i < jsonFiles.length; i++) {
var d = JSON.parse(fs.readFileSync(jsonFiles[i], { encoding: "utf-8" }));
@ -19,6 +24,12 @@ for (var i = 0; i < jsonFiles.length; i++) {
data[k] = d[k];
}
var ajvValid = ajvValidate(data);
if (!ajvValid) {
console.log(ajvValidate.errors);
process.exit(1);
}
var doT = require("dot");
doT.templateSettings.strip = false;
@ -63,4 +74,48 @@ var api = {
}
};
data.tibia = {
CGetUTF8StringLiteral: function (string, maxLength) {
if (!maxLength)
maxLength = Infinity;
var v = (new TextEncoder()).encode(string);
var s = "";
for (var i = 0; i < maxLength && i < v.length; ) {
if (!(v[i] & 0x80)) {
if (v[i] < 0x20 || v[i] == 0x7f) {
s += "\\" + v[i].toString(8).padStart(3, "0");
} else if (v[i] == 0x22)
s += "\\\"";
else if (v[i] == 0x5c)
s += "\\\\";
else
s += String.fromCharCode(v[i]);
i++;
} else if ((v[i] & 0xe0) == 0xc0) {
if (maxLength - i < 2)
break;
for (var j = 0; j < 2; j++) {
s += "\\" + v[i].toString(8).padStart(3, "0");
i++;
}
} else if ((v[i] & 0xf0) == 0xe0) {
if (maxLength - i < 3)
break;
for (var j = 0; j < 3; j++) {
s += "\\" + v[i].toString(8).padStart(3, "0");
i++;
}
} else if ((v[i] & 0xf8) == 0xf0) {
if (maxLength - i < 4)
break;
for (var j = 0; j < 4; j++) {
s += "\\" + v[i].toString(8).padStart(3, "0");
i++;
}
}
}
return s;
}
};
require(path.resolve(template + path.sep + "tibia-index.js"))(data, api);