1 /*----------------------------------------------------------------------
2  * 19 May 2006: top-level routines to read NIML datasets         [rickr]
3  *              routines to process NI_SURF_DSET datasets
4  *----------------------------------------------------------------------
5 */
6 #include "mrilib.h"
7 #include "suma_objs.h" /* 21 Apr 2020 */
8 /*------------------------------------------------------------*/
9 
10 /* globals to control I/O of niml data      3 Aug 2006 [rickr] */
11 typedef struct {
12     int add_nodes;       /* add to output    (AFNI_NSD_ADD_NODES == Y)  */
13     int debug;           /* debug level      (AFNI_NIML_DEBUG level)    */
14     int to_float;        /* convert to float (AFNI_NSD_TO_FLOAT == Y)   */
15     int write_mode;      /* NI_TEXT_MODE     (AFNI_NIML_TEXT_DATA == Y) */
16 } ni_globals;
17 static ni_globals gni =         /* default values for globals   */
18 {
19         0,                      /* add_nodes                    */
20         0,                      /* debug                        */
21         0,                      /* to_float                     */
22         NI_BINARY_MODE          /* write_mode                   */
23 };
24 
25 static int    nsd_are_sorted_ints(int *, int);
26 static int    loc_append_vals(char **, int *, char *, float, float, int, int);
27 static char * my_strndup(char *, int);
28 static int    nsd_add_colms_range(NI_group *, THD_3dim_dataset *);
29 static char * afni2suma_typestring(int afnitype);
30 static int    nsd_add_colms_type(int, int ctp, NI_group *);
31 static int    nsd_add_sparse_data(NI_group *, THD_3dim_dataset *);
32 static int    nsd_add_str_atr_to_group(char*, char*, THD_datablock*, NI_group*);
33 static int    nsd_add_atr_to_group(char*, char*, THD_datablock*, NI_group*);
34 static int    nsd_fill_index_list(NI_group *, THD_3dim_dataset *);
35 static int    process_NSD_attrs(THD_3dim_dataset *);
36 static int    process_NSD_labeltable(NI_group * ngr, THD_3dim_dataset *dset);
37 static int    process_NSD_group_attrs(NI_group *, THD_3dim_dataset *);
38 static int    process_NSD_index_list(NI_group *, THD_datablock *);
39 static int    process_NSD_sparse_data(NI_group *, THD_3dim_dataset *);
40 
41 static NI_group * nsd_pad_to_node(NI_group * ngr);
42 
43 /* list of AFNI_dataset group attributes to copy along */
44 static char * ni_surf_dset_attrs[] = {
45                 "label",
46                 "domain_parent_idcode",
47                 "geometry_parent_idcode",
48                 "sorted_node_def"
49                                      };
50 
51 #define NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp)             \
52         do { int ii;                                                    \
53              min=max=data[0];  minp=maxp=0;                             \
54              for(ii = 1; ii < len; ii++)                                \
55                 if(data[ii]<min){ min=data[ii]; minp=ii; }              \
56                 else if(data[ii]>max){ max=data[ii]; maxp=ii; }         \
57         } while (0)
58 
59 #define NOTYPE_GETC_MIN_MAX_POSN(data,len,min,minp,max,maxp,phase)      \
60         do { int ii; double dd;                                         \
61              if (phase)  min=max=CARG(data[0]);                         \
62              else   min=max=CABS(data[0]);                              \
63              minp=maxp=0;                                               \
64              for(ii = 1; ii < len; ii++){                               \
65                 if (phase)  dd=CARG(data[ii]);                          \
66                 else   dd=CABS(data[ii]);                               \
67                 if(dd<min){ min=dd; minp=ii; }                          \
68                 else if(dd>max){ max=dd; maxp=ii; }                     \
69              }                                                          \
70         } while (0)
71 
72 /* do not assume the dataset is of type MRI_float   4 Aug 2006 [rickr] */
get_blk_min_max_posn(THD_datablock * blk,int ind,int len,float * fmin,int * imin,float * fmax,int * imax)73 static int get_blk_min_max_posn(THD_datablock * blk, int ind, int len,
74                      float * fmin, int * imin, float * fmax, int * imax)
75 {
76     float ffac = DBLK_BRICK_FACTOR(blk,ind);
77     static int iwarn = 0;
78 
79 ENTRY("get_blk_min_max_posn");
80 
81     if( ffac == 0.0 ) ffac = 1.0;
82 
83     switch(DBLK_BRICK_TYPE(blk, ind)){
84         default:{
85             if (!iwarn) {
86                fprintf(stderr,"** GBMMP, bad or unsupported dtype %d\n"
87                               "Similar warnings will be muted.\n",
88                      DBLK_BRICK_TYPE(blk, ind));
89                ++iwarn;
90             }
91             *fmin = *fmax = 0.0;  *imin = *imax = 0;
92             break;
93         }
94         case MRI_byte:
95         {
96             byte * data = DBLK_ARRAY(blk,ind);
97             byte   min, max;
98             int    minp, maxp;
99             NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp);
100             *fmin = min*ffac;  *fmax = max*ffac;
101             *imin = minp;  *imax = maxp;
102             break;
103         }
104         case MRI_short:
105         {
106             short * data = DBLK_ARRAY(blk,ind);
107             short   min, max;
108             int     minp, maxp;
109             NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp);
110             *fmin = min*ffac;  *fmax = max*ffac;
111             *imin = minp;  *imax = maxp;
112             break;
113         }
114         case MRI_int:
115         {
116             int * data = DBLK_ARRAY(blk,ind);
117             int   min, max;
118             int   minp, maxp;
119             NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp);
120             *fmin = min*ffac;  *fmax = max*ffac;
121             *imin = minp;  *imax = maxp;
122             break;
123         }
124         case MRI_float:
125         {
126             float * data = DBLK_ARRAY(blk,ind);
127             float   min, max;
128             int     minp, maxp;
129             NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp);
130             *fmin = min*ffac;  *fmax = max*ffac;
131             *imin = minp;  *imax = maxp;
132             break;
133         }
134         case MRI_double:
135         {
136             double * data = DBLK_ARRAY(blk,ind);
137             double   min, max;
138             int      minp, maxp;
139             NOTYPE_GET_MIN_MAX_POSN(data,len,min,minp,max,maxp);
140             *fmin = min*ffac;  *fmax = max*ffac;
141             *imin = minp;  *imax = maxp;
142             break;
143         }
144         case MRI_complex:
145         {
146             complex * data = DBLK_ARRAY(blk,ind);
147             double   min, max;
148             int      minp, maxp;
149             NOTYPE_GETC_MIN_MAX_POSN(data,len,min,minp,max,maxp,0);
150             *fmin = min*ffac;  *fmax = max*ffac;
151             *imin = minp;  *imax = maxp;
152             break;
153         }
154     }
155 
156     RETURN(0);
157 }
158 
159 /*----------------------------------------------------------------------*/
160 /*! Open a NIML file as an AFNI dataset.
161 
162     - read as niml
163  *----------------------------------------------------------------------
164 */
THD_open_niml(char * fname)165 THD_3dim_dataset * THD_open_niml( char * fname )
166 {
167     THD_3dim_dataset * dset = NULL;
168     void             * nel;
169     int                smode;
170 
171 ENTRY("THD_open_niml");
172 
173     set_ni_globs_from_env();   /* 3 Aug 2006 [rickr] */
174 
175     nel = read_niml_file(fname, 1);  /* we need data for node_indices */
176     if( !nel ){
177 STATUS("read_niml_file returned NULL") ;
178        RETURN(NULL);
179     }
180 
181     smode = storage_mode_from_niml(nel);
182 STATUSi("smode from niml",smode) ;
183     switch( smode )
184     {
185         case STORAGE_BY_3D:
186 STATUS("STORAGE_BY_3D") ;
187             NI_free_element_data(nel);  /* nuke all data */
188             dset = THD_niml_3D_to_dataset(nel, fname);
189             if(gni.debug) fprintf(stderr,"-d opening 3D dataset '%s'\n",fname);
190             if( !dset && gni.debug )
191                 fprintf(stderr,
192                         "** THD_niml_3D_to_dataset failed on '%s'\n",fname);
193         break;
194 
195         case STORAGE_BY_NIML:
196 STATUS("STORAGE_BY_NIML") ;
197             NI_free_element_data(nel);  /* nuke all data */
198             if(gni.debug)fprintf(stderr,"-d opening NIML dataset '%s'\n",fname);
199             dset = THD_niml_to_dataset(nel, 1); /* no data */
200             if( !dset && gni.debug )
201                 fprintf(stderr,"** THD_niml_to_dataset failed on '%s'\n",fname);
202         break;
203 
204         case STORAGE_BY_NI_SURF_DSET:
205 STATUS("STORAGE_BY_NI_SURF_DSET") ;
206             if(gni.debug)fprintf(stderr,"-d opening NI_SURF_DSET '%s'\n",fname);
207             dset = THD_ni_surf_dset_to_afni(nel, 0); /* no data */
208         break;
209 
210         default:
211 STATUS("Unknown smode :(") ;
212             if( gni.debug )
213                 fprintf(stderr,"** unknown storage mode for '%s'\n", fname);
214         break;
215     }
216 
217     NI_free_element(nel);
218 
219     if( dset )
220     {
221         char * pp = THD_trailname(fname, 0);
222 STATUS("setting prefix etc") ;
223         EDIT_dset_items(dset, ADN_prefix, pp, ADN_none);
224         NI_strncpy(dset->dblk->diskptr->brick_name, fname, THD_MAX_NAME);
225         THD_set_storage_mode(dset, smode);
226         if(gni.debug > 1) fprintf(stderr,"+d success for dataset '%s'\n",fname);
227     }
228 
229     RETURN(dset);
230 }
231 
232 
233 /*----------------------------------------------------------------------*/
234 /*! Open a NIML file as an AFNI dataset.
235 
236     - read as niml
237  *----------------------------------------------------------------------
238 */
THD_load_niml(THD_datablock * dblk)239 int THD_load_niml( THD_datablock * dblk )
240 {
241     void   * nel;
242     char   * fname;
243     int      smode, rv;
244 
245 ENTRY("THD_load_niml");
246 
247     if( !dblk || !dblk->diskptr || !dblk->diskptr->brick_name )
248         RETURN(1);
249 
250     fname = dblk->diskptr->brick_name;
251     smode = dblk->diskptr->storage_mode;
252 
253     if( gni.debug > 1 )
254         fprintf(stderr,"-d THD_load_niml: file %s, smode %d\n", fname, smode);
255 
256     switch( smode )
257     {
258         case STORAGE_BY_3D:
259             if(gni.debug) fprintf(stderr,"-d loading 3D dataset '%s'\n",fname);
260             THD_load_3D(dblk);
261             break;
262         case STORAGE_BY_NIML:
263             if(gni.debug)fprintf(stderr,"-d loading NIML dataset '%s'\n",fname);
264             nel = read_niml_file(fname, 1);  /* read in data now */
265             if( !nel ){
266                 fprintf(stderr,"** failed to load niml file '%s'\n",fname);
267                 RETURN(1);
268             }
269             rv = THD_add_bricks(dblk->parent, nel, NULL);
270             NI_free_element(nel);  /* in any case */
271             if( rv <= 0 ){
272                 fprintf(stderr,"** add bricks returned %d for '%s'\n",rv,fname);
273                 RETURN(1);
274             }
275             else if( rv < dblk->nvals ){
276                 fprintf(stderr,"** loaded only %d bricks for '%s'\n",rv,fname);
277                 RETURN(1);
278             }
279             break;
280         case STORAGE_BY_NI_SURF_DSET:
281             if(gni.debug)fprintf(stderr,"-d loading NI_SURF_DSET '%s'\n",fname);
282             nel = read_niml_file(fname, 1);  /* read in data now */
283             if( !nel ){
284                 fprintf(stderr,"** failed to load NI_SURF_DSET '%s'\n",fname);
285                 RETURN(1);
286             }
287             rv = THD_add_sparse_data(dblk->parent, nel);
288             NI_free_element(nel);  /* in any case */
289             if( rv <= 0 ){
290                 fprintf(stderr,"** add sdata returned %d for '%s'\n",rv,fname);
291                 RETURN(1);
292             }
293             else if( rv < dblk->nvals ){
294                 fprintf(stderr,"** loaded only %d vols for '%s'\n",rv,fname);
295                 RETURN(1);
296             }
297             break;
298         default:
299             fprintf(stderr,"** cannot load NIML dataset '%s' of mode %d\n",
300                     fname, smode);
301             break;
302     }
303 
304     RETURN(0);
305 }
306 
307 
308 /*----------------------------------------------------------------------*/
309 /*! try to deduce the STORAGE mode from the niml data
310 
311     tested modes are:
312         STORAGE_BY_3D
313                 element, AFNI_3D_dataset
314         STORAGE_BY_NIML
315                 group, AFNI_dataset
316                 THD_niml_to_dataset()
317                 ngr->part[i]->name == "VOLUME_DATA"
318         STORAGE_BY_NI_SURF_DSET
319   ----------------------------------------------------------------------*/
storage_mode_from_niml(void * nini)320 int storage_mode_from_niml( void * nini )
321 {
322     int ni_type;
323 
324 ENTRY("storage_mode_from_niml");
325 
326     ni_type = NI_element_type( nini );
327 
328     if( ni_type == NI_ELEMENT_TYPE )                /* can only be 3D */
329     {
330         NI_element * nel = (NI_element *)nini;
331         if( ! strcmp(nel->name, "AFNI_3D_dataset") )
332             RETURN(STORAGE_BY_3D);
333 
334         /* cannot deal with simple niml "3dVol2Surf_dataset", yet */
335 
336         if(gni.debug)
337             fprintf(stderr,"** SMFN: unknown NI_element %s\n", nel->name);
338     }
339     else if( ni_type == NI_GROUP_TYPE )             /* AFNI or SUMA */
340     {
341         NI_group * ng = (NI_group *)nini;
342         char     * atr;
343         if( ! strcmp(ng->name, "AFNI_dataset") )
344         {
345             atr = NI_get_attribute(ng, "dset_type");
346             if( atr &&
347                 ( !strcmp(atr, "Node_Bucket") ||
348                   !strcmp(atr, "Node_ROI")    ||
349                   !strcmp(atr, "Node_Label")  ||
350                   !strcmp(atr, "Voxel_Bucket")||    /* 5 Aug 2015 [rickr] */
351                   !strcmp(atr, "Graph_Bucket"))   ) /* then SUMA DSET */
352                 RETURN(STORAGE_BY_NI_SURF_DSET);
353             RETURN(STORAGE_BY_NIML);                 /* else assume AFNI */
354         } else if ( ! strcmp(ng->name, "bundle") )
355         {
356             RETURN(STORAGE_BY_NI_TRACT);
357         }
358         else if(gni.debug)
359             fprintf(stderr,"** SMFN: NI_group, but bad name '%s'\n",ng->name);
360     }
361     else if(gni.debug) fprintf(stderr,"** SMFN: bad ni_type %d\n",ni_type);
362 
363     RETURN(STORAGE_UNDEFINED);
364 }
365 
366 #undef  MY_BUFSIZE
367 #define MY_BUFSIZE (1024*1024*16)  /* 21 Nov 2007 */
368 
369 /*! inhale any NIML data within a file */
read_niml_file(char * fname,int get_data)370 void * read_niml_file( char * fname, int get_data )
371 {
372     NI_stream    ns;
373     NI_element * nel;
374     char       * nname;
375     int read_head_only_state=0;
376 ENTRY("read_niml_file");
377 STATUSs("filename",fname) ;
378 
379     if( !fname || !*fname )
380     {
381         if(gni.debug) fprintf(stderr,"** read_niml_file: empty filename\n");
382         RETURN(NULL);
383     }
384 
385     /* set the stream name */
386     nname = (char *)calloc(sizeof(char), strlen(fname)+10);
387     strcpy(nname, "file:");
388     strcat(nname, fname);
389 
390     /* open the stream */
391     ns = NI_stream_open(nname, "r");
392     free(nname);
393     if( !ns )
394     {
395         if(gni.debug)fprintf(stderr,"** RNF: failed to open file '%s'\n",fname);
396 STATUS("read_niml_file failed to open") ;
397         RETURN(NULL);
398     }
399 
400     if( get_data && NI_stream_getbufsize(ns) < MY_BUFSIZE ) /* 21 Nov 2007: RWCox */
401       NI_stream_setbufsize(ns,MY_BUFSIZE) ;
402 
403     /* read the file */
404     read_head_only_state = NI_get_read_header_only();
405     NI_skip_procins(1);  NI_set_read_header_only(!get_data);
406     nel = NI_read_element(ns, 333);
407     NI_skip_procins(0);  NI_set_read_header_only(read_head_only_state);
408 if( nel == NULL ) STATUS("failed to read NIML data") ;
409 
410     /* close the stream */
411     NI_stream_close(ns);
412 
413     /* possibly check the results */
414     if(gni.debug && !nel) fprintf(stderr,"** RNF: failed to read '%s'\n",fname);
415     else if(gni.debug>1)  fprintf(stderr,"+d success for niml file %s\n",fname);
416 
417     RETURN(nel);
418 }
419 
420 
421 /*! write NIML data to a file */
write_niml_file(char * fname,NI_group * ngr)422 int write_niml_file( char * fname, NI_group * ngr )
423 {
424     NI_stream   ns;
425     char      * str_name;
426 
427 ENTRY("write_niml_file");
428 
429     if( !fname || !ngr ){
430         fprintf(stderr,"** write_niml_file: empty parameters\n");
431         RETURN(1);
432     }
433 
434     /* set the output stream name (5 for 'file:' and 1 for '\0') */
435     str_name = (char *)malloc((strlen(fname)+6) * sizeof(char) );
436     strcpy(str_name, "file:");
437     strcat(str_name, fname);
438 
439     ns = NI_stream_open(str_name, "w");
440     free(str_name); /* lose this either way */
441 
442     if( !ns ){
443         fprintf(stderr,"** cannot open NIML stream for file '%s'\n", fname);
444         RETURN(1);
445     }
446 
447     if( NI_write_element( ns, ngr, NI_TEXT_MODE ) <= 0 ){
448         fprintf(stderr,"** failed to write NIML output file '%s'\n", fname);
449         RETURN(1);
450     }
451 
452     NI_stream_close(ns); /* close the stream */
453 
454     RETURN(0);
455 }
456 
457 /*! write NIML data to a stream
458  *  (same as write_niml_file, but do not convert file to a stream)
459  *                                             [10 Oct 2019 rickr] */
write_niml_stream(char * stream,NI_group * ngr)460 int write_niml_stream( char * stream, NI_group * ngr )
461 {
462     NI_stream   ns;
463 
464 ENTRY("write_niml_stream");
465 
466     if( !stream || !ngr ){
467         fprintf(stderr,"** write_niml_stream: empty parameters\n");
468         RETURN(1);
469     }
470 
471     ns = NI_stream_open(stream, "w");
472 
473     if( !ns ){
474         fprintf(stderr,"** cannot open NIML stream '%s'\n", stream);
475         RETURN(1);
476     }
477 
478     if( NI_write_element( ns, ngr, NI_TEXT_MODE ) <= 0 ){
479         fprintf(stderr,"** failed to write NIML output stream '%s'\n", stream);
480         RETURN(1);
481     }
482 
483     NI_stream_close(ns); /* close the stream */
484 
485     RETURN(0);
486 }
487 
488 
489 /*! Write out a NIML dataset (3D, NIML, NI_SURF_DSET).
490     Return True or False, based on success.
491 */
THD_write_niml(THD_3dim_dataset * dset,int write_data)492 RwcBoolean THD_write_niml( THD_3dim_dataset * dset, int write_data )
493 {
494     NI_group * ngr;
495     char     * prefix, * outfile;
496     int        smode, rv;
497 ENTRY("THD_write_niml");
498 
499     set_ni_globs_from_env();
500     prefix = DSET_PREFIX(dset);
501     outfile = DSET_HEADNAME(dset);
502 
503     if( !outfile ) {
504         if(gni.debug) fprintf(stderr,"** THD_write_niml: no dset headname\n");
505         RETURN(False);
506     }
507 
508     smode = storage_mode_from_filename(outfile);
509     if( gni.debug )
510         fprintf(stderr,"-d THD_write_niml: file %s, smode %d\n",outfile,smode);
511 
512     switch(smode)
513     {
514         case STORAGE_BY_3D:
515             THD_write_3D(NULL, NULL, dset);
516             break;
517 
518         case STORAGE_BY_NIML:
519             if( write_data ) ngr = THD_dataset_to_niml(dset);
520             else             ngr = THD_nimlize_dsetatr(dset);
521             if( !ngr ){
522                 fprintf(stderr,"** failed dset to niml on '%s'\n", outfile);
523                 RETURN(False);
524             }
525             NI_rename_group(ngr, "AFNI_dataset");
526             NI_set_attribute(ngr, "self_prefix", prefix);
527             rv = write_niml_file(outfile, ngr);
528             NI_free_element(ngr); /* either way */
529             if( rv ){
530                 fprintf(stderr,"** write_niml_file failed for '%s'\n",outfile);
531                 RETURN(False);
532             }
533             break;
534 
535         case STORAGE_BY_NI_SURF_DSET:
536             ngr = THD_dset_to_ni_surf_dset(dset, write_data);
537             if( !ngr )
538             {
539                 fprintf(stderr,"** failed dset to ni_SD on '%s'\n",outfile);
540                 RETURN(False);
541             }
542             rv = write_niml_file(outfile, ngr);
543             NI_free_element(ngr); /* either way */
544             if( rv ){
545                 fprintf(stderr,"** write_niml_file failed for '%s'\n",outfile);
546                 RETURN(False);
547             }
548             break;
549 
550         default:
551             fprintf(stderr,"** invalid storage mode %d to write '%s'\n",
552                     smode, outfile);
553             RETURN(False);
554             break;
555     }
556 
557     RETURN(True);
558 }
559 
560 /*! Write a NIML heaer (3D, NIML, NI_SURF_DSET) to a text stream
561     if by_smode: allow for this to vary based on storage mode
562                  (which will generally require adding some code...)
563     Return True or False, based on success.     [10 Oct 2019 rickr]
564 */
THD_write_niml_to_stream(THD_3dim_dataset * dset,char * stream,int by_smode)565 RwcBoolean THD_write_niml_to_stream( THD_3dim_dataset * dset, char * stream,
566                                   int by_smode )
567 {
568     NI_group * ngr;
569     char     * prefix;
570     char     * func = "THD_write_niml_to_stream";
571     int        smode, rv;
572 
573 ENTRY("THD_write_niml_to_stream");
574 
575     set_ni_globs_from_env();
576 
577     if( ! ISVALID_DSET(dset) || ! stream ) {
578         fprintf(stderr,"-d %s: invaliad dset %d or stream %s\n",
579                 func, ISVALID_DSET(dset), stream);
580         RETURN(False);
581     }
582 
583     prefix = DSET_PREFIX(dset);
584     if( !prefix )
585         prefix = "PIZZA_314159";
586 
587     if( by_smode )
588        smode = storage_mode_from_filename(prefix);
589     else
590        smode = STORAGE_BY_NIML;
591 
592     if( gni.debug )
593         fprintf(stderr,"-d THD_write_niml_to_stream: %s, smode %d\n",
594                 stream, smode);
595 
596     switch(smode)
597     {
598         case STORAGE_BY_NI_SURF_DSET:
599             ngr = THD_dset_to_ni_surf_dset(dset, 0);
600             if( !ngr ) {
601                 fprintf(stderr,"** failed dset to ni_SD on '%s'\n",prefix);
602                 RETURN(False);
603             }
604             break;
605 
606         case STORAGE_BY_NIML:
607         default:
608             ngr = THD_nimlize_dsetatr(dset);
609             if( !ngr ){
610                 fprintf(stderr,"** failed dset to niml on '%s'\n",prefix);
611                 RETURN(False);
612             }
613             break;
614      }
615 
616      NI_rename_group(ngr, "AFNI_dataset");
617      NI_set_attribute(ngr, "self_prefix", prefix);
618 
619      /* and write to the NI_stream */
620      rv = write_niml_stream(stream, ngr);
621 
622      NI_free_element(ngr); /* either way */
623      if( rv ){
624          fprintf(stderr,"** write niml stream failed for '%s'\n", stream);
625          RETURN(False);
626      }
627 
628     RETURN(True);
629 }
630 
631 
632 /*! Convert a string attribute (in NI_SURF_DSET form) to a list of
633     strings.  Each element of the list will be allocated here.
634 
635     The NI_SURF_DSET form for strings uses ';' to separate them.
636 
637         slist           pointer to string list - will be allocated
638         llen            length of list to be set
639         atr             string attribute to get list from
640                         (if NULL, strings will get default "#%d")
641 
642     return the number of strings found
643 */
nsd_string_atr_to_slist(char *** slist,int llen,ATR_string * atr)644 int nsd_string_atr_to_slist(char *** slist, int llen, ATR_string * atr)
645 {
646     int sind, posn, prev, copy_len;
647     int found = 0;
648 
649 ENTRY("nsd_string_atr_to_slist");
650 
651     if(!slist || llen < 1)
652     {
653         fprintf(stderr,"** NSATS: bad params\n");
654         RETURN(0);
655     }
656 
657     if( !atr )      /* we're outta here */
658     {
659         *slist = NULL;
660         if(gni.debug > 1) fprintf(stderr,"NSATS: no attribute to parse\n");
661         RETURN(0);
662     }
663 
664     if(gni.debug > 2)
665     {
666         if( atr ) fprintf(stderr,"+d getting string attrs from %s\n",atr->name);
667         else      fprintf(stderr,"+d setting default strings\n");
668     }
669 
670     /* allocate memory for the list */
671     *slist = (char **)malloc(llen * sizeof(char *));
672 
673     posn = -1;
674     for( sind = 0; sind < llen && posn < atr->nch; sind++ )
675     {
676         /* find end of next string (end with nul or ';') */
677         prev = posn;
678         for( posn = prev+1;
679              posn < atr->nch && atr->ch[posn] && atr->ch[posn] != ';' ;
680              posn++ )
681             ;  /* just search */
682 
683         if( posn > prev+1 ) /* then we have found some */
684         {
685             copy_len = posn - prev - 1;
686             if( copy_len > THD_MAX_LABEL-1 ) copy_len = THD_MAX_LABEL-1;
687             (*slist)[sind] = my_strndup(atr->ch+prev+1, copy_len);
688             found++;
689 
690             if(gni.debug>1)
691                 fprintf(stderr,"-d string %d = %s\n",sind,(*slist)[sind]);
692         }
693         else
694         {
695             (*slist)[sind] = (char *)malloc(10 * sizeof(char));
696             sprintf((*slist)[sind], "#%d", sind);
697         }
698     }
699 
700     for( ; sind < llen; sind++ )
701     {
702         (*slist)[sind] = (char *)malloc(10 * sizeof(char));
703         sprintf((*slist)[sind], "#%d", sind);
704     }
705 
706     if(gni.debug>1) fprintf(stderr,"-d found %d of %d strings\n", found, llen);
707 
708     RETURN(found);
709 }
710 
711 /*! create an AFNI dataset from a NI_SURF_DSET group */
THD_ni_surf_dset_to_afni(NI_group * ngr,int read_data)712 THD_3dim_dataset * THD_ni_surf_dset_to_afni(NI_group * ngr, int read_data)
713 {
714     THD_3dim_dataset * dset = NULL;
715     int                rv;
716 
717 ENTRY("THD_ni_surf_dset_to_afni");
718 
719     if( !ngr || NI_element_type(ngr) != NI_GROUP_TYPE ) RETURN(NULL);
720 
721     dset = EDIT_empty_copy(NULL);
722 
723     THD_dblkatr_from_niml(ngr, dset->dblk); /* store NIML attributes in dblk */
724 
725     rv = process_NSD_index_list(ngr, dset->dblk);      /* INDEX_LIST attr   */
726     if( !rv ) rv = process_NSD_sparse_data(ngr, dset); /* SPARSE_DATA attr  */
727     if( !rv ) rv = process_NSD_group_attrs(ngr, dset); /* store group attrs */
728     if( !rv ) rv = process_NSD_attrs(dset);            /* apply other attrs */
729     if( !rv ) rv = process_NSD_labeltable(ngr, dset);  /* get AFNI_labeltable */
730     RETURN(dset);
731 }
732 
733 /*
734    Change AFNI_Labeltable, if any, to VALUE_LABEL_DTABLE
735    This is a lossy conversion, colors are not preserved in DTABLE
736 */
737 
process_NSD_labeltable(NI_group * ngr,THD_3dim_dataset * dset)738 static int process_NSD_labeltable(NI_group * ngr, THD_3dim_dataset *dset)
739 {
740     NI_element     * nel = NULL, * tel;
741     void           ** elist = NULL;
742     int            ind, ncols, length, c, ii = 0;
743     NI_group       * ltg = NULL;
744     float          * rgba = NULL;
745     char           * cp, *label_table=NULL, sval[32]={""};
746     Dtable         *dt = NULL;
747 
748 ENTRY("process_NSD_labeltable");
749 
750     if( !ngr || !ISVALID_DSET(dset) )
751     {
752         if(gni.debug) fprintf(stderr,"** PNSDLT: bad params\n");
753         RETURN(1);
754     }
755 
756     /* find the SPARSE_DATA element of the AFNI_labeltable group */
757     ind = NI_search_group_shallow(ngr, "AFNI_labeltable", &elist);
758     if(ind > 0){ ltg = (NI_group *)elist[0]; NI_free(elist); elist = NULL; }
759     if( !ltg ) { /* not an error, but we are done */
760         if( gni.debug > 0) fprintf(stderr,"-- NSDG: no AFNI_labeltable\n");
761         RETURN(0);
762     }
763 
764     ind = NI_search_group_shallow(ltg, "SPARSE_DATA", &elist);
765     if(ind > 0){ nel = (NI_element *)elist[0]; NI_free(elist); elist = NULL; }
766     if( !nel ) { /* probably an error */
767         if(gni.debug > 0)
768             fprintf(stderr,"-- NSDG: AFNI_labeltable: missing SPARSE_DATA\n");
769         RETURN(0);
770     }
771 
772     ncols = nel->vec_num;
773     length = nel->vec_len;
774 
775     /* verify either 2 or 6 columns */
776     if( ncols != 2 && ncols != 6 ) {
777         fprintf(stderr,"** NIML ALT SData, bad ncols = %d\n", ncols);
778         RETURN(1);
779     }
780     if( length <= 0 ) {
781         fprintf(stderr,"** NIML ALT SData, bad length = %d\n", length);
782         RETURN(1);
783     }
784 
785     /* verify COLMS_LABS, if present (2 or 6 columns) */
786     tel = NI_find_element_by_aname(ltg,"AFNI_atr","atr_name","COLMS_LABS");
787     if( tel ) { /* then verify */
788         cp = ((char **)tel->vec[0])[0];
789         if( ncols == 6 ) {
790             if( strcmp(cp, "R;G;B;A;key;name") )
791                fprintf(stderr,"** have ALT CLABS '%s', should be '%s'\n",
792                               cp, "R;G;B;A;key;name");
793         } else if( ncols == 2 ) {
794             if( strcmp(cp, "key;name") )
795                fprintf(stderr,"** have ALT CLABS '%s', should be '%s'\n",
796                               cp, "key;name");
797         }
798         if(gni.debug>0) fprintf(stderr,"-- SData len %d, COLMS_LABS[%d]='%s'\n",
799                                length,ncols,cp);
800     }
801 
802     /* verify types: require 4*float,int,String or just int,String */
803 
804     ind = 0;
805     if( ncols == 6 ) {
806         if( nel->vec_typ[ind  ] != NI_FLOAT32 ||
807             nel->vec_typ[ind+1] != NI_FLOAT32 ||
808             nel->vec_typ[ind+2] != NI_FLOAT32 ||
809             nel->vec_typ[ind+3] != NI_FLOAT32 ) {
810             fprintf(stderr,"** bad types for NIML ALT RGBA\n");
811             RETURN(1);
812         }
813         ind += 4;
814     }
815     if( nel->vec_typ[ind] != NI_INT || nel->vec_typ[ind+1] != NI_STRING ) {
816         fprintf(stderr,"** bad types for NIML ALT key;name\n");
817         RETURN(1);
818     }
819 
820     /* if there are colors, get them */
821     if( ncols == 6 ){ if(gni.debug) INFO_message("Colors will be ignored"); }
822     else            {  rgba = NULL; }
823 
824     /* convert to label table */
825     ii = rint(sqrt(2*length+1.0l)) ;
826     if( ii < 7 ) ii = 7 ; else if( ii%2 == 0 ) ii++ ;
827 
828     /* make table, insert strings */
829     dt = new_Dtable( ii ) ;
830     for( ii=0 ; ii < length ; ii++ ) {
831       sprintf(sval,"%d", ((int *)nel->vec[ind])[ii]);
832       addto_Dtable( sval , ((char **)nel->vec[ind+1])[ii] , dt ) ;
833     }
834     label_table = Dtable_to_nimlstring(dt, "VALUE_LABEL_DTABLE");
835     destroy_Dtable(dt); dt = NULL;
836     THD_set_string_atr( dset->dblk ,
837                         "VALUE_LABEL_DTABLE" , label_table ) ;
838     free(label_table); label_table = NULL;
839 
840    RETURN(0);
841 }
842 
843 
844 /* process any INDEX_LIST attribute, setting nnodes and node_list in
845    the datablock (going from NIML to AFNI)
846 */
process_NSD_index_list(NI_group * ngr,THD_datablock * dblk)847 static int process_NSD_index_list(NI_group * ngr, THD_datablock * dblk )
848 {
849     NI_element  * nel = NULL;
850     void       ** elist = NULL;
851     int           byte_order, nvals;
852 
853 ENTRY("process_NSD_index_list");
854 
855     if( !ngr || !ISVALID_DBLK(dblk) )
856     {
857         if(gni.debug) fprintf(stderr,"** PNSDIL: bad params\n");
858         RETURN(1);
859     }
860 
861     /* grab the any INDEX_LIST element (should be only one) */
862     nvals = NI_search_group_shallow(ngr, "INDEX_LIST", &elist);
863     if( nvals <= 0 )
864     {
865         if( gni.debug ) fprintf(stderr,"-d no INDEX_LIST element\n");
866         RETURN(0);
867     }
868 
869     nel = (NI_element *)elist[0];       /* grab first element (only!?)  */
870     NI_free(elist);                     /* and we're done with the list */
871 
872     /* make sure this is a single array of ints, else ignore */
873     if( !nel || nel->vec_num != 1 || nel->vec_len <= 0 )
874     {
875         if(gni.debug) fprintf(stderr,"-- empty INDEX_LIST element\n");
876         RETURN(0);
877     }
878 
879     if( nel->vec_typ[0] != NI_INT )
880     {
881         if(gni.debug)
882             fprintf(stderr,"** INDEX_LIST has bad type %d\n",nel->vec_typ[0]);
883         RETURN(0);
884     }
885 
886     /* note the byte order, in case we need to swap the index bytes */
887     byte_order = NI_get_byte_order(nel);
888     if( byte_order == NATIVE_ORDER ) byte_order = mri_short_order();
889 
890     /* and copy the node list */
891     dblk->nnodes = nel->vec_len;
892     dblk->node_list = (int *)RwcMalloc(dblk->nnodes * sizeof(int));
893     memcpy(dblk->node_list, nel->vec[0], dblk->nnodes*sizeof(int));
894     if( byte_order != mri_short_order() )
895     {
896         if(gni.debug > 1) fprintf(stderr,"+d swapping bytes in node list\n");
897         nifti_swap_4bytes(dblk->nnodes, dblk->node_list);
898     }
899 
900     if(gni.debug) fprintf(stderr,"+d have node list of len, %d\n",dblk->nnodes);
901 
902     RETURN(0);
903 }
904 
905 /* initialize the datablock using the SPARSE_DATA NI_SURF_DSET element
906    and possibly the INDEX_LIST element
907 
908    (going from NIML to AFNI)
909 */
process_NSD_sparse_data(NI_group * ngr,THD_3dim_dataset * dset)910 static int process_NSD_sparse_data(NI_group * ngr, THD_3dim_dataset * dset )
911 {
912     THD_datablock * blk;
913     THD_diskptr   * dkptr;
914     THD_ivec3       nxyz;
915     NI_element    * nel = NULL;
916     void         ** elist = NULL;
917     float           tr;
918     char          * rhs;
919     int             ind, ncomp, tpafni;
920 
921 ENTRY("process_NSD_sparse_data");
922 
923     if( !ngr || !ISVALID_DSET(dset) )
924     {
925         if(gni.debug) fprintf(stderr,"** PNSDSD: bad params\n");
926         RETURN(1);
927     }
928     blk   = dset->dblk;
929     dkptr = blk->diskptr;
930 
931     /* grab the first SPARSE_DATA element (should be only) */
932     ind = NI_search_group_shallow(ngr, "SPARSE_DATA", &elist);
933     if( ind > 0 ) { nel = (NI_element *)elist[0]; NI_free(elist); }
934 
935     if(!nel || nel->vec_num <= 0 || nel->vec_len <= 0)
936     {
937         if(gni.debug) fprintf(stderr,"** missing SPARSE_DATA element\n");
938         RETURN(1);
939     }
940 
941     /* so nel points to the SPARSE_DATA element */
942 
943     if(gni.debug>1)fprintf(stderr,"-d found SPARSE_DATA in NI_SURF_DSET\n");
944 
945     /* if we have use ni_form to set the byte_order */
946     dkptr->byte_order = NI_get_byte_order(nel);
947     if( dkptr->byte_order == NATIVE_ORDER )
948         dkptr->byte_order = mri_short_order();
949 
950     if(gni.debug>1)
951         fprintf(stderr,"+d using byte order %s\n",
952                 BYTE_ORDER_STRING(dkptr->byte_order));
953 
954     /* verify that we have "data_type=Node_Bucket_data"
955        acceptable should also be Node_ROI_data but on
956        output, all will become Node_Bucket_data ZSS: Dec 07  */
957     rhs = NI_get_attribute(nel, "data_type");
958     /* added Voxel_Bucket_data, though it is not properly handled
959        (but headed in the right direction)     5 Aug 2015 [rickr] */
960     if( !rhs ||
961          (  strcmp(rhs, "Node_Bucket_data")  &&
962             strcmp(rhs, "Node_ROI_data")     &&
963             strcmp(rhs, "Node_Label_data")   &&
964             strcmp(rhs, "Voxel_Bucket_data") &&
965             strcmp(rhs, "Graph_Bucket_data")   ) )
966     {
967         if(gni.debug)
968           fprintf(stderr,"** SPARSE_DATA without data_type "
969              "Node_Bucket_data or Node_ROI_data or Node_Label_data "
970              "or Node_Bucket_data\n");
971         RETURN(1);
972     }
973     if(gni.debug && !strcmp(rhs, "Graph_Bucket_data"))
974       fprintf(stderr,"+d Reading graph data but output will not retain type\n");
975 
976     if(!strcmp(rhs, "Voxel_Bucket_data"))
977       fprintf(stderr,"** Voxel_Bucket dataset will be collapsed to 1D...\n");
978 
979     /* if we have a node list, verify that it matches the data in length */
980     if( blk->nnodes > 0 && (blk->nnodes != nel->vec_len) )
981     {
982         if( blk->nnodes != nel->vec_len )
983         {
984             fprintf(stderr,"** node list len (%d) != data len (%d)\n",
985                 blk->nnodes, nel->vec_len);
986             RETURN(1);
987         }
988         if(gni.debug > 1) fprintf(stderr,"-d length of nodes and data match\n");
989     }
990 
991     /* COMPLEX is OK, if all sub-bricks are of the same type */
992     for( ncomp = 0, ind = 0; ind < nel->vec_num; ind++ ) {
993       if (nel->vec_typ[ind] == NI_COMPLEX) ++ncomp;
994     }
995     if (ncomp == nel->vec_num) tpafni = MRI_complex;
996     else tpafni = MRI_float;
997 
998     /* node index list is now in INDEX_LIST attribute  29 Aug 2006 [rickr] */
999     for( ind = 0; tpafni == MRI_float && ind < nel->vec_num; ind++ )
1000         if( nel->vec_typ[ind] != NI_FLOAT &&
1001             nel->vec_typ[ind] != NI_INT )
1002         {
1003             fprintf(stderr,"** NI_SURF_DSET has has invalid type %d\n",
1004                     nel->vec_typ[ind]);
1005             RETURN(1);
1006         }
1007 
1008     /* set nxyz */
1009     nxyz.ijk[0] = nel->vec_len;   nxyz.ijk[1] = nxyz.ijk[2] = 1;
1010 
1011     if(gni.debug > 1)
1012         fprintf(stderr,
1013                 "+d setting datum, nxyz, nvals to %s, %d, %d\n",
1014                 tpafni == MRI_float ? "float":"complex" ,
1015                 nel->vec_len, nel->vec_num);
1016 
1017     EDIT_dset_items(dset,
1018                         ADN_datum_all,  tpafni,
1019                         ADN_nxyz,       nxyz,
1020                         ADN_nvals,      nel->vec_num,
1021                      ADN_none );
1022 
1023     /*--- check for a ni_timestep attribute ---*/
1024     rhs = NI_get_attribute(nel, "ni_timestep");
1025     if( rhs && nel->vec_num > 1 )  /* then make time dependant */
1026     {
1027         tr = strtod(rhs, NULL);
1028         if(gni.debug > 1) fprintf(stderr,"-d found TR = %f\n", tr);
1029         if( tr <= 0.0 ) tr = 1.0;   /* just be safe */
1030         EDIT_dset_items(dset,
1031                             ADN_func_type, ANAT_EPI_TYPE,
1032                             ADN_ntt      , nel->vec_num,
1033                             ADN_ttdel    , tr,
1034                             ADN_tunits   , UNITS_SEC_TYPE,
1035                         ADN_none);
1036     }
1037 
1038     RETURN(0);
1039 }
1040 
1041 /* apply known NI_SURF_DSET attributes  (niml -> afni) */
process_NSD_attrs(THD_3dim_dataset * dset)1042 static int process_NSD_attrs(THD_3dim_dataset * dset)
1043 {
1044     THD_datablock * blk;
1045     THD_diskptr   * dkptr;
1046     THD_ivec3       ori;
1047     THD_fvec3       del, org;
1048     ATR_string    * atr_str;
1049     int             ind, nvals;
1050 
1051 ENTRY("process_NSD_attrs");
1052 
1053     /* orientation, grid and origin are meaningless, but apply defaults */
1054 
1055     /* set orientation as RAI */
1056     ori.ijk[0] = ORI_R2L_TYPE;
1057     ori.ijk[1] = ORI_A2P_TYPE;
1058     ori.ijk[2] = ORI_I2S_TYPE;
1059 
1060     /* set grid spacings to 1 mm, and origin to 0.0 */
1061     del.xyz[0] = del.xyz[1] = del.xyz[2] = 1.0;
1062     org.xyz[0] = org.xyz[1] = org.xyz[2] = 0.0;
1063 
1064     blk = dset->dblk;
1065     dkptr = blk->diskptr;
1066 
1067     EDIT_dset_items(dset,
1068                         ADN_xyzdel,      del,
1069                         ADN_xyzorg,      org,
1070                         ADN_xyzorient,   ori,
1071                         ADN_malloc_type, DATABLOCK_MEM_MALLOC,
1072                         ADN_type,        HEAD_ANAT_TYPE,
1073                     ADN_none);
1074 
1075     /* if not time dependant, set as bucket */
1076     if( ! dset->taxis || dset->taxis->ntt <= 1)
1077         EDIT_dset_items(dset, ADN_func_type,  ANAT_BUCK_TYPE, ADN_none);
1078 
1079     dkptr->storage_mode = STORAGE_BY_NI_SURF_DSET;
1080 
1081     /* now process some attributes */
1082 
1083     nvals = blk->nvals;
1084 
1085     /*--- init and fill any column labels ---*/
1086     atr_str = THD_find_string_atr(blk, "COLMS_LABS");
1087     if( !atr_str ) atr_str = THD_find_string_atr(blk, ATRNAME_BRICK_LABS);
1088     nsd_string_atr_to_slist(&blk->brick_lab, nvals, atr_str);
1089 
1090     /*--- init and fill any statistic symbols ---*/
1091     atr_str = THD_find_string_atr(blk , "COLMS_STATSYM");
1092     if( !atr_str ) atr_str = THD_find_string_atr(blk , "BRICK_STATSYM");
1093     if(  atr_str ) /* only do this if we have some codes */
1094     {
1095         char **sar ; int scode,np ; float parm[3];
1096         np = nsd_string_atr_to_slist(&sar, nvals, atr_str);
1097         if( sar )
1098         {
1099             for( ind = 0; ind < nvals; ind++ )
1100             {
1101                 NI_stat_decode(sar[ind], &scode, parm,parm+1,parm+2 );
1102                 if(scode >= AFNI_FIRST_STATCODE && scode <= AFNI_LAST_STATCODE)
1103                 {
1104                     np = NI_stat_numparam(scode);
1105                     THD_store_datablock_stataux(blk, ind, scode, np, parm);
1106                 }
1107                 free(sar[ind]);
1108             }
1109             free(sar);
1110         }
1111     }
1112 
1113     RETURN(0);
1114 }
1115 
1116 /* store any attribute in the ni_surf_dset_attrs[] list
1117 
1118     - for each attr, if it has a valid rhs, store it
1119     - the self_idcode should be stored separately (as it
1120         will not be copied to a new dataset)
1121 
1122     return 0 on success
1123 */
process_NSD_group_attrs(NI_group * ngr,THD_3dim_dataset * dset)1124 static int process_NSD_group_attrs(NI_group * ngr, THD_3dim_dataset * dset )
1125 {
1126     char       ** aname;
1127     char        * rhs;
1128     int         ac, natr;
1129 
1130 ENTRY("process_NSD_group_attrs");
1131 
1132     natr = sizeof(ni_surf_dset_attrs) / sizeof(char *);
1133 
1134     for( ac = 0, aname = ni_surf_dset_attrs; ac < natr; ac++, aname++ )
1135     {
1136         rhs = NI_get_attribute(ngr, *aname);
1137         if( rhs && *rhs )
1138         {
1139             if(gni.debug>1)
1140                 fprintf(stderr,"-d found group attr %s = %s\n",*aname,rhs);
1141             THD_set_string_atr(dset->dblk, *aname, rhs);
1142         }
1143         else if(gni.debug>2)
1144                 fprintf(stderr,"-d did not find group attr %s\n",*aname);
1145     }
1146 
1147     /* idcode: from nel:self_idcode or ni_idcode */
1148     rhs = NI_get_attribute(ngr, "self_idcode");
1149     if( !rhs ) rhs = NI_get_attribute(ngr, "ni_idcode");
1150     if(  rhs ) NI_strncpy(dset->idcode.str, rhs, MCW_IDSIZE);
1151     /* else, keep the one from EDIT_empty_copy() */
1152 
1153     RETURN(0);
1154 }
1155 
1156 /*------------------------------------------------------------------------*/
1157 /*! Load data from the SPARSE_DATA NIML element.          3 Jul 2006 [rickr]
1158  *  (adding it to the dataset block)
1159  *
1160  *  - Return value is the number of sub-bricks found.
1161  *  - Data must be of type float or complex.
1162  *  - Free NIML data as it is applied.
1163  *------------------------------------------------------------------------*/
THD_add_sparse_data(THD_3dim_dataset * dset,NI_group * ngr)1164 int THD_add_sparse_data(THD_3dim_dataset * dset, NI_group * ngr )
1165 {
1166     THD_datablock  * blk;
1167     NI_element     * nel = NULL;
1168     float          * data = NULL;
1169     complex        * cdata = NULL;
1170     void          ** elist = NULL;
1171     int              nvals, ind, mind, sub, swap, len, tpo;
1172     int            * mlist = NULL;  /* master list */
1173 
1174 ENTRY("THD_add_sparse_data");
1175 
1176     if( !dset || !ngr ) {
1177         if(gni.debug > 1) fprintf(stderr,"** bad params to add_sparse_data\n");
1178         RETURN(0);
1179     }
1180     blk = dset->dblk;
1181     nvals = blk->nvals;
1182 
1183     ind = NI_search_group_shallow(ngr, "SPARSE_DATA", &elist);
1184     if( ind > 0 ) { nel = (NI_element *)elist[0]; NI_free(elist); }
1185     if( !nel ) {
1186         if(gni.debug > 1) fprintf(stderr,"-- no SPARSE_DATA to add\n");
1187         RETURN(0);
1188     }
1189 
1190     /* if mlist is NULL, no mastery */
1191     if( DBLK_IS_MASTERED(blk) ) mlist = blk->master_ival;
1192 
1193     /*-- verify sizes --*/
1194     if( nel->vec_num != nvals )
1195     {
1196         if(gni.debug)
1197         {
1198             fprintf(stderr,"** TASD: vec_num = %d, but nvals = %d\n",
1199                     nel->vec_num, nvals);
1200             if( mlist ) fprintf(stderr,"   (dataset is mastered)\n");
1201         }
1202         if( !mlist ) RETURN(0); /* no mastery means failure here */
1203     }
1204     if( nel->vec_len != DSET_NX(dset) )
1205     {
1206         if(gni.debug) fprintf(stderr,"** TASD: vec_len = %d, but NX = %d\n",
1207                                     nel->vec_len, DSET_NX(dset));
1208         RETURN(0);
1209     }
1210 
1211     /*-- verify types --*/
1212     for( ind = 0; ind < nvals; ind++ )
1213     {
1214         mind = mlist ? mlist[ind] : ind;  /* maybe use master index */
1215         if( nel->vec_typ[mind] != NI_FLOAT &&
1216             nel->vec_typ[mind] != NI_INT   &&
1217             nel->vec_typ[mind] != NI_COMPLEX)
1218         {
1219             if(gni.debug)
1220                fprintf(stderr,"** TASD: vec[%d] not float or complex\n",mind);
1221             RETURN(0);
1222         }
1223         else if( ! nel->vec[mind] )
1224         {
1225             if(gni.debug) fprintf(stderr,"** TASD: vec[%d] not filled!\n",mind);
1226             RETURN(0);
1227         }
1228     }
1229 
1230     /* check for output type */
1231     tpo = DBLK_BRICK_TYPE(blk,0);
1232     if (tpo != MRI_float && tpo != MRI_complex) {
1233       fprintf(stderr,"** TASD: brick not float or complex\n");
1234       RETURN(0);
1235     }
1236 
1237     /* check for necessary swapping */
1238     swap = (blk->diskptr->byte_order != mri_short_order());
1239     if(gni.debug>1 && swap) fprintf(stderr,"+d will byte_swap data\n");
1240     len = nel->vec_len;
1241 
1242     /*-- we seem to have all of the data, now copy it --*/
1243     sub = 0;
1244     for( ind = 0; ind < nvals; ind++ )
1245     {
1246         mind = mlist ? mlist[ind] : ind;  /* maybe use master index */
1247         if (tpo==MRI_float) {
1248          data = (float *)RwcMalloc(len * sizeof(float));
1249         } else {
1250          cdata = (complex *)RwcMalloc(len * sizeof(complex));
1251         }
1252         if(!data && !cdata){
1253            fprintf(stderr,"**ASD alloc fail: %d values\n",len);
1254            RETURN(0);
1255         }
1256         if( nel->vec_typ[mind] == NI_FLOAT ) {
1257            memcpy(data, nel->vec[mind], len * sizeof(float));
1258            if( swap ) nifti_swap_4bytes(len, data);
1259         } else if( nel->vec_typ[mind] == NI_INT ) {/* ZSS: Dec. 07. Note that*/
1260            int *idata=NULL, ii=0;                  /* int dsets become floats*/
1261            idata = (int *)RwcMalloc(len * sizeof(int));
1262            if(!idata){
1263               fprintf(stderr,"**ASD alloc fail: %d bytes\n",len);
1264               RETURN(0);
1265            }
1266            memcpy(idata, nel->vec[mind], len * sizeof(int));
1267            if( swap ) nifti_swap_4bytes(len, idata);
1268            for(ii=0; ii<len; ++ii) data[ii] = (float)idata[ii];
1269            RwcFree((char*)idata); idata=NULL;
1270         } else if( nel->vec_typ[mind] == NI_COMPLEX ) {
1271            memcpy(cdata, nel->vec[mind], len * sizeof(complex));
1272            /* in AFNI, complex is a float pair struct */
1273            if( swap ) nifti_swap_Nbytes(2*len, sizeof(complex)/2, data);
1274         } else {
1275            fprintf(stderr,"**ASD should never have been here.\n");
1276            RETURN(0);
1277         }
1278         if (tpo == MRI_float) mri_fix_data_pointer(data, DBLK_BRICK(blk,sub));
1279         else mri_fix_data_pointer(cdata, DBLK_BRICK(blk,sub));
1280         sub++;
1281 
1282         /* we can only nuke the old stuff if we know we're done with it */
1283         if( !mlist ){ NI_free(nel->vec[mind]);  nel->vec[mind] = NULL; }
1284     }
1285 
1286     if( DBLK_IS_MASTER_SUBRANGED(blk) )
1287         THD_apply_master_subrange(blk);
1288 
1289     RETURN(nvals);
1290 }
1291 
1292 /*! convert an AFNI dataset to a NIML group of type NI_SURF_DSET
1293 
1294     - set group attributes from ni_surf_dset_attrs
1295     - create SPARSE_DATA element, if requested
1296     - create attributes for COLMS_RANGE, COLMS_LABS, COLMS_TYPE, COLMS_STATSYM
1297     - apply HISTORY_NOTE
1298 
1299 */
THD_dset_to_ni_surf_dset(THD_3dim_dataset * dset,int copy_data)1300 NI_group * THD_dset_to_ni_surf_dset( THD_3dim_dataset * dset, int copy_data )
1301 {
1302     THD_datablock * blk;
1303     NI_group      * ngr;
1304     int             nx, ibr=0;
1305     char name[100]={""};
1306 
1307 ENTRY("THD_dset_to_ni_surf_dset");
1308 
1309     if( !ISVALID_DSET(dset) ) RETURN(NULL);
1310     blk = dset->dblk;
1311     if( !blk ) RETURN(NULL);
1312     nx = DSET_NVOX(dset); /* I want it all */
1313 
1314     if( blk->nnodes > 0 && blk->nnodes != nx ) {
1315         fprintf(stderr,"** datablock nnodes differs from nx %d, %d\n",
1316                 blk->nnodes, nx);
1317         RETURN(NULL);
1318     } else if ( blk->nnodes > 0 && !blk->node_list ) {
1319         fprintf(stderr,"** datablock has nnodes but no node_list\n");
1320         RETURN(NULL);
1321     }
1322 
1323     THD_set_dataset_attributes(dset);  /* load attributes for processing */
1324 
1325     /* create group element */
1326     /* All gets forced into Node_Bucket for now */
1327     ngr = NI_new_group_element();
1328     NI_rename_group(ngr, "AFNI_dataset");
1329     if (DSET_NY(dset) == 1) {
1330       NI_set_attribute(ngr, "dset_type", "Node_Bucket");
1331     } else {
1332       NI_set_attribute(ngr, "dset_type", "Voxel_Bucket");
1333       nsd_add_atr_to_group("DATASET_DIMENSIONS", NULL, blk, ngr);
1334       nsd_add_atr_to_group("IJK_TO_DICOM_REAL", NULL, blk, ngr);
1335       nsd_add_atr_to_group("BRICK_FLOAT_FACS", NULL, blk, ngr);
1336       nsd_add_atr_to_group("ORIENT_SPECIFIC", NULL, blk, ngr);
1337     }
1338     NI_set_attribute(ngr, "self_idcode", dset->idcode.str);
1339     NI_set_attribute(ngr, "filename", blk->diskptr->brick_name);
1340 
1341     nsd_add_str_atr_to_group("BRICK_LABS", "COLMS_LABS", blk, ngr);
1342     nsd_add_colms_range(ngr, dset);
1343     if (gni.to_float) {
1344       if (DBLK_BRICK_TYPE(blk,0) == MRI_complex) {
1345          nsd_add_colms_type(blk->nvals, DBLK_BRICK_TYPE(blk,0), ngr);
1346       } else {
1347          nsd_add_colms_type(blk->nvals, MRI_float, ngr);
1348       }
1349     } else {
1350       nsd_add_colms_type(blk->nvals, DBLK_BRICK_TYPE(blk,0), ngr);
1351     }
1352     nsd_add_str_atr_to_group("BRICK_STATSYM", "COLMS_STATSYM", blk, ngr);
1353     nsd_add_str_atr_to_group("HISTORY_NOTE", NULL, blk, ngr);
1354     nsd_add_str_atr_to_group("ATLAS_LABEL_TABLE", NULL, blk, ngr);
1355     nsd_add_str_atr_to_group("VALUE_LABEL_DTABLE", NULL, blk, ngr);
1356 
1357     for (ibr=0; ibr<DSET_NVALS(dset); ++ibr) {
1358       sprintf(name,"FDRCURVE_%06d",ibr) ;
1359       nsd_add_atr_to_group(name, NULL, blk, ngr);
1360 #if 0
1361       sprintf(name,"MDFCURVE_%06d",ibr) ;
1362       nsd_add_atr_to_group(name, NULL, blk, ngr);
1363 #endif
1364     }
1365 
1366     nsd_fill_index_list(ngr, dset);                  /* add INDEX_LIST */
1367     if( copy_data ) nsd_add_sparse_data(ngr, dset);  /* add SPARSE_DATA */
1368 
1369     /* maybe pad the node list to a certain level */
1370     ngr = nsd_pad_to_node(ngr);
1371 
1372     RETURN(ngr);
1373 }
1374 
1375 
1376 /*! possibly pad to a certain node index - use SUMA functionality
1377 
1378    return NI_group * with any update
1379 */
nsd_pad_to_node(NI_group * ngr)1380 static NI_group * nsd_pad_to_node(NI_group * ngr)
1381 {
1382     SUMA_DSET * sdset, * sdnew;
1383     NI_group  * new_nel;
1384     int         pad2node = MRILIB_DomainMaxNodeIndex;
1385 
1386     ENTRY("nsd_pad_to_node");
1387 
1388     if( pad2node < 0 ) RETURN(ngr);
1389 
1390     /* so there is something to do */
1391     sdset = SUMA_ngr_2_dset(ngr, 0);
1392     if( !sdset ) {
1393         fprintf(stderr,"** NPTN: failed SUMA_ngr_2_dset for pad2node\n");
1394         RETURN(ngr);
1395     }
1396 
1397     if( pad2node == 0 ) {
1398         DSET_MAX_NODE_INDEX(sdset, pad2node);
1399         if( pad2node < 0 ) {
1400             fprintf(stderr,"** failed to get made node index for pad2node\n");
1401             RETURN(ngr);
1402         }
1403     }
1404 
1405     if( pad2node <= 0 ) RETURN(ngr);
1406 
1407     if(gni.debug > 1) fprintf(stderr,"-- applying pad2node = %d\n",pad2node);
1408 
1409     /* make padded copy, steal pointer, free everything */
1410     sdnew = SUMA_PaddedCopyofDset(sdset, pad2node);
1411     if( !sdnew ) {
1412         fprintf(stderr,"** NPTN: failed pad to node %d\n", pad2node);
1413         RETURN(ngr);
1414     }
1415 
1416     ngr = sdnew->ngr;
1417     sdnew->ngr = NULL;
1418 
1419     SUMA_FreeDset(sdset);
1420     SUMA_FreeDset(sdnew);
1421 
1422     RETURN(ngr);
1423 }
1424 
1425 /* return the SUMA string corresponding to the AFNI type */
afni2suma_typestring(int afnitype)1426 static char * afni2suma_typestring(int afnitype)
1427 {
1428     switch(afnitype) {
1429         case MRI_byte:      return "Generic_Byte;";
1430         case MRI_short:     return "Generic_Short;";
1431         case MRI_int:       return "Generic_Int;";
1432         case MRI_float:     return "Generic_Float;";
1433         case MRI_complex:   return "Generic_Complex;";
1434     }
1435 
1436     return NULL;  /* bad idea? */
1437 }
1438 
1439 /*! add a COLMS_TYPE attribute element
1440 
1441    return 0 on success
1442 */
nsd_add_colms_type(int nvals,int tp,NI_group * ngr)1443 static int nsd_add_colms_type(int nvals, int tp, NI_group * ngr)
1444 {
1445     NI_element * nel;
1446     char       * str, * slist[1];  /* add_column requires a list of strings */
1447     int          c, plen;
1448     char       * tps;
1449 
1450 ENTRY("nsd_add_colms_type");
1451 
1452     /* check usage */
1453     if( nvals <= 0 || !ngr ) RETURN(1);
1454 
1455     /* create a new string: "Generic_Float;Generic_Float;..." */
1456     if (!(tps = afni2suma_typestring(tp))) { /* ZSS, Nov. 2013 */
1457       fprintf(stderr,
1458          "** Could not determine column equivalent for afni type %d\n",
1459          tp);
1460       RETURN(1);
1461     }
1462 
1463     /* rcr - update this with more types (that agree with SUMA) */
1464 
1465     plen = (strlen(tps)+1)*nvals + 1;
1466     str = (char *)malloc(plen * sizeof(char));
1467 
1468     /* insert first string */
1469     strcpy(str, tps);
1470 
1471     /* and then the rest */
1472     for( c = 1; c < nvals; c++ )
1473         strcat(str, tps);
1474 
1475     /* remove last ; */
1476     str[strlen(str)-1] = '\0';
1477 
1478     /* now add it to the group */
1479     slist[0] = str;
1480     nel = NI_new_data_element("AFNI_atr", 1);
1481     nel->outmode = NI_TEXT_MODE;
1482     NI_set_attribute(nel, "atr_name", "COLMS_TYPE");
1483     NI_add_column(nel, NI_STRING, slist);
1484     NI_add_to_group(ngr, nel);
1485 
1486     free(str); /* nuke local copy */
1487 
1488     RETURN(0);
1489 }
1490 
1491 
1492 /* - find the given attribute in the datablock
1493    - put it in a data element
1494    - add it to the group
1495 
1496    aname  - AFNI attribute name
1497    niname - NIML attribute name (if NULL, use aname)
1498    blk    - datablock
1499    ngr    - NI_group to insert new element into
1500 
1501    ZSS Feb 08: pilfered from THD_nimlize_dsetatr
1502 
1503    return 0 on success
1504 */
nsd_add_atr_to_group(char * aname,char * niname,THD_datablock * blk,NI_group * ngr)1505 static int nsd_add_atr_to_group(char * aname, char * niname,
1506                                 THD_datablock * blk, NI_group * ngr)
1507 {
1508     ATR_any *atr_any ;
1509     ATR_string * atr_str;
1510     NI_element * nel;
1511     char       * dest;
1512 
1513 ENTRY("nsd_add_atr_to_group");
1514 
1515    /* check usage */
1516    if( !aname || !blk || !ngr ) RETURN(1);
1517 
1518    atr_any = THD_find_atr(blk, aname);
1519    if( !atr_any ) RETURN(0);  /* nothing to add */
1520 
1521    if(gni.debug > 1){
1522      fprintf(stderr, "-d adding '%s' atr: ", niname?niname:aname);
1523    }
1524 
1525    switch( atr_any->type ){   /* pilfered from THD_nimlize_dsetatr */
1526        case ATR_FLOAT_TYPE:{
1527          ATR_float *atr_flo = (ATR_float *)atr_any ;
1528 
1529          nel = NI_new_data_element( "AFNI_atr" , atr_flo->nfl ) ;
1530          nel->outmode = NI_TEXT_MODE ;
1531          NI_set_attribute( nel , "atr_name" , atr_flo->name ) ;
1532          NI_add_column( nel , NI_FLOAT , atr_flo->fl ) ;
1533          NI_add_to_group( ngr , nel ) ;
1534        }
1535        break ;
1536 
1537        case ATR_INT_TYPE:{
1538          ATR_int *atr_int = (ATR_int *)atr_any ;
1539 
1540          nel = NI_new_data_element( "AFNI_atr" , atr_int->nin ) ;
1541          nel->outmode = NI_TEXT_MODE ;
1542          NI_set_attribute( nel , "atr_name" , atr_int->name ) ;
1543          NI_add_column( nel , NI_INT , atr_int->in ) ;
1544          NI_add_to_group( ngr , nel ) ;
1545        }
1546        break ;
1547 
1548        case ATR_STRING_TYPE:{
1549          nsd_add_str_atr_to_group(aname, niname, blk, ngr);
1550        }
1551        break;
1552 
1553        default:
1554          fprintf(stderr, "*** unexpected type!\n");
1555          RETURN(1);
1556    }
1557 
1558    RETURN(0);
1559 }
1560 
1561 /* - find the given attribute in the datablock
1562    - now just add 1 to length (should we bother?)
1563    - put it in a data element
1564    - add it to the group
1565 
1566    aname  - AFNI attribute name
1567    niname - NIML attribute name (if NULL, use aname)
1568    blk    - datablock
1569    ngr    - NI_group to insert new element into
1570 
1571    return 0 on success
1572 */
nsd_add_str_atr_to_group(char * aname,char * niname,THD_datablock * blk,NI_group * ngr)1573 static int nsd_add_str_atr_to_group(char * aname, char * niname,
1574                                     THD_datablock * blk, NI_group * ngr)
1575 {
1576     ATR_string * atr;
1577     NI_element * nel;
1578     char       * dest;
1579 
1580 ENTRY("nsd_add_str_atr_to_group");
1581 
1582     /* check usage */
1583     if( !aname || !blk || !ngr ) RETURN(1);
1584 
1585     atr = THD_find_string_atr(blk, aname);
1586     if( !atr ) RETURN(0);  /* nothing to add */
1587 
1588     if(gni.debug > 1){
1589         fprintf(stderr, "-d adding '%s' atr: ", niname?niname:aname);
1590         fwrite(atr->ch, sizeof(char), atr->nch, stderr);
1591         fputc('\n', stderr);
1592     }
1593 
1594     /* create a new string */
1595     dest = (char *)calloc(atr->nch+1, sizeof(char)); /* +1 for last '\0' */
1596     memcpy(dest, atr->ch, atr->nch);
1597     if(gni.debug > 2) fprintf(stderr, "-d new atr (orig): '%s'\n", dest);
1598     THD_zblock_ch(atr->nch, dest, ZSBLOCK);  /* swap out nul chars */
1599     dest[atr->nch] = '\0';
1600 
1601     /* now add it to the group */
1602     nel = NI_new_data_element("AFNI_atr", 1);
1603     nel->outmode = NI_TEXT_MODE;
1604     NI_set_attribute(nel, "atr_name", niname ? niname : aname);
1605     NI_add_column(nel, NI_STRING, &dest);
1606     NI_add_to_group(ngr, nel);
1607 
1608     if(gni.debug > 1) fprintf(stderr, "-d new atr is: '%s'\n", dest);
1609 
1610     free(dest); /* nuke local copy */
1611 
1612     RETURN(0);
1613 }
1614 
1615 
1616 /* add a COLMS_RANGE attribute element to the group
1617  *
1618  * do not assume that the data is of type float, though
1619  * evaluate ranges as if it is
1620  */
nsd_add_colms_range(NI_group * ngr,THD_3dim_dataset * dset)1621 static int nsd_add_colms_range(NI_group * ngr, THD_3dim_dataset * dset)
1622 {
1623     THD_datablock * blk;
1624     NI_element    * nel;
1625     float           fmin, fmax;
1626     char          * str;
1627     int             ind, nx;
1628     int             len;  /* dynamic length of str */
1629     int             minp, maxp;
1630 
1631 ENTRY("nsd_add_colms_range");
1632 
1633     nx = DSET_NVOX(dset); /* I want it all.   ZSS: Nov. 1 2013*/
1634     blk = dset->dblk;
1635 
1636     /*-- create the string --*/
1637     len = 512;
1638     str = (char *)malloc(len * sizeof(char));
1639     str[0] = '\0';
1640 
1641     /* stick the nodes in the list */
1642     get_blk_min_max_posn(blk, 0, nx, &fmin, &minp, &fmax, &maxp);
1643     loc_append_vals(&str, &len, "", fmin, fmax, minp, maxp);
1644 
1645     for( ind = 1; ind < blk->nvals; ind++ )  /* keep appending the next set */
1646     {
1647         get_blk_min_max_posn(blk, ind, nx, &fmin, &minp, &fmax, &maxp);
1648         loc_append_vals(&str, &len, ";", fmin, fmax, minp, maxp);
1649     }
1650 
1651     /*-- now we have the string, insert it as an attribute element --*/
1652 
1653     /* create initial element */
1654     nel = NI_new_data_element("AFNI_atr", 1);
1655 
1656     nel = NI_new_data_element("AFNI_atr", 1);
1657     nel->outmode = NI_TEXT_MODE;
1658     NI_set_attribute(nel, "atr_name", "COLMS_RANGE");
1659     NI_add_column(nel, NI_STRING, &str);
1660     NI_add_to_group(ngr, nel);
1661 
1662     if(gni.debug > 1) fprintf(stderr,"+d added COLMS_RANGE atr: '%s'\n", str);
1663 
1664     free(str); /* nuke allocated string */
1665 
1666     RETURN(0);
1667 }
1668 
1669 
1670 /*------------------------------------------------------------------------*/
1671 /*! Add the INDEX_LIST attribute element from the AFNI dset to the
1672     NIML group.
1673  *
1674  *  If dset has no node_list, the user may still want it filled to a
1675  *    default list, based on AFNI_NSD_ADD_NODES (gni.add_nodes).
1676  *  If the datum is not float, convert it.
1677  * -----------------------------------------------------------------------*/
nsd_fill_index_list(NI_group * ngr,THD_3dim_dataset * dset)1678 static int nsd_fill_index_list(NI_group * ngr, THD_3dim_dataset * dset)
1679 {
1680     NI_element    * nel;
1681     THD_datablock * blk;
1682     char            str[4];
1683     int           * node_list, * new_list = NULL;
1684     int             c, nx;
1685 
1686 ENTRY("nsd_fill_index_list");
1687 
1688     blk = dset->dblk;
1689     nx = DSET_NVOX(dset); /* I want it all. ZSS: Nov. 1 2013*/
1690 
1691     node_list = blk->node_list;
1692     if( blk->nnodes <= 0 || ! blk->node_list )  /* no node list */
1693     {
1694         /* create a default list                       30 Aug 2006 [rickr]
1695          * (either requested or in prep for pad2node)   6 Sep 2012 [rickr] */
1696         if( gni.add_nodes || MRILIB_DomainMaxNodeIndex >= 0 )
1697         {
1698             if(gni.debug) fprintf(stderr,"+d creating default INDEX_LIST\n");
1699             new_list = (int *)malloc(nx * sizeof(int));
1700             if( !new_list ){
1701                 fprintf(stderr,"** NFIL: failed to alloc %d nodes\n",nx);
1702                 RETURN(1);
1703             }
1704             for( c = 0; c < nx; c++ ) new_list[c] = c;
1705             node_list = new_list;
1706         }
1707         else
1708         {
1709             if(gni.debug) fprintf(stderr,"-d no INDEX_LIST to add\n");
1710             RETURN(0);
1711         }
1712     }
1713     else if( blk->nnodes != nx )
1714     {
1715         fprintf(stderr,"** node list len (%d) != NX (%d), skipping nodes\n",
1716                 blk->nnodes, nx);
1717         RETURN(1);
1718     }
1719 
1720     if(gni.debug) fprintf(stderr,"+d adding INDEX_LIST, len %d\n", nx);
1721 
1722     nel = NI_new_data_element("INDEX_LIST", nx);
1723 
1724     nel->outmode = gni.write_mode; /* ASCII or BINARY mode (from globals) */
1725 
1726     if( nsd_are_sorted_ints(node_list, nx)) strcpy(str, "Yes");
1727     else                                    strcpy(str, "No");
1728 
1729     NI_set_attribute(nel, "sorted_node_def", str);
1730     if(gni.debug > 1) fprintf(stderr,"+d set sorted_node_def = %s\n", str);
1731 
1732     NI_add_column(nel, NI_INT, node_list);
1733 
1734     if( new_list ) free(new_list);    /* lose any new list */
1735 
1736     NI_add_to_group(ngr, nel);
1737 
1738     RETURN(0);
1739 }
1740 
1741 /*------------------------------------------------------------------------*/
1742 /*! Add SPARSE_DATA from the AFNI dset to the NIML group.
1743  *
1744  *  If the datum is not float or complex, convert it.
1745  * -----------------------------------------------------------------------*/
nsd_add_sparse_data(NI_group * ngr,THD_3dim_dataset * dset)1746 static int nsd_add_sparse_data(NI_group * ngr, THD_3dim_dataset * dset)
1747 {
1748     NI_element    * nel;
1749     THD_datablock * blk;
1750     float         * fdata = NULL;
1751     int             ind, nx, c;
1752 
1753 ENTRY("nsd_add_sparse_data");
1754 
1755     blk = dset->dblk;
1756     nx = DSET_NVOX(dset); /* I want it all, now carting volumes too.
1757                              ZSS: Nov. 1 2013 */
1758 
1759     if(gni.debug) fprintf(stderr,"+d adding SPARSE_DATA element\n");
1760 
1761     /* check whether we have all floats, of not prepare for conversion */
1762     /*                                              4 Aug 2006 [rickr] */
1763     for( ind = 0; ind < blk->nvals; ind++ )
1764         if( DBLK_BRICK_TYPE(blk, ind) != MRI_float &&
1765             DBLK_BRICK_TYPE(blk, ind) != MRI_complex) /* then allocate floats */
1766         {
1767             if( ! gni.to_float &&
1768                 DBLK_BRICK_TYPE(blk, ind) != MRI_short &&
1769                 DBLK_BRICK_TYPE(blk, ind) != MRI_byte  ){
1770                 fprintf(stderr,
1771                    "** dset has non-floats/complex/short/byte and\n"
1772                    "   AFNI_NSD_TO_FLOAT is NO life has become unbearable...\n");
1773                 RETURN(1);
1774             }
1775 
1776             fdata = malloc(nx * sizeof(float));  /* create our float array */
1777             if( !fdata ) {
1778                 fprintf(stderr,"** NASD: failed to malloc conversion floats\n");
1779                 RETURN(1);
1780             }
1781             if(gni.debug)
1782                 fprintf(stderr,"+d converting NI_SURF_DSET to floats\n");
1783 
1784             break;  /* and terminate the loop */
1785         }
1786 
1787     /* create initial element of length nx */
1788     nel = NI_new_data_element("SPARSE_DATA", nx);
1789 
1790     if(gni.debug > 1)
1791         fprintf(stderr,"+d sparse_data: adding %d data columns\n", blk->nvals);
1792 
1793     /* insert data */
1794     for( ind = 0; ind < blk->nvals; ind++ )
1795     {
1796         float fac;
1797         if( DBLK_BRICK_TYPE(blk, ind) == MRI_float )
1798         {
1799             NI_add_column(nel, NI_FLOAT, DBLK_ARRAY(blk, ind)); /* use dblk */
1800         }
1801         else if( DBLK_BRICK_TYPE(blk, ind) == MRI_complex )
1802         {
1803             NI_add_column(nel, NI_COMPLEX, DBLK_ARRAY(blk, ind)); /* use dblk */
1804         }
1805         else if( ! gni.to_float && DBLK_BRICK_TYPE(blk, ind) == MRI_short )
1806         {
1807             NI_add_column(nel, NI_SHORT, DBLK_ARRAY(blk, ind)); /* use dblk */
1808         }
1809         else if( ! gni.to_float && DBLK_BRICK_TYPE(blk, ind) == MRI_byte )
1810         {
1811             NI_add_column(nel, NI_BYTE, DBLK_ARRAY(blk, ind)); /* use dblk */
1812         }
1813         else
1814         {
1815             EDIT_convert_dtype(nx, DBLK_BRICK_TYPE(blk,ind),DBLK_ARRAY(blk,ind),
1816                                    MRI_float, fdata, 0);
1817             /* apply any factor */
1818             fac = DBLK_BRICK_FACTOR(blk,ind); if( fac == 0.0 ) fac = 1.0;
1819             if( fac != 1.0 ) for(c = 0; c < nx; c++) fdata[c] *= fac;
1820 
1821             NI_add_column(nel, NI_FLOAT, fdata);  /* and add to element */
1822         }
1823     }
1824 
1825     if( fdata ) free(fdata);  /* fly! (thud) be free! (thud) */
1826 
1827     /* Next declare output to be Node_Bucket_data always. Someday
1828     we will change that IF we ever feel the need. ZSS Dec 07 */
1829     {
1830       char name[256], *att;
1831       if ((att = NI_get_attribute(ngr,"dset_type"))) {
1832          snprintf(name, 255,"%s_data", att);
1833          NI_set_attribute(nel, "data_type", name);
1834       } else {
1835          NI_set_attribute(nel, "data_type", "Node_Bucket_data");
1836       }
1837     }
1838 
1839     set_sparse_data_attribs(nel, dset, 1);
1840 
1841     NI_add_to_group(ngr, nel);
1842 
1843     RETURN(0);
1844 }
1845 
1846 
1847 /*------------------------------------------------------------------------*/
1848 /*! set element attribute specific to SPARSE DATA and the dataset 3 Aug 2006
1849 --------------------------------------------------------------------------*/
set_sparse_data_attribs(NI_element * nel,THD_3dim_dataset * dset,int nodes_from_dset)1850 int set_sparse_data_attribs(NI_element * nel, THD_3dim_dataset * dset,
1851                             int nodes_from_dset)
1852 {
1853     char str[32];
1854     float TR=0.0;
1855 
1856 ENTRY("set_sparse_data_attribs");
1857 
1858     if( !nel || !dset ) RETURN(1);
1859 
1860     nel->outmode = gni.write_mode; /* ASCII or BINARY mode (from globals) */
1861 
1862     /* check for need of the ni_timestep attribute */
1863     if( DSET_NUM_TIMES(dset) > 1 )  /* then it is time dependent */
1864     {
1865         TR = DSET_TIMESTEP(dset);
1866         if( DSET_TIMEUNITS(dset) == UNITS_MSEC_TYPE ) TR *= 0.001;
1867         strcpy(str, MV_format_fval(TR));
1868         NI_set_attribute(nel, "ni_timestep", str);
1869         if(gni.debug > 1) fprintf(stderr,"+d setting ni_timestep = %s\n", str);
1870     }
1871 
1872     RETURN(0);
1873 }
1874 
1875 
1876 /*------------------------------------------------------------------------*/
1877 /*! return whether the given list is sorted            23 Aug 2006 [rickr]
1878 --------------------------------------------------------------------------*/
nsd_are_sorted_ints(int * list,int len)1879 int nsd_are_sorted_ints(int *list, int len)
1880 {
1881     int c;
1882 
1883 ENTRY("nsd_are_sorted_ints");
1884 
1885     if( !list || len <= 0 ) RETURN(0);
1886     for( c = 0; c < len - 1; c++ )
1887         if( list[c] > list[c+1] )
1888             RETURN(0);
1889     RETURN(1);
1890 }
1891 
1892 
1893 /*------------------------------------------------------------------------*/
1894 /*! Like strndup, relies on length, not nul            11 Jul 2006 [rickr]
1895 --------------------------------------------------------------------------*/
my_strndup(char * str,int len)1896 static char * my_strndup(char *str, int len)
1897 {
1898    char *dup;
1899    if( str == NULL || len < 0 ) return NULL;
1900    dup = (char *)calloc(len+1, sizeof(char));
1901    strncpy(dup,str,len);
1902    dup[len] = '\0';
1903    return dup;
1904 }
1905 
1906 /* append 2 floats and ints to str, subject to total length, len,
1907    and pre-pended with sep */
loc_append_vals(char ** str,int * len,char * sep,float f1,float f2,int i1,int i2)1908 static int loc_append_vals(char ** str, int * len, char * sep,
1909                            float f1, float f2, int i1, int i2)
1910 {
1911     char lbuf[256], fs1[32];  /* for first call to MV_format_fval */
1912     int  req;
1913 
1914 ENTRY("loc_append_vals");
1915 
1916     if( !str || !*str || !len || !sep ) RETURN(1);
1917     if( strlen(sep) > 32 )     RETURN(1);
1918 
1919     /* first, just stuff them in a sufficient buffer */
1920     /* (make a copy of first float, and then format it all) */
1921     strcpy(fs1, MV_format_fval(f1));
1922     sprintf(lbuf, "%s%s %s %d %d", sep, fs1, MV_format_fval(f2), i1, i2);
1923 
1924     req = strlen(*str) + strlen(lbuf) + 1;
1925     if( req > *len )
1926     {
1927         *len = req + 512;       /* include some extra space */
1928         *str = (char *)realloc(*str, *len * sizeof(char));
1929     }
1930 
1931     strcat(*str, lbuf); /* finally, copy the data in */
1932 
1933     RETURN(0);
1934 }
1935 
1936 /* ----------------------------------------------------------------------
1937  * get the byte order from any ni_form attribute
1938  * return LSB_FIRST, MSB_FIRST, or NATIVE_ORDER (if none is found)
1939 */
NI_get_byte_order(NI_element * nel)1940 int NI_get_byte_order(NI_element * nel)
1941 {
1942     char * rhs;
1943     int    order = NATIVE_ORDER;
1944 
1945 ENTRY("NI_get_byte_order");
1946 
1947     if( !nel ) RETURN(NATIVE_ORDER);
1948 
1949     rhs = NI_get_attribute(nel, "ni_form");
1950     if( !rhs )
1951     {
1952         if(gni.debug > 1) fprintf(stderr,"-d no ni_form for byte order\n");
1953         RETURN(NATIVE_ORDER);
1954     }
1955 
1956     if( strstr(rhs, "lsbfirst") ) order = LSB_FIRST;
1957     if( strstr(rhs, "msbfirst") ) order = MSB_FIRST;
1958 
1959     if( gni.debug > 1 )
1960         fprintf(stderr,"-d found byte order string, %s\n",
1961                 order == LSB_FIRST ? LSB_FIRST_STRING :
1962                 order == MSB_FIRST ? MSB_FIRST_STRING :
1963                                      NATIVE_STRING );
1964 
1965     RETURN(order);
1966 }
1967 
1968 
1969 /* ---------------------------------------------------------------------- */
1970 /* NIML globals access functions                       3 Aug 2006 [rickr] */
1971 
1972 /* return the corresponding NI_type, and -1 on failure (since 0 is used) */
dtype_nifti_to_niml(int dtype)1973 int dtype_nifti_to_niml(int dtype) {
1974     switch(dtype) {
1975         case NIFTI_TYPE_INT16:    { return NI_SHORT;     }
1976         case NIFTI_TYPE_INT32:    { return NI_INT;       }
1977         case NIFTI_TYPE_FLOAT32:  { return NI_FLOAT32;   }
1978         case NIFTI_TYPE_FLOAT64:  { return NI_FLOAT64;   }
1979         case NIFTI_TYPE_INT8:     { return NI_BYTE;      }
1980         case NIFTI_TYPE_COMPLEX64:{ return NI_COMPLEX;   }
1981     }
1982 
1983     return -1;
1984 }
1985 
1986 /* return the corresponding NIFTI_type, and DT_UNKNOWN on failure */
dtype_niml_to_nifti(int dtype)1987 int dtype_niml_to_nifti(int dtype) {
1988     switch(dtype) {
1989         case NI_SHORT:  { return NIFTI_TYPE_INT16;    }
1990         case NI_INT:    { return NIFTI_TYPE_INT32;    }
1991         case NI_FLOAT32:{ return NIFTI_TYPE_FLOAT32;  }
1992         case NI_FLOAT64:{ return NIFTI_TYPE_FLOAT64;  }
1993         case NI_BYTE:   { return NIFTI_TYPE_INT8;     }
1994         case NI_COMPLEX:{ return NIFTI_TYPE_COMPLEX64;}
1995     }
1996 
1997     return 0;   /* some #define seems to get in the way of DT_UNKNOWN */
1998 }
1999 
2000 /* return the first element where name is 'ename' and atr_name is 'atr_name' */
NI_find_element_by_aname(NI_group * ngr,char * ename,char * aname,char * aval)2001 NI_element * NI_find_element_by_aname(NI_group * ngr, char * ename,
2002                                       char * aname, char * aval)
2003 {
2004     NI_element  * nel = NULL;
2005     void       ** elist = NULL;
2006     char       ** sar, * atr;
2007     int           ind, c;
2008 
2009     ENTRY("NI_find_element_by_aname");
2010 
2011     if( !ngr || !ename || !aname || !aval ) RETURN(NULL);
2012 
2013     ind = NI_search_group_shallow(ngr, ename, &elist);
2014     if( ind <= 0 ) RETURN(NULL);  /* no such name */
2015 
2016     for( c = 0; c < ind; c++ ) {
2017         atr = NI_get_attribute(elist[c], aname);
2018         if( !strcmp(atr, aval) ) {   /* found! */
2019             nel = (NI_element *)elist[c];
2020             break;
2021         }
2022     }
2023 
2024     NI_free(elist);
2025 
2026     RETURN(nel);
2027 }
2028 
2029 /* Read the NIML file and determine the order of labels in the
2030  * ColumnLabels element.
2031  *
2032  * return values are:
2033  * 0 : unknown order (includes error conditions)
2034  * 1 : slice-major order (labels are s0.*, s1,*, ...)
2035  * 2 : slice-minor order (labels are s0.L0, s1.L0, ...)
2036  */
niml_get_major_label_order(char * fname)2037 int niml_get_major_label_order(char * fname)
2038 {
2039     NI_element   * nel=NULL;            /* main read element      */
2040     NI_str_array * lablist=NULL;        /* array of parsed labels */
2041     char         * labstr=NULL;         /* unparsed label string  */
2042     int            c, order=0;          /* init order as unknown  */
2043 
2044 ENTRY("niml_get_major_label_order");
2045 
2046     gni.debug = AFNI_numenv("AFNI_NIML_DEBUG"); /* maybe we want info */
2047     if(gni.debug > 3) fprintf(stderr,"-- get_major_label_order\n");
2048 
2049     /* check each step of the way ... */
2050 
2051     if( !fname ) {
2052         fprintf(stderr,"** major_label_order: fname is NULL\n");
2053         RETURN(0);
2054     }
2055     if ( (nel = (NI_element*)read_niml_file(fname, 0)) == NULL ) {
2056         if( gni.debug )
2057             fprintf(stderr,"** MLO: failed to read %s as NIML\n", fname);
2058         RETURN(0);
2059     }
2060     if(gni.debug > 2) fprintf(stderr,"-- NGMLO: vec_num = %d, vec_len = %d\n",
2061                               nel->vec_num, nel->vec_len);
2062 
2063     labstr = NI_get_attribute(nel, "ColumnLabels");
2064     if( !labstr ) {
2065         if( gni.debug ) fprintf(stderr,"** MLO: no ColumnLabels in %s\n",fname);
2066         RETURN(0);
2067     }
2068     if(gni.debug > 3) fprintf(stderr,"-- NGMLO: labstr = %-.66s...\n", labstr);
2069     lablist = NI_decode_string_list(labstr, ";");
2070     if( !lablist ) {
2071         if(gni.debug) fprintf(stderr,"** MLO: bad ColumnLabels in %s\n",fname);
2072         RETURN(0);
2073     }
2074     if( lablist->num < 3 ) {
2075         if( gni.debug ) fprintf(stderr,"** MLO: vec_num = %d\n",nel->vec_num);
2076         RETURN(0);
2077     }
2078 
2079     /* we have the label list, now just check the first 2 labels */
2080     if(gni.debug > 2) fprintf(stderr,"== NGMLO: l[0]=%s, l[1]=%s, l[2]=%s\n",
2081                           lablist->str[0], lablist->str[1], lablist->str[2]);
2082 
2083     if( !strncmp(lablist->str[0], "s0", 2) &&
2084         !strncmp(lablist->str[1], "s0", 2) ) {
2085         if(gni.debug>1) fprintf(stderr,"-- %s is slice-major order\n",fname);
2086         order = 1;
2087     } else if( !strncmp(lablist->str[0], "s0", 2) &&
2088                !strncmp(lablist->str[1], "s1", 2) ) {
2089         if(gni.debug>1) fprintf(stderr,"-- %s is slice-minor order\n",fname);
2090         order = 2;
2091     } else {
2092         if(gni.debug>1) fprintf(stderr,"-- %s has indeterminate order\n",fname);
2093         order = 0;
2094     }
2095 
2096     NI_delete_str_array(lablist);
2097     NI_free(nel);
2098 
2099     RETURN(order);
2100 }
2101 
2102 /* apply any escape characters, and return a new string
2103  *  * (which will not exceed the orignal string in length)
2104  *   *
2105  *    * \n, \t, \b                   31 Jul 2009 */
unescape_unix_str(const char * ustr)2106 char * unescape_unix_str(const char * ustr)
2107 {
2108     char * newstr = NULL;
2109     int    len, c, nind;
2110     if( !ustr ) return NULL;
2111     len = strlen(ustr);
2112     newstr = (char *)malloc(len+1);
2113 
2114     for( c = 0, nind = 0; c < len; c++, nind++ ) {
2115         if( ustr[c] == '\\' ) {
2116             switch(ustr[c+1]) {
2117                 case 'n':
2118                     newstr[nind] = '\n';
2119                     c++;
2120                     break;
2121                 case 't':
2122                     newstr[nind] = '\t';
2123                     c++;
2124                     break;
2125                 case 'b':
2126                     newstr[nind] = '\b';
2127                     c++;
2128                     break;
2129                 default:
2130                     newstr[nind] = ustr[c]; /* no escape applied */
2131                     break;
2132             }
2133         } else newstr[nind] = ustr[c];      /* no escape found */
2134     }
2135 
2136     newstr[nind] = '\0';
2137 
2138     return newstr;
2139 }
2140 
2141 
set_ni_globs_from_env(void)2142 int set_ni_globs_from_env(void)
2143 {
2144 ENTRY("set_ni_globs_from_env");
2145 
2146     /* if datasets don't have nodes, the user may want to add a default list */
2147     gni.add_nodes = AFNI_yesenv("AFNI_NSD_ADD_NODES");        /* 30 Aug 2006 */
2148 
2149     gni.debug = AFNI_numenv("AFNI_NIML_DEBUG"); /* maybe the user wants info */
2150 
2151     /* if having no conversion is desired, block it */
2152     gni.to_float = AFNI_noenv("AFNI_NSD_TO_FLOAT") ? 0 : 1;
2153 
2154     /* if text desired, use it for writing */
2155     gni.write_mode = AFNI_yesenv("AFNI_NIML_TEXT_DATA") ? NI_TEXT_MODE :
2156                                                           NI_BINARY_MODE;
2157 
2158     RETURN(0);
2159 }
2160 
set_gni_add_nodes(int add_nodes)2161 void set_gni_add_nodes( int add_nodes ){ gni.add_nodes = add_nodes; }
get_gni_add_nodes(void)2162 int  get_gni_add_nodes( void          ){ return gni.add_nodes;  }
2163 
set_gni_debug(int debug)2164 void set_gni_debug( int debug ){ gni.debug = debug; }
get_gni_debug(void)2165 int  get_gni_debug( void      ){ return gni.debug;  }
2166 
set_gni_to_float(int flag)2167 void set_gni_to_float( int flag ){ gni.to_float = flag; }      /* 4 Aug 2006 */
get_gni_to_float(void)2168 int  get_gni_to_float( void     ){ return gni.to_float; }
2169 
set_gni_write_mode(int mode)2170 void set_gni_write_mode( int mode ){ gni.write_mode = mode; }
get_gni_write_mode(void)2171 int  get_gni_write_mode( void     ){ return gni.write_mode; }
2172 
2173