From ed78bbe5ba12be1f9bcc736f14c51da6b4f639f3 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Wed, 8 Feb 2012 04:56:24 +0000 Subject: Rearrange tree so top level can be used as an include path for standard style LV2 includes. --- lv2/lv2plug.in/ns/ext/osc/ext.pc.in | 1 + lv2/lv2plug.in/ns/ext/osc/lv2_osc.c | 238 ++++++++++++++++++++++++++++++ lv2/lv2plug.in/ns/ext/osc/lv2_osc_print.c | 66 +++++++++ lv2/lv2plug.in/ns/ext/osc/lv2_osc_test.c | 55 +++++++ lv2/lv2plug.in/ns/ext/osc/manifest.ttl | 9 ++ lv2/lv2plug.in/ns/ext/osc/osc-print.h | 42 ++++++ lv2/lv2plug.in/ns/ext/osc/osc.h | 119 +++++++++++++++ lv2/lv2plug.in/ns/ext/osc/osc.ttl | 39 +++++ lv2/lv2plug.in/ns/ext/osc/waf | 1 + lv2/lv2plug.in/ns/ext/osc/wscript | 1 + 10 files changed, 571 insertions(+) create mode 120000 lv2/lv2plug.in/ns/ext/osc/ext.pc.in create mode 100644 lv2/lv2plug.in/ns/ext/osc/lv2_osc.c create mode 100644 lv2/lv2plug.in/ns/ext/osc/lv2_osc_print.c create mode 100644 lv2/lv2plug.in/ns/ext/osc/lv2_osc_test.c create mode 100644 lv2/lv2plug.in/ns/ext/osc/manifest.ttl create mode 100644 lv2/lv2plug.in/ns/ext/osc/osc-print.h create mode 100644 lv2/lv2plug.in/ns/ext/osc/osc.h create mode 100644 lv2/lv2plug.in/ns/ext/osc/osc.ttl create mode 120000 lv2/lv2plug.in/ns/ext/osc/waf create mode 120000 lv2/lv2plug.in/ns/ext/osc/wscript (limited to 'lv2/lv2plug.in/ns/ext/osc') diff --git a/lv2/lv2plug.in/ns/ext/osc/ext.pc.in b/lv2/lv2plug.in/ns/ext/osc/ext.pc.in new file mode 120000 index 0000000..03dd044 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/ext.pc.in @@ -0,0 +1 @@ +../../../../../ext.pc.in \ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/osc/lv2_osc.c b/lv2/lv2plug.in/ns/ext/osc/lv2_osc.c new file mode 100644 index 0000000..afea2c9 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/lv2_osc.c @@ -0,0 +1,238 @@ +/* LV2 OSC Messages Extension + * Copyright (C) 2007-2009 David Robillard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "lv2_osc.h" +#include "lv2_osc_print.h" + +/*#ifndef BIG_ENDIAN + #ifndef LITTLE_ENDIAN + #warning This code requires BIG_ENDIAN or LITTLE_ENDIAN to be defined + #warning Assuming little endian. THIS MAY BREAK HORRIBLY! + #endif +#endif*/ + +#define lv2_osc_swap32(x) \ +({ \ + uint32_t __x = (x); \ + ((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ +}) + +#define lv2_osc_swap64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + + +/** Pad a size to a multiple of 32 bits */ +inline static uint32_t +lv2_osc_pad_size(uint32_t size) +{ + return size + 3 - ((size-1) % 4); +} + + +inline static uint32_t +lv2_osc_string_size(const char *s) +{ + return lv2_osc_pad_size((uint32_t)strlen(s) + 1); +} + + +static inline uint32_t +lv2_osc_blob_size(const void* blob) +{ + return sizeof(uint32_t) + lv2_osc_pad_size(*((uint32_t*)blob)); +} + + +uint32_t +lv2_osc_arg_size(char type, const LV2_OSC_Argument* arg) +{ + switch (type) { + case 'c': + case 'i': + case 'f': + case 'S': // Symbol (URI-mapped integer) + return 4; + + case 'h': + case 'd': + return 8; + + case 's': + return lv2_osc_string_size(&arg->s); + + case 'b': + return lv2_osc_blob_size(&arg->b); + + default: + fprintf(stderr, "Warning: unknown OSC type '%c'.", type); + return 0; + } +} + + +void +lv2_osc_argument_swap_byte_order(char type, LV2_OSC_Argument* arg) +{ + switch (type) { + case 'i': + case 'f': + case 'b': + case 'c': + *(int32_t*)arg = lv2_osc_swap32(*(int32_t*)arg); + break; + + case 'h': + case 'd': + *(int64_t*)arg = lv2_osc_swap64(*(int64_t*)arg); + break; + } +} + + +/** Convert a message from network byte order to host byte order. */ +void +lv2_osc_message_swap_byte_order(LV2_OSC_Event* msg) +{ + const char* const types = lv2_osc_get_types(msg); + + for (uint32_t i=0; i < msg->argument_count; ++i) + lv2_osc_argument_swap_byte_order(types[i], lv2_osc_get_argument(msg, i)); +} + + +/** Not realtime safe, returned value must be free()'d by caller. */ +LV2_OSC_Event* +lv2_osc_message_new(const char* path, const char* types, ...) +{ + /* FIXME: path only */ + + LV2_OSC_Event* result = malloc(sizeof(LV2_OSC_Event) + + 4 + lv2_osc_string_size(path)); + + const uint32_t path_size = lv2_osc_string_size(path); + result->data_size = path_size + 4; // 4 for types + result->argument_count = 0; + result->types_offset = lv2_osc_string_size(path) + 1; + (&result->data)[result->types_offset - 1] = ','; + (&result->data)[result->types_offset] = '\0'; + + memcpy(&result->data, path, strlen(path) + 1); + + return result; +} + + +/** Create a new LV2_OSC_Event from a raw OSC message. + * + * If \a out_buf is NULL, new memory will be allocated. Otherwise the returned + * value will be equal to buf, unless there is insufficient space in which + * case NULL is returned. + */ +LV2_OSC_Event* +lv2_osc_message_from_raw(uint32_t out_buf_size, + void* out_buf, + uint32_t raw_msg_size, + void* raw_msg) +{ + const uint32_t message_header_size = (sizeof(uint32_t) * 4); + + const uint32_t path_size = lv2_osc_string_size((char*)raw_msg); + const uint32_t types_len = strlen((char*)(raw_msg + path_size + 1)); + uint32_t index_size = types_len * sizeof(uint32_t); + + if (out_buf == NULL) { + out_buf_size = message_header_size + index_size + raw_msg_size; + out_buf = malloc((size_t)out_buf_size); + } else if (out_buf && out_buf_size < message_header_size + raw_msg_size) { + return NULL; + } + + LV2_OSC_Event* write_loc = (LV2_OSC_Event*)(out_buf); + write_loc->argument_count = types_len; + write_loc->data_size = index_size + raw_msg_size; + + // Copy raw message + memcpy(&write_loc->data + index_size, raw_msg, raw_msg_size); + + write_loc->types_offset = index_size + path_size + 1; + const char* const types = lv2_osc_get_types(write_loc); + + // Calculate/Write index + uint32_t args_base_offset = write_loc->types_offset + lv2_osc_string_size(types) - 1; + uint32_t arg_offset = 0; + + for (uint32_t i=0; i < write_loc->argument_count; ++i) { + ((uint32_t*)&write_loc->data)[i] = args_base_offset + arg_offset; + const LV2_OSC_Argument* const arg = (LV2_OSC_Argument*)(&write_loc->data + args_base_offset + arg_offset); + // Special case because size is still big-endian +#ifndef BIG_ENDIAN + if (types[i] == 'b') // special case because size is still big-endian + arg_offset += lv2_osc_swap32(*((int32_t*)arg)); + else +#endif + arg_offset += lv2_osc_arg_size(types[i], arg); + } + + /*printf("Index:\n"); + for (uint32_t i=0; i < write_loc->argument_count; ++i) { + printf("%u ", ((uint32_t*)&write_loc->data)[i]); + } + printf("\n"); + + printf("Data:\n"); + for (uint32_t i=0; i < (write_loc->argument_count * 4) + size; ++i) { + printf("%3u", i % 10); + } + printf("\n"); + for (uint32_t i=0; i < (write_loc->argument_count * 4) + size; ++i) { + char c = *(((char*)&write_loc->data) + i); + if (c >= 32 && c <= 126) + printf("%3c", c); + else + printf("%3d", (int)c); + } + printf("\n");*/ + + // Swap to host byte order if necessary +#ifndef BIG_ENDIAN + lv2_osc_message_swap_byte_order(write_loc); +#endif + + printf("Created message:\n"); + lv2_osc_message_print(write_loc); + + return write_loc; +} diff --git a/lv2/lv2plug.in/ns/ext/osc/lv2_osc_print.c b/lv2/lv2plug.in/ns/ext/osc/lv2_osc_print.c new file mode 100644 index 0000000..5282d46 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/lv2_osc_print.c @@ -0,0 +1,66 @@ +/* LV2 OSC Messages Extension - Pretty printing methods + * Copyright (C) 2007-2009 David Robillard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "lv2_osc_print.h" + +void +lv2_osc_argument_print(char type, const LV2_OSC_Argument* arg) +{ + int32_t blob_size; + + switch (type) { + case 'c': + printf("%c", arg->c); break; + case 'i': + printf("%d", arg->i); break; + case 'f': + printf("%f", arg->f); break; + case 'h': + printf("%ld", arg->h); break; + case 'd': + printf("%f", arg->d); break; + case 's': + printf("\"%s\"", &arg->s); break; + /*case 'S': + printf("\"%s\"", &arg->S); break;*/ + case 'b': + blob_size = *((int32_t*)arg); + printf("{ "); + for (int32_t i=0; i < blob_size; ++i) + printf("%X, ", (&arg->b)[i+4]); + printf(" }"); + break; + default: + printf("?"); + } +} + + +void +lv2_osc_print(const LV2_OSC_Event* msg) +{ + const char* const types = lv2_osc_get_types(msg); + + printf("%s (%s) ", lv2_osc_get_path(msg), types); + for (uint32_t i=0; i < msg->argument_count; ++i) { + lv2_osc_argument_print(types[i], lv2_osc_get_argument(msg, i)); + printf(" "); + } + printf("\n"); +} + diff --git a/lv2/lv2plug.in/ns/ext/osc/lv2_osc_test.c b/lv2/lv2plug.in/ns/ext/osc/lv2_osc_test.c new file mode 100644 index 0000000..3f76d41 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/lv2_osc_test.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include "lv2_osc.h" +#include "lv2_osc_print.h" + +int +main() +{ + lo_message lo_msg = lo_message_new(); + //lo_message_add_symbol(lo_msg, "a_sym"); + lo_message_add_string(lo_msg, "Hello World"); + lo_message_add_char(lo_msg, 'a'); + lo_message_add_int32(lo_msg, 1234); + lo_message_add_float(lo_msg, 0.1234); + lo_message_add_int64(lo_msg, 5678); + lo_message_add_double(lo_msg, 0.5678); + + + /*unsigned char blob_data[] = { 0,1,2,3,4,5,6,7,8,9 }; + lo_blob blob = lo_blob_new(10, blob_data); + lo_message_add_blob(lo_msg, blob);*/ + + /* Leaks like a sieve */ + + size_t raw_msg_size = 0; + void* raw_msg = lo_message_serialise(lo_msg, "/foo/bar", NULL, &raw_msg_size); + + LV2Message* msg = lv2_osc_message_from_raw(0.0, 0, NULL, raw_msg_size, raw_msg); + assert(msg); + + LV2OSCBuffer* buf = lv2_osc_buffer_new(1024); + + int ret = lv2_osc_buffer_append_message(buf, msg); + if (ret) + fprintf(stderr, "Message append failed: %s", strerror(ret)); + + lo_message lo_msg_2 = lo_message_new(); + lo_message_add_string(lo_msg_2, "Another message"); + + raw_msg = lo_message_serialise(lo_msg_2, "/baz", NULL, &raw_msg_size); + + msg = lv2_osc_message_from_raw(0.0, 0, NULL, raw_msg_size, raw_msg); + assert(msg); + + ret = lv2_osc_buffer_append_message(buf, msg); + if (ret) + fprintf(stderr, "Message append failed: %s", strerror(ret)); + + printf("\nBuffer contents:\n\n"); + lv2_osc_buffer_print(buf); + + return 0; +} diff --git a/lv2/lv2plug.in/ns/ext/osc/manifest.ttl b/lv2/lv2plug.in/ns/ext/osc/manifest.ttl new file mode 100644 index 0000000..7209b05 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/manifest.ttl @@ -0,0 +1,9 @@ +@prefix lv2: . +@prefix rdfs: . + + + a lv2:Specification ; + lv2:minorVersion 0 ; + lv2:microVersion 1 ; + rdfs:seeAlso . + diff --git a/lv2/lv2plug.in/ns/ext/osc/osc-print.h b/lv2/lv2plug.in/ns/ext/osc/osc-print.h new file mode 100644 index 0000000..ceebbf7 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/osc-print.h @@ -0,0 +1,42 @@ +/* LV2 OSC Messages Extension - Pretty printing methods + * Copyright (C) 2007-2009 David Robillard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** @file + * Helper functions for printing LV2 OSC messages as defined by the + * LV2 OSC extension . + */ + +#ifndef LV2_OSC_PRINT_H +#define LV2_OSC_PRINT_H + +#include "lv2/lv2plug.in/ns/ext/osc/osc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +lv2_osc_argument_print(char type, const LV2_OSC_Argument* arg); + +void +lv2_osc_message_print(const LV2_OSC_Event* msg); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_OSC_PRINT_H */ diff --git a/lv2/lv2plug.in/ns/ext/osc/osc.h b/lv2/lv2plug.in/ns/ext/osc/osc.h new file mode 100644 index 0000000..05e39cc --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/osc.h @@ -0,0 +1,119 @@ +/* LV2 OSC Messages Extension + * Copyright (C) 2007-2009 David Robillard + * + * This header is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This header is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LV2_OSC_H +#define LV2_OSC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @file + * C header for the LV2 OSC extension . + * This extension defines a format for (raw) OSC messages/events. + */ + + +/** Argument (in a message). + * + * The name of the element in this union directly corresponds to the OSC + * type tag character in LV2_Event::types. + */ +typedef union { + /* Standard OSC types */ + int32_t i; /**< 32 bit signed integer */ + float f; /**< 32 bit IEEE-754 floating point number ("float") */ + char s; /**< Standard C, NULL terminated string */ + uint8_t b; /**< Blob (int32 size then size bytes padded to 32 bits) */ + + /* "Nonstandard" OSC types (defined in the OSC standard) */ + int64_t h; /* 64 bit signed integer */ + // t /* OSC-timetag */ + double d; /* 64 bit IEEE 754 floating point number ("double") */ + // S /* Symbol, represented as an OSC-string */ + int32_t c; /* Character, represented as a 32-bit integer */ + // r /* 32 bit RGBA color */ + // m /* 4 byte MIDI message. Bytes from MSB to LSB are: port id, status byte, data1, data2 */ + // T /* True. No bytes are allocated in the argument data. */ + // F /* False. No bytes are allocated in the argument data. */ + // N /* Nil. No bytes are allocated in the argument data. */ + // I /* Infinitum. No bytes are allocated in the argument data. */ + // [ /* The beginning of an array. */ + // ] /* The end of an array. */ +} LV2_OSC_Argument; + + + +/** Message. + * + * This is an OSC message at heart, but with some additional cache information + * to allow fast access to parameters. This is the payload of an LV2_Event, + * time stamp and size (being generic) are in the containing header. + */ +typedef struct { + uint32_t data_size; /**< Total size of data, in bytes */ + uint32_t argument_count; /**< Number of arguments in data */ + uint32_t types_offset; /**< Offset of types string in data */ + + /** Take the address of this member to get a pointer to the remaining data. + * + * Contents are an argument index: + * uint32_t argument_index[argument_count] + * + * followed by a standard OSC message: + * char path[path_length] (padded OSC string) + * char types[argument_count] (padded OSC string) + * void data[data_size] + */ + char data; + +} LV2_OSC_Event; + +LV2_OSC_Event* lv2_osc_event_new(const char* path, const char* types, ...); + +LV2_OSC_Event* lv2_osc_event_from_raw(uint32_t out_buf_size, void* out_buf, + uint32_t raw_msg_size, void* raw_msg); + +static inline uint32_t lv2_osc_get_osc_message_size(const LV2_OSC_Event* msg) + { return (msg->argument_count * sizeof(char) + 1) + msg->data_size; } + +static inline const void* lv2_osc_get_osc_message(const LV2_OSC_Event* msg) + { return (const void*)(&msg->data + (sizeof(uint32_t) * msg->argument_count)); } + +static inline const char* lv2_osc_get_path(const LV2_OSC_Event* msg) + { return (const char*)(&msg->data + (sizeof(uint32_t) * msg->argument_count)); } + +static inline const char* lv2_osc_get_types(const LV2_OSC_Event* msg) + { return (const char*)(&msg->data + msg->types_offset); } + +static inline LV2_OSC_Argument* lv2_osc_get_argument(const LV2_OSC_Event* msg, uint32_t i) + { return (LV2_OSC_Argument*)(&msg->data + ((uint32_t*)&msg->data)[i]); } + +/* +int lv2_osc_buffer_append_message(LV2_Event_Buffer* buf, LV2_Event* msg); +int lv2_osc_buffer_append(LV2_Event_Buffer* buf, double time, const char* path, const char* types, ...); +void lv2_osc_buffer_compact(LV2_Event_Buffer* buf); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_OSC_H */ diff --git a/lv2/lv2plug.in/ns/ext/osc/osc.ttl b/lv2/lv2plug.in/ns/ext/osc/osc.ttl new file mode 100644 index 0000000..c83a69d --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/osc.ttl @@ -0,0 +1,39 @@ +# LV2 OSC Messages Extension +# Copyright (C) 2007 David Robillard +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +@prefix osc: . +@prefix lv2: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix doap: . +@prefix foaf: . + + a lv2:Specification ; + doap:license ; + doap:name "LV2 OSC Events" ; + doap:shortdesc "A data type for raw OSC." ; + doap:maintainer [ + a foaf:Person ; + foaf:name "David Robillard" ; + foaf:homepage ; + rdfs:seeAlso + ] . diff --git a/lv2/lv2plug.in/ns/ext/osc/waf b/lv2/lv2plug.in/ns/ext/osc/waf new file mode 120000 index 0000000..5235032 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/waf @@ -0,0 +1 @@ +../../../../../waf \ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/osc/wscript b/lv2/lv2plug.in/ns/ext/osc/wscript new file mode 120000 index 0000000..7e2c01b --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/osc/wscript @@ -0,0 +1 @@ +../../../../../ext.wscript \ No newline at end of file -- cgit v1.2.1