[med-svn] [dcm2niix] 01/01: New upstream version 1.0.20170429

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu May 11 18:39:07 UTC 2017


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch upstream/latest
in repository dcm2niix.

commit 9e8d453f5e4397fd76ae9e6d88621d0406a38d15
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date:   Thu May 11 09:42:22 2017 +0100

    New upstream version 1.0.20170429
---
 .gitignore                         |    3 +
 .travis.yml                        |   33 +-
 CMakeLists.txt                     |   38 +-
 README.md => COMPILE.md            |  164 +----
 Dockerfile                         |   16 +
 README.md                          |  192 ++----
 SuperBuild/External-OPENJPEG.cmake |   15 +
 SuperBuild/External-YAML-CPP.cmake |   15 +
 SuperBuild/SuperBuild.cmake        |  111 ++++
 appveyor.yml                       |   32 +-
 console/CMakeLists.txt             |  169 +++--
 console/jpg_0XC3.cpp               |   78 ++-
 console/main_console.cpp           |   84 ++-
 console/main_console_batch.cpp     |  107 ++-
 console/makefile                   |    2 +-
 console/nifti1_io_core.cpp         |   28 +-
 console/nifti1_io_core.h           |    7 +
 console/nii_dicom.cpp              | 1191 ++++++++++++++++++---------------
 console/nii_dicom.h                |   58 +-
 console/nii_dicom_batch.cpp        | 1273 ++++++++++++++++++++----------------
 console/nii_dicom_batch.h          |   25 +-
 console/nii_foreign.cpp            |  404 ++++++++++++
 console/nii_foreign.h              |   19 +
 console/nii_ortho.cpp              |   16 +-
 console/nii_ortho.h                |    3 +
 console/print.h                    |   45 ++
 ucm.cmake => console/ucm.cmake     | 1245 ++++++++++++++++++-----------------
 console/ujpeg.cpp                  |  290 +++++++-
 console/ujpeg.h                    |    8 -
 docs/CMakeLists.txt                |   36 +
 docs/source/conf.py                |  340 ++++++++++
 docs/source/dcm2niibatch.rst       |   81 +++
 docs/source/dcm2niix.rst           |   73 +++
 docs/source/index.rst              |   22 +
 license.txt                        |    4 +-
 35 files changed, 3964 insertions(+), 2263 deletions(-)

diff --git a/.gitignore b/.gitignore
index e43b0f9..4afaec8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
 .DS_Store
+build/
+bin/
+
diff --git a/.travis.yml b/.travis.yml
index 2f84669..79f8259 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,39 +1,30 @@
 language: cpp
+
+git:
+  depth: 1
+
 matrix:
   include:
-    # Stable channel
     - os: linux
-      env: TARGET=linux
       dist: trusty
-      sudo: required
-      addons:
-        apt:
-          packages: pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
+      env: TARGET=lnx
     - os: osx
-      env: TARGET=osx
+      env: TARGET=mac
 
-before_install:
-  - "if [ ${TRAVIS_OS_NAME} = 'osx' ]; then brew update && brew install yaml-cpp; fi"
 script:
-  - mkdir build && cd build && cmake -DBATCH_VERSION=ON .. && make && cd -
+  - mkdir build && cd build && cmake -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON .. && make && cd -
 
 before_deploy:
-  - zip ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.zip build/bin/dcm2niibatch build/bin/dcm2niix
+  - export DATE=`date +%-d-%b-%Y`
+  - zip -j dcm2niix_${DATE}_${TARGET}.zip build/bin/*
+  - sleep 300 # make sure appveyor deployment is done, thus proper release name is set
 
 deploy:
   provider: releases
-  # TODO Regenerate this api_key for your project, this one won't work for you. Here's how:
-  # - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the
-  # `public_repo` scope enabled
-  # - Call `travis encrypt $github_token` where $github_token is the token you got in the previous
-  # step and `travis` is the official Travis CI gem (see https://rubygems.org/gems/travis/)
-  # - Enter the "encrypted value" below
   api_key:
-    secure: aLyVgmimaRMCvh2YF4ek0ZseWqKpqCSrvvYdf5rxhZUBnYK+KKlHBsPudtmWbHTh19PPC1C3wSHa8y94IObGl3iRRTICh5e/Oz9xzCiPYlQ0ZGmtM+MfRtUq2xFRc6aaAkrnfW0ur3uxk4a7ZBIOMg3D7kG7Ah4dg5dVf26OmpSBdEijo9bcvOLHXwZIc1bby+MJl8kxzdlwaV7UTrqHQFh+tmMsoO0GQcQpCP3y45f5/8aTkHRmnC1IKCiHKDZpRFDWhdoz02NGWcCnoH43iSwOobe1jacHicmZ7dNHKT/e1tfUIPpbO81fvQ7FHifvBRoO64Wvl5l/IaoDcngv6o11JlWRxIuZnr01oDv+DW8kv9POLYeh2xzMaQkZ8NkPZl82hqr8t0q0OtXZjm/Hysdcvr0T26hbwqs1sOkAAaeRdR0zl/Log53hNqM4HaZZ0CnKGU8dSatC+NOgHEfz68fHArf8DUdK [...]
+    secure: sVIYRakcEQdMPEdGSSePtMVCMQvaohqV7NNzEErAgZ+b/4ofv2aPpJb5kNTv3JRl2FrPy7iXJ8lOUQ/95pqvimX6jv5ztksTNXtSMnHZNbjjWwIc99enPY+mSdWMO2lb9vGBWQ9GNfXjmk7MgtDHPjjygbuZfUw9fmGy4ocxkws=
   file_glob: true
-  file: ${PROJECT_NAME}-${TRAVIS_TAG}-${TARGET}.*
-  # don't delete the artifacts from previous phases
+  file: dcm2niix*.zip
   skip_cleanup: true
-  # deploy when a new tag is pushed
   on:
     tags: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 707d390..e392dea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,37 +1,9 @@
-project(dcm2niix)
-cmake_minimum_required(VERSION 2.6)
-
-include(ucm.cmake)
+cmake_minimum_required(VERSION 2.8.11)
 
-# Option1: Choose whether to build the batch version
-option(BATCH_VERSION "Build dcm2niibatch for multiple conversions as well" OFF)
-message(${BATCH_VERSION})
-
-# Option2: Choose whether to use static runtime
-option(USE_STATIC_RUNTIME "Use static runtime" ON)
-if(USE_STATIC_RUNTIME)
-    ucm_set_runtime(STATIC)
-else()
-    ucm_set_runtime(DYNAMIC)
+if(COMMAND CMAKE_POLICY)
+  CMAKE_POLICY(SET CMP0003 NEW)
 endif()
 
-# Option3: Choose build type
-set(CMAKE_BUILD_TYPE "Release" CACHE STRING
-    "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
-
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
-
-#
-# Zlib
-#
-#find_package(ZLIB)
-
-# Predefined permission set to enforce proper permissions
-# during install even if files in the sources have different
-# settings
-set(FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+project(dcm2niix)
 
-##
-## Sub-projects
-##
-add_subdirectory(console)
+include(${CMAKE_SOURCE_DIR}/SuperBuild/SuperBuild.cmake)
diff --git a/README.md b/COMPILE.md
similarity index 50%
copy from README.md
copy to COMPILE.md
index 9d6b339..ed69b48 100644
--- a/README.md
+++ b/COMPILE.md
@@ -1,179 +1,61 @@
-[![Build Status](https://travis-ci.org/rordenlab/dcm2niix.svg?branch=master)](https://travis-ci.org/rordenlab/dcm2niix)
-[![Build status](https://ci.appveyor.com/api/projects/status/xdkqua54f90x4049/branch/master?svg=true)](https://ci.appveyor.com/project/chrisfilo/dcm2niix)
-
 ## About
 
-dcm2niix is a designed to convert neuroimaging data from the DICOM format to the NIfTI format. For details and compiled versions visit the [NITRC wiki](http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage).
-
-## License
-
-This software is open source. The bulk of the code is covered by the BSD license. Some units are either public domain or have similar licenses. See the license.txt file for more details.
-
-## Versions
-
-1-Nov-2016
- - AppVeyor Support (Ningfei Li & Chris Filo Gorgolewski)
- - Swap 3rd/4th dimensions for GE sequential multi-phase acquisitions (Niels Janssen)
-
-10-Oct-2016
- - Restores/improves building for the Windows operating system using MinGW.
-
-30-Sept-2016
- - Save ImageType (0x0008,0x0008) to BIDS.
- - Separate CT scans with different exposures.
- - Fixed issues where some compilers would generate erratic filenames for zero-padded series (e.g. "-f %3s")
-
-21-Sept-2016
- - Reduce verbosity (reduce number of repeated warnings, less scary warnings for derived rather than raw images).
- - Re-enable custom output directory "-o" option broken by 30-Apr-2016 version.
- - Deal with mis-behaved GE CT images where slice direction across images is not consistent.
- - Add new BIDS fields (field strength, manufacturer, etc).
- - Philips PAR/REC conversion now reports inconsistent requested vs measured TR (due to prospect. motion corr.?)
- - GE: Locations In Acquisition (0054, 0081) is inaccurate if slices are interpolated, use Images In Acquisition (0020,1002) if available
- - New filename options %d Series description (0008,103E), %z Sequence Name (0018,0024)
- - New filename options %a antenna (coil) number, %e echo number
- - Initialize unused portions of NIfTI header to zero so multiple runs always produce identical results
- - Supports 3D lossless JPEG saved as [multiple fragments](http://www.nitrc.org/forum/forum.php?thread_id=5872&forum_id=4703)
-
-
-5-May-2016
- - Crop 3D T1 acquisitions (e.g. ./dcm2niix -x y ~/DICOM).
-
-30-Apr-2016
- - Convert multiple files/folders with single command line invocation (e.g. ./dcm2niix -b y ~/tst ~/tst2).
-
-22-Apr-2016
- - Detect Siemens Phase maps (phase image names end with "_ph").
- - Use current working directory if file name not specified.
-
-12-Apr-2016
- - Provide override (command line option "-m y") to stack images of the same series even if they differ in study date/time, echo/coil number, or slice orientation. This mechanism allows users to concatenate images that break strict DICOM compliance.
+The README.md file describes the typical compilation of the software, using the `make` command to build the software. This document describes advanced methods for compiling and tuning the software.
 
-22-Mar-2016
- - Experimental support for [DICOM datasets without DICOM file meta information](http://dicom.nema.org/dicom/2013/output/chtml/part10/chapter_7.html).
+## Choosing your compiler
 
-12-Dec-2015
- - Support PAR/REC FP values when possible (see PMC3998685).
+The text below generally describes how to build dcm2niix using the [GCC](https://gcc.gnu.org) compiler using the `g++` command. However, the code is portable and you can use different compilers. For [clang/llvm](https://clang.llvm.org) compile using `clang++`.  If you have the [Intel C compiler](https://software.intel.com/en-us/c-compilers), you can substitute the `icc` command. For [Microsoft's C compiler](http://landinghub.visualstudio.com/visual-cpp-build-tools) you would use the `cl` [...]
 
-11-Nov-2015
- - Minor refinements.
 
-12-June-2015
- - Uses less memory (helpful for large datasets).
+## Building the command line version without cmake
 
-2-Feb-2015
- - Support for Visual Studio.
- - Remove dependency on zlib (now uses miniz).
-
-1-Jan-2015
- - Images separated based on TE (fieldmaps).
- - Support for JPEG2000 using OpenJPEG or Jasper libraries.
- - Support for JPEG using NanoJPEG library.
- - Support for lossless JPEG using custom library.
-
-24-Nov-2014
- - Support for CT scans with gantry tilt and varying distance between slices.
-
-11-Oct-2014
- - Initial public release.
-
-## Running
-
-Command line usage is described in the [NITRC wiki](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#General_Usage). The minimal command line call would be `dcm2niix /path/to/dicom/folder`. However, you may want to invoke additional options, for example the call `dcm2niix -z y -f %p_%t_%s -o /path/ouput /path/to/dicom/folder` will save data as gzip compressed, with the filename based on the protocol name (%p) acquisition time (%t) and DICOM series number (%s), with all file [...]
-
-**Optional batch processing version:**
-
-Perform a batch conversion of multiple dicoms using the configurations specified in a yaml file.
-```bash
-dcm2niibatch batch_config.yml
-```
+You can also build the software without C-make. The easiest way to do this is to run the function "make" from the "console" folder. Note that this only creates the default version of dcm2niix, not the optional batch version described above. The make command simply calls the g++ compiler, and if you want you can tune this for your build. In essence, the make function simply calls
 
-The configuration file should be in yaml format as shown in example `batch_config.yaml`
-
-```yaml
-Options:
-  isGz:             false
-  isFlipY:          false
-  isVerbose:        false
-  isCreateBIDS:     false
-  isOnlySingleFile: false
-Files:
-    -
-      in_dir:           /path/to/first/folder
-      out_dir:          /path/to/output/folder
-      filename:         dcemri
-    -
-      in_dir:           /path/to/second/folder
-      out_dir:          /path/to/output/folder
-      filename:         fa3
 ```
-
-You can add as many files as you want to convert as long as this structure stays consistent. Note that a dash must separate each file.
-
-## Build
-
-### Build command line version with cmake (Linux, Windows, OSx)
-
-```bash
-mkdir build && cd build
-cmake ..
-make
+g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
 ```
-`dcm2niix` will be created in the `bin` folder
-
-**optional batch processing version:**
-
-The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`
 
-This requires the following libraries:
-- pkg-config
-- yaml-cpp
-- a compiler that supports c++11
+The following sub-sections list how you can modify this basic recipe for your needs.
 
-e.g. the dependencies can be installed as follows:
+##### ZLIB BUILD
+ If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ)
 
-Ubuntu 14.04
 ```
-sudo apt-get install pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
-```
-OSX
-```
-brew install pkg-config yaml-cpp cmake
+g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -dead_strip -o dcm2niix -lz -DmyDisableMiniZ
 ```
 
+##### MINGW BUILD
 
-### Building the command line version without cmake
-
-You can also build the software without C-make. The easiest way to do this is to run the function "make" from the "console" folder. Note that this only creates the default version of dcm2niix, not the optional batch version described above. The make command simply calls the g++ compiler, and if you want you can tune this for your build. In essence, the make function simply calls
+If you use the (obsolete) compiler MinGW on Windows you will want to include the rare libgcc libraries with your executable so others can use it. Here I also demonstrate the optional "-DmyDisableZLib" to remove zip support.
 
 ```
-g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
+g++ -O3 -s -DmyDisableOpenJPEG -DmyDisableZLib -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -o dcm2niix  -static-libgcc
 ```
 
-The following sub-sections list how you can modify this basic recipe for your needs.
+##### DISABLING CLASSIC JPEG
 
-##### ZLIB BUILD
- If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ)
+DICOM images can be stored as either raw data or compressed using one of many formats as described by the [transfer syntaxes](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Transfer_Syntaxes_and_Compressed_Images). One of the compressed formats is the lossy classic JPEG format (which is separate from and predates the lossy JPEG 2000 format). This software comes with the [NanoJPEG](http://keyj.emphy.de/nanojpeg/) library to handle these images. However, you can use the `my [...]
 
 ```
-g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -dead_strip -o dcm2niix -lz -DmyDisableMiniZ
+g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableClassicJPEG -DmyDisableOpenJPEG -DmyDisableJasper
 ```
 
-##### MINGW BUILD
+##### USING LIBJPEG-TURBO TO DECODE CLASSIC JPEG
 
-If you use the (obsolete) compiler MinGW on Windows you will want to include the rare libgcc libraries with your executable so others can use it. Here I also demonstrate the optional "-DmyDisableZLib" to remove zip support.
+By default, classic JPEG images will be decoded using the [compact NanoJPEG decoder](http://keyj.emphy.de/nanojpeg/). However, the compiler directive `myTurboJPEG`  will create an executable based on the [libjpeg-turbo](http://www.libjpeg-turbo.org) library. This library is a faster decoder and is the standard for many Linux distributions. On the other hand, the lossy classic JPEG is rarely used for DICOM images, so this compilation has extra dependencies and can result in a larger execu [...]
 
 ```
-g++ -O3 -s -DmyDisableOpenJPEG -DmyDisableZLib -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -o dcm2niix  -static-libgcc
+g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper -DmyTurboJPEG -I/opt/libjpeg-turbo/include /opt/libjpeg-turbo/lib/libturbojpeg.a
 ```
 
 ##### JPEG2000 BUILD
 
- If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries...
- svn checkout http://openjpeg.googlecode.com/svn/trunk/ openjpeg-read-only
+ If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries where you would [download the code](https://github.com/uclouvain/openjpeg) and run
+```
  cmake -DBUILD_SHARED_LIBS:bool=off .
  make
  sudo make install
+```
 You should then be able to run then run:
 
 ```
@@ -232,7 +114,7 @@ file ./dcm2niix
 
 You can building the OSX graphical user interface using Xcode. First, Copy contents of "console" folder to /xcode/dcm2/core. Next, open and compile the project "dcm2.xcodeproj" with Xcode 4.6 or later
 
-##### THE QT AND wxWIDGETS GUIs ARE NOT YET SUPPORT - FOLLOWING LINES FOR FUTURE VERSIONS
+##### THE QT AND wxWIDGETS GUIs ARE NOT YET SUPPORTED - FOLLOWING LINES FOR FUTURE VERSIONS
 
 Building QT graphical user interface:
   Open "dcm2.pro" with QTCreator. This should work on OSX and Linux. On Windows the printf information is not redirected to the user interface
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..6f394ce
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,16 @@
+FROM ubuntu:trusty
+MAINTAINER <alexcohen at gmail.com> # feel free to change/adopt
+
+# Install Dependencies
+RUN apt-get update && apt-get upgrade -y && \
+	apt-get install -y build-essential pkg-config cmake git pigz && \
+	apt-get clean -y && apt-get autoclean -y && apt-get autoremove -y
+
+# Get dcm2niix from github and compile
+RUN cd /tmp && \
+	git clone https://github.com/rordenlab/dcm2niix.git && \
+	cd dcm2niix && mkdir build && cd build && \
+	cmake -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON .. && \
+	make && make install
+
+ENTRYPOINT ["/usr/local/bin/dcm2niix"]
diff --git a/README.md b/README.md
index 9d6b339..2e1fb63 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,41 @@
 [![Build Status](https://travis-ci.org/rordenlab/dcm2niix.svg?branch=master)](https://travis-ci.org/rordenlab/dcm2niix)
-[![Build status](https://ci.appveyor.com/api/projects/status/xdkqua54f90x4049/branch/master?svg=true)](https://ci.appveyor.com/project/chrisfilo/dcm2niix)
+[![Build status](https://ci.appveyor.com/api/projects/status/7o0xp2fgbhadkgn1?svg=true)](https://ci.appveyor.com/project/neurolabusc/dcm2niix)
 
 ## About
 
-dcm2niix is a designed to convert neuroimaging data from the DICOM format to the NIfTI format. For details and compiled versions visit the [NITRC wiki](http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage).
+dcm2niix is a designed to convert neuroimaging data from the DICOM format to the NIfTI format. This web page hosts the developmental source code - a compiled version for Linux, MacOS, and Windows of the most recent stable release is included with [MRIcroGL](https://www.nitrc.org/projects/mricrogl/). A full manual for this software is available in the form of a [NITRC wiki](http://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage).
 
 ## License
 
-This software is open source. The bulk of the code is covered by the BSD license. Some units are either public domain or have similar licenses. See the license.txt file for more details.
+This software is open source. The bulk of the code is covered by the BSD license. Some units are either public domain (nifti*.*, miniz.c) or use the MIT license (ujpeg.cpp). See the license.txt file for more details.
 
 ## Versions
 
+Development
+ - Remove all derived images from [Philips DTI series](http://www.nitrc.org/forum/message.php?msg_id=21025).
+
+28-April-2017
+ - Experimental [ECAT support](https://github.com/rordenlab/dcm2niix/issues/95).
+ - Updated cmake to make JPEG2000 support easier with improved Travis and AppVeyor support [Ningfei Li](https://github.com/ningfei).
+ - Supports Data/Time for images that report Data/Time (0008,002A) but not separate Date and Time (0008,0022 and 0008,0032).
+ - [BIDS reports morning times correctly](http://www.nitrc.org/forum/message.php?msg_id=20852).
+ - Options -1..-9 to control [gz compression level](https://github.com/rordenlab/dcm2niix/issues/90).
+ - Includes some [PET details in the BIDS JSON sidecar](https://github.com/rordenlab/dcm2niix/issues/87).
+ - Better detection of image order for Philips 4D DICOM (reported by Jason McMorrow and Stephen Wilson).
+ - [Include StudyInstanceUID and SeriesInstanceUID in filename](https://github.com/rordenlab/dcm2niix/issues/94).
+
+7-Feb-2017
+ - Can be compiled to use either Philips [Float or Display](http://www.nitrc.org/forum/message.php?msg_id=20213) intensity intercept and slope values.
+ - Handle 3D Philips DICOM and [PAR/REC](https://www.nitrc.org/forum/forum.php?thread_id=7707&forum_id=4703) files where images are not stored in a spatially contiguous order.
+ - Handle DICOM violations where icon is uncompressed but image data is compressed.
+ - Best guess matrix for 2D slices (similar to dcm2nii, SPM and MRIconvert).
+ - Linux (case sensitive filenames) now handles par/rec as well as PAR/REC.
+ - Images with unknown phase encoding do not generate [BIDS entry](https://github.com/rordenlab/dcm2niix/issues/79).
+ - Unified printMessage/printWarning/printError aids embedding in other projects, such as [divest](https://github.com/jonclayden/divest).
+
 1-Nov-2016
  - AppVeyor Support (Ningfei Li & Chris Filo Gorgolewski)
- - Swap 3rd/4th dimensions for GE sequential multi-phase acquisitions (Niels Janssen)
+ - Swap 3rd/4th dimensions for GE sequential multi-phase acquisitions (Niels Janssen).
 
 10-Oct-2016
  - Restores/improves building for the Windows operating system using MinGW.
@@ -21,20 +43,19 @@ This software is open source. The bulk of the code is covered by the BSD license
 30-Sept-2016
  - Save ImageType (0x0008,0x0008) to BIDS.
  - Separate CT scans with different exposures.
- - Fixed issues where some compilers would generate erratic filenames for zero-padded series (e.g. "-f %3s")
+ - Fixed issues where some compilers would generate erratic filenames for zero-padded series (e.g. "-f %3s").
 
 21-Sept-2016
  - Reduce verbosity (reduce number of repeated warnings, less scary warnings for derived rather than raw images).
  - Re-enable custom output directory "-o" option broken by 30-Apr-2016 version.
  - Deal with mis-behaved GE CT images where slice direction across images is not consistent.
  - Add new BIDS fields (field strength, manufacturer, etc).
- - Philips PAR/REC conversion now reports inconsistent requested vs measured TR (due to prospect. motion corr.?)
- - GE: Locations In Acquisition (0054, 0081) is inaccurate if slices are interpolated, use Images In Acquisition (0020,1002) if available
- - New filename options %d Series description (0008,103E), %z Sequence Name (0018,0024)
- - New filename options %a antenna (coil) number, %e echo number
- - Initialize unused portions of NIfTI header to zero so multiple runs always produce identical results
- - Supports 3D lossless JPEG saved as [multiple fragments](http://www.nitrc.org/forum/forum.php?thread_id=5872&forum_id=4703)
-
+ - Philips PAR/REC conversion now reports inconsistent requested vs measured TR (due to prospect. motion corr.?).
+ - GE: Locations In Acquisition (0054, 0081) is inaccurate if slices are interpolated, use Images In Acquisition (0020,1002) if available.
+ - New filename options %d Series description (0008,103E), %z Sequence Name (0018,0024).
+ - New filename options %a antenna (coil) number, %e echo number.
+ - Initialize unused portions of NIfTI header to zero so multiple runs always produce identical results.
+ - Supports 3D lossless JPEG saved as [multiple fragments](http://www.nitrc.org/forum/forum.php?thread_id=5872&forum_id=4703).
 
 5-May-2016
  - Crop 3D T1 acquisitions (e.g. ./dcm2niix -x y ~/DICOM).
@@ -112,150 +133,33 @@ You can add as many files as you want to convert as long as this structure stays
 
 ## Build
 
-### Build command line version with cmake (Linux, Windows, OSx)
+### Build command line version with cmake (Linux, MacOS, Windows)
+
+`cmake` and `pkg-config` (optional) can be installed as follows:
+
+Ubuntu: `sudo apt-get install cmake pkg-config`
 
+MacOS: `brew install cmake pkg-config`
+
+**To build:**
 ```bash
 mkdir build && cd build
 cmake ..
 make
 ```
-`dcm2niix` will be created in the `bin` folder
+`dcm2niix` will be created in the `bin` subfolder. To install on the system run `make install`.
 
-**optional batch processing version:**
+**optional building with OpenJPEG:**
 
-The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`
+Support for JPEG2000 using OpenJPEG is optional. To build with OpenJPEG change the cmake command to `cmake -DUSE_OPENJPEG=ON ..`
 
-This requires the following libraries:
-- pkg-config
-- yaml-cpp
-- a compiler that supports c++11
+**optional batch processing version:**
 
-e.g. the dependencies can be installed as follows:
+The batch processing binary `dcm2niibatch` is optional. To build `dcm2niibatch` as well change the cmake command to `cmake -DBATCH_VERSION=ON ..`
 
-Ubuntu 14.04
-```
-sudo apt-get install pkg-config libyaml-cpp-dev libyaml-cpp0.5 cmake libboost-dev
-```
-OSX
-```
-brew install pkg-config yaml-cpp cmake
-```
+This requires a compiler that supports c++11.
 
 
 ### Building the command line version without cmake
 
-You can also build the software without C-make. The easiest way to do this is to run the function "make" from the "console" folder. Note that this only creates the default version of dcm2niix, not the optional batch version described above. The make command simply calls the g++ compiler, and if you want you can tune this for your build. In essence, the make function simply calls
-
-```
-g++ -dead_strip -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
-```
-
-The following sub-sections list how you can modify this basic recipe for your needs.
-
-##### ZLIB BUILD
- If we have zlib, we can use it (-lz) and disable [miniz](https://code.google.com/p/miniz/) (-myDisableMiniZ)
-
-```
-g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -dead_strip -o dcm2niix -lz -DmyDisableMiniZ
-```
-
-##### MINGW BUILD
-
-If you use the (obsolete) compiler MinGW on Windows you will want to include the rare libgcc libraries with your executable so others can use it. Here I also demonstrate the optional "-DmyDisableZLib" to remove zip support.
-
-```
-g++ -O3 -s -DmyDisableOpenJPEG -DmyDisableZLib -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -o dcm2niix  -static-libgcc
-```
-
-##### JPEG2000 BUILD
-
- If you want to build this with JPEG2000 decompression support using OpenJPEG. You will need to have the OpenJPEG 2.1 libraries installed (https://code.google.com/p/openjpeg/wiki/Installation). I suggest building static libraries...
- svn checkout http://openjpeg.googlecode.com/svn/trunk/ openjpeg-read-only
- cmake -DBUILD_SHARED_LIBS:bool=off .
- make
- sudo make install
-You should then be able to run then run:
-
-```
-g++ -O3 -dead_strip -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  jpg_0XC3.cpp ujpeg.cpp -o dcm2niix -lopenjp2
-```
-
-But in my experience this works best if you explicitly tell the software how to find the libraries, so your compile will probably look like one of these two options:
-
-```
-g++ -O3 -dead_strip -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -o dcm2niix  -I/usr/local/include /usr/local/lib/libopenjp2.a
-```
-
-```
-g++ -O3 -dead_strip -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -o dcm2niix  -I/usr/local/lib /usr/local/lib/libopenjp2.a
-```
-
- If you want to build this with JPEG2000 decompression support using Jasper: You will need to have the Jasper (http://www.ece.uvic.ca/~frodo/jasper/) and libjpeg (http://www.ijg.org) libraries installed which for Linux users may be as easy as running 'sudo apt-get install libjasper-dev' (otherwise, see http://www.ece.uvic.ca/~frodo/jasper/#doc). You can then run:
-
-```
-g++ -O3 -DmyDisableOpenJPEG -DmyEnableJasper -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp  -s -o dcm2niix -ljasper -ljpeg
-```
-
-##### VISUAL STUDIO BUILD
-
-This software can be compiled with VisualStudio 2015. This example assumes the compiler is in your path.
-
-```
-vcvarsall amd64
-cl /EHsc main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -DmyDisableOpenJPEG -DmyDisableJasper /odcm2niix
-```
-
-##### OSX BUILD WITH BOTH 32 AND 64-BIT SUPPORT
-
-Building command line version universal binary from OSX 64 bit system:
- This requires a C compiler. With a terminal, change directory to the 'conosle' folder and run the following:
-
-```
-g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -dead_strip -arch i386 -o dcm2niix32
-```
-
-```
-g++ -O3 -DmyDisableOpenJPEG -I. main_console.cpp nii_dicom.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp jpg_0XC3.cpp ujpeg.cpp -dead_strip -o dcm2niix64
-```
-
-```
-lipo -create dcm2niix32 dcm2niix64 -o dcm2niix
-```
-
- To validate that the resulting executable supports both architectures type
-
-```
-file ./dcm2niix
-```
-
-##### OSX GRAPHICAL INTERFACE BUILD
-
-You can building the OSX graphical user interface using Xcode. First, Copy contents of "console" folder to /xcode/dcm2/core. Next, open and compile the project "dcm2.xcodeproj" with Xcode 4.6 or later
-
-##### THE QT AND wxWIDGETS GUIs ARE NOT YET SUPPORT - FOLLOWING LINES FOR FUTURE VERSIONS
-
-Building QT graphical user interface:
-  Open "dcm2.pro" with QTCreator. This should work on OSX and Linux. On Windows the printf information is not redirected to the user interface
-  If compile gives you grief look at the .pro file which has notes for different operating systems.
-
-Building using wxWidgets
-wxWdigets makefiles are pretty complex and specific for your operating system. For simplicity, we will build the "clipboard" example that comes with wxwidgets and then substitute our own code. The process goes something like this.
- a.) Install wxwdigets
- b.) successfully "make" the samples/clipboard program
- c.) DELETE console/makefile. WE DO NOT WANT TO OVERWRITE the WX MAKEFILE
- d.) with the exception of "makefile", copy the contents of console to /samples/clipboard
- e.) overwrite the original /samples/clipboard.cpp with the dcm2niix file of the same name
- f.) Older Xcodes have problems with .cpp files, whereas wxWidgets's makefiles do not compile with "-x cpp". So the core files are called '.c' but we will rename them to .cpp for wxWidgets:
- rename 's/\.c$/\.cpp/' *
- g.) edit the /samples/clipboard makefile: Add "nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \" to CLIPBOARD_OBJECTS:
-CLIPBOARD_OBJECTS =  \
-  nii_dicom.o nifti1_io_core.o nii_ortho.o nii_dicom_batch.o \
-  $(__clipboard___win32rc) \
-  $(__clipboard_os2_lib_res) \
-  clipboard_clipboard.o
- h.) edit the /samples/clipboard makefile: With wxWidgets we will capture std::cout comments, not printf, so we need to add "-DDmyUseCOut" to CXXFLAGS:
-CXXFLAGS = -DmyUseCOut -DWX_PRECOMP ....
- i.) For a full refresh
-rm clipboard
-rm *.o
-make
+[See the COMPILE.md file for details on manual compilation](./COMPILE.md).
diff --git a/SuperBuild/External-OPENJPEG.cmake b/SuperBuild/External-OPENJPEG.cmake
new file mode 100644
index 0000000..38f7301
--- /dev/null
+++ b/SuperBuild/External-OPENJPEG.cmake
@@ -0,0 +1,15 @@
+set(OPENJPEG_TAG 151e322) # version openjepg-2.1
+
+ExternalProject_Add(openjpeg
+    GIT_REPOSITORY "${git_protocol}://github.com/ningfei/openjpeg.git"
+    GIT_TAG "${OPENJPEG_TAG}"
+    SOURCE_DIR openjpeg
+    BINARY_DIR openjpeg-build
+    CMAKE_ARGS
+        -Wno-dev
+        --no-warn-unused-cli
+        -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
+)
+
+set(OPENJPEG_DIR ${CMAKE_BINARY_DIR}/lib/openjpeg-2.1)
diff --git a/SuperBuild/External-YAML-CPP.cmake b/SuperBuild/External-YAML-CPP.cmake
new file mode 100644
index 0000000..ada7d8f
--- /dev/null
+++ b/SuperBuild/External-YAML-CPP.cmake
@@ -0,0 +1,15 @@
+set(YAML-CPP_TAG 5c3cb09) # version yaml-cpp-0.5.3
+
+ExternalProject_Add(yaml-cpp
+    GIT_REPOSITORY "${git_protocol}://github.com/ningfei/yaml-cpp.git"
+    GIT_TAG "${YAML-CPP_TAG}"
+    SOURCE_DIR yaml-cpp
+    BINARY_DIR yaml-cpp-build
+    CMAKE_ARGS
+        -Wno-dev
+        --no-warn-unused-cli
+        -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
+)
+
+set(YAML-CPP_DIR ${CMAKE_BINARY_DIR}/lib/cmake/yaml-cpp)
diff --git a/SuperBuild/SuperBuild.cmake b/SuperBuild/SuperBuild.cmake
new file mode 100644
index 0000000..969b95a
--- /dev/null
+++ b/SuperBuild/SuperBuild.cmake
@@ -0,0 +1,111 @@
+# Check if git exists
+find_package(Git)
+if(NOT GIT_FOUND)
+    message(ERROR "Cannot find git. git is required for Superbuild")
+endif()
+
+# Use git protocol or not
+option(USE_GIT_PROTOCOL "If behind a firewall turn this off to use http instead." ON)
+if(USE_GIT_PROTOCOL)
+    set(git_protocol "git")
+else()
+    set(git_protocol "https")
+endif()
+
+# Basic CMake build settings
+set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+    "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+
+option(USE_STATIC_RUNTIME "Use static runtime" ON)
+
+option(USE_SYSTEM_ZLIB "Use the system zlib" OFF)
+option(USE_SYSTEM_TURBOJPEG "Use the system TurboJPEG" OFF)
+option(USE_JASPER "Compile with Jasper support" OFF)
+
+option(USE_OPENJPEG "Build with OpenJPEG support" OFF)
+option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF)
+
+include(ExternalProject)
+
+set(DEPENDENCIES)
+
+if(USE_OPENJPEG)
+    message("-- Build with OpenJPEG: ${USE_OPENJPEG}")
+
+    find_package(PkgConfig)
+    if(PKG_CONFIG_FOUND)
+        pkg_check_modules(OPENJPEG libopenjp2)
+    endif()
+
+    if(OPENJPEG_FOUND)
+        set(OPENJPEG_DIR ${OPENJPEG_LIBDIR}/openjepg-2.1)
+        message("--     Using the system OpenJPEG")
+    else()
+        include(${CMAKE_SOURCE_DIR}/SuperBuild/External-OPENJPEG.cmake)
+        list(APPEND DEPENDENCIES openjpeg)
+        set(BUILD_OPENJPEG TRUE)
+        message("--     Will build OpenJPEG from github")
+    endif()
+endif()
+
+if(BATCH_VERSION)
+    message("-- Build dcm2niibatch: ${BATCH_VERSION}")
+
+    find_package(PkgConfig)
+    if(PKG_CONFIG_FOUND)
+        pkg_check_modules(YAML-CPP yaml-cpp)
+    endif()
+
+    if(YAML-CPP_FOUND)
+        set(YAML-CPP_DIR ${YAML-CPP_LIBDIR}/cmake/yaml-cpp)
+        message("--     Using the system yaml-cpp")
+    else()
+        include(${CMAKE_SOURCE_DIR}/SuperBuild/External-YAML-CPP.cmake)
+        list(APPEND DEPENDENCIES yaml-cpp)
+        set(BUILD_YAML-CPP TRUE)
+        message("--     Will build yaml-cpp from github")
+    endif()
+endif()
+
+ExternalProject_Add(console
+    DEPENDS ${DEPENDENCIES}
+    DOWNLOAD_COMMAND ""
+    SOURCE_DIR ${CMAKE_SOURCE_DIR}/console
+    BINARY_DIR console-build
+    CMAKE_ARGS
+        -Wno-dev
+        --no-warn-unused-cli
+        -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+        -DUSE_STATIC_RUNTIME:BOOL=${USE_STATIC_RUNTIME}
+        -DUSE_SYSTEM_ZLIB:BOOL=${USE_SYSTEM_ZLIB}
+        -DUSE_SYSTEM_TURBOJPEG:BOOL=${USE_SYSTEM_TURBOJPEG}
+        -DUSE_SYSTEM_JASPER:BOOL=${USE_SYSTEM_JASPER}
+         # OpenJPEG
+        -DUSE_OPENJPEG:BOOL=${USE_OPENJPEG}
+        -DOpenJPEG_DIR:PATH=${OPENJPEG_DIR}
+        # yaml-cpp
+        -DBATCH_VERSION:BOOL=${BATCH_VERSION}
+        -DYAML-CPP_DIR:PATH=${YAML-CPP_DIR}
+        -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}
+)
+
+install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/ DESTINATION bin
+        USE_SOURCE_PERMISSIONS)
+
+# Install dependent libraries for future use
+if(BUILD_OPENJPEG OR BUILD_YAML-CPP)
+    option(INSTALL_DEPENDENCIES "Optionally install dependent libraries (OpenJPEG and yaml-cpp) for future use." OFF)
+    if(INSTALL_DEPENDENCIES)
+        install(DIRECTORY ${CMAKE_BINARY_DIR}/include/ DESTINATION include
+                USE_SOURCE_PERMISSIONS)
+        install(DIRECTORY ${CMAKE_BINARY_DIR}/lib/ DESTINATION lib
+                USE_SOURCE_PERMISSIONS)
+    endif()
+endif()
+
+option(BUILD_DOCS "Build documentation (manpages)" OFF)
+if(BUILD_DOCS)
+    add_subdirectory(docs)
+endif()
+
diff --git a/appveyor.yml b/appveyor.yml
index f964fdc..3c099d1 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -14,25 +14,25 @@ clone_folder: c:\projects\dcm2niix
 
 init:
 - ps: >-
-    $version = Get-Date -Format "d-MMM-yyyy"
+    $env:DATE = $(Get-Date -Format d-MMM-yyyy)
 
     $githash = $env:APPVEYOR_REPO_COMMIT.Substring(0, 7)
 
-    Update-AppveyorBuild -Version "$version-$githash"
+    $gittag = if ($env:APPVEYOR_REPO_TAG -eq $True) {"_$($env:APPVEYOR_REPO_TAG_NAME)"} else {""}
 
-before_build:
-- cmd: >-
-    echo "Clone yaml-cpp"
+    Update-AppveyorBuild -Version "$($env:DATE)_g${githash}${gittag}"
 
-    git clone -q --depth=1 --branch=master https://github.com/jbeder/yaml-cpp.git c:\projects\dcm2niix\yaml-cpp
+    $env:release_version = $(Get-Date -Format d-MMMM-yyyy)
 
+before_build:
+- cmd: >-
     echo "Running cmake"
 
     mkdir c:\projects\dcm2niix\build
 
     cd c:\projects\dcm2niix\build
 
-    cmake -G "Visual Studio 14 2015 Win64" -DBATCH_VERSION=ON -Wno-dev ..\
+    cmake -G "Visual Studio 14 2015 Win64" -DBATCH_VERSION=ON -DUSE_OPENJPEG=ON ..\
 
 build:
   project: c:\projects\dcm2niix\build\dcm2niix.sln
@@ -43,8 +43,22 @@ after_build:
 - ps: >-
     cd c:\projects\dcm2niix
 
-    7z a dcm2niix-$version-win.zip c:\projects\dcm2niix\bin\Release\* >$null
+    7z a dcm2niix_$($env:DATE)_win.zip c:\projects\dcm2niix\build\bin\* >$null
 
 artifacts:
-  - path: 'dcm2niix-*.zip'
+  - path: dcm2niix*.zip
     name: dcm2niix
+
+deploy:
+  - provider: GitHub
+    tag: $(appveyor_repo_tag_name)
+    release: version $(release_version) ($(appveyor_repo_tag_name))
+    description: ""
+    auth_token:
+      secure: gCltVLQEWsjSTRlsi8qw7FGP54ujBq60apjXkWTV954b65bOHl95hXMxxkQ734L4
+    artifact: dcm2niix
+    draft: false
+    prerelease: false
+    on:
+      branch: master
+      appveyor_repo_tag: true
diff --git a/console/CMakeLists.txt b/console/CMakeLists.txt
index 30199f8..e57086a 100644
--- a/console/CMakeLists.txt
+++ b/console/CMakeLists.txt
@@ -1,93 +1,130 @@
+cmake_minimum_required(VERSION 2.8.11)
+
 project(console)
-set(PROGRAMS dcm2niix)
 
-if(POLICY CMP0054)
-  cmake_policy(SET CMP0054 NEW)
+# Option Choose whether to use static runtime
+include(ucm.cmake)
+option(USE_STATIC_RUNTIME "Use static runtime" ON)
+if(USE_STATIC_RUNTIME)
+    ucm_set_runtime(STATIC)
+else()
+    ucm_set_runtime(DYNAMIC)
 endif()
 
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+set(PROGRAMS dcm2niix)
+
+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
     # using Clang
-    add_definitions(-dead_strip)
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+    add_definitions(-Wno-deprecated-register)
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
     # using GCC
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
+    add_definitions(-Wno-unused-result)
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
     # using Intel C++
-elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
     # using Visual Studio C++
-endif ()
+    add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4018")   # '<': signed/unsigned mismatch
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068")   # unknown pragma
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4101")   # unreferenced local variable
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244")   # 'initializing': conversion from 'double' to 'int', possible loss of data
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267")   # 'initializing': conversion from 'size_t' to 'int', possible loss of data
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4305")   # 'argument': truncation from 'double' to 'float'
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4308")   # negative integral constant converted to unsigned type
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4334")   # '<<': result of 32-bit shift implicitly converted to 64 bits
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800")   # 'uint32_t' : forcing value to bool 'true' or 'false'
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")   # The file contains a character that cannot be represented in the current code page
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996")   # 'access': The POSIX name for this item is deprecated
+endif()
+
+add_executable(dcm2niix
+    main_console.cpp
+    nii_dicom.cpp
+    jpg_0XC3.cpp
+    ujpeg.cpp
+    nifti1_io_core.cpp
+    nii_foreign.cpp
+    nii_ortho.cpp
+    nii_dicom_batch.cpp)
+
+option(USE_SYSTEM_ZLIB "Use the system zlib" OFF)
+if(USE_SYSTEM_ZLIB)
+    find_package(ZLIB REQUIRED)
+    add_definitions(-DmyDisableMiniZ)
+    target_include_directories(dcm2niix PRIVATE ${ZLIB_INCLUDE_DIRS})
+    target_link_libraries(dcm2niix ${ZLIB_LIBRARIES})
+endif()
+
+option(USE_SYSTEM_TURBOJPEG "Use the system TurboJPEG" OFF)
+if(USE_SYSTEM_TURBOJPEG)
+    find_package(PkgConfig REQUIRED)
+    pkg_check_modules(TurboJPEG libturbojpeg)
+    add_definitions(-DmyTurboJPEG)
+    target_include_directories(dcm2niix PRIVATE ${TurboJPEG_INCLUDE_DIRS})
+    target_link_libraries(dcm2niix ${TurboJPEG_LIBRARIES})
+endif()
 
+option(USE_JASPER "Compile with Jasper support" OFF)
+if(USE_JASPER)
+    find_package(Jasper REQUIRED)
+    add_definitions(-DmyEnableJasper)
+    target_include_directories(dcm2niix PRIVATE ${JASPER_INCLUDE_DIR})
+    target_link_libraries(dcm2niix ${JASPER_LIBRARIES})
+else ()
+    add_definitions(-DmyDisableJASPER)
+endif()
 
-#We now use miniz, removing zlib dependency
-#if(ZLIB_FOUND)
-#  TARGET_LINK_LIBRARIES(dcm2niix z)
-#else(ZLIB_FOUND)
-#  ADD_DEFINITIONS(-DmyDisableZlib)
-#endif(ZLIB_FOUND)
-add_definitions(-DmyDisableJasper)
-add_definitions(-DmyDisableOpenJPEG)
+option(USE_OPENJPEG "Build with OpenJPEG support" OFF)
+if(USE_OPENJPEG)
+    option(OpenJPEG_DIR "Path to OpenJPEG configuration file" "")
 
-add_executable(dcm2niix
-        main_console.cpp
+    find_package(OpenJPEG)
+    target_include_directories(dcm2niix PRIVATE ${OPENJPEG_INCLUDE_DIRS})
+    target_link_libraries(dcm2niix ${OPENJPEG_LIBRARIES})
+else ()
+    add_definitions(-DmyDisableOpenJPEG)
+endif()
+
+option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF)
+if(BATCH_VERSION)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+    add_executable(dcm2niibatch
+        main_console_batch.cpp
         nii_dicom.cpp
         jpg_0XC3.cpp
         ujpeg.cpp
         nifti1_io_core.cpp
+        nii_foreign.cpp
         nii_ortho.cpp
         nii_dicom_batch.cpp)
 
+    option(YAML-CPP_DIR "Path to yaml-cpp configuration file" "")
 
-if (BATCH_VERSION)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    find_package(YAML-CPP)
+    target_include_directories(dcm2niibatch PRIVATE ${YAML_CPP_INCLUDE_DIR})
+    target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARIES})
 
-    message("Creating batch version")
-
-    # Resolve system dependency on yaml-cpp, which apparently does not
-    # provide a CMake find_package() module.
-
-    # Step 1: Search for library using pkg-config
-    find_package(PkgConfig)
-    if (PKG_CONFIG_FOUND)
-        pkg_check_modules(YAML_CPP yaml-cpp)
-        find_path(YAML_CPP_INCLUDE_DIR
-                NAMES yaml-cpp
-                PATHS ${YAML_CPP_INCLUDE_DIRS})
-        find_library(YAML_CPP_LIBRARY
-                NAMES yaml-cpp
-                PATHS ${YAML_CPP_LIBRARY_DIRS})
-    endif ()
-
-    # Step 2: Search for library locally. Only searches for a folder called yaml-cpp
-    if (NOT YAML_CPP_LIBRARY)
-        message("Checking for yaml-cpp local folder: yaml-cpp")
-        if (EXISTS ${CMAKE_SOURCE_DIR}/yaml-cpp)
-            message("local version found")
-            SET(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Build test packages off")
-            add_subdirectory(${CMAKE_SOURCE_DIR}/yaml-cpp ${CMAKE_SOURCE_DIR}/yaml-cpp)
-            set(YAML_CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/yaml-cpp/include)
-        else()
-            message("Local version not found")
-        endif()
-    endif ()
+    if(ZLIB_FOUND)
+        add_definitions(-DmyDisableMiniZ)
+        target_link_libraries(dcm2niibatch z)
+    endif()
 
-    add_executable(dcm2niibatch
-            main_console_batch.cpp
-            nii_dicom.cpp
-            jpg_0XC3.cpp
-            ujpeg.cpp
-            nifti1_io_core.cpp
-            nii_ortho.cpp
-            nii_dicom_batch.cpp)
-
-    if (YAML_CPP_LIBRARY)
-        target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARY})
+    if(JASPER_FOUND)
+        target_link_libraries(dcm2niibatch ${JASPER_LIBRARIES})
     else()
-        target_link_libraries(dcm2niibatch yaml-cpp)
+        add_definitions(-DmyDisableJASPER)
     endif()
 
+    if(USE_OPENJPEG)
+        target_include_directories(dcm2niibatch PRIVATE ${OPENJPEG_INCLUDE_DIRS})
+        target_link_libraries(dcm2niibatch ${OPENJPEG_LIBRARIES})
+    else()
+        add_definitions(-DmyDisableOpenJPEG)
+    endif()
 
-    if (YAML_CPP_INCLUDE_DIR)
-        include_directories(${YAML_CPP_INCLUDE_DIR})
-    endif ()
-endif ()
+    list(APPEND PROGRAMS dcm2niibatch)
+endif()
 
 install(TARGETS ${PROGRAMS} DESTINATION bin)
diff --git a/console/jpg_0XC3.cpp b/console/jpg_0XC3.cpp
index 742ea1c..c8ca375 100644
--- a/console/jpg_0XC3.cpp
+++ b/console/jpg_0XC3.cpp
@@ -4,6 +4,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "jpg_0XC3.h"
+#include "print.h"
 
 unsigned char  readByte(unsigned char *lRawRA, long *lRawPos, long lRawSz) {
     unsigned char ret = 0x00;
@@ -103,27 +104,32 @@ int decodePixelDifference(unsigned char *lRawRA, long *lRawPos, int *lCurrentBit
 
 unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbose, int *dimX, int *dimY, int *bits, int *frames, int diskBytes) {
     //decompress JPEG image named "fn" where image data is located skipBytes into file. diskBytes is compressed size of image (set to 0 if unknown)
-    #define abortGoto() free(lRawRA); return NULL;
+    //next line breaks MSVC
+    // #define abortGoto(...) ({printError(__VA_ARGS__); free(lRawRA); return NULL;})
+    #define abortGoto(...) do {printError(__VA_ARGS__); free(lRawRA); return NULL;} while(0)
     unsigned char *lImgRA8 = NULL;
     FILE *reader = fopen(fn, "rb");
-    fseek(reader, 0, SEEK_END);
+    int lSuccess = fseek(reader, 0, SEEK_END);
     long lRawSz = ftell(reader)- skipBytes;
     if ((diskBytes > 0) && (diskBytes < lRawSz)) //only if diskBytes is known and does not exceed length of file
         lRawSz = diskBytes;
-    if (lRawSz <= 8) {
-        printf("Error opening %s\n", fn);
+    if ((lSuccess != 0) || (lRawSz <= 8)) {
+        printError("Unable to load 0XC3 JPEG %s\n", fn);
+        return NULL; //read failure
+    }
+    lSuccess = fseek(reader, skipBytes, SEEK_SET); //If successful, the function returns zero
+    if (lSuccess != 0) {
+        printError("Unable to open 0XC3 JPEG  %s\n", fn);
         return NULL; //read failure
     }
-    fseek(reader, skipBytes, SEEK_SET);
     unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
-    fread(lRawRA, 1, lRawSz, reader);
+    size_t lSz = fread(lRawRA, 1, lRawSz, reader);
     fclose(reader);
-    if ((lRawRA[0] != 0xFF) || (lRawRA[1] != 0xD8) || (lRawRA[2] != 0xFF)) {
-        printf("Error: JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
-        abortGoto();//goto abortGoto; //signature failure http://en.wikipedia.org/wiki/List_of_file_signatures
+    if ((lSz < lRawSz) || (lRawRA[0] != 0xFF) || (lRawRA[1] != 0xD8) || (lRawRA[2] != 0xFF)) {
+        abortGoto("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);//signature failure http://en.wikipedia.org/wiki/List_of_file_signatures
     }
     if (verbose)
-        printf("JPEG signature 0xFFD8FF found at offset %d of %s\n", skipBytes, fn);
+        printMessage("JPEG signature 0xFFD8FF found at offset %d of %s\n", skipBytes, fn);
     //next: read header
     long lRawPos = 2; //Skip initial 0xFFD8, begin with third byte
     //long lRawPos = 0; //Skip initial 0xFFD8, begin with third byte
@@ -138,8 +144,7 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
         do {
             btS1 = readByte(lRawRA, &lRawPos, lRawSz);
             if (btS1 != 0xFF) {
-                printf("JPEG header tag must begin with 0xFF\n");
-                abortGoto(); //goto abortGoto;
+                abortGoto("JPEG header tag must begin with 0xFF\n");
             }
             btMarkerType =  readByte(lRawRA, &lRawPos, lRawSz);
             if ((btMarkerType == 0x01) || (btMarkerType == 0xFF) || ((btMarkerType >= 0xD0) && (btMarkerType <= 0xD7) ) )
@@ -148,10 +153,11 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
         } while ((lRawPos < lRawSz) && (btMarkerType == 0));
         uint16_t lSegmentLength = readWord (lRawRA, &lRawPos, lRawSz); //read marker length
         long lSegmentEnd = lRawPos+(lSegmentLength - 2);
-        if (lSegmentEnd > lRawSz)
-            abortGoto(); //goto abortGoto;
+        if (lSegmentEnd > lRawSz) {
+            abortGoto("Segment larger than image\n");
+        }
         if (verbose)
-            printf("btMarkerType %#02X length %d@%ld\n", btMarkerType, lSegmentLength, lRawPos);
+            printMessage("btMarkerType %#02X length %d@%ld\n", btMarkerType, lSegmentLength, lRawPos);
         if ( ((btMarkerType >= 0xC0) && (btMarkerType <= 0xC3)) || ((btMarkerType >= 0xC5) && (btMarkerType <= 0xCB)) || ((btMarkerType >= 0xCD) && (btMarkerType <= 0xCF)) )  {
             //if Start-Of-Frame (SOF) marker
             SOFprecision = readByte(lRawRA, &lRawPos, lRawSz);
@@ -160,18 +166,16 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
             SOFnf = readByte(lRawRA, &lRawPos, lRawSz);
             //SOFarrayPos = lRawPos;
             lRawPos = (lSegmentEnd);
-            if (verbose) printf(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf);
+            if (verbose) printMessage(" [Precision %d X*Y %d*%d Frames %d]\n", SOFprecision, SOFxdim, SOFydim, SOFnf);
             if (btMarkerType != 0xC3) { //lImgTypeC3 = true;
-                printf("This JPEG decoder can only decompress lossless JPEG ITU-T81 images (SoF must be 0XC3, not %#02X)\n",btMarkerType );
-                abortGoto(); //goto abortGoto;
+                abortGoto("This JPEG decoder can only decompress lossless JPEG ITU-T81 images (SoF must be 0XC3, not %#02X)\n",btMarkerType );
             }
             if ( (SOFprecision < 1) || (SOFprecision > 16) || (SOFnf < 1) || (SOFnf == 2) || (SOFnf > 3)
                 || ((SOFnf == 3) &&  (SOFprecision > 8))   ) {
-                printf("Scalar data must be 1..16 bit, RGB data must be 8-bit (%d-bit, %d frames)\n", SOFprecision, SOFnf);
-                abortGoto(); //goto abortGoto;
+                abortGoto("Scalar data must be 1..16 bit, RGB data must be 8-bit (%d-bit, %d frames)\n", SOFprecision, SOFnf);
             }
         } else if (btMarkerType == 0xC4) {//if SOF marker else if define-Huffman-tables marker (DHT)
-            if (verbose) printf(" [Huffman Length %d]\n", lSegmentLength);
+            if (verbose) printMessage(" [Huffman Length %d]\n", lSegmentLength);
             int lFrameCount = 1;
             do {
                 uint8_t DHTnLi = readByte(lRawRA, &lRawPos, lRawSz ); //we read but ignore DHTtcth.
@@ -181,10 +185,11 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
                     l[lFrameCount].DHTliRA[lInc] = readByte(lRawRA, &lRawPos, lRawSz);
                     DHTnLi = DHTnLi +  l[lFrameCount].DHTliRA[lInc];
                     if (l[lFrameCount].DHTliRA[lInc] != 0) l[lFrameCount].MaxHufSi = lInc;
+                    if (verbose) printMessage("DHT has %d combinations with %d bits\n", l[lFrameCount].DHTliRA[lInc], lInc);
+                    
                 }
                 if (DHTnLi > 17) {
-                    printf("Huffman table corrupted.\n");
-                    abortGoto(); //goto abortGoto;
+                    abortGoto("Huffman table corrupted.\n");
                 }
                 int lIncY = 0; //frequency
                 for (int lInc = 0; lInc <= 31; lInc++) {//lInc := 0 to 31 do begin
@@ -200,11 +205,11 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
                             btS1 = readByte(lRawRA, &lRawPos, lRawSz);
                             l[lFrameCount].HufVal[lIncY] = btS1;
                             l[lFrameCount].MaxHufVal = btS1;
-                            if ((btS1 >= 0) && (btS1 <= 16))
+                            if (verbose) printMessage("DHT combination %d has a value of %d\n", lIncY, btS1);
+                            if (btS1 <= 16) //unsigned ints ALWAYS >0, so no need for(btS1 >= 0)
                                 l[lFrameCount].HufSz[lIncY] = lInc;
                             else {
-                                printf("Huffman size array corrupted.\n");
-                                abortGoto(); //goto abortGoto;
+                                abortGoto("Huffman size array corrupted.\n");
                             }
                         }
                     }
@@ -228,15 +233,14 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
                 } while (K <= DHTnLi);
                 //if (verbose)
                 //    for (int j = 1; j <= DHTnLi; j++)
-                //        printf(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[j]);
+                //        printMessage(" [%d Sz %d Code %d Value %d]\n", j, l[lFrameCount].HufSz[j], l[lFrameCount].HufCode[j], l[lFrameCount].HufVal[j]);
                 lFrameCount++;
             } while ((lSegmentEnd-lRawPos) >= 18);
             lnHufTables = lFrameCount - 1;
             lRawPos = (lSegmentEnd);
-            if (verbose) printf(" [FrameCount %d]\n", lnHufTables);
+            if (verbose) printMessage(" [FrameCount %d]\n", lnHufTables);
         } else if (btMarkerType == 0xDD) {  //if DHT marker else if Define restart interval (DRI) marker
-            printf("This image uses Restart Segments - please contact Chris Rorden to add support for this format.\n");
-            abortGoto(); //goto abortGoto;
+            abortGoto("btMarkerType == 0xDD: unsupported Restart Segments\n");
             //lRestartSegmentSz = ReadWord(lRawRA, &lRawPos, lRawSz);
             //lRawPos = lSegmentEnd;
         } else if (btMarkerType == 0xDA) {  //if DRI marker else if read Start of Scan (SOS) marker
@@ -257,17 +261,17 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
             SOSahal = readByte(lRawRA, &lRawPos, lRawSz); //lower 4bits= pointtransform
             SOSpttrans = SOSahal & 16;
             if (verbose)
-                printf(" [Predictor: %d Transform %d]\n", SOSss, SOSahal);
+                printMessage(" [Predictor: %d Transform %d]\n", SOSss, SOSahal);
             lRawPos = (lSegmentEnd);
         } else  //if SOS marker else skip marker
             lRawPos = (lSegmentEnd);
     } while ((lRawPos < lRawSz) && (btMarkerType != 0xDA)); //0xDA=Start of scan: loop for reading header
     //NEXT: Huffman decoding
     if (lnHufTables < 1) {
-        printf("Decoding error: no Huffman tables.\n");
-        abortGoto(); //goto abortGoto;
+        abortGoto("Decoding error: no Huffman tables.\n");
     }
     //NEXT: unpad data - delete byte that follows $FF
+    int lIsRestartSegments = 0;
     long lIncI = lRawPos; //input position
     long lIncO = lRawPos; //output position
     do {
@@ -277,10 +281,14 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
                 lIncI = lIncI+1;
             else if (lRawRA[lIncI+1] == 0xD9)
                 lIncO = -666; //end of padding
+            else
+                lIsRestartSegments = lRawRA[lIncI+1];
         }
         lIncI++;
         lIncO++;
     } while (lIncO > 0);
+    if (lIsRestartSegments != 0) //detects both restart and corruption https://groups.google.com/forum/#!topic/comp.protocols.dicom/JUuz0B_aE5o
+        printWarning("Detected restart segments, decompress with dcmdjpeg or gdcmconv 0xFF%02X.\n", lIsRestartSegments);
     //NEXT: some RGB images use only a single Huffman table for all 3 colour planes. In this case, replicate the correct values
     //NEXT: prepare lookup table
     for (int lFrameCount = 1; lFrameCount <= lnHufTables; lFrameCount ++) {
@@ -306,7 +314,7 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
                         }
                     } else
                         l[lFrameCount].LookUpRA[k] = lHufVal; //SSSS
-                    //printf("Frame %d SSSS %d Size %d Code %d SHL %d EmptyBits %ld\n", lFrameCount, lHufRA[lFrameCount][lIncY].HufVal, lHufRA[lFrameCount][lIncY].HufSz,lHufRA[lFrameCount][lIncY].HufCode, k, lInc);
+                    //printMessage("Frame %d SSSS %d Size %d Code %d SHL %d EmptyBits %ld\n", lFrameCount, lHufRA[lFrameCount][lIncY].HufVal, lHufRA[lFrameCount][lIncY].HufSz,lHufRA[lFrameCount][lIncY].HufCode, k, lInc);
                 } //Set SSSS
             } //Length of size lInc > 0
         } //for lInc := 1 to 8
@@ -489,6 +497,6 @@ unsigned char *  decode_JPEG_SOF_0XC3 (const char *fn, int skipBytes, bool verbo
     *dimY = SOFydim;
     *frames = SOFnf;
     if (verbose)
-        printf("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes);
+        printMessage("JPEG ends %ld@%ld\n", lRawPos, lRawPos+skipBytes);
     return lImgRA8;
 }// decode_JPEG_SOF_0XC3()
\ No newline at end of file
diff --git a/console/main_console.cpp b/console/main_console.cpp
index b1ecc1d..3137e01 100644
--- a/console/main_console.cpp
+++ b/console/main_console.cpp
@@ -41,9 +41,22 @@
 #include <stdio.h>
 #include "nii_dicom_batch.h"
 #include "nii_dicom.h"
-#include "nifti1_io_core.h"
 #include <math.h>
 
+#if !defined(_WIN64) && !defined(_WIN32)
+#include <time.h>
+#include <sys/time.h>
+double get_wall_time(){
+    struct timeval time;
+    if (gettimeofday(&time,NULL)){
+        //  Handle error
+        return 0;
+    }
+    return (double)time.tv_sec + (double)time.tv_usec * .000001;
+}
+#endif
+
+
 const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe"
     const char* pDelimeter = strrchr (path, '\\');
     if (pDelimeter)
@@ -58,18 +71,26 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
     const char *cstr = removePath(argv[0]);
     printf("usage: %s [options] <in_folder>\n", cstr);
     printf(" Options :\n");
-    printf("  -b : BIDS sidecar (y/n, default n)\n");
+    printf("  -1..-9 : gz compression level (1=fastest, 9=smallest)\n");
+    char bidsCh = 'n';
+    if (opts.isCreateBIDS) bidsCh = 'y';
+    printf("  -b : BIDS sidecar (y/n, default %c)\n", bidsCh);
+    if (opts.isAnonymizeBIDS) bidsCh = 'y'; else bidsCh = 'n';
+    printf("   -ba : anonymize BIDS (y/n, default %c)\n", bidsCh);
     #ifdef mySegmentByAcq
-    printf("  -f : filename (%%a=antenna  (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
+    printf("  -f : filename (%%a=antenna  (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%j seriesInstanceUID, %%k studyInstanceUID, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%q=sequence number, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
     #else
-    printf("  -f : filename (%%a=antenna  (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
+    printf("  -f : filename (%%a=antenna  (coil) number, %%c=comments, %%d=description, %%e echo number, %%f=folder name, %%i ID of patient, %%j seriesInstanceUID, %%k studyInstanceUID, %%m=manufacturer, %%n=name of patient, %%p=protocol, %%s=series number, %%t=time, %%u=acquisition number, %%z sequence name; default '%s')\n",opts.filename);
     #endif
     printf("  -h : show help\n");
+    printf("  -i : ignore derived, localizer and 2D images (y/n, default n)\n");
+    printf("  -t : text notes includes private patient details (y/n, default n)\n");
     printf("  -m : merge 2D slices from same series regardless of study time, echo, coil, orientation, etc. (y/n, default n)\n");
     printf("  -o : output directory (omit to save to input folder)\n");
+    printf("  -p : Philips precise float (not display) scaling (y/n, default y)\n");
     printf("  -s : single file mode, do not convert other images in folder (y/n, default n)\n");
     printf("  -t : text notes includes private patient details (y/n, default n)\n");
-    printf("  -v : verbose (y/n, default n)\n");
+    printf("  -v : verbose (n/y or 0/1/2 [no, yes, logorrheic], default 0)\n");
     printf("  -x : crop (y/n, default n)\n");
     char gzCh = 'n';
     if (opts.isGz) gzCh = 'y';
@@ -79,7 +100,11 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
 		else
 			printf("  -z : gz compress images (y/n, default %c) [REQUIRES pigz]\n", gzCh);
     #else
+    	#ifdef myDisableMiniZ
+    	printf("  -z : gz compress images (y/i/n, default %c) [y=pigz, i=internal:zlib, n=no]\n", gzCh);
+		#else
 		printf("  -z : gz compress images (y/i/n, default %c) [y=pigz, i=internal, n=no]\n", gzCh);
+		#endif
     #endif
 
 #if defined(_WIN64) || defined(_WIN32)
@@ -94,6 +119,7 @@ void showHelp(const char * argv[], struct TDCMopts opts) {
     printf(" Examples :\n");
     printf("  %s /Users/chris/dir\n", cstr);
     printf("  %s -o /users/cr/outdir/ -z y ~/dicomdir\n", cstr);
+    printf("  %s -f %%pp_%%s -b y -ba n ~/dicomdir\n", cstr);
     printf("  %s -f mystudy%%s ~/dicomdir\n", cstr);
     printf("  %s -o \"~/dir with spaces/dir\" ~/dicomdir\n", cstr);
 #endif
@@ -122,25 +148,52 @@ int main(int argc, const char * argv[])
         showHelp(argv, opts);
         return 0;
     }
-    bool isCustomOutDir = false;
+    //bool isCustomOutDir = false;
     int i = 1;
     int lastCommandArg = 0;
     while (i < (argc)) { //-1 as final parameter is DICOM directory
         if ((strlen(argv[i]) > 1) && (argv[i][0] == '-')) { //command
             if (argv[i][1] == 'h')
                 showHelp(argv, opts);
-            else if ((argv[i][1] == 'b') && ((i+1) < argc)) {
+            else if ((argv[i][1] >= '1') && (argv[i][1] <= '9')) {
+            	opts.gzLevel = abs((int)strtol(argv[i], NULL, 10));
+            	if (opts.gzLevel > 11)
+        	 		opts.gzLevel = 11;
+            } else if ((argv[i][1] == 'b') && ((i+1) < argc)) {
+                if (strlen(argv[i]) < 3) { //"-b y"
+                	i++;
+                	if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    	opts.isCreateBIDS = false;
+                	else
+                    	opts.isCreateBIDS = true;
+                } else if (argv[i][2] == 'a') {//"-ba y"
+                	i++;
+                	if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    	opts.isAnonymizeBIDS = false;
+                	else
+                    	opts.isAnonymizeBIDS = true;
+
+                } else
+                	printf("Error: Unknown command line argument: '%s'\n", argv[i]);
+            } else if ((argv[i][1] == 'i') && ((i+1) < argc)) {
                 i++;
                 if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
-                    opts.isCreateBIDS = false;
+                    opts.isIgnoreDerivedAnd2D = false;
                 else
-                    opts.isCreateBIDS = true;
+                    opts.isIgnoreDerivedAnd2D = true;
             } else if ((argv[i][1] == 'm') && ((i+1) < argc)) {
                 i++;
                 if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
                     opts.isForceStackSameSeries = false;
                 else
                     opts.isForceStackSameSeries = true;
+            } else if ((argv[i][1] == 'p') && ((i+1) < argc)) {
+                i++;
+                if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
+                    opts.isPhilipsFloatNotDisplayScaling = false;
+                else
+                    opts.isPhilipsFloatNotDisplayScaling = true;
+
             } else if ((argv[i][1] == 's') && ((i+1) < argc)) {
                 i++;
                 if ((argv[i][0] == 'n') || (argv[i][0] == 'N')  || (argv[i][0] == '0'))
@@ -181,7 +234,7 @@ int main(int argc, const char * argv[])
                 strcpy(opts.filename,argv[i]);
             } else if ((argv[i][1] == 'o') && ((i+1) < argc)) {
                 i++;
-                isCustomOutDir = true;
+                //isCustomOutDir = true;
                 strcpy(opts.outdir,argv[i]);
             }
             lastCommandArg = i;
@@ -198,14 +251,21 @@ int main(int argc, const char * argv[])
         return EXIT_SUCCESS;
     }
 #endif
+	#if !defined(_WIN64) && !defined(_WIN32)
+	double startWall = get_wall_time();
+	#endif
     clock_t start = clock();
     for (i = (lastCommandArg+1); i < argc; i++) {
     	strcpy(opts.indir,argv[i]); // [argc-1]
-    	if (!isCustomOutDir) strcpy(opts.outdir,opts.indir);
+    	//if (!isCustomOutDir) strcpy(opts.outdir,opts.indir);
     	if (nii_loadDir(&opts) != EXIT_SUCCESS)
     		return EXIT_FAILURE;
     }
-    printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC);
+    #if !defined(_WIN64) && !defined(_WIN32)
+		printf ("Conversion required %f seconds (%f for core code).\n",get_wall_time() - startWall, ((float)(clock()-start))/CLOCKS_PER_SEC);
+	#else
+	printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC);
+    #endif
     saveIniFile(opts);
     return EXIT_SUCCESS;
 }
diff --git a/console/main_console_batch.cpp b/console/main_console_batch.cpp
index 0230554..23c8662 100644
--- a/console/main_console_batch.cpp
+++ b/console/main_console_batch.cpp
@@ -3,18 +3,25 @@
 //  Copyright (c) 2014 Chris Rorden. All rights reserved.
 //  yaml batch suport by Benjamin Irving, 2016 - maintains copyright
 
-#include <stdlib.h>
+#ifdef _MSC_VER
+	#include  <io.h> //access()
+	#ifndef F_OK
+	#define F_OK 0 /* existence check */
+	#endif
+#else
+	#include <unistd.h> //access()
+#endif
+#include <cmath>
+#include <fstream>
+#include <iostream>
 #include <stdbool.h>
-#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
 #include <stdio.h>
-#include <cmath>
+#include <stdlib.h>
 #include <string>
-#include <iostream>
-
-#include "nii_dicom_batch.h"
-
-#include <fstream>
+#include <time.h>  // clock_t, clock, CLOCKS_PER_SEC
 #include <yaml-cpp/yaml.h>
+#include "nii_dicom.h"
+#include "nii_dicom_batch.h"
 
 const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filename.exe"
     const char* pDelimeter = strrchr (path, '\\');
@@ -26,30 +33,77 @@ const char* removePath(const char* path) { // "/usr/path/filename.exe" -> "filen
     return path;
 } //removePath()
 
-
-int rmainbatch(TDCMopts opts)
-{
-    printf("Chris Rorden's dcm2niiX version %s (%lu-bit)\n",kDCMvers, sizeof(size_t)*8);
-
+int rmainbatch(TDCMopts opts) {
     clock_t start = clock();
     nii_loadDir(&opts);
     printf ("Conversion required %f seconds.\n",((float)(clock()-start))/CLOCKS_PER_SEC);
     return EXIT_SUCCESS;
-}
-
-int main(int argc, const char * argv[])
-{
-
+} //rmainbatch()
+
+void showHelp(const char * argv[]) {
+    const char *cstr = removePath(argv[0]);
+    printf("Usage: %s <batch_config.yml>\n", cstr);
+    printf("\n");
+	printf("The configuration file must be in yaml format as shown below\n");
+	printf("\n");
+	printf("### START YAML FILE ###\n");
+	printf("Options:\n");
+	printf("   isGz:             false\n");
+	printf("   isFlipY:          false\n");
+	printf("   isVerbose:        false\n");
+	printf("   isCreateBIDS:     false\n");
+	printf("   isOnlySingleFile: false\n");
+	printf("Files:\n");
+	printf("   -\n");
+	printf("    in_dir:           /path/to/first/folder\n");
+	printf("    out_dir:          /path/to/output/folder\n");
+	printf("    filename:         dcemri\n");
+	printf("   -\n");
+	printf("    in_dir:           /path/to/second/folder\n");
+	printf("    out_dir:          /path/to/output/folder\n");
+	printf("    filename:         fa3\n");
+	printf("### END YAML FILE ###\n");
+	printf("\n");
+#if defined(_WIN64) || defined(_WIN32)
+    printf(" Example :\n");
+    printf("  %s c:\\dir\\yaml.yml\n", cstr);
+
+    printf("  %s \"c:\\dir with spaces\\yaml.yml\"\n", cstr);
+#else
+    printf(" Examples :\n");
+    printf("  %s /Users/chris/yaml.yml\n", cstr);
+    printf("  %s \"/Users/dir with spaces/yaml.yml\"\n", cstr);
+#endif
+} //showHelp()
+
+int main(int argc, const char * argv[]) {
+	#if defined(__APPLE__)
+		#define kOS "MacOS"
+	#elif (defined(__linux) || defined(__linux__))
+		#define kOS "Linux"
+	#else
+		#define kOS "Windows"
+	#endif
+	printf("dcm2niibatch using Chris Rorden's dcm2niiX version %s (%llu-bit %s)\n",kDCMvers, (unsigned long long) sizeof(size_t)*8, kOS);
     if (argc != 2) {
-        std::cout << "Do not include additional inputs with a config file \n";
-        throw;
+    	if (argc < 2)
+    		printf(" Please provide location of config file\n");
+    	else
+        	printf(" Do not include additional inputs with a config file\n");
+        printf("\n");
+        showHelp(argv);
+        return EXIT_FAILURE;
     }
-
+	if( access( argv[1], F_OK ) == -1 ) {
+        printf(" Please provide location of config file\n");
+        printf("\n");
+        showHelp(argv);
+        return EXIT_FAILURE;
+	}
     // Process it all via a yaml file
     std::string yaml_file = argv[1];
     std::cout << "yaml_path: " << yaml_file << std::endl;
     YAML::Node config = YAML::LoadFile(yaml_file);
-
     struct TDCMopts opts;
     readIniFile(&opts, argv); //setup defaults, e.g. path to pigz
     opts.isCreateBIDS = config["Options"]["isCreateBIDS"].as<bool>();
@@ -63,18 +117,13 @@ int main(int argc, const char * argv[])
 		//in general, pigz is faster unless you have a very slow network, in which case the internal compressor is better
     }*/
     for (auto i: config["Files"]) {
-
         std::string indir = i["in_dir"].as<std::string>();
         strcpy(opts.indir, indir.c_str());
-
         std::string outdir = i["out_dir"].as<std::string>();
         strcpy(opts.outdir, outdir.c_str());
-
         std::string filename = i["filename"].as<std::string>();
         strcpy(opts.filename, filename.c_str());
-
         rmainbatch(opts);
-
     }
-    return 1;
-}
+    return EXIT_SUCCESS;
+} // main()
diff --git a/console/makefile b/console/makefile
old mode 100644
new mode 100755
index 3cf9f9a..f311167
--- a/console/makefile
+++ b/console/makefile
@@ -6,4 +6,4 @@ ifneq ($(OS),Windows_NT)
 	endif
 endif
 all:
-	g++ $(CFLAGS) -O3 -I. main_console.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
+	g++ $(CFLAGS) -O3 -I. main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp  -o dcm2niix -DmyDisableOpenJPEG -DmyDisableJasper
diff --git a/console/nifti1_io_core.cpp b/console/nifti1_io_core.cpp
index 73c3add..34efd04 100644
--- a/console/nifti1_io_core.cpp
+++ b/console/nifti1_io_core.cpp
@@ -1,3 +1,20 @@
+//This unit uses a subset of the functions from the nifti1_io available from
+//  https://sourceforge.net/projects/niftilib/files/nifticlib/
+//These functions were extended by Chris Rorden (2014) and maintain the same license
+/*****===================================================================*****/
+/*****     Sample functions to deal with NIFTI-1 and ANALYZE files       *****/
+/*****...................................................................*****/
+/*****            This code is released to the public domain.            *****/
+/*****...................................................................*****/
+/*****  Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH          *****/
+/*****  Date:   August 2003                                              *****/
+/*****...................................................................*****/
+/*****  Neither the National Institutes of Health (NIH), nor any of its  *****/
+/*****  employees imply any warranty of usefulness of this software for  *****/
+/*****  any purpose, and do not assume any liability for damages,        *****/
+/*****  incidental or otherwise, caused by any use of this document.     *****/
+/*****===================================================================*****/
+
 #include "nifti1_io_core.h"
 #include <math.h>
 #include <stdlib.h>
@@ -12,6 +29,9 @@
 #include <unistd.h>
 #endif
 
+#include "print.h"
+
+#ifndef HAVE_R
 void nifti_swap_8bytes( size_t n , void *ar )    // 4 bytes at a time
 {
     size_t ii ;
@@ -58,6 +78,7 @@ void nifti_swap_2bytes( size_t n , void *ar )    // 2 bytes at a time
     }
     return ;
 }
+#endif
 
 int isSameFloat (float a, float b) {
     return (fabs (a - b) <= FLT_EPSILON);
@@ -130,7 +151,7 @@ mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4])
     mat33 Q, diagVox;
     Q.m[0][0] = orient[1]; Q.m[0][1] = orient[2] ; Q.m[0][2] = orient[3] ; // load Q
     Q.m[1][0] = orient[4]; Q.m[1][1] = orient[5] ; Q.m[1][2] = orient[6];
-    //printf("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] );
+    //printMessage("Orient %g %g %g %g %g %g\n",orient[1],orient[2],orient[3],orient[4],orient[5],orient[6] );
     /* normalize row 1 */
     double val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ;
     if( val > 0.0l ){
@@ -167,6 +188,7 @@ mat44 nifti_dicom2mat(float orient[7], float patientPosition[4], float xyzMM[4])
     return Q44;
 }
 
+#ifndef HAVE_R
 float nifti_mat33_determ( mat33 R )   /* determinant of 3x3 matrix */
 {
     double r11,r12,r13,r21,r22,r23,r31,r32,r33 ;
@@ -189,6 +211,7 @@ mat33 nifti_mat33_mul( mat33 A , mat33 B )  /* multiply 2 3x3 matrices */
             + A.m[i][2] * B.m[2][j] ;
     return C ;
 }
+#endif
 
 mat44 nifti_mat44_mul( mat44 A , mat44 B )  /* multiply 2 3x3 matrices */
 {
@@ -212,6 +235,7 @@ mat33 nifti_mat33_transpose( mat33 A )  /* transpose 3x3 matrix */
     return B;
 }
 
+#ifndef HAVE_R
 mat33 nifti_mat33_inverse( mat33 R )   /* inverse of 3x3 matrix */
 {
     double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ;
@@ -468,7 +492,7 @@ mat44 nifti_mat44_inverse( mat44 R )
     Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; // failure flag if deti == 0
     return Q ;
 }
-
+#endif
 
 
 
diff --git a/console/nifti1_io_core.h b/console/nifti1_io_core.h
index 41f4615..0e1588e 100644
--- a/console/nifti1_io_core.h
+++ b/console/nifti1_io_core.h
@@ -4,6 +4,11 @@
 #ifndef _NIFTI_IO_CORE_HEADER_
 #define _NIFTI_IO_CORE_HEADER_
 
+#ifdef HAVE_R
+#define STRICT_R_HEADERS
+#include "RNifti.h"
+#endif
+
 #ifdef  __cplusplus
 extern "C" {
 #endif
@@ -11,12 +16,14 @@ extern "C" {
 #include <stdbool.h>
 #include <string.h>
 
+#ifndef HAVE_R
 typedef struct {                   /** 4x4 matrix struct **/
     float m[3][3] ;
 } mat33 ;
 typedef struct {                   /** 4x4 matrix struct **/
     float m[4][4] ;
 } mat44 ;
+#endif
 typedef struct {                   /** x4 vector struct **/
     float v[4] ;
 } vec4 ;
diff --git a/console/nii_dicom.cpp b/console/nii_dicom.cpp
index 94c74d9..059d5b9 100644
--- a/console/nii_dicom.cpp
+++ b/console/nii_dicom.cpp
@@ -16,7 +16,10 @@
 	#include <unistd.h>
 #endif
 //#include <time.h> //clock()
+#ifndef HAVE_R
 #include "nifti1.h"
+#endif
+#include "print.h"
 #include "nii_dicom.h"
 #include <sys/types.h>
 #include <sys/stat.h> // discriminate files from folders
@@ -30,15 +33,24 @@
 #include <float.h>
 #include <stdint.h>
 #include "nifti1_io_core.h"
-#include "ujpeg.h"
-#ifdef myUseCOut
-#include <iostream>
+
+#ifdef HAVE_R
+#undef isnan
+#define isnan ISNAN
+#endif
+
+#ifndef myDisableClassicJPEG
+  #ifdef myTurboJPEG
+   #include <turbojpeg.h>
+  #else
+	#include "ujpeg.h"
+  #endif
 #endif
 #ifdef myEnableJasper
     #include <jasper/jasper.h>
 #endif
 #ifndef myDisableOpenJPEG
-    #include <openjpeg-2.1/openjpeg.h>//"openjpeg.h"
+    #include <openjpeg.h>
 
 #ifdef myEnableJasper
 ERROR: YOU CAN NOT COMPILE WITH myEnableJasper AND NOT myDisableOpenJPEG OPTIONS SET SIMULTANEOUSLY
@@ -67,15 +79,15 @@ unsigned char * imagetoimg(opj_image_t * image)
     }
     if ((image->comps[0].prec < 1) || (image->comps[0].prec > 16)) isOK = false; //currently we only handle 1 and 2 byte data
     if (!isOK) {
-        printf("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts,  opj_version());
+        printMessage("jpeg decode failure w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts,  opj_version());
         return NULL;
     }
     #ifdef MY_DEBUG
-    printf("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts,  opj_version());
+    printMessage("w*h %d*%d bpp %d sgnd %d components %d OpenJPEG=%s\n", width, height, bpp, sgnd, numcmpts,  opj_version());
     #endif
     //extract the data
     if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) {
-        printf("Serious catastrophic decompression error\n");
+        printError("Catastrophic decompression error\n");
         return NULL;
     }
     unsigned char *img = (unsigned char *)malloc(imgbytes);
@@ -84,7 +96,7 @@ unsigned char * imagetoimg(opj_image_t * image)
     if (sgnd) bpp = -bpp;
     if (bpp == -1) {
         free(img);
-        printf("Error: Signed 8-bit DICOM?\n");
+        printError("Signed 8-bit DICOM?\n");
         return NULL;
     }
     //n.b. Analyze rgb-24 are PLANAR e.g. RRR..RGGG..GBBB..B not RGBRGBRGB...RGB
@@ -126,7 +138,6 @@ static void my_stream_free (void * p_user_data) { //do nothing
 
 static OPJ_UINT32 opj_read_from_buffer(void * p_buffer, OPJ_UINT32 p_nb_bytes, BufInfo* p_file) {
     OPJ_UINT32 l_nb_read;
-
     if(p_file->cur + p_nb_bytes < p_file->buf + p_file->len )
     {
         l_nb_read = p_nb_bytes;
@@ -196,8 +207,9 @@ unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr
     if (size <= 8) return NULL;
     fseek(reader, dcm.imageStart, SEEK_SET);
     unsigned char *data = (unsigned char*) malloc(size);
-    fread(data, 1, size, reader);
+    size_t sz = fread(data, 1, size, reader);
     fclose(reader);
+    if (sz < size) return NULL;
     OPJ_CODEC_FORMAT format = OPJ_CODEC_JP2;
     //DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header
     if (data[0] == 0xFF && data[1] == 0x4F && data[2] == 0xFF && data[3] == 0x51) format = OPJ_CODEC_J2K;
@@ -213,12 +225,12 @@ unsigned char * nii_loadImgCoreOpenJPEG(char* imgname, struct nifti_1_header hdr
     if ( !opj_setup_decoder(codec, &params) ) goto cleanup2;
     // Read the main header of the codestream and if necessary the JP2 boxes
     if(! opj_read_header( stream, codec, &jpx)){
-        printf( "OpenJPEG error: failed to read the header %s\n", imgname);
+        printError( "OpenJPEG failed to read the header %s\n", imgname);
         goto cleanup2;
     }
     // Get the decoded image
     if ( !( opj_decode(codec, stream, jpx) && opj_end_decompress(codec,stream) ) ) {
-        printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",imgname);
+        printError( "OpenJPEG j2k_to_image failed to decode %s\n",imgname);
         goto cleanup1;
     }
     ret = imagetoimg(jpx);
@@ -230,105 +242,6 @@ cleanup2:
     opj_destroy_codec(codec);
     return ret;
 }
-
-/* OpenJPEG code very clumsy for common instance when JPEG is embedded in a file, like PDF, DICOM, etc. Perhaps future versions will allow foo2's simpler methods...
-int foo (float vx) {
-    const char *fn = "/Users/rorden/Desktop/tester/mecanix.dcm";
-    int dcmimageStart = 1282;
-    unsigned char * ret = NULL;
-    //OpenJPEG library is not well documented and has changed between versions
-    //Since the JPEG is embedded in a DICOM we need to skip bytes at the start of the file
-    // In theory we might also want to strip data that exists AFTER the image, see gdcmJPEG2000Codec.c
-    opj_dparameters_t params;
-    opj_codec_t *codec;
-    opj_image_t *jpx;
-    opj_stream_t *stream;
-    FILE *reader = fopen(fn, "rb");
-    fseek(reader, 0, SEEK_END);
-    long size = ftell(reader)- dcmimageStart;
-    if (size <= 8) return NULL;
-    fseek(reader, dcmimageStart, SEEK_SET);
-    unsigned char *data = (unsigned char*) malloc(size);
-    fread(data, 1, size, reader);
-    fclose(reader);
-    OPJ_CODEC_FORMAT format = OPJ_CODEC_JP2;
-    //DICOM JPEG2k is SUPPOSED to start with codestream, but some vendors include a header
-    if (data[0] == 0xFF && data[1] == 0x4F && data[2] == 0xFF && data[3] == 0x51) format = OPJ_CODEC_J2K;
-    opj_set_default_decoder_parameters(&params);
-    BufInfo dx;
-    dx.buf = data;
-    dx.cur = data;
-    dx.len = size;
-    stream = opj_stream_create_buffer_stream(&dx, (OPJ_UINT32)size, true);
-    if (stream == NULL) return NULL;
-    codec = opj_create_decompress(format);
-    // setup the decoder decoding parameters using user parameters
-    if ( !opj_setup_decoder(codec, &params) ) goto cleanup2;
-    // Read the main header of the codestream and if necessary the JP2 boxes
-    if(! opj_read_header( stream, codec, &jpx)){
-        printf( "OpenJPEG error: failed to read the header %s\n",fn);
-        goto cleanup2;
-    }
-    // Get the decoded image
-    if ( !( opj_decode(codec, stream, jpx) && opj_end_decompress(codec,stream) ) ) {
-        printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",fn);
-        goto cleanup1;
-    }
-    ret = imagetoimg(jpx);
-    if (ret != NULL) free(ret);
-  cleanup1:
-    opj_image_destroy(jpx);
-  cleanup2:
-    free(dx.buf);
-    opj_stream_destroy(stream);
-    opj_destroy_codec(codec);
-    return NULL;
-} //foo()
-
-int foo2 (float vx) {
-    const char *fn = "/Users/rorden/Desktop/tester/mecanix.dcm";
-    unsigned char * ret = NULL;
-    opj_dparameters_t parameters;	// decompression parameters
-    opj_image_t *jpx = NULL;
-    opj_codec_t *l_codec = NULL;	// handle to a decompressor
-    opj_stream_t *l_stream = NULL;
-    // set decoding parameters to default values
-    opj_set_default_decoder_parameters(&parameters);
-    // set a byte stream
-    l_stream = opj_stream_create_default_file_stream( fn, OPJ_TRUE);
-    if (!l_stream){
-        printf("OpenJPEG error: failed to open the file %s\n", fn);
-        return NULL;
-    }
-    // get a decoder handle - DICOM specifies JPEG-2000 codestream
-    //l_codec = opj_create_decompress(OPJ_CODEC_JP2);
-    l_codec = opj_create_decompress(OPJ_CODEC_J2K);
-    // setup the decoder decoding parameters using user parameters
-    if ( !opj_setup_decoder(l_codec, &parameters) ) return NULL;
-    // Read the main header of the codestream and if necessary the JP2 boxes
-    if(! opj_read_header( l_stream, l_codec, &jpx)){
-        printf( "OpenJPEG error: failed to read the header %s\n",fn);
-        opj_stream_destroy(l_stream);
-        opj_destroy_codec(l_codec);
-        opj_image_destroy(jpx);
-        return NULL;
-    }
-    // Get the decoded image
-    if ( !( opj_decode(l_codec, l_stream, jpx) && opj_end_decompress(l_codec,l_stream) ) ) {
-        printf( "OpenJPEG error: j2k_to_image: failed to decode %s\n",fn);
-        opj_stream_destroy(l_stream);
-        opj_destroy_codec(l_codec);
-        opj_image_destroy(jpx);
-        return NULL;
-    }
-    printf("image is decoded!\n");
-    opj_stream_destroy(l_stream);
-    ret = imagetoimg(jpx);
-    if (ret != NULL) free(ret);
-    if(l_codec) opj_destroy_codec(l_codec);
-    opj_image_destroy(jpx);
-    return EXIT_SUCCESS;
-} //foo() */
 #endif //if
 
 #ifndef M_PI
@@ -345,7 +258,7 @@ float deFuzz(float v) {
 }
 
 void reportMat33(char *str, mat33 A) {
-    printf("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str,
+    printMessage("%s = [%g %g %g ; %g %g %g; %g %g %g ]\n",str,
            deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]),
            deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]),
            deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2]));
@@ -353,7 +266,7 @@ void reportMat33(char *str, mat33 A) {
 
 void reportMat44(char *str, mat44 A) {
 //example: reportMat44((char*)"out",*R);
-    printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str,
+    printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str,
            deFuzz(A.m[0][0]),deFuzz(A.m[0][1]),deFuzz(A.m[0][2]),deFuzz(A.m[0][3]),
            deFuzz(A.m[1][0]),deFuzz(A.m[1][1]),deFuzz(A.m[1][2]),deFuzz(A.m[1][3]),
            deFuzz(A.m[2][0]),deFuzz(A.m[2][1]),deFuzz(A.m[2][2]),deFuzz(A.m[2][3]));
@@ -373,14 +286,14 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
         pos = d2.patientPosition[iSL];
         if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN;
 #ifdef MY_DEBUG
-        if (!isnan(pos)) printf("position determined using lastFile %f\n",pos);
+        if (!isnan(pos)) printMessage("position determined using lastFile %f\n",pos);
 #endif
     }
     if (isnan(pos) &&( !isnan(d.patientPositionLast[iSL]) ) ) { //patient position fields exist
         pos = d.patientPositionLast[iSL];
         if (isSameFloat(pos, d.patientPosition[iSL])) pos = NAN;
 #ifdef MY_DEBUG
-        if (!isnan(pos)) printf("position determined using last (4d) %f\n",pos);
+        if (!isnan(pos)) printMessage("position determined using last (4d) %f\n",pos);
 #endif
     }
     if (isnan(pos) && ( !isnan(d.stackOffcentre[iSL])) )
@@ -396,11 +309,7 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
         flip = (pos > R->m[iSL-1][3]) != (pos1 > R->m[iSL-1][3]); // same direction?, note C indices from 0
     else {// we do some guess work and warn user
     	if (!d.isNonImage) //do not warn user if image is derived
-#ifdef myUseCOut
-     	std::cout<<"Warning: Unable to determine slice direction: please check whether slices are flipped" <<std::endl;
-#else
-        printf("Warning: Unable to determine slice direction: please check whether slices are flipped\n");
-#endif
+        	printWarning("Unable to determine slice direction: please check whether slices are flipped\n");
     }
     if (flip) {
         for (int i = 0; i < 4; i++)
@@ -409,16 +318,16 @@ int verify_slice_dir (struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_
     if (flip)
         iSL = -iSL;
 	#ifdef MY_DEBUG
-    printf("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
+    printMessage("verify slice dir %d %d %d\n",h->dim[1],h->dim[2],h->dim[3]);
     //reportMat44((char*)"Rout",*R);
-    printf("flip = %d\n",flip);
-    printf("sliceDir = %d\n",iSL);
-    printf(" pos1 = %f\n",pos1);
+    printMessage("flip = %d\n",flip);
+    printMessage("sliceDir = %d\n",iSL);
+    printMessage(" pos1 = %f\n",pos1);
 	#endif
 	return iSL;
 } //verify_slice_dir()
 
-mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
+mat44 noNaN(mat44 Q44, bool isVerbose) //simplify any headers that have NaN values
 {
     mat44 ret = Q44;
     bool isNaN44 = false;
@@ -427,7 +336,8 @@ mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
             if (isnan(ret.m[i][j]))
                 isNaN44 = true;
     if (isNaN44) {
-        printf("Warning: bogus spatial matrix (perhaps non-spatial image): inspect spatial orientation\n");
+        if (isVerbose)
+        	printWarning("Bogus spatial matrix (perhaps non-spatial image): inspect spatial orientation\n");
         for (int i = 0; i < 4; i++)
             for (int j = 0; j < 4; j++)
                 if (i == j)
@@ -439,8 +349,8 @@ mat44 noNaN(mat44 Q44) //simplify any headers that have NaN values
     return ret;
 }
 
-void setQSForm(struct nifti_1_header *h, mat44 Q44i) {
-    mat44 Q44 = noNaN(Q44i);
+void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose) {
+    mat44 Q44 = noNaN(Q44i, isVerbose);
     h->sform_code = NIFTI_XFORM_SCANNER_ANAT;
     h->srow_x[0] = Q44.m[0][0];
     h->srow_x[1] = Q44.m[0][1];
@@ -516,12 +426,12 @@ mat44 xform_mat(struct TDICOMdata d) {
     R = nifti_mat33_transpose(R);
 	//reportMat33((char*)"R",R);
 	ivec3 ixyz = maxCol(R);
-	//printf("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]);
+	//printMessage("%d %d %d\n", ixyz.v[0], ixyz.v[1], ixyz.v[2]);
 	int iSL = ixyz.v[2]; // 1/2/3 for Sag/Cor/Tra slice
 	float cosSL = R.m[iSL-1][2];
-	//printf("cosSL\t%g\n", cosSL);
+	//printMessage("cosSL\t%g\n", cosSL);
 	//vec3 pixdim = setVec3(d.xyzMM[1], d.xyzMM[2], d.xyzMM[3]);
-	//printf("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]);
+	//printMessage("%g %g %g\n", pixdim.v[0], pixdim.v[1], pixdim.v[2]);
 	mat33 pixdim;
     LOAD_MAT33(pixdim, d.xyzMM[1], 0.0, 0.0,
     	0.0, d.xyzMM[2], 0.0,
@@ -546,10 +456,10 @@ mat44 xform_mat(struct TDICOMdata d) {
         dim.v[2] = d.CSA.mosaicSlices;
 		vec4 dim4 = setVec4((nRowCol-1)*dim.v[0]/2.0f, (nRowCol-1)*dim.v[1]/2.0f, 0);
 		vec4 offset = nifti_vect44mat44_mul(dim4, R44 );
-        //printf("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]);
-        //printf("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]);
-        //printf("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]);
-		//printf("nRowCol\t%g\n", nRowCol);
+        //printMessage("%g %g %g\n", dim.v[0], dim.v[1], dim.v[2]);
+        //printMessage("%g %g %g\n", dim4.v[0], dim4.v[1], dim4.v[2]);
+        //printMessage("%g %g %g %g\n", offset.v[0], offset.v[1], offset.v[2], offset.v[3]);
+		//printMessage("nRowCol\t%g\n", nRowCol);
 		R44.m[0][3] = offset.v[0];
 		R44.m[1][3] = offset.v[1];
 		R44.m[2][3] = offset.v[2];
@@ -564,11 +474,11 @@ mat44 xform_mat(struct TDICOMdata d) {
 		return R44;
 	} else if (true) {
 //SliceNormalVector TO DO
-		printf("Not completed");
+		printMessage("Not completed");
 		exit(2);
 		return R44;
 	}
-	printf("Unable to determine spatial transform\n");
+	printMessage("Unable to determine spatial transform\n");
 	exit(1);
 }
 
@@ -585,6 +495,14 @@ mat44 set_nii_header(struct TDICOMdata d) {
 }
 #endif
 
+float deFuzz(float v) {
+    if (fabs(v) < 0.00001)
+        return 0;
+    else
+        return v;
+
+}
+
 // This code predates  Xiangrui Li's set_nii_header function
 mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h, int* sliceDir) {
     *sliceDir = 0;
@@ -593,6 +511,7 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1
         double nRowCol = ceil(sqrt((double) d.CSA.mosaicSlices));
         double lFactorX = (d.xyzDim[1] -(d.xyzDim[1]/nRowCol)   )/2.0;
         double lFactorY = (d.xyzDim[2] -(d.xyzDim[2]/nRowCol)   )/2.0;
+        //printf("%g %g\n", lFactorX, lFactorY);
         Q44.m[0][3] =(float)((Q44.m[0][0]*lFactorX)+(Q44.m[0][1]*lFactorY)+Q44.m[0][3]);
 		Q44.m[1][3] = (float)((Q44.m[1][0] * lFactorX) + (Q44.m[1][1] * lFactorY) + Q44.m[1][3]);
 		Q44.m[2][3] = (float)((Q44.m[2][0] * lFactorX) + (Q44.m[2][1] * lFactorY) + Q44.m[2][3]);
@@ -622,11 +541,15 @@ mat44 set_nii_header_x(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1
     return Q44;
 }
 
-int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_1_header *h) { //fill header s and q form
+int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_1_header *h, int isVerbose) { //fill header s and q form
     //see http://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1_io.c
     //returns sliceDir: 0=unknown,1=sag,2=coro,3=axial,-=reversed slices
     int sliceDir = 0;
-    if (h->dim[3] < 2) return sliceDir; //don't care direction for single slice
+    if (h->dim[3] < 2) {
+    	mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir);
+    	setQSForm(h,Q44, isVerbose);
+    	return sliceDir; //don't care direction for single slice
+    }
     h->sform_code = NIFTI_XFORM_UNKNOWN;
     h->qform_code = NIFTI_XFORM_UNKNOWN;
     bool isOK = false;
@@ -636,14 +559,14 @@ int headerDcm2NiiSForm(struct TDICOMdata d, struct TDICOMdata d2,  struct nifti_
         //we will have to guess, assume axial acquisition saved in standard Siemens style?
         d.orient[1] = 1.0f; d.orient[2] = 0.0f;  d.orient[3] = 0.0f;
         d.orient[1] = 0.0f; d.orient[2] = 1.0f;  d.orient[3] = 0.0f;
-        if ((d.isNonImage) || ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3) && (d.manufacturer == kMANUFACTURER_SIEMENS)))
-           printf("Unable to determine spatial orientation: 0020,0037 missing (probably not a problem: derived image)\n");
-        else
-            printf("Unable to determine spatial orientation: 0020,0037 missing!\n");
+        if ((d.isNonImage) || ((d.bitsAllocated == 8) && (d.samplesPerPixel == 3) && (d.manufacturer == kMANUFACTURER_SIEMENS))) {
+           printMessage("Unable to determine spatial orientation: 0020,0037 missing (probably not a problem: derived image)\n");
+        } else {
+            printMessage("Unable to determine spatial orientation: 0020,0037 missing!\n");
+        }
     }
-    //mat44 Q44 = set_nii_header(d);
     mat44 Q44 = set_nii_header_x(d, d2, h, &sliceDir);
-    setQSForm(h,Q44);
+    setQSForm(h,Q44, isVerbose);
     return sliceDir;
 } //headerDcm2NiiSForm()
 
@@ -675,7 +598,7 @@ int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_hea
     snprintf(h->descrip,80, "%s",txt);
     if (strlen(d.imageComments) > 0)
         snprintf(h->aux_file,24,"%s",d.imageComments);
-    return headerDcm2NiiSForm(d,d2, h);
+    return headerDcm2NiiSForm(d,d2, h, true);
 } //headerDcm2Nii2()
 
 int dcmStrLen (int len) {
@@ -703,48 +626,64 @@ struct TDICOMdata clear_dicom_data() {
         d.xyzDim[i] = 1;
     for (int i = 0; i < 7; i++)
         d.orient[i] = 0.0f;
-    d.patientPositionSequentialRepeats = 0;//d.isHasMixed = false;
+    strcpy(d.patientName, "");
+    strcpy(d.patientID, "");
+    strcpy(d.imageType,"");
+    strcpy(d.imageComments, "");
+    strcpy(d.studyDate, "");
+    strcpy(d.studyTime, "");
+    strcpy(d.protocolName, "");
+    strcpy(d.seriesDescription, "");
+    strcpy(d.sequenceName, "");
+    strcpy(d.scanningSequence, "");
+    strcpy(d.sequenceVariant, "");
+    strcpy(d.manufacturersModelName, "N/A");
+    strcpy(d.procedureStepDescription, "");
+    strcpy(d.seriesInstanceUID, "");
+    strcpy(d.studyInstanceUID, "");
+    strcpy(d.bodyPartExamined,"");
+    d.patientPositionSequentialRepeats = 0;
     d.isHasPhase = false;
     d.isHasMagnitude = false;
     d.sliceOrient = kSliceOrientUnknown;
-    strcpy(d.patientName, "John_Doe");
-    strcpy(d.patientID, "ID123");
-    strcpy(d.imageType,"ORIGINAL");
-    strcpy(d.imageComments, "imgComments");
-    strcpy(d.studyDate, "1/1/1977");
-    strcpy(d.studyTime, "11:11:11");
-    strcpy(d.manufacturersModelName, "N/A");
     d.dateTime = (double)19770703150928.0;
     d.acquisitionTime = 0.0f;
     d.acquisitionDate = 0.0f;
-    strcpy(d.protocolName, "MPRAGE");
-    strcpy(d.seriesDescription, "T1_mprage");
-    strcpy(d.sequenceName, "T1");
-    strcpy(d.scanningSequence, "tfl3d1_ns");
     d.manufacturer = kMANUFACTURER_UNKNOWN;
     d.isPlanarRGB = false;
     d.lastScanLoc = NAN;
-    d.TR = 0;
-    d.TE = 0;
+    d.TR = 0.0;
+    d.TE = 0.0;
+    d.TI = 0.0;
     d.flipAngle = 0.0;
     d.fieldStrength = 0.0;
     d.numberOfDynamicScans = 0;
     d.echoNum = 1;
     d.coilNum = 1;
+    d.patientPositionNumPhilips = 0;
     d.imageBytes = 0;
     d.intenScale = 1;
+    d.intenScalePhilips = 0;
     d.intenIntercept = 0;
     d.gantryTilt = 0.0;
+    d.radionuclidePositronFraction = 0.0;
+    d.radionuclideTotalDose = 0.0;
+    d.radionuclideHalfLife = 0.0;
+    d.doseCalibrationFactor = 0.0;
+    d.ecat_isotope_halflife = 0.0;
+    d.ecat_dosage = 0.0;
     d.seriesNum = 1;
     d.acquNum = 0;
     d.imageNum = 1;
     d.imageStart = 0;
     d.is3DAcq = false; //e.g. MP-RAGE, SPACE, TFE
+    d.isSlicesSpatiallySequentialPhilips = true; //Philips can save slices in random order, e.g. 4,5,6,1,2,3
     d.isNonImage = false; //0008,0008 = DERIVED,CSAPARALLEL,POSDISP
     d.bitsAllocated = 16;//bits
     d.bitsStored = 0;
     d.samplesPerPixel = 1;
     d.isValid = false;
+    d.isXRay = false;
     d.isSigned = false; //default is unsigned!
     d.isFloat = false; //default is for integers, not single or double precision
     d.isResampled = false; //assume data not resliced to remove gantry tilt problems
@@ -783,15 +722,15 @@ void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
     //lLength = (int)strlen(test);
 
     if (lLength < 1) return;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	char * cString = (char *)malloc(sizeof(char) * (lLength + 1));
-#else
-	char cString[lLength + 1];
-#endif
+//#else
+//	char cString[lLength + 1];
+//#endif
     cString[lLength] =0;
     memcpy(cString, (char*)&lBuffer[0], lLength);
     //memcpy(cString, test, lLength);
-    //printf("X%dX\n", (unsigned char)d.patientName[1]);
+    //printMessage("X%dX\n", (unsigned char)d.patientName[1]);
     for (int i = 0; i < lLength; i++)
         //assume specificCharacterSet (0008,0005) is ISO_IR 100 http://en.wikipedia.org/wiki/ISO/IEC_8859-1
         if (cString[i]< 1) {
@@ -832,21 +771,27 @@ void dcmStr(int lLength, unsigned char lBuffer[], char* lOut) {
     //while ((len > 0) && (cString[len]=='_')) len--; //remove trailing '_'
     cString[len] = 0; //null-terminate, strlcpy does this anyway
     len = dcmStrLen(len);
+    if (len == kDICOMStr) { //we need space for null-termination
+		if (cString[len-2] == '_') len = len -2;
+	}
     memcpy(lOut,cString,len-1);
     lOut[len-1] = 0;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(cString);
-#endif
+//#endif
 } //dcmStr()
 
+inline bool littleEndianPlatform ()
+{
+    uint32_t value = 1;
+    return (*((char *) &value) == 1);
+}
+
 float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//read binary 32-bit float
     //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
-#ifdef __BIG_ENDIAN__
-    bool swap = littleEndian;
-#else
-    bool swap = !littleEndian;
-#endif
-    float retVal;
+    bool swap = (littleEndian != littleEndianPlatform());
+    float retVal = 0;
+    if (lByteLength < 4) return retVal;
     memcpy(&retVal, (char*)&lBuffer[0], 4);
     if (!swap) return retVal;
     float swapVal;
@@ -856,17 +801,13 @@ float dcmFloat(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//r
     outFloat[1] = inFloat[2];
     outFloat[2] = inFloat[1];
     outFloat[3] = inFloat[0];
-    //printf("swapped val = %f\n",swapVal);
+    //printMessage("swapped val = %f\n",swapVal);
     return swapVal;
 } //dcmFloat()
 
 double dcmFloatDouble(int lByteLength, unsigned char lBuffer[], bool littleEndian) {//read binary 32-bit float
     //http://stackoverflow.com/questions/2782725/converting-float-values-from-big-endian-to-little-endian
-#ifdef __BIG_ENDIAN__
-    bool swap = littleEndian;
-#else
-    bool swap = !littleEndian;
-#endif
+    bool swap = (littleEndian != littleEndianPlatform());
     double retVal = 0.0f;
     if (lByteLength < 8) return retVal;
     memcpy(&retVal, (char*)&lBuffer[0], 8);
@@ -882,7 +823,7 @@ double dcmFloatDouble(int lByteLength, unsigned char lBuffer[], bool littleEndia
     returnFloat[5] = floatToConvert[2];
     returnFloat[6] = floatToConvert[1];
     returnFloat[7] = floatToConvert[0];
-    //printf("swapped val = %f\n",retVal);
+    //printMessage("swapped val = %f\n",retVal);
     return retVal;
 } //dcmFloatDouble()
 
@@ -898,33 +839,32 @@ int dcmInt (int lByteLength, unsigned char lBuffer[], bool littleEndian) { //rea
 } //dcmInt()
 
 int dcmStrInt (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
-	char cString[lByteLength + 1];
-#endif
-
+//#else
+//	char cString[lByteLength + 1];
+//#endif
     cString[lByteLength] =0;
     memcpy(cString, (char*)&lBuffer[0], lByteLength);
-    //printf(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
+    //printMessage(" --> *%s* %s%s\n",cString, &lBuffer[0],&lBuffer[1]);
     int ret = atoi(cString);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(cString);
-#endif
+//#endif
 	return ret;
 } //dcmStrInt()
 
 int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float stored as a string
     if (lByteLength < 2) return kMANUFACTURER_UNKNOWN;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
-	char cString[lByteLength + 1];
-#endif
+//#else
+//	char cString[lByteLength + 1];
+//#endif
 	int ret = kMANUFACTURER_UNKNOWN;
     cString[lByteLength] =0;
     memcpy(cString, (char*)&lBuffer[0], lByteLength);
-    //printf("MANU %s\n",cString);
+    //printMessage("MANU %s\n",cString);
     if ((toupper(cString[0])== 'S') && (toupper(cString[1])== 'I'))
         ret = kMANUFACTURER_SIEMENS;
     if ((toupper(cString[0])== 'G') && (toupper(cString[1])== 'E'))
@@ -933,9 +873,9 @@ int dcmStrManufacturer (int lByteLength, unsigned char lBuffer[]) {//read float
         ret = kMANUFACTURER_PHILIPS;
     if ((toupper(cString[0])== 'T') && (toupper(cString[1])== 'O'))
         ret = kMANUFACTURER_TOSHIBA;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(cString);
-#endif
+//#endif
 	return ret;
 } //dcmStrManufacturer
 
@@ -978,21 +918,19 @@ float csaMultiFloat (unsigned char buff[], int nItems, float Floats[], int *Item
     for (int lI = 1; lI <= nItems; lI++) {
         memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
         lPos +=sizeof(itemCSA);
+
+        // Storage order is always little-endian, so byte-swap required values if necessary
+        if (!littleEndianPlatform())
+            nifti_swap_4bytes(1, &itemCSA.xx2_Len);
+
         if (itemCSA.xx2_Len > 0) {
-#ifdef _MSC_VER
-			char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len));
-#else
-			char cString[itemCSA.xx2_Len];
-#endif
-            memcpy(cString, &buff[lPos], sizeof(cString)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
+            char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len));
+            memcpy(cString, &buff[lPos], itemCSA.xx2_Len); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
             lPos += ((itemCSA.xx2_Len +3)/4)*4;
-            //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
+            //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
             Floats[lI] = (float) atof(cString);
             *ItemsOK = lI; //some sequences have store empty items
-
-#ifdef _MSC_VER
-			free(cString);
-#endif
+            free(cString);
         }
     } //for each item
     return Floats[1];
@@ -1006,20 +944,25 @@ bool csaIsPhaseMap (unsigned char buff[], int nItems) {
     for (int lI = 1; lI <= nItems; lI++) {
         memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
         lPos +=sizeof(itemCSA);
+
+        // Storage order is always little-endian, so byte-swap required values if necessary
+        if (!littleEndianPlatform())
+            nifti_swap_4bytes(1, &itemCSA.xx2_Len);
+
         if (itemCSA.xx2_Len > 0) {
-#ifdef _MSC_VER
-            char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len));
-#else
-            char cString[itemCSA.xx2_Len];
-#endif
-            memcpy(cString, &buff[lPos], sizeof(cString)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
+//#ifdef _MSC_VER
+            char * cString = (char *)malloc(sizeof(char) * (itemCSA.xx2_Len + 1));
+//#else
+ //           char cString[itemCSA.xx2_Len];
+//#endif
+            memcpy(cString, &buff[lPos], sizeof(itemCSA.xx2_Len)); //TPX memcpy(&cString, &buff[lPos], sizeof(cString));
             lPos += ((itemCSA.xx2_Len +3)/4)*4;
-            //printf(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
+            //printMessage(" %d item length %d = %s\n",lI, itemCSA.xx2_Len, cString);
             if (strcmp(cString, "CC:ComplexAdd") == 0)
                 return true;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
             free(cString);
-#endif
+//#endif
         }
     } //for each item
     return false;
@@ -1027,7 +970,7 @@ bool csaIsPhaseMap (unsigned char buff[], int nItems) {
 
 int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, int isVerbose, struct TDTI4D *dti4D) {
     //see also http://afni.nimh.nih.gov/pub/dist/src/siemens_dicom_csa.c
-    //printf("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]);
+    //printMessage("%c%c%c%c\n",buff[0],buff[1],buff[2],buff[3]);
     if (lLength < 36) return EXIT_FAILURE;
     if ((buff[0] != 'S') || (buff[1] != 'V') || (buff[2] != '1') || (buff[3] != '0') ) return EXIT_FAILURE;
     int lPos = 8; //skip 8 bytes of data, 'SV10' plus  2 32-bit values unused1 and unused2
@@ -1041,8 +984,13 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
     for (int lT = 1; lT <= lnTag; lT++) {
         memcpy(&tagCSA, &buff[lPos], sizeof(tagCSA)); //read tag
         lPos +=sizeof(tagCSA);
+
+        // Storage order is always little-endian, so byte-swap required values if necessary
+        if (!littleEndianPlatform())
+            nifti_swap_4bytes(1, &tagCSA.nitems);
+
         if (isVerbose > 1) //extreme verbosity: show every CSA tag
-        	printf("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems);
+        	printMessage("%d CSA of %s %d\n",lPos, tagCSA.name, tagCSA.nitems);
         if (tagCSA.nitems > 0) {
             if (strcmp(tagCSA.name, "ImageHistory") == 0)
                 CSA->isPhaseMap =  csaIsPhaseMap(&buff[lPos], tagCSA.nitems);
@@ -1051,7 +999,7 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
             else if (strcmp(tagCSA.name, "B_value") == 0) {
                 CSA->dtiV[0] = csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK);
                 if (CSA->dtiV[0] < 0.0) {
-                    printf("WARNING: (Corrupt) CSA reports negative b-value! %g\n",CSA->dtiV[0]);
+                    printWarning("(Corrupt) CSA reports negative b-value! %g\n",CSA->dtiV[0]);
                     CSA->dtiV[0] = 0.0;
                 }
                 CSA->numDti = 1; //triggered by b-value, as B0 images do not have DiffusionGradientDirection tag
@@ -1061,23 +1009,23 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
                 CSA->dtiV[2] = lFloats[2];
                 CSA->dtiV[3] = lFloats[3];
                 if (isVerbose)
-                    printf("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]);
+                    printMessage("DiffusionGradientDirection %f %f %f\n",lFloats[1],lFloats[2],lFloats[3]);
             } else if ((strcmp(tagCSA.name, "SliceNormalVector") == 0) && (tagCSA.nitems > 2)){
                 CSA->sliceNormV[1] = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
                 CSA->sliceNormV[2] = lFloats[2];
                 CSA->sliceNormV[3] = lFloats[3];
                 if (isVerbose)
-                    printf("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]);
+                    printMessage("SliceNormalVector %f %f %f\n",CSA->sliceNormV[1],CSA->sliceNormV[2],CSA->sliceNormV[3]);
             } else if (strcmp(tagCSA.name, "SliceMeasurementDuration") == 0)
                 CSA->sliceMeasurementDuration = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
             else if (strcmp(tagCSA.name, "BandwidthPerPixelPhaseEncode") == 0)
                 CSA->bandwidthPerPixelPhaseEncode = csaMultiFloat (&buff[lPos], 3,lFloats, &itemsOK);
             else if ((strcmp(tagCSA.name, "MosaicRefAcqTimes") == 0) && (tagCSA.nitems > 3)  ){
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 				float * sliceTimes = (float *)malloc(sizeof(float) * (tagCSA.nitems + 1));
-#else
-				float sliceTimes[tagCSA.nitems + 1];
-#endif
+//#else
+//				float sliceTimes[tagCSA.nitems + 1];
+//#endif
                 csaMultiFloat (&buff[lPos], tagCSA.nitems,sliceTimes, &itemsOK);
                 float maxTimeValue, minTimeValue, timeValue1;
                 for (int z = 0; z < kMaxDTI4D; z++)
@@ -1096,10 +1044,10 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
                 minTimeValue = sliceTimes[1];
                 maxTimeValue = minTimeValue;
                 if (isVerbose)
-                    printf("sliceTimes %g\t", sliceTimes[1]);
+                    printMessage("sliceTimes %g\t", sliceTimes[1]);
 				for (int z = 2; z <= itemsOK; z++) { //find index and value of fastest time
                     if (isVerbose)
-                        printf("%g\t",  sliceTimes[z]);
+                        printMessage("%g\t",  sliceTimes[z]);
                     if (sliceTimes[z] == 0)
                     	nTimeZero++;
                     if (sliceTimes[z] < minTimeValue) {
@@ -1113,7 +1061,7 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
                     if (sliceTimes[z] == timeValue1) CSA->multiBandFactor++;
 				}
                 if (isVerbose)
-                    printf("\n");
+                    printMessage("\n");
                 CSA->slice_start = minTimeIndex -1;
                 CSA->slice_end = maxTimeIndex -1;
                 if (minTimeIndex == 2)
@@ -1134,17 +1082,17 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
                      [sliceTimesNS addObject:[NSNumber numberWithFloat:sliceTimes[z]]];
                      NSLog(@" Warning: unable to determine slice order for %lu slice mosaic: %@",(unsigned long)[sliceTimesNS count],sliceTimesNS );
                      */
-                    printf("Warning: unable to determine slice order from CSA tag MosaicRefAcqTimes\n");
+                    printWarning("Unable to determine slice order from CSA tag MosaicRefAcqTimes\n");
                 }
                 if ((CSA->sliceOrder != NIFTI_SLICE_UNKNOWN) && (nTimeZero > 1)) {
                 	if (isVerbose)
-                		printf(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder);
+                		printMessage(" Multiband x%d sequence: setting slice order as UNKNOWN (instead of %d)\n", nTimeZero, CSA->sliceOrder);
                 	CSA->sliceOrder = NIFTI_SLICE_UNKNOWN;
 
                 }
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 				free(sliceTimes);
-#endif
+//#endif
             } else if (strcmp(tagCSA.name, "ProtocolSliceNumber") == 0)
                 CSA->protocolSliceNumber1 = (int) round (csaMultiFloat (&buff[lPos], 1,lFloats, &itemsOK));
             else if (strcmp(tagCSA.name, "PhaseEncodingDirectionPositive") == 0)
@@ -1152,6 +1100,9 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
             for (int lI = 1; lI <= tagCSA.nitems; lI++) {
                 memcpy(&itemCSA, &buff[lPos], sizeof(itemCSA));
                 lPos +=sizeof(itemCSA);
+                // Storage order is always little-endian, so byte-swap required values if necessary
+                if (!littleEndianPlatform())
+                    nifti_swap_4bytes(1, &itemCSA.xx2_Len);
                 lPos += ((itemCSA.xx2_Len +3)/4)*4;
             }
         } //if at least 1 item
@@ -1162,11 +1113,11 @@ int readCSAImageHeader(unsigned char *buff, int lLength, struct TCSAdata *CSA, i
 void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloats) {
     //warning: lFloats indexed from 1! will fill lFloats[1]..[nFloats]
     if ((lnFloats < 1) || (lByteLength < 1)) return;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
-	char cString[lByteLength + 1];
-#endif
+//#else
+//	char cString[lByteLength + 1];
+//#endif
     memcpy(cString, (char*)&lBuffer[0], lByteLength);
     cString[lByteLength] = 0; //null terminate
     char *temp=( char *)malloc(lByteLength+1);
@@ -1177,40 +1128,40 @@ void dcmMultiFloat (int lByteLength, char lBuffer[], int lnFloats, float *lFloat
         if ((isOK) && ((i == (lByteLength)) || (lBuffer[i] == '/')  || (lBuffer[i] == ' ')  || (lBuffer[i] == '\\') )){
             //x strlcpy(temp,&cString[lStart],i-lStart+1);
             snprintf(temp,i-lStart+1,"%s",&cString[lStart]);
-            //printf("dcmMultiFloat %s\n",temp);
+            //printMessage("dcmMultiFloat %s\n",temp);
             if (f < lnFloats) {
                 f ++;
                 lFloats[f] = (float) atof(temp);
                 isOK = false;
-                //printf("%d == %f\n", f, atof(temp));
+                //printMessage("%d == %f\n", f, atof(temp));
             } //if f <= nFloats
             lStart = i+1;
         } //if isOK
     }  //for i to length
     free(temp);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(cString);
-#endif
+//#endif
 } //dcmMultiFloat()
 
 float dcmStrFloat (int lByteLength, unsigned char lBuffer[]) { //read float stored as a string
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	char * cString = (char *)malloc(sizeof(char) * (lByteLength + 1));
-#else
-	char cString[lByteLength + 1];
-#endif
+//#else
+//	char cString[lByteLength + 1];
+//#endif
     memcpy(cString, (char*)&lBuffer[0], lByteLength);
     cString[lByteLength] = 0; //null terminate
     float ret = (float) atof(cString);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(cString);
-#endif
+//#endif
 	return ret;
 } //dcmStrFloat()
 
-int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) {
-    //printf("bytes %dx%dx%d %d, %d\n",d.XYZdim[1],d.XYZdim[2],d.XYZdim[3], d.Allocbits_per_pixel, d.samplesPerPixel);
-    memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent
+int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) {
+    //printMessage("bytes %dx%dx%d %d, %d\n",d.XYZdim[1],d.XYZdim[2],d.XYZdim[3], d.Allocbits_per_pixel, d.samplesPerPixel);
+	memset(h, 0, sizeof(nifti_1_header)); //zero-fill structure so unused items are consistent
     for (int i = 0; i < 80; i++) h->descrip[i] = 0;
     for (int i = 0; i < 24; i++) h->aux_file[i] = 0;
     for (int i = 0; i < 18; i++) h->db_name[i] = 0;
@@ -1234,11 +1185,7 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) {
     else if ((d.bitsAllocated == 64) && (d.isFloat))
         h->datatype = DT_FLOAT64;
     else {
-#ifdef myUseCOut
-     	std::cout<<"Unsupported DICOM bit-depth " <<d.bitsAllocated << " with " << d.samplesPerPixel << "samples per pixel" <<std::endl;
-#else
-        printf("Unsupported DICOM bit-depth %d with %d samples per pixel\n",d.bitsAllocated,d.samplesPerPixel);
-#endif
+        printMessage("Unsupported DICOM bit-depth %d with %d samples per pixel\n",d.bitsAllocated,d.samplesPerPixel);
         return EXIT_FAILURE;
     }
     if ((h->datatype == DT_UINT16) && (d.bitsStored > 0) &&(d.bitsStored < 16))
@@ -1305,7 +1252,8 @@ int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h) {
     h->pixdim[0] = 1; //QFactor should be 1 or -1
     h->sizeof_hdr = 348; //used to signify header does not need to be byte-swapped
     h->slice_code = d.CSA.sliceOrder;
-    headerDcm2Nii2(d, d, h);
+    if (isComputeSForm)
+    	headerDcm2Nii2(d, d, h);
     return EXIT_SUCCESS;
 } // headerDcm2Nii()
 
@@ -1332,13 +1280,6 @@ void changeExt (char *file_name, const char* ext) {
     }
 } //changeExt()
 
-float PhilipsPreciseVal (float lPV, float lRS, float lRI, float lSS) {
-    if ((lRS*lSS) == 0) //avoid divide by zero
-        return 0.0;
-    else
-        return (lPV * lRS + lRI) / (lRS * lSS);
-}
-
 struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D) {
     struct TDICOMdata d = clear_dicom_data();
     strcpy(d.protocolName, ""); //erase dummy with empty
@@ -1381,7 +1322,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
 #define	kv3	46
 #define	kASL	48
     char buff[LINESZ];
-    float intenScalePhilips = 0.0f;
+    //float intenScalePhilips = 0.0f;
     float maxBValue = 0.0f;
     float maxDynTime = 0.0f;
     float minDynTime = 999999.0f;
@@ -1411,11 +1352,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
                        ,Comment[4], Comment[5],Comment[6]);
                 parVers = (int)round(atof(Comment[6])*10); //4.2 = 42 etc
                 if (parVers < 40) {
-#ifdef myUseCOut
-                    std::cout<<"This software is unable to convert ancient PAR files: please use legacy dcm2nii" <<std::endl;
-#else
-                    printf("This software is unable to convert ancient PAR files: please use legacy dcm2nii\n");
-#endif
+                    printMessage("This software is unable to convert ancient PAR files: please use legacy dcm2nii\n");
                     return d;
                     //nCols = 26; //e.g. PAR 3.0 has 26 relevant columns
                 } else if (parVers < 41)
@@ -1444,7 +1381,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
                 strcat(d.patientName, Comment[5]);
                 strcat(d.patientName, Comment[6]);
                 strcat(d.patientName, Comment[7]);
-                //printf("%s\n",d.patientName);
+                //printMessage("%s\n",d.patientName);
 
             }
             if ((strcmp(Comment[0], "Protocol") == 0) && (strcmp(Comment[1], "name") == 0)) {
@@ -1453,7 +1390,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
                 strcat(d.protocolName, Comment[5]);
                 strcat(d.protocolName, Comment[6]);
                 strcat(d.protocolName, Comment[7]);
-                //printf("%s\n",d.protocolName);
+                //printMessage("%s\n",d.protocolName);
             }
             if ((strcmp(Comment[0], "Examination") == 0) && (strcmp(Comment[1], "date/time") == 0)) {
                 strcpy(d.studyDate, Comment[3]);
@@ -1484,11 +1421,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
             continue;
         }
         if (parVers < 20) {
-#ifdef myUseCOut
-            std::cout<<"Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: " << parname<<std::endl;
-#else
-            printf("Error: PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname);
-#endif
+            printError("PAR files should have 'CLINICAL TRYOUT' line with a version from 2.0-4.2: %s\n", parname);
             free (cols);
             return d;
         }
@@ -1516,14 +1449,10 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
 			d.bitsStored = (int) cols[kBitsPerVoxel];
             d.intenIntercept = cols[kRI];
             d.intenScale = cols[kRS];
-            intenScalePhilips = cols[kSS];
+            d.intenScalePhilips = cols[kSS];
         } else {
             if ((d.xyzDim[1] != cols[kXdim]) || (d.xyzDim[2] != cols[kYdim]) || (d.bitsAllocated != cols[kBitsPerVoxel]) ) {
-#ifdef myUseCOut
-                std::cout<<"Error: slice dimensions or bit depth varies "<< parname <<std::endl;
-#else
-                printf("Error: slice dimensions or bit depth varies %s\n", parname);
-#endif
+                printError("Slice dimensions or bit depth varies %s\n", parname);
                 return d;
             }
             if ((d.patientPositionSequentialRepeats == 0) && ((!isSameFloat(d.patientPosition[1],cols[kPositionRL])) ||
@@ -1538,6 +1467,21 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
         if (cols[kImageType] != 0) d.isHasPhase = true;
         if (cols[kDynTime] > maxDynTime) maxDynTime = cols[kDynTime];
         if (cols[kDynTime] < minDynTime) minDynTime = cols[kDynTime];
+        if ((cols[kEcho] == 1) && (cols[kDyn] == 1) && (cols[kCardiac] == 1) && (cols[kGradientNumber] == 1)) {
+			if (cols[kSlice] == 1) {
+				d.patientPosition[1] = cols[kPositionRL];
+            	d.patientPosition[2] = cols[kPositionAP];
+            	d.patientPosition[3] = cols[kPositionFH];
+			}
+			if (d.patientPositionNumPhilips < kMaxDTI4D) {
+				dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips = round(cols[kSlice]);
+				if ((d.patientPositionNumPhilips > 0) && (dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips < dti4D->S[d.patientPositionNumPhilips-1].sliceNumberMrPhilips)) {
+					d.isSlicesSpatiallySequentialPhilips = false;
+					//printMessage("slices are not contiguous\n");
+				}
+			}
+			d.patientPositionNumPhilips++;
+        }
         if (cols[kGradientNumber] > 0) {
 			/*int dir = (int) cols[kGradientNumber];
             if ((dir > 0) && (cols[kbval] > 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) && (cols[kv1] == 0.0) ) {
@@ -1551,8 +1495,8 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
                 int dir = d.CSA.numDti;
                 if (dir <= kMaxDTI4D) {
                     if (isVerbose ) {
-                        if (d.CSA.numDti == 1) printf("n\tdir\tbValue\tV1\tV2\tV3\n");
-                        printf("%d\t%g\t%g\t%g\t%g\t%g\n", dir-1, cols[kGradientNumber], cols[kbval], cols[kv1], cols[kv2], cols[kv3]);
+                        if (d.CSA.numDti == 1) printMessage("n\tdir\tbValue\tV1\tV2\tV3\n");
+                        printMessage("%d\t%g\t%g\t%g\t%g\t%g\n", dir-1, cols[kGradientNumber], cols[kbval], cols[kv1], cols[kv2], cols[kv3]);
                     }
                     dti4D->S[dir-1].V[0] = cols[kbval];
                     dti4D->S[dir-1].V[1] = cols[kv1];
@@ -1564,7 +1508,7 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
 
             }
         } //if DTI directions
-        //printf("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
+        //printMessage("%f %f %lu\n",cols[9],cols[kGradientNumber], strlen(buff))
         p = fgets (buff, LINESZ, fp);//get next line
     }
 
@@ -1574,37 +1518,25 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
     d.isValid = true;
     d.isSigned = true;
     if ((slice % d.xyzDim[3]) != 0) {
-        printf("Error: slices (%d) not divisible by slices (%d) [acquisition aborted]. Try nii_rescue_par to fix this: %s\n", slice, d.xyzDim[3], parname);
+        printError("Total number of slices (%d) not divisible by slices per 3D volume (%d) [acquisition aborted]. Try nii_rescue_par to fix this: %s\n", slice, d.xyzDim[3], parname);
         d.isValid = true;
     }
     d.xyzDim[4] = slice/d.xyzDim[3];
     d.locationsInAcquisition = d.xyzDim[3];
-#ifdef myUseCOut
-    if (isIntenScaleVaries)
-        std::cout<<"Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]" <<std::endl;
-
-    if (!isIndexSequential)
-        std::cout<<"Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]" <<std::endl;
-    printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n");
-    std::cout<<"Done reading PAR header version "<< (float)parVers/10<<" with "<< d.CSA.numDti << " volumes"<<std::endl;
-#else
     if (ADCwarning)
-        printf("Warning: PAR/REC dataset includes an ADC map that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n");
-
+        printWarning("PAR/REC dataset includes an ADC map that could disrupt analysis. Please remove volume and ensure vectors are reported correctly\n");
     if (isIntenScaleVaries)
-       printf("Warning: intensity slope/intercept varies between slices! [solution: user dcm2nii instead]\n");
-        if (!isIndexSequential)
-            printf("Warning: slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n");
-            printf("Done reading PAR header version %.1f, with %d volumes\n", (float)parVers/10, d.CSA.numDti);
-#endif
-
-            //see Xiangrui Li 's dicm2nii (also BSD license)
-            // http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter
-            // Rotation order and signs are figured out by try and err, not 100% sure
-            float d2r = (float) (M_PI/180.0);
-            vec3 ca = setVec3(cos(d.angulation[1]*d2r),cos(d.angulation[2]*d2r),cos(d.angulation[3]*d2r));
-            vec3 sa = setVec3(sin(d.angulation[1]*d2r),sin(d.angulation[2]*d2r),sin(d.angulation[3]*d2r));
-            mat33 rx,ry,rz;
+       printWarning("Intensity slope/intercept varies between slices! [solution: user dcm2nii instead]\n");
+    if (!isIndexSequential)
+    	printWarning("Slice order not saved to disk sequentially! [solution: user dcm2nii instead]\n");
+    printMessage("Done reading PAR header version %.1f, with %d volumes\n", (float)parVers/10, d.CSA.numDti);
+	//see Xiangrui Li 's dicm2nii (also BSD license)
+	// http://www.mathworks.com/matlabcentral/fileexchange/42997-dicom-to-nifti-converter
+	// Rotation order and signs are figured out by try and err, not 100% sure
+	float d2r = (float) (M_PI/180.0);
+	vec3 ca = setVec3(cos(d.angulation[1]*d2r),cos(d.angulation[2]*d2r),cos(d.angulation[3]*d2r));
+	vec3 sa = setVec3(sin(d.angulation[1]*d2r),sin(d.angulation[2]*d2r),sin(d.angulation[3]*d2r));
+	mat33 rx,ry,rz;
     LOAD_MAT33(rx,1.0f, 0.0f, 0.0f, 0.0f, ca.v[0], -sa.v[0], 0.0f, sa.v[0], ca.v[0]);
     LOAD_MAT33(ry, ca.v[1], 0.0f, sa.v[1], 0.0f, 1.0f, 0.0f, -sa.v[1], 0.0f, ca.v[1]);
     LOAD_MAT33(rz, ca.v[2], -sa.v[2], 0.0f, sa.v[2], ca.v[2], 0.0f, 0.0f, 0.0f, 1.0f);
@@ -1634,9 +1566,9 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
     vec3 x;
     if (parVers > 40) //guess
         x = setVec3(((float)d.xyzDim[1]-1)/2,((float)d.xyzDim[2]-1)/2,((float)d.xyzDim[3]-1)/2);
-        else
-            x = setVec3((float)d.xyzDim[1]/2,(float)d.xyzDim[2]/2,((float)d.xyzDim[3]-1)/2);
-            mat44 eye;
+    else
+        x = setVec3((float)d.xyzDim[1]/2,(float)d.xyzDim[2]/2,((float)d.xyzDim[3]-1)/2);
+    mat44 eye;
     LOAD_MAT44(eye, 1.0f,0.0f,0.0f,x.v[0],
                0.0f,1.0f,0.0f,x.v[1],
                0.0f,0.0f,1.0f,x.v[2]);
@@ -1666,34 +1598,22 @@ struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D
             }
     //finish up
     changeExt (parname, "REC");
+    #ifndef _MSC_VER //Linux is case sensitive, #include <unistd.h>
+    if( access( parname, F_OK ) != 0 ) changeExt (parname, "rec");
+	#endif
     d.locationsInAcquisition = d.xyzDim[3];
     d.manufacturer = kMANUFACTURER_PHILIPS;
     d.imageStart = 0;
     if (d.CSA.numDti >= kMaxDTI4D) {
-        printf("Error: unable to convert DTI [increase kMaxDTI4D]\n");
+        printError("Unable to convert DTI [increase kMaxDTI4D]\n");
         d.CSA.numDti = 0;
     };
     if ((maxBValue <= 0.0f) && (maxDynTime > minDynTime) && (d.CSA.numDti > 1)) {
     	float TRms =  1000.0f * (maxDynTime - minDynTime) / (float)(d.CSA.numDti-1);
     	if (fabs(TRms - d.TR) > 0.005f)
-    		printf("Warning: reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms);
+    		printWarning("Reported TR=%gms, measured TR=%gms (prospect. motion corr.?)\n", d.TR, TRms);
     	d.TR = TRms;
     }
-    if (intenScalePhilips != 0.0) {
-        //printf("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d.intenScale,d.intenIntercept,intenScalePhilips);
-        //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/
-        //# === PIXEL VALUES =============================================================
-		//#  PV = pixel value in REC file, FP = floating point value, DV = displayed value on console
-		//#  RS = rescale slope,           RI = rescale intercept,    SS = scale slope
-		//#  DV = PV * RS + RI             FP = DV / (RS * SS)
-        float l0 = PhilipsPreciseVal (0, d.intenScale, d.intenIntercept, intenScalePhilips);
-        float l1 = PhilipsPreciseVal (1, d.intenScale, d.intenIntercept, intenScalePhilips);
-        if (l0 != l1) {
-            d.intenIntercept = l0;
-            d.intenScale = l1-l0;
-        }
-    }
-
     return d;
 } //nii_readParRec()
 
@@ -1704,7 +1624,7 @@ size_t nii_SliceBytes(struct nifti_1_header hdr) {
         if (hdr.dim[i]  > 1)
             imgsz = imgsz * hdr.dim[i];
     return imgsz;
-} //nii_ImgBytes()
+} //nii_SliceBytes()
 
 size_t nii_ImgBytes(struct nifti_1_header hdr) {
     size_t imgsz = hdr.bitpix/8;
@@ -1746,11 +1666,7 @@ unsigned char * nii_demosaic(unsigned char* inImg, struct nifti_1_header *hdr, i
     } //for m = each mosaic slice
     /* //we now provide a warning once per series rather than once per volume (see nii_dicom_batch)
     if (ProtocolSliceNumber1 > 1) {
-#ifdef myUseCOut
-     	std::cout<<"WARNING: CSA 'ProtocolSliceNumber' SUGGESTS REVERSED SLICE ORDER: SPATIAL AND DTI COORDINATES UNTESTED" <<std::endl;
-#else
-        printf("WARNING: WEIRD CSA 'ProtocolSliceNumber': SPATIAL AND DTI TRANSFORMS UNTESTED\n");
-#endif
+        printWarning("Weird CSA 'ProtocolSliceNumber': SPATIAL AND DTI TRANSFORMS UNTESTED\n");
     }*/
     /*if ((ProtocolSliceNumber1 > 1) && (hdr->dim[3] > 1)) { //exceptionally rare: reverse order of slices - now handled in matrix...
      int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8;
@@ -1778,11 +1694,11 @@ unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
         lineBytes = hdr->dim[1];
         dim3to7 = dim3to7 * 3;
     } //rgb data saved planar (RRR..RGGGG..GBBB..B
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	unsigned char * line = (unsigned char *)malloc(sizeof(unsigned char) * (lineBytes));
-#else
-	unsigned char line[lineBytes];
-#endif
+//#else
+//	unsigned char line[lineBytes];
+//#endif
     size_t sliceBytes = hdr->dim[2] * lineBytes;
     int halfY = hdr->dim[2] / 2; //note truncated toward zero, so halfY=2 regardless of 4 or 5 columns
     for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
@@ -1797,9 +1713,9 @@ unsigned char * nii_flipImgY(unsigned char* bImg, struct nifti_1_header *hdr){
             slBottom += lineBytes;
         } //for y
     } //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(line);
-#endif
+//#endif
     return bImg;
 } // nii_flipImgY()
 
@@ -1811,15 +1727,15 @@ unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
     for (int i = 4; i < 8; i++)
         if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i];
     int sliceBytes = hdr->dim[1] * hdr->dim[2] * hdr->bitpix/8;
-    long long volBytes = sliceBytes * hdr->dim[3];
-#ifdef _MSC_VER
+    size_t volBytes = sliceBytes * hdr->dim[3];
+//#ifdef _MSC_VER
 	unsigned char * slice = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes));
-#else
-	unsigned char slice[sliceBytes];
-#endif
+//#else
+//	unsigned char slice[sliceBytes];
+//#endif
     for (int vol = 0; vol < dim4to7; vol++) { //for each 2D slice
-        long long slBottom = vol*volBytes;
-        long long slTop = ((vol+1)*volBytes)-sliceBytes;
+        size_t slBottom = vol*volBytes;
+        size_t slTop = ((vol+1)*volBytes)-sliceBytes;
         for (int z = 0; z < halfZ; z++) {
             //swap order of lines
             memcpy(slice, &bImg[slBottom], sliceBytes); //TPX memcpy(&slice, &bImg[slBottom], sliceBytes);
@@ -1829,12 +1745,41 @@ unsigned char * nii_flipImgZ(unsigned char* bImg, struct nifti_1_header *hdr){
             slBottom += sliceBytes;
         } //for Z
     } //for each volume
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(slice);
-#endif
+//#endif
     return bImg;
 } // nii_flipImgZ()
 
+unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D){
+    //flip slice order - Philips scanners can save data in non-contiguous order
+    //if ((h->dim[3] < 2) || (h->dim[4] > 1)) return bImg;
+    if (h->dim[3] < 2) return bImg;
+    if (h->dim[3] >= kMaxDTI4D) {
+    	printWarning("Unable to reorder slices (%d > %d)\n", h->dim[3], kMaxDTI4D);
+    	return bImg;
+    }
+    //printMessage("<<< Slices not spatially contiguous: please check output [new feature]\n");
+    int dim4to7 = 1;
+    for (int i = 4; i < 8; i++)
+        if (h->dim[i] > 1) dim4to7 = dim4to7 * h->dim[i];
+    int sliceBytes = h->dim[1] * h->dim[2] * h->bitpix/8;
+    if (sliceBytes < 0)  return bImg;
+    size_t volBytes = sliceBytes * h->dim[3];
+    unsigned char *srcImg = (unsigned char *)malloc(volBytes);
+    for (int v = 0; v < dim4to7; v++) {
+    	size_t volStart = v * volBytes;
+    	memcpy(&srcImg[0], &bImg[volStart], volBytes); //dest, src, size
+    	for (int z = 0; z < h->dim[3]; z++) { //for each slice
+			int src = dti4D->S[z].sliceNumberMrPhilips - 1; //-1 as Philips indexes slices from 1 not 0
+			if ((src < 0) || (src >= h->dim[3])) continue;
+			memcpy(&bImg[volStart+(src*sliceBytes)], &srcImg[z*sliceBytes], sliceBytes); //dest, src, size
+    	}
+    }
+    free(srcImg);
+    return bImg;
+}// nii_reorderSlices()
+
 unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
     //flip slice order
     if (h->dim[3] < 2) return bImg;
@@ -1853,9 +1798,9 @@ unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h){
     LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0],
                s.m[1][0],s.m[1][1],s.m[1][2],v.v[1],
                s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]);
-    //printf(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]);
-    setQSForm(h,Q44);
-    //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8);
+    //printMessage(" ----------> %f %f %f\n",v.v[0],v.v[1],v.v[2]);
+    setQSForm(h,Q44, true);
+    //printMessage("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8);
     return nii_flipImgZ(bImg,h);
 }// nii_flipZ()
 
@@ -1876,8 +1821,8 @@ unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
     LOAD_MAT44(Q44, s.m[0][0],s.m[0][1],s.m[0][2],v.v[0],
                s.m[1][0],s.m[1][1],s.m[1][2],v.v[1],
                s.m[2][0],s.m[2][1],s.m[2][2],v.v[2]);
-    setQSForm(h,Q44);
-    //printf("nii_flipImgY dims %dx%dx%d %d \n",h->dim[1],h->dim[2], dim3to7,h->bitpix/8);
+    setQSForm(h,Q44, true);
+    //printMessage("nii_flipImgY dims %dx%d %d \n",h->dim[1],h->dim[2], h->bitpix/8);
     return nii_flipImgY(bImg,h);
 }// nii_flipY()
 
@@ -1885,7 +1830,7 @@ unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h){
 //convert 12-bit allocated data to 16-bit
 // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/
 // looks wrong: this sample toggles between big and little endian stores
-	printf("Warning: support for images that allocate 12 bits is experimental\n");
+	printWarning("Support for images that allocate 12 bits is experimental\n");
 	int nVox = nii_ImgBytes(hdr) / (hdr.bitpix/8);
     for (int i=(nVox-1); i >= 0; i--) {
     	int i16 = i * 2;
@@ -1912,7 +1857,7 @@ void conv12bit16bit(unsigned char * img, struct nifti_1_header hdr) {
 //convert 12-bit allocated data to 16-bit
 // works for MR-MONO2-12-angio-an1 from http://www.barre.nom.fr/medical/samples/
 // looks wrong: this sample toggles between big and little endian stores
-	printf("Warning: support for images that allocate 12 bits is experimental\n");
+	printWarning("Support for images that allocate 12 bits is experimental\n");
 	int nVox = (int) nii_ImgBytes(hdr) / (hdr.bitpix/8);
     for (int i=(nVox-1); i >= 0; i--) {
     	int i16 = i * 2;
@@ -1936,19 +1881,23 @@ unsigned char * nii_loadImgCore(char* imgname, struct nifti_1_header hdr, int bi
          imgszRead = round(imgsz * 0.75);
     FILE *file = fopen(imgname , "rb");
 	if (!file) {
-         printf("Error: unable to open %s\n", imgname);
+         printError("Unable to open %s\n", imgname);
          return NULL;
     }
 	fseek(file, 0, SEEK_END);
-	long long fileLen=ftell(file);
+	long fileLen=ftell(file);
     if (fileLen < (imgszRead+hdr.vox_offset)) {
-        printf("File not large enough to store image data: %s\n", imgname);
+        printMessage("File not large enough to store image data: %s\n", imgname);
         return NULL;
     }
 	fseek(file, (long) hdr.vox_offset, SEEK_SET);
     unsigned char *bImg = (unsigned char *)malloc(imgsz);
-    fread(bImg, imgszRead, 1, file);
+    size_t  sz = fread(bImg, 1, imgszRead, file);
 	fclose(file);
+	if (sz < imgszRead) {
+         printError("Only loaded %zu of %zu bytes for %s\n", sz, imgszRead, imgname);
+         return NULL;
+    }
 	if (bitsAllocated == 12)
 	 conv12bit16bit(bImg, hdr);
     return bImg;
@@ -1965,12 +1914,11 @@ unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr,
                         //int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
                         int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
                         int sliceBytes24 = sliceBytes8 * 3;
-    //printf("planar->rgb %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
                         unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
-#else
-                        unsigned char  slice24[ sliceBytes24 ];
-#endif
+//#else
+//                        unsigned char  slice24[ sliceBytes24 ];
+//#endif
     int sliceOffsetRGB = 0;
                         int sliceOffsetR = 0;
                         int sliceOffsetG = sliceOffsetR + sliceBytes8;
@@ -1987,9 +1935,9 @@ unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr,
                             }
                             sliceOffsetRGB += sliceBytes24;
                         } //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
                         free(slice24);
-#endif
+//#endif
                         return bImg;
 } //nii_rgb2Planar()
 
@@ -2004,12 +1952,12 @@ unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr,
     //int sliceBytes24 = hdr->dim[1]*hdr->dim[2] * hdr->bitpix/8;
     int sliceBytes8 = hdr->dim[1]*hdr->dim[2];
     int sliceBytes24 = sliceBytes8 * 3;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	unsigned char * slice24 = (unsigned char *)malloc(sizeof(unsigned char) * (sliceBytes24));
-#else
-	unsigned char  slice24[ sliceBytes24 ];
-#endif
-    //printf("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
+//#else
+//	unsigned char  slice24[ sliceBytes24 ];
+//#endif
+    //printMessage("rgb->planar %dx%dx%d\n", hdr->dim[1],hdr->dim[2], dim3to7);
     int sliceOffsetR = 0;
     for (int sl = 0; sl < dim3to7; sl++) { //for each 2D slice
         memcpy(slice24, &bImg[sliceOffsetR], sliceBytes24); //TPX memcpy(&slice24, &bImg[sliceOffsetR], sliceBytes24);
@@ -2025,9 +1973,9 @@ unsigned char * nii_rgb2planar(unsigned char* bImg, struct nifti_1_header *hdr,
         }
         sliceOffsetR += sliceBytes24;
     } //for each slice
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(slice24);
-#endif
+//#endif
     return bImg;
 } //nii_rgb2Planar()
 
@@ -2074,9 +2022,9 @@ unsigned char * nii_XYTZ_XYZT(unsigned char* bImg, struct nifti_1_header *hdr, i
     for (int i = 4; i < 8; i++)
         if (hdr->dim[i] > 1) dim4to7 = dim4to7 * hdr->dim[i];
     if ((hdr->dim[3] < 2) || (dim4to7 < 2)) return bImg;
-    printf("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7);
+    printMessage("Converting XYTZ to XYZT with %d slices (Z) and %d volumes (T).\n",hdr->dim[3], dim4to7);
     if ((dim4to7 % seqRepeats) != 0) {
-        printf("Error: patient position repeats %d times, but this does not evenly divide number of volumes (%d)\n", seqRepeats,dim4to7);
+        printError("Patient position repeats %d times, but this does not evenly divide number of volumes (%d)\n", seqRepeats,dim4to7);
         seqRepeats = 1;
     }
     uint64_t typeRepeats = dim4to7 / seqRepeats;
@@ -2123,20 +2071,20 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
     jas_image_t *image;
     jas_setdbglevel(0);
     if (!(in = jas_stream_fopen(imgname, "rb"))) {
-        printf( "Error: cannot open input image file %s\n", imgname);
+        printError( "Cannot open input image file %s\n", imgname);
         return NULL;
     }
     //int isSeekable = jas_stream_isseekable(in);
     jas_stream_seek(in, dcm.imageStart, 0);
     int infmt = jas_image_getfmt(in);
     if (infmt < 0) {
-        printf( "Error: input image has unknown format %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
+        printError( "Input image has unknown format %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
         return NULL;
     }
     char opt[] = "\0";
     char *inopts = opt;
     if (!(image = jas_image_decode(in, infmt, inopts))) {
-        printf("Error: cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
+        printError("Cannot decode image data %s offset %d bytes %d\n", imgname, dcm.imageStart, dcm.imageBytes);
         return NULL;
     }
     int numcmpts;
@@ -2144,7 +2092,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
     switch (jas_clrspc_fam(jas_image_clrspc(image))) {
         case JAS_CLRSPC_FAM_RGB:
             if (jas_image_clrspc(image) != JAS_CLRSPC_SRGB)
-                printf("Warning: inaccurate color\n");
+                printWarning("Inaccurate color\n");
             numcmpts = 3;
             if ((cmpts[0] = jas_image_getcmptbytype(image,
                                                     JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0 ||
@@ -2152,22 +2100,22 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
                                                     JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0 ||
                 (cmpts[2] = jas_image_getcmptbytype(image,
                                                     JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0) {
-                printf("Error: missing color component\n");
+                printError("Missing color component\n");
                 return NULL;
             }
             break;
         case JAS_CLRSPC_FAM_GRAY:
             if (jas_image_clrspc(image) != JAS_CLRSPC_SGRAY)
-                printf("Warning: inaccurate color\n");
+                printWarning("Inaccurate color\n");
             numcmpts = 1;
             if ((cmpts[0] = jas_image_getcmptbytype(image,
                                                     JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y))) < 0) {
-                printf("Error: missing color component\n");
+                printError("Missing color component\n");
                 return NULL;
             }
             break;
         default:
-            printf("error: unsupported color space\n");
+            printError("Unsupported color space\n");
             return NULL;
             break;
     }
@@ -2176,7 +2124,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
     int prec = jas_image_cmptprec(image, cmpts[0]);
     int sgnd = jas_image_cmptsgnd(image, cmpts[0]);
     #ifdef MY_DEBUG
-    printf("offset %d w*h %d*%d bpp %d sgnd %d components %d '%s' Jasper=%s\n",dcm.imageStart, width, height, prec, sgnd, numcmpts, imgname, jas_getversion());
+    printMessage("offset %d w*h %d*%d bpp %d sgnd %d components %d '%s' Jasper=%s\n",dcm.imageStart, width, height, prec, sgnd, numcmpts, imgname, jas_getversion());
     #endif
     for (int cmptno = 0; cmptno < numcmpts; ++cmptno) {
         if (jas_image_cmptwidth(image, cmpts[cmptno]) != width ||
@@ -2187,7 +2135,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
             jas_image_cmptvstep(image, cmpts[cmptno]) != jas_image_cmptvstep(image, 0) ||
             jas_image_cmpttlx(image, cmpts[cmptno]) != jas_image_cmpttlx(image, 0) ||
             jas_image_cmpttly(image, cmpts[cmptno]) != jas_image_cmpttly(image, 0)) {
-            printf("The NIfTI format cannot be used to represent an image with this geometry.\n");
+            printMessage("The NIfTI format cannot be used to represent an image with this geometry.\n");
             return NULL;
         }
     }
@@ -2195,7 +2143,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
     int bpp = (prec + 7) >> 3; //e.g. 12 bits requires 2 bytes
     int imgbytes = bpp * width * height * numcmpts;
     if ((bpp < 1) || (bpp > 2) || (width < 1) || (height < 1) || (imgbytes < 1)) {
-        printf("Serious catastrophic error decompression error\n");
+        printError("Catastrophic decompression error\n");
         return NULL;
     }
     jas_seqent_t v;
@@ -2204,7 +2152,7 @@ unsigned char * nii_loadImgCoreJasper(char* imgname, struct nifti_1_header hdr,
     int16_t * img16i = (int16_t*) img; //signed 16-bit
     if (sgnd) bpp = -bpp;
     if (bpp == -1) {
-        printf("Error: Signed 8-bit DICOM?\n");
+        printError("Signed 8-bit DICOM?\n");
         return NULL;
     }
     jas_matrix_t *data;
@@ -2263,13 +2211,17 @@ TJPEG *  decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
     fseek(reader, 0, SEEK_END);
     long lRawSz = ftell(reader)- skipBytes;
     if (lRawSz <= 8) {
-        printf("Error opening %s\n", fn);
+        printError("Unable to open %s\n", fn);
         abortGoto(); //read failure
     }
     fseek(reader, skipBytes, SEEK_SET);
     unsigned char *lRawRA = (unsigned char*) malloc(lRawSz);
-    fread(lRawRA, 1, lRawSz, reader);
+    size_t lSz = fread(lRawRA, 1, lRawSz, reader);
     fclose(reader);
+    if (lSz < lRawSz) {
+        printError("Unable to read %s\n", fn);
+        abortGoto(); //read failure
+    }
     long lRawPos = 0; //starting position
     int frame = 0;
     while ((frame < frames) && ((lRawPos+10) < lRawSz)) {
@@ -2278,11 +2230,11 @@ TJPEG *  decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
         int tagLength = dcmInt(4,&lRawRA[lRawPos],isLittleEndian);
         long tagEnd =lRawPos + tagLength + 4;
         if (isVerbose)
-            printf("Tag %#x length %d end at %ld\n", tag, tagLength, tagEnd+skipBytes);
+            printMessage("Tag %#x length %d end at %ld\n", tag, tagLength, tagEnd+skipBytes);
         lRawPos += 4; //read tag length
         if ((lRawRA[lRawPos] != 0xFF) || (lRawRA[lRawPos+1] != 0xD8) || (lRawRA[lRawPos +2] != 0xFF)) {
             if (isVerbose)
-                printf("Warning: JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
+                printWarning("JPEG signature 0xFFD8FF not found at offset %d of %s\n", skipBytes, fn);
         } else {
             lOffsetRA[frame].offset = lRawPos+skipBytes;
             lOffsetRA[frame].size = tagLength;
@@ -2292,7 +2244,7 @@ TJPEG *  decode_JPEG_SOF_0XC3_stack (const char *fn, int skipBytes, bool isVerbo
     }
     free(lRawRA);
     if (frame < frames) {
-        printf("Only found %d of %d JPEG fragments. Please use dcmdjpeg to uncompress data.\n", frame, frames);
+        printMessage("Only found %d of %d JPEG fragments. Please use dcmdjpeg to uncompress data.\n", frame, frames);
         abortGoto();
     }
     return lOffsetRA;
@@ -2302,10 +2254,26 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
     //arcane and inefficient lossless compression method popularized by dcmcjpeg, examples at http://www.osirix-viewer.com/resources/dicom-image-library/
     int dimX, dimY, bits, frames;
     //clock_t start = clock();
+    // https://github.com/rii-mango/JPEGLosslessDecoderJS/blob/master/tests/data/jpeg_lossless_sel1-8bit.dcm
+    //N.B. this current code can not extract a 2D image that is saved as multiple fragments, for example see the JPLL files at
+    // ftp://medical.nema.org/MEDICAL/Dicom/DataSets/WG04/
+    //Live javascript code that can handle these is at
+    // https://github.com/chafey/cornerstoneWADOImageLoader
+    //I have never seen these segmented images in the wild, so we will simply warn the user if we encounter such a file
+    //int Sz = JPEG_SOF_0XC3_sz (imgname, (dcm.imageStart - 4), dcm.isLittleEndian);
+    //printf("Sz %d %d\n", Sz, dcm.imageBytes );
+    //This behavior is legal but appears extremely rare
+    //ftp://medical.nema.org/medical/dicom/final/cp900_ft.pdf
+    if (65536 == dcm.imageBytes)
+        printError("One frame may span multiple fragments. SOFxC3 lossless JPEG. Please extract with dcmdjpeg or gdcmconv.\n");
     unsigned char * ret = decode_JPEG_SOF_0XC3 (imgname, dcm.imageStart, isVerbose, &dimX, &dimY, &bits, &frames, 0);
-    //printf("JPEG %fms\n", ((double)(clock()-start))/1000);
+    if (ret == NULL) {
+    	printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
+        return NULL;
+    }
+    //printMessage("JPEG %fms\n", ((double)(clock()-start))/1000);
     if (hdr.dim[3] != frames) { //multi-slice image saved as multiple image fragments rather than a single image
-        //printf("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]);
+        //printMessage("Unable to decode all slices (%d/%d). Please use dcmdjpeg to uncompress data.\n", frames, hdr.dim[3]);
         if (ret != NULL) free(ret);
         TJPEG * offsetRA = decode_JPEG_SOF_0XC3_stack (imgname, dcm.imageStart-8, isVerbose, hdr.dim[3], dcm.isLittleEndian);
         if (offsetRA == NULL) return NULL;
@@ -2315,10 +2283,10 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
         unsigned char *bImg = (unsigned char *)malloc(imgsz);
         for (int frame = 0; frame < hdr.dim[3]; frame++) {
             if (isVerbose)
-                printf("JPEG frame %d has %ld bytes @ %ld\n", frame, offsetRA[frame].size, offsetRA[frame].offset);
+                printMessage("JPEG frame %d has %ld bytes @ %ld\n", frame, offsetRA[frame].size, offsetRA[frame].offset);
             unsigned char * ret = decode_JPEG_SOF_0XC3 (imgname, (int)offsetRA[frame].offset, false, &dimX, &dimY, &bits, &frames, (int)offsetRA[frame].size);
             if (ret == NULL) {
-                printf("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
+                printMessage("Unable to decode JPEG. Please use dcmdjpeg to uncompress data.\n");
                 free(bImg);
                 return NULL;
             }
@@ -2336,10 +2304,65 @@ unsigned char * nii_loadImgJPEGC3(char* imgname, struct nifti_1_header hdr, stru
 #define F_OK 0 /* existence check */
 #endif
 
+#ifndef myDisableClassicJPEG
+
+#ifdef myTurboJPEG //if turboJPEG instead of nanoJPEG for classic JPEG decompression
+
+unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
+//decode classic JPEG using nanoJPEG
+    //printMessage("50 offset %d\n", dcm.imageStart);
+    if ((dcm.samplesPerPixel != 1) && (dcm.samplesPerPixel != 3)) {
+        printError("%d components (expected 1 or 3) in a JPEG image '%s'\n", dcm.samplesPerPixel, imgname);
+        return NULL;
+    }
+    if( access(imgname, F_OK ) == -1 ) {
+        printError("Unable to find '%s'\n", imgname);
+        return NULL;
+    }
+    //load compressed data
+    FILE *f = fopen(imgname, "rb");
+    fseek(f, 0, SEEK_END);
+    long unsigned int _jpegSize = (long unsigned int) ftell(f);
+    _jpegSize = _jpegSize - dcm.imageStart;
+    if (_jpegSize < 8) {
+        printError("File too small\n");
+        fclose(f);
+        return NULL;
+    }
+    unsigned char* _compressedImage = (unsigned char *)malloc(_jpegSize);
+    fseek(f, dcm.imageStart, SEEK_SET);
+    _jpegSize = (long unsigned int) fread(_compressedImage, 1, _jpegSize, f);
+    fclose(f);
+    int jpegSubsamp, width, height;
+    //printMessage("Decoding with turboJPEG\n");
+	tjhandle _jpegDecompressor = tjInitDecompress();
+	tjDecompressHeader2(_jpegDecompressor, _compressedImage, _jpegSize, &width, &height, &jpegSubsamp);
+	int COLOR_COMPONENTS = dcm.samplesPerPixel;
+	//printMessage("turboJPEG h*w %d*%d sampling %d components %d\n", width, height, jpegSubsamp, COLOR_COMPONENTS);
+	if ((jpegSubsamp == TJSAMP_GRAY) && (COLOR_COMPONENTS != 1)) {
+        printError("Grayscale jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname);
+	}
+	if ((jpegSubsamp != TJSAMP_GRAY) && (COLOR_COMPONENTS != 3)) {
+        printError("Color jpegs should not have %d components '%s'\n", COLOR_COMPONENTS, imgname);
+	}
+	//unsigned char bImg[width*height*COLOR_COMPONENTS]; //!< will contain the decompressed image
+	unsigned char *bImg = (unsigned char *)malloc(width*height*COLOR_COMPONENTS);
+	if (COLOR_COMPONENTS == 1) //TJPF_GRAY
+		tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0/*pitch*/, height, TJPF_GRAY, TJFLAG_FASTDCT);
+	else
+		tjDecompress2(_jpegDecompressor, _compressedImage, _jpegSize, bImg, width, 0/*pitch*/, height, TJPF_RGB, TJFLAG_FASTDCT);
+	//printMessage("turboJPEG h*w %d*%d (sampling %d)\n", width, height, jpegSubsamp);
+	tjDestroy(_jpegDecompressor);
+	return bImg;
+}
+
+#else //if turboJPEG else use nanojpeg...
+
 unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, struct TDICOMdata dcm) {
-    //printf("50 offset %d\n", dcm.imageStart);
+//decode classic JPEG using nanoJPEG
+    //printMessage("50 offset %d\n", dcm.imageStart);
     if( access(imgname, F_OK ) == -1 ) {
-        printf("Error: unable to find '%s'\n", imgname);
+        printError("Unable to find '%s'\n", imgname);
         return NULL;
     }
     //load compressed data
@@ -2348,7 +2371,7 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
     int size = (int) ftell(f);
     size = size - dcm.imageStart;
     if (size < 8) {
-        printf("Error file too small\n");
+        printError("File too small '%s'\n", imgname);
         fclose(f);
         return NULL;
     }
@@ -2359,7 +2382,7 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
     //decode
     njInit();
     if (njDecode(buf, size)) {
-        printf("Error decoding JPEG image.\n");
+        printError("Unable to decode JPEG image.\n");
         return NULL;
     }
     free(buf);
@@ -2368,13 +2391,22 @@ unsigned char * nii_loadImgJPEG50(char* imgname, struct nifti_1_header hdr, stru
     njDone();
     return bImg;
 }
+#endif
+#endif
 
 unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose) {
 //provided with a filename (imgname) and DICOM header (dcm), creates NIfTI header (hdr) and img
-    if (headerDcm2Nii(dcm, hdr) == EXIT_FAILURE) return NULL;
+    if (headerDcm2Nii(dcm, hdr, true) == EXIT_FAILURE) return NULL; //TOFU
     unsigned char * img;
     if (dcm.compressionScheme == kCompress50)  {
-        img = nii_loadImgJPEG50(imgname, *hdr, dcm);
+    	#ifdef myDisableClassicJPEG
+        	printMessage("Software not compiled to decompress classic JPEG DICOM images\n");
+        	return NULL;
+    	#else
+        	img = nii_loadImgJPEG50(imgname, *hdr, dcm);
+    		if (hdr->datatype ==DT_RGB24) //convert to planar
+        		img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped
+        #endif
     } else if (dcm.compressionScheme == kCompressC3) {
             img = nii_loadImgJPEGC3(imgname, *hdr, dcm, (isVerbose > 0));
     } else
@@ -2390,19 +2422,14 @@ unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct
         #endif
     #endif
     if (dcm.compressionScheme == kCompressYes) {
-        printf("Software not set up to decompress DICOM\n");
+        printMessage("Software not set up to decompress DICOM\n");
         return NULL;
     } else
         img = nii_loadImgCore(imgname, *hdr, dcm.bitsAllocated);
     if (img == NULL) return img;
     if (dcm.compressionScheme == kCompressNone) {
-#ifdef __BIG_ENDIAN__
-    if ((dcm.isLittleEndian) && (hdr->bitpix > 8))
-        img = nii_byteswap(img, hdr);
-#else
-    if ((!dcm.isLittleEndian) && (hdr->bitpix > 8))
+    if ((dcm.isLittleEndian != littleEndianPlatform()) && (hdr->bitpix > 8))
         img = nii_byteswap(img, hdr);
-#endif
     }
     if ((dcm.compressionScheme == kCompressNone) && (hdr->datatype ==DT_RGB24)) //img = nii_planar2rgb(img, hdr, dcm.isPlanarRGB); //
         img = nii_rgb2planar(img, hdr, dcm.isPlanarRGB);//do this BEFORE Y-Flip, or RGB order can be flipped
@@ -2422,7 +2449,7 @@ unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct
     }
     if ((hdr->dim[0] > 3) && (dcm.patientPositionSequentialRepeats > 1)) //swizzle 3rd and 4th dimension (Philips stores time as 3rd dimension)
         img = nii_XYTZ_XYZT(img, hdr,dcm.patientPositionSequentialRepeats );
-    headerDcm2NiiSForm(dcm,dcm, hdr);
+    headerDcm2NiiSForm(dcm,dcm, hdr, false);
     return img;
 } //nii_loadImgXL()
 
@@ -2430,15 +2457,16 @@ int isDICOMfile(const char * fname) { //0=NotDICOM, 1=DICOM, 2=Maybe(not Part 10
     FILE *fp = fopen(fname, "rb");
 	if (!fp)  return 0;
 	fseek(fp, 0, SEEK_END);
-	long long fileLen=ftell(fp);
+	long fileLen=ftell(fp);
     if (fileLen < 256) {
         fclose(fp);
         return 0;
     }
 	fseek(fp, 0, SEEK_SET);
 	unsigned char buffer[256];
-	fread(buffer, 256, 1, fp);
+	size_t sz = fread(buffer, 1, 256, fp);
 	fclose(fp);
+	if (sz < 256) return 0;
     if ((buffer[128] == 'D') && (buffer[129] == 'I')  && (buffer[130] == 'C') && (buffer[131] == 'M'))
     	return 1; //valid DICOM
     if ((buffer[0] == 8) && (buffer[1] == 0)  && (buffer[3] == 0))
@@ -2458,7 +2486,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
     struct stat s;
     if( stat(fname,&s) == 0 ) {
         if( !(s.st_mode & S_IFREG) ){
-            printf( "DICOM read fail: not a valid file (perhaps a directory) %s\n",fname);
+            printMessage( "DICOM read fail: not a valid file (perhaps a directory) %s\n",fname);
             return d;
         }
     }
@@ -2472,34 +2500,30 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 
     FILE *file = fopen(fname, "rb");
 	if (!file) {
-#ifdef myUseCOut
-     	std::cout<<"Unable to open file "<< fname <<std::endl;
-#else
-        printf( "Unable to open file %s\n", fname);
-#endif
+        printMessage("Unable to open file %s\n", fname);
 		return d;
 	}
 	fseek(file, 0, SEEK_END);
-	long long fileLen=ftell(file); //Get file length
+	long fileLen=ftell(file); //Get file length
     if (fileLen < 256) {
-#ifdef myUseCOut
-     	std::cout<<"File too small to be a DICOM image "<< fname <<std::endl;
-#else
-        printf( "File too small to be a DICOM image %s\n", fname);
-#endif
+        printMessage( "File too small to be a DICOM image %s\n", fname);
 		return d;
 	}
 	fseek(file, 0, SEEK_SET);
 	//Allocate memory
 	unsigned char *buffer=(unsigned char *)malloc(fileLen+1);
 	if (!buffer) {
-		printf( "Memory error!");
+		printError( "Memory exhausted!");
         fclose(file);
 		return d;
 	}
 	//Read file contents into buffer
-	fread(buffer, fileLen, 1, file);
+	size_t sz = fread(buffer, 1, fileLen, file);
 	fclose(file);
+	if (sz < fileLen) {
+         printError("Only loaded %zu of %ld bytes for %s\n", sz, fileLen, fname);
+         return d;
+    }
 	//bool isPart10prefix = true; //assume 132 byte header http://nipy.bic.berkeley.edu/nightly/nibabel/doc/dicom/dicom_intro.html
     //if ((buffer[128] != 'D') || (buffer[129] != 'I')  || (buffer[130] != 'C') || (buffer[131] != 'M')) {
     //    if ((buffer[0] != 8) || (buffer[1] != 0)  || (buffer[2] != 5) || (buffer[3] != 0)){
@@ -2508,7 +2532,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
     //	}
     //	isPart10prefix = false; //no 132 byte header, not a valid part 10 file http://fileformats.archiveteam.org/wiki/DICOM
     //	d.isExplicitVR = false;
-    //	//printf("Warning: not a valid part 10 DICOM (missing 'DICM' signature): %s\n", fname);
+    //	//printWarning("Not a valid part 10 DICOM (missing 'DICM' signature): %s\n", fname);
     //}
     //DEFINE DICOM TAGS
 #define  kUnused 0x0001+(0x0001 << 16 )
@@ -2518,6 +2542,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 #define  kImageTypeTag 0x0008+(0x0008 << 16 )
 #define  kStudyDate 0x0008+(0x0020 << 16 )
 #define  kAcquisitionDate 0x0008+(0x0022 << 16 )
+#define  kAcquisitionDateTime 0x0008+(0x002A << 16 )
 #define  kStudyTime 0x0008+(0x0030 << 16 )
 #define  kAcquisitionTime 0x0008+(0x0032 << 16 )
 #define  kManufacturer 0x0008+(0x0070 << 16 )
@@ -2527,17 +2552,23 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 #define  kComplexImageComponent (uint32_t) 0x0008+(0x9208 << 16 )//'0008' '9208' 'CS' 'ComplexImageComponent'
 #define  kPatientName 0x0010+(0x0010 << 16 )
 #define  kPatientID 0x0010+(0x0020 << 16 )
+#define  kBodyPartExamined 0x0018+(0x0015 << 16)
 #define  kScanningSequence 0x0018+(0x0020 << 16)
+#define  kSequenceVariant 0x0018+(0x0021 << 16)
 #define  kMRAcquisitionType 0x0018+(0x0023 << 16)
 #define  kSequenceName 0x0018+(0x0024 << 16)
 #define  kZThick  0x0018+(0x0050 << 16 )
 #define  kTR  0x0018+(0x0080 << 16 )
 #define  kTE  0x0018+(0x0081 << 16 )
+#define  kTI  0x0018+(0x0082 << 16) // Inversion time
 #define  kEchoNum  0x0018+(0x0086 << 16 ) //IS
 #define  kMagneticFieldStrength  0x0018+(0x0087 << 16 ) //DS
 #define  kZSpacing  0x0018+(0x0088 << 16 ) //'DS' 'SpacingBetweenSlices'
 #define  kPhaseEncodingSteps  0x0018+(0x0089 << 16 ) //'IS'
 #define  kProtocolName  0x0018+(0x1030<< 16 )
+#define  kRadionuclideTotalDose  0x0018+(0x1074<< 16 )
+#define  kRadionuclideHalfLife  0x0018+(0x1075<< 16 )
+#define  kRadionuclidePositronFraction  0x0018+(0x1076<< 16 )
 #define  kGantryTilt  0x0018+(0x1120  << 16 )
 #define  kXRayExposure  0x0018+(0x1152  << 16 )
 #define  kFlipAngle  0x0018+(0x1314  << 16 )
@@ -2548,6 +2579,8 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 #define  kDiffusionDirectionGEX  0x0019+(0x10BB<< 16 ) //DS
 #define  kDiffusionDirectionGEY  0x0019+(0x10BC<< 16 ) //DS
 #define  kDiffusionDirectionGEZ  0x0019+(0x10BD<< 16 ) //DS
+#define  kStudyInstanceUID 0x0020+(0x000D << 16 )
+#define  kSeriesInstanceUID 0x0020+(0x000E << 16 )
 #define  kPatientPosition 0x0020+(0x0032 << 16 )
 #define  kSeriesNum 0x0020+(0x0011 << 16 )
 #define  kAcquNum 0x0020+(0x0012 << 16 )
@@ -2571,15 +2604,20 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 #define  kGeiisFlag 0x0029+(0x0010 << 16 ) //warn user if dreaded GEIIS was used to process image
 #define  kCSAImageHeaderInfo  0x0029+(0x1010 << 16 )
     //#define  kObjectGraphics  0x0029+(0x1210 << 16 )    //0029,1210 syngoPlatformOOGInfo Object Oriented Graphics
+#define  kProcedureStepDescription 0x0040+(0x0254 << 16 )
 #define  kRealWorldIntercept  0x0040+uint32_t(0x9224 << 16 ) //IS dicm2nii's SlopInt_6_9
 #define  kRealWorldSlope  0x0040+uint32_t(0x9225 << 16 ) //IS dicm2nii's SlopInt_6_9
 #define  kDiffusionBFactorGE  0x0043+(0x1039 << 16 ) //IS dicm2nii's SlopInt_6_9
 #define  kCoilSiemens  0x0051+(0x100F << 16 )
 #define  kLocationsInAcquisition  0x0054+(0x0081 << 16 )
+#define  kDoseCalibrationFactor  0x0054+(0x1322<< 16 )
 #define  kIconImageSequence 0x0088+(0x0200 << 16 )
 #define  kDiffusionBFactor  0x2001+(0x1003 << 16 )// FL
+#define  kSliceNumberMrPhilips 0x2001+(0x100A << 16 ) //IS Slice_Number_MR
+#define  kNumberOfSlicesMrPhilips 0x2001+(0x1018 << 16 )//SL 0x2001, 0x1018 ), "Number_of_Slices_MR"
 #define  kSliceOrient  0x2001+(0x100B << 16 )//2001,100B Philips slice orientation (TRANSVERSAL, AXIAL, SAGITTAL)
-#define  kLocationsInAcquisitionPhilips  0x2001+(0x1018 << 16 )
+//#define  kLocationsInAcquisitionPhilips  0x2001+(0x1018 << 16 ) //
+//#define  kStackSliceNumber  0x2001+(0x1035 << 16 )//? Potential way to determine slice order for Philips?
 #define  kNumberOfDynamicScans  0x2001+(0x1081 << 16 )//'2001' '1081' 'IS' 'NumberOfDynamicScans'
 #define  kMRAcquisitionTypePhilips 0x2005+(0x106F << 16)
 #define  kAngulationAP 0x2005+(0x1071 << 16)//'2005' '1071' 'FL' 'MRStackAngulationAP'
@@ -2613,14 +2651,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
     	lPos = 128+4; //4-byte signature starts at 128
     	groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
     	if (groupElement != kStart)
-    	#ifdef myUseCOut
-            std::cout<<"DICOM appears corrupt: first group:element should be 0x0002:0x0000" <<std::endl;
-    	#else
-        	printf("DICOM appears corrupt: first group:element should be 0x0002:0x0000\n");
-    	#endif
+        	printMessage("DICOM appears corrupt: first group:element should be 0x0002:0x0000 '%s'\n",  fname);
     }
     char vr[2];
-    float intenScalePhilips = 0.0;
+    //float intenScalePhilips = 0.0;
+    char acquisitionDateTimeTxt[kDICOMStr] = "";
     bool isEncapsulatedData = false;
     bool isOrient = false;
     bool isIconImageSequence = false;
@@ -2632,9 +2667,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
     bool isAtFirstPatientPosition = false; //for 3d and 4d files: flag is true for slices at same position as first slice
     bool isMosaic = false;
     int phaseEncodingSteps = 0;
-    int patientPositionCount = 0;
+    int patientPositionNum = 0;
     int sqDepth = 0;
-    //long coilNum = 0; //Siemens can save one image per coil (H12,H13,etc) or one combined image for array (HEA;HEP)
+    float patientPosition[4] = {NAN, NAN, NAN, NAN}; //used to compute slice direction for Philips 4D
+    float patientPositionEndPhilips[4] = {NAN, NAN, NAN, NAN};
+    float patientPositionStartPhilips[4] = {NAN, NAN, NAN, NAN};
     while ((d.imageStart == 0) && ((lPos+8) <  fileLen)) {
         if (d.isLittleEndian)
             groupElement = buffer[lPos] | (buffer[lPos+1] << 8) | (buffer[lPos+2] << 16) | (buffer[lPos+3] << 24);
@@ -2656,7 +2693,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             vr[0] = 'N';
             vr[1] = 'A';
             if (groupElement == kUnnest2) sqDepth--;
-            //if (groupElement == kUnnest2) printf("SQend %d\n", sqDepth);
+            //if (groupElement == kUnnest2) printMessage("SQend %d\n", sqDepth);
 
             //if (groupElement == kUnnest) geiisBug = false; //don't exit if there is a proprietary thumbnail
             lLength = 4;
@@ -2706,36 +2743,36 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 
         if   ((vr[0] == 'S') && (vr[1] == 'Q')) {
             sqDepth++;
-            //printf("SQstart %d\n", sqDepth);
+            //printMessage("SQstart %d\n", sqDepth);
         }
         if ((groupElement == kNest) || ((vr[0] == 'S') && (vr[1] == 'Q'))) nest++;
         if (groupElement == kUnnest) nest--;
         //next: look for required tags
         if ((groupElement == kNest)  && (isEncapsulatedData)) {
             d.imageBytes = dcmInt(4,&buffer[lPos-4],d.isLittleEndian);
-            //printf("compressed data %d-> %ld\n",d.imageBytes, lPos);
-            if (d.imageBytes > 12) {
+            //printMessage("compressed data %d-> %ld\n",d.imageBytes, lPos);
+            if (d.imageBytes > 128) {
                 d.imageStart = (int)lPos;
             }
         }
         if ((isIconImageSequence) && ((groupElement & 0x0028) == 0x0028 )) groupElement = kUnused; //ignore icon dimensions
         if (true) { //(nest <= 0) { //some Philips images have different 0020,0013
         //verbose reporting :
-        // printf("Pos %ld GroupElement %#08x,%#08x Length %d isLittle %d\n", lPos, (groupElement & 0xFFFF), (groupElement >> 16), lLength, d.isLittleEndian);
+        // printMessage("Pos %ld GroupElement %#08x,%#08x Length %d isLittle %d\n", lPos, (groupElement & 0xFFFF), (groupElement >> 16), lLength, d.isLittleEndian);
         switch ( groupElement ) {
             case 	kTransferSyntax: {
                 char transferSyntax[kDICOMStr];
                 dcmStr (lLength, &buffer[lPos], transferSyntax);
-                //printf("transfer syntax '%s'\n", transferSyntax);
+                //printMessage("transfer syntax '%s'\n", transferSyntax);
                 if (strcmp(transferSyntax, "1.2.840.10008.1.2.1") == 0)
                     ; //default isExplicitVR=true; //d.isLittleEndian=true
                 else if  (strcmp(transferSyntax, "1.2.840.10008.1.2.4.50") == 0) {
                     d.compressionScheme = kCompress50;
-                    //printf("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
+                    //printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
                     //d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
                 } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.51") == 0) {
                         d.compressionScheme = kCompress50;
-                        //printf("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
+                        //printMessage("Lossy JPEG: please decompress with Osirix or dcmdjpg. %s\n", transferSyntax);
                         //d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
                 //uJPEG does not decode these: ..53 ...55
                 // } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.53") == 0) {
@@ -2744,18 +2781,18 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                     //d.isCompressed = true;
                     //https://www.medicalconnections.co.uk/kb/Transfer_Syntax should be SOF = 0xC3
                     d.compressionScheme = kCompressC3;
-                    //printf("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
+                    //printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
                 } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) {
                     //d.isCompressed = true;
                     d.compressionScheme = kCompressC3;
-                    //printf("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
+                    //printMessage("Ancient JPEG-lossless (SOF type 0xc3): please check conversion\n");
                     //d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
                 } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.90") == 0)) {
                     d.compressionScheme = kCompressYes;
-                    //printf("JPEG2000 Lossless support is new: please validate conversion\n");
+                    //printMessage("JPEG2000 Lossless support is new: please validate conversion\n");
                 } else if ((compressFlag != kCompressNone) && (strcmp(transferSyntax, "1.2.840.10008.1.2.4.91") == 0)) {
                     d.compressionScheme = kCompressYes;
-                    //printf("JPEG2000 support is new: please validate conversion\n");
+                    //printMessage("JPEG2000 support is new: please validate conversion\n");
                 } else if (strcmp(transferSyntax, "1.2.840.10008.1.2.2") == 0)
                     isSwitchToBigEndian = true; //isExplicitVR=true;
                 //else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.91") == 0)
@@ -2763,11 +2800,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 else if (strcmp(transferSyntax, "1.2.840.10008.1.2") == 0)
                     isSwitchToImplicitVR = true; //d.isLittleEndian=true
                 else {
-#ifdef myUseCOut
-                    std::cout<<"Unsupported transfer syntax "<< transferSyntax<<std::endl;
-#else
-                    printf("Unsupported transfer syntax '%s' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n",transferSyntax);
-#endif
+                    printMessage("Unsupported transfer syntax '%s' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)\n",transferSyntax);
                     d.imageStart = 1;//abort as invalid (imageStart MUST be >128)
                 }
                 break;} //{} provide scope for variable 'transferSyntax
@@ -2790,6 +2823,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 dcmStr (lLength, &buffer[lPos], acquisitionDateTxt);
                 d.acquisitionDate = atof(acquisitionDateTxt);
             	break;
+            case kAcquisitionDateTime:
+            	//char acquisitionDateTimeTxt[kDICOMStr];
+                dcmStr (lLength, &buffer[lPos], acquisitionDateTimeTxt);
+                //printMessage("%s\n",acquisitionDateTimeTxt);
+            	break;
             case kStudyDate:
                 dcmStr (lLength, &buffer[lPos], d.studyDate);
                 break;
@@ -2804,6 +2842,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 char acquisitionTimeTxt[kDICOMStr];
                 dcmStr (lLength, &buffer[lPos], acquisitionTimeTxt);
                 d.acquisitionTime = atof(acquisitionTimeTxt);
+                //printMessage("%s\n",acquisitionTimeTxt);
                 break;
             case 	kStudyTime :
                 dcmStr (lLength, &buffer[lPos], d.studyTime);
@@ -2815,7 +2854,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 dcmStr (lLength, &buffer[lPos], d.patientID);
                 break;
             case kSeriesDescription: {
-                //if (strlen(d.protocolName) < 1)
                 dcmStr (lLength, &buffer[lPos], d.seriesDescription);
                 break; }
             case kManufacturersModelName :
@@ -2840,7 +2878,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 break;
                 /*case kDiffusionBFactorSiemens :
                  if (d.manufacturer == kMANUFACTURER_SIEMENS)
-                 printf(">>>>%f\n,",dcmStrFloat(lLength, &buffer[lPos]));
+                 printMessage("last scan location %f\n,",dcmStrFloat(lLength, &buffer[lPos]));
 
                  break;*/
             case kDiffusionDirectionGEX :
@@ -2855,31 +2893,41 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                     d.CSA.numDti = 1;
                 }
                 break;
+            case kStudyInstanceUID :
+                dcmStr (lLength, &buffer[lPos], d.studyInstanceUID);
+                break;
+
+            case kSeriesInstanceUID :
+            	dcmStr (lLength, &buffer[lPos], d.seriesInstanceUID);
+                break;
             case 	kPatientPosition :
                 if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (is2005140FSQ)) {
-#ifdef myUseCOut
-                    if (!is2005140FSQwarned)
-                        std::cout<<"Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch." <<std::endl;
-#else
                     if (!is2005140FSQwarned)
-                        printf("Warning: Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n");
-#endif
+                        printWarning("Philips R3.2.2 can report different positions for the same slice. Attempting patch.\n");
                     is2005140FSQwarned = true;
                 } else {
-                    patientPositionCount++;
+                    patientPositionNum++;
                     isAtFirstPatientPosition = true;
-                    if (isnan(d.patientPosition[1]))
-                        dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position
-                    else {
-                        dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D
+                    dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &patientPosition[0]); //slice position
+                    if (isnan(d.patientPosition[1])) {
+                        //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPosition[0]); //slice position
+						for (int k = 0; k < 4; k++)
+							d.patientPosition[k] = patientPosition[k];
+                    } else {
+                        //dcmMultiFloat(lLength, (char*)&buffer[lPos], 3, &d.patientPositionLast[0]); //slice direction for 4D
+                        for (int k = 0; k < 4; k++)
+							d.patientPositionLast[k] = patientPosition[k];
                         if ((isFloatDiff(d.patientPositionLast[1],d.patientPosition[1]))  ||
                             (isFloatDiff(d.patientPositionLast[2],d.patientPosition[2]))  ||
                             (isFloatDiff(d.patientPositionLast[3],d.patientPosition[3])) ) {
                             isAtFirstPatientPosition = false; //this slice is not at position of 1st slice
                             if (d.patientPositionSequentialRepeats == 0) //this is the first slice with different position
-                                d.patientPositionSequentialRepeats = patientPositionCount-1;
+                                d.patientPositionSequentialRepeats = patientPositionNum-1;
                         } //if different position from 1st slice in file
                     } //if not first slice in file
+                    if (isVerbose > 1)
+                    	printMessage("   Patient Position 0020,0032 (#,@,X,Y,Z)\t%d\t%ld\t%g\t%g\t%g\n", patientPositionNum, lPos, patientPosition[1], patientPosition[2], patientPosition[3]);
+
                 } //not after 2005,140F
                 break;
             case 	kInPlanePhaseEncodingDirection:
@@ -2920,6 +2968,9 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case 	kLocationsInAcquisitionGE:
                 locationsInAcquisitionGE = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
                 break;
+            case kDoseCalibrationFactor :
+                d.doseCalibrationFactor = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
             case 	kBitsAllocated :
                 d.bitsAllocated = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
                 break;
@@ -2935,6 +2986,9 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case 	kTE :
             	d.TE = dcmStrFloat(lLength, &buffer[lPos]);
                 break;
+            case 	kTI :
+                d.TI = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
             case kEchoNum :
                 d.echoNum =  dcmStrInt(lLength, &buffer[lPos]);
                 break;
@@ -2950,19 +3004,30 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case kFlipAngle :
             	d.flipAngle = dcmStrFloat(lLength, &buffer[lPos]);
             	break;
+            case kRadionuclideTotalDose :
+                d.radionuclideTotalDose = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kRadionuclideHalfLife :
+                d.radionuclideHalfLife = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
+            case kRadionuclidePositronFraction :
+                d.radionuclidePositronFraction = dcmStrFloat(lLength, &buffer[lPos]);
+                break;
             case kGantryTilt :
                 d.gantryTilt = dcmStrFloat(lLength, &buffer[lPos]);
                 break;
             case kXRayExposure : //CTs do not have echo times, we use this field to detect different exposures: https://github.com/neurolabusc/dcm2niix/pull/48
-            	if (d.TE == 0) // for CT we will use exposure (0018,1152) whereas for MR we use echo time (0018,0081)
-                	d.TE = dcmStrFloat(lLength, &buffer[lPos]);
+            	if (d.TE == 0) {// for CT we will use exposure (0018,1152) whereas for MR we use echo time (0018,0081)
+                	d.isXRay = true;
+            		d.TE = dcmStrFloat(lLength, &buffer[lPos]);
+                }
             	break;
             case 	kSlope :
                 d.intenScale = dcmStrFloat(lLength, &buffer[lPos]);
                 break;
             case kPhilipsSlope :
                 if ((lLength == 4) && (d.manufacturer == kMANUFACTURER_PHILIPS))
-                    intenScalePhilips = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
+                    d.intenScalePhilips = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
                 break;
 
             case 	kIntercept :
@@ -2987,12 +3052,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case 	kLocationsInAcquisition :
                 d.locationsInAcquisition = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
                 break;
-            case 	kLocationsInAcquisitionPhilips:
-                locationsInAcquisitionPhilips = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
-                break;
             case kIconImageSequence:
                 isIconImageSequence = true;
                 break;
+            /*case kStackSliceNumber: { //https://github.com/Kevin-Mattheus-Moerman/GIBBON/blob/master/dicomDict/PMS-R32-dict.txt
+            	int stackSliceNumber = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+            	printf("%d\n",stackSliceNumber);
+            	break;
+			}*/
             case 	kNumberOfDynamicScans:
                 d.numberOfDynamicScans =  dcmStrInt(lLength, &buffer[lPos]);
                 break;
@@ -3001,10 +3068,18 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 if (lLength > 1) d.is3DAcq = (buffer[lPos]=='3') && (toupper(buffer[lPos+1]) == 'D');
                 #endif
                 break;
+            case kBodyPartExamined : {
+                dcmStr (lLength, &buffer[lPos], d.bodyPartExamined);
+                break;
+            }
             case kScanningSequence : {
                 dcmStr (lLength, &buffer[lPos], d.scanningSequence);
                 break;
             }
+            case kSequenceVariant : {
+                dcmStr (lLength, &buffer[lPos], d.sequenceVariant);
+                break;
+            }
             case kSequenceName : {
                 //if (strlen(d.protocolName) < 1) //precedence given to kProtocolName and kProtocolNameGE
                 dcmStr (lLength, &buffer[lPos], d.sequenceName);
@@ -3052,7 +3127,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                         dti4D->S[0].V[2] = d.CSA.dtiV[2];
                         dti4D->S[0].V[3] = d.CSA.dtiV[3];
                     }
-
                     d.CSA.dtiV[0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
                     if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D))
                         dti4D->S[d.CSA.numDti-1].V[0] = d.CSA.dtiV[0];
@@ -3060,6 +3134,44 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                        d.CSA.dtiV[d.CSA.numDti-1][0] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
                 }
                 break;
+            case kSliceNumberMrPhilips :
+            	if (d.manufacturer != kMANUFACTURER_PHILIPS)
+            		break;
+            	/*if ((d.patientPositionNumPhilips >= kMaxDTI4D) || (d.patientPositionNumPhilips >= locationsInAcquisitionPhilips)) {
+            		d.patientPositionNumPhilips++;
+            		break;
+            	}*/
+				if ((locationsInAcquisitionPhilips > 0) && (d.patientPositionNumPhilips == locationsInAcquisitionPhilips))
+					break; //we have acquired all slices in volume (e.g. all volumes after 1st for XYZT storage
+				int sliceNumber;
+            	sliceNumber = dcmStrInt(lLength, &buffer[lPos]);
+				if ((d.patientPositionNumPhilips > 0) && (sliceNumber == dti4D->S[d.patientPositionNumPhilips-1].sliceNumberMrPhilips)  )
+					break; //repeated spatial position (e.g. data saved XYTZ so several time points
+				if (d.patientPositionNumPhilips >= kMaxDTI4D) {
+            		d.patientPositionNumPhilips++; //fail: out of space
+            		break;
+            	}
+				dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips = sliceNumber;
+				if ((d.patientPositionNumPhilips > 0) && (abs(dti4D->S[d.patientPositionNumPhilips].sliceNumberMrPhilips - dti4D->S[d.patientPositionNumPhilips -1].sliceNumberMrPhilips) > 1) )
+					d.isSlicesSpatiallySequentialPhilips = false; //slices not sequential (1,2,3,4 or 4,3,2,1) but 4,3,1,2
+            	d.patientPositionNumPhilips++;
+            	//Philips can save 3D acquisitions in a single file with slices stored in non-sequential order. We need to know the first and final spatial position
+            	if (sliceNumber == 1) {
+            		for (int k = 0; k < 4; k++)
+						patientPositionStartPhilips[k] = patientPosition[k];
+            	}
+            	if (sliceNumber == locationsInAcquisitionPhilips) {
+            		for (int k = 0; k < 4; k++)
+						patientPositionEndPhilips[k] = patientPosition[k];
+            	}
+				if (isVerbose > 1)
+					printMessage("slice %d is spatial position %d\n", d.patientPositionNumPhilips, sliceNumber);
+            	break;
+            case kNumberOfSlicesMrPhilips :
+            	if (d.manufacturer != kMANUFACTURER_PHILIPS)
+            		break;
+                locationsInAcquisitionPhilips = dcmInt(lLength,&buffer[lPos],d.isLittleEndian);
+				break;
             case    kDiffusionDirectionRL:
                 if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition)) {
                     d.CSA.dtiV[1] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
@@ -3083,7 +3195,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                     d.CSA.dtiV[3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);
                     if ((d.CSA.numDti > 1) && (d.CSA.numDti < kMaxDTI4D))
                         dti4D->S[d.CSA.numDti-1].V[3] = d.CSA.dtiV[3];
-                    //printf("dti XYZ %g %g %g\n",d.CSA.dtiV[1],d.CSA.dtiV[2],d.CSA.dtiV[3]);
+                    //printMessage("dti XYZ %g %g %g\n",d.CSA.dtiV[1],d.CSA.dtiV[2],d.CSA.dtiV[3]);
                 }
                 /*if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (isAtFirstPatientPosition) && (d.CSA.numDti > 0) && (d.CSA.numDti <= kMaxDTIv))
                     d.CSA.dtiV[d.CSA.numDti-1][3] = dcmFloat(lLength, &buffer[lPos],d.isLittleEndian);*/
@@ -3091,18 +3203,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 break;
             case 	kWaveformSq:
                 d.imageStart = 1; //abort!!!
-#ifdef myUseCOut
-                std::cout<<"Skipping DICOM (audio not image) " <<std::endl;
-#else
-                printf("Skipping DICOM (audio not image) '%s'\n", fname);
-#endif
+                printMessage("Skipping DICOM (audio not image) '%s'\n", fname);
                 break;
             case 	kCSAImageHeaderInfo:
                 readCSAImageHeader(&buffer[lPos], lLength, &d.CSA, isVerbose, dti4D);
                 d.isHasPhase = d.CSA.isPhaseMap;
                 break;
                 //case kObjectGraphics:
-                //    printf("---->%d,",lLength);
+                //    printMessage("---->%d,",lLength);
                 //    break;
             case 	kRealWorldIntercept:
                 if (isSameFloat(0.0, d.intenIntercept)) //give precedence to standard value
@@ -3118,16 +3226,14 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case kGeiisFlag:
                 if ((lLength > 4) && (buffer[lPos]=='G') && (buffer[lPos+1]=='E') && (buffer[lPos+2]=='I')  && (buffer[lPos+3]=='I')) {
                     //read a few digits, as bug is specific to GEIIS, while GEMS are fine
-#ifdef myUseCOut
-                    std::cout<<"Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor." <<std::endl;
-#else
-                    printf("Warning: GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n");
-#endif
+                    printWarning("GEIIS violates the DICOM standard. Inspect results and admonish your vendor.\n");
                     isIconImageSequence = true;
                     //geiisBug = true; //compressed thumbnails do not follow transfer syntax! GE should not re-use pulbic tags for these proprietary images http://sonca.kasshin.net/gdcm/Doc/GE_ImageThumbnails
-
                 }
                 break;
+            case kProcedureStepDescription:
+                dcmStr (lLength, &buffer[lPos], d.procedureStepDescription);
+                break;
             case 	kOrientationACR : //use in emergency if kOrientation is not present!
                 if (!isOrient) dcmMultiFloat(lLength, (char*)&buffer[lPos], 6, d.orient);
                 break;
@@ -3138,7 +3244,6 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
             case kImagesInAcquisition :
                 imagesInAcquisition =  dcmStrInt(lLength, &buffer[lPos]);
                 break;
-
             case 	kImageStart:
                 //if ((!geiisBug) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
                 if ((d.compressionScheme == kCompressNone ) && (!isIconImageSequence)) //do not exit for proprietary thumbnails
@@ -3146,12 +3251,11 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 //geiisBug = false;
                 //http://www.dclunie.com/medical-image-faq/html/part6.html
                 //unlike raw data, Encapsulated data is stored as Fragments contained in Items that are the Value field of Pixel Data
-                if (d.compressionScheme != kCompressNone) {
+                if ((d.compressionScheme != kCompressNone) && (!isIconImageSequence)) {
                     lLength = 0;
                     isEncapsulatedData = true;
                 }
-
-                isIconImageSequence = false;
+				isIconImageSequence = false;
                 break;
             case 	kImageStartFloat:
                 d.isFloat = true;
@@ -3160,7 +3264,7 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
                 isIconImageSequence = false;
                 break;
             case 	kImageStartDouble:
-                printf("WARNING: double-precision DICOM conversion untested: please provide samples to developer\n");
+                printWarning("Double-precision DICOM conversion untested: please provide samples to developer\n");
                 d.isFloat = true;
                 if (!isIconImageSequence) //do not exit for proprietary thumbnails
                     d.imageStart = (int)lPos;
@@ -3170,15 +3274,46 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
         } //switch/case for groupElement
         } //if nest
         //#ifdef MY_DEBUG
-        if (isVerbose > 1)
-            printf(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\n",   groupElement & 65535,groupElement>>16, lLength, lPos, nest);
-            //printf(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n",   groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest);
+        if (isVerbose > 1) {
+        	if ((lLength > 12) && (lLength < 128)) { //if length is greater than 8 bytes (+4 hdr) the data must be a string [or image data]
+        		char tagStr[kDICOMStr];
+            	tagStr[0] = 'X'; //avoid compiler warning: orientStr filled by dcmStr
+                dcmStr (lLength, &buffer[lPos], tagStr);
+                if (strlen(tagStr) > 1) {
+                	for (int pos = 0; pos<strlen(tagStr); pos ++)
+						if ((tagStr[pos] == '<') || (tagStr[pos] == '>') || (tagStr[pos] == ':')
+            				|| (tagStr[pos] == '"') || (tagStr[pos] == '\\') || (tagStr[pos] == '/')
+           					|| (tagStr[pos] == '^') || (tagStr[pos] < 33)
+           					|| (tagStr[pos] == '*') || (tagStr[pos] == '|') || (tagStr[pos] == '?'))
+            					tagStr[pos] = 'x';
+				}
+            	printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\t%s\n",   groupElement & 65535,groupElement>>16, lLength, lPos, tagStr);
+            	//printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\t%s\n",   groupElement & 65535,groupElement>>16, lLength, lPos, nest, tagStr);
+            } else
+            	printMessage(" Tag\t%04x,%04x\tSize=%u\tOffset=%ld\tnest=%d\n",   groupElement & 65535,groupElement>>16, lLength, lPos, nest);
+        }   //printMessage(" tag=%04x,%04x length=%u pos=%ld %c%c nest=%d\n",   groupElement & 65535,groupElement>>16, lLength, lPos,vr[0], vr[1], nest);
         //#endif
         lPos = lPos + (lLength);
-    }
+        //printMessage("%d\n",d.imageStart);
+    } //while d.imageStart == 0
     free (buffer);
+    //Recent Philips images include DateTime (0008,002A) but not separate date and time (0008,0022 and 0008,0032)
+    #define kYYYYMMDDlen 8 //how many characters to encode year,month,day in "YYYYDDMM" format
+    if ((strlen(acquisitionDateTimeTxt) > (kYYYYMMDDlen+5)) && (!isFloatDiff(d.acquisitionTime, 0.0f)) && (!isFloatDiff(d.acquisitionDate, 0.0f)) ) {
+		// 20161117131643.80000 -> date 20161117 time 131643.80000
+		//printMessage("acquisitionDateTime %s\n",acquisitionDateTimeTxt);
+    	char acquisitionDateTxt[kDICOMStr];
+        strncpy(acquisitionDateTxt, acquisitionDateTimeTxt, kYYYYMMDDlen);
+		acquisitionDateTxt[kYYYYMMDDlen] = '\0'; // IMPORTANT!
+        d.acquisitionDate = atof(acquisitionDateTxt);
+        char acquisitionTimeTxt[kDICOMStr];
+		int timeLen = (int)strlen(acquisitionDateTimeTxt) - kYYYYMMDDlen;
+        strncpy(acquisitionTimeTxt, &acquisitionDateTimeTxt[kYYYYMMDDlen], timeLen);
+		acquisitionTimeTxt[timeLen] = '\0'; // IMPORTANT!
+		d.acquisitionTime = atof(acquisitionTimeTxt);
+    }
     d.dateTime = (atof(d.studyDate)* 1000000) + atof(d.studyTime);
-    //printf("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips);
+    //printMessage("slices in Acq %d %d\n",d.locationsInAcquisition,locationsInAcquisitionPhilips);
     if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (d.locationsInAcquisition == 0))
         d.locationsInAcquisition = locationsInAcquisitionPhilips;
     if ((d.manufacturer == kMANUFACTURER_GE) && (imagesInAcquisition > 0))
@@ -3187,27 +3322,27 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
         d.locationsInAcquisition = locationsInAcquisitionGE;
     if (zSpacing > 0)
     	d.xyzMM[3] = zSpacing; //use zSpacing if provided: depending on vendor, kZThick may or may not include a slice gap
-    //printf("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionCount,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans);
-    if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionCount > d.xyzDim[3]))
-        printf("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n",patientPositionCount, d.xyzDim[3]); //Philips reported different positions for each slice!
+    //printMessage("patientPositions = %d XYZT = %d slicePerVol = %d numberOfDynamicScans %d\n",patientPositionNum,d.xyzDim[3], d.locationsInAcquisition, d.numberOfDynamicScans);
+    if ((d.manufacturer == kMANUFACTURER_PHILIPS) && (patientPositionNum > d.xyzDim[3]))
+        printMessage("Please check slice thicknesses: Philips R3.2.2 bug can disrupt estimation (%d positions reported for %d slices)\n",patientPositionNum, d.xyzDim[3]); //Philips reported different positions for each slice!
     if ((d.imageStart > 144) && (d.xyzDim[1] > 1) && (d.xyzDim[2] > 1))
     	d.isValid = true;
     if ((d.xyzMM[1] > FLT_EPSILON) && (d.xyzMM[2] < FLT_EPSILON)) {
-    	printf("Please check voxel size\n");
+    	printMessage("Please check voxel size\n");
         d.xyzMM[2] = d.xyzMM[1];
     }
     if ((d.xyzMM[2] > FLT_EPSILON) && (d.xyzMM[1] < FLT_EPSILON)) {
-        printf("Please check voxel size\n");
+        printMessage("Please check voxel size\n");
         d.xyzMM[1] = d.xyzMM[2];
     }
 
     if ((d.xyzMM[3] < FLT_EPSILON)) {
-        printf("Unable to determine slice thickness: please check voxel size\n");
+        printMessage("Unable to determine slice thickness: please check voxel size\n");
         d.xyzMM[3] = 1.0;
     }
-    //printf("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]);
-    //printf("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart);
-    // printf("ser %ld\n", d.seriesNum);
+    //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3]);
+    //printMessage("Patient Position\t%g\t%g\t%g\tThick\t%g\tStart\t%d\n",d.patientPosition[1],d.patientPosition[2],d.patientPosition[3], d.xyzMM[3], d.imageStart);
+    // printMessage("ser %ld\n", d.seriesNum);
 
 
     //int kEchoMult = 100; //For Siemens/GE Series 1,2,3... save 2nd echo as 201, 3rd as 301, etc
@@ -3219,12 +3354,12 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
     //    d.seriesNum = d.seriesNum + (kEchoMult*d.echoNum);
     if ((d.compressionScheme == kCompress50) && (d.bitsAllocated > 8) ) {
         //dcmcjpg with +ee can create .51 syntax images that are 8,12,16,24-bit: we can only decode 8/24-bit
-        printf("Error: unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg\n", d.bitsAllocated);
+        printError("Unable to decode %d-bit images with Transfer Syntax 1.2.840.10008.1.2.4.51, decompress with dcmdjpg\n", d.bitsAllocated);
         d.isValid = false;
     }
     if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (isMosaic) && (d.CSA.mosaicSlices < 1) && (phaseEncodingSteps > 0) && ((d.xyzDim[1] % phaseEncodingSteps) == 0) && ((d.xyzDim[2] % phaseEncodingSteps) == 0) ) {
     	d.CSA.mosaicSlices = (d.xyzDim[1] / phaseEncodingSteps) * (d.xyzDim[2] / phaseEncodingSteps);
-    	printf("Warning: mosaic inferred without CSA header (check number of slices and spatial orientation)\n");
+    	printWarning("Mosaic inferred without CSA header (check number of slices and spatial orientation)\n");
     }
     if ((d.manufacturer == kMANUFACTURER_SIEMENS) && (d.CSA.dtiV[1] < -1.0) && (d.CSA.dtiV[2] < -1.0) && (d.CSA.dtiV[3] < -1.0))
     	d.CSA.dtiV[0] = 0; //SiemensTrio-Syngo2004A reports B=0 images as having impossible b-vectors.
@@ -3236,32 +3371,35 @@ struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, stru
 		strcpy(d.protocolName, d.sequenceName);
 	//     if (!isOrient) {
 	//     	if (d.isNonImage)
-	//     		printf("Warning: spatial orientation ambiguous  (tag 0020,0037 not found) [probably not important: derived image]: %s\n", fname);
+	//     		printWarning("Spatial orientation ambiguous  (tag 0020,0037 not found) [probably not important: derived image]: %s\n", fname);
 	//     	else if (((d.manufacturer == kMANUFACTURER_SIEMENS)) && (d.samplesPerPixel != 1))
-	//     		printf("Warning: spatial orientation ambiguous (tag 0020,0037 not found) [perhaps derived FA that is not required]: %s\n", fname);
+	//     		printWarning("Spatial orientation ambiguous (tag 0020,0037 not found) [perhaps derived FA that is not required]: %s\n", fname);
 	//     	else
-	//     		printf("Serious error: spatial orientation ambiguous (tag 0020,0037 not found): %s\n", fname);
+	//     		printWarning("Spatial orientation ambiguous (tag 0020,0037 not found): %s\n", fname);
 	//     }
+	if ((d.numberOfDynamicScans < 2) && (!d.isSlicesSpatiallySequentialPhilips) && (!isnan(patientPositionStartPhilips[1])) && (!isnan(patientPositionEndPhilips[1]))) {
+		//to do: check for d.numberOfDynamicScans > 1
+		for (int k = 0; k < 4; k++) {
+			d.patientPosition[k] = patientPositionStartPhilips[k];
+			d.patientPositionLast[k] = patientPositionEndPhilips[k];
+		}
+		printMessage("Slices not spatially contiguous: please check output [new feature]\n");
+    }
     if (isVerbose) {
-        printf("%s\n patient position\t%g\t%g\t%g\n",fname, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
-        printf(" acq %d img %d ser %ld dim %dx%dx%d mm %gx%gx%g offset %d dyn %d loc %d valid %d ph %d mag %d posReps %d nDTI %d 3d %d bits %d littleEndian %d echo %d coil %d\n",d.acquNum,d.imageNum,d.seriesNum,d.xyzDim[1],d.xyzDim[2],d.xyzDim[3],d.xyzMM[1],d.xyzMM[2],d.xyzMM[3],d.imageStart, d.numberOfDynamicScans, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude,d.patientPositionSequentialRepeats, d.CSA.numDti, d.is3DAcq, d.bitsAllocated, d.isLittleEndian, d.echoNum, [...]
+        printMessage("%s\n patient position\t%g\t%g\t%g\n",fname, d.patientPosition[1],d.patientPosition[2],d.patientPosition[3]);
+        printMessage(" acq %d img %d ser %ld dim %dx%dx%d mm %gx%gx%g offset %d dyn %d loc %d valid %d ph %d mag %d posReps %d nDTI %d 3d %d bits %d littleEndian %d echo %d coil %d TE %g TR %g\n",d.acquNum,d.imageNum,d.seriesNum,d.xyzDim[1],d.xyzDim[2],d.xyzDim[3],d.xyzMM[1],d.xyzMM[2],d.xyzMM[3],d.imageStart, d.numberOfDynamicScans, d.locationsInAcquisition, d.isValid, d.isHasPhase, d.isHasMagnitude,d.patientPositionSequentialRepeats, d.CSA.numDti, d.is3DAcq, d.bitsAllocated, d.isLittle [...]
         if (d.CSA.dtiV[0] > 0)
-        	printf(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
+        	printMessage(" DWI bxyz %g %g %g %g\n", d.CSA.dtiV[0], d.CSA.dtiV[1], d.CSA.dtiV[2], d.CSA.dtiV[3]);
     }
-    if (d.CSA.numDti >= kMaxDTI4D) {
-        printf("Error: unable to convert DTI [increase kMaxDTI4D]\n");
+    if (d.patientPositionNumPhilips >= kMaxDTI4D) {
+        printError("Too many 2D slices in a single file [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.patientPositionNumPhilips, kMaxDTI4D);
         d.CSA.numDti = 0;
     }
-    if (intenScalePhilips != 0.0) {
-        printf("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d.intenScale,d.intenIntercept,intenScalePhilips);
-        //we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/
-        float l0 = PhilipsPreciseVal (0, d.intenScale, d.intenIntercept, intenScalePhilips);
-        float l1 = PhilipsPreciseVal (1, d.intenScale, d.intenIntercept, intenScalePhilips);
-        if (l0 != l1) {
-            d.intenIntercept = l0;
-            d.intenScale = l1-l0;
-        }
+    if (d.CSA.numDti >= kMaxDTI4D) {
+        printError("Unable to convert DTI [recompile with increased kMaxDTI4D] detected=%d, max = %d\n", d.CSA.numDti, kMaxDTI4D);
+        d.CSA.numDti = 0;
     }
+    //d.isValid = false; //debug only - will not create output!
     return d;
 } // readDICOM()
 
@@ -3271,3 +3409,4 @@ struct TDICOMdata readDICOM(char * fname) {
 } // readDICOM()
 
 
+
diff --git a/console/nii_dicom.h b/console/nii_dicom.h
index e087d38..7792b9a 100644
--- a/console/nii_dicom.h
+++ b/console/nii_dicom.h
@@ -1,6 +1,9 @@
 #include <stdbool.h>
 #include <string.h>
+#include "nifti1_io_core.h"
+#ifndef HAVE_R
 #include "nifti1.h"
+#endif
 
 #ifndef MRIpro_nii_dcm_h
 
@@ -10,18 +13,34 @@
 extern "C" {
 #endif
 
-#ifdef myEnableJasper
-    #define kDCMvers "1Nov2016j" //JASPER for JPEG2000
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+ #ifdef myEnableJasper
+  #define kDCMsuf " (JasPer build)"
+ #else
+  #ifdef myDisableOpenJPEG
+    #define kDCMsuf ""
+  #else
+    #define kDCMsuf " (OpenJPEG build)"
+  #endif
+ #endif
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+	#define kCCsuf  " IntelCC" STR(__INTEL_COMPILER)
+#elif defined(_MSC_VER)
+	#define kCCsuf  " MSC" STR(_MSC_VER)
+#elif defined(__clang__)
+	#define kCCsuf  " Clang" STR(__clang_major__) "." STR(__clang_minor__) "." STR(__clang_patchlevel__)
+#elif defined(__GNUC__) || defined(__GNUG__)
+    #define kCCsuf  " GCC" STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__)
 #else
-	#ifdef myDisableOpenJPEG
-    #define kDCMvers "1Nov2016" //no decompressor
-	#else
-    #define kDCMvers "1Nov2016o" //OPENJPEG for JPEG2000
-    #endif
+	#define kCCsuf " CompilerNA" //unknown compiler!
 #endif
 
-static const int kMaxDTI4D = 4000; //#define kMaxDTIv  4000
-#define kDICOMStr  31
+ #define kDCMvers "v1.0.20170429" kDCMsuf kCCsuf
+
+static const int kMaxDTI4D = 4096; //maximum number of DTI directions for 4D (Philips) images, also maximum number of 3D slices for Philips 3D and 4D images
+#define kDICOMStr 64
 #define kMANUFACTURER_UNKNOWN  0
 #define kMANUFACTURER_SIEMENS  1
 #define kMANUFACTURER_GE  2
@@ -39,6 +58,7 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
     struct TDTI {
         float V[4];
         float sliceTiming;
+        int sliceNumberMrPhilips;
     };
     struct TDTI4D {
         struct TDTI S[kMaxDTI4D];
@@ -51,14 +71,16 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
     };
     struct TDICOMdata {
         long seriesNum;
-        int xyzDim[5];//, xyzOri[4];
-        int coilNum, echoNum,sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme; //
-        float flipAngle, fieldStrength, TE, TR,intenScale,intenIntercept, gantryTilt, lastScanLoc, angulation[4];
-        float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4]; //patientPosition2nd[4],
+        int xyzDim[5];
+        int patientPositionNumPhilips, coilNum, echoNum, sliceOrient,numberOfDynamicScans, manufacturer, converted2NII, acquNum, imageNum, imageStart, imageBytes, bitsStored, bitsAllocated, samplesPerPixel,patientPositionSequentialRepeats,locationsInAcquisition, compressionScheme;
+        float flipAngle, fieldStrength, TE, TI, TR, intenScale, intenIntercept, intenScalePhilips, gantryTilt, lastScanLoc, angulation[4];
+        float orient[7], patientPosition[4], patientPositionLast[4], xyzMM[4], stackOffcentre[4];
+        float radionuclidePositronFraction, radionuclideTotalDose, radionuclideHalfLife, doseCalibrationFactor; //PET ISOTOPE MODULE ATTRIBUTES (C.8-57)
+		float ecat_isotope_halflife, ecat_dosage;
         double dateTime, acquisitionTime, acquisitionDate;
-        bool isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled;
+        bool isXRay, isSlicesSpatiallySequentialPhilips, isNonImage, isValid, is3DAcq, isExplicitVR, isLittleEndian, isPlanarRGB, isSigned, isHasPhase,isHasMagnitude,isHasMixed, isFloat, isResampled;
         char phaseEncodingRC;
-        char  imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr],  studyDate[kDICOMStr],studyTime[kDICOMStr], imageComments[kDICOMStr];
+        char seriesInstanceUID[kDICOMStr], studyInstanceUID[kDICOMStr], bodyPartExamined[kDICOMStr], procedureStepDescription[kDICOMStr], imageType[kDICOMStr], manufacturersModelName[kDICOMStr], patientID[kDICOMStr], patientOrient[kDICOMStr], patientName[kDICOMStr],seriesDescription[kDICOMStr], sequenceName[kDICOMStr], protocolName[kDICOMStr],sequenceVariant[kDICOMStr],scanningSequence[kDICOMStr], birthDate[kDICOMStr], gender[kDICOMStr], age[kDICOMStr],  studyDate[kDICOMStr],studyTime[kD [...]
         struct TCSAdata CSA;
     };
 
@@ -66,14 +88,16 @@ static const int kCompress50 = 3; //obsolete JPEG lossy
     struct TDICOMdata readDICOMv(char * fname, int isVerbose, int compressFlag, struct TDTI4D *dti4D);
     struct TDICOMdata readDICOM(char * fname);
     struct TDICOMdata clear_dicom_data();
+    struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D);
     unsigned char * nii_flipY(unsigned char* bImg, struct nifti_1_header *h);
     unsigned char * nii_flipZ(unsigned char* bImg, struct nifti_1_header *h);
+    unsigned char * nii_reorderSlices(unsigned char* bImg, struct nifti_1_header *h, struct TDTI4D *dti4D);
     void changeExt (char *file_name, const char* ext);
-    struct TDICOMdata  nii_readParRec (char * parname, int isVerbose, struct TDTI4D *dti4D);
     unsigned char * nii_planar2rgb(unsigned char* bImg, struct nifti_1_header *hdr, int isPlanar);
 	int isDICOMfile(const char * fname); //0=not DICOM, 1=DICOM, 2=NOTSURE(not part 10 compliant)
-
+    void setQSForm(struct nifti_1_header *h, mat44 Q44i, bool isVerbose);
     int headerDcm2Nii2(struct TDICOMdata d, struct TDICOMdata d2, struct nifti_1_header *h);
+    int headerDcm2Nii(struct TDICOMdata d, struct nifti_1_header *h, bool isComputeSForm) ;
     //unsigned char * nii_loadImgX(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries);
     unsigned char * nii_loadImgXL(char* imgname, struct nifti_1_header *hdr, struct TDICOMdata dcm, bool iVaries, int compressFlag, int isVerbose);
     //int foo (float vx);
diff --git a/console/nii_dicom_batch.cpp b/console/nii_dicom_batch.cpp
index dd874f6..0220742 100755
--- a/console/nii_dicom_batch.cpp
+++ b/console/nii_dicom_batch.cpp
@@ -16,7 +16,6 @@
 #endif
 
 #if defined(__APPLE__) && defined(__MACH__)
-//#include "nii_foreign.h"
 #endif
 #ifndef myDisableZLib
     #ifdef MiniZ
@@ -25,14 +24,15 @@
         #include <zlib.h>
     #endif
 #endif
-#ifdef myUseCOut
-	#include <iostream>
-#endif
+#include "tinydir.h"
+#include "print.h"
 #include "nifti1_io_core.h"
+#ifndef HAVE_R
 #include "nifti1.h"
+#endif
 #include "nii_dicom_batch.h"
+#include "nii_foreign.h"
 #include "nii_dicom.h"
-#include "tinydir.h"
 #include <ctype.h> //toupper
 #include <float.h>
 #include <math.h>
@@ -62,6 +62,13 @@
 	const char kFileSep[2] = "/";
 #endif
 
+#ifdef HAVE_R
+#include "ImageList.h"
+
+#undef isnan
+#define isnan ISNAN
+#endif
+
 struct TDCMsort {
     uint64_t indx, img;
 };
@@ -176,22 +183,18 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
     if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S'))
         ; //participant was head first supine
     else {
-    	#ifdef myUseCOut
-     	std::cout<<"GE DTI directions require head first supine acquisition" <<std::endl;
-    	#else
-        printf("GE DTI directions require head first supine acquisition\n");
-		#endif
-        return;
+    	printMessage("GE DTI directions require head first supine acquisition\n");
+		return;
     }
     bool col = false;
-    if (d->phaseEncodingRC== 'C')
+    if (d->phaseEncodingRC == 'C')
         col = true;
-    else if (d->phaseEncodingRC!= 'R') {
-        printf("Error: Unable to determine DTI gradients, 0018,1312 should be either R or C");
+    else if (d->phaseEncodingRC != 'R') {
+        printWarning("Unable to determine DTI gradients, 0018,1312 should be either R or C");
         return;
     }
     if (abs(sliceDir) != 3)
-        printf("Warning: GE DTI only tested for axial acquisitions (solution: use Xiangrui Li's dicm2nii)\n");
+        printWarning("GE DTI only tested for axial acquisitions (solution: use Xiangrui Li's dicm2nii)\n");
     //GE vectors from Xiangrui Li' dicm2nii, validated with datasets from https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#Diffusion_Tensor_Imaging
 	ivec3 flp;
 	if (abs(sliceDir) == 1)
@@ -201,12 +204,12 @@ void geCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI *vx){
 	else if (abs(sliceDir) == 3)
 		flp = setiVec3(0, 0, 1); //AXIAL
 	else
-		printf("Impossible GE slice orientation!");
+		printMessage("Impossible GE slice orientation!");
 	if (sliceDir < 0)
     	flp.v[2] = 1 - flp.v[2];
-    printf("Reorienting %s : %d GE DTI vectors: please validate. isCol=%d sliceDir=%d flp=%d %d %d\n", d->protocolName, d->CSA.numDti, col, sliceDir, flp.v[0], flp.v[1],flp.v[2]);
+    printMessage("Reorienting %s : %d GE DTI vectors: please validate. isCol=%d sliceDir=%d flp=%d %d %d\n", d->protocolName, d->CSA.numDti, col, sliceDir, flp.v[0], flp.v[1],flp.v[2]);
 	if (!col)
-		printf(" reorienting for ROW phase-encoding untested.\n");
+		printMessage(" reorienting for ROW phase-encoding untested.\n");
     for (int i = 0; i < d->CSA.numDti; i++) {
         float vLen = sqrt( (vx[i].V[1]*vx[i].V[1])
                           + (vx[i].V[2]*vx[i].V[2])
@@ -252,11 +255,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
     if ((toupper(d->patientOrient[0])== 'H') && (toupper(d->patientOrient[1])== 'F') && (toupper(d->patientOrient[2])== 'S'))
         ; //participant was head first supine
     else {
-    #ifdef myUseCOut
-    std::cout<<"Siemens/Philips DTI directions require head first supine acquisition"<<std::endl;
-    #else
-        printf("Siemens/Philips DTI directions require head first supine acquisition\n");
-        #endif
+        printMessage("Siemens/Philips DTI directions require head first supine acquisition\n");
         return;
     }
     vec3 read_vector = setVec3(d->orient[1],d->orient[2],d->orient[3]);
@@ -271,7 +270,7 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
                           + (vx[i].V[3]*vx[i].V[3]));
         if ((vx[i].V[0] <= FLT_EPSILON)|| (vLen <= FLT_EPSILON) ) { //bvalue=0
             if (vx[i].V[0] > FLT_EPSILON)
-                printf("Warning: volume %d appears to be an ADC map (non-zero b-value with zero vector length)\n", i);
+                printWarning("Volume %d appears to be an ADC map (non-zero b-value with zero vector length)\n", i);
             //for (int v= 0; v < 4; v++)
             //    vx[i].V[v] =0.0f;
             continue; //do not normalize or reorient b0 vectors
@@ -286,13 +285,13 @@ void siemensPhilipsCorrectBvecs(struct TDICOMdata *d, int sliceDir, struct TDTI
         for (int v= 0; v < 4; v++)
             if (vx[i].V[v] == -0.0f) vx[i].V[v] = 0.0f; //remove sign from values that are virtually zero
     } //for each direction
-
-    if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant)
-       printf("WARNING: please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal).\n");
-    else if ( d->sliceOrient == kSliceOrientTra)
-        printf("Saving %d DTI gradients. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti);
-    else
-        printf("WARNING: DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files.\n");
+    if (abs(sliceDir) == kSliceOrientMosaicNegativeDeterminant) {
+       printWarning("Please validate DTI vectors (matrix had a negative determinant, perhaps Siemens sagittal).\n");
+    } else if ( d->sliceOrient == kSliceOrientTra) {
+        printMessage("Saving %d DTI gradients. Please validate if you are conducting DTI analyses.\n", d->CSA.numDti);
+    } else {
+        printWarning("DTI gradient directions only tested for axial (transverse) acquisitions. Please validate bvec files.\n");
+    }
 }// siemensPhilipsCorrectBvecs()
 
 bool isNanPosition(struct TDICOMdata d) { //in 2007 some Siemens RGB DICOMs did not include the PatientPosition 0020,0032 tag
@@ -315,9 +314,9 @@ void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
 	char txtname[2048] = {""};
 	strcpy (txtname,pathoutname);
     strcat (txtname,".txt");
-    //printf("Saving text %s\n",txtname);
+    //printMessage("Saving text %s\n",txtname);
     FILE *fp = fopen(txtname, "w");
-    fprintf(fp, "%s\tField Strength:\t%g\tProtocolName:\t%s\tScanningSequence00180020:\t%s\tTE:\t%g\tTR:\t%g\tSeriesNum:\t%ld\tAcquNum:\t%d\tImageNum:\t%d\tImageComments:\t%s\tDateTime:\t%F\tName:\t%s\tConvVers:\t%s\tDoB:\t%s\tGender:\t%s\tAge:\t%s\tDimXYZT:\t%d\t%d\t%d\t%d\tCoil:\t%d\tEchoNum:\t%d\tOrient(6)\t%g\t%g\t%g\t%g\t%g\t%g\tbitsAllocated\t%d\tInputName\t%s\n",
+    fprintf(fp, "%s\tField Strength:\t%g\tProtocolName:\t%s\tScanningSequence00180020:\t%s\tTE:\t%g\tTR:\t%g\tSeriesNum:\t%ld\tAcquNum:\t%d\tImageNum:\t%d\tImageComments:\t%s\tDateTime:\t%f\tName:\t%s\tConvVers:\t%s\tDoB:\t%s\tGender:\t%s\tAge:\t%s\tDimXYZT:\t%d\t%d\t%d\t%d\tCoil:\t%d\tEchoNum:\t%d\tOrient(6)\t%g\t%g\t%g\t%g\t%g\t%g\tbitsAllocated\t%d\tInputName\t%s\n",
       pathoutname, d.fieldStrength, d.protocolName, d.scanningSequence, d.TE, d.TR, d.seriesNum, d.acquNum, d.imageNum, d.imageComments,
       d.dateTime, d.patientName, kDCMvers, d.birthDate, d.gender, d.age, h->dim[1], h->dim[2], h->dim[3], h->dim[4],
             d.coilNum,d.echoNum, d.orient[1], d.orient[2], d.orient[3], d.orient[4], d.orient[5], d.orient[6],
@@ -325,6 +324,14 @@ void nii_SaveText(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
     fclose(fp);
 }// nii_SaveText()
 
+bool isDerived(struct TDICOMdata d) {
+	#define kDerivedStr "DERIVED"
+	if ((strlen(d.imageType) < strlen(kDerivedStr)) || (strstr(d.imageType, kDerivedStr) == NULL))
+		return false;
+	else
+		return true;
+}
+
 void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h) {
 //https://docs.google.com/document/d/1HFUkAEE-pB-angVcYe6pf_-fVf4sCpOHKesUvfb8Grc/edit#
 // Generate Brain Imaging Data Structure (BIDS) info
@@ -336,7 +343,6 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
 	char txtname[2048] = {""};
 	strcpy (txtname,pathoutname);
 	strcat (txtname,".json");
-	//printf("Saving DTI %s\n",txtname);
 	FILE *fp = fopen(txtname, "w");
 	fprintf(fp, "{\n");
 	switch (d.manufacturer) {
@@ -354,6 +360,26 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
 			break;
 	};
 	fprintf(fp, "\t\"ManufacturersModelName\": \"%s\",\n", d.manufacturersModelName );
+	if (!opts.isAnonymizeBIDS) {
+		if (strlen(d.seriesInstanceUID) > 0)
+			fprintf(fp, "\t\"SeriesInstanceUID\": \"%s\",\n", d.seriesInstanceUID );
+		if (strlen(d.studyInstanceUID) > 0)
+			fprintf(fp, "\t\"StudyInstanceUID\": \"%s\",\n", d.studyInstanceUID );
+	}
+	if (strlen(d.procedureStepDescription) > 0)
+		fprintf(fp, "\t\"ProcedureStepDescription\": \"%s\",\n", d.procedureStepDescription );
+	if (strlen(d.scanningSequence) > 0)
+		fprintf(fp, "\t\"ScanningSequence\": \"%s\",\n", d.scanningSequence );
+	if (strlen(d.sequenceVariant) > 0)
+		fprintf(fp, "\t\"SequenceVariant\": \"%s\",\n", d.sequenceVariant );
+	if (strlen(d.seriesDescription) > 0)
+		fprintf(fp, "\t\"SeriesDescription\": \"%s\",\n", d.seriesDescription );
+	if (strlen(d.bodyPartExamined) > 0)
+		fprintf(fp, "\t\"BodyPartExamined\": \"%s\",\n", d.bodyPartExamined );
+	if (strlen(d.protocolName) > 0)
+		fprintf(fp, "\t\"ProtocolName\": \"%s\",\n", d.protocolName );
+	if (strlen(d.sequenceName) > 0)
+		fprintf(fp, "\t\"SequenceName\": \"%s\",\n", d.sequenceName );
 	if (strlen(d.imageType) > 0) {
 		fprintf(fp, "\t\"ImageType\": [\"");
 		bool isSep = false;
@@ -370,26 +396,68 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
 	}
 	//Chris Gorgolewski: BIDS standard specifies ISO8601 date-time format (Example: 2016-07-06T12:49:15.679688)
 	//Lines below directly save DICOM values
+	if (d.acquisitionTime > 0.0 && d.acquisitionDate > 0.0){
+		long acquisitionDate = d.acquisitionDate;
+		double acquisitionTime = d.acquisitionTime;
+		char acqDateTimeBuf[64];
+		//snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+08f", acquisitionDate, acquisitionTime);
+		snprintf(acqDateTimeBuf, sizeof acqDateTimeBuf, "%+08ld%+013.5f", acquisitionDate, acquisitionTime); //CR 20170404 add zero pad so 1:23am appears as +012300.00000 not +12300.00000
+		//printMessage("acquisitionDateTime %s\n",acqDateTimeBuf);
+		int ayear,amonth,aday,ahour,amin;
+		double asec;
+		int count = 0;
+		sscanf(acqDateTimeBuf, "%5d%2d%2d%3d%2d%lf%n", &ayear, &amonth, &aday, &ahour, &amin, &asec, &count);  //CR 20170404 %lf not %f for double precision
+		//printf("-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec);
+		if (count) { // ISO 8601 specifies a sign must exist for distant years.
+			//report time of the day only format, https://www.cs.tut.fi/~jkorpela/iso8601.html
+			fprintf(fp, "\t\"AcquisitionTime\": \"%02d:%02d:%02.6f\",\n",ahour, amin, asec);
+			//report date and time together
+			if (!opts.isAnonymizeBIDS) {
+				fprintf(fp, "\t\"AcquisitionDateTime\": ");
+				fprintf(fp, (ayear >= 0 && ayear <= 9999) ? "\"%4d" : "\"%+4d", ayear);
+				fprintf(fp, "-%02d-%02dT%02d:%02d:%02.6f\",\n", amonth, aday, ahour, amin, asec);
+
+			}
+		} //if (count)
+	} //if acquisitionTime and acquisitionDate recorded
 	// if (d.acquisitionTime > 0.0) fprintf(fp, "\t\"AcquisitionTime\": %f,\n", d.acquisitionTime );
 	// if (d.acquisitionDate > 0.0) fprintf(fp, "\t\"AcquisitionDate\": %8.0f,\n", d.acquisitionDate );
 	//if conditionals: the following values are required for DICOM MRI, but not available for CT
+	if ((d.intenScalePhilips != 0) || (d.manufacturer == kMANUFACTURER_PHILIPS)) { //for details, see PhilipsPrecise()
+		fprintf(fp, "\t\"PhilipsRescaleSlope\": %g,\n", d.intenScale );
+		fprintf(fp, "\t\"PhilipsRescaleIntercept\": %g,\n", d.intenIntercept );
+		fprintf(fp, "\t\"PhilipsScaleSlope\": %g,\n", d.intenScalePhilips );
+		fprintf(fp, "\t\"UsePhilipsFloatNotDisplayScaling\": %d,\n", opts.isPhilipsFloatNotDisplayScaling);
+	}
+	//PET ISOTOPE MODULE ATTRIBUTES
+	if (d.radionuclidePositronFraction > 0.0) fprintf(fp, "\t\"RadionuclidePositronFraction\": %g,\n", d.radionuclidePositronFraction );
+	if (d.radionuclideTotalDose > 0.0) fprintf(fp, "\t\"RadionuclideTotalDose\": %g,\n", d.radionuclideTotalDose );
+	if (d.radionuclideHalfLife > 0.0) fprintf(fp, "\t\"RadionuclideHalfLife\": %g,\n", d.radionuclideHalfLife );
+	if (d.doseCalibrationFactor > 0.0) fprintf(fp, "\t\"DoseCalibrationFactor\": %g,\n", d.doseCalibrationFactor );
+	//MRI parameters
 	if (d.fieldStrength > 0.0) fprintf(fp, "\t\"MagneticFieldStrength\": %g,\n", d.fieldStrength );
 	if (d.flipAngle > 0.0) fprintf(fp, "\t\"FlipAngle\": %g,\n", d.flipAngle );
-	if (d.TE > 0.0) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 );
+	if ((d.TE > 0.0) && (!d.isXRay)) fprintf(fp, "\t\"EchoTime\": %g,\n", d.TE / 1000.0 );
+    if ((d.TE > 0.0) && (d.isXRay)) fprintf(fp, "\t\"XRayExposure\": %g,\n", d.TE );
     if (d.TR > 0.0) fprintf(fp, "\t\"RepetitionTime\": %g,\n", d.TR / 1000.0 );
+    if (d.TI > 0.0) fprintf(fp, "\t\"InversionTime\": %g,\n", d.TI / 1000.0 );
+    if (d.ecat_isotope_halflife > 0.0) fprintf(fp, "\t\"IsotopeHalfLife\": %g,\n", d.ecat_isotope_halflife);
+    if (d.ecat_dosage > 0.0) fprintf(fp, "\t\"Dosage\": %g,\n", d.ecat_dosage);
     if ((d.CSA.bandwidthPerPixelPhaseEncode > 0.0) &&  (h->dim[2] > 0) && (h->dim[1] > 0)) {
-		float dwellTime = 0;
-		if (d.phaseEncodingRC =='C')
+		float dwellTime = 0.0f;
+		if  (h->dim[2] == h->dim[2]) //phase encoding does not matter
 			dwellTime =  1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
-		else
+		else if (d.phaseEncodingRC =='R')
+			dwellTime =  1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[2];
+		else if (d.phaseEncodingRC =='C')
 			dwellTime =  1.0/d.CSA.bandwidthPerPixelPhaseEncode/h->dim[1];
-		fprintf(fp, "\t\"EffectiveEchoSpacing\": %g,\n", dwellTime );
-
+		if (dwellTime != 0.0f) //as long as phase encoding = R or C or does not matter
+			fprintf(fp, "\t\"EffectiveEchoSpacing\": %g,\n", dwellTime );
     }
 	bool first = 1;
 	if (dti4D->S[0].sliceTiming >= 0.0) {
    		fprintf(fp, "\t\"SliceTiming\": [\n");
-		for (int i = 0; i < kMaxDTI4D; i++) {
+   		for (int i = 0; i < kMaxDTI4D; i++) {
 			if (dti4D->S[i].sliceTiming >= 0.0){
 			  if (!first)
 				  fprintf(fp, ",\n");
@@ -400,30 +468,68 @@ void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts,
 		}
 		fprintf(fp, "\t],\n");
 	}
-	if (d.phaseEncodingRC == 'C')
-		fprintf(fp, "\t\"PhaseEncodingDirection\": \"j");
-	else
-		fprintf(fp, "\t\"PhaseEncodingDirection\": \"i");
-	//phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1)
-	//However, DICOM and NIfTI are reversed in the j (ROW) direction
-	//Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end"
-	if ((d.CSA.phaseEncodingDirectionPositive == 1) && ((opts.isFlipY)))
-		fprintf(fp, "-");
-	if ((d.CSA.phaseEncodingDirectionPositive == 0) && ((!opts.isFlipY)))
-		fprintf(fp, "-");
-	fprintf(fp, "\"\n");
+	if (((d.phaseEncodingRC == 'R') || (d.phaseEncodingRC == 'C')) && ((d.CSA.phaseEncodingDirectionPositive == 1) || (d.CSA.phaseEncodingDirectionPositive == 0))) {
+		if (d.phaseEncodingRC == 'C') //Values should be "R"ow, "C"olumn or "?"Unknown
+			fprintf(fp, "\t\"PhaseEncodingDirection\": \"j");
+		else if (d.phaseEncodingRC == 'R')
+				fprintf(fp, "\t\"PhaseEncodingDirection\": \"i");
+		else
+			fprintf(fp, "\t\"PhaseEncodingDirection\": \"?");
+		//phaseEncodingDirectionPositive has one of three values: UNKNOWN (-1), NEGATIVE (0), POSITIVE (1)
+		//However, DICOM and NIfTI are reversed in the j (ROW) direction
+		//Equivalent to dicm2nii's "if flp(iPhase), phPos = ~phPos; end"
+		if (d.CSA.phaseEncodingDirectionPositive == -1)
+			fprintf(fp, "?"); //unknown
+		else if ((d.CSA.phaseEncodingDirectionPositive == 1) && ((opts.isFlipY)))
+			fprintf(fp, "-");
+		else if ((d.CSA.phaseEncodingDirectionPositive == 0) && ((!opts.isFlipY)))
+			fprintf(fp, "-");
+		fprintf(fp, "\",\n");
+	} //only save PhaseEncodingDirection if BOTH direction and POLARITY are known
+	fprintf(fp, "\t\"ConversionSoftware\": \"dcm2niix\",\n");
+	fprintf(fp, "\t\"ConversionSoftwareVersion\": \"%s\"\n", kDCMvers );
+	//fprintf(fp, "\t\"DicomConversion\": [\"dcm2niix\", \"%s\"]\n", kDCMvers );
     fprintf(fp, "}\n");
     fclose(fp);
-}// nii_SaveBIDS()
+}// nii_SaveBIDS() step
+
+bool isADCnotDTI(TDTI bvec) { //returns true if bval!=0 but all bvecs == 0 (Philips code for derived ADC image)
+	return ((!isSameFloat(bvec.V[0],0.0f)) && //not a B-0 image
+    	((isSameFloat(bvec.V[1],0.0f)) && (isSameFloat(bvec.V[2],0.0f)) && (isSameFloat(bvec.V[3],0.0f)) ) );
+}
 
-int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D) {
-    //reports non-zero if last volumes should be excluded (e.g. philip stores an ADC maps)
+unsigned char * removeADC(struct nifti_1_header *hdr, unsigned char *inImg, bool * isADC) {
+	int numVolOut = 0;
+	int numVolIn = hdr->dim[4];
+	int numVolBytes = hdr->dim[1]*hdr->dim[2]*hdr->dim[3]*(hdr->bitpix/8);
+	if ((!isADC) || (numVolIn < 1) || (numVolBytes < 1)) return inImg;
+	for (int i = 0; i < numVolIn; i++)
+		if (!isADC[i])
+			numVolOut++;
+	if (numVolOut < 1) return inImg;
+	//printMessage("Removing ADC maps, %d volumes reduced to %d\n", numVolIn, numVolOut);
+	unsigned char *outImg = (unsigned char *)malloc(numVolBytes * numVolOut);
+	int outPos = 0;
+	for (int i = 0; i < numVolIn; i++) {
+		if (!isADC[i]) {
+			memcpy(&outImg[outPos], &inImg[i * numVolBytes], numVolBytes); // dest, src, bytes
+            outPos += numVolBytes;
+		}
+	} //for each volume
+	free(isADC);
+	free(inImg);
+	hdr->dim[4] = numVolOut;
+	return outImg;
+} //removeADC()
+
+
+bool * nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TDCMopts opts, int sliceDir, struct TDTI4D *dti4D) {
+    //reports non-zero if any volumes should be excluded (e.g. philip stores an ADC maps)
     //to do: works with 3D mosaics and 4D files, must remove repeated volumes for 2D sequences....
     uint64_t indx0 = dcmSort[0].indx; //first volume
     int numDti = dcmList[indx0].CSA.numDti;
-
-    if (numDti < 1) return false;
-    if ((numDti < 3) && (nConvert < 3)) return false;
+    if (numDti < 1) return NULL;
+    if ((numDti < 3) && (nConvert < 3)) return NULL;
     TDTI * vx = NULL;
     if (numDti > 2) {
         vx = (TDTI *)malloc(numDti * sizeof(TDTI));
@@ -439,10 +545,9 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
                 for (int v = 0; v < 4; v++) //for each vector+B-value
                     vx[numDti].V[v] = dcmList[dcmSort[i].indx].CSA.dtiV[v];  //dcmList[indx0].CSA.dtiV[numDti][v] = dcmList[dcmSort[i].indx].CSA.dtiV[0][v];
                 numDti++;
-
             } //for slices with repeats
         }//for each file
-        dcmList[indx0].CSA.numDti = numDti;
+        dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope!
     }
     bool bValueVaries = false;
     for (int i = 1; i < numDti; i++) //check if all bvalues match first volume
@@ -456,13 +561,13 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
         }
         if (!bVecVaries) {
                 free(vx);
-                return false;
+                return NULL;
         }
         for (int i = 1; i < numDti; i++)
-                printf("bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]);
-        printf("Error: only one B-value reported for all volumes: %g\n",vx[0].V[0]);
+                printMessage("bxyz %g %g %g %g\n",vx[i].V[0],vx[i].V[1],vx[i].V[2],vx[i].V[3]);
+        printWarning("No bvec/bval files created. Only one B-value reported for all volumes: %g\n",vx[0].V[0]);
         free(vx);
-        return false;
+        return NULL;
     }
     int firstB0 = -1;
     for (int i = 0; i < numDti; i++) //check if all bvalues match first volume
@@ -470,38 +575,39 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
             firstB0 = i;
             break;
         }
-    #ifdef myUseCOut
-    if (firstB0 < 0)
-    	std::cout<<"Warning: this diffusion series does not have a B0 (reference) volume"<<std::endl;
-	if (firstB0 > 0)
-    	std::cout<<"Note: B0 not the first volume in the series (FSL eddy reference volume is "<<firstB0<<")"<<std::endl;
-
-	#else
-    if (firstB0 < 0) printf("Warning: this diffusion series does not have a B0 (reference) volume\n");
-    if (firstB0 > 0) printf("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", firstB0);
-	#endif
-    int numFinalADC = 0;
+    if (firstB0 < 0) printWarning("This diffusion series does not have a B0 (reference) volume\n");
+    if (firstB0 > 0) printMessage("Note: B0 not the first volume in the series (FSL eddy reference volume is %d)\n", firstB0);
+	int numADC = 0;
+    bool * isADC = NULL;
     if (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS) {
-        int i = numDti - 1;
-        while ((i > 0) && (!isSameFloat(vx[i].V[0],0.0f)) && //not a B-0 image
-                   ((isSameFloat(vx[i].V[1],0.0f)) &&
-                   (isSameFloat(vx[i].V[2],0.0f)) &&
-                   (isSameFloat(vx[i].V[3],0.0f)) ) ){//yet all vectors are zero!!!! must be ADC
-                numFinalADC++; //final volume is ADC map
-                numDti --; //remove final volume - it is a computed ADC map!
-                dcmList[indx0].CSA.numDti = numDti;
-                i --;
+    	isADC = (bool *)malloc(numDti * sizeof(bool));
+    	int o = 0; //output index
+        for (int i = 0; i < numDti; i++) {//check if all bvalues match first volume
+        	if (isADCnotDTI(vx[i])) {
+        	    isADC[i] = true;
+            	numADC++;
+            	printMessage("Volume %d is not a normal DTI image (ADC?)\n", i+1);
+            } else { //save output
+            	vx[o] = vx[i];
+            	isADC[i] = false;
+            	o++;
+            }
         } //
-        if (numFinalADC > 0)
-            printf("Note: final %d volumes appear to be ADC images that will be removed to allow processing\n", numFinalADC);
-        /*for (int i = 0; i < (numDti); i++) {
-            if ((!isSameFloat(vx[i].V[0],0.0f)) && //not a B-0 image
-                ((isSameFloat(vx[i].V[1],0.0f)) &&
-                 (isSameFloat(vx[i].V[2],0.0f)) &&
-                 (isSameFloat(vx[i].V[3],0.0f)) ) )
-                printf("Warning: volume %d appears to be an ADC volume %g %g %g\n", i+1, vx[i].V[1], vx[i].V[2], vx[i].V[3]);
-
-        }*/
+        numDti = numDti - numADC;
+        dcmList[indx0].CSA.numDti = numDti; //warning structure not changed outside scope!
+        if (numADC > 0) {
+            printMessage("Note: %d volumes appear to be ADC images that will be removed to allow processing\n", numADC);
+        	if (numDti == 0) {
+        		printWarning("No bvec/bval files created: no volumes with bvecs applied \n");
+        		free(isADC);
+        		free(vx);
+        		return NULL;
+        	}
+        }
+        if (numADC == 0) { //no ADC images
+        	free(isADC);
+        	isADC = NULL;
+        }
     }
     // philipsCorrectBvecs(&dcmList[indx0]); //<- replaced by unified siemensPhilips solution
     geCorrectBvecs(&dcmList[indx0],sliceDir, vx);
@@ -514,20 +620,34 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
     } //if not a mosaic
     if (opts.isVerbose) {
         for (int i = 0; i < (numDti); i++) {
-            printf("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, vx[i].V[0],
+            printMessage("%d\tB=\t%g\tVec=\t%g\t%g\t%g\n",i, vx[i].V[0],
                    vx[i].V[1],vx[i].V[2],vx[i].V[3]);
 
         } //for each direction
     }
-    //printf("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]);
+    //printMessage("%f\t%f\t%f",dcmList[indx0].CSA.dtiV[1][1],dcmList[indx0].CSA.dtiV[1][2],dcmList[indx0].CSA.dtiV[1][3]);
+#ifdef HAVE_R
+    std::vector<double> bValues(numDti);
+    std::vector<double> bVectors(numDti*3);
+    for (int i = 0; i < numDti; i++)
+    {
+        bValues[i] = vx[i].V[0];
+        for (int j = 0; j < 3; j++)
+            bVectors[i+j*numDti] = vx[i].V[j+1];
+    }
+    // The image hasn't been created yet, so the attributes must be deferred
+    ImageList *images = (ImageList *) opts.imageList;
+    images->addDeferredAttribute("bValues", bValues);
+    images->addDeferredAttribute("bVectors", bVectors, numDti, 3);
+#else
     char txtname[2048] = {""};
     strcpy (txtname,pathoutname);
     strcat (txtname,".bval");
-    //printf("Saving DTI %s\n",txtname);
+    //printMessage("Saving DTI %s\n",txtname);
     FILE *fp = fopen(txtname, "w");
     if (fp == NULL) {
         free(vx);
-        return numFinalADC;
+        return isADC;
     }
     for (int i = 0; i < (numDti-1); i++) {
         if (opts.isCreateBIDS) {
@@ -540,11 +660,11 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
     fclose(fp);
     strcpy(txtname,pathoutname);
     strcat (txtname,".bvec");
-    //printf("Saving DTI %s\n",txtname);
+    //printMessage("Saving DTI %s\n",txtname);
     fp = fopen(txtname, "w");
     if (fp == NULL) {
         free(vx);
-        return numFinalADC;
+        return isADC;
     }
     for (int v = 1; v < 4; v++) {
         for (int i = 0; i < (numDti-1); i++) {
@@ -557,8 +677,9 @@ int nii_SaveDTI(char pathoutname[],int nConvert, struct TDCMsort dcmSort[],struc
         fprintf(fp, "%g\n", vx[numDti-1].V[v]);
     }
     fclose(fp);
+#endif
     free(vx);
-    return numFinalADC;
+    return isADC;
 }// nii_SaveDTI()
 
 float sqr(float v){
@@ -580,23 +701,23 @@ float intersliceDistance(struct TDICOMdata d1, struct TDICOMdata d2) {
 void swapDim3Dim4(int d3, int d4, struct TDCMsort dcmSort[]) {
     //swap space and time: input A0,A1...An,B0,B1...Bn output A0,B0,A1,B1,...
     int nConvert = d3 * d4;
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	TDCMsort * dcmSortIn = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
-#else
-    struct TDCMsort dcmSortIn[nConvert];
-#endif
+//#else
+//    struct TDCMsort dcmSortIn[nConvert];
+//#endif
     for (int i = 0; i < nConvert; i++) dcmSortIn[i] = dcmSort[i];
     int i = 0;
     for (int b = 0; b < d3; b++)
         for (int a = 0; a < d4; a++) {
             int k = (a *d3) + b;
-            //printf("%d -> %d %d ->%d\n",i,a, b, k);
+            //printMessage("%d -> %d %d ->%d\n",i,a, b, k);
             dcmSort[k] = dcmSortIn[i];
             i++;
         }
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 	free(dcmSortIn);
-#endif
+//#endif
 } //swapDim3Dim4()
 
 bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[]){
@@ -638,7 +759,7 @@ bool intensityScaleVaries(int nConvert, struct TDCMsort dcmSort[],struct TDICOMd
  sliceOffsetR += sliceBytes8;
  } //for each slice
  return bImg;
- } //nii_ImgBytes()*/
+ } */
 
 bool niiExists(const char*pathoutname) {
     char niiname[2048] = {""};
@@ -665,18 +786,10 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
             if (getcwd(pth, sizeof(pth)) != NULL) {
             w =access(pth,W_OK);
             if (w != 0) {
-            	#ifdef myUseCOut
-    			std::cout<<"Error: you do not have write permissions for the directory "<<opts.outdir<<std::endl;
-				#else
-                printf("Error: you do not have write permissions for the directory %s\n",opts.outdir);
-                #endif
+            	printError("You do not have write permissions for the directory %s\n",opts.outdir);
                 return EXIT_FAILURE;
             }
-            #ifdef myUseCOut
-    		std::cout<<"Warning: "<<opts.outdir<<" write permission denied. Saving to working directory "<<pth<<std::endl;
-			#else
-            printf("Warning: %s write permission denied. Saving to working directory %s \n", opts.outdir, pth);
-            #endif
+            printWarning("%s write permission denied. Saving to working directory %s \n", opts.outdir, pth);
             }
         }
      }
@@ -706,19 +819,25 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
                 sprintf(newstr, "%02d", dcm.coilNum);
                 strcat (outname,newstr);
             }
+            if (f == 'C')
+                strcat (outname,dcm.imageComments);
+            if (f == 'D')
+                strcat (outname,dcm.seriesDescription);
         	if (f == 'E') {
         		isEchoReported = true;
                 sprintf(newstr, "%d", dcm.echoNum);
                 strcat (outname,newstr);
             }
-            if (f == 'C')
-                strcat (outname,dcm.imageComments);
-            if (f == 'D')
-                strcat (outname,dcm.seriesDescription);
             if (f == 'F')
                 strcat (outname,opts.indirParent);
             if (f == 'I')
                 strcat (outname,dcm.patientID);
+            if (f == 'J')
+                strcat (outname,dcm.seriesInstanceUID);
+            if (f == 'K')
+                strcat (outname,dcm.studyInstanceUID);
+            if (f == 'L') //"L"ocal Institution-generated description or classification of the Procedure Step that was performed.
+                strcat (outname,dcm.procedureStepDescription);
             if (f == 'M') {
                 if (dcm.manufacturer == kMANUFACTURER_GE)
                     strcat (outname,"GE");
@@ -737,16 +856,6 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
                 strcat (outname,dcm.protocolName);
             if (f == 'Q')
                 strcat (outname,dcm.scanningSequence);
-            if ((f >= '0') && (f <= '9')) {
-                if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
-                    char zeroPad[12] = {""};
-                    //sprintf(zeroPad,"%%0%dd",atoi(&f));
-                    sprintf(zeroPad,"%%0%dd",f - '0');
-                    sprintf(newstr, zeroPad, dcm.seriesNum);
-                    strcat (outname,newstr);
-                    pos++; // e.g. %3f requires extra increment: skip both number and following character
-                }
-            }
             if (f == 'S') {
                 sprintf(newstr, "%ld", dcm.seriesNum);
                 strcat (outname,newstr);
@@ -760,11 +869,21 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
 				sprintf(newstr, "%d", dcm.acquNum);
 				strcat (outname,newstr);
 				#else
-    			printf("Warning: ignoring '%%f' in output filename (recompile to segment by acquisition)\n");
+    			printWarning("Ignoring '%%f' in output filename (recompile to segment by acquisition)\n");
     			#endif
 			}
             if (f == 'Z')
                 strcat (outname,dcm.sequenceName);
+            if ((f >= '0') && (f <= '9')) {
+                if ((pos<strlen(inname)) && (toupper(inname[pos+1]) == 'S')) {
+                    char zeroPad[12] = {""};
+                    //sprintf(zeroPad,"%%0%dd",atoi(&f));
+                    sprintf(zeroPad,"%%0%dd",f - '0');
+                    sprintf(newstr, zeroPad, dcm.seriesNum);
+                    strcat (outname,newstr);
+                    pos++; // e.g. %3f requires extra increment: skip both number and following character
+                }
+            }
             start = pos + 1;
         } //found a % character
         pos++;
@@ -793,7 +912,7 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
             || (outname[pos] == '^')
             || (outname[pos] == '*') || (outname[pos] == '|') || (outname[pos] == '?'))
             outname[pos] = '_';
-    //printf("outname=*%s* %d %d\n", outname, pos,start);
+    //printMessage("outname=*%s* %d %d\n", outname, pos,start);
     char baseoutname[2048] = {""};
     strcat (baseoutname,pth);
     char appendChar[2] = {"a"};
@@ -811,25 +930,37 @@ int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopt
         i++;
     }
     if (i >= 26) {
-            #ifdef myUseCOut
-    		std::cout<<"Error: too many NIFTI images with the name "<<baseoutname<<std::endl;
-			#else
-        printf("Error: too many NIFTI images with the name %s\n", baseoutname);
-        #endif
+        printError("Too many NIFTI images with the name %s\n", baseoutname);
         return EXIT_FAILURE;
     }
-    //printf("-->%s\n",pathoutname); return EXIT_SUCCESS;
-    //printf("outname=%s\n", pathoutname);
+    //printMessage("-->%s\n",pathoutname); return EXIT_SUCCESS;
+    //printMessage("outname=%s\n", pathoutname);
     strcpy(niiFilename,pathoutname);
     return EXIT_SUCCESS;
 } //nii_createFilename()
 
 void  nii_createDummyFilename(char * niiFilename, struct TDCMopts opts) {
     //generate string that illustrating sample of filename
-    struct TDICOMdata dcm = clear_dicom_data();
+    struct TDICOMdata d = clear_dicom_data();
+    strcpy(d.patientName, "John_Doe");
+    strcpy(d.patientID, "ID123");
+    strcpy(d.imageType,"ORIGINAL");
+    strcpy(d.imageComments, "imgComments");
+    strcpy(d.studyDate, "1/1/1977");
+    strcpy(d.studyTime, "11:11:11");
+    strcpy(d.protocolName, "MPRAGE");
+    strcpy(d.seriesDescription, "T1_mprage");
+    strcpy(d.sequenceName, "T1");
+    strcpy(d.scanningSequence, "tfl3d1_ns");
+    strcpy(d.sequenceVariant, "tfl3d1_ns");
+    strcpy(d.manufacturersModelName, "N/A");
+    strcpy(d.procedureStepDescription, "");
+    strcpy(d.seriesInstanceUID, "");
+    strcpy(d.studyInstanceUID, "");
+    strcpy(d.bodyPartExamined,"");
     strcpy(opts.indirParent,"myFolder");
     char niiFilenameBase[1024] = {"/usr/myFolder/dicom.dcm"};
-    nii_createFilename(dcm, niiFilenameBase, opts) ;
+    nii_createFilename(d, niiFilenameBase, opts) ;
     strcpy(niiFilename,"Example output filename: '");
     strcat(niiFilename,niiFilenameBase);
     if (opts.isGz)
@@ -850,7 +981,11 @@ unsigned long mz_crc32(unsigned long crc, const unsigned char *ptr, size_t buf_l
 }
 #endif
 
-void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src_buffer, unsigned long src_len) {
+#ifndef MZ_UBER_COMPRESSION //defined in miniz, not defined in zlib
+ #define MZ_UBER_COMPRESSION 9
+#endif
+
+void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src_buffer, unsigned long src_len, int gzLevel) {
     //create gz file in RAM, save to disk http://www.zlib.net/zlib_how.html
     // in general this single-threaded approach is slower than PIGZ but is useful for slow (network attached) disk drives
     char fname[2048] = {""};
@@ -867,8 +1002,12 @@ void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src
     strm.opaque = Z_NULL;
     strm.next_out = pCmp; // output char array
     strm.avail_out = (unsigned int)cmp_len; // size of output
-    //if ( deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY)!= Z_OK) return;
-    if (deflateInit(&strm, Z_DEFAULT_COMPRESSION)!= Z_OK) {
+    int zLevel = Z_DEFAULT_COMPRESSION;
+    if ((gzLevel > 0) && (gzLevel < 11))
+    	zLevel = gzLevel;
+    if (zLevel > MZ_UBER_COMPRESSION)
+    	zLevel = MZ_UBER_COMPRESSION;
+    if (deflateInit(&strm, zLevel)!= Z_OK) {
         free(pCmp);
         return;
     }
@@ -928,13 +1067,91 @@ void writeNiiGz (char * baseName, struct nifti_1_header hdr,  unsigned char* src
 } //writeNiiGz()
 #endif
 
+#ifdef HAVE_R
+
+// Version of nii_saveNII() for R/divest: create nifti_image pointer and push onto stack
+int nii_saveNII (char *niiFilename, struct nifti_1_header hdr, unsigned char *im, struct TDCMopts opts)
+{
+    hdr.vox_offset = 352;
+
+    // Extract the basename from the full file path
+    // R always uses '/' as the path separator, so this should work on all platforms
+    char *start = niiFilename + strlen(niiFilename);
+    while (*start != '/')
+        start--;
+    std::string name(++start);
+
+    nifti_image *image = nifti_convert_nhdr2nim(hdr, niiFilename);
+    if (image == NULL)
+        return EXIT_FAILURE;
+    image->data = (void *) im;
+
+    ImageList *images = (ImageList *) opts.imageList;
+    images->append(image, name);
+
+    free(image);
+    return EXIT_SUCCESS;
+}
+
+void nii_saveAttributes (struct TDICOMdata &data, struct nifti_1_header &header, struct TDCMopts &opts)
+{
+    ImageList *images = (ImageList *) opts.imageList;
+    switch (data.manufacturer) {
+        case kMANUFACTURER_SIEMENS:
+        	images->addAttribute("manufacturer", "Siemens");
+        	break;
+        case kMANUFACTURER_GE:
+        	images->addAttribute("manufacturer", "GE");
+        	break;
+        case kMANUFACTURER_PHILIPS:
+        	images->addAttribute("manufacturer", "Philips");
+        	break;
+        case kMANUFACTURER_TOSHIBA:
+        	images->addAttribute("manufacturer", "Toshiba");
+        	break;
+    }
+    if (strlen(data.manufacturersModelName) > 0)
+        images->addAttribute("scannerModelName", data.manufacturersModelName);
+    if (strlen(data.imageType) > 0)
+        images->addAttribute("imageType", data.imageType);
+    if (strlen(data.studyDate) >= 8 && strcmp(data.studyDate,"00000000") != 0)
+        images->addDateAttribute("studyDate", data.studyDate);
+    if (strlen(data.studyTime) > 0 && strncmp(data.studyTime,"000000",6) != 0)
+        images->addAttribute("studyTime", data.studyTime);
+    if (data.fieldStrength > 0.0)
+        images->addAttribute("fieldStrength", data.fieldStrength);
+    if (data.flipAngle > 0.0)
+        images->addAttribute("flipAngle", data.flipAngle);
+    if (data.TE > 0.0)
+        images->addAttribute("echoTime", data.TE);
+    if (data.TR > 0.0)
+        images->addAttribute("repetitionTime", data.TR);
+    if ((data.CSA.bandwidthPerPixelPhaseEncode > 0.0) && (header.dim[2] > 0) && (header.dim[1] > 0)) {
+        if (data.phaseEncodingRC =='C')
+            images->addAttribute("dwellTime", 1.0/data.CSA.bandwidthPerPixelPhaseEncode/header.dim[2]);
+        else if (data.phaseEncodingRC == 'R')
+            images->addAttribute("dwellTime", 1.0/data.CSA.bandwidthPerPixelPhaseEncode/header.dim[1]);
+    }
+    if (data.phaseEncodingRC == 'C')
+        images->addAttribute("phaseEncodingDirection", "j");
+    else if (data.phaseEncodingRC == 'R')
+        images->addAttribute("phaseEncodingDirection", "i");
+    if (data.CSA.phaseEncodingDirectionPositive != -1)
+        images->addAttribute("phaseEncodingSign", data.CSA.phaseEncodingDirectionPositive == 0 ? -1 : 1);
+}
+
+#else
+
 int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) {
     hdr.vox_offset = 352;
     size_t imgsz = nii_ImgBytes(hdr);
+    if (imgsz < 1) {
+    	printMessage("Error: Image size is zero bytes %s\n", niiFilename);
+    	return EXIT_FAILURE;
+    }
     #ifndef myDisableZLib
     if  ((opts.isGz) &&  (strlen(opts.pigzname)  < 1) &&  ((imgsz+hdr.vox_offset) <  2147483647) ) { //use internal compressor
-    //if (true) {//TPX
-        writeNiiGz (niiFilename, hdr,  im,imgsz);
+        writeNiiGz (niiFilename, hdr,  im, imgsz, opts.gzLevel);
         return EXIT_SUCCESS;
     }
     #endif
@@ -952,7 +1169,12 @@ int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im
     	char command[768];
     	strcpy(command, "\"" );
         strcat(command, opts.pigzname );
-        strcat(command, "\" -n -f \""); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\'
+        if ((opts.gzLevel > 0) &&  (opts.gzLevel < 12)) {
+        	char newstr[256];
+        	sprintf(newstr, "\" -n -f -%d \"", opts.gzLevel);
+        	strcat(command, newstr);
+        } else
+        	strcat(command, "\" -n -f \""); //current versions of pigz (2.3) built on Windows can hang if the filename is included, presumably because it is not finding the path characters ':\'
         strcat(command, fname);
         strcat(command, "\""); //add quotes in case spaces in filename 'pigz "c:\my dir\img.nii"'
     	#if defined(_WIN64) || defined(_WIN32) //using CreateProcess instead of system to run in background (avoids screen flicker)
@@ -962,24 +1184,22 @@ int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im
    		startupInfo.cb = sizeof(startupInfo);
     		//StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
     		if(CreateProcess(NULL, command, NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,NULL, NULL,&startupInfo,&ProcessInfo)) {
-                //printf("compression --- %s\n",command);
+                //printMessage("compression --- %s\n",command);
         		WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
         		CloseHandle(ProcessInfo.hThread);
         		CloseHandle(ProcessInfo.hProcess);
     		} else
-    			printf("compression failed %s\n",command);
+    			printMessage("compression failed %s\n",command);
     	#else //if win else linux
         system(command);
         #endif //else linux
-        #ifdef myUseCOut
-    	std::cout<<"compress: "<<command<<std::endl;
-		#else
-        printf("compress: %s\n",command);
-        #endif
+        printMessage("compress: %s\n",command);
     }
     return EXIT_SUCCESS;
 }// nii_saveNII()
 
+#endif
+
 int nii_saveNII3D(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) {
     //save 4D series as sequence of 3D volumes
     struct nifti_1_header hdr1 = hdr;
@@ -1018,9 +1238,10 @@ void nii_check16bitUnsigned(unsigned char *img, struct nifti_1_header *hdr){
     for (int i=0; i < nVox; i++)
         if (img16[i] > max16)
             max16 = img16[i];
-    //printf("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000);
-    if (max16 > 32767)
-        printf("Note: rare 16-bit UNSIGNED integer image. Older tools may require 32-bit conversion\n");
+    //printMessage("max16= %d vox=%d %fms\n",max16, nVox, ((double)(clock()-start))/1000);
+    if (max16 > 32767) {
+        printMessage("Note: rare 16-bit UNSIGNED integer image. Older tools may require 32-bit conversion\n");
+    }
     else
         hdr->datatype = DT_INT16;
 } //nii_check16bitUnsigned()
@@ -1034,11 +1255,7 @@ int siemensCtKludge(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dc
     for (int i = 1; i < nConvert; i++) {
         float dx = intersliceDistance(dcmList[indx0],dcmList[dcmSort[i].indx]);
         if ((!isSameFloat(dx,0.0f)) && (dx < prevDx)) {
-            #ifdef myUseCOut
-            std::cout<<"Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)"<<std::endl;
-            #else
-            printf("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n");
-            #endif
+            printMessage("Slices skipped: image position not sequential, admonish your vendor (Siemens OOG?)\n");
             return i;
         }
         prevDx = dx;
@@ -1057,10 +1274,10 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
     int nVox2DIn = hdrIn.dim[1]*hdrIn.dim[2];
     if ((nVox2DIn < 1) || (hdrIn.dim[0] != 3) || (hdrIn.dim[3] < 3)) return im;
     if (hdrIn.datatype != DT_INT16) {
-        printf("Only able to correct gantry tilt for 16-bit integer data with at least 3 slices.");
+        printMessage("Only able to correct gantry tilt for 16-bit integer data with at least 3 slices.");
         return im;
     }
-    printf("Gantry Tilt Correction is new: please validate conversions\n");
+    printMessage("Gantry Tilt Correction is new: please validate conversions\n");
     float GNTtanPx = tan(gantryTiltDeg / (180/M_PI))/hdrIn.pixdim[2]; //tangent(degrees->radian)
     //unintuitive step: reverse sign for negative gantry tilt, therefore -27deg == +27deg (why @!?#)
     // seen in http://www.mathworks.com/matlabcentral/fileexchange/28141-gantry-detector-tilt-correction/content/gantry2.m
@@ -1073,7 +1290,7 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
         ; //do nothing
     else
         if (gantryTiltDeg < 0.0) GNTtanPx = - GNTtanPx; //see Toshiba examples from John Muschelli
-    // printf("gantry tilt pixels per mm %g\n",GNTtanPx);
+    // printMessage("gantry tilt pixels per mm %g\n",GNTtanPx);
     short * imIn16 = ( short*) im;
 	//create new output image: larger due to skew
 	// compute how many pixels slice must be extended due to skew
@@ -1081,7 +1298,7 @@ unsigned char * nii_saveNII3Dtilt(char * niiFilename, struct nifti_1_header * hd
     float maxSliceMM = fabs(s * hdrIn.pixdim[3]);
     if (sliceMMarray != NULL) maxSliceMM = fabs(sliceMMarray[s]);
     int pxOffset = ceil(fabs(GNTtanPx*maxSliceMM));
-    // printf("Tilt extends slice by %d pixels", pxOffset);
+    // printMessage("Tilt extends slice by %d pixels", pxOffset);
 	hdr->dim[2] = hdr->dim[2] + pxOffset;
 	int nVox2D = hdr->dim[1]*hdr->dim[2];
 	unsigned char * imOut = (unsigned char *)malloc(nVox2D * hdrIn.dim[3] * 2);// *2 as 16-bits per voxel, sizeof( short) );
@@ -1133,7 +1350,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
     int nVox2D = hdr.dim[1]*hdr.dim[2];
     if ((nVox2D < 1) || (hdr.dim[0] != 3) ) return EXIT_FAILURE;
     if ((hdr.datatype != DT_UINT8) && (hdr.datatype != DT_RGB24) && (hdr.datatype != DT_INT16)) {
-        printf("Only able to make equidistant slices from 3D 8,16,24-bit volumes with at least 3 slices.");
+        printMessage("Only able to make equidistant slices from 3D 8,16,24-bit volumes with at least 3 slices.");
         return EXIT_FAILURE;
     }
     float mn = sliceMMarray[1] - sliceMMarray[0];
@@ -1144,7 +1361,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
             mn = sliceMMarray[i] - sliceMMarray[i-1];
     }
     if (mn <= 0.0f) {
-    	printf("Unable to equalize slice distances: slice number not consistent with slice position.\n");
+    	printMessage("Unable to equalize slice distances: slice number not consistent with slice position.\n");
     	return EXIT_FAILURE;
     }
     int slices = hdr.dim[3];
@@ -1153,7 +1370,7 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
         slices = 2 * hdr.dim[3];
         mn = (sliceMMarray[hdr.dim[3]-1]) / (slices-1);
     }
-    //printf("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]);
+    //printMessage("-->%g mn slices %d orig %d\n", mn, slices, hdr.dim[3]);
     if (slices < 3) return EXIT_FAILURE;
     struct nifti_1_header hdrX = hdr;
     hdrX.dim[3] = slices;
@@ -1228,6 +1445,45 @@ int nii_saveNII3Deq(char * niiFilename, struct nifti_1_header hdr, unsigned char
     return EXIT_SUCCESS;
 }// nii_saveNII3Deq()
 
+float PhilipsPreciseVal (float lPV, float lRS, float lRI, float lSS) {
+    if ((lRS*lSS) == 0) //avoid divide by zero
+        return 0.0;
+    else
+        return (lPV * lRS + lRI) / (lRS * lSS);
+}
+
+void PhilipsPrecise (struct TDICOMdata * d, bool isPhilipsFloatNotDisplayScaling, struct nifti_1_header *h) {
+	if ((d->intenScalePhilips == 0) || (d->manufacturer != kMANUFACTURER_PHILIPS)) return; //not Philips
+	//we will report calibrated "FP" values http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3998685/
+	float l0 = PhilipsPreciseVal (0, d->intenScale, d->intenIntercept, d->intenScalePhilips);
+	float l1 = PhilipsPreciseVal (1, d->intenScale, d->intenIntercept, d->intenScalePhilips);
+	float intenScaleP = d->intenScale;
+	float intenInterceptP = d->intenIntercept;
+	if (l0 != l1) {
+		intenInterceptP = l0;
+		intenScaleP = l1-l0;
+	}
+	if (isSameFloat(d->intenIntercept,intenInterceptP) && isSameFloat(d->intenScale, intenScaleP)) return; //same result for both methods: nothing to do or report!
+	printMessage("Philips Precise RS:RI:SS = %g:%g:%g (see PMC3998685)\n",d->intenScale,d->intenIntercept,d->intenScalePhilips);
+	printMessage(" R = raw value, P = precise value, D = displayed value\n");
+	printMessage(" RS = rescale slope, RI = rescale intercept,  SS = scale slope\n");
+	printMessage(" D = R * RS + RI    , P = D/(RS * SS)\n");
+	printMessage(" D scl_slope:scl_inter = %g:%g\n", d->intenScale,d->intenIntercept);
+	printMessage(" P scl_slope:scl_inter = %g:%g\n", intenScaleP,intenInterceptP);
+	//#define myUsePhilipsPrecise
+	if (isPhilipsFloatNotDisplayScaling) {
+		printMessage(" Using P values ('-p n ' for D values)\n");
+		//to change DICOM:
+		//d->intenScale = intenScaleP;
+		//d->intenIntercept = intenInterceptP;
+		//to change NIfTI
+    	h->scl_slope = intenScaleP;
+    	h->scl_inter = intenInterceptP;
+    	d->intenScalePhilips = 0; //so we never run this TWICE!
+	} else
+		printMessage(" Using D values ('-p y ' for P values)\n");
+} //PhilipsPrecise()
+
 void smooth1D(int num, double * im) {
 	if (num < 3) return;
 	double * src = (double *) malloc(sizeof(double)*num);
@@ -1238,13 +1494,13 @@ void smooth1D(int num, double * im) {
 	free(src);
 }// smooth1D()
 
-void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) {
+int nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts) {
     //remove excess neck slices - assumes output of nii_setOrtho()
     int nVox2D = hdr.dim[1]*hdr.dim[2];
-    if ((nVox2D < 1) || (fabs(hdr.pixdim[3]) < 0.001) || (hdr.dim[0] != 3) || (hdr.dim[3] < 128)) return;
+    if ((nVox2D < 1) || (fabs(hdr.pixdim[3]) < 0.001) || (hdr.dim[0] != 3) || (hdr.dim[3] < 128)) return EXIT_FAILURE;
     if ((hdr.datatype != DT_INT16) && (hdr.datatype != DT_UINT16)) {
-        printf("Only able to crop 16-bit volumes.");
-        return;
+        printMessage("Only able to crop 16-bit volumes.");
+        return EXIT_FAILURE;
     }
 	short * im16 = ( short*) im;
 	unsigned short * imu16 = (unsigned short*) im;
@@ -1275,18 +1531,17 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
     }
     if (maxSliceVal <= 0) {
     	free(sliceSums);
-    	return;
+    	return EXIT_FAILURE;
     }
     smooth1D(slices, sliceSums);
-    for (int i = 0; i  < slices; i++)
-    	sliceSums[i] = sliceSums[i] / maxSliceVal; //so brightest slice has value 1
+    for (int i = 0; i  < slices; i++) sliceSums[i] = sliceSums[i] / maxSliceVal; //so brightest slice has value 1
 	//dorsal crop: eliminate slices with more than 5% brightness
 	int dorsalCrop;
 	for (dorsalCrop = (slices-1); dorsalCrop >= 1; dorsalCrop--)
 		if (sliceSums[dorsalCrop-1] > kThresh) break;
 	if (dorsalCrop <= 1) {
 		free(sliceSums);
-		return;
+		return EXIT_FAILURE;
 	}
 	/*
 	//find brightest band within 90mm of top of head
@@ -1300,7 +1555,7 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
     ventralMaxSlice = maxSlice - round(45 /fabs(hdr.pixdim[3])); //gap at least 60mm
     if (ventralMaxSlice < 0) {
     	free(sliceSums);
-    	return;
+    	return EXIT_FAILURE;
     }
     int ventralMinSlice = maxSlice - round(90/fabs(hdr.pixdim[3])); //gap no more than 120mm
     if (ventralMinSlice < 0) ventralMinSlice = 0;
@@ -1312,12 +1567,12 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
     for (int i = ventralMaxSlice; i  < maxSlice; i++)
     	if (sliceSums[i] < sliceSums[minSlice])
     		minSlice = i;
-    //printf("%d %d %d\n", ventralMaxSlice, minSlice, maxSlice);
+    //printMessage("%d %d %d\n", ventralMaxSlice, minSlice, maxSlice);
 	int gap = round((maxSlice-minSlice)*0.8);//add 40% for cerebellum
 	if ((minSlice-gap) > 1)
         ventralCrop = minSlice-gap;
 	free(sliceSums);
-	if (ventralCrop > dorsalCrop) return;
+	if (ventralCrop > dorsalCrop) return EXIT_FAILURE;
 	//FindDVCrop2
 	const double kMaxDVmm = 180.0;
     double sliceMM = hdr.pixdim[3] * (dorsalCrop-ventralCrop);
@@ -1330,7 +1585,7 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
     ventralCrop = dorsalCrop - round( kMaxDVmm / hdr.pixdim[3]);
     if (ventralCrop < 0) ventralCrop = 0;
 	//apply crop
-	printf(" Cropping from slice %d to %d (of %d)\n", ventralCrop, dorsalCrop, slices);
+	printMessage(" Cropping from slice %d to %d (of %d)\n", ventralCrop, dorsalCrop, slices);
     struct nifti_1_header hdrX = hdr;
     slices = dorsalCrop - ventralCrop + 1;
     hdrX.dim[3] = slices;
@@ -1352,9 +1607,9 @@ void nii_saveCrop(char * niiFilename, struct nifti_1_header hdr, unsigned char*
     char niiFilenameCrop[2048] = {""};
     strcat(niiFilenameCrop,niiFilename);
     strcat(niiFilenameCrop,"_Crop");
-    nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts);
+    const int returnCode = nii_saveNII3D(niiFilenameCrop, hdrX, imX, opts);
     free(imX);
-    return;
+    return returnCode;
 }// nii_saveCrop()
 
 int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmList[], struct TSearchList *nameList, struct TDCMopts opts, struct TDTI4D *dti4D) {
@@ -1362,25 +1617,34 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
     float *sliceMMarray = NULL; //only used if slices are not equidistant
     uint64_t indx = dcmSort[0].indx;
     uint64_t indx0 = dcmSort[0].indx;
+    if (opts.isIgnoreDerivedAnd2D && isDerived(dcmList[indx])) {
+    	printMessage("Ignoring derived image(s) of series %ld %s\n", dcmList[indx].seriesNum,  nameList->str[indx]);
+    	return EXIT_SUCCESS;
+    }
+    if ((opts.isIgnoreDerivedAnd2D) && (strcmp(dcmList[indx].sequenceName, "_fl2d1")== 0)) {
+    	printMessage("Ignoring localizer (sequence %s) of series %ld %s\n", dcmList[indx].sequenceName, dcmList[indx].seriesNum,  nameList->str[indx]);
+    	return EXIT_SUCCESS;
+    }
+    if ((opts.isIgnoreDerivedAnd2D) && (nConvert < 2) && (dcmList[indx].xyzDim[3] < 2)) {
+    	printMessage("Ignoring 2D image of series %ld %s\n", dcmList[indx].seriesNum,  nameList->str[indx]);
+    	return EXIT_SUCCESS;
+    }
     bool saveAs3D = dcmList[indx].isHasPhase;
     struct nifti_1_header hdr0;
     unsigned char * img = nii_loadImgXL(nameList->str[indx], &hdr0,dcmList[indx], iVaries, opts.compressFlag, opts.isVerbose);
     if (opts.isVerbose)
-    #ifdef myUseCOut
-    	std::cout<<"Converting "<<nameList->str[indx]<<std::endl;
-	#else
-        printf("Converting %s\n",nameList->str[indx]);
-    #endif
+        printMessage("Converting %s\n",nameList->str[indx]);
     if (img == NULL) return EXIT_FAILURE;
     //if (iVaries) img = nii_iVaries(img, &hdr0);
     size_t imgsz = nii_ImgBytes(hdr0);
     unsigned char *imgM = (unsigned char *)malloc(imgsz* (uint64_t)nConvert);
     memcpy(&imgM[0], &img[0], imgsz);
     free(img);
-    //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]);
+    //printMessage(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]);
     if (nConvert > 1) {
+        //next: determine gantry tilt
         if (dcmList[indx0].gantryTilt != 0.0f)
-            printf(" Warning: note these images have gantry tilt of %g degrees (manufacturer ID = %d)\n", dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer);
+            printMessage(" Warning: note these images have gantry tilt of %g degrees (manufacturer ID = %d)\n", dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer);
         if (hdr0.dim[3] < 2) {
             //stack volumes with multiple acquisitions
             int nAcq = 1;
@@ -1399,7 +1663,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
             }
             /*int nImg = 1+abs( dcmList[dcmSort[nConvert-1].indx].imageNum-dcmList[dcmSort[0].indx].imageNum);
             if (((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) && (nImg == nConvert) && (dcmList[dcmSort[0].indx].locationsInAcquisition == 0) ) {
-                printf(" stacking %d acquisitions as a single volume\n", nAcq);
+                printMessage(" stacking %d acquisitions as a single volume\n", nAcq);
                 //some Siemens CT scans use multiple acquisitions for a single volume, perhaps also check that slice position does not repeat?
                 hdr0.dim[3] = nConvert;
             } else*/ if ( (nAcq > 1) && ((nConvert/nAcq) > 1) && ((nConvert%nAcq)==0) ) {
@@ -1409,7 +1673,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
             } else {
                 hdr0.dim[3] = nConvert;
                 if (nAcq > 1) {
-                    printf("Slice positions repeated, but number of slices (%d) not divisible by number of repeats (%d): missing images?\n", nConvert, nAcq);
+                    printMessage("Slice positions repeated, but number of slices (%d) not divisible by number of repeats (%d): missing images?\n", nConvert, nAcq);
                 }
             }
             float dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]);
@@ -1421,25 +1685,25 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
                 if (dxVaries) {
                     sliceMMarray = (float *) malloc(sizeof(float)*nConvert);
                     sliceMMarray[0] = 0.0f;
-                    printf("Dims %d %d %d %d %d\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], nAcq);
-                    printf("Warning: interslice distance varies in this volume (incompatible with NIfTI format).\n");
-                    printf(" Distance from first slice:\n");
-                    printf("dx=[0");
+                    printMessage("Dims %d %d %d %d %d\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], nAcq);
+                    printWarning("Interslice distance varies in this volume (incompatible with NIfTI format).\n");
+                    printMessage(" Distance from first slice:\n");
+                    printMessage("dx=[0");
                     for (int i = 1; i < nConvert; i++) {
                         float dx0 = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[i].indx]);
-                        printf(" %g", dx0);
+                        printMessage(" %g", dx0);
                         sliceMMarray[i] = dx0;
                     }
-                    printf("]\n");
+                    printMessage("]\n");
                 }
             }
             if ((hdr0.dim[4] > 0) && (dxVaries) && (dx == 0.0) &&  ((dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_GE)  || (dcmList[dcmSort[0].indx].manufacturer == kMANUFACTURER_PHILIPS))  ) { //Niels Janssen has provided GE sequential multi-phase acquisitions that also require swizzling
                 swapDim3Dim4(hdr0.dim[3],hdr0.dim[4],dcmSort);
                 dx = intersliceDistance(dcmList[dcmSort[0].indx],dcmList[dcmSort[1].indx]);
-                printf("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx);
+                printMessage("swizzling 3rd and 4th dimensions (XYTZ -> XYZT), assuming interslice distance is %f\n",dx);
             }
             if ((dx == 0.0 ) && (!dxVaries)) { //all images are the same slice - 16 Dec 2014
-                printf(" Warning: all images appear to be a single slice - please check slice/vector orientation\n");
+                printMessage(" Warning: all images appear to be a single slice - please check slice/vector orientation\n");
                 hdr0.dim[3] = 1;
                 hdr0.dim[4] = nConvert;
                 hdr0.dim[0] = 4;
@@ -1454,7 +1718,20 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
             hdr0.dim[5] = nConvert;
             hdr0.dim[0] = 5;
         }
-        //printf(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]);
+        /*if (nConvert > 1) { //next determine if TR is true time between volumes
+        	double startTime = dcmList[indx0].acquisitionTime;
+        	double endTime = startTime;
+        	for (int i = 1; i < nConvert; i++) {
+            	double sliceTime = dcmList[dcmSort[i].indx].acquisitionTime;
+            	if (sliceTime < startTime) startTime = sliceTime;
+            	if (sliceTime > endTime) endTime = sliceTime;
+            }
+            double seriesTime = (endTime - startTime);
+            if (endTime > 0)
+            	printMessage("%g - %g = %g\n", endTime, startTime, seriesTime);
+
+        }*/
+        //printMessage(" %d %d %d %d %lu\n", hdr0.dim[1], hdr0.dim[2], hdr0.dim[3], hdr0.dim[4], (unsigned long)[imgM length]);
         struct nifti_1_header hdrI;
         for (int i = 1; i < nConvert; i++) { //stack additional images
             indx = dcmSort[i].indx;
@@ -1462,11 +1739,7 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
             img = nii_loadImgXL(nameList->str[indx], &hdrI, dcmList[indx],iVaries, opts.compressFlag, opts.isVerbose);
             if (img == NULL) return EXIT_FAILURE;
             if ((hdr0.dim[1] != hdrI.dim[1]) || (hdr0.dim[2] != hdrI.dim[2]) || (hdr0.bitpix != hdrI.bitpix)) {
-                    #ifdef myUseCOut
-    	std::cout<<"Error: image dimensions differ "<<nameList->str[dcmSort[0].indx]<<"  "<<nameList->str[indx]<<std::endl;
-		#else
-                printf("Error: image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]);
-                #endif
+                printError("Image dimensions differ %s %s",nameList->str[dcmSort[0].indx], nameList->str[indx]);
                 free(imgM);
                 free(img);
                 return EXIT_FAILURE;
@@ -1485,14 +1758,12 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
         return EXIT_FAILURE;
     }
     int sliceDir = 0;
-    if (hdr0.dim[3] > 1)
-        sliceDir = headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
+    if (hdr0.dim[3] > 1)sliceDir = headerDcm2Nii2(dcmList[dcmSort[0].indx],dcmList[dcmSort[nConvert-1].indx] , &hdr0);
 	//UNCOMMENT NEXT TWO LINES TO RE-ORDER MOSAIC WHERE CSA's protocolSliceNumber does not start with 1
 	if (dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1 > 1) {
-		printf("WARNING: WEIRD CSA 'ProtocolSliceNumber': SPATIAL, SLICE-ORDER AND DTI TRANSFORMS UNTESTED\n");
+		printWarning("Weird CSA 'ProtocolSliceNumber' (%d): SPATIAL, SLICE-ORDER AND DTI TRANSFORMS UNTESTED\n", dcmList[dcmSort[0].indx].CSA.protocolSliceNumber1);
 		//see https://github.com/neurolabusc/dcm2niix/issues/40
 		sliceDir = -1; //not sure how to handle negative determinants?
-
 	}
 	if (sliceDir < 0) {
         imgM = nii_flipZ(imgM, &hdr0);
@@ -1500,67 +1771,76 @@ int saveDcm2Nii(int nConvert, struct TDCMsort dcmSort[],struct TDICOMdata dcmLis
     }
     nii_SaveBIDS(pathoutname, dcmList[dcmSort[0].indx], opts, dti4D, &hdr0);
 	nii_SaveText(pathoutname, dcmList[dcmSort[0].indx], opts, &hdr0, nameList->str[indx]);
-    int numFinalADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D);
-    numFinalADC = numFinalADC; //simply to silence compiler warning when myNoSave defined
+    bool * isADC = nii_SaveDTI(pathoutname,nConvert, dcmSort, dcmList, opts, sliceDir, dti4D);
     if ((hdr0.datatype == DT_UINT16) &&  (!dcmList[dcmSort[0].indx].isSigned)) nii_check16bitUnsigned(imgM, &hdr0);
-    #ifdef myUseCOut
-     std::cout<<"Convert "<<nConvert<<" DICOM as "<<pathoutname<<
-     	" ("<<hdr0.dim[1]<<"x"<<hdr0.dim[2]<<"x"<<hdr0.dim[3]<<"x"<<hdr0.dim[4]<<")" <<std::endl;
-    #else
-    printf( "Convert %d DICOM as %s (%dx%dx%dx%d)\n",  nConvert, pathoutname, hdr0.dim[1],hdr0.dim[2],hdr0.dim[3],hdr0.dim[4]);
-    #endif
+    printMessage( "Convert %d DICOM as %s (%dx%dx%dx%d)\n",  nConvert, pathoutname, hdr0.dim[1],hdr0.dim[2],hdr0.dim[3],hdr0.dim[4]);
+    PhilipsPrecise(&dcmList[dcmSort[0].indx], opts.isPhilipsFloatNotDisplayScaling, &hdr0);
+
+    if (!dcmList[dcmSort[0].indx].isSlicesSpatiallySequentialPhilips)
+    	nii_reorderSlices(imgM, &hdr0, dti4D);
     if (hdr0.dim[3] < 2)
-    #ifdef myUseCOut
-    	std::cout<<"WARNING: check that 2D images are not mirrored"<<std::endl;
-		#else
-        printf("WARNING: check that 2D images are not mirrored.\n");
-        #endif
+    	printWarning("Check that 2D images are not mirrored.\n");
+#ifndef HAVE_R
     else
         fflush(stdout); //GUI buffers printf, display all results
+#endif
     if ((dcmList[dcmSort[0].indx].is3DAcq) && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4))
-        imgM = nii_setOrtho(imgM, &hdr0); //printf("ortho %d\n", echoInt (33));
+        imgM = nii_setOrtho(imgM, &hdr0); //printMessage("ortho %d\n", echoInt (33));
     else if (opts.isFlipY)//(FLIP_Y) //(dcmList[indx0].CSA.mosaicSlices < 2) &&
         imgM = nii_flipY(imgM, &hdr0);
     else
-    #ifdef myUseCOut
-    	std::cout<<"DICOM row order preserved: may appear upside down in tools that ignore spatial transforms"<<std::endl;
-		#else
-        printf("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n");
-        #endif
+    	printMessage("DICOM row order preserved: may appear upside down in tools that ignore spatial transforms\n");
 #ifndef myNoSave
-    //printf(" x--> %d ----\n", nConvert);
+    // Indicates success or failure of the (last) save
+    int returnCode = EXIT_FAILURE;
+    //printMessage(" x--> %d ----\n", nConvert);
     if (! opts.isRGBplanar) //save RGB as packed RGBRGBRGB... instead of planar RRR..RGGG..GBBB..B
         imgM = nii_planar2rgb(imgM, &hdr0, true);
     if ((hdr0.dim[4] > 1) && (saveAs3D))
-        nii_saveNII3D(pathoutname, hdr0, imgM,opts);
+        returnCode = nii_saveNII3D(pathoutname, hdr0, imgM,opts);
     else {
-        if ((numFinalADC > 0) && (hdr0.dim[4] > (numFinalADC+1))) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+        if (isADC) { //ADC maps can disrupt analysis: save a copy with the ADC map, and another without
+#ifndef HAVE_R
             char pathoutnameADC[2048] = {""};
             strcat(pathoutnameADC,pathoutname);
             strcat(pathoutnameADC,"_ADC");
             nii_saveNII(pathoutnameADC, hdr0, imgM, opts);
-            hdr0.dim[4] = hdr0.dim[4]-numFinalADC;
+#endif
+			imgM = removeADC(&hdr0, imgM, isADC);
+            //hdr0.dim[4] = hdr0.dim[4]-numFinalADC;
         };
-        nii_saveNII(pathoutname, hdr0, imgM, opts);
+#ifndef HAVE_R
+        returnCode = nii_saveNII(pathoutname, hdr0, imgM, opts);
+#endif
     }
 #endif
     if (dcmList[indx0].gantryTilt != 0.0) {
-        if (dcmList[indx0].isResampled)
-            printf("Tilt correction skipped: 0008,2111 reports RESAMPLED\n");
-        else if (opts.isTiltCorrect)
+        if (dcmList[indx0].isResampled) {
+            printMessage("Tilt correction skipped: 0008,2111 reports RESAMPLED\n");
+        } else if (opts.isTiltCorrect) {
             imgM = nii_saveNII3Dtilt(pathoutname, &hdr0, imgM,opts, sliceMMarray, dcmList[indx0].gantryTilt, dcmList[indx0].manufacturer);
-        else
-            printf("Tilt correction skipped\n");
+            strcat(pathoutname,"_Tilt");
+        } else
+            printMessage("Tilt correction skipped\n");
     }
     if (sliceMMarray != NULL) {
-        if (dcmList[indx0].isResampled)
-            printf("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n");
+        if (dcmList[indx0].isResampled) {
+            printMessage("Slice thickness correction skipped: 0008,2111 reports RESAMPLED\n");
+        }
         else
-            nii_saveNII3Deq(pathoutname, hdr0, imgM,opts, sliceMMarray);
+            returnCode = nii_saveNII3Deq(pathoutname, hdr0, imgM,opts, sliceMMarray);
         free(sliceMMarray);
     }
     if ((opts.isCrop) && (dcmList[indx0].is3DAcq)   && (hdr0.dim[3] > 1) && (hdr0.dim[0] < 4))//for T1 scan: && (dcmList[indx0].TE < 25)
-    	nii_saveCrop(pathoutname, hdr0, imgM,opts); //n.b. must be run AFTER nii_setOrtho()!
+        returnCode = nii_saveCrop(pathoutname, hdr0, imgM,opts); //n.b. must be run AFTER nii_setOrtho()!
+
+#ifdef HAVE_R
+    // Note that for R, only one image should be created per series
+    // Hence the logical OR here
+    if (returnCode == EXIT_SUCCESS || nii_saveNII(pathoutname,hdr0,imgM,opts) == EXIT_SUCCESS)
+        nii_saveAttributes(dcmList[dcmSort[0].indx], hdr0, opts);
+#endif
+
     free(imgM);
     return EXIT_SUCCESS;
 }// saveDcm2Nii()
@@ -1578,11 +1858,13 @@ int compareTDCMsort(void const *item1, void const *item2) {
 
 int isSameFloatGE (float a, float b) {
 //Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!!
+    //return (a == b); //niave approach does not have any tolerance for rounding errors
     return (fabs (a - b) <= 0.0001);
 }
 
 int isSameFloatDouble (double a, double b) {
     //Kludge for bug in 0002,0016="DIGITAL_JACKET", 0008,0070="GE MEDICAL SYSTEMS" DICOM data: Orient field (0020:0037) can vary 0.00604261 == 0.00604273 !!!
+    // return (a == b); //niave approach does not have any tolerance for rounding errors
     return (fabs (a - b) <= 0.0001);
 }
 
@@ -1612,45 +1894,47 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam
     #else
     if (d1.acquNum != d2.acquNum) {
         if (!warnings->acqNumVaries)
-        	printf("slices stacked despite varying acquisition numbers (if this is not desired please recompile)\n");
+        	printMessage("slices stacked despite varying acquisition numbers (if this is not desired please recompile)\n");
         warnings->acqNumVaries = true;
     }
     #endif
 	if ((d1.bitsAllocated != d2.bitsAllocated) || (d1.xyzDim[1] != d2.xyzDim[1]) || (d1.xyzDim[2] != d2.xyzDim[2]) || (d1.xyzDim[3] != d2.xyzDim[3]) ) {
         if (!warnings->bitDepthVaries)
-        	printf("slices not stacked: dimensions or bit-depth varies\n");
+        	printMessage("slices not stacked: dimensions or bit-depth varies\n");
         warnings->bitDepthVaries = true;
         return false;
     }
     if (isForceStackSameSeries) return true; //we will stack these images, even if they differ in the following attributes
     if (!isSameFloatDouble(d1.dateTime, d2.dateTime)) { //beware, some vendors incorrectly store Image Time (0008,0033) as Study Time (0008,0030).
     	if (!warnings->dateTimeVaries)
-    		printf("slices not stacked: Study Data/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime);
+    		printMessage("slices not stacked: Study Data/Time (0008,0020 / 0008,0030) varies %12.12f ~= %12.12f\n", d1.dateTime, d2.dateTime);
     	warnings->dateTimeVaries = true;
     	return false;
     }
     if ((d1.TE != d2.TE) || (d1.echoNum != d2.echoNum)) {
-        if (!warnings->echoVaries)
-        	printf("slices not stacked: echo varies (TE %g, %g; echo %d, %d)\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum );
+        if ((!warnings->echoVaries) && (d1.isXRay)) //for CT/XRay we check DICOM tag 0018,1152 (XRayExposure)
+        	printMessage("slices not stacked: X-Ray Exposure varies (%g, %g; number %d, %d)\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum );
+        if ((!warnings->echoVaries) && (!d1.isXRay)) //for MRI
+        	printMessage("slices not stacked: echo varies (TE %g, %g; echo %d, %d)\n", d1.TE, d2.TE,d1.echoNum, d2.echoNum );
         warnings->echoVaries = true;
         return false;
     }
     if (d1.coilNum != d2.coilNum) {
         if (!warnings->coilVaries)
-        	printf("slices not stacked: coil varies\n");
+        	printMessage("slices not stacked: coil varies\n");
         warnings->coilVaries = true;
         return false;
     }
     if ((strcmp(d1.protocolName, d2.protocolName) != 0)) {
         if ((!warnings->nameVaries))
-        	printf("slices not stacked: protocol name varies\n");
+        	printMessage("slices not stacked: protocol name varies\n");
         warnings->nameVaries = true;
         return false;
     }
     if ((!isSameFloatGE(d1.orient[1], d2.orient[1]) || !isSameFloatGE(d1.orient[2], d2.orient[2]) ||  !isSameFloatGE(d1.orient[3], d2.orient[3]) ||
     		!isSameFloatGE(d1.orient[4], d2.orient[4]) || !isSameFloatGE(d1.orient[5], d2.orient[5]) ||  !isSameFloatGE(d1.orient[6], d2.orient[6]) ) ) {
         if (!warnings->orientVaries)
-        	printf("slices not stacked: orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n",
+        	printMessage("slices not stacked: orientation varies (localizer?) [%g %g %g %g %g %g] != [%g %g %g %g %g %g]\n",
                d1.orient[1], d1.orient[2], d1.orient[3],d1.orient[4], d1.orient[5], d1.orient[6],
                d2.orient[1], d2.orient[2], d2.orient[3],d2.orient[4], d2.orient[5], d2.orient[6]);
         warnings->orientVaries = true;
@@ -1659,49 +1943,11 @@ bool isSameSet (struct TDICOMdata d1, struct TDICOMdata d2, bool isForceStackSam
     return true;
 }// isSameSet()
 
-/*
-#if defined(__APPLE__) && defined(__MACH__)
-void  convertForeign2Nii(char * fname, struct TDCMopts* opts) {//, struct TDCMopts opts) {
-
-    struct nifti_1_header niiHdr;
-    unsigned char * img =  nii_readForeignC(fname, &niiHdr, 0, 65535);
-    if (img == NULL) return;
-    char pth[1024] = {""};
-    if (strlen(opts->outdir) > 0) {
-        strcpy(pth, opts->outdir);
-        int w =access(pth,W_OK);
-        if (w != 0) {
-            if (getcwd(pth, sizeof(pth)) != NULL) {
-                w =access(pth,W_OK);
-                if (w != 0) {
-                    printf("Error: you do not have write permissions for the directory %s\n",opts->outdir);
-                    return;
-                }
-                printf("Warning: %s write permission denied. Saving to working directory %s \n", opts->outdir, pth);
-
-            }
-        }
-        char appendChar[2] = {"a"};
-        appendChar[0] = kPathSeparator;
-        if (pth[strlen(pth)-1] != kPathSeparator)
-            strcat (pth,appendChar);
-        char fn[1024] = {""};
-        getFileName(fn, fname);
-        strcat(pth,fn);
-    } else {
-        strcat(pth, fname);
-    }
-    printf("Converted foreign image '%s'\n",fname);
-    nii_saveNII(pth, niiHdr, img, *opts);
-    free(img);
-} //convertForeign2Nii()
-#endif */
-
 int singleDICOM(struct TDCMopts* opts, char *fname) {
     char filename[768] ="";
     strcat(filename, fname);
     if (isDICOMfile(filename) == 0) {
-        printf("Error: not a DICOM image : %s\n", filename);
+        printError("Not a DICOM image : %s\n", filename);
         return 0;
     }
     struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc( sizeof(struct  TDICOMdata));
@@ -1721,41 +1967,58 @@ int singleDICOM(struct TDCMopts* opts, char *fname) {
     return saveDcm2Nii(1, dcmSort, dcmList, &nameList, *opts, &dti4D);
 }// singleDICOM()
 
-void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth) {
+size_t fileBytes(const char * fname) {
+    FILE *fp = fopen(fname, "rb");
+	if (!fp)  return 0;
+	fseek(fp, 0, SEEK_END);
+	size_t fileLen = ftell(fp);
+    fclose(fp);
+    return fileLen;
+} //fileBytes()
+
+int strcicmp(char const *a, char const *b) //case insensitive compare
+{
+    for (;; a++, b++) {
+        int d = tolower(*a) - tolower(*b);
+        if (d != 0 || !*a)
+            return d;
+    }
+}// strcicmp()
+
+void searchDirForDICOM(char *path, struct TSearchList *nameList, int maxDepth, int depth, struct TDCMopts* opts ) {
     tinydir_dir dir;
     tinydir_open(&dir, path);
     while (dir.has_next) {
         tinydir_file file;
         file.is_dir = 0; //avoids compiler warning: this is set by tinydir_readfile
         tinydir_readfile(&dir, &file);
-        //printf("%s\n", file.name);
+        //printMessage("%s\n", file.name);
         char filename[768] ="";
         strcat(filename, path);
         strcat(filename,kFileSep);
         strcat(filename, file.name);
         if ((file.is_dir) && (depth < maxDepth) && (file.name[0] != '.'))
-            searchDirForDICOM(filename, nameList, maxDepth, depth+1);
-        else if (!file.is_reg) //ignore hidden files...
+            searchDirForDICOM(filename, nameList, maxDepth, depth+1, opts);
+        else if (!file.is_reg) //ignore files "." and ".."
             ;
+        else if ((strlen(file.name) < 1) || (file.name[0]=='.'))
+        	; //printMessage("skipping hidden file %s\n", file.name);
+        else if ((strlen(file.name) == 8) && (strcicmp(file.name, "DICOMDIR") == 0))
+        	; //printMessage("skipping DICOMDIR\n");
         else if (isDICOMfile(filename) > 0) {
             if (nameList->numItems < nameList->maxItems) {
                 nameList->str[nameList->numItems]  = (char *)malloc(strlen(filename)+1);
                 strcpy(nameList->str[nameList->numItems],filename);
-                //printf("OK\n");
+                //printMessage("OK\n");
             }
             nameList->numItems++;
-            //printf("dcm %lu %s \n",nameList->numItems, filename);
+            //printMessage("dcm %lu %s \n",nameList->numItems, filename);
         } else {
-            #if defined(__APPLE__) && defined(__MACH__)
-            //convertForeign2Nii(filename, opts);
-            #endif
-        #ifdef MY_DEBUG
-            #ifdef myUseCOut
-                std::cout<<"Not a dicom"<< filename <<std::endl;
-            #else
-                printf("Not a dicom:\t%s\n", filename);
-            #endif
-        #endif
+        	if (fileBytes(filename) > 2048)
+            	convert_foreign (filename, *opts);
+        	#ifdef MY_DEBUG
+            	printMessage("Not a dicom:\t%s\n", filename);
+        	#endif
         }
         tinydir_next(&dir);
     }
@@ -1775,11 +2038,7 @@ int removeDuplicates(int nConvert, struct TDCMsort dcmSort[]){
         }
     }
     if (nDuplicates > 0)
-        #ifdef myUseCOut
-    	std::cout<<"Some images have identical time, series, acquisition and image values. DUPLICATES REMOVED."<<std::endl;
-		#else
-    	printf("Some images have identical time, series, acquisition and image values. DUPLICATES REMOVED.\n");
-    	#endif
+        printMessage("Some images have identical time, series, acquisition and image values. DUPLICATES REMOVED.\n");
     return nConvert - nDuplicates;
 }// removeDuplicates()
 
@@ -1789,35 +2048,18 @@ int removeDuplicatesVerbose(int nConvert, struct TDCMsort dcmSort[], struct TSea
     int nDuplicates = 0;
     for (int i = 1; i < nConvert; i++) {
         if (dcmSort[i].img == dcmSort[i-1].img) {
-                #ifdef myUseCOut
-    	std::cout<<"\t"<<nameList->str[dcmSort[i-1].indx]<<"\t=\t"<<nameList->str[dcmSort[i].indx] <<std::endl;
-		#else
-            printf("\t%s\t=\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
-            #endif
+            printMessage("\t%s\t=\t%s\n",nameList->str[dcmSort[i-1].indx],nameList->str[dcmSort[i].indx]);
             nDuplicates ++;
-        }else {
+        } else {
             dcmSort[i-nDuplicates].img = dcmSort[i].img;
             dcmSort[i-nDuplicates].indx = dcmSort[i].indx;
         }
     }
     if (nDuplicates > 0)
-            #ifdef myUseCOut
-    	std::cout<<"Some images have identical time, series, acquisition and image values. Duplicates removed."<<std::endl;
-		#else
-    	printf("Some images have identical time, series, acquisition and image values. Duplicates removed.\n");
-    	#endif
-    	return nConvert - nDuplicates;
+        printMessage("Some images have identical time, series, acquisition and image values. Duplicates removed.\n");
+    return nConvert - nDuplicates;
 }// removeDuplicates()
 
-int strcicmp(char const *a, char const *b) //case insensitive compare
-{
-    for (;; a++, b++) {
-        int d = tolower(*a) - tolower(*b);
-        if (d != 0 || !*a)
-            return d;
-    }
-}// strcicmp()
-
 bool isExt (char *file_name, const char* ext) {
     char *p_extension;
     if((p_extension = strrchr(file_name,'.')) != NULL )
@@ -1826,115 +2068,6 @@ bool isExt (char *file_name, const char* ext) {
     return false;
 }// isExt()
 
-/*int nii_readpic(char * fname, struct nifti_1_header *nhdr) {
-    //https://github.com/jefferis/pic2nifti/blob/master/libpic2nifti.c
-#define BIORAD_HEADER_SIZE 76
-#define BIORAD_NOTE_HEADER_SIZE 16
-#define BIORAD_NOTE_SIZE 80
-    typedef struct
-    {
-        unsigned short nx, ny;    //  0   2*2     image width and height in pixels
-        short npic;               //  4   2       number of images in file
-        short ramp1_min;          //  6   2*2     LUT1 ramp min. and max.
-        short ramp1_max;
-        int32_t notes;                // 10   4       no notes=0; has notes=non zero
-        short byte_format;        // 14   2       bytes=TRUE(1); words=FALSE(0)
-        unsigned short n;         // 16   2       image number within file
-        char name[32];            // 18   32      file name
-        short merged;             // 50   2       merged format
-        unsigned short color1;    // 52   2       LUT1 color status
-        unsigned short file_id;   // 54   2       valid .PIC file=12345
-        short ramp2_min;          // 56   2*2     LUT2 ramp min. and max.
-        short ramp2_max;
-        unsigned short color2;    // 60   2       LUT2 color status
-        short edited;             // 62   2       image has been edited=TRUE(1)
-        short lens;               // 64   2       Integer part of lens magnification
-        float mag_factor;         // 66   4       4 byte real mag. factor (old ver.)
-        unsigned short dummy[3];  // 70   6       NOT USED (old ver.=real lens mag.)
-    } biorad_header;
-    typedef struct
-    {
-        short blank;		// 0	2
-        int note_flag;		// 2	4
-        int blank2;			// 6	4
-        short note_type;	// 10	2
-        int blank3;			// 12	4
-    } biorad_note_header;
-    size_t n;
-    unsigned char buffer[BIORAD_HEADER_SIZE];
-    FILE *f = fopen(fname, "rb");
-    if (f)
-        n = fread(&buffer, BIORAD_HEADER_SIZE, 1, f);
-    if(!f || n!=1) {
-        printf("Problem reading biorad file!\n");
-        fclose(f);
-        return EXIT_FAILURE;
-    }
-    biorad_header bhdr;
-    memcpy( &bhdr.nx, buffer+0, sizeof( bhdr.nx ) );
-    memcpy( &bhdr.ny, buffer+2, sizeof( bhdr.ny ) );
-    memcpy( &bhdr.npic, buffer+4, sizeof( bhdr.npic ) );
-    memcpy( &bhdr.byte_format, buffer+14, sizeof( bhdr.byte_format ) );
-    memcpy( &bhdr.file_id, buffer+54, sizeof( bhdr.file_id ) );
-    if (bhdr.file_id != 12345) {
-        fclose(f);
-        return EXIT_FAILURE;
-    }
-    nhdr->dim[0]=3;//3D
-    nhdr->dim[1]=bhdr.nx;
-    nhdr->dim[2]=bhdr.ny;
-    nhdr->dim[3]=bhdr.npic;
-    nhdr->dim[4]=0;
-    nhdr->pixdim[1]=1.0;
-    nhdr->pixdim[2]=1.0;
-    nhdr->pixdim[3]=1.0;
-    if (bhdr.byte_format == 1)
-        nhdr->datatype = DT_UINT8; // 2
-    else
-        nhdr->datatype = DT_UINT16;
-    nhdr->vox_offset = BIORAD_HEADER_SIZE;
-    if(fseek(f, bhdr.nx*bhdr.ny*bhdr.npic*bhdr.byte_format, SEEK_CUR)==0) {
-        biorad_note_header nh;
-        char noteheaderbuf[BIORAD_NOTE_HEADER_SIZE];
-        char note[BIORAD_NOTE_SIZE];
-        while (!feof(f)) {
-            fread(&noteheaderbuf, BIORAD_NOTE_HEADER_SIZE, 1, f);
-            fread(&note, BIORAD_NOTE_SIZE, 1, f);
-            memcpy(&nh.note_flag, noteheaderbuf+2, sizeof(nh.note_flag));
-            memcpy(&nh.note_type, noteheaderbuf+10, sizeof(nh.note_type));
-            //		printf("regular note line %s\n",note);
-            //		printf("note flag = %d, note type = %d\n",nh.note_flag,nh.note_type);
-            // These are not interesting notes
-            if(nh.note_type==1) continue;
-
-            // Look for calibration information
-            double d1, d2, d3;
-            if ( 3 == sscanf( note, "AXIS_2 %lf %lf %lf", &d1, &d2, &d3 ) )
-                nhdr->pixdim[1] = d3;
-            if ( 3 == sscanf( note, "AXIS_3 %lf %lf %lf", &d1, &d2, &d3 ) )
-                nhdr->pixdim[2] = d3;
-            if ( 3 == sscanf( note, "AXIS_4 %lf %lf %lf", &d1, &d2, &d3 ) )
-                nhdr->pixdim[3] = d3;
-            if(nh.note_flag==0) break;
-        }
-    }
-    nhdr->sform_code = 1;
-    nhdr->srow_x[0]=nhdr->pixdim[1];nhdr->srow_x[1]=0.0f;nhdr->srow_x[2]=0.0f;nhdr->srow_x[3]=0.0f;
-    nhdr->srow_y[0]=0.0f;nhdr->srow_y[1]=nhdr->pixdim[2];nhdr->srow_y[2]=0.0f;nhdr->srow_y[3]=0.0f;
-    nhdr->srow_z[0]=0.0f;nhdr->srow_z[1]=0.0f;nhdr->srow_z[2]=nhdr->pixdim[3];nhdr->srow_z[3]=0.0f;
-    fclose(f);
-    convertForeignToNifti(nhdr);
-    return EXIT_SUCCESS;
-}
-
-
-int convert_foreign(struct TDCMopts opts) {
-    nifti_1_header nhdr ;
-    int OK = EXIT_FAILURE;
-    OK = nii_readpic(opts.indir, &nhdr);
-    return OK;
-}*/
-
 int convert_parRec(struct TDCMopts opts) {
     //sample dataset from Ed Gronenschild <ed.gronenschild at maastrichtuniversity.nl>
     struct TSearchList nameList;
@@ -1950,13 +2083,8 @@ int convert_parRec(struct TDCMopts opts) {
     dcmSort[0].indx = 0;
     saveDcm2Nii(1, dcmSort, dcmList, &nameList, opts, &dti4D);
     free(dcmList);//if (nConvertTotal == 0)
-    if (nameList.numItems < 1) {
-     #ifdef myUseCOut
-    	std::cout<<"No valid PAR/REC files were found"<<std::endl;
-		#else
-		printf("No valid PAR/REC files were found\n");
-		#endif
-    }
+    if (nameList.numItems < 1)
+    	printMessage("No valid PAR/REC files were found\n");
     if (nameList.numItems > 0)
         for (int i = 0; i < nameList.numItems; i++)
             free(nameList.str[i]);
@@ -1978,30 +2106,26 @@ void freeNameList(struct TSearchList nameList) {
 int nii_loadDir(struct TDCMopts* opts) {
     //Identifies all the DICOM files in a folder and its subfolders
     if (strlen(opts->indir) < 1) {
-         #ifdef myUseCOut
-    	std::cout<<"No input"<<std::endl;
-		#else
-        printf("No input\n");
-        #endif
+        printMessage("No input\n");
         return EXIT_FAILURE;
     }
     char indir[512];
     strcpy(indir,opts->indir);
     bool isFile = is_fileNotDir(opts->indir);
+    //bool isParRec = (isFile && ( (isExt(indir, ".par")) || (isExt(indir, ".rec"))) );
     if (isFile) //if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom
         dropFilenameFromPath(opts->indir);//getParentFolder(opts.indir, opts.indir);
     dropTrailingFileSep(opts->indir);
-    if (strlen(opts->outdir) < 1)
+    if (strlen(opts->outdir) < 1) {
         strcpy(opts->outdir,opts->indir);
-    dropTrailingFileSep(opts->outdir);
-    if (is_fileNotDir(opts->outdir)) //if user passes ~/dicom/mr1.dcm we will look at all files in ~/dicom
-        dropFilenameFromPath(opts->outdir);//getParentFolder(opts.indir, opts.indir);
+    } else
+    	dropTrailingFileSep(opts->outdir);
     if (!is_dir(opts->outdir,true)) {
 		#ifdef myUseInDirIfOutDirUnavailable
-		printf("Warning: output folder invalid %s will try %s\n",opts->outdir,opts->indir);
+		printWarning("Output folder invalid %s will try %s\n",opts->outdir,opts->indir);
 		strcpy(opts->outdir,opts->indir);
 		#else
-		printf("Error: output folder invalid: %s\n",opts->outdir);
+		printError("Output folder invalid: %s\n",opts->outdir);
 		return EXIT_FAILURE;
 		#endif
     }
@@ -2012,56 +2136,46 @@ int nii_loadDir(struct TDCMopts* opts) {
          #endif
         #endif
     }*/
-    /*if (isFile && ((isExt(indir, ".mha")) || (isExt(indir, ".mhd"))) ) {
-        strcpy(opts->indir, indir); //set to original file name, not path
-        return convert_foreign(*opts);
-    }*/
     getFileName(opts->indirParent, opts->indir);
-    if (isFile && ((isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
+    if (isFile && ( (isExt(indir, ".v"))) )
+		return convert_foreign (indir, *opts);
+    if (isFile && ( (isExt(indir, ".par")) || (isExt(indir, ".rec"))) ) {
         char pname[512], rname[512];
         strcpy(pname,indir);
         strcpy(rname,indir);
         changeExt (pname, "PAR");
         changeExt (rname, "REC");
+        #ifndef _MSC_VER //Linux is case sensitive, #include <unistd.h>
+   		if( access( rname, F_OK ) != 0 ) changeExt (rname, "rec");
+   		if( access( pname, F_OK ) != 0 ) changeExt (pname, "par");
+		#endif
         if (is_fileNotDir(rname)  &&  is_fileNotDir(pname) ) {
             strcpy(opts->indir, pname); //set to original file name, not path
             return convert_parRec(*opts);
-        } else if (isExt(indir, ".par")) //Linux is case sensitive...
-            return convert_parRec(*opts);
+        };
     }
     if ((isFile) && (opts->isOnlySingleFile))
         return singleDICOM(opts, indir);
     struct TSearchList nameList;
-	nameList.maxItems = 32000; // larger requires more memory, smaller more passes
+	nameList.maxItems = 24000; // larger requires more memory, smaller more passes
     //1: find filenames of dicom files: up to two passes if we found more files than we allocated memory
     for (int i = 0; i < 2; i++ ) {
         nameList.str = (char **) malloc((nameList.maxItems+1) * sizeof(char *)); //reserve one pointer (32 or 64 bits) per potential file
         nameList.numItems = 0;
-        searchDirForDICOM(opts->indir, &nameList,  5,1);
+        searchDirForDICOM(opts->indir, &nameList,  5, 1, opts);
         if (nameList.numItems <= nameList.maxItems)
             break;
         freeNameList(nameList);
         nameList.maxItems = nameList.numItems+1;
-        //printf("Second pass required, found %ld images\n", nameList.numItems);
+        //printMessage("Second pass required, found %ld images\n", nameList.numItems);
     }
     if (nameList.numItems < 1) {
-        #ifdef myUseCOut
-    	std::cout << "Error: unable to find any DICOM images in "<< opts->indir <<std::endl;
-    	#else
-        printf("Error: unable to find any DICOM images in %s\n", opts->indir);
-        #endif
+        printError("Unable to find any DICOM images in %s\n", opts->indir);
         free(nameList.str); //ignore compile warning - memory only freed on first of 2 passes
         return EXIT_FAILURE;
     }
-    long long nDcm = nameList.numItems;
-    #ifdef myUseCOut
-    //stdout is piped in XCode just like printf, if this works with QT then we could replace these duplicate commands...
-    //try this out the next QT build:
-    fprintf(stdout, "STDOUT PIPE TEST\n");
-    std::cout << "Found "<< nameList.numItems <<" DICOM images" <<std::endl;
-    #else
-    printf( "Found %lu DICOM image(s)\n", nameList.numItems);
-    #endif
+    size_t nDcm = nameList.numItems;
+    printMessage( "Found %lu DICOM image(s)\n", nameList.numItems);
     // struct TDICOMdata dcmList [nameList.numItems]; //<- this exhausts the stack for large arrays
     struct TDICOMdata *dcmList  = (struct TDICOMdata *)malloc(nameList.numItems * sizeof(struct  TDICOMdata));
     struct TDTI4D dti4D;
@@ -2069,7 +2183,7 @@ int nii_loadDir(struct TDCMopts* opts) {
     bool compressionWarning = false;
     for (int i = 0; i < nDcm; i++ ) {
         dcmList[i] = readDICOMv(nameList.str[i], opts->isVerbose, opts->compressFlag, &dti4D); //ignore compile warning - memory only freed on first of 2 passes
-        if (dcmList[i].CSA.numDti > 1) { //4D dataset: dti4D arrays require huge amounts of RAM - write this immediately
+        if ((dcmList[i].isValid) &&((dcmList[i].patientPositionNumPhilips > 1) || (dcmList[i].CSA.numDti > 1))) { //4D dataset: dti4D arrays require huge amounts of RAM - write this immediately
             struct TDCMsort dcmSort[1];
             dcmSort[0].indx = i;
             dcmSort[0].img = ((uint64_t)dcmList[i].seriesNum << 32) + dcmList[i].imageNum;
@@ -2079,9 +2193,45 @@ int nii_loadDir(struct TDCMopts* opts) {
         }
     	if ((dcmList[i].compressionScheme != kCompressNone) && (!compressionWarning) && (opts->compressFlag != kCompressNone)) {
     		compressionWarning = true; //generate once per conversion rather than once per image
-        	printf("Image Decompression is new: please validate conversions\n");
+        	printMessage("Image Decompression is new: please validate conversions\n");
     	}
     }
+#ifdef HAVE_R
+    if (opts->isScanOnly) {
+        TWarnings warnings = setWarnings();
+
+        // Create the first series from the first DICOM file
+        TDicomSeries firstSeries;
+        firstSeries.representativeData = dcmList[0];
+        firstSeries.files.push_back(nameList.str[0]);
+        opts->series.push_back(firstSeries);
+
+        // Iterate over the remaining files
+        for (size_t i = 1; i < nDcm; i++) {
+            bool matched = false;
+
+            // If the file matches an existing series, add it to the corresponding file list
+            for (int j = 0; j < opts->series.size(); j++) {
+                if (isSameSet(opts->series[j].representativeData, dcmList[i], opts->isForceStackSameSeries, &warnings)) {
+                    opts->series[j].files.push_back(nameList.str[i]);
+                    matched = true;
+                    break;
+                }
+            }
+
+            // If not, create a new series object
+            if (!matched) {
+                TDicomSeries nextSeries;
+                nextSeries.representativeData = dcmList[i];
+                nextSeries.files.push_back(nameList.str[i]);
+                opts->series.push_back(nextSeries);
+            }
+        }
+
+        // To avoid a spurious warning below
+        nConvertTotal = nDcm;
+    } else {
+#endif
     //3: stack DICOMs with the same Series
     for (int i = 0; i < nDcm; i++ ) {
 		if ((dcmList[i].converted2NII == 0) && (dcmList[i].isValid)) {
@@ -2092,11 +2242,11 @@ int nii_loadDir(struct TDCMopts* opts) {
 					nConvert++;
 			if (nConvert < 1) nConvert = 1; //prevents compiler warning for next line: never executed since j=i always causes nConvert ++
 
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 			TDCMsort * dcmSort = (TDCMsort *)malloc(nConvert * sizeof(TDCMsort));
-#else
-			struct TDCMsort dcmSort[nConvert];
-#endif
+//#else
+//			struct TDCMsort dcmSort[nConvert];
+//#endif
 			nConvert = 0;
 			//warnings = setWarnings();
 			for (int j = i; j < nDcm; j++)
@@ -2113,18 +2263,17 @@ int nii_loadDir(struct TDCMopts* opts) {
 				nConvert = removeDuplicates(nConvert, dcmSort);
 			nConvertTotal += nConvert;
 			saveDcm2Nii(nConvert, dcmSort, dcmList, &nameList, *opts, &dti4D);
-#ifdef _MSC_VER
+//#ifdef _MSC_VER
 			free(dcmSort);
-#endif
+//#endif
 		}//convert all images of this series
     }
+#ifdef HAVE_R
+    }
+#endif
     free(dcmList);
     if (nConvertTotal == 0) {
-        #ifdef myUseCOut
-    	std::cout << "No valid DICOM files were found\n" <<std::endl;
-    	#else
-    	printf("No valid DICOM files were found\n");
-    	#endif
+        printMessage("No valid DICOM files were found\n");
     }
     freeNameList(nameList);
     //if (nameList.numItems > 0)
@@ -2162,19 +2311,11 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
     #if defined(_WIN64) || defined(_WIN32)
     strcpy(opts->pigzname,"pigz.exe");
     if (!is_exe(opts->pigzname)) {
-    #ifdef myUseCOut
-        #ifdef myDisableZLib
-        std::cout << "Compression requires "<<opts->pigzname<<" in the same folder as the executable"<<std::endl;
-		#else //myUseZLib
- 		std::cout << "Compression will be faster with "<<opts->pigzname<<" in the same folder as the executable "<<std::endl;
-		#endif
-    #else
         #ifdef myDisableZLib
-        printf("Compression requires %s in the same folder as the executable\n",opts->pigzname);
+        printMessage("Compression requires %s in the same folder as the executable\n",opts->pigzname);
 		#else //myUseZLib
- 		printf("Compression will be faster with %s in the same folder as the executable\n",opts->pigzname);
+ 		printMessage("Compression will be faster with %s in the same folder as the executable\n",opts->pigzname);
 		#endif
-	#endif
         strcpy(opts->pigzname,"");
     } else
     	strcpy(opts->pigzname,".\\pigz"); //drop
@@ -2186,7 +2327,12 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
         strcpy(opts->pigzname,"/usr/bin/pigz");
         if (!is_exe(opts->pigzname)) {
         strcpy(opts->pigzname,"/usr/local/bin/pigz_mricron");
-        if (!is_exe(opts->pigzname)) {
+        if (argv == NULL) { //no exectuable path provided
+			if (!is_exe(opts->pigzname))
+				strcpy(opts->pigzname,"");
+        	return;
+        }
+        if (!is_exe(opts->pigzname))  {
             strcpy(opts->pigzname,argv[0]);
             dropFilenameFromPath(opts->pigzname);//, opts.pigzname);
             char appendChar[2] = {"a"};
@@ -2197,20 +2343,12 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
             strcat(opts->pigzname,".exe");
             #endif
             if (!is_exe(opts->pigzname)) {
-             #ifdef myUseCOut
-              #ifdef myDisableZLib
-                std::cout << "Compression requires "<<pigz<<std::endl;
-                #else //myUseZLib
-                std::cout << "Compression will be faster with "<<pigz<<std::endl;
-            	#endif
-    		#else
-            	#ifdef myDisableZLib
-                printf("Compression requires %s\n",pigz);
+             	#ifdef myDisableZLib
+                printMessage("Compression requires %s\n",pigz);
             	#else //myUseZLib
-                printf("Compression will be faster with %s\n",pigz);
+                printMessage("Compression will be faster with %s\n",pigz);
             	#endif
-            #endif
-                strcpy(opts->pigzname,"");
+            	strcpy(opts->pigzname,"");
             } //no pigz_mricron in exe's folder
         } //no /usr/local/pigz_mricron
        }//no /usr/bin/pigz
@@ -2218,23 +2356,8 @@ void readFindPigz (struct TDCMopts *opts, const char * argv[]) {
     #endif
 } //readFindPigz()
 
-#if defined(_WIN64) || defined(_WIN32)
-//windows has unusual file permissions for many users - lets save preferences to the registry
-void saveIniFile (struct TDCMopts opts) {
-HKEY hKey;
-if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\dcm2nii",0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) {
-	RegCloseKey(hKey);
-	return;
-}
-DWORD dwValue    = opts.isGz;
-  //RegSetValueEx(hKey,"isGZ", 0, REG_DWORD,reinterpret_cast<BYTE *>(&dwValue),sizeof(dwValue));
-  //RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, (LPDWORD)&dwValue, sizeof(dwValue));
-  RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, reinterpret_cast<BYTE *>(&dwValue), sizeof(dwValue));
-  RegSetValueExA(hKey,"filename",0, REG_SZ,(LPBYTE)opts.filename, strlen(opts.filename)+1);
-  RegCloseKey(hKey);
-} //saveIniFile()
-
-void readIniFile (struct TDCMopts *opts, const char * argv[]) {
+void setDefaultOpts (struct TDCMopts *opts, const char * argv[]) { //either "setDefaultOpts(opts,NULL)" or "setDefaultOpts(opts,argv)" where argv[0] is path to search
+    strcpy(opts->pigzname,"");
     readFindPigz(opts, argv);
     #ifdef myEnableJasper
     opts->compressFlag = kCompressYes; //JASPER for JPEG2000
@@ -2245,23 +2368,52 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
 		opts->compressFlag = kCompressYes; //OPENJPEG for JPEG2000
 		#endif
 	#endif
+    //printMessage("%d %s\n",opts->compressFlag, opts->compressname);
     strcpy(opts->indir,"");
     strcpy(opts->outdir,"");
     opts->isOnlySingleFile = false; //convert all files in a directory, not just a single file
     opts->isForceStackSameSeries = false;
+    opts->isIgnoreDerivedAnd2D = false;
+    opts->isPhilipsFloatNotDisplayScaling = true;
     opts->isCrop = false;
     opts->isGz = false;
-    opts->isFlipY = true;
+    opts->gzLevel = -1;
+    opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
     opts->isRGBplanar = false;
-    opts->isCreateBIDS =  false;
+    opts->isCreateBIDS =  true;
+    #ifdef isAnonymizeBIDS
+    opts->isAnonymizeBIDS = true;
+    #else
+    opts->isAnonymizeBIDS = false;
+    #endif
     opts->isCreateText = false;
 #ifdef myDebug
-    opts->isVerbose =   true;
+        opts->isVerbose =   true;
 #else
-    opts->isVerbose = false;
+        opts->isVerbose = false;
 #endif
     opts->isTiltCorrect = true;
     strcpy(opts->filename,"%f_%p_%t_%s");
+} // setDefaultOpts()
+
+#if defined(_WIN64) || defined(_WIN32)
+//windows has unusual file permissions for many users - lets save preferences to the registry
+void saveIniFile (struct TDCMopts opts) {
+HKEY hKey;
+if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\dcm2nii",0, KEY_SET_VALUE, &hKey) != ERROR_SUCCESS) {
+	RegCloseKey(hKey);
+	return;
+}
+DWORD dwValue    = opts.isGz;
+  //RegSetValueEx(hKey,"isGZ", 0, REG_DWORD,reinterpret_cast<BYTE *>(&dwValue),sizeof(dwValue));
+  //RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, (LPDWORD)&dwValue, sizeof(dwValue));
+  RegSetValueExA(hKey, "isGZ", 0, REG_DWORD, reinterpret_cast<BYTE *>(&dwValue), sizeof(dwValue));
+  RegSetValueExA(hKey,"filename",0, REG_SZ,(LPBYTE)opts.filename, strlen(opts.filename)+1);
+  RegCloseKey(hKey);
+} //saveIniFile()
+
+void readIniFile (struct TDCMopts *opts, const char * argv[]) {
+    setDefaultOpts(opts, argv);
      HKEY  hKey;
     DWORD vSize     = 0;
     DWORD dwDataType = 0;
@@ -2288,42 +2440,15 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
 #define STATUSFILENAME "/.dcm2nii.ini"
 
 void readIniFile (struct TDCMopts *opts, const char * argv[]) {
-    readFindPigz(opts, argv);
-    #ifdef myEnableJasper
-    opts->compressFlag = kCompressYes; //JASPER for JPEG2000
-	#else
-		#ifdef myDisableOpenJPEG
-		opts->compressFlag = kCompressNone; //no decompressor
-		#else
-		opts->compressFlag = kCompressYes; //OPENJPEG for JPEG2000
-		#endif
-	#endif
-    //printf("%d %s\n",opts->compressFlag, opts->compressname);
+	setDefaultOpts(opts, argv);
     sprintf(opts->optsname, "%s%s", getenv("HOME"), STATUSFILENAME);
-    strcpy(opts->indir,"");
-    strcpy(opts->outdir,"");
-    opts->isOnlySingleFile = false; //convert all files in a directory, not just a single file
-    opts->isForceStackSameSeries = false;
-    opts->isCrop = false;
-    opts->isGz = false;
-    opts->isFlipY = true; //false: images in raw DICOM orientation, true: image rows flipped to cartesian coordinates
-    opts->isRGBplanar = false;
-    opts->isCreateBIDS =  false;
-    opts->isCreateText = false;
-#ifdef myDebug
-        opts->isVerbose =   true;
-#else
-        opts->isVerbose = false;
-#endif
-    opts->isTiltCorrect = true;
-    strcpy(opts->filename,"%f_%p_%t_%s");
     FILE *fp = fopen(opts->optsname, "r");
     if (fp == NULL) return;
     char Setting[20],Value[255];
     //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) {
     //while ( fscanf(fp, "%[^=]=%s\n", Setting, Value) == 2 ) {
     while ( fscanf(fp, "%[^=]=%[^\n]\n", Setting, Value) == 2 ) {
-        //printf(">%s<->'%s'\n",Setting,Value);
+        //printMessage(">%s<->'%s'\n",Setting,Value);
         if ( strcmp(Setting,"isGZ") == 0 )
             opts->isGz = atoi(Value);
         else if ( strcmp(Setting,"isBIDS") == 0 )
@@ -2336,7 +2461,7 @@ void readIniFile (struct TDCMopts *opts, const char * argv[]) {
 
 void saveIniFile (struct TDCMopts opts) {
     FILE *fp = fopen(opts.optsname, "w");
-    //printf("%s\n",localfilename);
+    //printMessage("%s\n",localfilename);
     if (fp == NULL) return;
     fprintf(fp, "isGZ=%d\n", opts.isGz);
     fprintf(fp, "isBIDS=%d\n", opts.isCreateBIDS);
diff --git a/console/nii_dicom_batch.h b/console/nii_dicom_batch.h
index cc8c67f..37a25a8 100644
--- a/console/nii_dicom_batch.h
+++ b/console/nii_dicom_batch.h
@@ -10,21 +10,36 @@ extern "C" {
 
 #include <stdbool.h>
 #include <string.h>
+#ifndef HAVE_R
 #include "nifti1.h"
-#include "nifti1.h"
+#endif
 #include "nii_dicom.h"
 
+#ifdef HAVE_R
+    struct TDicomSeries {
+        TDICOMdata representativeData;
+        std::vector<std::string> files;
+    };
+#endif
+
     struct TDCMopts {
-        bool isGz, isFlipY,  isCreateBIDS, isCreateText, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackSameSeries, isCrop;
-        int isVerbose, compressFlag; //support for compressed data 0=none,
+        bool isGz, isFlipY,  isCreateBIDS, isAnonymizeBIDS, isCreateText, isIgnoreDerivedAnd2D, isPhilipsFloatNotDisplayScaling, isTiltCorrect, isRGBplanar, isOnlySingleFile, isForceStackSameSeries, isCrop;
+        int isVerbose, compressFlag, gzLevel; //support for compressed data 0=none,
         char filename[512], outdir[512], indir[512], pigzname[512], optsname[512], indirParent[512];
+#ifdef HAVE_R
+        bool isScanOnly;
+        void *imageList;
+        std::vector<TDicomSeries> series;
+#endif
     };
     void saveIniFile (struct TDCMopts opts);
+    void setDefaultOpts (struct TDCMopts *opts, const char * argv[]); //either "setDefaultOpts(opts,NULL)" or "setDefaultOpts(opts,argv)" where argv[0] is path to search
     void readIniFile (struct TDCMopts *opts, const char * argv[]);
     int nii_saveNII(char * niiFilename, struct nifti_1_header hdr, unsigned char* im, struct TDCMopts opts);
     //void readIniFile (struct TDCMopts *opts);
-    int nii_loadDir (struct TDCMopts *opts) ;
-    //int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts);
+    int nii_loadDir (struct TDCMopts *opts);
+    void nii_SaveBIDS(char pathoutname[], struct TDICOMdata d, struct TDCMopts opts, struct TDTI4D *dti4D, struct nifti_1_header *h);
+    int nii_createFilename(struct TDICOMdata dcm, char * niiFilename, struct TDCMopts opts);
     void  nii_createDummyFilename(char * niiFilename, struct TDCMopts opts);
     //void findExe(char name[512], const char * argv[]);
 #ifdef  __cplusplus
diff --git a/console/nii_foreign.cpp b/console/nii_foreign.cpp
new file mode 100644
index 0000000..1096201
--- /dev/null
+++ b/console/nii_foreign.cpp
@@ -0,0 +1,404 @@
+#include "nii_foreign.h"
+#include "nii_dicom.h"
+#include "nifti1_io_core.h"
+#include "nii_dicom_batch.h"
+#include "nifti1.h"
+//#include "nifti1_io_core.h"
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h>
+#include "print.h"
+#ifdef _MSC_VER
+	#include <direct.h>
+	#define getcwd _getcwd
+	#define chdir _chrdir
+	#include "io.h"
+	#include <math.h>
+	//#define snprintMessage _snprintMessage
+	//#define vsnprintMessage _vsnprintMessage
+	#define strcasecmp _stricmp
+	#define strncasecmp _strnicmp
+#else
+	#include <unistd.h>
+#endif
+
+#ifndef Float32
+	#define Float32 float
+#endif
+#ifndef uint32
+	#define uint32 uint32_t
+#endif
+
+#ifdef __GNUC__
+    #define PACK(...) __VA_ARGS__ __attribute__((__packed__))
+#else
+    #define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
+#endif
+
+
+/*nii_readEcat7 2017 by Chris Rorden, BSD license
+ http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7_8h_source.html#l00060
+ http://www.turkupetcentre.net/software/libdoc/libtpcimgio/ecat7r_8c_source.html#l00717
+ http://xmedcon.sourcearchive.com/documentation/0.10.7-1build1/ecat7_8h_source.html
+ https://github.com/BIC-MNI/minc-tools/tree/master/conversion/ecattominc
+ https://github.com/nipy/nibabel/blob/ec4567fb09b4472c5a4bb9a13dbcc9eb0a63d875/nibabel/ecat.py
+*/
+
+void strClean(char * cString) {
+	int len = strlen(cString);
+	if (len < 1) return;
+	for (int i = 0; i < len; i++) {
+		char c = cString[i];
+		if ( (c < char(32)) || (c == char(127)) || (c == char(255)) ) cString[i] = 0;
+		if ( (c==' ') || (c==',') || (c=='^') || (c=='/') || (c=='\\')  || (c=='%') || (c=='*')) cString[i] = '_';
+	}
+}
+
+unsigned char * readEcat7(const char *fname, struct TDICOMdata *dcm, struct nifti_1_header *hdr, struct TDCMopts opts, bool isWarnIfNotEcat) {
+//data type
+#define	ECAT7_BYTE 1
+#define	ECAT7_VAXI2 2
+#define ECAT7_VAXI4 3
+#define ECAT7_VAXR4 4
+#define ECAT7_IEEER4 5
+#define	ECAT7_SUNI2 6
+#define	ECAT7_SUNI4 7
+//file types
+//#define ECAT7_UNKNOWN 0
+#define ECAT7_2DSCAN 1
+#define ECAT7_IMAGE16 2
+#define ECAT7_ATTEN 3
+#define ECAT7_2DNORM 4
+#define ECAT7_POLARMAP 5
+#define ECAT7_VOLUME8 6
+#define ECAT7_VOLUME16 7
+#define ECAT7_PROJ 8
+#define ECAT7_PROJ16 9
+#define ECAT7_IMAGE8 10
+#define ECAT7_3DSCAN 11
+#define ECAT7_3DSCAN8 12
+#define ECAT7_3DNORM 13
+#define ECAT7_3DSCANFIT 14
+    PACK( typedef struct  {
+        char magic[14],original_filename[32];
+        uint16_t sw_version, system_type, file_type;
+        char serial_number[10];
+        uint32 scan_start_time;
+        char isotope_name[8];
+        Float32 isotope_halflife;
+        char radiopharmaceutical[32];
+        Float32 gantry_tilt, gantry_rotation, bed_elevation, intrinsic_tilt;
+        int16_t wobble_speed, transm_source_type;
+        Float32 distance_scanned, transaxial_fov;
+        uint16_t angular_compression, coin_samp_mode, axial_samp_mode;
+        Float32 ecat_calibration_factor;
+        uint16_t calibration_unitS, calibration_units_type, compression_code;
+        char study_type[12], patient_id[16], patient_name[32], patient_sex, patient_dexterity;
+        Float32 patient_age, patient_height, patient_weight;
+        uint32 patient_birth_date;
+        char physician_name[32], operator_name[32], study_description[32];
+        uint16_t acquisition_type, patient_orientation;
+        char facility_name[20];
+        uint16_t num_planes, num_frames, num_gates, num_bed_pos;
+        Float32 init_bed_position;
+        Float32 bed_position[15];
+        Float32 plane_separation;
+        uint16_t lwr_sctr_thres, lwr_true_thres, upr_true_thres;
+        char user_process_code[10];
+        uint16_t acquisition_mode;
+        Float32 bin_size, branching_fraction;
+        uint32 dose_start_time;
+        Float32 dosage, well_counter_corr_factor;
+        char data_units[32];
+        uint16_t septa_state;
+        char fill[12];
+    }) ecat_main_hdr;
+PACK( typedef struct {
+	int16_t data_type, num_dimensions, x_dimension, y_dimension, z_dimension;
+	Float32 x_offset, y_offset, z_offset, recon_zoom, scale_factor;
+	int16_t image_min, image_max;
+	Float32 x_pixel_size, y_pixel_size, z_pixel_size;
+	int32_t frame_duration, frame_start_time;
+	int16_t filter_code;
+	Float32 x_resolution, y_resolution, z_resolution, num_r_elements, num_angles, z_rotation_angle, decay_corr_fctr;
+	int32_t processing_code, gate_duration, r_wave_offset, num_accepted_beats;
+	Float32 filter_cutoff_frequenc, filter_resolution, filter_ramp_slope;
+	int16_t filter_order;
+	Float32 filter_scatter_fraction, filter_scatter_slope;
+	char annotation[40];
+	Float32 mtx[9], rfilter_cutoff, rfilter_resolution;
+	int16_t rfilter_code, rfilter_order;
+	Float32 zfilter_cutoff, zfilter_resolution;
+	int16_t zfilter_code, zfilter_order;
+	Float32 mtx_1_4, mtx_2_4, mtx_3_4;
+	int16_t scatter_type, recon_type, recon_views, fill_cti[87], fill_user[49];
+}) ecat_img_hdr;
+    PACK( typedef struct  {
+        int32_t hdr[4], r[31][4];
+    }) ecat_list_hdr;
+    bool swapEndian = false;
+    size_t n;
+    FILE *f;
+    ecat_main_hdr mhdr;
+    f = fopen(fname, "rb");
+    if (f)
+        n = fread(&mhdr, sizeof(mhdr), 1, f);
+    if(!f || n!=1) {
+        printMessage("Problem reading ECAT7 file!\n");
+        fclose(f);
+        return NULL;
+    }
+    if ((mhdr.magic[0] != 'M') || (mhdr.magic[1] != 'A') || (mhdr.magic[2] != 'T')
+        || (mhdr.magic[3] != 'R') || (mhdr.magic[4] != 'I') || (mhdr.magic[5] != 'X') ) {
+        if (isWarnIfNotEcat)
+        	printMessage("Signature not 'MATRIX' (ECAT7): '%s'\n", fname);
+        fclose(f);
+        return NULL;
+    }
+    swapEndian = mhdr.file_type > 255;
+    if (swapEndian) {
+        nifti_swap_2bytes(2, &mhdr.sw_version);
+        nifti_swap_2bytes(1, &mhdr.file_type);
+        //nifti_swap_2bytes(1, &mhdr.num_frames);
+        nifti_swap_4bytes(1, &mhdr.ecat_calibration_factor);
+        nifti_swap_4bytes(1, &mhdr.isotope_halflife);
+        nifti_swap_4bytes(2, &mhdr.dosage);
+    }
+    if ((mhdr.file_type < ECAT7_2DSCAN) || (mhdr.file_type > ECAT7_3DSCANFIT)) {
+        printMessage("Unknown ECAT file type %d\n", mhdr.file_type);
+        fclose(f);
+        return NULL;
+    }
+    //read list matrix
+    ecat_list_hdr lhdr;
+    fseek(f, 512, SEEK_SET);
+    fread(&lhdr, sizeof(lhdr), 1, f);
+    if (swapEndian) nifti_swap_4bytes(128, &lhdr.hdr[0]);
+    //offset to first image
+    int img_StartBytes = lhdr.r[0][1] * 512;
+    //load image header for first image
+    fseek(f, img_StartBytes - 512, SEEK_SET); //image header is block immediately before image
+    ecat_img_hdr ihdr;
+    fread(&ihdr, sizeof(ihdr), 1, f);
+    if (swapEndian) {
+    	nifti_swap_2bytes(5, &ihdr.data_type);
+        nifti_swap_4bytes(5, &ihdr.x_offset);
+        nifti_swap_2bytes(2, &ihdr.image_min);
+        nifti_swap_4bytes(5, &ihdr.x_pixel_size);
+        nifti_swap_2bytes(1, &ihdr.filter_code);
+        nifti_swap_4bytes(14, &ihdr.x_resolution);
+        nifti_swap_2bytes(1, &ihdr.filter_order);
+        nifti_swap_4bytes(2, &ihdr.filter_scatter_fraction);
+        nifti_swap_4bytes(11, &ihdr.mtx);
+        nifti_swap_2bytes(2, &ihdr.rfilter_code);
+        nifti_swap_4bytes(2, &ihdr.zfilter_cutoff);
+        nifti_swap_2bytes(2, &ihdr.zfilter_code);
+        nifti_swap_4bytes(3, &ihdr.mtx_1_4);
+        nifti_swap_2bytes(3, &ihdr.scatter_type);
+    }
+    if ((ihdr.data_type != ECAT7_BYTE) && (ihdr.data_type != ECAT7_SUNI2) && (ihdr.data_type != ECAT7_SUNI4)) {
+        printMessage("Unknown or unsupported ECAT data type %d\n", ihdr.data_type);
+        fclose(f);
+        return NULL;
+    }
+    int bytesPerVoxel = 2;
+	if (ihdr.data_type == ECAT7_BYTE) bytesPerVoxel = 1;
+    if (ihdr.data_type == ECAT7_SUNI4) bytesPerVoxel = 4;
+    //next: read offsets for each volume: data not saved sequentially (each volume preceded by its own ecat_img_hdr)
+    int num_vol = 0;
+    bool isAbort = false;
+    bool isScaleFactorVaries = false;
+    #define kMaxVols 16000
+	size_t * imgOffsets = (size_t *)malloc(sizeof(size_t) * (kMaxVols));
+    float * imgSlopes = (float *)malloc(sizeof(float) * (kMaxVols));
+    ecat_img_hdr ihdrN;
+    while ((lhdr.hdr[0]+lhdr.hdr[3]) == 31) { //while valid list
+    	if (num_vol > 0) { //read the next list
+    		fseek(f, 512 * (lhdr.hdr[1] -1), SEEK_SET);
+    		fread(&lhdr, 512, 1, f);
+    		if (swapEndian) nifti_swap_4bytes(128, &lhdr.hdr[0]);
+    	}
+		if ((lhdr.hdr[0]+lhdr.hdr[3]) != 31) break; //if valid list
+		if (lhdr.hdr[3] < 1) break;
+		for (int k = 0; k < lhdr.hdr[3]; k++) {
+    		//check images' ecat_img_hdr matches first
+    		fseek(f, (lhdr.r[k][1]-1) * 512, SEEK_SET); //image header is block immediately before image
+    		fread(&ihdrN, sizeof(ihdrN), 1, f);
+    		if (swapEndian) {
+				nifti_swap_2bytes(5, &ihdrN.data_type);
+				nifti_swap_4bytes(5, &ihdrN.x_offset);
+				nifti_swap_2bytes(2, &ihdrN.image_min);
+				nifti_swap_4bytes(5, &ihdrN.x_pixel_size);
+				nifti_swap_2bytes(1, &ihdrN.filter_code);
+				nifti_swap_4bytes(14, &ihdrN.x_resolution);
+				nifti_swap_2bytes(1, &ihdrN.filter_order);
+				nifti_swap_4bytes(2, &ihdrN.filter_scatter_fraction);
+				nifti_swap_4bytes(11, &ihdrN.mtx);
+				nifti_swap_2bytes(2, &ihdrN.rfilter_code);
+				nifti_swap_4bytes(2, &ihdrN.zfilter_cutoff);
+				nifti_swap_2bytes(2, &ihdrN.zfilter_code);
+				nifti_swap_4bytes(3, &ihdrN.mtx_1_4);
+				nifti_swap_2bytes(3, &ihdrN.scatter_type);
+			}
+    		if (ihdr.scale_factor != ihdrN.scale_factor)
+    			isScaleFactorVaries = true;
+    		if ((ihdr.data_type != ihdrN.data_type) || (ihdr.x_dimension != ihdrN.x_dimension) || (ihdr.y_dimension != ihdrN.y_dimension) || (ihdr.z_dimension != ihdrN.z_dimension)) {
+    			printError("Error: ECAT volumes have varying image dimensions\n");
+    			isAbort = true;
+    		}
+    		if (num_vol < kMaxVols) {
+    			imgOffsets[num_vol]	= lhdr.r[k][1];
+    			imgSlopes[num_vol] = ihdrN.scale_factor;
+    		}
+    		num_vol ++;
+    	}
+    	if ((lhdr.hdr[0] > 0) || (isAbort)) break; //this list contains empty volumes: all lists have been read
+    } //read all image offsets
+    //report error reading image offsets
+    if ((num_vol < 1) || (isAbort) || (num_vol >= kMaxVols)) {
+        printMessage("Failure to extract ECAT7 images\n");
+        if (num_vol >= kMaxVols) printMessage("Increase kMaxVols");
+        fclose(f);
+        free (imgOffsets);
+        free(imgSlopes);
+        return NULL;
+    }
+    if ((isScaleFactorVaries) && (bytesPerVoxel != 2)) {
+    	printError("ECAT scale factor varies between volumes (check for updates) '%s'\n", fname);
+        fclose(f);
+        free (imgOffsets);
+        free(imgSlopes);
+        return NULL;
+	}
+	//load image data
+	unsigned char * img = NULL;
+	if ((isScaleFactorVaries) && (bytesPerVoxel == 2)) { //we need to convert volumes from 16-bit to 32-bit to preserve scaling factors
+		int num_vox = ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension;
+		size_t bytesPerVolumeIn = num_vox * bytesPerVoxel; //bytesPerVoxel == 2
+		unsigned char * imgIn = (unsigned char*)malloc(bytesPerVolumeIn);
+		int16_t * img16i = (int16_t*) imgIn;
+		bytesPerVoxel = 4;
+		size_t bytesPerVolume = num_vox * bytesPerVoxel;
+		img = (unsigned char*)malloc(bytesPerVolume * num_vol);
+		float * img32 = (float*) img;
+		for (int v = 0; v < num_vol; v++) {
+			fseek(f, imgOffsets[v] * 512, SEEK_SET);
+			fread( &imgIn[0], 1, bytesPerVolumeIn, f);
+			if (swapEndian)
+				nifti_swap_2bytes(num_vox, imgIn);
+			int volOffset = v * num_vox;
+			float scale = imgSlopes[v] * mhdr.ecat_calibration_factor;
+			for (int i = 0; i < num_vox; i++)
+				img32[i+volOffset] = (img16i[i] * scale);
+		}
+		//we have applied the scale factors to the data, so eliminate them
+		ihdr.scale_factor = 1.0;
+		mhdr.ecat_calibration_factor = 1.0;
+
+	} else { //if isScaleFactorVaries else simple conversion
+		size_t bytesPerVolume = ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * bytesPerVoxel;
+		img = (unsigned char*)malloc(bytesPerVolume * num_vol);
+		for (int v = 0; v < num_vol; v++) {
+			fseek(f, imgOffsets[v] * 512, SEEK_SET);
+			size_t  sz = fread( &img[v * bytesPerVolume], 1, bytesPerVolume, f);
+		}
+		if ((swapEndian) && (bytesPerVoxel == 2)) nifti_swap_2bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img);
+		if ((swapEndian) && (bytesPerVoxel == 4)) nifti_swap_4bytes(ihdr.x_dimension * ihdr.y_dimension * ihdr.z_dimension * num_vol, img);
+	}
+	printWarning("ECAT support VERY experimental (Spatial transforms unknown)\n");
+    free (imgOffsets);
+    free(imgSlopes);
+    fclose(f);
+    //fill DICOM header
+    float timeBetweenVolumes = ihdr.frame_duration;
+    if (num_vol > 1)
+    	timeBetweenVolumes = (float)(ihdrN.frame_start_time- ihdr.frame_start_time)/(float)(num_vol-1);
+    //copy and clean strings (ECAT can use 0x0D as a string terminator)
+    strncpy(dcm->patientName, mhdr.patient_name, 32);
+    strncpy(dcm->patientID, mhdr.patient_id, 16);
+    strncpy(dcm->seriesDescription, mhdr.study_description, 32);
+    strncpy(dcm->protocolName, mhdr.study_type, 12);
+    strncpy(dcm->imageComments, mhdr.isotope_name, 8);
+	strncpy(dcm->procedureStepDescription, mhdr.radiopharmaceutical, 32);
+	strClean(dcm->patientName);
+	strClean(dcm->patientID);
+	strClean(dcm->seriesDescription);
+	strClean(dcm->protocolName);
+	strClean(dcm->imageComments);
+	strClean(dcm->procedureStepDescription);
+	dcm->ecat_dosage = mhdr.dosage;
+	dcm->ecat_isotope_halflife = mhdr.isotope_halflife;
+    if (opts.isVerbose) {
+    	printMessage("ECAT7 details for '%s'\n", fname);
+    	printMessage(" Software version %d\n", mhdr.sw_version);
+    	printMessage(" System Type %d\n", mhdr.system_type);
+    	printMessage(" Frame duration %dms\n", ihdr.frame_duration);
+    	printMessage(" Time between volumes %gms\n", timeBetweenVolumes );
+    	printMessage(" Patient name '%s'\n", dcm->patientName);
+    	printMessage(" Patient ID '%s'\n", dcm->patientID);
+    	printMessage(" Study description '%s'\n", dcm->seriesDescription);
+    	printMessage(" Study type '%s'\n", dcm->protocolName);
+    	printMessage(" Isotope name '%s'\n", dcm->imageComments);
+    	printMessage(" Isotope halflife %gs\n", mhdr.isotope_halflife);
+    	printMessage(" Radiopharmaceutical '%s'\n", dcm->procedureStepDescription);
+    	printMessage(" Dosage %gbequerels/cc\n", mhdr.dosage);
+    	if (!isScaleFactorVaries) {
+    		printMessage(" Scale factor %12.12g\n", ihdr.scale_factor);
+    		printMessage(" ECAT calibration factor %8.12g\n", mhdr.ecat_calibration_factor);
+    	}
+    	printMessage(" NIfTI scale slope %12.12g\n",ihdr.scale_factor * mhdr.ecat_calibration_factor);
+    }
+	dcm->manufacturer = kMANUFACTURER_SIEMENS;
+	//dcm->manufacturersModelName = itoa(mhdr.system_type);
+	sprintf(dcm->manufacturersModelName, "%d", mhdr.system_type);
+    dcm->bitsAllocated = bytesPerVoxel * 8;
+    if (isScaleFactorVaries) dcm->isFloat = true;
+    dcm->bitsStored = 15; //ensures 16-bit images saved as INT16 not UINT16
+	dcm->samplesPerPixel = 1;
+	dcm->xyzMM[1] = ihdr.x_pixel_size * 10.0; //cm -> mm
+	dcm->xyzMM[2] = ihdr.y_pixel_size * 10.0; //cm -> mm
+	dcm->xyzMM[3] = ihdr.z_pixel_size * 10.0; //cm -> mm
+	dcm->TR = timeBetweenVolumes;
+	dcm->xyzDim[1] = ihdr.x_dimension;
+	dcm->xyzDim[2] = ihdr.y_dimension;
+	dcm->xyzDim[3] = ihdr.z_dimension;
+    dcm->xyzDim[4] = num_vol;
+    //create a NIfTI header
+	headerDcm2Nii(*dcm, hdr, false);
+	//here we mimic SPM's spatial starting estimate SForm
+	mat44 m44;
+	LOAD_MAT44(m44, -hdr->pixdim[1], 0.0f, 0.0f, ((float)dcm->xyzDim[1]-2.0)/2.0*dcm->xyzMM[1],
+		0.0f, -hdr->pixdim[2], 0.0f, ((float)dcm->xyzDim[2]-2.0)/2.0*dcm->xyzMM[2],
+		0.0f, 0.0f, -hdr->pixdim[3], ((float)dcm->xyzDim[3]-2.0)/2.0*dcm->xyzMM[3]);
+	setQSForm(hdr, m44, false);
+	//make sure image does not include a spatial matrix
+	bool isMatrix = false;
+	for (int i = 0; i < 9; i++)
+		if (ihdr.mtx[i] != 0.0) isMatrix = true;
+	if (isMatrix)
+		printWarning("ECAT volume appears to store spatial transformation matrix (please check for updates)\n");
+	hdr->scl_slope = ihdr.scale_factor * mhdr.ecat_calibration_factor;
+    if (mhdr.gantry_tilt != 0.0) printMessage("Warning: ECAT gantry tilt not supported %g\n", mhdr.gantry_tilt);
+    return img;
+}
+
+int  convert_foreign (const char *fn, struct TDCMopts opts){
+	struct nifti_1_header hdr;
+	struct TDICOMdata dcm = clear_dicom_data();
+	unsigned char * img = NULL;
+	img = readEcat7(fn, &dcm, &hdr, opts, false); //false: silent, do not report if file is not ECAT format
+	if (!img) return EXIT_FAILURE;
+	char niiFilename[1024];
+	int ret = nii_createFilename(dcm, niiFilename, opts);
+	printMessage("Saving ECAT as '%s'\n", niiFilename);
+	if (ret != EXIT_SUCCESS) return ret;
+	struct TDTI4D dti4D;
+	dti4D.S[0].sliceTiming = -1.0;
+	nii_SaveBIDS(niiFilename, dcm, opts, &dti4D, &hdr);
+	ret = nii_saveNII(niiFilename, hdr, img, opts);
+	free(img);
+    return ret;
+}// open_foreign()
diff --git a/console/nii_foreign.h b/console/nii_foreign.h
new file mode 100644
index 0000000..cf4bc46
--- /dev/null
+++ b/console/nii_foreign.h
@@ -0,0 +1,19 @@
+//Attempt to open non-DICOM image
+
+#ifndef _NII_FOREIGN_
+#define _NII_FOREIGN_
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#include "nii_dicom_batch.h"
+
+//int  open_foreign (const char *fn);
+int  convert_foreign (const char *fn, struct TDCMopts opts);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/console/nii_ortho.cpp b/console/nii_ortho.cpp
index 5f00430..6f72ce4 100644
--- a/console/nii_ortho.cpp
+++ b/console/nii_ortho.cpp
@@ -1,4 +1,6 @@
+#ifndef HAVE_R
 #include "nifti1.h"
+#endif
 #include "nifti1_io_core.h"
 #include "nii_ortho.h"
 #include <math.h>
@@ -19,6 +21,8 @@
 #endif
 //#define MY_DEBUG //verbose text reporting
 
+#include "print.h"
+
 typedef struct  {
     int v[3];
 } vec3i;
@@ -313,7 +317,7 @@ vec3 minCornerFlip (struct nifti_1_header *h, vec3i* flipVec)
 
 #ifdef MY_DEBUG
 void reportMat44o(char *str, mat44 A) {
-    printf("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str,
+    printMessage("%s = [%g %g %g %g; %g %g %g %g; %g %g %g %g; 0 0 0 1]\n",str,
            A.m[0][0],A.m[0][1],A.m[0][2],A.m[0][3],
            A.m[1][0],A.m[1][1],A.m[1][2],A.m[1][3],
            A.m[2][0],A.m[2][1],A.m[2][2],A.m[2][3]);
@@ -331,14 +335,14 @@ unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
     }
     if (h->sform_code == NIFTI_XFORM_UNKNOWN) {
          #ifdef MY_DEBUG
-         printf("No Q or S spatial transforms - assuming canonical orientation");
+         printMessage("No Q or S spatial transforms - assuming canonical orientation");
          #endif
          return img;
     }
     mat44 s = sFormMat(h);
     if (isMat44Canonical( s)) {
         #ifdef MY_DEBUG
-        printf("Image in perfect alignment: no need to reorient");
+        printMessage("Image in perfect alignment: no need to reorient");
         #endif
         return img;
     }
@@ -348,7 +352,7 @@ unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
     vec3i orientVec = setOrientVec(orient);
     if ((orientVec.v[0]==1) && (orientVec.v[1]==2) && (orientVec.v[2]==3) ) {
         #ifdef MY_DEBUG
-        printf("Image already near best orthogonal alignment: no need to reorient\n");
+        printMessage("Image already near best orthogonal alignment: no need to reorient\n");
         #endif
         return img;
     }
@@ -365,8 +369,8 @@ unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h) {
         h->dim[3] = h->dim[3] / 3;
     }
     #ifdef MY_DEBUG
-    printf("NewRotation= %d %d %d\n", orientVec.v[0],orientVec.v[1],orientVec.v[2]);
-    printf("MinCorner= %.2f %.2f %.2f\n", minMM.v[0],minMM.v[1],minMM.v[2]);
+    printMessage("NewRotation= %d %d %d\n", orientVec.v[0],orientVec.v[1],orientVec.v[2]);
+    printMessage("MinCorner= %.2f %.2f %.2f\n", minMM.v[0],minMM.v[1],minMM.v[2]);
     reportMat44o((char*)"input",s);
     s = sFormMat(h);
     reportMat44o((char*)"output",s);
diff --git a/console/nii_ortho.h b/console/nii_ortho.h
index 1fc9ac7..3fe6124 100644
--- a/console/nii_ortho.h
+++ b/console/nii_ortho.h
@@ -5,7 +5,10 @@
 extern "C" {
 #endif
     
+#ifndef HAVE_R
 #include "nifti1.h"
+#endif
+    
     void mat2sForm (struct nifti_1_header *h, mat44 s);
     bool isMat44Canonical(mat44 R);
 	unsigned char *  nii_setOrtho(unsigned char* img, struct nifti_1_header *h);
diff --git a/console/print.h b/console/print.h
new file mode 100755
index 0000000..497d3ec
--- /dev/null
+++ b/console/print.h
@@ -0,0 +1,45 @@
+//This unit allows us to re-direct text messages
+// For standard C programs send text messages to the console via "printf"
+//     The XCode project shows how you can re-direct these messages to a NSTextView
+// For QT programs, we can sent text messages to the cout buffer
+//     The QT project shows how you can re-direct these to a Qtextedit
+// For R programs, we can intercept these messages.
+
+#ifndef _R_PRINT_H_
+	#define _R_PRINT_H_
+	#include <stdarg.h>
+	#ifdef HAVE_R
+		#define R_USE_C99_IN_CXX
+		#include <R_ext/Print.h>
+		#define printMessage(...) { Rprintf("[dcm2niix info] "); Rprintf(__VA_ARGS__); }
+		#define printWarning(...) { Rprintf("[dcm2niix WARNING] "); Rprintf(__VA_ARGS__); }
+		#define printError(...) { Rprintf("[dcm2niix ERROR] "); Rprintf(__VA_ARGS__); }
+	#else
+		#ifdef myUseCOut
+			//for piping output to Qtextedit
+			// printf and cout buffers are not the same
+			// #define printMessage(...) ({fprintf(stdout,__VA_ARGS__);})
+			#include <iostream>
+			template< typename... Args >
+			void printMessage( const char* format, Args... args ) {
+			  //std::printf( format, args... );
+			  //fprintf(stdout,"Short read on %s: Expected 512, got %zd\n",path, bytes_read);
+			  int length = std::snprintf( nullptr, 0, format, args... );
+			  if ( length <= 0 ) return;
+			  char* buf = new char[length + 1];
+			  std::snprintf( buf, length + 1, format, args... );
+			  std::cout << buf;
+			  delete[] buf;
+			}
+		#else
+			#include<stdio.h>
+			#define printMessage printf
+		#endif //myUseCOut
+		//n.b. use ({}) for multi-line macros http://www.geeksforgeeks.org/multiline-macros-in-c/
+		//these next lines work on GCC but not _MSC_VER
+		// #define printWarning(...) ({printMessage("Warning: "); printMessage(__VA_ARGS__);})
+		// #define printError(...) ({ printMessage("Error: "); printMessage(__VA_ARGS__);})
+		#define printWarning(...) do {printMessage("Warning: "); printMessage(__VA_ARGS__);} while(0)
+		#define printError(...) do { printMessage("Error: "); printMessage(__VA_ARGS__);} while(0)
+	#endif //HAVE_R
+#endif //_R_PRINT_H_
diff --git a/ucm.cmake b/console/ucm.cmake
similarity index 82%
rename from ucm.cmake
rename to console/ucm.cmake
index 32b407c..2ab74ea 100644
--- a/ucm.cmake
+++ b/console/ucm.cmake
@@ -1,611 +1,634 @@
-#
-# ucm.cmake - useful cmake macros
-#
-# Copyright (c) 2016 Viktor Kirilov
-#
-# Distributed under the MIT Software License
-# See accompanying file LICENSE.txt or copy at
-# https://opensource.org/licenses/MIT
-#
-# The documentation can be found at the library's page:
-# https://github.com/onqtam/ucm
-
-cmake_minimum_required(VERSION 2.8.12)
-
-include(CMakeParseArguments)
-
-# optionally include cotire - the git submodule might not be inited (or the user might have already included it)
-if(NOT COMMAND cotire)
-    include(${CMAKE_CURRENT_LIST_DIR}/../cotire/CMake/cotire.cmake OPTIONAL)
-endif()
-
-if(COMMAND cotire AND "1.7.7" VERSION_LESS "${COTIRE_CMAKE_MODULE_VERSION}")
-    set(ucm_with_cotire 1)
-else()
-    set(ucm_with_cotire 0)
-endif()
-
-# option(UCM_UNITY_BUILD          "Enable unity build for targets registered with the ucm_add_target() macro"                     OFF)
-# option(UCM_NO_COTIRE_FOLDER     "Do not use a cotire folder in the solution explorer for all unity and cotire related targets"  ON)
-
-# ucm_add_flags
-# Adds compiler flags to CMAKE_<LANG>_FLAGS or to a specific config
-macro(ucm_add_flags)
-    cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "CONFIG" "" ${ARGN})
-    
-    # determine to which flags to add
-    set(CXX_FLAGS CMAKE_CXX_FLAGS)
-    set(C_FLAGS CMAKE_C_FLAGS)
-    if(NOT "${ARG_CONFIG}" STREQUAL "")
-        string(TOUPPER ${ARG_CONFIG} ARG_CONFIG)
-        set(CXX_FLAGS CMAKE_CXX_FLAGS_${ARG_CONFIG})
-        set(C_FLAGS CMAKE_C_FLAGS_${ARG_CONFIG})
-    endif()
-    
-    # clear the old flags
-    if(${ARG_CLEAR_OLD})
-        if("${ARG_CXX}" OR NOT "${ARG_C}")
-            set(${CXX_FLAGS} "")
-        endif()
-        if("${ARG_C}" OR NOT "${ARG_CXX}")
-            set(${C_FLAGS} "")
-        endif()
-    endif()
-    
-    # add all the passed flags
-    foreach(flag ${ARG_UNPARSED_ARGUMENTS})
-        if("${ARG_CXX}" OR NOT "${ARG_C}")
-            set(${CXX_FLAGS} "${${CXX_FLAGS}} ${flag}")
-        endif()
-        if("${ARG_C}" OR NOT "${ARG_CXX}")
-            set(${C_FLAGS} "${${C_FLAGS}} ${flag}")
-        endif()
-    endforeach()
-endmacro()
-
-# ucm_set_flags
-# Sets the CMAKE_<LANG>_FLAGS compiler flags or for a specific config
-macro(ucm_set_flags)
-    ucm_add_flags(CLEAR_OLD ${ARGN})
-endmacro()
-
-# ucm_add_linker_flags
-# Adds linker flags to CMAKE_<TYPE>_LINKER_FLAGS or to a specific config
-macro(ucm_add_linker_flags)
-    cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "CONFIG" "" ${ARGN})
-    
-    string(TOUPPER "${ARG_CONFIG}" ARG_CONFIG)
-    
-    if(NOT ${ARG_EXE} AND NOT ${ARG_MODULE} AND NOT ${ARG_SHARED} AND NOT ${ARG_STATIC})
-        set(ARG_EXE 1)
-        set(ARG_MODULE 1)
-        set(ARG_SHARED 1)
-        set(ARG_STATIC 1)
-    endif()
-    
-    set(flags_configs "")
-    if(${ARG_EXE})
-        if(NOT "${ARG_CONFIG}" STREQUAL "")
-            list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${ARG_CONFIG})
-        else()
-            list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS)
-        endif()
-    endif()
-    if(${ARG_MODULE})
-        if(NOT "${ARG_CONFIG}" STREQUAL "")
-            list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${ARG_CONFIG})
-        else()
-            list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS)
-        endif()
-    endif()
-    if(${ARG_SHARED})
-        if(NOT "${ARG_CONFIG}" STREQUAL "")
-            list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${ARG_CONFIG})
-        else()
-            list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS)
-        endif()
-    endif()
-    if(${ARG_STATIC})
-        if(NOT "${ARG_CONFIG}" STREQUAL "")
-            list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${ARG_CONFIG})
-        else()
-            list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS)
-        endif()
-    endif()
-    
-    # clear the old flags
-    if(${ARG_CLEAR_OLD})
-        foreach(flags ${flags_configs})
-            set(${flags} "")
-        endforeach()
-    endif()
-    
-    # add all the passed flags
-    foreach(flag ${ARG_UNPARSED_ARGUMENTS})
-        foreach(flags ${flags_configs})
-            set(${flags} "${${flags}} ${flag}")
-        endforeach()
-    endforeach()
-endmacro()
-
-# ucm_set_linker_flags
-# Sets the CMAKE_<TYPE>_LINKER_FLAGS linker flags or for a specific config
-macro(ucm_set_linker_flags)
-    ucm_add_linker_flags(CLEAR_OLD ${ARGN})
-endmacro()
-
-# ucm_gather_flags
-# Gathers all lists of flags for printing or manipulation
-macro(ucm_gather_flags with_linker result)
-    set(${result} "")
-    # add the main flags without a config
-    list(APPEND ${result} CMAKE_C_FLAGS)
-    list(APPEND ${result} CMAKE_CXX_FLAGS)
-    if(${with_linker})
-        list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS)
-        list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS)
-        list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS)
-        list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS)
-    endif()
-    
-    if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
-        # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set
-        string(TOUPPER ${CMAKE_BUILD_TYPE} config)
-        list(APPEND ${result} CMAKE_C_FLAGS_${config})
-        list(APPEND ${result} CMAKE_CXX_FLAGS_${config})
-        if(${with_linker})
-            list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config})
-            list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config})
-            list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config})
-            list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config})
-        endif()
-    else()
-        # handle multi config generators (like msvc, xcode)
-        foreach(config ${CMAKE_CONFIGURATION_TYPES})
-            string(TOUPPER ${config} config)
-            list(APPEND ${result} CMAKE_C_FLAGS_${config})
-            list(APPEND ${result} CMAKE_CXX_FLAGS_${config})
-            if(${with_linker})
-                list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config})
-                list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config})
-                list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config})
-                list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config})
-            endif()
-        endforeach()
-    endif()
-endmacro()
-
-# ucm_set_runtime
-# Sets the runtime (static/dynamic) for msvc/gcc
-macro(ucm_set_runtime)
-    cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN})
-    
-    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "")
-        message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!")
-    endif()
-    
-    ucm_gather_flags(0 flags_configs)
-    
-    # add/replace the flags
-    # note that if the user has messed with the flags directly this function might fail
-    # - for example if with MSVC and the user has removed the flags - here we just switch/replace them
-    if("${ARG_STATIC}")
-        foreach(flags ${flags_configs})
-            if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-                if(NOT ${flags} MATCHES "-static-libstdc\\+\\+")
-                    set(${flags} "${${flags}} -static-libstdc++")
-                endif()
-                if(NOT ${flags} MATCHES "-static-libgcc")
-                    set(${flags} "${${flags}} -static-libgcc")
-                endif()
-            elseif(MSVC)
-                if(${flags} MATCHES "/MD")
-                    string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}")
-                endif()
-            endif()
-        endforeach()
-    elseif("${ARG_DYNAMIC}")
-        foreach(flags ${flags_configs})
-            if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-                if(${flags} MATCHES "-static-libstdc\\+\\+")
-                    string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}")
-                endif()
-                if(${flags} MATCHES "-static-libgcc")
-                    string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}")
-                endif()
-            elseif(MSVC)
-                if(${flags} MATCHES "/MT")
-                    string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}")
-                endif()
-            endif()
-        endforeach()
-    endif()
-endmacro()
-
-# ucm_print_flags
-# Prints all compiler flags for all configurations
-macro(ucm_print_flags)
-    ucm_gather_flags(1 flags_configs)
-    message("")
-    foreach(flags ${flags_configs})
-        message("${flags}: ${${flags}}")
-    endforeach()
-    message("")
-endmacro()
-
-# ucm_count_sources
-# Counts the number of source files
-macro(ucm_count_sources)
-    cmake_parse_arguments(ARG "" "RESULT" "" ${ARGN})
-    if(${ARG_RESULT} STREQUAL "")
-        message(FATAL_ERROR "Need to pass RESULT and a variable name to ucm_count_sources()")
-    endif()
-    
-    set(result 0)
-    foreach(SOURCE_FILE ${ARG_UNPARSED_ARGUMENTS})
-        if("${SOURCE_FILE}" MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx|i|ii\)$)
-            math(EXPR result "${result} + 1")
-        endif()
-    endforeach()
-    set(${ARG_RESULT} ${result})
-endmacro()
-
-# ucm_include_file_in_sources
-# Includes the file to the source with compiler flags
-macro(ucm_include_file_in_sources)
-    cmake_parse_arguments(ARG "" "HEADER" "" ${ARGN})
-    if(${ARG_HEADER} STREQUAL "")
-        message(FATAL_ERROR "Need to pass HEADER and a header file to ucm_include_file_in_sources()")
-    endif()
-    
-    foreach(src ${ARG_UNPARSED_ARGUMENTS})
-        if(${src} MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx\)$)
-            # get old flags
-            get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS)
-            if(old_compile_flags STREQUAL "NOTFOUND")
-                set(old_compile_flags "")
-            endif()
-            
-            # update flags
-            if(MSVC)
-                set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS
-                    "${old_compile_flags} /FI\"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"")
-            else()
-                set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS
-                    "${old_compile_flags} -include \"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"")
-            endif()
-        endif()
-    endforeach()
-endmacro()
-
-# ucm_dir_list
-# Returns a list of subdirectories for a given directory
-macro(ucm_dir_list thedir result)
-    file(GLOB sub-dir "${thedir}/*")
-    set(list_of_dirs "")
-    foreach(dir ${sub-dir})
-        if(IS_DIRECTORY ${dir})
-            get_filename_component(DIRNAME ${dir} NAME)
-            LIST(APPEND list_of_dirs ${DIRNAME})
-        endif()
-    endforeach()
-    set(${result} ${list_of_dirs})
-endmacro()
-
-# ucm_trim_front_words
-# Trims X times the front word from a string separated with "/" and removes
-# the front "/" characters after that (used for filters for visual studio)
-macro(ucm_trim_front_words source out num_filter_trims)
-    set(result "${source}")
-    set(counter 0)
-    while(${counter} LESS ${num_filter_trims})
-        MATH(EXPR counter "${counter} + 1")
-        # removes everything at the front up to a "/" character
-        string(REGEX REPLACE "^([^/]+)" "" result "${result}")
-        # removes all consecutive "/" characters from the front
-        string(REGEX REPLACE "^(/+)" "" result "${result}")
-    endwhile()
-    set(${out} ${result})
-endmacro()
-
-# ucm_remove_files
-# Removes source files from a list of sources (path is the relative path for it to be found)
-macro(ucm_remove_files)
-    cmake_parse_arguments(ARG "" "FROM" "" ${ARGN})
-    
-    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
-        message(FATAL_ERROR "Need to pass some relative files to ucm_remove_files()")
-    endif()
-    if(${ARG_FROM} STREQUAL "")
-        message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_files()")
-    endif()
-    
-    foreach(cur_file ${ARG_UNPARSED_ARGUMENTS})
-        list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
-    endforeach()
-endmacro()
-
-# ucm_remove_directories
-# Removes all source files from the given directories from the sources list
-macro(ucm_remove_directories)
-    cmake_parse_arguments(ARG "" "FROM" "MATCHES" ${ARGN})
-    
-    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
-        message(FATAL_ERROR "Need to pass some relative directories to ucm_remove_directories()")
-    endif()
-    if(${ARG_FROM} STREQUAL "")
-        message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_directories()")
-    endif()
-    
-    foreach(cur_dir ${ARG_UNPARSED_ARGUMENTS})
-        foreach(cur_file ${${ARG_FROM}})
-            string(REGEX MATCH ${cur_dir} res ${cur_file})
-            if(NOT "${res}" STREQUAL "")
-                if("${ARG_MATCHES}" STREQUAL "")
-                    list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
-                else()
-                    foreach(curr_ptrn ${ARG_MATCHES})
-                        string(REGEX MATCH ${curr_ptrn} res ${cur_file})
-                        if(NOT "${res}" STREQUAL "")
-                            list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
-                            break()
-                        endif()
-                    endforeach()
-                endif()
-            endif()
-        endforeach()
-    endforeach()
-endmacro()
-
-# ucm_add_files_impl
-macro(ucm_add_files_impl result trim files)
-    foreach(cur_file ${files})
-        SET(${result} ${${result}} ${cur_file})
-        get_filename_component(FILEPATH ${cur_file} PATH)
-        ucm_trim_front_words("${FILEPATH}" FILEPATH "${trim}")
-        # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...)
-        STRING(REPLACE "/" "\\" FILTERS "${FILEPATH}")
-        SOURCE_GROUP("${FILTERS}" FILES ${cur_file})
-    endforeach()
-endmacro()
-
-# ucm_add_files
-# Adds files to a list of sources
-macro(ucm_add_files)
-    cmake_parse_arguments(ARG "" "TO;FILTER_POP" "" ${ARGN})
-    
-    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
-        message(FATAL_ERROR "Need to pass some relative files to ucm_add_files()")
-    endif()
-    if(${ARG_TO} STREQUAL "")
-        message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_files()")
-    endif()
-    
-    if("${ARG_FILTER_POP}" STREQUAL "")
-        set(ARG_FILTER_POP 0)
-    endif()
-    
-    ucm_add_files_impl(${ARG_TO} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}")
-endmacro()
-
-# ucm_add_dir_impl
-macro(ucm_add_dir_impl result rec trim dirs_in)
-    set(dirs "${dirs_in}")
-    
-    # handle the "" and "." cases
-    if("${dirs}" STREQUAL "" OR "${dirs}" STREQUAL ".")
-        set(dirs "./")
-    endif()
-    
-    foreach(cur_dir ${dirs})
-        # to circumvent some linux/cmake/path issues - barely made it work...
-        if(cur_dir STREQUAL "./")
-            set(cur_dir "")
-        else()
-            set(cur_dir "${cur_dir}/")
-        endif()
-        
-        # since unix is case sensitive - add these valid extensions too
-        # we don't use "UNIX" but instead "CMAKE_HOST_UNIX" because we might be cross
-        # compiling (for example emscripten) under windows and UNIX may be set to 1
-        # Also OSX is case insensitive like windows...
-        set(additional_file_extensions "")
-        if(CMAKE_HOST_UNIX AND NOT APPLE)
-            set(additional_file_extensions
-                "${cur_dir}*.CPP"
-                "${cur_dir}*.C"
-                "${cur_dir}*.H"
-                "${cur_dir}*.HPP"
-                )
-        endif()
-        
-        # find all sources and set them as result
-        FILE(GLOB found_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
-        # https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Overall-Options.html#index-file-name-suffix-71
-        # sources
-            "${cur_dir}*.cpp"
-            "${cur_dir}*.cxx"
-            "${cur_dir}*.c++"
-            "${cur_dir}*.cc"
-            "${cur_dir}*.cp"
-            "${cur_dir}*.c"
-            "${cur_dir}*.i"
-            "${cur_dir}*.ii"
-        # headers
-            "${cur_dir}*.h"
-            "${cur_dir}*.h++"
-            "${cur_dir}*.hpp"
-            "${cur_dir}*.hxx"
-            "${cur_dir}*.hh"
-            "${cur_dir}*.inl"
-            "${cur_dir}*.inc"
-            "${cur_dir}*.ipp"
-            "${cur_dir}*.ixx"
-            "${cur_dir}*.txx"
-            "${cur_dir}*.tpp"
-            "${cur_dir}*.tcc"
-            "${cur_dir}*.tpl"
-            ${additional_file_extensions})
-        SET(${result} ${${result}} ${found_sources})
-        
-        # set the proper filters
-        ucm_trim_front_words("${cur_dir}" cur_dir "${trim}")
-        # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...)
-        STRING(REPLACE "/" "\\" FILTERS "${cur_dir}")
-        SOURCE_GROUP("${FILTERS}" FILES ${found_sources})
-    endforeach()
-    
-    if(${rec})
-        foreach(cur_dir ${dirs})
-            ucm_dir_list("${cur_dir}" subdirs)
-            foreach(subdir ${subdirs})
-                ucm_add_dir_impl(${result} ${rec} ${trim} "${cur_dir}/${subdir}")
-            endforeach()
-        endforeach()
-    endif()
-endmacro()
-
-# ucm_add_dirs
-# Adds all files from directories traversing them recursively to a list of sources
-# and generates filters according to their location (accepts relative paths only).
-# Also this macro trims X times the front word from the filter string for visual studio filters.
-macro(ucm_add_dirs)
-    cmake_parse_arguments(ARG "RECURSIVE" "TO;FILTER_POP" "" ${ARGN})
-    
-    if(${ARG_TO} STREQUAL "")
-        message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_dirs()")
-    endif()
-    
-    if("${ARG_FILTER_POP}" STREQUAL "")
-        set(ARG_FILTER_POP 0)
-    endif()
-    
-    ucm_add_dir_impl(${ARG_TO} ${ARG_RECURSIVE} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}")
-endmacro()
-
-# ucm_add_target
-# Adds a target eligible for cotiring - unity build and/or precompiled header
-macro(ucm_add_target)
-    cmake_parse_arguments(ARG "UNITY" "NAME;TYPE;PCH_FILE;CPP_PER_UNITY" "UNITY_EXCLUDED;SOURCES" ${ARGN})
-    
-    if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
-        message(FATAL_ERROR "Unrecognized options passed to ucm_add_target()")
-    endif()
-    if("${ARG_NAME}" STREQUAL "")
-        message(FATAL_ERROR "Need to pass NAME and a name for the target to ucm_add_target()")
-    endif()
-    set(valid_types EXECUTABLE STATIC SHARED MODULE)
-    list(FIND valid_types "${ARG_TYPE}" is_type_valid)
-    if(${is_type_valid} STREQUAL "-1")
-        message(FATAL_ERROR "Need to pass TYPE and the type for the target [EXECUTABLE/STATIC/SHARED/MODULE] to ucm_add_target()")
-    endif()
-    if("${ARG_SOURCES}" STREQUAL "")
-        message(FATAL_ERROR "Need to pass SOURCES and a list of source files to ucm_add_target()")
-    endif()
-    
-    # init with the global unity flag
-    set(do_unity ${UCM_UNITY_BUILD})
-    
-    # check the UNITY argument
-    if(NOT ARG_UNITY)
-        set(do_unity FALSE)
-    endif()
-    
-    # if target is excluded through the exclusion list
-    list(FIND UCM_UNITY_BUILD_EXCLUDE_TARGETS ${ARG_NAME} is_target_excluded)
-    if(NOT ${is_target_excluded} STREQUAL "-1")
-        set(do_unity FALSE)
-    endif()
-    
-    # unity build only for targets with > 1 source file (otherwise there will be an additional unnecessary target)
-    if(do_unity) # optimization
-        ucm_count_sources(${ARG_SOURCES} RESULT num_sources)
-        if(${num_sources} LESS 2)
-            set(do_unity FALSE)
-        endif()
-    endif()
-    
-    set(wanted_cotire ${do_unity})
-    
-    # if cotire cannot be used
-    if(do_unity AND NOT ucm_with_cotire)
-        set(do_unity FALSE)
-    endif()
-    
-	# inform the developer that the current target might benefit from a unity build
-	if(NOT ARG_UNITY AND ${UCM_UNITY_BUILD})
-		ucm_count_sources(${ARG_SOURCES} RESULT num_sources)
-		if(${num_sources} GREATER 1)
-			message(AUTHOR_WARNING "Target '${ARG_NAME}' may benefit from a unity build.\nIt has ${num_sources} sources - enable with UNITY flag")
-		endif()
-	endif()
-    
-    # prepare for the unity build
-    set(orig_target ${ARG_NAME})
-    if(do_unity)
-        # the original target will be added with a different name than the requested
-        set(orig_target ${ARG_NAME}_ORIGINAL)
-        
-        # exclude requested files from unity build of the current target
-        foreach(excluded_file "${ARG_UNITY_EXCLUDED}")
-            set_source_files_properties(${excluded_file} PROPERTIES COTIRE_EXCLUDED TRUE)
-        endforeach()
-    endif()
-    
-    # add the original target
-    if(${ARG_TYPE} STREQUAL "EXECUTABLE")
-        add_executable(${orig_target} ${ARG_SOURCES})
-    else()
-        add_library(${orig_target} ${ARG_TYPE} ${ARG_SOURCES})
-    endif()
-    
-    if(do_unity)
-        # set the number of unity cpp files to be used for the unity target
-        if(NOT "${ARG_CPP_PER_UNITY}" STREQUAL "")
-            set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${ARG_CPP_PER_UNITY}")
-		else()
-			set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "100")
-		endif()
-        
-        if(NOT "${ARG_PCH_FILE}" STREQUAL "")
-            set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}")
-        else()
-            set_target_properties(${orig_target} PROPERTIES COTIRE_ENABLE_PRECOMPILED_HEADER FALSE)
-        endif()
-        # add a unity target for the original one with the name intended for the original
-        set_target_properties(${orig_target} PROPERTIES COTIRE_UNITY_TARGET_NAME ${ARG_NAME})
-        
-        # this is the library call that does the magic
-        cotire(${orig_target})
-        
-        # disable the original target and enable the unity one
-        get_target_property(unity_target_name ${orig_target} COTIRE_UNITY_TARGET_NAME)
-        set_target_properties(${orig_target} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
-        set_target_properties(${unity_target_name} PROPERTIES EXCLUDE_FROM_ALL 0 EXCLUDE_FROM_DEFAULT_BUILD 0)
-        
-        # also set the name of the target output as the original one
-        set_target_properties(${unity_target_name} PROPERTIES OUTPUT_NAME ${ARG_NAME})
-        if(UCM_NO_COTIRE_FOLDER)
-            # reset the folder property so all unity targets dont end up in a single folder in the solution explorer of VS
-            set_target_properties(${unity_target_name} PROPERTIES FOLDER "")
-            set_target_properties(all_unity PROPERTIES FOLDER "")
-            set_target_properties(clean_cotire PROPERTIES FOLDER "")
-        endif()
-    elseif(NOT "${ARG_PCH_FILE}" STREQUAL "")
-        set(wanted_cotire TRUE)
-        if(ucm_with_cotire)
-            set_target_properties(${orig_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
-            set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}")
-            cotire(${orig_target})
-        endif()
-    endif()
-    
-    # print a message if the target was requested to be cotired but it couldn't
-    if(wanted_cotire AND NOT ucm_with_cotire)
-        if(NOT COMMAND cotire)
-            message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire isn't loaded")
-        else()
-            message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire is older than the required version")
-        endif()
-    endif()
-endmacro()
+#
+# ucm.cmake - useful cmake macros
+#
+# Copyright (c) 2016 Viktor Kirilov
+#
+# Distributed under the MIT Software License
+# See accompanying file LICENSE.txt or copy at
+# https://opensource.org/licenses/MIT
+#
+# The documentation can be found at the library's page:
+# https://github.com/onqtam/ucm
+
+cmake_minimum_required(VERSION 2.8.12)
+
+include(CMakeParseArguments)
+
+# optionally include cotire - the git submodule might not be inited (or the user might have already included it)
+if(NOT COMMAND cotire)
+    include(${CMAKE_CURRENT_LIST_DIR}/../cotire/CMake/cotire.cmake OPTIONAL)
+endif()
+
+if(COMMAND cotire AND "1.7.7" VERSION_LESS "${COTIRE_CMAKE_MODULE_VERSION}")
+    set(ucm_with_cotire 1)
+else()
+    set(ucm_with_cotire 0)
+endif()
+
+# option(UCM_UNITY_BUILD          "Enable unity build for targets registered with the ucm_add_target() macro"                     OFF)
+# option(UCM_NO_COTIRE_FOLDER     "Do not use a cotire folder in the solution explorer for all unity and cotire related targets"  ON)
+
+# ucm_add_flags
+# Adds compiler flags to CMAKE_<LANG>_FLAGS or to a specific config
+macro(ucm_add_flags)
+    cmake_parse_arguments(ARG "C;CXX;CLEAR_OLD" "" "CONFIG" ${ARGN})
+
+    if(NOT ARG_CONFIG)
+        set(ARG_CONFIG " ")
+    endif()
+
+    foreach(CONFIG ${ARG_CONFIG})
+        # determine to which flags to add
+        if(NOT ${CONFIG} STREQUAL " ")
+            string(TOUPPER ${CONFIG} CONFIG)
+            set(CXX_FLAGS CMAKE_CXX_FLAGS_${CONFIG})
+            set(C_FLAGS CMAKE_C_FLAGS_${CONFIG})
+        else()
+            set(CXX_FLAGS CMAKE_CXX_FLAGS)
+            set(C_FLAGS CMAKE_C_FLAGS)
+        endif()
+
+        # clear the old flags
+        if(${ARG_CLEAR_OLD})
+            if("${ARG_CXX}" OR NOT "${ARG_C}")
+                set(${CXX_FLAGS} "")
+            endif()
+            if("${ARG_C}" OR NOT "${ARG_CXX}")
+                set(${C_FLAGS} "")
+            endif()
+        endif()
+
+        # add all the passed flags
+        foreach(flag ${ARG_UNPARSED_ARGUMENTS})
+            if("${ARG_CXX}" OR NOT "${ARG_C}")
+                set(${CXX_FLAGS} "${${CXX_FLAGS}} ${flag}")
+            endif()
+            if("${ARG_C}" OR NOT "${ARG_CXX}")
+                set(${C_FLAGS} "${${C_FLAGS}} ${flag}")
+            endif()
+        endforeach()
+    endforeach()
+
+endmacro()
+
+# ucm_set_flags
+# Sets the CMAKE_<LANG>_FLAGS compiler flags or for a specific config
+macro(ucm_set_flags)
+    ucm_add_flags(CLEAR_OLD ${ARGN})
+endmacro()
+
+# ucm_add_linker_flags
+# Adds linker flags to CMAKE_<TYPE>_LINKER_FLAGS or to a specific config
+macro(ucm_add_linker_flags)
+    cmake_parse_arguments(ARG "CLEAR_OLD;EXE;MODULE;SHARED;STATIC" "" "CONFIG" ${ARGN})
+
+    if(NOT ARG_CONFIG)
+        set(ARG_CONFIG " ")
+    endif()
+
+    foreach(CONFIG ${ARG_CONFIG})
+        string(TOUPPER "${ARG_CONFIG}" ARG_CONFIG)
+
+        if(NOT ${ARG_EXE} AND NOT ${ARG_MODULE} AND NOT ${ARG_SHARED} AND NOT ${ARG_STATIC})
+            set(ARG_EXE 1)
+            set(ARG_MODULE 1)
+            set(ARG_SHARED 1)
+            set(ARG_STATIC 1)
+        endif()
+
+        set(flags_configs "")
+        if(${ARG_EXE})
+            if(NOT "${CONFIG}" STREQUAL " ")
+                list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS_${CONFIG})
+            else()
+                list(APPEND flags_configs CMAKE_EXE_LINKER_FLAGS)
+            endif()
+        endif()
+        if(${ARG_MODULE})
+            if(NOT "${CONFIG}" STREQUAL " ")
+                list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS_${CONFIG})
+            else()
+                list(APPEND flags_configs CMAKE_MODULE_LINKER_FLAGS)
+            endif()
+        endif()
+        if(${ARG_SHARED})
+            if(NOT "${CONFIG}" STREQUAL " ")
+                list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS_${CONFIG})
+            else()
+                list(APPEND flags_configs CMAKE_SHARED_LINKER_FLAGS)
+            endif()
+        endif()
+        if(${ARG_STATIC})
+            if(NOT "${CONFIG}" STREQUAL " ")
+                list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS_${CONFIG})
+            else()
+                list(APPEND flags_configs CMAKE_STATIC_LINKER_FLAGS)
+            endif()
+        endif()
+
+        # clear the old flags
+        if(${ARG_CLEAR_OLD})
+            foreach(flags ${flags_configs})
+                set(${flags} "")
+            endforeach()
+        endif()
+
+        # add all the passed flags
+        foreach(flag ${ARG_UNPARSED_ARGUMENTS})
+            foreach(flags ${flags_configs})
+                set(${flags} "${${flags}} ${flag}")
+            endforeach()
+        endforeach()
+    endforeach()
+endmacro()
+
+# ucm_set_linker_flags
+# Sets the CMAKE_<TYPE>_LINKER_FLAGS linker flags or for a specific config
+macro(ucm_set_linker_flags)
+    ucm_add_linker_flags(CLEAR_OLD ${ARGN})
+endmacro()
+
+# ucm_gather_flags
+# Gathers all lists of flags for printing or manipulation
+macro(ucm_gather_flags with_linker result)
+    set(${result} "")
+    # add the main flags without a config
+    list(APPEND ${result} CMAKE_C_FLAGS)
+    list(APPEND ${result} CMAKE_CXX_FLAGS)
+    if(${with_linker})
+        list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS)
+        list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS)
+        list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS)
+        list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS)
+    endif()
+
+    if("${CMAKE_CONFIGURATION_TYPES}" STREQUAL "" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "")
+        # handle single config generators - like makefiles/ninja - when CMAKE_BUILD_TYPE is set
+        string(TOUPPER ${CMAKE_BUILD_TYPE} config)
+        list(APPEND ${result} CMAKE_C_FLAGS_${config})
+        list(APPEND ${result} CMAKE_CXX_FLAGS_${config})
+        if(${with_linker})
+            list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config})
+            list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config})
+            list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config})
+            list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config})
+        endif()
+    else()
+        # handle multi config generators (like msvc, xcode)
+        foreach(config ${CMAKE_CONFIGURATION_TYPES})
+            string(TOUPPER ${config} config)
+            list(APPEND ${result} CMAKE_C_FLAGS_${config})
+            list(APPEND ${result} CMAKE_CXX_FLAGS_${config})
+            if(${with_linker})
+                list(APPEND ${result} CMAKE_EXE_LINKER_FLAGS_${config})
+                list(APPEND ${result} CMAKE_MODULE_LINKER_FLAGS_${config})
+                list(APPEND ${result} CMAKE_SHARED_LINKER_FLAGS_${config})
+                list(APPEND ${result} CMAKE_STATIC_LINKER_FLAGS_${config})
+            endif()
+        endforeach()
+    endif()
+endmacro()
+
+# ucm_set_runtime
+# Sets the runtime (static/dynamic) for msvc/gcc
+macro(ucm_set_runtime)
+    cmake_parse_arguments(ARG "STATIC;DYNAMIC" "" "" ${ARGN})
+
+    if(ARG_UNPARSED_ARGUMENTS)
+        message(FATAL_ERROR "unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}")
+    endif()
+
+    if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" STREQUAL "")
+        message(AUTHOR_WARNING "ucm_set_runtime() does not support clang yet!")
+    endif()
+
+    ucm_gather_flags(0 flags_configs)
+
+    # add/replace the flags
+    # note that if the user has messed with the flags directly this function might fail
+    # - for example if with MSVC and the user has removed the flags - here we just switch/replace them
+    if("${ARG_STATIC}")
+        foreach(flags ${flags_configs})
+            if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+                if(NOT ${flags} MATCHES "-static-libstdc\\+\\+")
+                    set(${flags} "${${flags}} -static-libstdc++")
+                endif()
+                if(NOT ${flags} MATCHES "-static-libgcc")
+                    set(${flags} "${${flags}} -static-libgcc")
+                endif()
+            elseif(MSVC)
+                if(${flags} MATCHES "/MD")
+                    string(REGEX REPLACE "/MD" "/MT" ${flags} "${${flags}}")
+                endif()
+            endif()
+        endforeach()
+    elseif("${ARG_DYNAMIC}")
+        foreach(flags ${flags_configs})
+            if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+                if(${flags} MATCHES "-static-libstdc\\+\\+")
+                    string(REGEX REPLACE "-static-libstdc\\+\\+" "" ${flags} "${${flags}}")
+                endif()
+                if(${flags} MATCHES "-static-libgcc")
+                    string(REGEX REPLACE "-static-libgcc" "" ${flags} "${${flags}}")
+                endif()
+            elseif(MSVC)
+                if(${flags} MATCHES "/MT")
+                    string(REGEX REPLACE "/MT" "/MD" ${flags} "${${flags}}")
+                endif()
+            endif()
+        endforeach()
+    endif()
+endmacro()
+
+# ucm_print_flags
+# Prints all compiler flags for all configurations
+macro(ucm_print_flags)
+    ucm_gather_flags(1 flags_configs)
+    message("")
+    foreach(flags ${flags_configs})
+        message("${flags}: ${${flags}}")
+    endforeach()
+    message("")
+endmacro()
+
+# ucm_count_sources
+# Counts the number of source files
+macro(ucm_count_sources)
+    cmake_parse_arguments(ARG "" "RESULT" "" ${ARGN})
+    if(${ARG_RESULT} STREQUAL "")
+        message(FATAL_ERROR "Need to pass RESULT and a variable name to ucm_count_sources()")
+    endif()
+
+    set(result 0)
+    foreach(SOURCE_FILE ${ARG_UNPARSED_ARGUMENTS})
+        if("${SOURCE_FILE}" MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx|i|ii\)$)
+            math(EXPR result "${result} + 1")
+        endif()
+    endforeach()
+    set(${ARG_RESULT} ${result})
+endmacro()
+
+# ucm_include_file_in_sources
+# Includes the file to the source with compiler flags
+macro(ucm_include_file_in_sources)
+    cmake_parse_arguments(ARG "" "HEADER" "" ${ARGN})
+    if(${ARG_HEADER} STREQUAL "")
+        message(FATAL_ERROR "Need to pass HEADER and a header file to ucm_include_file_in_sources()")
+    endif()
+
+    foreach(src ${ARG_UNPARSED_ARGUMENTS})
+        if(${src} MATCHES \\.\(c|C|cc|cp|cpp|CPP|c\\+\\+|cxx\)$)
+            # get old flags
+            get_source_file_property(old_compile_flags ${src} COMPILE_FLAGS)
+            if(old_compile_flags STREQUAL "NOTFOUND")
+                set(old_compile_flags "")
+            endif()
+
+            # update flags
+            if(MSVC)
+                set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS
+                    "${old_compile_flags} /FI\"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"")
+            else()
+                set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS
+                    "${old_compile_flags} -include \"${CMAKE_CURRENT_SOURCE_DIR}/${ARG_HEADER}\"")
+            endif()
+        endif()
+    endforeach()
+endmacro()
+
+# ucm_dir_list
+# Returns a list of subdirectories for a given directory
+macro(ucm_dir_list thedir result)
+    file(GLOB sub-dir "${thedir}/*")
+    set(list_of_dirs "")
+    foreach(dir ${sub-dir})
+        if(IS_DIRECTORY ${dir})
+            get_filename_component(DIRNAME ${dir} NAME)
+            LIST(APPEND list_of_dirs ${DIRNAME})
+        endif()
+    endforeach()
+    set(${result} ${list_of_dirs})
+endmacro()
+
+# ucm_trim_front_words
+# Trims X times the front word from a string separated with "/" and removes
+# the front "/" characters after that (used for filters for visual studio)
+macro(ucm_trim_front_words source out num_filter_trims)
+    set(result "${source}")
+    set(counter 0)
+    while(${counter} LESS ${num_filter_trims})
+        MATH(EXPR counter "${counter} + 1")
+        # removes everything at the front up to a "/" character
+        string(REGEX REPLACE "^([^/]+)" "" result "${result}")
+        # removes all consecutive "/" characters from the front
+        string(REGEX REPLACE "^(/+)" "" result "${result}")
+    endwhile()
+    set(${out} ${result})
+endmacro()
+
+# ucm_remove_files
+# Removes source files from a list of sources (path is the relative path for it to be found)
+macro(ucm_remove_files)
+    cmake_parse_arguments(ARG "" "FROM" "" ${ARGN})
+
+    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Need to pass some relative files to ucm_remove_files()")
+    endif()
+    if(${ARG_FROM} STREQUAL "")
+        message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_files()")
+    endif()
+
+    foreach(cur_file ${ARG_UNPARSED_ARGUMENTS})
+        list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
+    endforeach()
+endmacro()
+
+# ucm_remove_directories
+# Removes all source files from the given directories from the sources list
+macro(ucm_remove_directories)
+    cmake_parse_arguments(ARG "" "FROM" "MATCHES" ${ARGN})
+
+    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Need to pass some relative directories to ucm_remove_directories()")
+    endif()
+    if(${ARG_FROM} STREQUAL "")
+        message(FATAL_ERROR "Need to pass FROM and a variable name to ucm_remove_directories()")
+    endif()
+
+    foreach(cur_dir ${ARG_UNPARSED_ARGUMENTS})
+        foreach(cur_file ${${ARG_FROM}})
+            string(REGEX MATCH ${cur_dir} res ${cur_file})
+            if(NOT "${res}" STREQUAL "")
+                if("${ARG_MATCHES}" STREQUAL "")
+                    list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
+                else()
+                    foreach(curr_ptrn ${ARG_MATCHES})
+                        string(REGEX MATCH ${curr_ptrn} res ${cur_file})
+                        if(NOT "${res}" STREQUAL "")
+                            list(REMOVE_ITEM ${ARG_FROM} ${cur_file})
+                            break()
+                        endif()
+                    endforeach()
+                endif()
+            endif()
+        endforeach()
+    endforeach()
+endmacro()
+
+# ucm_add_files_impl
+macro(ucm_add_files_impl result trim files)
+    foreach(cur_file ${files})
+        SET(${result} ${${result}} ${cur_file})
+        get_filename_component(FILEPATH ${cur_file} PATH)
+        ucm_trim_front_words("${FILEPATH}" FILEPATH "${trim}")
+        # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...)
+        STRING(REPLACE "/" "\\" FILTERS "${FILEPATH}")
+        SOURCE_GROUP("${FILTERS}" FILES ${cur_file})
+    endforeach()
+endmacro()
+
+# ucm_add_files
+# Adds files to a list of sources
+macro(ucm_add_files)
+    cmake_parse_arguments(ARG "" "TO;FILTER_POP" "" ${ARGN})
+
+    if("${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Need to pass some relative files to ucm_add_files()")
+    endif()
+    if(${ARG_TO} STREQUAL "")
+        message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_files()")
+    endif()
+
+    if("${ARG_FILTER_POP}" STREQUAL "")
+        set(ARG_FILTER_POP 0)
+    endif()
+
+    ucm_add_files_impl(${ARG_TO} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}")
+endmacro()
+
+# ucm_add_dir_impl
+macro(ucm_add_dir_impl result rec trim dirs_in additional_ext)
+    set(dirs "${dirs_in}")
+
+    # handle the "" and "." cases
+    if("${dirs}" STREQUAL "" OR "${dirs}" STREQUAL ".")
+        set(dirs "./")
+    endif()
+
+    foreach(cur_dir ${dirs})
+        # to circumvent some linux/cmake/path issues - barely made it work...
+        if(cur_dir STREQUAL "./")
+            set(cur_dir "")
+        else()
+            set(cur_dir "${cur_dir}/")
+        endif()
+
+        # since unix is case sensitive - add these valid extensions too
+        # we don't use "UNIX" but instead "CMAKE_HOST_UNIX" because we might be cross
+        # compiling (for example emscripten) under windows and UNIX may be set to 1
+        # Also OSX is case insensitive like windows...
+        set(additional_file_extensions "")
+        if(CMAKE_HOST_UNIX AND NOT APPLE)
+            set(additional_file_extensions
+                "${cur_dir}*.CPP"
+                "${cur_dir}*.C"
+                "${cur_dir}*.H"
+                "${cur_dir}*.HPP"
+                )
+        endif()
+
+        foreach(ext ${additional_ext})
+            list(APPEND additional_file_extensions "${cur_dir}*.${ext}")
+        endforeach()
+
+        # find all sources and set them as result
+        FILE(GLOB found_sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
+        # https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Overall-Options.html#index-file-name-suffix-71
+        # sources
+            "${cur_dir}*.cpp"
+            "${cur_dir}*.cxx"
+            "${cur_dir}*.c++"
+            "${cur_dir}*.cc"
+            "${cur_dir}*.cp"
+            "${cur_dir}*.c"
+            "${cur_dir}*.i"
+            "${cur_dir}*.ii"
+        # headers
+            "${cur_dir}*.h"
+            "${cur_dir}*.h++"
+            "${cur_dir}*.hpp"
+            "${cur_dir}*.hxx"
+            "${cur_dir}*.hh"
+            "${cur_dir}*.inl"
+            "${cur_dir}*.inc"
+            "${cur_dir}*.ipp"
+            "${cur_dir}*.ixx"
+            "${cur_dir}*.txx"
+            "${cur_dir}*.tpp"
+            "${cur_dir}*.tcc"
+            "${cur_dir}*.tpl"
+            ${additional_file_extensions})
+        SET(${result} ${${result}} ${found_sources})
+
+        # set the proper filters
+        ucm_trim_front_words("${cur_dir}" cur_dir "${trim}")
+        # replacing forward slashes with back slashes so filters can be generated (back slash used in parsing...)
+        STRING(REPLACE "/" "\\" FILTERS "${cur_dir}")
+        SOURCE_GROUP("${FILTERS}" FILES ${found_sources})
+    endforeach()
+
+    if(${rec})
+        foreach(cur_dir ${dirs})
+            ucm_dir_list("${cur_dir}" subdirs)
+            foreach(subdir ${subdirs})
+                ucm_add_dir_impl(${result} ${rec} ${trim} "${cur_dir}/${subdir}" "${additional_ext}")
+            endforeach()
+        endforeach()
+    endif()
+endmacro()
+
+# ucm_add_dirs
+# Adds all files from directories traversing them recursively to a list of sources
+# and generates filters according to their location (accepts relative paths only).
+# Also this macro trims X times the front word from the filter string for visual studio filters.
+macro(ucm_add_dirs)
+    cmake_parse_arguments(ARG "RECURSIVE" "TO;FILTER_POP" "ADDITIONAL_EXT" ${ARGN})
+
+    if(${ARG_TO} STREQUAL "")
+        message(FATAL_ERROR "Need to pass TO and a variable name to ucm_add_dirs()")
+    endif()
+
+    if("${ARG_FILTER_POP}" STREQUAL "")
+        set(ARG_FILTER_POP 0)
+    endif()
+
+    ucm_add_dir_impl(${ARG_TO} ${ARG_RECURSIVE} ${ARG_FILTER_POP} "${ARG_UNPARSED_ARGUMENTS}" "${ARG_ADDITIONAL_EXT}")
+endmacro()
+
+# ucm_add_target
+# Adds a target eligible for cotiring - unity build and/or precompiled header
+macro(ucm_add_target)
+    cmake_parse_arguments(ARG "UNITY" "NAME;TYPE;PCH_FILE;CPP_PER_UNITY" "UNITY_EXCLUDED;SOURCES" ${ARGN})
+
+    if(NOT "${ARG_UNPARSED_ARGUMENTS}" STREQUAL "")
+        message(FATAL_ERROR "Unrecognized options passed to ucm_add_target()")
+    endif()
+    if("${ARG_NAME}" STREQUAL "")
+        message(FATAL_ERROR "Need to pass NAME and a name for the target to ucm_add_target()")
+    endif()
+    set(valid_types EXECUTABLE STATIC SHARED MODULE)
+    list(FIND valid_types "${ARG_TYPE}" is_type_valid)
+    if(${is_type_valid} STREQUAL "-1")
+        message(FATAL_ERROR "Need to pass TYPE and the type for the target [EXECUTABLE/STATIC/SHARED/MODULE] to ucm_add_target()")
+    endif()
+    if("${ARG_SOURCES}" STREQUAL "")
+        message(FATAL_ERROR "Need to pass SOURCES and a list of source files to ucm_add_target()")
+    endif()
+
+    # init with the global unity flag
+    set(do_unity ${UCM_UNITY_BUILD})
+
+    # check the UNITY argument
+    if(NOT ARG_UNITY)
+        set(do_unity FALSE)
+    endif()
+
+    # if target is excluded through the exclusion list
+    list(FIND UCM_UNITY_BUILD_EXCLUDE_TARGETS ${ARG_NAME} is_target_excluded)
+    if(NOT ${is_target_excluded} STREQUAL "-1")
+        set(do_unity FALSE)
+    endif()
+
+    # unity build only for targets with > 1 source file (otherwise there will be an additional unnecessary target)
+    if(do_unity) # optimization
+        ucm_count_sources(${ARG_SOURCES} RESULT num_sources)
+        if(${num_sources} LESS 2)
+            set(do_unity FALSE)
+        endif()
+    endif()
+
+    set(wanted_cotire ${do_unity})
+
+    # if cotire cannot be used
+    if(do_unity AND NOT ucm_with_cotire)
+        set(do_unity FALSE)
+    endif()
+
+	# inform the developer that the current target might benefit from a unity build
+	if(NOT ARG_UNITY AND ${UCM_UNITY_BUILD})
+		ucm_count_sources(${ARG_SOURCES} RESULT num_sources)
+		if(${num_sources} GREATER 1)
+			message(AUTHOR_WARNING "Target '${ARG_NAME}' may benefit from a unity build.\nIt has ${num_sources} sources - enable with UNITY flag")
+		endif()
+	endif()
+
+    # prepare for the unity build
+    set(orig_target ${ARG_NAME})
+    if(do_unity)
+        # the original target will be added with a different name than the requested
+        set(orig_target ${ARG_NAME}_ORIGINAL)
+
+        # exclude requested files from unity build of the current target
+        foreach(excluded_file "${ARG_UNITY_EXCLUDED}")
+            set_source_files_properties(${excluded_file} PROPERTIES COTIRE_EXCLUDED TRUE)
+        endforeach()
+    endif()
+
+    # add the original target
+    if(${ARG_TYPE} STREQUAL "EXECUTABLE")
+        add_executable(${orig_target} ${ARG_SOURCES})
+    else()
+        add_library(${orig_target} ${ARG_TYPE} ${ARG_SOURCES})
+    endif()
+
+    if(do_unity)
+        # set the number of unity cpp files to be used for the unity target
+        if(NOT "${ARG_CPP_PER_UNITY}" STREQUAL "")
+            set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${ARG_CPP_PER_UNITY}")
+		else()
+			set_property(TARGET ${orig_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "100")
+		endif()
+
+        if(NOT "${ARG_PCH_FILE}" STREQUAL "")
+            set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}")
+        else()
+            set_target_properties(${orig_target} PROPERTIES COTIRE_ENABLE_PRECOMPILED_HEADER FALSE)
+        endif()
+        # add a unity target for the original one with the name intended for the original
+        set_target_properties(${orig_target} PROPERTIES COTIRE_UNITY_TARGET_NAME ${ARG_NAME})
+
+        # this is the library call that does the magic
+        cotire(${orig_target})
+        set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets")
+
+        # disable the original target and enable the unity one
+        get_target_property(unity_target_name ${orig_target} COTIRE_UNITY_TARGET_NAME)
+        set_target_properties(${orig_target} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
+        set_target_properties(${unity_target_name} PROPERTIES EXCLUDE_FROM_ALL 0 EXCLUDE_FROM_DEFAULT_BUILD 0)
+
+        # also set the name of the target output as the original one
+        set_target_properties(${unity_target_name} PROPERTIES OUTPUT_NAME ${ARG_NAME})
+        if(UCM_NO_COTIRE_FOLDER)
+            # reset the folder property so all unity targets dont end up in a single folder in the solution explorer of VS
+            set_target_properties(${unity_target_name} PROPERTIES FOLDER "")
+        endif()
+        set_target_properties(all_unity PROPERTIES FOLDER "CMakePredefinedTargets")
+    elseif(NOT "${ARG_PCH_FILE}" STREQUAL "")
+        set(wanted_cotire TRUE)
+        if(ucm_with_cotire)
+            set_target_properties(${orig_target} PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
+            set_target_properties(${orig_target} PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARG_PCH_FILE}")
+            cotire(${orig_target})
+            set_target_properties(clean_cotire PROPERTIES FOLDER "CMakePredefinedTargets")
+        endif()
+    endif()
+
+    # print a message if the target was requested to be cotired but it couldn't
+    if(wanted_cotire AND NOT ucm_with_cotire)
+        if(NOT COMMAND cotire)
+            message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire isn't loaded")
+        else()
+            message(AUTHOR_WARNING "Target \"${ARG_NAME}\" not cotired because cotire is older than the required version")
+        endif()
+    endif()
+endmacro()
diff --git a/console/ujpeg.cpp b/console/ujpeg.cpp
index 516292e..14eebec 100644
--- a/console/ujpeg.cpp
+++ b/console/ujpeg.cpp
@@ -1,7 +1,253 @@
+// NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
+// version 1.3.5 (2016-11-14)
+// Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler at gmx.net>
+// published under the terms of the MIT license
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DOCUMENTATION SECTION                                                     //
+// read this if you want to know what this is all about                      //
+///////////////////////////////////////////////////////////////////////////////
+
+// INTRODUCTION
+// ============
+//
+// This is a minimal decoder for baseline JPEG images. It accepts memory dumps
+// of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
+// RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
+// are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
+// not supported. All YCbCr subsampling schemes with power-of-two ratios are
+// supported, as are restart intervals. Progressive or lossless JPEG is not
+// supported.
+// Summed up, NanoJPEG should be able to decode all images from digital cameras
+// and most common forms of other non-progressive JPEG images.
+// The decoder is not optimized for speed, it's optimized for simplicity and
+// small code. Image quality should be at a reasonable level. A bicubic chroma
+// upsampling filter ensures that subsampled YCbCr images are rendered in
+// decent quality. The decoder is not meant to deal with broken JPEG files in
+// a graceful manner; if anything is wrong with the bitstream, decoding will
+// simply fail.
+// The code should work with every modern C compiler without problems and
+// should not emit any warnings. It uses only (at least) 32-bit integer
+// arithmetic and is supposed to be endianness independent and 64-bit clean.
+// However, it is not thread-safe.
+
+
+// COMPILE-TIME CONFIGURATION
+// ==========================
+//
+// The following aspects of NanoJPEG can be controlled with preprocessor
+// defines:
+//
+// _NJ_EXAMPLE_PROGRAM     = Compile a main() function with an example
+//                           program.
+// _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
+//                           file for NanoJPEG. Example:
+//                               #define _NJ_INCLUDE_HEADER_ONLY
+//                               #include "nanojpeg.c"
+//                               int main(void) {
+//                                   njInit();
+//                                   // your code here
+//                                   njDone();
+//                               }
+// NJ_USE_LIBC=1           = Use the malloc(), free(), memset() and memcpy()
+//                           functions from the standard C library (default).
+// NJ_USE_LIBC=0           = Don't use the standard C library. In this mode,
+//                           external functions njAlloc(), njFreeMem(),
+//                           njFillMem() and njCopyMem() need to be defined
+//                           and implemented somewhere.
+// NJ_USE_WIN32=0          = Normal mode (default).
+// NJ_USE_WIN32=1          = If compiling with MSVC for Win32 and
+//                           NJ_USE_LIBC=0, NanoJPEG will use its own
+//                           implementations of the required C library
+//                           functions (default if compiling with MSVC and
+//                           NJ_USE_LIBC=0).
+// NJ_CHROMA_FILTER=1      = Use the bicubic chroma upsampling filter
+//                           (default).
+// NJ_CHROMA_FILTER=0      = Use simple pixel repetition for chroma upsampling
+//                           (bad quality, but faster and less code).
+
+
+// API
+// ===
+//
+// For API documentation, read the "header section" below.
+
+
+// EXAMPLE
+// =======
+//
+// A few pages below, you can find an example program that uses NanoJPEG to
+// convert JPEG files into PGM or PPM. To compile it, use something like
+//     gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
+// You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
+// The only thing you might need is -Wno-shift-negative-value, because this
+// code relies on the target machine using two's complement arithmetic, but
+// the C standard does not, even though *any* practically useful machine
+// nowadays uses two's complement.
+
+
+///////////////////////////////////////////////////////////////////////////////
+// HEADER SECTION                                                            //
+// copy and pase this into nanojpeg.h if you want                            //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _NANOJPEG_H
+#define _NANOJPEG_H
+
+// nj_result_t: Result codes for njDecode().
+typedef enum _nj_result {
+    NJ_OK = 0,        // no error, decoding successful
+    NJ_NO_JPEG,       // not a JPEG file
+    NJ_UNSUPPORTED,   // unsupported format
+    NJ_OUT_OF_MEM,    // out of memory
+    NJ_INTERNAL_ERR,  // internal error
+    NJ_SYNTAX_ERROR,  // syntax error
+    __NJ_FINISHED,    // used internally, will never be reported
+} nj_result_t;
+
+// njInit: Initialize NanoJPEG.
+// For safety reasons, this should be called at least one time before using
+// using any of the other NanoJPEG functions.
+void njInit(void);
+
+// njDecode: Decode a JPEG image.
+// Decodes a memory dump of a JPEG file into internal buffers.
+// Parameters:
+//   jpeg = The pointer to the memory dump.
+//   size = The size of the JPEG file.
+// Return value: The error code in case of failure, or NJ_OK (zero) on success.
+nj_result_t njDecode(const void* jpeg, const int size);
+
+// njGetWidth: Return the width (in pixels) of the most recently decoded
+// image. If njDecode() failed, the result of njGetWidth() is undefined.
+int njGetWidth(void);
+
+// njGetHeight: Return the height (in pixels) of the most recently decoded
+// image. If njDecode() failed, the result of njGetHeight() is undefined.
+int njGetHeight(void);
+
+// njIsColor: Return 1 if the most recently decoded image is a color image
+// (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
+// of njGetWidth() is undefined.
+int njIsColor(void);
+
+// njGetImage: Returns the decoded image data.
+// Returns a pointer to the most recently image. The memory layout it byte-
+// oriented, top-down, without any padding between lines. Pixels of color
+// images will be stored as three consecutive bytes for the red, green and
+// blue channels. This data format is thus compatible with the PGM or PPM
+// file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
+// If njDecode() failed, the result of njGetImage() is undefined.
+unsigned char* njGetImage(void);
+
+// njGetImageSize: Returns the size (in bytes) of the image data returned
+// by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
+// undefined.
+int njGetImageSize(void);
+
+// njDone: Uninitialize NanoJPEG.
+// Resets NanoJPEG's internal state and frees all memory that has been
+// allocated at run-time by NanoJPEG. It is still possible to decode another
+// image after a njDone() call.
+void njDone(void);
+
+#endif//_NANOJPEG_H
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CONFIGURATION SECTION                                                     //
+// adjust the default settings for the NJ_ defines here                      //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef NJ_USE_LIBC
+    #define NJ_USE_LIBC 1
+#endif
+
+#ifndef NJ_USE_WIN32
+  #ifdef _MSC_VER
+    #define NJ_USE_WIN32 (!NJ_USE_LIBC)
+  #else
+    #define NJ_USE_WIN32 0
+  #endif
+#endif
+
+#ifndef NJ_CHROMA_FILTER
+    #define NJ_CHROMA_FILTER 1
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// EXAMPLE PROGRAM                                                           //
+// just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC)    //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef  _NJ_EXAMPLE_PROGRAM
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "ujpeg.h"
+
+int main(int argc, char* argv[]) {
+    int size;
+    char *buf;
+    FILE *f;
+
+    if (argc < 2) {
+        printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
+        return 2;
+    }
+    f = fopen(argv[1], "rb");
+    if (!f) {
+        printf("Error opening the input file.\n");
+        return 1;
+    }
+    fseek(f, 0, SEEK_END);
+    size = (int) ftell(f);
+    buf = (char*) malloc(size);
+    fseek(f, 0, SEEK_SET);
+    size = (int) fread(buf, 1, size, f);
+    fclose(f);
+
+    njInit();
+    if (njDecode(buf, size)) {
+        free((void*)buf);
+        printf("Error decoding the input file.\n");
+        return 1;
+    }
+    free((void*)buf);
+
+    f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
+    if (!f) {
+        printf("Error opening the output file.\n");
+        return 1;
+    }
+    fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
+    fwrite(njGetImage(), 1, njGetImageSize(), f);
+    fclose(f);
+    njDone();
+    return 0;
+}
+
+#endif
 
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -19,8 +265,6 @@
     #define NJ_FORCE_INLINE static inline
 #endif
 
-#define NJ_USE_LIBC 1
-
 #if NJ_USE_LIBC
     #include <stdlib.h>
     #include <string.h>
@@ -280,10 +524,12 @@ NJ_INLINE void njDecodeSOF(void) {
     int i, ssxmax = 0, ssymax = 0;
     nj_component_t* c;
     njDecodeLength();
+    njCheckError();
     if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR);
     if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED);
     nj.height = njDecode16(nj.pos+1);
     nj.width = njDecode16(nj.pos+3);
+    if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR);
     nj.ncomp = nj.pos[5];
     njSkip(6);
     switch (nj.ncomp) {
@@ -312,19 +558,17 @@ NJ_INLINE void njDecodeSOF(void) {
     }
     nj.mbsizex = ssxmax << 3;
     nj.mbsizey = ssymax << 3;
-    if ((nj.mbsizex == 0) || (nj.mbsizey == 0)) return;
     nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex;
     nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey;
     for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c) {
         c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax;
-        c->stride = (c->width + 7) & 0x7FFFFFF8;
         c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
-        c->stride = nj.mbwidth * nj.mbsizex * c->ssx / ssxmax;
+        c->stride = nj.mbwidth * c->ssx << 3;
         if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED);
-        if (!(c->pixels = (unsigned char *)njAllocMem(c->stride * (nj.mbheight * nj.mbsizey * c->ssy / ssymax)))) njThrow(NJ_OUT_OF_MEM);
+        if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * c->ssy << 3))) njThrow(NJ_OUT_OF_MEM);
     }
     if (nj.ncomp == 3) {
-        nj.rgb = (unsigned char *)njAllocMem(nj.width * nj.height * nj.ncomp);
+        nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
         if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
     }
     njSkip(nj.length);
@@ -335,6 +579,7 @@ NJ_INLINE void njDecodeDHT(void) {
     nj_vlc_code_t *vlc;
     static unsigned char counts[16];
     njDecodeLength();
+    njCheckError();
     while (nj.length >= 17) {
         i = nj.pos[0];
         if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
@@ -353,7 +598,7 @@ NJ_INLINE void njDecodeDHT(void) {
             remain -= currcnt << (16 - codelen);
             if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
             for (i = 0;  i < currcnt;  ++i) {
-                unsigned char code = nj.pos[i];
+                register unsigned char code = nj.pos[i];
                 for (j = spread;  j;  --j) {
                     vlc->bits = (unsigned char) codelen;
                     vlc->code = code;
@@ -374,6 +619,7 @@ NJ_INLINE void njDecodeDQT(void) {
     int i;
     unsigned char *t;
     njDecodeLength();
+    njCheckError();
     while (nj.length >= 65) {
         i = nj.pos[0];
         if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
@@ -388,6 +634,7 @@ NJ_INLINE void njDecodeDQT(void) {
 
 NJ_INLINE void njDecodeDRI(void) {
     njDecodeLength();
+    njCheckError();
     if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
     nj.rstinterval = njDecode16(nj.pos);
     njSkip(nj.length);
@@ -433,6 +680,7 @@ NJ_INLINE void njDecodeScan(void) {
     int rstcount = nj.rstinterval, nextrst = 0;
     nj_component_t* c;
     njDecodeLength();
+    njCheckError();
     if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
     if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
     njSkip(1);
@@ -489,7 +737,7 @@ NJ_INLINE void njUpsampleH(nj_component_t* c) {
     const int xmax = c->width - 3;
     unsigned char *out, *lin, *lout;
     int x, y;
-    out = njAllocMem((c->width * c->height) << 1);
+    out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
     if (!out) njThrow(NJ_OUT_OF_MEM);
     lin = c->pixels;
     lout = out;
@@ -509,7 +757,7 @@ NJ_INLINE void njUpsampleH(nj_component_t* c) {
     }
     c->width <<= 1;
     c->stride = c->width;
-    njFreeMem(c->pixels);
+    njFreeMem((void*)c->pixels);
     c->pixels = out;
 }
 
@@ -517,7 +765,7 @@ NJ_INLINE void njUpsampleV(nj_component_t* c) {
     const int w = c->width, s1 = c->stride, s2 = s1 + s1;
     unsigned char *out, *cin, *cout;
     int x, y;
-    out = njAllocMem((c->width * c->height) << 1);
+    out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
     if (!out) njThrow(NJ_OUT_OF_MEM);
     for (x = 0;  x < w;  ++x) {
         cin = &c->pixels[x];
@@ -538,7 +786,7 @@ NJ_INLINE void njUpsampleV(nj_component_t* c) {
     }
     c->height <<= 1;
     c->stride = c->width;
-    njFreeMem(c->pixels);
+    njFreeMem((void*) c->pixels);
     c->pixels = out;
 }
 
@@ -549,9 +797,9 @@ NJ_INLINE void njUpsample(nj_component_t* c) {
     unsigned char *out, *lin, *lout;
     while (c->width < nj.width) { c->width <<= 1; ++xshift; }
     while (c->height < nj.height) { c->height <<= 1; ++yshift; }
-    out = (unsigned char *)njAllocMem(c->width * c->height);
+    out = (unsigned char*) njAllocMem(c->width * c->height);
     if (!out) njThrow(NJ_OUT_OF_MEM);
-    //lin = c->pixels;
+    lin = c->pixels;
     lout = out;
     for (y = 0;  y < c->height;  ++y) {
         lin = &c->pixels[(y >> yshift) * c->stride];
@@ -560,13 +808,13 @@ NJ_INLINE void njUpsample(nj_component_t* c) {
         lout += c->width;
     }
     c->stride = c->width;
-    njFreeMem(c->pixels);
+    njFreeMem((void*) c->pixels);
     c->pixels = out;
 }
 
 #endif
 
-NJ_INLINE void njConvert() {
+NJ_INLINE void njConvert(void) {
     int i;
     nj_component_t* c;
     for (i = 0, c = nj.comp;  i < nj.ncomp;  ++i, ++c) {
@@ -592,9 +840,9 @@ NJ_INLINE void njConvert() {
         const unsigned char *pcr = nj.comp[2].pixels;
         for (yy = nj.height;  yy;  --yy) {
             for (x = 0;  x < nj.width;  ++x) {
-                 int y = py[x] << 8;
-                 int cb = pcb[x] - 128;
-                 int cr = pcr[x] - 128;
+                register int y = py[x] << 8;
+                register int cb = pcb[x] - 128;
+                register int cr = pcr[x] - 128;
                 *prgb++ = njClip((y            + 359 * cr + 128) >> 8);
                 *prgb++ = njClip((y -  88 * cb - 183 * cr + 128) >> 8);
                 *prgb++ = njClip((y + 454 * cb            + 128) >> 8);
@@ -665,4 +913,4 @@ int njIsColor(void)             { return (nj.ncomp != 1); }
 unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
 int njGetImageSize(void)        { return nj.width * nj.height * nj.ncomp; }
 
-#endif // _NJ_INCLUDE_HEADER_ONLY
+#endif // _NJ_INCLUDE_HEADER_ONLY
\ No newline at end of file
diff --git a/console/ujpeg.h b/console/ujpeg.h
index 9321970..cf913b8 100644
--- a/console/ujpeg.h
+++ b/console/ujpeg.h
@@ -1,10 +1,6 @@
 #ifndef _NANOJPEG_H
 #define _NANOJPEG_H
 
-#ifdef  __cplusplus
-extern "C" {
-#endif
-
 // nj_result_t: Result codes for njDecode().
 typedef enum _nj_result {
     NJ_OK = 0,        // no error, decoding successful
@@ -61,9 +57,5 @@ int njGetImageSize(void);
 // allocated at run-time by NanoJPEG. It is still possible to decode another
 // image after a njDone() call.
 void njDone(void);
-    
-#ifdef  __cplusplus
-}
-#endif
 
 #endif//_NANOJPEG_H
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644
index 0000000..7edff52
--- /dev/null
+++ b/docs/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright 2016, The dcm2niix contributors
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the new BSD license. See the license.txt file or visit
+# https://opensource.org/licenses/BSD-3-Clause for details.
+
+find_program(SPHINX_EXECUTABLE
+        NAMES sphinx-build
+        HINTS $ENV{SPHINX_DIR}
+        PATH_SUFFIXES bin
+        DOC "Sphinx documentation generator")
+
+if (NOT SPHINX_EXECUTABLE)
+        message(FATAL_ERROR "sphinx-build executable not found")
+endif ()
+
+set(SPHINX_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/source")
+set(SPHINX_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/build")
+set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/doctrees")
+set(SPHINX_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/man")
+
+configure_file("${SPHINX_SOURCE_DIR}/conf.py"
+        "${SPHINX_BUILD_DIR}/conf.py"
+        COPYONLY)
+
+add_custom_target(build-man ALL
+        COMMAND ${SPHINX_EXECUTABLE}
+                -b man
+                -c "${SPHINX_BUILD_DIR}"
+                -d "${SPHINX_CACHE_DIR}"
+                "${SPHINX_SOURCE_DIR}"
+                "${SPHINX_OUTPUT_DIR}"
+        COMMENT "Generating man pages with Sphinx")
+
+install(DIRECTORY "${SPHINX_OUTPUT_DIR}/"
+        DESTINATION "share/man/man1")
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..526b366
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,340 @@
+# -*- coding: utf-8 -*-
+#
+# dcm2niix documentation build configuration file, created by
+# sphinx-quickstart on Tue Dec 20 16:22:24 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#
+# source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'dcm2niix'
+copyright = u'2016 The dcm2niix contributors'
+author = u'The dcm2niix contributors'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = u'1.0'
+# The full version, including alpha/beta/rc tags.
+release = u'1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+# html_theme_path = []
+
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = u'dcm2niix v1.0'
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#
+# html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+#
+# html_domain_indices = True
+
+# If false, no index is generated.
+#
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'dcm2niixdoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+     # The paper size ('letterpaper' or 'a4paper').
+     #
+     # 'papersize': 'letterpaper',
+
+     # The font size ('10pt', '11pt' or '12pt').
+     #
+     # 'pointsize': '10pt',
+
+     # Additional stuff for the LaTeX preamble.
+     #
+     # 'preamble': '',
+
+     # Latex figure (float) alignment
+     #
+     # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'dcm2niix.tex', u'dcm2niix Documentation',
+     author, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, 	itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('dcm2niix', 'dcm2niix', u'DICOM to NifTI converter',
+     ['This manual was developed and is maintained by Ghislain Antony Vaillant <ghisvail at gmail.com>'], 1),
+    ('dcm2niibatch', 'dcm2niibatch', u'DICOM to NifTI batch converter',
+     ['This manual was developed and is maintained by Benjamin Irving <mail at birving.com>'], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'dcm2niix', u'dcm2niix Documentation',
+     author, 'dcm2niix', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
diff --git a/docs/source/dcm2niibatch.rst b/docs/source/dcm2niibatch.rst
new file mode 100644
index 0000000..1e6bc14
--- /dev/null
+++ b/docs/source/dcm2niibatch.rst
@@ -0,0 +1,81 @@
+:orphan:
+
+dcm2niibatch manual
+===================
+
+Synopsis
+--------
+
+**dcm2niixbatch** <*configuration-file*>
+
+
+Description
+-----------
+
+Most medical imaging devices save images in some variation of the popular DICOM
+format. However, most scientific tools expect medical images to be stored in
+the comparatively simpler NIfTI format. **dcm2niix** is designed to perform
+such conversion from DICOM to NIfTI with a simple command-line interface.
+
+**dcm2niibatch** acts as a wrapper around **dcm2niix** and allows the processing of
+multiple DICOM sequences by specifying the list of files and settings in a yaml text file.
+The makes processing a dataset of DICOM scans simpler and more easily repeatable.
+
+In addition, yaml files are designed to be both human and machine readable and a
+script can easily be written in many programming languages to automatically create a
+yaml file based on an existing folder structure.
+
+Please be advised that **dcm2niix** and **dcm2niibatch** have been developed for research
+purposes only and should not be considered a clinical tool.
+
+
+Configuration file format
+-------------------------
+
+Perform a batch conversion of multiple dicoms using **dcm2niibatch**, which is run by passing a
+configuration file e.g *dcm2niibatch batch_config.yml*
+
+The configuration file should be in yaml format as shown in example *batch_config.yaml*
+
+.. code-block:: yaml
+
+    Options:
+      isGz:             false
+      isFlipY:          false
+      isVerbose:        false
+      isCreateBIDS:     false
+      isOnlySingleFile: false
+    Files:
+        -
+          in_dir:           /path/to/first/folder
+          out_dir:          /path/to/output/folder
+          filename:         dcemri
+        -
+          in_dir:           /path/to/second/folder
+          out_dir:          /path/to/output/folder
+          filename:         fa3
+
+You can add as many files as you want to convert as long as this structure stays consistent.
+Note that a dash must separate each file.
+
+in_dir     Path to the dicom files
+
+out_dir    Path to save the nifti file
+
+filename   File name of nifti file to save
+
+
+See also
+--------
+
+:manpage:`dcm2niix(1)`
+
+
+Licensing
+---------
+
+Copying and distribution of this file, with or without modification, are
+permitted in any medium without royalty provided the copyright notice and this
+notice are preserved. This file is offered as-is, without any warranty.
+
+
diff --git a/docs/source/dcm2niix.rst b/docs/source/dcm2niix.rst
new file mode 100644
index 0000000..8d27ca8
--- /dev/null
+++ b/docs/source/dcm2niix.rst
@@ -0,0 +1,73 @@
+:orphan:
+
+dcm2niix manual
+===============
+
+Synopsis
+--------
+
+**dcm2niix** [*options*] <*sourcedir*>
+
+
+Description
+-----------
+
+Most medical imaging devices save images in some variation of the popular DICOM
+format. However, most scientific tools expect medical images to be stored in
+the comparatively simpler NIfTI format. **dcm2niix** is designed to perform
+such conversion from DICOM to NIfTI with a simple command-line interface.
+
+Please be advised that **dcm2niix** has been developed for research purposes
+only and should not be considered a clinical tool.
+
+
+Options
+-------
+
+-b <y/n>        Save additional BIDS metadata to a side-car .json file.
+
+-f <format>     Format string for the output filename(s). The following
+                specifiers are supported:
+
+                - %a, antenna (coil) number
+                - %c, comments
+                - %d, description
+                - %e, echo number
+                - %f, folder name
+                - %i, patient ID
+                - %m, manufacturer
+                - %n, patient name
+                - %p, protocol
+                - %s, series number
+                - %t, time
+                - %u, acquisition number
+                - %z, sequence name.
+
+                The default format string is "%p_%e_%4s".
+
+-m <y/n>        Merge slices from the same series regardless of study time,
+                echo, coil, orientation, etc...
+
+-o <path>       Output directory where the converted files should be saved. If
+                unspecified, the files are saved within the specified source
+                directory.
+
+-s <y/n>        Convert a single file only.
+
+-t <y/n>        Save patient details.
+
+-v <h/y/n>  	Enable verbose output. "n" for succinct, "y" for verbose, "h" for
+                high verbosity
+
+-x <y/n>        Crop images.
+
+-z <y/i/n>      Desired compression method. The "y"es option uses the external
+                program pigz if available. The "i" option compresses the image
+                using the slower built-in compression routines.
+
+Licensing
+---------
+
+Copying and distribution of this file, with or without modification, are
+permitted in any medium without royalty provided the copyright notice and this
+notice are preserved. This file is offered as-is, without any warranty.
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..73b3174
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,22 @@
+.. dcm2niix documentation master file, created by
+   sphinx-quickstart on Tue Dec 20 16:22:24 2016.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to dcm2niix's documentation!
+====================================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/license.txt b/license.txt
index c956305..0b422b9 100644
--- a/license.txt
+++ b/license.txt
@@ -1,9 +1,9 @@
 The following files are from different authors and have their own licenses
 
-nifti.h, nifti1_io.h/nifti1_io.c is public domain
+nifti.h, nifti1_io.h/nifti1_io_core.cpp is public domain
   http://niftilib.sourceforge.net
   http://sourceforge.net/projects/niftilib/files/latest/download
-ujpeg.h/ujpeg.c see web site for licenses
+ujpeg.h/ujpeg.cpp uses the MIT license (see file for license text)
   http://keyj.emphy.de/nanojpeg/
 miniz.c is public domain (http://unlicense.org)
  https://code.google.com/p/miniz/

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/dcm2niix.git



More information about the debian-med-commit mailing list