1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include "mrilib.h"
8 #include "thd.h"
9 
10 /*---------------------------------------------------------------------*/
11 
12 static int compress_mode = COMPRESS_NOFILE ;
13 
THD_set_write_compression(int mm)14 void THD_set_write_compression( int mm )
15 {
16    if( mm >= COMPRESS_NONE && mm <= COMPRESS_LASTCODE )
17       compress_mode = mm ;
18    else
19       compress_mode = COMPRESS_NONE ;
20    return ;
21 }
22 
23 /*---------------------------------------------------------------------*/
24 
THD_get_write_compression(void)25 int THD_get_write_compression(void)
26 {
27    if( compress_mode == COMPRESS_NOFILE ) THD_enviro_write_compression() ;
28    return compress_mode ;
29 }
30 
31 /*---------------------------------------------------------------------*/
32 
THD_enviro_write_compression(void)33 int THD_enviro_write_compression(void)
34 {
35    char *hh = my_getenv("AFNI_COMPRESSOR") ;
36    int ii ;
37 
38    compress_mode = COMPRESS_NONE ;
39    if( hh == NULL ) return COMPRESS_NONE ;
40 
41    for( ii=0 ; ii <= COMPRESS_LASTCODE ; ii++ ){
42       if( strcmp(hh,COMPRESS_enviro[ii]) == 0 ){
43          compress_mode = ii ;
44          return ii ;
45       }
46    }
47 
48    return COMPRESS_NONE ;
49 }
50 
51 /*---------------------------------------------------------------------*/
52 
53 static int native_order = -1 ;
54 static int output_order = -1 ;
55 
THD_set_write_order(int mm)56 void THD_set_write_order( int mm )
57 {
58    if( mm == LSB_FIRST || mm == MSB_FIRST )
59       output_order = mm ;
60    else
61       output_order = -1 ;
62    return ;
63 }
64 
65 /*---------------------------------------------------------------------*/
66 
THD_enviro_write_order(void)67 void THD_enviro_write_order(void)
68 {
69    char *hh = my_getenv("AFNI_BYTEORDER") ;
70 
71    if( hh == NULL ){ output_order = -1 ; return ; }
72 
73    if( strcmp(hh,LSB_FIRST_STRING) == 0 ){ output_order = LSB_FIRST; return; }
74    if( strcmp(hh,MSB_FIRST_STRING) == 0 ){ output_order = MSB_FIRST; return; }
75 
76    output_order = -1 ; return ;
77 }
78 
79 /*---------------------------------------------------------------------*/
80 
THD_get_write_order(void)81 int THD_get_write_order(void)
82 {
83    if( native_order < 0 ) native_order = mri_short_order() ;
84    if( output_order < 0 ) THD_enviro_write_order() ;
85 
86    return (output_order > 0) ? output_order
87                              : native_order ;
88 }
89 
90 /*---------------------------------------------------------------------*/
91 /*! Write an AFNI datablock to disk in the .HEAD/.BRIK format:
92      - Returns True if OK, False if an error.
93      - See also AFNI_refashion_dataset.
94      - All attributes must now be set prior to calling this,
95        via function THD_set_dataset_attributes().
96      - The one exception to the above rule is the BYTEORDER attribute.
97 -----------------------------------------------------------------------*/
98 
THD_write_datablock(THD_datablock * blk,RwcBoolean write_brick)99 RwcBoolean THD_write_datablock( THD_datablock *blk , RwcBoolean write_brick )
100 {
101    THD_diskptr *dkptr ;
102    RwcBoolean good ;
103    int id , nx , ny , nz , nv , nxy , nxyz , ibr ;
104    int atrank[ATRSIZE_DATASET_RANK] , atdims[ATRSIZE_DATASET_DIMENSIONS] ;
105    MRI_IMAGE *im ;
106    int save_order ;
107    int64_t nb , idone ;
108    int do_mripurge ;
109 
110    /*-- sanity checks --*/
111 
112    if( ! ISVALID_DATABLOCK(blk) ) return False ;
113    if( DBLK_IS_MASTERED(blk) )    return False ;  /* 11 Jan 1999 */
114    if( DBLK_IS_MINC(blk) ) WRITE_ERR("MINC with bad name extension?") ;
115                                                                /* 29 Oct 2001 */
116    if( DBLK_IS_ANALYZE(blk) ) WRITE_ERR("ANALYZE but bad name extension?") ;
117                                                                /* 27 Aug 2002 */
118    if( DBLK_IS_NIFTI(blk) ) WRITE_ERR("NIFTI but bad name extension?") ;
119                                                                /* 28 Aug 2003 */
120 
121    dkptr = blk->diskptr ;
122    if( ! ISVALID_DISKPTR(dkptr) ) WRITE_ERR("illegal file type") ;
123 
124    if( strlen(dkptr->directory_name) == 0 ||
125        strlen(dkptr->header_name)    == 0 ||
126        strlen(dkptr->filecode)       == 0   )
127      WRITE_ERR("illegal file names stored in dataset") ;
128 
129    if( dkptr->rank != 3 )
130      WRITE_ERR("cannot write non-3D datablock") ;
131 
132    /*-- create directory if necessary --*/
133 
134    if( ! THD_is_directory(dkptr->directory_name) ){
135      id = mkdir( dkptr->directory_name , THD_MKDIR_MODE ) ;
136      if( id != 0 ){
137        fprintf(stderr,
138             "\n"
139             "*** cannot mkdir new directory: %s\n"
140             "  - Do you have permission to write to this disk?\n"
141             "  - Is the disk full?\n" ,
142             dkptr->directory_name) ;
143        return False ;
144      }
145    }
146 
147    /* 25 April 1998: deal with byte order issues */
148 
149    if( native_order < 0 ){                /* initialization */
150      native_order = mri_short_order() ;
151      if( output_order < 0 ) THD_enviro_write_order() ;
152    }
153    if( dkptr->byte_order <= 0 ) dkptr->byte_order = native_order ;
154    save_order = (output_order > 0) ? output_order
155                                    : dkptr->byte_order ;
156 
157 #if 0
158 fprintf(stderr,"THD_write_datablock: save_order=%d  dkptr->byte_order=%d\n",
159                save_order, dkptr->byte_order ) ;
160 #endif
161 
162    if( save_order != LSB_FIRST && save_order != MSB_FIRST )
163      save_order = native_order ;
164 
165    if( save_order == LSB_FIRST )
166      THD_set_string_atr( blk , ATRNAME_BYTEORDER , LSB_FIRST_STRING ) ;
167    else if( save_order == MSB_FIRST )
168      THD_set_string_atr( blk , ATRNAME_BYTEORDER , MSB_FIRST_STRING ) ;
169 
170    /*-- actually write attributes to disk --*/
171 
172    good = THD_write_atr( blk ) ;
173    if( good == False )
174      WRITE_ERR(
175      "failure to write attributes - is disk full? do you have write permission?");
176 
177    /*-- if not writing data, can exit --*/
178 
179    if( write_brick == False || blk->brick == NULL ||
180        dkptr->storage_mode == STORAGE_UNDEFINED     ) return True ;
181 
182    if( dkptr->storage_mode == STORAGE_BY_VOLUMES ){  /* 20 Jun 2002 */
183      fprintf(stderr,"** Writing dataset by VOLUMES not yet supported.\n") ;
184      return False ;
185    }
186 
187    /*-- check each brick for existence:
188           if none exist, cannot write, but is OK
189           if some but not all exist, cannot write, and is an error --*/
190 
191    id = THD_count_potential_databricks( blk ) ;
192    if( id <= 0 )         return True ;
193    if( id < blk->nvals ){
194      ERROR_message("Write dataset error: only %d out of %d bricks in memory",
195                    id,blk->nvals) ; return False ;
196    }
197 
198    if( blk->malloc_type == DATABLOCK_MEM_UNDEFINED )
199      WRITE_ERR("undefined data exists in memory") ;
200 
201    /*-- 13 Mar 2006: check for free disk space --*/
202 
203    { int mm = THD_freemegabytes( dkptr->header_name ) ;
204      int rr = blk->total_bytes / (1024l * 1024l) ;
205      if( mm >= 0 && mm <= rr )
206        WARNING_message("Disk space: writing file %s (%d MB),"
207                        " but only %d free MB on disk"        ,
208          dkptr->brick_name , rr , mm ) ;
209    }
210 
211    /*-- write data out in whatever format is ordered --*/
212 
213    nx = dkptr->dimsizes[0] ;
214    ny = dkptr->dimsizes[1] ;  nxy  = nx * ny ;
215    nz = dkptr->dimsizes[2] ;  nxyz = nxy * nz ;
216    nv = dkptr->nvals ;        nb   = blk->total_bytes ;
217 
218    switch( dkptr->storage_mode ){
219 
220       default: WRITE_ERR("illegal storage_mode!") ; break ;
221 
222       case STORAGE_BY_BRICK:{
223          FILE *far ;
224          RwcBoolean purge_when_done = False , ok ;
225          int force_gzip=0 , csave=COMPRESS_NONE ;
226 
227          /** if we have a mmap-ed file, copy into RAM (ugh) **/
228 
229          if( blk->malloc_type == DATABLOCK_MEM_MMAP ){
230             char *bnew , *bold ;
231             int64_t offset ;
232 
233             bnew = (char *) malloc( (size_t)nb ) ;  /* work space */
234             bold = DBLK_ARRAY(blk,0) ;              /* start of mapped file */
235 
236             if( bnew == NULL )
237               WRITE_ERR("cannot rewrite due to malloc failure - is memory exhausted?") ;
238 
239             memcpy( bnew , bold , (size_t)nb ) ;    /* make a copy,    */
240             munmap( (void *)bold , (size_t)nb ) ;  /* then unmap file */
241 
242             /* fix sub-brick pointers */
243 
244             offset = 0 ;
245             for( ibr=0 ; ibr < nv ; ibr++ ){
246                mri_fix_data_pointer( (void *)(bnew+offset) , DBLK_BRICK(blk,ibr) ) ;
247                offset += DBLK_BRICK_BYTES(blk,ibr) ;
248                DBLK_BRICK(blk,ibr)->fondisk = 0 ;   /* 31 Jan 2007 */
249             }
250 
251             purge_when_done = True ;
252 
253          } /** fall thru to here if have a malloc-ed dataset **/
254 
255          if( save_order != native_order ) purge_when_done = True ;
256 
257          /** delete old file, if any **/
258 
259          COMPRESS_unlink( dkptr->brick_name ) ; /* Feb 1998 */
260 
261          /** create new file **/
262 
263          id = strlen(dkptr->directory_name) ;
264          ok = ( dkptr->directory_name[id-1] == '/' ) ;
265          if( ok ) sprintf( dkptr->brick_name , "%s%s.%s",
266                            dkptr->directory_name ,
267                            dkptr->filecode , DATASET_BRICK_SUFFIX );
268 
269          else     sprintf( dkptr->brick_name , "%s/%s.%s",
270                            dkptr->directory_name ,
271                            dkptr->filecode , DATASET_BRICK_SUFFIX );
272 
273       /** COMPRESS for output added Feb 1998 */
274 
275          if( compress_mode == COMPRESS_NOFILE ) THD_enviro_write_compression() ;
276 
277 #ifdef COMPRESS_GZIP
278          /*-- 02 Mar 2001: check if we will force gzip --*/
279 
280          if( compress_mode == COMPRESS_NONE && AFNI_yesenv("AFNI_AUTOGZIP") ){
281             double entrop = ENTROPY_datablock(blk) ;
282             force_gzip = (entrop < 2.7) ;
283 #if 0
284 fprintf(stderr,"Entropy=%g ==> forcing write gzip on %s\n",entrop,dkptr->brick_name) ;
285 #endif
286          } else {
287             force_gzip = 0 ;
288          }
289          if( force_gzip ){
290             csave = compress_mode ; compress_mode = COMPRESS_GZIP ;
291          }
292 #endif
293 
294          far = COMPRESS_fopen_write( dkptr->brick_name , compress_mode ) ;
295          if( far == NULL ){
296            if( compress_mode != COMPRESS_NONE ){
297              compress_mode = COMPRESS_NONE ; force_gzip = 0 ;
298              far = COMPRESS_fopen_write( dkptr->brick_name , compress_mode ) ;
299            }
300          }
301          if( far == NULL )
302            WRITE_ERR("cannot open output brick file - do you have write permission?") ;
303 
304          /** write each brick out in a separate operation **/
305 
306          idone = 0 ;
307          for( ibr=0 ; ibr < nv ; ibr++ ){
308 
309            do_mripurge = MRI_IS_PURGED( DBLK_BRICK(blk,ibr) ) ;
310            if( do_mripurge ) mri_unpurge( DBLK_BRICK(blk,ibr) ) ;
311 
312            if( save_order != native_order ){       /* 25 April 1998 */
313              switch( DBLK_BRICK_TYPE(blk,ibr) ){
314                default: break ;
315                case MRI_short:
316                  mri_swap2( DBLK_BRICK_NVOX(blk,ibr) , DBLK_ARRAY(blk,ibr) ) ;
317                break ;
318 
319                case MRI_complex:   /* 23 Nov 1999 */
320                  mri_swap4( 2*DBLK_BRICK_NVOX(blk,ibr), DBLK_ARRAY(blk,ibr)) ;
321                break ;
322 
323                case MRI_float:     /* 23 Nov 1999 */
324                case MRI_int:
325                  mri_swap4( DBLK_BRICK_NVOX(blk,ibr) , DBLK_ARRAY(blk,ibr) ) ;
326                break ;
327              }
328            }
329 
330            idone += fwrite( DBLK_ARRAY(blk,ibr), 1, DBLK_BRICK_BYTES(blk,ibr), far );
331 
332            if( do_mripurge ){                    /* 31 Jan 2007 */
333              if( !purge_when_done ) mri_purge( DBLK_BRICK(blk,ibr) ) ;
334              else                   mri_clear( DBLK_BRICK(blk,ibr) ) ;
335            }
336          } /* end of loop over sub-bricks */
337 
338          COMPRESS_fclose(far) ;
339 
340          if( purge_when_done ){
341            if( blk->malloc_type == DATABLOCK_MEM_MMAP ){
342              free( DBLK_ARRAY(blk,0) ) ;
343              for( ibr=0 ; ibr < nv ; ibr++ )
344                mri_clear_data_pointer( DBLK_BRICK(blk,ibr) ) ;
345            } else {
346              THD_purge_datablock( blk , DATABLOCK_MEM_MALLOC ) ;
347            }
348          }
349 
350          if( compress_mode >= 0 || save_order != native_order ){
351            blk->malloc_type = DATABLOCK_MEM_MALLOC ;
352          }
353          DBLK_mmapfix(blk) ;  /* 28 Mar 2005 */
354 
355          if( force_gzip ) compress_mode = csave ; /* 02 Mar 2001 */
356 
357          if( idone != blk->total_bytes )
358            WRITE_ERR("Write error in brick file: Is disk full, or write_protected?") ;
359 
360          dkptr->byte_order = save_order ;  /* 23 Nov 1999 */
361 
362          return True ;
363       }
364       break ;
365 
366    }  /* end of switch over data storage mode */
367 
368    return False ;  /* should NEVER be reached */
369 }
370