1 /*
2 
3     Copyright (C) 2010-2019 Alois Schloegl <alois.schloegl@ist.ac.at>
4 
5     This file is part of the "BioSig for C/C++" repository
6     (biosig4c++) at http://biosig.sf.net/
7 
8     BioSig is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License
10     as published by the Free Software Foundation; either version 3
11     of the License, or (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 
21 References:
22 [1] TDMS File Format Internal Structure, Publish Date: Jun 23, 2014
23     NI-Tutorial-5696-en.pdf downloaded from ni.com
24 [2] npTDMS package for python
25     https://github.com/adamreeve/npTDMS.git
26 
27  */
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include "../biosig-dev.h"
35 
36 typedef struct {
37 	uint32_t TDMSCONST;
38 	uint32_t toc_mask;
39 	uint32_t version;
40 	uint64_t nextSegmentOffset;
41 	uint64_t rawDataOffset;
42 	uint32_t numObjects;
43 	size_t   data_position;
44 	size_t   size;
45 } __attribute__((aligned(4),packed)) tdms_segment_t;
46 #define LEAD_SIZE 28
47 
48 #define kTocMetaData 		(1L<<1)
49 #define kTocRawData 		(1L<<3)
50 #define kTocDAQmxRawData 	(1L<<7)
51 #define kTocInterleavedData 	(1L<<5)
52 #define kTocBigEndian 		(1L<<6)
53 #define kTocNewObjList		(1L<<2)
54 
55 typedef enum {
56 	tdsTypeVoid,
57 	tdsTypeI8,
58 	tdsTypeI16,
59 	tdsTypeI32,
60 	tdsTypeI64,
61 	tdsTypeU8,
62 	tdsTypeU16,
63 	tdsTypeU32,
64 	tdsTypeU64,
65 	tdsTypeSingleFloat,
66 	tdsTypeDoubleFloat,
67 	tdsTypeExtendedFloat,
68 	tdsTypeSingleFloatWithUnit=0x19,
69 	tdsTypeDoubleFloatWithUnit,
70 	tdsTypeExtendedFloatWithUnit,
71 	tdsTypeString=0x20,
72 	tdsTypeBoolean=0x21,
73 	tdsTypeTimeStamp=0x44,
74 	tdsTypeFixedPoint=0x4F,
75 	tdsTypeComplexSingleFloat=0x08000c,
76 	tdsTypeComplexDoubleFloat=0x10000d,
77 	tdsTypeDAQmxRawData=0xFFFFFFFF
78 } tdsDataType;
79 
80 
81 /*
82 	defines single tdms object
83 */
84 typedef struct {
85 	tdsDataType datatype;
86 	void *value;
87 	char *object_path;
88 } tdms_object_t;
89 
90 // define object store
91 typedef struct {
92 	size_t numObjects;
93 	tdms_object_t *list;
94 } tdms_object_store_t;
95 
96 // find element in object store
find_element(tdms_object_store_t * objList,const char * key)97 ssize_t find_element(tdms_object_store_t *objList, const char *key) {
98 	for (size_t k=0; k < objList->numObjects; k++) {
99 		if (!strcmp(objList->list[k].object_path, key)) return k;
100 	}
101 	return -1;
102 }
103 
104 // add (or replace) element in object store
add_element(tdms_object_store_t * objList,tdms_object_t element)105 void add_element(tdms_object_store_t *objList, tdms_object_t element) {
106 	ssize_t idx = find_element(objList, element.object_path);
107 	if (idx < 0) {
108 		objList->numObjects++;
109 		objList->list = realloc(objList->list, objList->numObjects * sizeof(tdms_object_t));
110 		idx = objList->numObjects - 1;
111 	}
112 	memcpy(objList->list+idx, &element, sizeof(tdms_object_t));
113 }
114 
115 // read number of objects into object store
read_tdms_objects(tdms_object_store_t O,void * ptr,uint32_t numObj)116 void read_tdms_objects(tdms_object_store_t O, void *ptr, uint32_t numObj) {
117 
118 	// this function is under construction
119 	return;
120 
121 	tdms_object_t element;
122 
123 	// skip first 4 bytes, theise contains number of objects
124 			uint32_t numberOfObjects = leu32p(ptr);
125 			ptr += 4;
126 			char *pstr = NULL;
127 			for (uint32_t k = 0; k < numObj; k++) {
128 				uint32_t plen = leu32p(ptr);
129 
130 				pstr  = realloc(pstr,plen+1);
131 				memcpy(pstr, ptr+4, plen);
132 				pstr[plen] = 0;
133 
134 	if (VERBOSE_LEVEL > 8) fprintf(stdout,"%s (line %i): Object %d [%d]<%s>\n",__func__,__LINE__, k, plen, pstr);
135 
136 				ptr += 4 + plen;
137 				uint32_t idx  = leu32p(ptr);
138 				ptr += 4;
139 				uint32_t numberOfProperties = leu32p(ptr);
140 				ptr += 4;
141 
142 	if (VERBOSE_LEVEL>8) fprintf(stdout,"%s (line %i): Object %i/%i <%s> ((%d) has %i properties \n",__func__,__LINE__, k, numberOfObjects, pstr, idx, numberOfProperties);
143 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i/%i path='%s' rawDataIdx=0x%08x\n",__func__,__LINE__,   k, numberOfObjects, pstr, idx);
144 
145 				char *propName = NULL;
146 				char *propVal  = NULL;
147 				for (uint32_t p = 0; p < numberOfProperties; p++) {
148 					// property name
149 					uint32_t plen = leu32p(ptr);
150 					propName  = realloc(propName,plen+1);
151 					memcpy(propName, ptr+4, plen);
152 					propName[plen] = 0;
153 					ptr += 4+plen;
154 
155 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i/%i <%s>\n",__func__,__LINE__, k, p, numberOfProperties, propName);
156 
157 					// property type
158 					uint32_t propType = leu32p(ptr);
159 					ptr += 4;
160 
161 					// property value
162 					int32_t val_i32;
163 					switch (propType) {
164 					case tdsTypeVoid:
165 					case tdsTypeI8:
166 					case tdsTypeI16:
167 
168 					case tdsTypeI32: {
169 						// int32
170 						int32_t val = lei32p(ptr);
171 						ptr += 4;
172 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int32)=%i\n",__func__,__LINE__, k, p,propName,val);
173 						break;
174 						}
175 					case tdsTypeI64:
176 					case tdsTypeU8:
177 					case tdsTypeU16:
178 
179 					case tdsTypeU32: {
180 						// uint32
181 						uint32_t val = leu32p(ptr);
182 						ptr += 4;
183 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int32)=%i\n",__func__,__LINE__, k, p,propName,val);
184 						break;
185 						}
186 
187 					case tdsTypeU64:
188 					case tdsTypeSingleFloat:
189 					case tdsTypeDoubleFloat:
190 					case tdsTypeExtendedFloat:
191 					case tdsTypeSingleFloatWithUnit: // =0x19,
192 					case tdsTypeDoubleFloatWithUnit:
193 					case tdsTypeExtendedFloatWithUnit:
194 
195 					case tdsTypeString: 	// case 0x20:
196 						plen = leu32p(ptr);
197 						propVal  = realloc(propVal,plen+1);
198 						memcpy(propVal,ptr+4,plen);
199 						propVal[plen]=0;
200 						ptr += 4+plen;
201 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(string)=<%s>\n",__func__,__LINE__, k, p,propName, propVal);
202 						break;
203 
204 					case tdsTypeBoolean:
205 
206 					case tdsTypeTimeStamp: // = 0x44
207 						ptr += 4+plen;
208 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(string)=<%s>\n",__func__,__LINE__, k, p,propName, propVal);
209 						break;
210 
211 					case tdsTypeFixedPoint: // 0x4F,
212 					case tdsTypeComplexSingleFloat: // =0x08000c,
213 					case tdsTypeComplexDoubleFloat: // =0x10000d,
214 					case tdsTypeDAQmxRawData: // =0xFFFFFFFF
215 
216 					default:
217 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %d property %d <%s>type=0x%x not supported. Skip %d bytes\n",__func__,__LINE__, k, p,propName,propType, plen);
218 					}
219 				}
220 
221 
222 				switch (idx) {
223 				case 0xffffffff :	// no raw data
224 					break;
225 				case 0x00001269 :	// DAQmx Format Changing scaler
226 					break;
227 				case 0x00001369 :	// DAQmx Digital Line scaler
228 					break;
229 				case 0x00000000 : 	//
230 					;
231 				}
232 			}
233 	if (pstr) free(pstr);
234 }
235 
236 
sopen_tdms_read(HDRTYPE * hdr)237 EXTERN_C void sopen_tdms_read(HDRTYPE* hdr) {
238 /*
239 	this function will be called by the function SOPEN in "biosig.c"
240 
241 	Input:
242 		char* Header	// contains the file content
243 
244 	Output:
245 		HDRTYPE *hdr	// defines the HDR structure accoring to "biosig.h"
246 */
247 
248 	// read whole file into memory
249 	size_t count = hdr->HeadLen;
250 	while (!ifeof(hdr)) {
251 		size_t bufsiz = 2*count;
252 		hdr->AS.Header = (uint8_t*)realloc(hdr->AS.Header, bufsiz+1);
253 		count  += ifread(hdr->AS.Header+count, 1, bufsiz-count, hdr);
254 	}
255 	hdr->AS.Header[count]=0;
256 	hdr->HeadLen = count;
257 
258 	tdms_object_store_t ObjectStore;
259 	ObjectStore.numObjects=0;
260 	ObjectStore.list=NULL;
261 
262 	/*
263 		Specification obtained from http://www.ni.com/white-paper/5696/en
264 		however, there are also TDMS data out there, that is based on an XML header
265 		and a separate binary file. This is somewhat confusing.
266 	 */
267 	fprintf(stderr,"%s (line %i): Format TDMS is very experimental\n",__func__,__LINE__);
268 
269 
270 	/***** Lead In *****/
271 	int currentSegment = 0;
272 	int currentSegmentTableSize = 16;
273 
274 	// top level comprised of a single object that holds file-specific information like author or title.
275 	tdms_segment_t *segTable = malloc(currentSegmentTableSize * sizeof(tdms_segment_t));
276 	memcpy(segTable, hdr->AS.Header, LEAD_SIZE+4);
277 
278 	hdr->FILE.LittleEndian    = !(segTable[0].toc_mask & kTocBigEndian);
279 	if ( (__BYTE_ORDER == __LITTLE_ENDIAN) != !(segTable[0].toc_mask & kTocBigEndian) ) {
280 		segTable[0].version           = bswap_32(segTable[0].version);
281 		segTable[0].nextSegmentOffset = bswap_64(segTable[0].nextSegmentOffset);
282 		segTable[0].rawDataOffset     = bswap_64(segTable[0].rawDataOffset);
283 		segTable[0].numObjects        = bswap_32(segTable[0].numObjects);
284 	}
285 	segTable[currentSegment].data_position = LEAD_SIZE + segTable[currentSegment].rawDataOffset;	// data position
286 	segTable[currentSegment].size = (segTable[currentSegment].toc_mask & kTocRawData) ?
287 					(segTable[currentSegment].nextSegmentOffset - segTable[currentSegment].rawDataOffset) : 0;
288 
289 	switch (segTable[0].version) {
290 	case 4712: hdr->Version = 1.0; break;
291 	case 4713: hdr->Version = 2.0; break;
292 	default:
293 		biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"This version of format TDMS is currently not supported");
294 	}
295 
296 	read_tdms_objects(ObjectStore, hdr->AS.Header+LEAD_SIZE ,segTable[0].numObjects);
297 
298 	size_t pos = LEAD_SIZE + segTable[currentSegment].nextSegmentOffset;
299 
300 	// read table of indices for all segments
301 	while (++currentSegment) {
302 		// manage memory allocation of index table
303 		if (currentSegment >= currentSegmentTableSize) {
304 			currentSegmentTableSize *= 2;
305 			segTable = realloc(segTable, currentSegmentTableSize * sizeof(tdms_segment_t));
306 		}
307 
308 		// read next segment
309 		memcpy(segTable + currentSegment, hdr->AS.Header + pos, LEAD_SIZE+4); 	// copy global, and 1 segment
310 		if ( (__BYTE_ORDER == __LITTLE_ENDIAN) != !(segTable[currentSegment].toc_mask & kTocBigEndian) ) {
311 			segTable[currentSegment].version           = bswap_32(segTable[currentSegment].version);
312 			segTable[currentSegment].nextSegmentOffset = bswap_64(segTable[currentSegment].nextSegmentOffset);
313 			segTable[currentSegment].rawDataOffset     = bswap_64(segTable[currentSegment].rawDataOffset);
314 			segTable[currentSegment].numObjects        = bswap_32(segTable[currentSegment].numObjects);
315 		}
316 		segTable[currentSegment].data_position = pos + LEAD_SIZE + segTable[currentSegment].rawDataOffset;
317 		segTable[currentSegment].size = (segTable[currentSegment].toc_mask & kTocRawData) ?
318 					(segTable[currentSegment].nextSegmentOffset - segTable[currentSegment].rawDataOffset) : -1;
319 
320 		read_tdms_objects(ObjectStore, hdr->AS.Header+pos+LEAD_SIZE, segTable[currentSegment].numObjects);
321 
322 		if (segTable[currentSegment].nextSegmentOffset == (uint64_t)(-1LL)) break;
323 		if (segTable[currentSegment].nextSegmentOffset == 0) break;
324 
325 	if (VERBOSE_LEVEL>8) fprintf(stdout,"%s (line %d): %s\n\tLittleEndian=%d\n\tVersion=%d\n\t@beginSeg %ld\n\t@NextSegment %ld\n\t@rawDataOffset %ld\n",__func__,__LINE__, hdr->AS.Header,hdr->FILE.LittleEndian, segTable[currentSegment].version, pos,  segTable[currentSegment].nextSegmentOffset, segTable[currentSegment].rawDataOffset);
326 
327 		pos += LEAD_SIZE + segTable[currentSegment].nextSegmentOffset;
328 	}
329 
330 	if (VERBOSE_LEVEL > 7) {
331 		// show index table segTable
332 		fprintf(stdout, "#seg\tTMDs\tmask\tver\tnumObj\tnextSegOff\trawDataOffset\tDataPosition\tsize\n");
333 		char tmpstr[8]; tmpstr[4]=0;
334 		for (int k = 0; k < currentSegment; k++) {
335 			memcpy(tmpstr,&segTable[k].TDMSCONST,4);
336 			fprintf(stdout, "%d\t%04s\t0x%04x\t%d\t%d\t%9ld\t%9ld\t%9ld\t%ld\n", k,
337 				tmpstr, segTable[k].toc_mask, segTable[k].version, segTable[k].numObjects,
338 				segTable[k].nextSegmentOffset, segTable[k].rawDataOffset, segTable[k].data_position, segTable[k].size);
339 		}
340 	}
341 
342 	uint64_t nextSegmentOffset = leu64p(hdr->AS.Header+12);
343 	uint64_t RawDataOffset     = leu64p(hdr->AS.Header+20);
344 	pos = 28;
345 
346 	/***** Meta data *****/
347 	if (1) {
348 	//while (pos<RawDataOffset) {
349 			uint32_t numberOfObjects = leu32p(hdr->AS.Header+pos);
350 			pos += 4;
351 			char *pstr=NULL;
352 			for (uint32_t k=0; k < numberOfObjects; k++) {
353 				uint32_t plen = leu32p(hdr->AS.Header+pos);
354 	if (VERBOSE_LEVEL>8) fprintf(stdout,"%s (line %i): %i %i %i\n",__func__,__LINE__, (int)hdr->HeadLen, (int)pos,plen);
355 				pstr  = realloc(pstr,plen+1);
356 				memcpy(pstr,hdr->AS.Header+pos+4,plen);
357 				pstr[plen]=0;
358 
359 				pos += 4+plen;
360 				uint32_t idx  = leu32p(hdr->AS.Header+pos);
361 				pos += 4;
362 				uint32_t numberOfProperties  = leu32p(hdr->AS.Header+pos);
363 				pos += 4;
364 
365 	if (VERBOSE_LEVEL>8) fprintf(stdout,"%s (line %i): Object %i/%i <%s> ((%d) has %i properties \n",
366 				__func__,__LINE__, k,numberOfObjects,pstr,idx,numberOfProperties);
367 
368 				char *propName=NULL;
369 				char *propVal=NULL;
370 				for (uint32_t p=0; p < numberOfProperties; p++) {
371 					// property name
372 					uint32_t plen = leu32p(hdr->AS.Header+pos);
373 					propName  = realloc(propName,plen+1);
374 					memcpy(propName,hdr->AS.Header+pos+4,plen);
375 					propName[plen]=0;
376 					pos += 4+plen;
377 
378 					// property type
379 					uint32_t propType = leu32p(hdr->AS.Header+pos);
380 					pos += 4;
381 
382 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i/%i %d<%s> 0x%08x\n",__func__,__LINE__, k, p, numberOfProperties, plen,propName, propType);
383 
384 
385 					// property value
386 					int32_t val_i32;
387 					switch (propType) {
388 					case tdsTypeVoid:
389 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(void)\n",__func__,__LINE__, k, p,propName);
390 						break;
391 					case tdsTypeI8: {
392 						int8_t val = (int8_t)hdr->AS.Header[pos];
393 						pos += 1;
394 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int8)=%i\n",__func__,__LINE__, k, p,propName,val);
395 						break;
396 						}
397 					case tdsTypeI16: {
398 						int16_t val = lei16p(hdr->AS.Header+pos);
399 						pos += 2;
400 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int16)=%i\n",__func__,__LINE__, k, p,propName,val);
401 						break;
402 						}
403 					case tdsTypeI32: {
404 						// int32
405 						int32_t val = lei32p(hdr->AS.Header+pos);
406 						pos += 4;
407 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int32)=%i\n",__func__,__LINE__, k, p,propName,val);
408 						break;
409 						}
410 					case tdsTypeI64: {
411 						// int64
412 						int64_t val = lei64p(hdr->AS.Header+pos);
413 						pos += 8;
414 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int64)=%ld\n",__func__,__LINE__, k, p,propName,val);
415 						break;
416 						}
417 					case tdsTypeU8: {
418 						uint8_t val = (uint8_t)hdr->AS.Header[pos];
419 						pos += 1;
420 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(uint8)=%i\n",__func__,__LINE__, k, p,propName,val);
421 						break;
422 						}
423 					case tdsTypeU16: {
424 						uint16_t val = leu16p(hdr->AS.Header+pos);
425 						pos += 1;
426 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(uint16)=%i\n",__func__,__LINE__, k, p,propName,val);
427 						break;
428 						}
429 
430 					case tdsTypeU32: {	// case 0x07:
431 						// uint32
432 						uint32_t val = leu32p(hdr->AS.Header+pos);
433 						pos += 4;
434 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(int32)=%i\n",__func__,__LINE__, k, p,propName,val);
435 						break;
436 						}
437 
438 					case tdsTypeU64: {
439 						// uint64
440 						uint64_t val = leu64p(hdr->AS.Header+pos);
441 						pos += 8;
442 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(uint64)=%ld\n",__func__,__LINE__, k, p,propName,val);
443 						break;
444 						}
445 					case tdsTypeSingleFloat: {
446 						float val = lef32p(hdr->AS.Header+pos);
447 						pos += 4;
448 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(float32)=%g\n",__func__,__LINE__, k, p,propName,val);
449 						break;
450 						}
451 					case tdsTypeDoubleFloat: {
452 						// double
453 						double val = lef64p(hdr->AS.Header+pos);
454 						pos += 8;
455 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(float64)=%ld\n",__func__,__LINE__, k, p,propName,val);
456 						break;
457 						}
458 					case tdsTypeExtendedFloat:{
459 						// double
460 						// double val = lef128p(hdr->AS.Header+pos);
461 						pos += 16;
462 						break;
463 						}
464 					case tdsTypeSingleFloatWithUnit: // =0x19,
465 					case tdsTypeDoubleFloatWithUnit:
466 					case tdsTypeExtendedFloatWithUnit:
467 
468 					case tdsTypeString:	// case 0x20:
469 						plen = leu32p(hdr->AS.Header+pos);
470 						propVal  = realloc(propVal,plen+1);
471 						memcpy(propVal,hdr->AS.Header+pos+4,plen);
472 						propVal[plen]=0;
473 						pos += 4+plen;
474 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(string)=<%s>\n",__func__,__LINE__, k, p,propName, propVal);
475 						break;
476 
477 					case tdsTypeBoolean: {
478 						uint8_t val = (uint8_t)hdr->AS.Header[pos];
479 						pos += 1;
480 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(boolean)=%i\n",__func__,__LINE__, k, p,propName,val);
481 						break;
482 						}
483 
484 					case tdsTypeTimeStamp: {	//=0x44
485 						// NI Timestamps according to http://www.ni.com/product-documentation/7900/en/
486 						int64_t time_seconds = lei64p(hdr->AS.Header+pos);
487 						uint64_t time_fraction = leu64p(hdr->AS.Header+pos+8);
488 						gdftime_t T0 = ldexp((time_seconds+ldexp(time_fraction,-64))/(24.0*3600.0)+695422,32);
489 						pos += 16;
490 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i timestamp %ld %.8g \n",__func__,__LINE__, k, time_seconds, ldexp(time_fraction,-64));
491 						break;
492 						}
493 
494 					case tdsTypeFixedPoint: // 0x4F,
495 						pos += 8;
496 						break;
497 
498 					case 0x61:
499 						//plen = leu32p(hdr->AS.Header+pos);
500 						pos += 16;
501 						break;
502 
503 					case tdsTypeComplexSingleFloat: { // =0x08000c,
504 						float val[2];
505 						val[0] = lef32p(hdr->AS.Header+pos);
506 						val[1] = lef32p(hdr->AS.Header+pos);
507 						pos += 16;
508 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(float64)=%g + i%h\n",__func__,__LINE__, k, p, propName, val[0], val[1]);
509 						break;
510 						}
511 					case tdsTypeComplexDoubleFloat: {// =0x10000d,
512 						double val[2];
513 						val[0] = lef64p(hdr->AS.Header+pos);
514 						val[1] = lef64p(hdr->AS.Header+pos);
515 						pos += 16;
516 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i property %i <%s>(float64)=%g + i%h\n",__func__,__LINE__, k, p, propName, val[0], val[1]);
517 						break;
518 						}
519 					case tdsTypeDAQmxRawData: { // =0xFFFFFFFF
520 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %d property %d <%s>type=0x%x not supported. Skip %d bytes\n",__func__,__LINE__, k, p,propName,propType, plen);
521 						break;
522 						}
523 
524 					default:
525 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %d property %d <%s>type=0x%x not supported. Skip %d bytes\n",__func__,__LINE__, k, p,propName,propType, plen);
526 					}
527 				}
528 
529 
530 	if (VERBOSE_LEVEL>6) fprintf(stdout,"%s (line %i): object %i/%i path='%s' rawDataIdx=0x%08x\n",__func__,__LINE__, k, numberOfObjects, pstr, idx);
531 
532 				switch (idx) {
533 				case 0xffffffff :	// no raw data
534 					break;
535 				case 0x00001269 :	// DAQmx Format Changing scaler
536 					break;
537 				case 0x00001369 :	// DAQmx Digital Line scaler
538 					break;
539 				case 0x00000000 : 	//
540 					;
541 				}
542 			}
543 	}
544 
545 	if (segTable) free(segTable);
546 	biosigERROR(hdr,B4C_FORMAT_UNSUPPORTED,"Format TDMS is currently not supported");
547 }
548 
549