1 /*
2  * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #ifndef LV2_OSC_WRITER_H
19 #define LV2_OSC_WRITER_H
20 
21 #include <stdbool.h>
22 #include <string.h>
23 
24 #include <osc.lv2/osc.h>
25 #include <osc.lv2/util.h>
26 #include <osc.lv2/endian.h>
27 
28 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 #undef LV2_ATOM_TUPLE_FOREACH // there is a bug in LV2 1.10.0
35 #define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \
36 	for (LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \
37 	     !lv2_atom_tuple_is_end(LV2_ATOM_BODY(tuple), (tuple)->atom.size, (iter)); \
38 	     (iter) = lv2_atom_tuple_next(iter))
39 
40 typedef struct _LV2_OSC_Writer LV2_OSC_Writer;
41 typedef struct _LV2_OSC_Writer_Frame LV2_OSC_Writer_Frame;
42 
43 struct _LV2_OSC_Writer {
44 	uint8_t *buf;
45 	uint8_t *ptr;
46 	const uint8_t *end;
47 };
48 
49 struct _LV2_OSC_Writer_Frame {
50 	uint8_t *ref;
51 };
52 
53 static inline void
lv2_osc_writer_initialize(LV2_OSC_Writer * writer,uint8_t * buf,size_t size)54 lv2_osc_writer_initialize(LV2_OSC_Writer *writer, uint8_t *buf, size_t size)
55 {
56 	writer->buf = buf;
57 	writer->ptr = buf;
58 	writer->end = buf + size;
59 }
60 
61 static inline size_t
lv2_osc_writer_get_size(LV2_OSC_Writer * writer)62 lv2_osc_writer_get_size(LV2_OSC_Writer *writer)
63 {
64 	if(writer->ptr > writer->buf)
65 		return writer->ptr - writer->buf;
66 
67 	return 0;
68 }
69 
70 static inline uint8_t *
lv2_osc_writer_finalize(LV2_OSC_Writer * writer,size_t * size)71 lv2_osc_writer_finalize(LV2_OSC_Writer *writer, size_t *size)
72 {
73 	*size = lv2_osc_writer_get_size(writer);
74 
75 	if(*size)
76 		return writer->buf;
77 
78 	return NULL;
79 }
80 
81 static inline bool
lv2_osc_writer_overflow(LV2_OSC_Writer * writer,size_t size)82 lv2_osc_writer_overflow(LV2_OSC_Writer *writer, size_t size)
83 {
84 	return writer->ptr + size >= writer->end;
85 }
86 
87 static inline bool
lv2_osc_writer_htobe32(LV2_OSC_Writer * writer,union swap32_t * s32)88 lv2_osc_writer_htobe32(LV2_OSC_Writer *writer, union swap32_t *s32)
89 {
90 	if(lv2_osc_writer_overflow(writer, 4))
91 		return false;
92 
93 	s32->u = htobe32(s32->u);
94 	*(uint32_t *)writer->ptr = s32->u;
95 	writer->ptr += 4;
96 
97 	return true;
98 }
99 
100 static inline bool
lv2_osc_writer_htobe64(LV2_OSC_Writer * writer,union swap64_t * s64)101 lv2_osc_writer_htobe64(LV2_OSC_Writer *writer, union swap64_t *s64)
102 {
103 	if(lv2_osc_writer_overflow(writer, 8))
104 		return false;
105 
106 	s64->u = htobe64(s64->u);
107 	*(uint64_t *)writer->ptr = s64->u;
108 	writer->ptr += 8;
109 
110 	return true;
111 }
112 
113 static inline bool
lv2_osc_writer_add_int32(LV2_OSC_Writer * writer,int32_t i)114 lv2_osc_writer_add_int32(LV2_OSC_Writer *writer, int32_t i)
115 {
116 	return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .i = i });
117 }
118 
119 static inline bool
lv2_osc_writer_add_float(LV2_OSC_Writer * writer,float f)120 lv2_osc_writer_add_float(LV2_OSC_Writer *writer, float f)
121 {
122 	return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .f = f });
123 }
124 
125 static inline bool
lv2_osc_writer_add_string(LV2_OSC_Writer * writer,const char * s)126 lv2_osc_writer_add_string(LV2_OSC_Writer *writer, const char *s)
127 {
128 	const size_t rawlen = strlen(s) + 1;
129 	const size_t padded = LV2_OSC_PADDED_SIZE(rawlen);
130 	if(lv2_osc_writer_overflow(writer, padded))
131 		return false;
132 
133 	const uint32_t blank = 0;
134 	memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));
135 	memcpy(writer->ptr, s, rawlen);
136 	writer->ptr += padded;
137 
138 	return true;
139 }
140 
141 static inline bool
lv2_osc_writer_add_symbol(LV2_OSC_Writer * writer,const char * S)142 lv2_osc_writer_add_symbol(LV2_OSC_Writer *writer, const char *S)
143 {
144 	return lv2_osc_writer_add_string(writer, S);
145 }
146 
147 static inline bool
lv2_osc_writer_add_int64(LV2_OSC_Writer * writer,int64_t h)148 lv2_osc_writer_add_int64(LV2_OSC_Writer *writer, int64_t h)
149 {
150 	return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .h = h });
151 }
152 
153 static inline bool
lv2_osc_writer_add_double(LV2_OSC_Writer * writer,double d)154 lv2_osc_writer_add_double(LV2_OSC_Writer *writer, double d)
155 {
156 	return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .d = d });
157 }
158 
159 static inline bool
lv2_osc_writer_add_timetag(LV2_OSC_Writer * writer,uint64_t u)160 lv2_osc_writer_add_timetag(LV2_OSC_Writer *writer, uint64_t u)
161 {
162 	return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .u = u });
163 }
164 
165 static inline bool
lv2_osc_writer_add_blob_inline(LV2_OSC_Writer * writer,int32_t len,uint8_t ** body)166 lv2_osc_writer_add_blob_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **body)
167 {
168 	const size_t len_padded = LV2_OSC_PADDED_SIZE(len);
169 	const size_t size = 4 + len_padded;
170 	if(lv2_osc_writer_overflow(writer, size))
171 		return false;
172 
173 	if(!lv2_osc_writer_add_int32(writer, len))
174 		return false;
175 
176 	*body = writer->ptr;
177 	//memset(&writer->ptr[len], 0x0, len_padded - len);
178 	writer->ptr += len_padded;
179 
180 	return true;
181 }
182 
183 static inline bool
lv2_osc_writer_add_blob(LV2_OSC_Writer * writer,int32_t len,const uint8_t * body)184 lv2_osc_writer_add_blob(LV2_OSC_Writer *writer, int32_t len, const uint8_t *body)
185 {
186 	uint8_t *dst;
187 	if(!lv2_osc_writer_add_blob_inline(writer, len, &dst))
188 		return false;
189 
190 	memcpy(dst, body, len);
191 
192 	return true;
193 }
194 
195 static inline bool
lv2_osc_writer_add_midi_inline(LV2_OSC_Writer * writer,int32_t len,uint8_t ** m)196 lv2_osc_writer_add_midi_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **m)
197 {
198 	if( (len > 4) || lv2_osc_writer_overflow(writer, 4))
199 		return false;
200 
201 	*m = writer->ptr;
202 	//memset(&writer->ptr[len], 0x0, 4 - len);
203 	writer->ptr += 4;
204 
205 	return true;
206 }
207 
208 static inline bool
lv2_osc_writer_add_midi(LV2_OSC_Writer * writer,int32_t len,const uint8_t * m)209 lv2_osc_writer_add_midi(LV2_OSC_Writer *writer, int32_t len, const uint8_t *m)
210 {
211 	uint8_t *dst;
212 	if(!lv2_osc_writer_add_midi_inline(writer, len, &dst))
213 		return false;
214 
215 	memcpy(dst, m, len);
216 
217 	return true;
218 }
219 
220 static inline bool
lv2_osc_writer_add_rgba(LV2_OSC_Writer * writer,uint8_t r,uint8_t g,uint8_t b,uint8_t a)221 lv2_osc_writer_add_rgba(LV2_OSC_Writer *writer, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
222 {
223 	if(lv2_osc_writer_overflow(writer, 4))
224 		return false;
225 
226 	writer->ptr[0] = r;
227 	writer->ptr[1] = g;
228 	writer->ptr[2] = b;
229 	writer->ptr[3] = a;
230 	writer->ptr += 4;
231 
232 	return true;
233 }
234 
235 static inline bool
lv2_osc_writer_add_char(LV2_OSC_Writer * writer,char c)236 lv2_osc_writer_add_char(LV2_OSC_Writer *writer, char c)
237 {
238 	return lv2_osc_writer_add_int32(writer, (int32_t)c);
239 }
240 
241 static inline bool
lv2_osc_writer_push_bundle(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame,uint64_t t)242 lv2_osc_writer_push_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame, uint64_t t)
243 {
244 	if(lv2_osc_writer_overflow(writer, 16))
245 		return false;
246 
247 	frame->ref = writer->ptr;
248 
249 	strncpy((char *)writer->ptr, "#bundle", 8);
250 	writer->ptr += 8;
251 
252 	return lv2_osc_writer_add_timetag(writer, t);
253 }
254 
255 static inline bool
lv2_osc_writer_pop_bundle(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)256 lv2_osc_writer_pop_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
257 {
258 	union swap32_t s32 = { .i = writer->ptr - frame->ref - 16};
259 
260 	if(s32.i <= 0)
261 	{
262 		writer->ptr = frame->ref;
263 		return false;
264 	}
265 
266 	return true;
267 }
268 
269 static inline  bool
lv2_osc_writer_push_item(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)270 lv2_osc_writer_push_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
271 {
272 	if(lv2_osc_writer_overflow(writer, 4))
273 		return false;
274 
275 	frame->ref = writer->ptr;
276 	writer->ptr += 4;
277 
278 	return true;
279 }
280 
281 static inline bool
lv2_osc_writer_pop_item(LV2_OSC_Writer * writer,LV2_OSC_Writer_Frame * frame)282 lv2_osc_writer_pop_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame)
283 {
284 	union swap32_t s32 = { .i = writer->ptr - frame->ref - 4};
285 
286 	if(s32.i <= 0)
287 	{
288 		writer->ptr = frame->ref;
289 		return false;
290 	}
291 
292 	s32.u = htobe32(s32.u);
293 	*(uint32_t *)frame->ref = s32.u;
294 
295 	return true;
296 }
297 
298 static inline bool
lv2_osc_writer_add_path(LV2_OSC_Writer * writer,const char * path)299 lv2_osc_writer_add_path(LV2_OSC_Writer *writer, const char *path)
300 {
301 	return lv2_osc_writer_add_string(writer, path);
302 }
303 
304 static inline bool
lv2_osc_writer_add_format(LV2_OSC_Writer * writer,const char * fmt)305 lv2_osc_writer_add_format(LV2_OSC_Writer *writer, const char *fmt)
306 {
307 	const size_t rawlen = strlen(fmt) + 1;
308 	const size_t padded = LV2_OSC_PADDED_SIZE(rawlen + 1);
309 	if(lv2_osc_writer_overflow(writer, padded))
310 		return false;
311 
312 	const uint32_t blank = 0;
313 	memcpy(writer->ptr + padded - sizeof(uint32_t), &blank, sizeof(uint32_t));
314 	*writer->ptr++ = ',';
315 	memcpy(writer->ptr, fmt, rawlen);
316 	writer->ptr += padded - 1;
317 
318 	return true;
319 }
320 
321 static inline bool
lv2_osc_writer_arg_varlist(LV2_OSC_Writer * writer,const char * fmt,va_list args)322 lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args)
323 {
324 	for(const char *type = fmt; *type; type++)
325 	{
326 		switch( (LV2_OSC_Type)*type)
327 		{
328 			case LV2_OSC_INT32:
329 				if(!lv2_osc_writer_add_int32(writer, va_arg(args, int32_t)))
330 					return false;
331 				break;
332 			case LV2_OSC_FLOAT:
333 				if(!lv2_osc_writer_add_float(writer, (float)va_arg(args, double)))
334 					return false;
335 				break;
336 			case LV2_OSC_STRING:
337 				if(!lv2_osc_writer_add_string(writer, va_arg(args, const char *)))
338 					return false;
339 				break;
340 			case LV2_OSC_BLOB:
341 			{
342 				const int32_t len = va_arg(args, int32_t);
343 				if(!lv2_osc_writer_add_blob(writer, len, va_arg(args, const uint8_t *)))
344 					return false;
345 			}	break;
346 
347 			case LV2_OSC_TRUE:
348 			case LV2_OSC_FALSE:
349 			case LV2_OSC_NIL:
350 			case LV2_OSC_IMPULSE:
351 				break;
352 
353 			case LV2_OSC_INT64:
354 				if(!lv2_osc_writer_add_int64(writer, va_arg(args, int64_t)))
355 					return false;
356 				break;
357 			case LV2_OSC_DOUBLE:
358 				if(!lv2_osc_writer_add_double(writer, va_arg(args, double)))
359 					return false;
360 				break;
361 			case LV2_OSC_TIMETAG:
362 				if(!lv2_osc_writer_add_timetag(writer, va_arg(args, uint64_t)))
363 					return false;
364 				break;
365 
366 			case LV2_OSC_MIDI:
367 			{
368 				const int32_t len = va_arg(args, int32_t);
369 				if(!lv2_osc_writer_add_midi(writer, len, va_arg(args, const uint8_t *)))
370 					return false;
371 			}	break;
372 			case LV2_OSC_SYMBOL:
373 				if(!lv2_osc_writer_add_symbol(writer, va_arg(args, const char *)))
374 					return false;
375 				break;
376 			case LV2_OSC_CHAR:
377 				if(!lv2_osc_writer_add_char(writer, va_arg(args, int)))
378 					return false;
379 				break;
380 			case LV2_OSC_RGBA:
381 			{
382 				const uint8_t r = va_arg(args, unsigned);
383 				const uint8_t g = va_arg(args, unsigned);
384 				const uint8_t b = va_arg(args, unsigned);
385 				const uint8_t a = va_arg(args, unsigned);
386 				if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
387 					return false;
388 			}	break;
389 		}
390 	}
391 
392 	return true;
393 }
394 
395 static inline bool
lv2_osc_writer_arg_vararg(LV2_OSC_Writer * writer,const char * fmt,...)396 lv2_osc_writer_arg_vararg(LV2_OSC_Writer *writer, const char *fmt, ...)
397 {
398   va_list args;
399   va_start(args, fmt);
400 
401 	const bool res = lv2_osc_writer_arg_varlist(writer, fmt, args);
402 
403 	va_end(args);
404 
405 	return res;
406 }
407 
408 static inline bool
lv2_osc_writer_message_varlist(LV2_OSC_Writer * writer,const char * path,const char * fmt,va_list args)409 lv2_osc_writer_message_varlist(LV2_OSC_Writer *writer, const char *path, const char *fmt, va_list args)
410 {
411 	if(!lv2_osc_writer_add_path(writer, path))
412 		return false;
413 
414 	if(!lv2_osc_writer_add_format(writer, fmt))
415 		return false;
416 
417 	return lv2_osc_writer_arg_varlist(writer, fmt, args);
418 }
419 
420 static inline bool
lv2_osc_writer_message_vararg(LV2_OSC_Writer * writer,const char * path,const char * fmt,...)421 lv2_osc_writer_message_vararg(LV2_OSC_Writer *writer, const char *path, const char *fmt, ...)
422 {
423   va_list args;
424   va_start(args, fmt);
425 
426 	const bool res = lv2_osc_writer_message_varlist(writer, path, fmt, args);
427 
428 	va_end(args);
429 
430 	return res;
431 }
432 
433 static inline bool
lv2_osc_writer_packet(LV2_OSC_Writer * writer,LV2_OSC_URID * osc_urid,LV2_URID_Unmap * unmap,uint32_t size,const LV2_Atom_Object_Body * body)434 lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid,
435 	LV2_URID_Unmap *unmap, uint32_t size, const LV2_Atom_Object_Body *body)
436 {
437 	if(body->otype == osc_urid->OSC_Bundle)
438 	{
439 		const LV2_Atom_Object *timetag = NULL;
440 		const LV2_Atom_Tuple *items = NULL;
441 
442 		if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items))
443 			return false;
444 
445 		LV2_OSC_Timetag tt;
446 		LV2_OSC_Writer_Frame bndl = { .ref = 0 };
447 
448 		lv2_osc_timetag_get(osc_urid, &timetag->atom, &tt);
449 		if(!lv2_osc_writer_push_bundle(writer, &bndl, lv2_osc_timetag_parse(&tt)))
450 			return false;
451 
452 		LV2_ATOM_TUPLE_FOREACH(items, atom)
453 		{
454 			const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
455 			LV2_OSC_Writer_Frame itm = { .ref = 0 };
456 
457 			if(  !lv2_osc_writer_push_item(writer, &itm)
458 				|| !lv2_osc_writer_packet(writer, osc_urid, unmap, obj->atom.size, &obj->body)
459 				|| !lv2_osc_writer_pop_item(writer, &itm) )
460 			{
461 				return false;
462 			}
463 		}
464 
465 		return lv2_osc_writer_pop_bundle(writer, &bndl);
466 	}
467 	else if(body->otype == osc_urid->OSC_Message)
468 	{
469 		const LV2_Atom_String *path = NULL;
470 		const LV2_Atom_Tuple *arguments = NULL;
471 
472 		if(lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments))
473 		{
474 			if(!lv2_osc_writer_add_path(writer, LV2_ATOM_BODY_CONST(path)))
475 				return false;
476 
477 			char fmt [128]; //TODO how big?
478 			char *ptr = fmt;
479 			LV2_ATOM_TUPLE_FOREACH(arguments, atom)
480 			{
481 				*ptr++ = lv2_osc_argument_type(osc_urid, atom);
482 			}
483 			*ptr = '\0';
484 			if(!lv2_osc_writer_add_format(writer, fmt))
485 				return false;
486 
487 			LV2_ATOM_TUPLE_FOREACH(arguments, atom)
488 			{
489 				const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom;
490 
491 				if(atom->type == osc_urid->ATOM_Int)
492 				{
493 					if(!lv2_osc_writer_add_int32(writer, ((const LV2_Atom_Int *)atom)->body))
494 						return false;
495 				}
496 				else if(atom->type == osc_urid->ATOM_Float)
497 				{
498 					if(!lv2_osc_writer_add_float(writer, ((const LV2_Atom_Float *)atom)->body))
499 						return false;
500 				}
501 				else if(atom->type == osc_urid->ATOM_String)
502 				{
503 					if(!lv2_osc_writer_add_string(writer, LV2_ATOM_BODY_CONST(atom)))
504 						return false;
505 				}
506 				else if(atom->type == osc_urid->ATOM_Chunk)
507 				{
508 					if(!lv2_osc_writer_add_blob(writer, atom->size, LV2_ATOM_BODY_CONST(atom)))
509 						return false;
510 				}
511 
512 				else if(atom->type == osc_urid->ATOM_Long)
513 				{
514 					if(!lv2_osc_writer_add_int64(writer, ((const LV2_Atom_Long *)atom)->body))
515 						return false;
516 				}
517 				else if(atom->type == osc_urid->ATOM_Double)
518 				{
519 					if(!lv2_osc_writer_add_double(writer, ((const LV2_Atom_Double *)atom)->body))
520 						return false;
521 				}
522 				else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) )
523 				{
524 					LV2_OSC_Timetag tt;
525 					lv2_osc_timetag_get(osc_urid, &obj->atom, &tt);
526 					if(!lv2_osc_writer_add_timetag(writer, lv2_osc_timetag_parse(&tt)))
527 						return false;
528 				}
529 
530 				// there is nothing to do for: true, false, nil, impulse
531 
532 				else if(atom->type == osc_urid->ATOM_URID)
533 				{
534 					const char *symbol = unmap->unmap(unmap->handle, ((const LV2_Atom_URID *)atom)->body);
535 					if(!symbol || !lv2_osc_writer_add_symbol(writer, symbol))
536 						return false;
537 				}
538 				else if(atom->type == osc_urid->MIDI_MidiEvent)
539 				{
540 					uint8_t *m = NULL;
541 					if(!lv2_osc_writer_add_midi_inline(writer, atom->size + 1, &m))
542 						return false;
543 					m[0] = 0x0; // port
544 					memcpy(&m[1], LV2_ATOM_BODY_CONST(atom), atom->size);
545 				}
546 				else if(atom->type == osc_urid->ATOM_Literal)
547 				{
548 					const LV2_Atom_Literal *lit = (LV2_Atom_Literal *)atom;
549 
550 					if(lit->body.datatype == osc_urid->OSC_Char)
551 					{
552 						const char c = *(const char *)LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, lit);
553 						if(!lv2_osc_writer_add_char(writer, c))
554 							return false;
555 					}
556 					else if(lit->body.datatype == osc_urid->OSC_RGBA)
557 					{
558 						const char *rgba = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Literal, atom);
559 						uint8_t r, g, b, a;
560 						if(sscanf(rgba, "%02"SCNx8"%02"SCNx8"%02"SCNx8"%02"SCNx8, &r, &g, &b, &a) != 4)
561 							return false;
562 						if(!lv2_osc_writer_add_rgba(writer, r, g, b, a))
563 							return false;
564 					}
565 				}
566 			}
567 		}
568 
569 		return true;
570 	}
571 
572 	return false;
573 }
574 
575 #ifdef __cplusplus
576 } // extern "C"
577 #endif
578 
579 #endif // LV2_OSC_WRITER_H
580