summaryrefslogtreecommitdiffstats
path: root/src/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/state.c')
-rw-r--r--src/state.c184
1 files changed, 149 insertions, 35 deletions
diff --git a/src/state.c b/src/state.c
index aae3fab..f50557f 100644
--- a/src/state.c
+++ b/src/state.c
@@ -73,6 +73,14 @@ property_cmp(const void* a, const void* b)
return pa->key - pb->key;
}
+static int
+value_cmp(const void* a, const void* b)
+{
+ const PortValue* pa = (const PortValue*)a;
+ const PortValue* pb = (const PortValue*)b;
+ return strcmp(pa->symbol, pb->symbol);
+}
+
LILV_API
const LilvNode*
lilv_state_get_plugin_uri(const LilvState* state)
@@ -131,7 +139,7 @@ store_callback(void* handle,
state->props = realloc(state->props,
(++state->num_props) * sizeof(Property));
Property* const prop = &state->props[state->num_props - 1];
-
+
prop->value = malloc(size);
memcpy(prop->value, value, size);
@@ -183,10 +191,12 @@ lilv_state_new_from_instance(const LilvPlugin* plugin,
#endif // HAVE_LV2_STATE
qsort(state->props, state->num_props, sizeof(Property), property_cmp);
+ qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);
return state;
}
+#ifdef HAVE_LV2_STATE
static const void*
retrieve_callback(void* handle,
uint32_t key,
@@ -208,6 +218,7 @@ retrieve_callback(void* handle,
}
return NULL;
}
+#endif // HAVE_LV2_STATE
LILV_API
void
@@ -261,9 +272,6 @@ property_from_node(LilvWorld* world,
prop->type = map->map(map->handle, NS_ATOM "URID");
prop->size = sizeof(uint32_t);
break;
- case LILV_VALUE_BLANK:
- // TODO: Hmm...
- break;
case LILV_VALUE_STRING:
prop->size = strlen(str) + 1;
prop->value = malloc(prop->size);
@@ -288,8 +296,11 @@ property_from_node(LilvWorld* world,
prop->type = map->map(map->handle, NS_ATOM "Float");
prop->size = sizeof(float);
break;
+ case LILV_VALUE_BLANK:
+ case LILV_VALUE_BLOB:
+ // TODO: Blank nodes in state
+ break;
}
- prop->flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
}
static LilvState*
@@ -302,9 +313,9 @@ new_state_from_model(LilvWorld* world,
memset(state, '\0', sizeof(LilvState));
// Get the plugin URI this state applies to
- const SordQuad pat1 = {
+ const SordQuad upat = {
node, world->lv2_appliesTo_node, NULL, NULL };
- SordIter* i = sord_find(model, pat1);
+ SordIter* i = sord_find(model, upat);
if (i) {
state->plugin_uri = lilv_node_new_from_node(
world, lilv_match_object(i));
@@ -314,9 +325,19 @@ new_state_from_model(LilvWorld* world,
sord_node_get_string(node));
}
+ // Get the state label
+ const SordQuad lpat = {
+ node, world->rdfs_label_node, NULL, NULL };
+ i = sord_find(model, lpat);
+ if (i) {
+ state->label = lilv_strdup(
+ (const char*)sord_node_get_string(lilv_match_object(i)));
+ sord_iter_free(i);
+ }
+
// Get port values
- const SordQuad pat2 = { node, world->lv2_port_node, NULL, NULL };
- SordIter* ports = sord_find(model, pat2);
+ const SordQuad ppat = { node, world->lv2_port_node, NULL, NULL };
+ SordIter* ports = sord_find(model, ppat);
FOREACH_MATCH(ports) {
const SordNode* port = lilv_match_object(ports);
const SordNode* label = get_one(model, port, world->rdfs_label_node);
@@ -346,31 +367,53 @@ new_state_from_model(LilvWorld* world,
SordNode* statep = sord_new_uri(world->world, USTR(NS_STATE "state"));
const SordNode* state_node = get_one(model, node, statep);
if (state_node) {
- const SordQuad pat3 = { state_node, NULL, NULL };
- SordIter* props = sord_find(model, pat3);
+ const SordQuad spat = { state_node, NULL, NULL };
+ SordIter* props = sord_find(model, spat);
FOREACH_MATCH(props) {
- const SordNode* p = lilv_match_predicate(props);
- const SordNode* o = lilv_match_object(props);
- LilvNode* onode = lilv_node_new_from_node(world, o);
-
- Property prop = { NULL, 0, 0, 0, 0 };
+ const SordNode* p = lilv_match_predicate(props);
+ const SordNode* o = lilv_match_object(props);
+
+ uint32_t flags = 0;
+#ifdef HAVE_LV2_STATE
+ flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
+#endif
+ Property prop = { NULL, 0, 0, 0, flags };
prop.key = map->map(map->handle,
(const char*)sord_node_get_string(p));
- property_from_node(world, map, onode, &prop);
+
+ if (sord_node_get_type(o) == SORD_BLANK) {
+ const SordNode* type = get_one(model, o, world->rdf_a_node);
+ const SordNode* value = get_one(model, o, world->rdf_value_node);
+ if (type && value) {
+ size_t len;
+ const uint8_t* b64 = sord_node_get_string_counted(value, &len);
+ prop.value = serd_base64_decode(b64, len, &prop.size);
+ prop.type = map->map(map->handle,
+ (const char*)sord_node_get_string(type));
+ } else {
+ LILV_ERRORF("Unable to parse blank node property <%p>\n",
+ sord_node_get_string(p));
+ }
+ } else {
+ LilvNode* onode = lilv_node_new_from_node(world, o);
+ property_from_node(world, map, onode, &prop);
+ lilv_node_free(onode);
+ }
+
if (prop.value) {
state->props = realloc(
state->props, (++state->num_props) * sizeof(Property));
state->props[state->num_props - 1] = prop;
}
- lilv_node_free(onode);
}
sord_iter_free(props);
}
sord_node_free(world->world, statep);
qsort(state->props, state->num_props, sizeof(Property), property_cmp);
-
+ qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);
+
return state;
}
@@ -425,10 +468,14 @@ lilv_state_new_from_file(LilvWorld* world,
}
static LilvNode*
-node_from_property(LilvWorld* world, const char* type, void* value, size_t size)
+node_from_property(LilvWorld* world, LV2_URID_Unmap* unmap,
+ const char* type, void* value, size_t size)
{
if (!strcmp(type, NS_ATOM "String")) {
return lilv_new_string(world, (const char*)value);
+ } else if (!strcmp(type, NS_ATOM "URID")) {
+ const char* str = unmap->unmap(unmap->handle, *(uint32_t*)value);
+ return lilv_new_uri(world, str);
} else if (!strcmp(type, NS_ATOM "Int32")) {
if (size == sizeof(int32_t)) {
return lilv_new_int(world, *(int32_t*)value);
@@ -491,8 +538,8 @@ add_state_to_manifest(const LilvNode* plugin_uri,
{
FILE* fd = fopen((char*)manifest_path, "a");
if (!fd) {
- fprintf(stderr, "error: Failed to open %s (%s)\n",
- manifest_path, strerror(errno));
+ LILV_ERRORF("Failed to open %s (%s)\n",
+ manifest_path, strerror(errno));
return 4;
}
@@ -508,6 +555,7 @@ add_state_to_manifest(const LilvNode* plugin_uri,
SerdEnv* env = serd_env_new(NULL);
serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
serd_env_set_prefix_from_strings(env, USTR("pset"), USTR(NS_PSET));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
#if defined(HAVE_LOCKF) && defined(HAVE_FILENO)
@@ -607,15 +655,14 @@ lilv_state_save(LilvWorld* world,
const char* const home = getenv("HOME");
if (!home) {
- fprintf(stderr, "error: $HOME is undefined\n");
+ LILV_ERROR("$HOME is undefined\n");
return 2;
}
// Create ~/.lv2/
char* const lv2dir = lilv_strjoin(home, "/.lv2/", NULL);
if (mkdir(lv2dir, 0755) && errno != EEXIST) {
- fprintf(stderr, "error: Unable to create %s (%s)\n",
- lv2dir, strerror(errno));
+ LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
free(lv2dir);
return 3;
}
@@ -623,13 +670,12 @@ lilv_state_save(LilvWorld* world,
// Create ~/.lv2/presets.lv2/
char* const bundle = lilv_strjoin(lv2dir, "presets.lv2/", NULL);
if (mkdir(bundle, 0755) && errno != EEXIST) {
- fprintf(stderr, "error: Unable to create %s (%s)\n",
- lv2dir, strerror(errno));
+ LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
free(lv2dir);
free(bundle);
return 4;
}
-
+
char* const filename = pathify(state->label);
default_path = lilv_strjoin(bundle, filename, ".ttl", NULL);
default_manifest_path = lilv_strjoin(bundle, "manifest.ttl", NULL);
@@ -648,8 +694,7 @@ lilv_state_save(LilvWorld* world,
FILE* fd = fopen(path, "w");
if (!fd) {
- fprintf(stderr, "error: Failed to open %s (%s)\n",
- path, strerror(errno));
+ LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
free(default_path);
free(default_manifest_path);
return 4;
@@ -658,6 +703,7 @@ lilv_state_save(LilvWorld* world,
SerdEnv* env = serd_env_new(NULL);
serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
serd_env_set_prefix_from_strings(env, USTR("pset"), USTR(NS_PSET));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
serd_env_set_prefix_from_strings(env, USTR("state"), USTR(NS_STATE));
@@ -744,20 +790,50 @@ lilv_state_save(LilvWorld* world,
LILV_WARNF("Failed to unmap property key `%d'\n", prop->key);
} else if (!type) {
LILV_WARNF("Failed to unmap property type `%d'\n", prop->type);
- } else if (!(prop->flags & LV2_STATE_IS_PORTABLE)) {
+#ifdef HAVE_LV2_STATE
+ } else if (!(prop->flags & LV2_STATE_IS_PORTABLE)
+ || !(prop->flags & LV2_STATE_IS_POD)) {
LILV_WARNF("Unable to save non-portable property <%s>\n", type);
+#endif
} else {
+ SerdNode t;
+ p = serd_node_from_string(SERD_URI, USTR(key));
LilvNode* const node = node_from_property(
- world, type, prop->value, prop->size);
+ world, unmap, type, prop->value, prop->size);
if (node) {
- p = serd_node_from_string(SERD_URI, USTR(key));
- SerdNode t;
node_to_serd(node, &o, &t);
serd_writer_write_statement(
writer, SERD_ANON_CONT, NULL,
&state_node, &p, &o, &t, NULL);
} else {
- LILV_WARNF("Unable to save property type <%s>\n", type);
+ char name[16];
+ snprintf(name, sizeof(name), "b%u", i);
+ const SerdNode blank = serd_node_from_string(
+ SERD_BLANK, (const uint8_t*)name);
+
+ // <state> <key> [
+ serd_writer_write_statement(
+ writer, SERD_ANON_O_BEGIN, NULL,
+ &state_node, &p, &blank, NULL, NULL);
+
+ // rdf:type <type>
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type"));
+ o = serd_node_from_string(SERD_URI, USTR(type));
+ serd_writer_write_statement(
+ writer, SERD_ANON_CONT, NULL,
+ &blank, &p, &o, NULL, NULL);
+
+ // rdf:value "string"^^<xsd:base64Binary>
+ SerdNode blob = serd_node_new_blob(prop->value, prop->size, true);
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "value"));
+ t = serd_node_from_string(SERD_URI,
+ USTR(LILV_NS_XSD "base64Binary"));
+ serd_writer_write_statement(
+ writer, SERD_ANON_CONT, NULL,
+ &blank, &p, &blob, &t, NULL);
+ serd_node_free(&blob);
+
+ serd_writer_end_anon(writer, &blank); // ]
}
}
}
@@ -781,6 +857,44 @@ lilv_state_save(LilvWorld* world,
}
LILV_API
+bool
+lilv_state_equals(const LilvState* a, const LilvState* b)
+{
+ if (!lilv_node_equals(a->plugin_uri, b->plugin_uri)
+ || (a->label && !b->label)
+ || (b->label && !a->label)
+ || (a->label && b->label && strcmp(a->label, b->label))
+ || a->num_props != b->num_props
+ || a->num_values != b->num_values) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < a->num_values; ++i) {
+ PortValue* const av = &a->values[i];
+ PortValue* const bv = &b->values[i];
+ if (strcmp(av->symbol, bv->symbol)) {
+ return false;
+ } else if (!lilv_node_equals(av->value, bv->value)) {
+ return false;
+ }
+ }
+
+ for (uint32_t i = 0; i < a->num_props; ++i) {
+ Property* const ap = &a->props[i];
+ Property* const bp = &b->props[i];
+ if (ap->size != bp->size
+ || ap->key != bp->key
+ || ap->type != bp->type
+ || ap->flags != bp->flags
+ || memcmp(ap->value, bp->value, ap->size)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LILV_API
void
lilv_state_free(LilvState* state)
{