1/*
2 * Copyright 2005-2015, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel Dörfler, axeld@pinc-software.de
7 *		Michael Lotz <mmlr@mlotz.ch>
8 */
9
10
11#include <MessageAdapter.h>
12#include <MessagePrivate.h>
13#include <MessageUtils.h>
14
15#include <stdlib.h>
16
17
18namespace BPrivate {
19
20#define R5_MESSAGE_FLAG_VALID			0x01
21#define R5_MESSAGE_FLAG_INCLUDE_TARGET	0x02
22#define R5_MESSAGE_FLAG_INCLUDE_REPLY	0x04
23#define R5_MESSAGE_FLAG_SCRIPT_MESSAGE	0x08
24
25#define R5_FIELD_FLAG_VALID				0x01
26#define R5_FIELD_FLAG_MINI_DATA			0x02
27#define R5_FIELD_FLAG_FIXED_SIZE		0x04
28#define R5_FIELD_FLAG_SINGLE_ITEM		0x08
29
30
31enum {
32	SECTION_MESSAGE_HEADER = 'FOB2',
33	SECTION_OFFSET_TABLE = 'STof',
34	SECTION_TARGET_INFORMATION = 'ENwh',
35	SECTION_SINGLE_ITEM_DATA = 'SGDa',
36	SECTION_FIXED_SIZE_ARRAY_DATA = 'FADa',
37	SECTION_VARIABLE_SIZE_ARRAY_DATA = 'VADa',
38	SECTION_SORTED_INDEX_TABLE = 'DXIn',
39	SECTION_END_OF_DATA = 'DDEn'
40};
41
42
43struct r5_message_header {
44	uint32	magic;
45	uint32	checksum;
46	int32	flattened_size;
47	int32	what;
48	uint8	flags;
49} _PACKED;
50
51
52struct dano_section_header {
53	uint32		code;
54	int32		size;
55	uint8		data[0];
56} _PACKED;
57
58
59struct dano_message_header {
60	int32		what;
61	int32		padding;
62} _PACKED;
63
64
65typedef struct offset_table_s {
66	int32		indexTable;
67	int32		endOfData;
68	int64		padding;
69} OffsetTable;
70
71
72struct dano_single_item {
73	type_code	type;
74	int32		item_size;
75	uint8		name_length;
76	char		name[0];
77} _PACKED;
78
79
80struct dano_fixed_size_array {
81	type_code	type;
82	int32		size_per_item;
83	uint8		name_length;
84	char		name[0];
85} _PACKED;
86
87
88struct dano_variable_size_array {
89	type_code	type;
90	int32		padding;
91	uint8		name_length;
92	char		name[0];
93} _PACKED;
94
95
96inline int32
97pad_to_8(int32 value)
98{
99	return (value + 7) & ~7;
100}
101
102
103/*static*/ ssize_t
104MessageAdapter::FlattenedSize(uint32 format, const BMessage *from)
105{
106	switch (format) {
107		case MESSAGE_FORMAT_R5:
108		case MESSAGE_FORMAT_R5_SWAPPED:
109			return _R5FlattenedSize(from);
110	}
111
112	return -1;
113}
114
115
116/*static*/ status_t
117MessageAdapter::Flatten(uint32 format, const BMessage *from, char *buffer,
118	ssize_t *size)
119{
120	switch (format) {
121		case MESSAGE_FORMAT_R5:
122		case MESSAGE_FORMAT_R5_SWAPPED:
123			return _FlattenR5Message(format, from, buffer, size);
124	}
125
126	return B_ERROR;
127}
128
129
130/*static*/ status_t
131MessageAdapter::Flatten(uint32 format, const BMessage *from, BDataIO *stream,
132	ssize_t *size)
133{
134	switch (format) {
135		case MESSAGE_FORMAT_R5:
136		case MESSAGE_FORMAT_R5_SWAPPED:
137		{
138			ssize_t flattenedSize = _R5FlattenedSize(from);
139			char *buffer = (char *)malloc(flattenedSize);
140			if (!buffer)
141				return B_NO_MEMORY;
142
143			status_t result = _FlattenR5Message(format, from, buffer,
144				&flattenedSize);
145			if (result < B_OK) {
146				free(buffer);
147				return result;
148			}
149
150			ssize_t written = stream->Write(buffer, flattenedSize);
151			if (written != flattenedSize) {
152				free(buffer);
153				return (written >= 0 ? B_ERROR : written);
154			}
155
156			if (size)
157				*size = flattenedSize;
158
159			free(buffer);
160			return B_OK;
161		}
162	}
163
164	return B_ERROR;
165}
166
167
168/*static*/ status_t
169MessageAdapter::Unflatten(uint32 format, BMessage *into, const char *buffer)
170{
171	if (format == KMessage::kMessageHeaderMagic) {
172		KMessage message;
173		status_t result = message.SetTo(buffer,
174			((KMessage::Header *)buffer)->size);
175		if (result != B_OK)
176			return result;
177
178		return _ConvertFromKMessage(&message, into);
179	}
180
181	try {
182		switch (format) {
183			case MESSAGE_FORMAT_R5:
184			{
185				r5_message_header *header = (r5_message_header *)buffer;
186				BMemoryIO stream(buffer + sizeof(uint32),
187					header->flattened_size - sizeof(uint32));
188				return _UnflattenR5Message(format, into, &stream);
189			}
190
191			case MESSAGE_FORMAT_R5_SWAPPED:
192			{
193				r5_message_header *header = (r5_message_header *)buffer;
194				BMemoryIO stream(buffer + sizeof(uint32),
195					__swap_int32(header->flattened_size) - sizeof(uint32));
196				return _UnflattenR5Message(format, into, &stream);
197			}
198
199			case MESSAGE_FORMAT_DANO:
200			case MESSAGE_FORMAT_DANO_SWAPPED:
201			{
202				dano_section_header *header = (dano_section_header *)buffer;
203				ssize_t size = header->size;
204				if (header->code == MESSAGE_FORMAT_DANO_SWAPPED)
205					size = __swap_int32(size);
206
207				BMemoryIO stream(buffer + sizeof(uint32), size - sizeof(uint32));
208				return _UnflattenDanoMessage(format, into, &stream);
209			}
210		}
211	} catch (status_t error) {
212		into->MakeEmpty();
213		return error;
214	}
215
216	return B_NOT_A_MESSAGE;
217}
218
219
220/*static*/ status_t
221MessageAdapter::Unflatten(uint32 format, BMessage *into, BDataIO *stream)
222{
223	try {
224		switch (format) {
225			case MESSAGE_FORMAT_R5:
226			case MESSAGE_FORMAT_R5_SWAPPED:
227				return _UnflattenR5Message(format, into, stream);
228
229			case MESSAGE_FORMAT_DANO:
230			case MESSAGE_FORMAT_DANO_SWAPPED:
231				return _UnflattenDanoMessage(format, into, stream);
232		}
233	} catch (status_t error) {
234		into->MakeEmpty();
235		return error;
236	}
237
238	return B_NOT_A_MESSAGE;
239}
240
241
242/*static*/ status_t
243MessageAdapter::ConvertToKMessage(const BMessage* from, KMessage& to)
244{
245	if (from == NULL)
246		return B_BAD_VALUE;
247
248	BMessage::Private fromPrivate(const_cast<BMessage*>(from));
249	BMessage::message_header* header = fromPrivate.GetMessageHeader();
250	uint8* data = fromPrivate.GetMessageData();
251
252	// Iterate through the fields and import them in the target message
253	BMessage::field_header* field = fromPrivate.GetMessageFields();
254	for (uint32 i = 0; i < header->field_count; i++, field++) {
255		const char* name = (const char*)data + field->offset;
256		const uint8* fieldData = data + field->offset + field->name_length;
257		bool fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0;
258
259		if (fixedSize) {
260			status_t status = to.AddArray(name, field->type, fieldData,
261				field->data_size / field->count, field->count);
262			if (status != B_OK)
263				return status;
264		} else {
265			for (uint32 i = 0; i < field->count; i++) {
266				uint32 itemSize = *(uint32*)fieldData;
267				fieldData += sizeof(uint32);
268				status_t status = to.AddData(name, field->type, fieldData,
269					itemSize, false);
270				if (status != B_OK)
271					return status;
272				fieldData += itemSize;
273			}
274		}
275	}
276	return B_OK;
277}
278
279
280/*static*/ status_t
281MessageAdapter::_ConvertFromKMessage(const KMessage *fromMessage,
282	BMessage *toMessage)
283{
284	if (!fromMessage || !toMessage)
285		return B_BAD_VALUE;
286
287	// make empty and init what of the target message
288	toMessage->MakeEmpty();
289	toMessage->what = fromMessage->What();
290
291	BMessage::Private toPrivate(toMessage);
292	toPrivate.SetTarget(fromMessage->TargetToken());
293	toPrivate.SetReply(B_SYSTEM_TEAM, fromMessage->ReplyPort(),
294		fromMessage->ReplyToken());
295	if (fromMessage->ReplyPort() >= 0) {
296		toPrivate.GetMessageHeader()->flags |= MESSAGE_FLAG_REPLY_AS_KMESSAGE
297			| MESSAGE_FLAG_REPLY_REQUIRED;
298	}
299
300	// Iterate through the fields and import them in the target message
301	KMessageField field;
302	while (fromMessage->GetNextField(&field) == B_OK) {
303		int32 elementCount = field.CountElements();
304		if (elementCount > 0) {
305			for (int32 i = 0; i < elementCount; i++) {
306				int32 size;
307				const void *data = field.ElementAt(i, &size);
308				status_t result;
309
310				if (field.TypeCode() == B_MESSAGE_TYPE) {
311					// message type: if it's a KMessage, convert it
312					KMessage message;
313					if (message.SetTo(data, size) == B_OK) {
314						BMessage bMessage;
315						result = _ConvertFromKMessage(&message, &bMessage);
316						if (result < B_OK)
317							return result;
318
319						result = toMessage->AddMessage(field.Name(), &bMessage);
320					} else {
321						// just add it
322						result = toMessage->AddData(field.Name(),
323							field.TypeCode(), data, size,
324							field.HasFixedElementSize(), 1);
325					}
326				} else {
327					result = toMessage->AddData(field.Name(), field.TypeCode(),
328						data, size, field.HasFixedElementSize(), 1);
329				}
330
331				if (result < B_OK)
332					return result;
333			}
334		}
335	}
336
337	return B_OK;
338}
339
340
341/*static*/ ssize_t
342MessageAdapter::_R5FlattenedSize(const BMessage *from)
343{
344	BMessage::Private messagePrivate((BMessage *)from);
345	BMessage::message_header* header = messagePrivate.GetMessageHeader();
346
347	// header size (variable, depending on the flags)
348
349	ssize_t flattenedSize = sizeof(r5_message_header);
350
351	if (header->target != B_NULL_TOKEN)
352		flattenedSize += sizeof(int32);
353
354	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
355		&& header->reply_team >= 0) {
356		// reply info + big flags
357		flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id) + 4;
358	}
359
360	// field size
361
362	uint8 *data = messagePrivate.GetMessageData();
363	BMessage::field_header *field = messagePrivate.GetMessageFields();
364	for (uint32 i = 0; i < header->field_count; i++, field++) {
365		// flags and type
366		flattenedSize += 1 + sizeof(type_code);
367
368#if 0
369		bool miniData = field->dataSize <= 255 && field->count <= 255;
370#else
371		// TODO: we don't know the R5 dataSize yet (padding)
372		bool miniData = false;
373#endif
374
375		// item count
376		if (field->count > 1)
377			flattenedSize += (miniData ? sizeof(uint8) : sizeof(uint32));
378
379		// data size
380		flattenedSize += (miniData ? sizeof(uint8) : sizeof(size_t));
381
382		// name length and name
383		flattenedSize += 1 + min_c(field->name_length - 1, 255);
384
385		// data
386		if (field->flags & FIELD_FLAG_FIXED_SIZE)
387			flattenedSize += field->data_size;
388		else {
389			uint8 *source = data + field->offset + field->name_length;
390
391			for (uint32 i = 0; i < field->count; i++) {
392				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
393				flattenedSize += pad_to_8(itemSize);
394				source += itemSize;
395			}
396		}
397	}
398
399	// pseudo field with flags 0
400	return flattenedSize + 1;
401}
402
403
404/*static*/ status_t
405MessageAdapter::_FlattenR5Message(uint32 format, const BMessage *from,
406	char *buffer, ssize_t *size)
407{
408	BMessage::Private messagePrivate((BMessage *)from);
409	BMessage::message_header *header = messagePrivate.GetMessageHeader();
410	uint8 *data = messagePrivate.GetMessageData();
411
412	r5_message_header *r5header = (r5_message_header *)buffer;
413	uint8 *pointer = (uint8 *)buffer + sizeof(r5_message_header);
414
415	r5header->magic = MESSAGE_FORMAT_R5;
416	r5header->what = from->what;
417	r5header->checksum = 0;
418
419	uint8 flags = R5_MESSAGE_FLAG_VALID;
420	if (header->target != B_NULL_TOKEN) {
421		*(int32 *)pointer = header->target;
422		pointer += sizeof(int32);
423		flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET;
424	}
425
426	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
427		&& header->reply_team >= 0) {
428		// reply info
429		*(port_id *)pointer = header->reply_port;
430		pointer += sizeof(port_id);
431		*(int32 *)pointer = header->reply_target;
432		pointer += sizeof(int32);
433		*(team_id *)pointer = header->reply_team;
434		pointer += sizeof(team_id);
435
436		// big flags
437		*pointer = (header->reply_target == B_PREFERRED_TOKEN ? 1 : 0);
438		pointer++;
439
440		*pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0);
441		pointer++;
442
443		*pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0);
444		pointer++;
445
446		*pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0);
447		pointer++;
448
449		flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY;
450	}
451
452	if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS)
453		flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE;
454
455	r5header->flags = flags;
456
457	// store the header size - used for the checksum later
458	ssize_t headerSize = (addr_t)pointer - (addr_t)buffer;
459
460	// collect and add the data
461	BMessage::field_header *field = messagePrivate.GetMessageFields();
462	for (uint32 i = 0; i < header->field_count; i++, field++) {
463		flags = R5_FIELD_FLAG_VALID;
464
465		if (field->count == 1)
466			flags |= R5_FIELD_FLAG_SINGLE_ITEM;
467		// TODO: we don't really know the data size now (padding missing)
468//		if (field->data_size <= 255 && field->count <= 255)
469//			flags |= R5_FIELD_FLAG_MINI_DATA;
470		if (field->flags & FIELD_FLAG_FIXED_SIZE)
471			flags |= R5_FIELD_FLAG_FIXED_SIZE;
472
473		*pointer = flags;
474		pointer++;
475
476		*(type_code *)pointer = field->type;
477		pointer += sizeof(type_code);
478
479		if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) {
480			if (flags & R5_FIELD_FLAG_MINI_DATA) {
481				*pointer = (uint8)field->count;
482				pointer++;
483			} else {
484				*(int32 *)pointer = field->count;
485				pointer += sizeof(int32);
486			}
487		}
488
489		// we may have to adjust this to account for padding later
490		uint8 *fieldSize = pointer;
491		if (flags & R5_FIELD_FLAG_MINI_DATA) {
492			*pointer = (uint8)field->data_size;
493			pointer++;
494		} else {
495			*(ssize_t *)pointer = field->data_size;
496			pointer += sizeof(ssize_t);
497		}
498
499		// name
500		int32 nameLength = min_c(field->name_length - 1, 255);
501		*pointer = (uint8)nameLength;
502		pointer++;
503
504		strncpy((char *)pointer, (char *)data + field->offset, nameLength);
505		pointer += nameLength;
506
507		// data
508		uint8 *source = data + field->offset + field->name_length;
509		if (flags & R5_FIELD_FLAG_FIXED_SIZE) {
510			memcpy(pointer, source, field->data_size);
511			pointer += field->data_size;
512		} else {
513			uint8 *previous = pointer;
514			for (uint32 i = 0; i < field->count; i++) {
515				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
516				memcpy(pointer, source, itemSize);
517				ssize_t paddedSize = pad_to_8(itemSize);
518				memset(pointer + itemSize, 0, paddedSize - itemSize);
519				pointer += paddedSize;
520				source += itemSize;
521			}
522
523			// adjust the field size to the padded value
524			if (flags & R5_FIELD_FLAG_MINI_DATA)
525				*fieldSize = (uint8)(pointer - previous);
526			else
527				*(ssize_t *)fieldSize = (pointer - previous);
528		}
529	}
530
531	// terminate the fields with a pseudo field with flags 0 (not valid)
532	*pointer = 0;
533	pointer++;
534
535	// calculate the flattened size from the pointers
536	r5header->flattened_size = (addr_t)pointer - (addr_t)buffer;
537	r5header->checksum = CalculateChecksum((uint8 *)(buffer + 8),
538		headerSize - 8);
539
540	if (size)
541		*size = r5header->flattened_size;
542
543	return B_OK;
544}
545
546
547/*static*/ status_t
548MessageAdapter::_UnflattenR5Message(uint32 format, BMessage *into,
549	BDataIO *stream)
550{
551	into->MakeEmpty();
552
553	BMessage::Private messagePrivate(into);
554	BMessage::message_header *header = messagePrivate.GetMessageHeader();
555
556	TReadHelper reader(stream);
557	if (format == MESSAGE_FORMAT_R5_SWAPPED)
558		reader.SetSwap(true);
559
560	// the stream is already advanced by the size of the "format"
561	r5_message_header r5header;
562	reader(((uint8 *)&r5header) + sizeof(uint32),
563		sizeof(r5header) - sizeof(uint32));
564
565	header->what = into->what = r5header.what;
566	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET)
567		reader(&header->target, sizeof(header->target));
568
569	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) {
570		// reply info
571		reader(&header->reply_port, sizeof(header->reply_port));
572		reader(&header->reply_target, sizeof(header->reply_target));
573		reader(&header->reply_team, sizeof(header->reply_team));
574
575		// big flags
576		uint8 bigFlag;
577		reader(bigFlag);
578		if (bigFlag)
579			header->reply_target = B_PREFERRED_TOKEN;
580
581		reader(bigFlag);
582		if (bigFlag)
583			header->flags |= MESSAGE_FLAG_REPLY_REQUIRED;
584
585		reader(bigFlag);
586		if (bigFlag)
587			header->flags |= MESSAGE_FLAG_REPLY_DONE;
588
589		reader(bigFlag);
590		if (bigFlag)
591			header->flags |= MESSAGE_FLAG_IS_REPLY;
592	}
593
594	if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE)
595		header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS;
596
597	uint8 flags;
598	reader(flags);
599	while ((flags & R5_FIELD_FLAG_VALID) != 0) {
600		bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE;
601		bool miniData = flags & R5_FIELD_FLAG_MINI_DATA;
602		bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM;
603
604		type_code type;
605		reader(type);
606
607		int32 itemCount;
608		if (!singleItem) {
609			if (miniData) {
610				uint8 miniCount;
611				reader(miniCount);
612				itemCount = miniCount;
613			} else
614				reader(itemCount);
615		} else
616			itemCount = 1;
617
618		int32 dataSize;
619		if (miniData) {
620			uint8 miniSize;
621			reader(miniSize);
622			dataSize = miniSize;
623		} else
624			reader(dataSize);
625
626		if (dataSize <= 0)
627			return B_ERROR;
628
629		// name
630		uint8 nameLength;
631		reader(nameLength);
632
633		char nameBuffer[256];
634		reader(nameBuffer, nameLength);
635		nameBuffer[nameLength] = '\0';
636
637		uint8 *buffer = (uint8 *)malloc(dataSize);
638		uint8 *pointer = buffer;
639		reader(buffer, dataSize);
640
641		status_t result = B_OK;
642		int32 itemSize = 0;
643		if (fixedSize)
644			itemSize = dataSize / itemCount;
645
646		if (format == MESSAGE_FORMAT_R5) {
647			for (int32 i = 0; i < itemCount; i++) {
648				if (!fixedSize) {
649					itemSize = *(int32 *)pointer;
650					pointer += sizeof(int32);
651				}
652
653				result = into->AddData(nameBuffer, type, pointer, itemSize,
654					fixedSize, itemCount);
655
656				if (result < B_OK) {
657					free(buffer);
658					return result;
659				}
660
661				if (fixedSize)
662					pointer += itemSize;
663				else {
664					pointer += pad_to_8(itemSize + sizeof(int32))
665						- sizeof(int32);
666				}
667			}
668		} else {
669			for (int32 i = 0; i < itemCount; i++) {
670				if (!fixedSize) {
671					itemSize = __swap_int32(*(int32 *)pointer);
672					pointer += sizeof(int32);
673				}
674
675				swap_data(type, pointer, itemSize, B_SWAP_ALWAYS);
676				result = into->AddData(nameBuffer, type, pointer, itemSize,
677					fixedSize, itemCount);
678
679				if (result < B_OK) {
680					free(buffer);
681					return result;
682				}
683
684				if (fixedSize)
685					pointer += itemSize;
686				else {
687					pointer += pad_to_8(itemSize + sizeof(int32))
688						- sizeof(int32);
689				}
690			}
691		}
692
693		free(buffer);
694
695		// flags of next field or termination byte
696		reader(flags);
697	}
698
699	return B_OK;
700}
701
702
703/*static*/ status_t
704MessageAdapter::_UnflattenDanoMessage(uint32 format, BMessage *into,
705	BDataIO *stream)
706{
707	into->MakeEmpty();
708
709	TReadHelper reader(stream);
710	if (format == MESSAGE_FORMAT_DANO_SWAPPED)
711		reader.SetSwap(true);
712
713	ssize_t size;
714	reader(size);
715
716	dano_message_header header;
717	reader(header);
718	into->what = header.what;
719
720	size -= sizeof(dano_section_header) + sizeof(dano_message_header);
721	int32 offset = 0;
722
723	while (offset < size) {
724		dano_section_header sectionHeader;
725		reader(sectionHeader);
726
727		// be safe. this shouldn't be necessary but in some testcases it was.
728		sectionHeader.size = pad_to_8(sectionHeader.size);
729
730		if (offset + sectionHeader.size > size || sectionHeader.size < 0)
731			return B_BAD_DATA;
732
733		ssize_t fieldSize = sectionHeader.size - sizeof(dano_section_header);
734		uint8 *fieldBuffer = NULL;
735		if (fieldSize <= 0) {
736			// there may be no data. we shouldn't fail because of that
737			offset += sectionHeader.size;
738			continue;
739		}
740
741		fieldBuffer = (uint8 *)malloc(fieldSize);
742		if (fieldBuffer == NULL)
743			throw (status_t)B_NO_MEMORY;
744
745		reader(fieldBuffer, fieldSize);
746
747		switch (sectionHeader.code) {
748			case SECTION_OFFSET_TABLE:
749			case SECTION_TARGET_INFORMATION:
750			case SECTION_SORTED_INDEX_TABLE:
751			case SECTION_END_OF_DATA:
752				// discard
753				break;
754
755			case SECTION_SINGLE_ITEM_DATA:
756			{
757				dano_single_item *field = (dano_single_item *)fieldBuffer;
758
759				int32 dataOffset = sizeof(dano_single_item)
760					+ field->name_length + 1;
761				dataOffset = pad_to_8(dataOffset);
762
763				if (offset + dataOffset + field->item_size > size)
764					return B_BAD_DATA;
765
766				// support for fixed size is not possible with a single item
767				bool fixedSize = false;
768				switch (field->type) {
769					case B_RECT_TYPE:
770					case B_POINT_TYPE:
771					case B_INT8_TYPE:
772					case B_INT16_TYPE:
773					case B_INT32_TYPE:
774					case B_INT64_TYPE:
775					case B_BOOL_TYPE:
776					case B_FLOAT_TYPE:
777					case B_DOUBLE_TYPE:
778					case B_POINTER_TYPE:
779					case B_MESSENGER_TYPE:
780						fixedSize = true;
781						break;
782					default:
783						break;
784				}
785
786				status_t result = into->AddData(field->name, field->type,
787					fieldBuffer + dataOffset, field->item_size, fixedSize);
788
789				if (result != B_OK) {
790					free(fieldBuffer);
791					throw result;
792				}
793				break;
794			}
795
796			case SECTION_FIXED_SIZE_ARRAY_DATA: {
797				dano_fixed_size_array *field
798					= (dano_fixed_size_array *)fieldBuffer;
799
800				int32 dataOffset = sizeof(dano_fixed_size_array)
801					+ field->name_length + 1;
802				dataOffset = pad_to_8(dataOffset);
803				int32 count = *(int32 *)(fieldBuffer + dataOffset);
804				dataOffset += 8; /* count and padding */
805
806				if (offset + dataOffset + count * field->size_per_item > size)
807					return B_BAD_DATA;
808
809				status_t result = B_OK;
810				for (int32 i = 0; i < count; i++) {
811					result = into->AddData(field->name, field->type,
812						fieldBuffer + dataOffset, field->size_per_item, true,
813						count);
814
815					if (result != B_OK) {
816						free(fieldBuffer);
817						throw result;
818					}
819
820					dataOffset += field->size_per_item;
821				}
822				break;
823			}
824
825			case SECTION_VARIABLE_SIZE_ARRAY_DATA: {
826				dano_variable_size_array *field
827					= (dano_variable_size_array *)fieldBuffer;
828
829				int32 dataOffset = sizeof(dano_variable_size_array)
830					+ field->name_length + 1;
831				dataOffset = pad_to_8(dataOffset);
832				int32 count = *(int32 *)(fieldBuffer + dataOffset);
833				dataOffset += sizeof(int32);
834				ssize_t totalSize = *(ssize_t *)(fieldBuffer + dataOffset);
835				dataOffset += sizeof(ssize_t);
836
837				int32 *endPoints = (int32 *)(fieldBuffer + dataOffset
838					+ totalSize);
839
840				status_t result = B_OK;
841				for (int32 i = 0; i < count; i++) {
842					int32 itemOffset = (i > 0 ? pad_to_8(endPoints[i - 1]) : 0);
843
844					result = into->AddData(field->name, field->type,
845						fieldBuffer + dataOffset + itemOffset,
846						endPoints[i] - itemOffset, false, count);
847
848					if (result != B_OK) {
849						free(fieldBuffer);
850						throw result;
851					}
852				}
853				break;
854			}
855		}
856
857		free(fieldBuffer);
858		offset += sectionHeader.size;
859	}
860
861	return B_OK;
862}
863
864
865} // namespace BPrivate
866